detached 5675ccb

fix(workspace): load URL in new split panes to enable script execution

New split panes were showing white screens because injected GUI scripts
never executed. Root cause: handlePaneSplit() created new panes via
splitNode() but never called clonePaneState() to load a URL.

Without a document load, WebKit doesn't execute document-start scripts,
so the GUI bootstrap never ran.

Changes:
- workspace_pane_mode.go: Call clonePaneState() after splitNode()
- workspace_pane_ops.go: Load about:blank with diagnostic logging
- gui/src/injected/gui.ts: Always call bootstrapGUI() (remove conditional)
- gui/src/injected/bootstrap.ts: Auto-open omnibox on about:blank pages

New split panes now load about:blank, trigger script execution, and
auto-open the omnibox for immediate navigation.
commit
5675ccb77993949c08e2963c87457fc124adcfd1
author
bamen · · 2025-10-13 17:52 UTC
committer
bamen · · 2025-10-13 17:53 UTC
parent
8b68c9e
signature
unsigned
4 files · +25 -20
M gui/src/injected/bootstrap.ts +8 -0
@@ -262,6 +262,14 @@ export function bootstrapGUI(): void {
262262 try {
263263 await initializeOmnibox();
264264 console.log("✅ Omnibox system initialized successfully");
265+
266+ // Auto-open omnibox on about:blank pages
267+ if (window.location.href === "about:blank" && window.__dumber_omnibox) {
268+ console.log("[about:blank] Auto-opening omnibox");
269+ setTimeout(() => {
270+ window.__dumber_omnibox?.open?.("omnibox", "");
271+ }, 100);
272+ }
265273 } catch (omniboxError) {
266274 console.error("❌ Failed to initialize omnibox system:", omniboxError);
267275 }
@@ -262,6 +262,14 @@ export function bootstrapGUI(): void {
262 try {262 try {
263 await initializeOmnibox();263 await initializeOmnibox();
264 console.log("✅ Omnibox system initialized successfully");264 console.log("✅ Omnibox system initialized successfully");
265+
266+ // Auto-open omnibox on about:blank pages
267+ if (window.location.href === "about:blank" && window.__dumber_omnibox) {
268+ console.log("[about:blank] Auto-opening omnibox");
269+ setTimeout(() => {
270+ window.__dumber_omnibox?.open?.("omnibox", "");
271+ }, 100);
272+ }
265 } catch (omniboxError) {273 } catch (omniboxError) {
266 console.error("❌ Failed to initialize omnibox system:", omniboxError);274 console.error("❌ Failed to initialize omnibox system:", omniboxError);
267 }275 }
M gui/src/injected/gui.ts +5 -16
@@ -3,23 +3,12 @@
33 *
44 * Delegates to the shared bootstrap so special pages can reuse the same setup
55 * without depending on this injected build.
6+ *
7+ * Always calls bootstrapGUI() - deduplication is handled inside bootstrap.ts
8+ * via window.__dumber_gui_ready_for check. This ensures GUI loads in all
9+ * contexts including new split panes and about:blank pages.
610 */
711
812 import { bootstrapGUI } from "./bootstrap";
913
10-const href = window.location.href;
11-const protocol = window.location.protocol;
12-const host = window.location.host || "";
13-
14-const isDumbProtocol = protocol === "dumb:" || href.startsWith("dumb://");
15-const dumbHost = host.toLowerCase();
16-const shouldSkip =
17- isDumbProtocol &&
18- (href.toLowerCase().startsWith("dumb://home") ||
19- href.toLowerCase().startsWith("dumb://homepage") ||
20- dumbHost === "home" ||
21- dumbHost === "homepage");
22-
23-if (!shouldSkip) {
24- bootstrapGUI();
25-}
14+bootstrapGUI();
@@ -3,23 +3,12 @@
3 *3 *
4 * Delegates to the shared bootstrap so special pages can reuse the same setup4 * Delegates to the shared bootstrap so special pages can reuse the same setup
5 * without depending on this injected build.5 * without depending on this injected build.
6+ *
7+ * Always calls bootstrapGUI() - deduplication is handled inside bootstrap.ts
8+ * via window.__dumber_gui_ready_for check. This ensures GUI loads in all
9+ * contexts including new split panes and about:blank pages.
6 */10 */
7 11
8 import { bootstrapGUI } from "./bootstrap";12 import { bootstrapGUI } from "./bootstrap";
9 13
10-const href = window.location.href;14+bootstrapGUI();
11-const protocol = window.location.protocol;
12-const host = window.location.host || "";
13-
14-const isDumbProtocol = protocol === "dumb:" || href.startsWith("dumb://");
15-const dumbHost = host.toLowerCase();
16-const shouldSkip =
17- isDumbProtocol &&
18- (href.toLowerCase().startsWith("dumb://home") ||
19- href.toLowerCase().startsWith("dumb://homepage") ||
20- dumbHost === "home" ||
21- dumbHost === "homepage");
22-
23-if (!shouldSkip) {
24- bootstrapGUI();
25-}
M internal/app/browser/workspace_pane_mode.go +5 -1
@@ -144,11 +144,15 @@ func (wm *WorkspaceManager) handlePaneSplit(node *paneNode, action string) {
144144 wm.dispatchPaneModeEvent("action", action)
145145
146146 // Perform the split (pass nil so it creates a fresh pane, not popup mode)
147- if _, err := wm.splitNode(node, direction, nil); err != nil {
147+ newNode, err := wm.splitNode(node, direction, nil)
148+ if err != nil {
148149 log.Printf("[pane-mode] Failed to split pane: %v", err)
149150 return
150151 }
151152
153+ // Load initial URL in the new pane so scripts execute
154+ wm.clonePaneState(node, newNode)
155+
152156 log.Printf("[pane-mode] Split successful: direction=%s", direction)
153157 }
154158
@@ -144,11 +144,15 @@ func (wm *WorkspaceManager) handlePaneSplit(node *paneNode, action string) {
144 wm.dispatchPaneModeEvent("action", action)144 wm.dispatchPaneModeEvent("action", action)
145 145
146 // Perform the split (pass nil so it creates a fresh pane, not popup mode)146 // Perform the split (pass nil so it creates a fresh pane, not popup mode)
147- if _, err := wm.splitNode(node, direction, nil); err != nil {147+ newNode, err := wm.splitNode(node, direction, nil)
148+ if err != nil {
148 log.Printf("[pane-mode] Failed to split pane: %v", err)149 log.Printf("[pane-mode] Failed to split pane: %v", err)
149 return150 return
150 }151 }
151 152
153+ // Load initial URL in the new pane so scripts execute
154+ wm.clonePaneState(node, newNode)
155+
152 log.Printf("[pane-mode] Split successful: direction=%s", direction)156 log.Printf("[pane-mode] Split successful: direction=%s", direction)
153 }157 }
154 158
M internal/app/browser/workspace_pane_ops.go +7 -3
@@ -114,19 +114,23 @@ func (wm *WorkspaceManager) createPane(view *webkit.WebView) (*BrowserPane, erro
114114 // clonePaneState sets up a new pane with cloned state from another pane
115115 func (wm *WorkspaceManager) clonePaneState(_ *paneNode, target *paneNode) {
116116 if wm == nil || target == nil {
117+ log.Printf("[workspace] clonePaneState: wm or target is nil")
117118 return
118119 }
119120 if target.pane == nil || target.pane.webView == nil {
121+ log.Printf("[workspace] clonePaneState: target pane or webView is nil")
120122 return
121123 }
122124
125+ // Load about:blank and auto-open omnibox
123126 const blankURL = "about:blank"
124127
128+ log.Printf("[workspace] clonePaneState: loading %s in pane %p", blankURL, target.pane)
125129 if err := target.pane.webView.LoadURL(blankURL); err != nil {
126- log.Printf("[workspace] failed to prepare blank pane: %v", err)
130+ log.Printf("[workspace] failed to load blank page in new pane: %v", err)
131+ } else {
132+ log.Printf("[workspace] successfully initiated load of %s", blankURL)
127133 }
128-
129- // Omnibox will auto-open on about:blank via client-side detection
130134 }
131135
132136 // safelyDetachControllersBeforeReparent marks nodes for controller reattachment after reparenting.
@@ -114,19 +114,23 @@ func (wm *WorkspaceManager) createPane(view *webkit.WebView) (*BrowserPane, erro
114 // clonePaneState sets up a new pane with cloned state from another pane114 // clonePaneState sets up a new pane with cloned state from another pane
115 func (wm *WorkspaceManager) clonePaneState(_ *paneNode, target *paneNode) {115 func (wm *WorkspaceManager) clonePaneState(_ *paneNode, target *paneNode) {
116 if wm == nil || target == nil {116 if wm == nil || target == nil {
117+ log.Printf("[workspace] clonePaneState: wm or target is nil")
117 return118 return
118 }119 }
119 if target.pane == nil || target.pane.webView == nil {120 if target.pane == nil || target.pane.webView == nil {
121+ log.Printf("[workspace] clonePaneState: target pane or webView is nil")
120 return122 return
121 }123 }
122 124
125+ // Load about:blank and auto-open omnibox
123 const blankURL = "about:blank"126 const blankURL = "about:blank"
124 127
128+ log.Printf("[workspace] clonePaneState: loading %s in pane %p", blankURL, target.pane)
125 if err := target.pane.webView.LoadURL(blankURL); err != nil {129 if err := target.pane.webView.LoadURL(blankURL); err != nil {
126- log.Printf("[workspace] failed to prepare blank pane: %v", err)130+ log.Printf("[workspace] failed to load blank page in new pane: %v", err)
131+ } else {
132+ log.Printf("[workspace] successfully initiated load of %s", blankURL)
127 }133 }
128-
129- // Omnibox will auto-open on about:blank via client-side detection
130 }134 }
131 135
132 // safelyDetachControllersBeforeReparent marks nodes for controller reattachment after reparenting.136 // safelyDetachControllersBeforeReparent marks nodes for controller reattachment after reparenting.

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