v0.14.0 76d1892

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// getStackTitleBorderColor returns a subtle border color slightly darker than the title background
40func getStackTitleBorderColor(isDark bool) string {
41	if isDark {
42		return "#353535" // Slightly darker than #404040
43	}
44	return "#e5e5e5" // Slightly darker than #f0f0f0
45}
46
47// getActiveTabBg returns a distinct background color for the active tab
48func getActiveTabBg(isDark bool) string {
49	if isDark {
50		return "#707070" // Significantly lighter gray than inactive (#404040) - similar to stacked panes
51	}
52	return "#c0c0c0" // Significantly darker gray than inactive (#f0f0f0) - similar to stacked panes
53}
54
55// getActivePaneBorderColor returns the border color for active panes based on config and theme
56func getActivePaneBorderColor(styling config.WorkspaceStylingConfig, isDark bool) string {
57	// Use configured border color if set
58	if styling.BorderColor != "" {
59		// Check if it's a GTK theme variable (starts with @)
60		if strings.HasPrefix(styling.BorderColor, "@") {
61			return styling.BorderColor
62		}
63		// Return the configured color as-is (could be hex, rgb, etc.)
64		return styling.BorderColor
65	}
66
67	// Fallback to hardcoded colors based on theme
68	if isDark {
69		return "#4A90E2" // Deeper blue pastel for dark theme
70	}
71	return "#87CEEB" // Sky blue pastel for light theme
72}
73
74// getInactivePaneBorderColor returns the border color for inactive panes based on config and theme
75func getInactivePaneBorderColor(styling config.WorkspaceStylingConfig, isDark bool) string {
76	// Use configured border color if set
77	if styling.InactiveBorderColor != "" {
78		// Check if it's a GTK theme variable (starts with @)
79		if strings.HasPrefix(styling.InactiveBorderColor, "@") {
80			return styling.InactiveBorderColor
81		}
82		// Return the configured color as-is (could be hex, rgb, etc.)
83		return styling.InactiveBorderColor
84	}
85
86	// Fallback to hardcoded colors based on theme
87	if isDark {
88		return "#333333" // Dark border for dark theme
89	}
90	return "#dddddd" // Light border for light theme
91}
92
93// generateWorkspaceCSS generates the complete CSS for workspace panes and stacked panes
94func (wm *WorkspaceManager) generateWorkspaceCSS() string {
95	cfg := config.Get()
96	styling := cfg.Workspace.Styling
97
98	// Get appropriate colors based on GTK theme preference
99	var windowBackgroundColor string
100	isDark := webkit.PrefersDarkTheme()
101	if isDark {
102		windowBackgroundColor = "#2b2b2b" // Dark window background
103	} else {
104		windowBackgroundColor = "#ffffff" // Light window background
105	}
106
107	activeBorderColor := getActivePaneBorderColor(styling, isDark)
108	inactiveBorderColor := getInactivePaneBorderColor(styling, isDark)
109
110	// Pane mode border color - use configured value or fallback to blue
111	paneModeColor := styling.PaneModeBorderColor
112	if paneModeColor == "" {
113		paneModeColor = "#4A90E2" // Fallback to blue if not configured
114	}
115
116	// Tab mode border color - use configured value or fallback to orange
117	tabModeColor := styling.TabModeBorderColor
118	if tabModeColor == "" {
119		tabModeColor = "#FFA500" // Fallback to orange if not configured
120	}
121
122	// Stacked title border - subtle separator between titles
123	stackedTitleBorder := fmt.Sprintf("1px solid %s", getStackTitleBorderColor(isDark))
124
125	// UI Scale calculations for title bar elements (base values at 1.0 scale)
126	uiScale := styling.UIScale
127	if uiScale <= 0 {
128		uiScale = 1.0 // Fallback to default if invalid
129	}
130	titleFontSize := int(12 * uiScale)              // Base: 12px
131	titlePaddingVertical := int(4 * uiScale)        // Base: 4px
132	titlePaddingHorizontal := int(8 * uiScale)      // Base: 8px
133	titleMinHeight := int(24 * uiScale)             // Base: 24px
134	faviconMargin := int(4 * uiScale)               // Base: 4px
135
136	css := fmt.Sprintf(`window {
137	  background-color: %s;
138	  padding: 0;
139	  margin: 0;
140	}
141
142	/* Pane mode active - change window background to border color */
143	window.pane-mode-active {
144	  background-color: %s;
145	}
146
147	/* Workspace root container */
148	paned, box {
149	  background-color: %s;
150	  transition: margin 150ms ease-in-out;
151	}
152
153	/* Base pane styling - configurable inactive borders */
154	.workspace-pane, .stacked-pane-container {
155	  border-width: %dpx;
156	  border-style: solid;
157	  border-color: %s;
158	  border-radius: %dpx;
159	  margin: 0;
160	  transition-property: border-color;
161	  transition-duration: %dms;
162	  transition-timing-function: ease-in-out;
163	}
164
165	/* Active pane border styling */
166	.workspace-pane-active {
167	  border-width: %dpx;
168	  border-color: %s;
169	}
170
171	/* Stacked panes styling */
172	.stacked-pane-container {
173	  background-color: %s;
174	}
175
176	/* Stacked pane containers keep inactive border when active */
177	.stacked-pane-container.workspace-pane-active {
178	  border-width: %dpx;
179	  border-color: %s;
180	}
181
182	.stacked-pane-title {
183	  background-color: %s;
184	  border-bottom: %s;
185	  padding: %dpx %dpx;
186	  min-height: %dpx;
187	  transition: background-color %dms ease-in-out;
188	}
189
190	.stacked-pane-title:hover {
191	  background-color: %s;
192	}
193
194	.stacked-pane-title-text {
195	  font-size: %dpx;
196	  color: %s;
197	  font-weight: 500;
198	}
199
200	.stacked-pane-favicon {
201	  margin-right: %dpx;
202	}
203
204	.stacked-pane-active {
205	  /* Active pane is fully visible */
206	}
207
208	.stacked-pane-collapsed {
209	  /* Collapsed panes are hidden - handled in code via widget visibility */
210	}
211
212	/* Tab bar styling */
213	.tab-bar {
214	  background-color: %s;
215	  border-top: 2px solid %s;
216	  padding: 0;
217	  min-height: %dpx;
218	}
219
220	/* Tab mode active - window background changes to border color (like pane mode) */
221	window.tab-mode-active {
222	  background-color: %s;
223	}
224
225	/* Tab button styling - matches stacked pane titles */
226	button.tab-button {
227	  background-color: %s;
228	  background-image: none;
229	  border: none;
230	  border-right: 1px solid %s;
231	  border-radius: 0;
232	  padding: %dpx %dpx;
233	  transition: background-color %dms ease-in-out;
234	}
235
236	button.tab-button:hover {
237	  background-color: %s;
238	  background-image: none;
239	}
240
241	button.tab-button.tab-button-active {
242	  background-color: %s;
243	  background-image: none;
244	  border: none;
245	  border-right: 1px solid %s;
246	  border-radius: 0;
247	  font-weight: 600;
248	}
249
250	/* Tab title text */
251	.tab-title {
252	  font-size: %dpx;
253	  color: %s;
254	  font-weight: 500;
255	}
256
257	/* Tab content area */
258	.tab-content-area {
259	  background-color: %s;
260	}
261
262	/* Tab workspace containers */
263	.tab-workspace-container {
264	  background-color: %s;
265	}
266
267	/* Pulse animation for tab mode */
268	@keyframes pulse-border {
269	  0%%, 100%% { opacity: 1.0; }
270	  50%% { opacity: 0.6; }
271	}`,
272		windowBackgroundColor,           // window background
273		paneModeColor,                   // window.pane-mode-active background (border color)
274		windowBackgroundColor,           // paned, box background
275		styling.InactiveBorderWidth,     // base pane border-width (inactive)
276		inactiveBorderColor,             // base pane border-color (inactive)
277		styling.BorderRadius,            // base pane border radius
278		styling.TransitionDuration,      // transition-duration
279		styling.BorderWidth,             // workspace-pane-active border-width
280		activeBorderColor,               // workspace-pane-active border color
281		windowBackgroundColor,           // stacked-pane-container background
282		styling.InactiveBorderWidth,     // stacked-pane-container.active border-width
283		inactiveBorderColor,             // stacked-pane-container.active border-color
284		getStackTitleBg(isDark),         // stacked-pane-title background
285		stackedTitleBorder,              // stacked-pane-title border-bottom
286		titlePaddingVertical,            // stacked-pane-title padding vertical
287		titlePaddingHorizontal,          // stacked-pane-title padding horizontal
288		titleMinHeight,                  // stacked-pane-title min-height
289		styling.TransitionDuration,      // stacked-pane-title transition
290		getStackTitleHoverBg(isDark),    // stacked-pane-title:hover background
291		titleFontSize,                   // stacked-pane-title-text font-size
292		getStackTitleTextColor(isDark),  // stacked-pane-title-text color
293		faviconMargin,                   // stacked-pane-favicon margin-right
294		// Tab bar styles
295		windowBackgroundColor,           // tab-bar background
296		getStackTitleBorderColor(isDark), // tab-bar border-top
297		int(32 * uiScale),               // tab-bar min-height
298		tabModeColor,                    // window.tab-mode-active .tab-bar border-top-color (orange, distinct from pane mode blue)
299		getStackTitleBg(isDark),         // tab-button background (same as stacked title)
300		getStackTitleBorderColor(isDark), // tab-button border-right
301		titlePaddingVertical,            // tab-button padding vertical
302		titlePaddingHorizontal,          // tab-button padding horizontal
303		styling.TransitionDuration,      // tab-button transition
304		getStackTitleHoverBg(isDark),    // tab-button:hover background
305		getActiveTabBg(isDark),          // tab-button-active background (much more visible)
306		getStackTitleBorderColor(isDark), // tab-button-active border-right
307		titleFontSize,                   // tab-title font-size
308		getStackTitleTextColor(isDark),  // tab-title color
309		windowBackgroundColor,           // tab-content-area background
310		windowBackgroundColor,           // tab-workspace-container background
311	)
312
313	return css
314}
315
316// Global CSS provider tracking to prevent duplication
317var (
318	globalCSSInitialized bool
319	globalCSSContent     string
320)
321
322// ensureWorkspaceStyles ensures that CSS styles are applied for workspace panes
323// Improved to prevent CSS provider duplication by using global CSS tracking
324func (wm *WorkspaceManager) ensureWorkspaceStyles() {
325	if wm == nil {
326		return
327	}
328
329	// Generate CSS content
330	workspaceCSS := wm.generateWorkspaceCSS()
331
332	// Only apply CSS if it hasn't been applied globally or if content changed
333	if !globalCSSInitialized || globalCSSContent != workspaceCSS {
334		if err := webkit.AddCSSProvider(workspaceCSS); err != nil {
335			log.Printf("[workspace] WARNING: Failed to add CSS provider: %v", err)
336		}
337		globalCSSInitialized = true
338		globalCSSContent = workspaceCSS
339		log.Printf("[workspace] Applied workspace CSS styles (%d bytes)", len(workspaceCSS))
340		log.Printf("[workspace] CSS preview: %s...", workspaceCSS[:min(200, len(workspaceCSS))])
341	}
342
343	wm.cssInitialized = true
344}

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