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}