detached d13852b

feat(tabs): add tab bar styling matching stacked panes

- Add .tab-bar, .tab-button, .tab-button-active CSS classes
- Match stacked pane title aesthetics (no border-radius, lighter active bg)
- Add orange border for tab mode (distinct from blue pane mode)
- Remove colored border-bottom on active tabs per user feedback
- Tab buttons use same padding and fonts as stacked titles
- Support UI scaling with ui_scale config multiplier
- Add pulse animation for tab mode active state
- Theme-aware colors for light/dark mode
commit
d13852bad5809bb3856f1c106eb7cce845be563b
author
bnema · · 2025-11-17 19:41 UTC
parent
965849a
signature
unsigned
1 file · +85 -2
M internal/app/browser/workspace_css.go +85 -2
@@ -99,10 +99,16 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string {
9999 activeBorderColor := getActivePaneBorderColor(styling, isDark)
100100 inactiveBorderColor := getInactivePaneBorderColor(styling, isDark)
101101
102- // Pane mode border color - use configured value or fallback to orange
102+ // Pane mode border color - use configured value or fallback to blue
103103 paneModeColor := styling.PaneModeBorderColor
104104 if paneModeColor == "" {
105- paneModeColor = "#FFA500" // Fallback to orange if not configured
105+ paneModeColor = "#4A90E2" // Fallback to blue if not configured
106+ }
107+
108+ // Tab mode border color - use configured value or fallback to orange
109+ tabModeColor := styling.TabModeBorderColor
110+ if tabModeColor == "" {
111+ tabModeColor = "#FFA500" // Fallback to orange if not configured
106112 }
107113
108114 // Stacked title border - subtle separator between titles
@@ -193,6 +199,66 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string {
193199
194200 .stacked-pane-collapsed {
195201 /* Collapsed panes are hidden - handled in code via widget visibility */
202+ }
203+
204+ /* Tab bar styling */
205+ .tab-bar {
206+ background-color: %s;
207+ border-top: 2px solid %s;
208+ padding: 0;
209+ min-height: %dpx;
210+ }
211+
212+ /* Tab mode active - window background changes to border color (like pane mode) */
213+ window.tab-mode-active {
214+ background-color: %s;
215+ }
216+
217+ /* Tab button styling - matches stacked pane titles */
218+ .tab-button {
219+ background-color: %s;
220+ border: none;
221+ border-right: 1px solid %s;
222+ border-radius: 0;
223+ padding: %dpx %dpx;
224+ min-width: 100px;
225+ max-width: 200px;
226+ transition: background-color %dms ease-in-out;
227+ }
228+
229+ .tab-button:hover {
230+ background-color: %s;
231+ }
232+
233+ .tab-button-active {
234+ background-color: %s;
235+ border: none;
236+ border-right: 1px solid %s;
237+ border-radius: 0;
238+ font-weight: 600;
239+ }
240+
241+ /* Tab title text */
242+ .tab-title {
243+ font-size: %dpx;
244+ color: %s;
245+ font-weight: 500;
246+ }
247+
248+ /* Tab content area */
249+ .tab-content-area {
250+ background-color: %s;
251+ }
252+
253+ /* Tab workspace containers */
254+ .tab-workspace-container {
255+ background-color: %s;
256+ }
257+
258+ /* Pulse animation for tab mode */
259+ @keyframes pulse-border {
260+ 0%%, 100%% { opacity: 1.0; }
261+ 50%% { opacity: 0.6; }
196262 }`,
197263 windowBackgroundColor, // window background
198264 paneModeColor, // window.pane-mode-active background (border color)
@@ -216,6 +282,23 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string {
216282 titleFontSize, // stacked-pane-title-text font-size
217283 getStackTitleTextColor(isDark), // stacked-pane-title-text color
218284 faviconMargin, // stacked-pane-favicon margin-right
285+ // Tab bar styles
286+ windowBackgroundColor, // tab-bar background
287+ getStackTitleBorderColor(isDark), // tab-bar border-top
288+ int(32 * uiScale), // tab-bar min-height
289+ tabModeColor, // window.tab-mode-active .tab-bar border-top-color (orange, distinct from pane mode blue)
290+ getStackTitleBg(isDark), // tab-button background (same as stacked title)
291+ getStackTitleBorderColor(isDark), // tab-button border-right
292+ titlePaddingVertical, // tab-button padding vertical
293+ titlePaddingHorizontal, // tab-button padding horizontal
294+ styling.TransitionDuration, // tab-button transition
295+ getStackTitleHoverBg(isDark), // tab-button:hover background
296+ getStackTitleHoverBg(isDark), // tab-button-active background (lighter/brighter)
297+ getStackTitleBorderColor(isDark), // tab-button-active border-right
298+ titleFontSize, // tab-title font-size
299+ getStackTitleTextColor(isDark), // tab-title color
300+ windowBackgroundColor, // tab-content-area background
301+ windowBackgroundColor, // tab-workspace-container background
219302 )
220303
221304 return css
@@ -99,10 +99,16 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string {
99 activeBorderColor := getActivePaneBorderColor(styling, isDark)99 activeBorderColor := getActivePaneBorderColor(styling, isDark)
100 inactiveBorderColor := getInactivePaneBorderColor(styling, isDark)100 inactiveBorderColor := getInactivePaneBorderColor(styling, isDark)
101 101
102- // Pane mode border color - use configured value or fallback to orange102+ // Pane mode border color - use configured value or fallback to blue
103 paneModeColor := styling.PaneModeBorderColor103 paneModeColor := styling.PaneModeBorderColor
104 if paneModeColor == "" {104 if paneModeColor == "" {
105- paneModeColor = "#FFA500" // Fallback to orange if not configured105+ paneModeColor = "#4A90E2" // Fallback to blue if not configured
106+ }
107+
108+ // Tab mode border color - use configured value or fallback to orange
109+ tabModeColor := styling.TabModeBorderColor
110+ if tabModeColor == "" {
111+ tabModeColor = "#FFA500" // Fallback to orange if not configured
106 }112 }
107 113
108 // Stacked title border - subtle separator between titles114 // Stacked title border - subtle separator between titles
@@ -193,6 +199,66 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string {
193 199
194 .stacked-pane-collapsed {200 .stacked-pane-collapsed {
195 /* Collapsed panes are hidden - handled in code via widget visibility */201 /* Collapsed panes are hidden - handled in code via widget visibility */
202+ }
203+
204+ /* Tab bar styling */
205+ .tab-bar {
206+ background-color: %s;
207+ border-top: 2px solid %s;
208+ padding: 0;
209+ min-height: %dpx;
210+ }
211+
212+ /* Tab mode active - window background changes to border color (like pane mode) */
213+ window.tab-mode-active {
214+ background-color: %s;
215+ }
216+
217+ /* Tab button styling - matches stacked pane titles */
218+ .tab-button {
219+ background-color: %s;
220+ border: none;
221+ border-right: 1px solid %s;
222+ border-radius: 0;
223+ padding: %dpx %dpx;
224+ min-width: 100px;
225+ max-width: 200px;
226+ transition: background-color %dms ease-in-out;
227+ }
228+
229+ .tab-button:hover {
230+ background-color: %s;
231+ }
232+
233+ .tab-button-active {
234+ background-color: %s;
235+ border: none;
236+ border-right: 1px solid %s;
237+ border-radius: 0;
238+ font-weight: 600;
239+ }
240+
241+ /* Tab title text */
242+ .tab-title {
243+ font-size: %dpx;
244+ color: %s;
245+ font-weight: 500;
246+ }
247+
248+ /* Tab content area */
249+ .tab-content-area {
250+ background-color: %s;
251+ }
252+
253+ /* Tab workspace containers */
254+ .tab-workspace-container {
255+ background-color: %s;
256+ }
257+
258+ /* Pulse animation for tab mode */
259+ @keyframes pulse-border {
260+ 0%%, 100%% { opacity: 1.0; }
261+ 50%% { opacity: 0.6; }
196 }`,262 }`,
197 windowBackgroundColor, // window background263 windowBackgroundColor, // window background
198 paneModeColor, // window.pane-mode-active background (border color)264 paneModeColor, // window.pane-mode-active background (border color)
@@ -216,6 +282,23 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string {
216 titleFontSize, // stacked-pane-title-text font-size282 titleFontSize, // stacked-pane-title-text font-size
217 getStackTitleTextColor(isDark), // stacked-pane-title-text color283 getStackTitleTextColor(isDark), // stacked-pane-title-text color
218 faviconMargin, // stacked-pane-favicon margin-right284 faviconMargin, // stacked-pane-favicon margin-right
285+ // Tab bar styles
286+ windowBackgroundColor, // tab-bar background
287+ getStackTitleBorderColor(isDark), // tab-bar border-top
288+ int(32 * uiScale), // tab-bar min-height
289+ tabModeColor, // window.tab-mode-active .tab-bar border-top-color (orange, distinct from pane mode blue)
290+ getStackTitleBg(isDark), // tab-button background (same as stacked title)
291+ getStackTitleBorderColor(isDark), // tab-button border-right
292+ titlePaddingVertical, // tab-button padding vertical
293+ titlePaddingHorizontal, // tab-button padding horizontal
294+ styling.TransitionDuration, // tab-button transition
295+ getStackTitleHoverBg(isDark), // tab-button:hover background
296+ getStackTitleHoverBg(isDark), // tab-button-active background (lighter/brighter)
297+ getStackTitleBorderColor(isDark), // tab-button-active border-right
298+ titleFontSize, // tab-title font-size
299+ getStackTitleTextColor(isDark), // tab-title color
300+ windowBackgroundColor, // tab-content-area background
301+ windowBackgroundColor, // tab-workspace-container background
219 )302 )
220 303
221 return css304 return css

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