fix(browser): fix pane borders visibility and keyboard shortcut conflicts
Fixes two issues in the gotk4 migration:
1. Pane border visibility:
- Changed inactive pane borders from transparent to inactiveBorderColor
- Fixed border application to use AsWidget() instead of RootWidget()
- Added CSS debug logging for troubleshooting
2. Keyboard shortcut handling:
- Only block WebKit defaults for conflicting shortcuts (Alt+Arrow, Ctrl+P)
- Allow WebKit to handle zoom (Ctrl+Plus/Minus/0), reload (Ctrl+R),
devtools (F12) and other native features
- Previously all shortcuts were blocked, breaking zoom functionality
This ensures panes have visible borders showing active/inactive state,
and preserves WebKit's native keyboard features while supporting custom
app shortcuts.- commit
- 57752f14ce41a67d2796181c178374d60ddc6b9f
- author
- bamen · · 2025-10-09 21:19 UTC
- committer
- bamen · · 2025-10-09 17:15 UTC
- parent
- 58f535a
- signature
- unsigned
3 files · +30 -4
M internal/app/browser/workspace_css.go +6 -2
| @@ -3,6 +3,7 @@ package browser | |||
| 3 | 3 | ||
| 4 | 4 | import ( | |
| 5 | 5 | "fmt" | |
| 6 | + | "log" | |
| 6 | 7 | "strings" | |
| 7 | 8 | ||
| 8 | 9 | "github.com/bnema/dumber/internal/config" | |
| @@ -76,9 +77,9 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||
| 76 | 77 | background-color: %s; | |
| 77 | 78 | } | |
| 78 | 79 | ||
| 79 | - | /* Base pane styling - transparent outline for smooth transitions */ | |
| 80 | + | /* Base pane styling - subtle border for inactive panes */ | |
| 80 | 81 | .workspace-pane, .stacked-pane-container { | |
| 81 | - | outline: 2px solid transparent; | |
| 82 | + | outline: 2px solid %s; | |
| 82 | 83 | outline-offset: -2px; | |
| 83 | 84 | transition: outline-color %dms ease-in-out; | |
| 84 | 85 | } | |
| @@ -129,6 +130,7 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||
| 129 | 130 | /* Collapsed panes are hidden - handled in code via widget visibility */ | |
| 130 | 131 | }`, | |
| 131 | 132 | windowBackgroundColor, // window background | |
| 133 | + | inactiveBorderColor, // base pane outline color (inactive) | |
| 132 | 134 | styling.TransitionDuration, // base pane outline transition | |
| 133 | 135 | activeBorderColor, // workspace-pane-active outline-color | |
| 134 | 136 | windowBackgroundColor, // stacked-pane-container background | |
| @@ -171,6 +173,8 @@ func (wm *WorkspaceManager) ensureWorkspaceStyles() { | |||
| 171 | 173 | webkit.AddCSSProvider(workspaceCSS) | |
| 172 | 174 | globalCSSInitialized = true | |
| 173 | 175 | globalCSSContent = workspaceCSS | |
| 176 | + | log.Printf("[workspace] Applied workspace CSS styles (%d bytes)", len(workspaceCSS)) | |
| 177 | + | log.Printf("[workspace] CSS preview: %s...", workspaceCSS[:min(200, len(workspaceCSS))]) | |
| 174 | 178 | } | |
| 175 | 179 | ||
| 176 | 180 | wm.cssInitialized = true | |
| @@ -3,6 +3,7 @@ package browser | |||||
| 3 | 3 | ||||
| 4 | import ( | 4 | import ( | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | + | "log" | |||
| 6 | "strings" | 7 | "strings" | ||
| 7 | 8 | ||||
| 8 | "github.com/bnema/dumber/internal/config" | 9 | "github.com/bnema/dumber/internal/config" | ||
| @@ -76,9 +77,9 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||||
| 76 | background-color: %s; | 77 | background-color: %s; | ||
| 77 | } | 78 | } | ||
| 78 | 79 | ||||
| 79 | - | /* Base pane styling - transparent outline for smooth transitions */ | 80 | + | /* Base pane styling - subtle border for inactive panes */ |
| 80 | .workspace-pane, .stacked-pane-container { | 81 | .workspace-pane, .stacked-pane-container { | ||
| 81 | - | outline: 2px solid transparent; | 82 | + | outline: 2px solid %s; |
| 82 | outline-offset: -2px; | 83 | outline-offset: -2px; | ||
| 83 | transition: outline-color %dms ease-in-out; | 84 | transition: outline-color %dms ease-in-out; | ||
| 84 | } | 85 | } | ||
| @@ -129,6 +130,7 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string { | |||||
| 129 | /* Collapsed panes are hidden - handled in code via widget visibility */ | 130 | /* Collapsed panes are hidden - handled in code via widget visibility */ | ||
| 130 | }`, | 131 | }`, | ||
| 131 | windowBackgroundColor, // window background | 132 | windowBackgroundColor, // window background | ||
| 133 | + | inactiveBorderColor, // base pane outline color (inactive) | |||
| 132 | styling.TransitionDuration, // base pane outline transition | 134 | styling.TransitionDuration, // base pane outline transition | ||
| 133 | activeBorderColor, // workspace-pane-active outline-color | 135 | activeBorderColor, // workspace-pane-active outline-color | ||
| 134 | windowBackgroundColor, // stacked-pane-container background | 136 | windowBackgroundColor, // stacked-pane-container background | ||
| @@ -171,6 +173,8 @@ func (wm *WorkspaceManager) ensureWorkspaceStyles() { | |||||
| 171 | webkit.AddCSSProvider(workspaceCSS) | 173 | webkit.AddCSSProvider(workspaceCSS) | ||
| 172 | globalCSSInitialized = true | 174 | globalCSSInitialized = true | ||
| 173 | globalCSSContent = workspaceCSS | 175 | globalCSSContent = workspaceCSS | ||
| 176 | + | log.Printf("[workspace] Applied workspace CSS styles (%d bytes)", len(workspaceCSS)) | |||
| 177 | + | log.Printf("[workspace] CSS preview: %s...", workspaceCSS[:min(200, len(workspaceCSS))]) | |||
| 174 | } | 178 | } | ||
| 175 | 179 | ||||
| 176 | wm.cssInitialized = true | 180 | wm.cssInitialized = true | ||
M internal/app/browser/workspace_utils.go +2 -1
| @@ -114,8 +114,9 @@ func (wm *WorkspaceManager) determineBorderContext(node *paneNode) *PaneBorderCo | |||
| 114 | 114 | ctx := &PaneBorderContext{} | |
| 115 | 115 | ||
| 116 | 116 | // Step 1: Get WebView widget for margin (same for all pane types) | |
| 117 | + | // Use AsWidget() to get the actual WebView widget, not the container | |
| 117 | 118 | if node.pane != nil && node.pane.webView != nil { | |
| 118 | - | ctx.webViewWidget = node.pane.webView.RootWidget() | |
| 119 | + | ctx.webViewWidget = node.pane.webView.AsWidget() | |
| 119 | 120 | } | |
| 120 | 121 | ||
| 121 | 122 | // Step 2: Determine the border container based on pane type | |
| @@ -114,8 +114,9 @@ func (wm *WorkspaceManager) determineBorderContext(node *paneNode) *PaneBorderCo | |||||
| 114 | ctx := &PaneBorderContext{} | 114 | ctx := &PaneBorderContext{} | ||
| 115 | 115 | ||||
| 116 | // Step 1: Get WebView widget for margin (same for all pane types) | 116 | // Step 1: Get WebView widget for margin (same for all pane types) | ||
| 117 | + | // Use AsWidget() to get the actual WebView widget, not the container | |||
| 117 | if node.pane != nil && node.pane.webView != nil { | 118 | if node.pane != nil && node.pane.webView != nil { | ||
| 118 | - | ctx.webViewWidget = node.pane.webView.RootWidget() | 119 | + | ctx.webViewWidget = node.pane.webView.AsWidget() |
| 119 | } | 120 | } | ||
| 120 | 121 | ||||
| 121 | // Step 2: Determine the border container based on pane type | 122 | // Step 2: Determine the border container based on pane type | ||
M pkg/webkit/keyboard_bridge.go +22 -1
| @@ -140,9 +140,30 @@ func (w *WebView) AttachKeyboardBridge() { | |||
| 140 | 140 | log.Printf("[keyboard-bridge] Failed to dispatch key event: %v", err) | |
| 141 | 141 | } | |
| 142 | 142 | log.Printf("[keyboard-bridge] Forwarded shortcut to JS: %s", shortcut) | |
| 143 | + | ||
| 144 | + | // Only block WebKit's default handling for shortcuts that would conflict: | |
| 145 | + | // - Alt+Arrow: WebKit uses these for back/forward navigation -> BLOCK | |
| 146 | + | // - Ctrl+P: Pane mode, would trigger print dialog -> BLOCK | |
| 147 | + | // - Ctrl+R, Ctrl+Shift+R: Page reload, WebKit should handle -> ALLOW | |
| 148 | + | // - F12: DevTools, WebKit should handle -> ALLOW | |
| 149 | + | // - All others: Custom app handlers, don't conflict with WebKit -> ALLOW | |
| 150 | + | if len(parts) >= 2 { | |
| 151 | + | modifier := parts[0] | |
| 152 | + | if modifier == modifierAlt { | |
| 153 | + | // Alt+Arrow keys - block WebKit's back/forward navigation | |
| 154 | + | return true | |
| 155 | + | } | |
| 156 | + | if modifier == modifierCmdOrCtrl && keyName == keyP { | |
| 157 | + | // Ctrl+P (with or without shift) - block WebKit's print dialog | |
| 158 | + | return true | |
| 159 | + | } | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | // Allow WebKit to also handle this event (for reload, devtools, zoom, etc.) | |
| 163 | + | return false | |
| 143 | 164 | } | |
| 144 | 165 | ||
| 145 | - | // Return false to allow GTK shortcuts to also handle this | |
| 166 | + | // No shortcut matched - allow WebKit to handle normally | |
| 146 | 167 | return false | |
| 147 | 168 | }) | |
| 148 | 169 | ||
| @@ -140,9 +140,30 @@ func (w *WebView) AttachKeyboardBridge() { | |||||
| 140 | log.Printf("[keyboard-bridge] Failed to dispatch key event: %v", err) | 140 | log.Printf("[keyboard-bridge] Failed to dispatch key event: %v", err) | ||
| 141 | } | 141 | } | ||
| 142 | log.Printf("[keyboard-bridge] Forwarded shortcut to JS: %s", shortcut) | 142 | log.Printf("[keyboard-bridge] Forwarded shortcut to JS: %s", shortcut) | ||
| 143 | + | ||||
| 144 | + | // Only block WebKit's default handling for shortcuts that would conflict: | |||
| 145 | + | // - Alt+Arrow: WebKit uses these for back/forward navigation -> BLOCK | |||
| 146 | + | // - Ctrl+P: Pane mode, would trigger print dialog -> BLOCK | |||
| 147 | + | // - Ctrl+R, Ctrl+Shift+R: Page reload, WebKit should handle -> ALLOW | |||
| 148 | + | // - F12: DevTools, WebKit should handle -> ALLOW | |||
| 149 | + | // - All others: Custom app handlers, don't conflict with WebKit -> ALLOW | |||
| 150 | + | if len(parts) >= 2 { | |||
| 151 | + | modifier := parts[0] | |||
| 152 | + | if modifier == modifierAlt { | |||
| 153 | + | // Alt+Arrow keys - block WebKit's back/forward navigation | |||
| 154 | + | return true | |||
| 155 | + | } | |||
| 156 | + | if modifier == modifierCmdOrCtrl && keyName == keyP { | |||
| 157 | + | // Ctrl+P (with or without shift) - block WebKit's print dialog | |||
| 158 | + | return true | |||
| 159 | + | } | |||
| 160 | + | } | |||
| 161 | + | ||||
| 162 | + | // Allow WebKit to also handle this event (for reload, devtools, zoom, etc.) | |||
| 163 | + | return false | |||
| 143 | } | 164 | } | ||
| 144 | 165 | ||||
| 145 | - | // Return false to allow GTK shortcuts to also handle this | 166 | + | // No shortcut matched - allow WebKit to handle normally |
| 146 | return false | 167 | return false | ||
| 147 | }) | 168 | }) | ||
| 148 | 169 | ||||