detached 57752f1

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
33
44 import (
55 "fmt"
6+ "log"
67 "strings"
78
89 "github.com/bnema/dumber/internal/config"
@@ -76,9 +77,9 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string {
7677 background-color: %s;
7778 }
7879
79- /* Base pane styling - transparent outline for smooth transitions */
80+ /* Base pane styling - subtle border for inactive panes */
8081 .workspace-pane, .stacked-pane-container {
81- outline: 2px solid transparent;
82+ outline: 2px solid %s;
8283 outline-offset: -2px;
8384 transition: outline-color %dms ease-in-out;
8485 }
@@ -129,6 +130,7 @@ func (wm *WorkspaceManager) generateWorkspaceCSS() string {
129130 /* Collapsed panes are hidden - handled in code via widget visibility */
130131 }`,
131132 windowBackgroundColor, // window background
133+ inactiveBorderColor, // base pane outline color (inactive)
132134 styling.TransitionDuration, // base pane outline transition
133135 activeBorderColor, // workspace-pane-active outline-color
134136 windowBackgroundColor, // stacked-pane-container background
@@ -171,6 +173,8 @@ func (wm *WorkspaceManager) ensureWorkspaceStyles() {
171173 webkit.AddCSSProvider(workspaceCSS)
172174 globalCSSInitialized = true
173175 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))])
174178 }
175179
176180 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 background132 windowBackgroundColor, // window background
133+ inactiveBorderColor, // base pane outline color (inactive)
132 styling.TransitionDuration, // base pane outline transition134 styling.TransitionDuration, // base pane outline transition
133 activeBorderColor, // workspace-pane-active outline-color135 activeBorderColor, // workspace-pane-active outline-color
134 windowBackgroundColor, // stacked-pane-container background136 windowBackgroundColor, // stacked-pane-container background
@@ -171,6 +173,8 @@ func (wm *WorkspaceManager) ensureWorkspaceStyles() {
171 webkit.AddCSSProvider(workspaceCSS)173 webkit.AddCSSProvider(workspaceCSS)
172 globalCSSInitialized = true174 globalCSSInitialized = true
173 globalCSSContent = workspaceCSS175 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 = true180 wm.cssInitialized = true
M internal/app/browser/workspace_utils.go +2 -1
@@ -114,8 +114,9 @@ func (wm *WorkspaceManager) determineBorderContext(node *paneNode) *PaneBorderCo
114114 ctx := &PaneBorderContext{}
115115
116116 // Step 1: Get WebView widget for margin (same for all pane types)
117+ // Use AsWidget() to get the actual WebView widget, not the container
117118 if node.pane != nil && node.pane.webView != nil {
118- ctx.webViewWidget = node.pane.webView.RootWidget()
119+ ctx.webViewWidget = node.pane.webView.AsWidget()
119120 }
120121
121122 // 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 type122 // 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() {
140140 log.Printf("[keyboard-bridge] Failed to dispatch key event: %v", err)
141141 }
142142 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
143164 }
144165
145- // Return false to allow GTK shortcuts to also handle this
166+ // No shortcut matched - allow WebKit to handle normally
146167 return false
147168 })
148169
@@ -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 this166+ // No shortcut matched - allow WebKit to handle normally
146 return false167 return false
147 })168 })
148 169

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