feat(stacked-panes): add visual enhancements to title bars
- Add subtle 1px border separator between stacked pane titles - Border color is theme-aware (darker in light mode, lighter in dark mode) - Implement UI scaling for all title bar elements (font, padding, height, favicon) - Base scale is 1.0 (100%), configurable via workspace.styling.ui_scale - Scales apply to: font size (12px), padding (4px/8px), height (24px), favicon (16px)
- commit
- 92941be09ef4f888987e385245e732c0dbfa8810
- author
- bnema · · 2025-11-16 22:41 UTC
- committer
- bnema · · 2025-11-16 20:05 UTC
- parent
- 524e65e
- signature
- unsigned
1 file · +29 -8
M internal/app/browser/workspace_css.go +29 -8
| @@ -36,6 +36,14 @@ func getStackTitleTextColor(isDark bool) string { | |||
| 36 | 36 | return "#333333" | |
| 37 | 37 | } | |
| 38 | 38 | ||
| 39 | + | // getStackTitleBorderColor returns a subtle border color slightly darker than the title background | |
| 40 | + | func getStackTitleBorderColor(isDark bool) string { | |
| 41 | + | if isDark { | |
| 42 | + | return "#353535" // Slightly darker than #404040 | |
| 43 | + | } | |
| 44 | + | return "#e5e5e5" // Slightly darker than #f0f0f0 | |
| 45 | + | } | |
| 46 | + | ||
| 39 | 47 | // getActivePaneBorderColor returns the border color for active panes based on config and theme | |
| 40 | 48 | func getActivePaneBorderColor(styling config.WorkspaceStylingConfig, isDark bool) string { | |
| 41 | 49 | // Use configured border color if set | |
| @@ -97,11 +105,19 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||
| 97 | 105 | paneModeColor = "#FFA500" // Fallback to orange if not configured | |
| 98 | 106 | } | |
| 99 | 107 | ||
| 100 | - | // Stacked title border - conditional based on config | |
| 101 | - | stackedTitleBorder := "0" | |
| 102 | - | if styling.ShowStackedTitleBorder { | |
| 103 | - | stackedTitleBorder = fmt.Sprintf("1px solid %s", inactiveBorderColor) | |
| 108 | + | // Stacked title border - subtle separator between titles | |
| 109 | + | stackedTitleBorder := fmt.Sprintf("1px solid %s", getStackTitleBorderColor(isDark)) | |
| 110 | + | ||
| 111 | + | // UI Scale calculations for title bar elements (base values at 1.0 scale) | |
| 112 | + | uiScale := styling.UIScale | |
| 113 | + | if uiScale <= 0 { | |
| 114 | + | uiScale = 1.0 // Fallback to default if invalid | |
| 104 | 115 | } | |
| 116 | + | titleFontSize := int(12 * uiScale) // Base: 12px | |
| 117 | + | titlePaddingVertical := int(4 * uiScale) // Base: 4px | |
| 118 | + | titlePaddingHorizontal := int(8 * uiScale) // Base: 8px | |
| 119 | + | titleMinHeight := int(24 * uiScale) // Base: 24px | |
| 120 | + | faviconMargin := int(4 * uiScale) // Base: 4px | |
| 105 | 121 | ||
| 106 | 122 | css := fmt.Sprintf(`window { | |
| 107 | 123 | background-color: %s; | |
| @@ -152,8 +168,8 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||
| 152 | 168 | .stacked-pane-title { | |
| 153 | 169 | background-color: %s; | |
| 154 | 170 | border-bottom: %s; | |
| 155 | - | padding: 4px 8px; | |
| 156 | - | min-height: 24px; | |
| 171 | + | padding: %dpx %dpx; | |
| 172 | + | min-height: %dpx; | |
| 157 | 173 | transition: background-color %dms ease-in-out; | |
| 158 | 174 | } | |
| 159 | 175 | ||
| @@ -162,13 +178,13 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||
| 162 | 178 | } | |
| 163 | 179 | ||
| 164 | 180 | .stacked-pane-title-text { | |
| 165 | - | font-size: 12px; | |
| 181 | + | font-size: %dpx; | |
| 166 | 182 | color: %s; | |
| 167 | 183 | font-weight: 500; | |
| 168 | 184 | } | |
| 169 | 185 | ||
| 170 | 186 | .stacked-pane-favicon { | |
| 171 | - | margin-right: 4px; | |
| 187 | + | margin-right: %dpx; | |
| 172 | 188 | } | |
| 173 | 189 | ||
| 174 | 190 | .stacked-pane-active { | |
| @@ -192,9 +208,14 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||
| 192 | 208 | inactiveBorderColor, // stacked-pane-container.active border-color | |
| 193 | 209 | getStackTitleBg(isDark), // stacked-pane-title background | |
| 194 | 210 | stackedTitleBorder, // stacked-pane-title border-bottom | |
| 211 | + | titlePaddingVertical, // stacked-pane-title padding vertical | |
| 212 | + | titlePaddingHorizontal, // stacked-pane-title padding horizontal | |
| 213 | + | titleMinHeight, // stacked-pane-title min-height | |
| 195 | 214 | styling.TransitionDuration, // stacked-pane-title transition | |
| 196 | 215 | getStackTitleHoverBg(isDark), // stacked-pane-title:hover background | |
| 216 | + | titleFontSize, // stacked-pane-title-text font-size | |
| 197 | 217 | getStackTitleTextColor(isDark), // stacked-pane-title-text color | |
| 218 | + | faviconMargin, // stacked-pane-favicon margin-right | |
| 198 | 219 | ) | |
| 199 | 220 | ||
| 200 | 221 | return css | |
| @@ -36,6 +36,14 @@ func getStackTitleTextColor(isDark bool) string { | |||||
| 36 | return "#333333" | 36 | return "#333333" | ||
| 37 | } | 37 | } | ||
| 38 | 38 | ||||
| 39 | + | // getStackTitleBorderColor returns a subtle border color slightly darker than the title background | |||
| 40 | + | func getStackTitleBorderColor(isDark bool) string { | |||
| 41 | + | if isDark { | |||
| 42 | + | return "#353535" // Slightly darker than #404040 | |||
| 43 | + | } | |||
| 44 | + | return "#e5e5e5" // Slightly darker than #f0f0f0 | |||
| 45 | + | } | |||
| 46 | + | ||||
| 39 | // getActivePaneBorderColor returns the border color for active panes based on config and theme | 47 | // getActivePaneBorderColor returns the border color for active panes based on config and theme | ||
| 40 | func getActivePaneBorderColor(styling config.WorkspaceStylingConfig, isDark bool) string { | 48 | func getActivePaneBorderColor(styling config.WorkspaceStylingConfig, isDark bool) string { | ||
| 41 | // Use configured border color if set | 49 | // Use configured border color if set | ||
| @@ -97,11 +105,19 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||||
| 97 | paneModeColor = "#FFA500" // Fallback to orange if not configured | 105 | paneModeColor = "#FFA500" // Fallback to orange if not configured | ||
| 98 | } | 106 | } | ||
| 99 | 107 | ||||
| 100 | - | // Stacked title border - conditional based on config | 108 | + | // Stacked title border - subtle separator between titles |
| 101 | - | stackedTitleBorder := "0" | 109 | + | stackedTitleBorder := fmt.Sprintf("1px solid %s", getStackTitleBorderColor(isDark)) |
| 102 | - | if styling.ShowStackedTitleBorder { | 110 | + | |
| 103 | - | stackedTitleBorder = fmt.Sprintf("1px solid %s", inactiveBorderColor) | 111 | + | // UI Scale calculations for title bar elements (base values at 1.0 scale) |
| 112 | + | uiScale := styling.UIScale | |||
| 113 | + | if uiScale <= 0 { | |||
| 114 | + | uiScale = 1.0 // Fallback to default if invalid | |||
| 104 | } | 115 | } | ||
| 116 | + | titleFontSize := int(12 * uiScale) // Base: 12px | |||
| 117 | + | titlePaddingVertical := int(4 * uiScale) // Base: 4px | |||
| 118 | + | titlePaddingHorizontal := int(8 * uiScale) // Base: 8px | |||
| 119 | + | titleMinHeight := int(24 * uiScale) // Base: 24px | |||
| 120 | + | faviconMargin := int(4 * uiScale) // Base: 4px | |||
| 105 | 121 | ||||
| 106 | css := fmt.Sprintf(`window { | 122 | css := fmt.Sprintf(`window { | ||
| 107 | background-color: %s; | 123 | background-color: %s; | ||
| @@ -152,8 +168,8 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||||
| 152 | .stacked-pane-title { | 168 | .stacked-pane-title { | ||
| 153 | background-color: %s; | 169 | background-color: %s; | ||
| 154 | border-bottom: %s; | 170 | border-bottom: %s; | ||
| 155 | - | padding: 4px 8px; | 171 | + | padding: %dpx %dpx; |
| 156 | - | min-height: 24px; | 172 | + | min-height: %dpx; |
| 157 | transition: background-color %dms ease-in-out; | 173 | transition: background-color %dms ease-in-out; | ||
| 158 | } | 174 | } | ||
| 159 | 175 | ||||
| @@ -162,13 +178,13 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||||
| 162 | } | 178 | } | ||
| 163 | 179 | ||||
| 164 | .stacked-pane-title-text { | 180 | .stacked-pane-title-text { | ||
| 165 | - | font-size: 12px; | 181 | + | font-size: %dpx; |
| 166 | color: %s; | 182 | color: %s; | ||
| 167 | font-weight: 500; | 183 | font-weight: 500; | ||
| 168 | } | 184 | } | ||
| 169 | 185 | ||||
| 170 | .stacked-pane-favicon { | 186 | .stacked-pane-favicon { | ||
| 171 | - | margin-right: 4px; | 187 | + | margin-right: %dpx; |
| 172 | } | 188 | } | ||
| 173 | 189 | ||||
| 174 | .stacked-pane-active { | 190 | .stacked-pane-active { | ||
| @@ -192,9 +208,14 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||||
| 192 | inactiveBorderColor, // stacked-pane-container.active border-color | 208 | inactiveBorderColor, // stacked-pane-container.active border-color | ||
| 193 | getStackTitleBg(isDark), // stacked-pane-title background | 209 | getStackTitleBg(isDark), // stacked-pane-title background | ||
| 194 | stackedTitleBorder, // stacked-pane-title border-bottom | 210 | stackedTitleBorder, // stacked-pane-title border-bottom | ||
| 211 | + | titlePaddingVertical, // stacked-pane-title padding vertical | |||
| 212 | + | titlePaddingHorizontal, // stacked-pane-title padding horizontal | |||
| 213 | + | titleMinHeight, // stacked-pane-title min-height | |||
| 195 | styling.TransitionDuration, // stacked-pane-title transition | 214 | styling.TransitionDuration, // stacked-pane-title transition | ||
| 196 | getStackTitleHoverBg(isDark), // stacked-pane-title:hover background | 215 | getStackTitleHoverBg(isDark), // stacked-pane-title:hover background | ||
| 216 | + | titleFontSize, // stacked-pane-title-text font-size | |||
| 197 | getStackTitleTextColor(isDark), // stacked-pane-title-text color | 217 | getStackTitleTextColor(isDark), // stacked-pane-title-text color | ||
| 218 | + | faviconMargin, // stacked-pane-favicon margin-right | |||
| 198 | ) | 219 | ) | ||
| 199 | 220 | ||||
| 200 | return css | 221 | return css | ||