v0.13.0 976176a

workspace_css.go RawHistoryFolder

1// workspace_css.go - CSS generation and styling for workspace panes
2package browser
3
4import (
5	"fmt"
6	"log"
7	"strings"
8
9	"github.com/bnema/dumber/internal/config"
10	"github.com/bnema/dumber/pkg/webkit"
11)
12
13// Helper functions for stacked pane CSS colors
14
15// getStackTitleBg returns the background color for stack titles based on theme
16func getStackTitleBg(isDark bool) string {
17	if isDark {
18		return "#404040"
19	}
20	return "#f0f0f0"
21}
22
23// getStackTitleHoverBg returns the hover background color for stack titles based on theme
24func getStackTitleHoverBg(isDark bool) string {
25	if isDark {
26		return "#505050"
27	}
28	return "#e0e0e0"
29}
30
31// getStackTitleTextColor returns the text color for stack titles based on theme
32func getStackTitleTextColor(isDark bool) string {
33	if isDark {
34		return "#ffffff"
35	}
36	return "#333333"
37}
38
39// getActivePaneBorderColor returns the border color for active panes based on config and theme
40func getActivePaneBorderColor(styling config.WorkspaceStylingConfig, isDark bool) string {
41	// Use configured border color if set
42	if styling.BorderColor != "" {
43		// Check if it's a GTK theme variable (starts with @)
44		if strings.HasPrefix(styling.BorderColor, "@") {
45			return styling.BorderColor
46		}
47		// Return the configured color as-is (could be hex, rgb, etc.)
48		return styling.BorderColor
49	}
50
51	// Fallback to hardcoded colors based on theme
52	if isDark {
53		return "#4A90E2" // Deeper blue pastel for dark theme
54	}
55	return "#87CEEB" // Sky blue pastel for light theme
56}
57
58// generateWorkspaceCSS generates the complete CSS for workspace panes and stacked panes
59func (wm *WorkspaceManager) generateWorkspaceCSS() string {
60	cfg := config.Get()
61	styling := cfg.Workspace.Styling
62
63	// Get appropriate colors based on GTK theme preference
64	var inactiveBorderColor, windowBackgroundColor string
65	isDark := webkit.PrefersDarkTheme()
66	if isDark {
67		inactiveBorderColor = "#333333"   // Dark border for dark theme
68		windowBackgroundColor = "#2b2b2b" // Dark window background
69	} else {
70		inactiveBorderColor = "#dddddd"   // Light border for light theme
71		windowBackgroundColor = "#ffffff" // Light window background
72	}
73
74	activeBorderColor := getActivePaneBorderColor(styling, isDark)
75
76	// Pane mode border color - use configured value or fallback to orange
77	paneModeColor := styling.PaneModeBorderColor
78	if paneModeColor == "" {
79		paneModeColor = "#FFA500" // Fallback to orange if not configured
80	}
81
82	css := fmt.Sprintf(`window {
83	  background-color: %s;
84	  padding: 0;
85	  margin: 0;
86	}
87
88	/* Pane mode active - change window background to border color */
89	window.pane-mode-active {
90	  background-color: %s;
91	}
92
93	/* Workspace root container */
94	paned, box {
95	  background-color: %s;
96	  transition: margin 150ms ease-in-out;
97	}
98
99	/* Base pane styling - subtle border for inactive panes */
100	.workspace-pane, .stacked-pane-container {
101	  border: 2px solid %s;
102	  border-radius: %dpx;
103	  transition: border-color %dms ease-in-out;
104	  margin: 0;
105	}
106
107	/* Active pane border styling */
108	.workspace-pane-active {
109	  border-color: %s;
110	}
111
112	/* Stacked panes styling */
113	.stacked-pane-container {
114	  background-color: %s;
115	}
116
117	/* Stacked pane containers never get active border - focus is shown via WebKit */
118	.stacked-pane-container.workspace-pane-active {
119	  border-color: %s;
120	}
121
122	.stacked-pane-title {
123	  background-color: %s;
124	  border-bottom: 1px solid %s;
125	  padding: 4px 8px;
126	  min-height: 24px;
127	  transition: background-color %dms ease-in-out;
128	}
129
130	.stacked-pane-title:hover {
131	  background-color: %s;
132	}
133
134	.stacked-pane-title-text {
135	  font-size: 12px;
136	  color: %s;
137	  font-weight: 500;
138	}
139
140	.stacked-pane-favicon {
141	  margin-right: 4px;
142	}
143
144	.stacked-pane-active {
145	  /* Active pane is fully visible */
146	}
147
148	.stacked-pane-collapsed {
149	  /* Collapsed panes are hidden - handled in code via widget visibility */
150	}`,
151		windowBackgroundColor,          // window background
152		paneModeColor,                  // window.pane-mode-active background (border color)
153		windowBackgroundColor,          // paned, box background
154		inactiveBorderColor,            // base pane border color (inactive)
155		styling.BorderRadius,           // base pane border radius
156		styling.TransitionDuration,     // base pane border transition
157		activeBorderColor,              // workspace-pane-active border color
158		windowBackgroundColor,          // stacked-pane-container background
159		inactiveBorderColor,            // stacked-pane-container.workspace-pane-active border color (keep subtle)
160		getStackTitleBg(isDark),        // stacked-pane-title background
161		inactiveBorderColor,            // stacked-pane-title border-bottom
162		styling.TransitionDuration,     // stacked-pane-title transition
163		getStackTitleHoverBg(isDark),   // stacked-pane-title:hover background
164		getStackTitleTextColor(isDark), // stacked-pane-title-text color
165	)
166
167	return css
168}
169
170// Global CSS provider tracking to prevent duplication
171var (
172	globalCSSInitialized bool
173	globalCSSContent     string
174)
175
176// ensureWorkspaceStyles ensures that CSS styles are applied for workspace panes
177// Improved to prevent CSS provider duplication by using global CSS tracking
178func (wm *WorkspaceManager) ensureWorkspaceStyles() {
179	if wm == nil {
180		return
181	}
182
183	// Generate CSS content
184	workspaceCSS := wm.generateWorkspaceCSS()
185
186	// Only apply CSS if it hasn't been applied globally or if content changed
187	if !globalCSSInitialized || globalCSSContent != workspaceCSS {
188		if err := webkit.AddCSSProvider(workspaceCSS); err != nil {
189			log.Printf("[workspace] WARNING: Failed to add CSS provider: %v", err)
190		}
191		globalCSSInitialized = true
192		globalCSSContent = workspaceCSS
193		log.Printf("[workspace] Applied workspace CSS styles (%d bytes)", len(workspaceCSS))
194		log.Printf("[workspace] CSS preview: %s...", workspaceCSS[:min(200, len(workspaceCSS))])
195	}
196
197	wm.cssInitialized = true
198}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close