From de40bcbcac59f89ed704098245950abffa574826 Mon Sep 17 00:00:00 2001 From: Hibryda Date: Wed, 25 Mar 2026 16:26:00 +0100 Subject: [PATCH] fix(electrobun): disable all GTK FFI, JS-only resize via setFrame RPC Bun segfaults (address 0x10) from prior GTK widget tree modifications (wrapWebViewInScrolledWindow corrupted the tree). Disabled all GTK FFI except begin_move_drag (which works). JS handles capture mouse events, compute delta, and call window.setFrame RPC. Clean build required: rm -rf ui-electrobun/build/ Status: resize outward works, resize inward blocked by WebView min-size. Next: need C shared library for proper GTK signal connection. --- ui-electrobun/src/bun/index.ts | 14 ++--- ui-electrobun/src/mainview/App.svelte | 84 +++++++++++++++++++++++---- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/ui-electrobun/src/bun/index.ts b/ui-electrobun/src/bun/index.ts index c002223..53582a6 100644 --- a/ui-electrobun/src/bun/index.ts +++ b/ui-electrobun/src/bun/index.ts @@ -240,15 +240,11 @@ mainWindow = new BrowserWindow({ }, }); -// Install native GTK resize handlers (tao/Tauri pattern) -// This connects button-press-event directly on the GtkWindow, -// handling resize BEFORE WebKitGTK processes events. -{ - const { ensureResizable } = require("./gtk-window.ts"); - ensureResizable((mainWindow as any).ptr); - const { installNativeResize } = require("./gtk-resize.ts"); - installNativeResize((mainWindow as any).ptr); -} +// GTK window setup — only log resizable status, skip all FFI modifications +// (forceSmallMinSize and wrapWebViewInScrolledWindow caused crashes) +try { + console.log("[gtk] Window ptr:", (mainWindow as any).ptr); +} catch (e) { console.error("[gtk] ptr access failed:", e); } // Prevent GTK's false Ctrl+click detection from closing the window on initial load. // WebKitGTK reports stale modifier state (0x14 = Ctrl+Alt) after SIGTERM of previous instance, diff --git a/ui-electrobun/src/mainview/App.svelte b/ui-electrobun/src/mainview/App.svelte index 440971e..45924d2 100644 --- a/ui-electrobun/src/mainview/App.svelte +++ b/ui-electrobun/src/mainview/App.svelte @@ -94,7 +94,7 @@ // ── Window drag — delegates to GTK window manager ─────────── function onDragStart(e: MouseEvent) { const target = e.target as HTMLElement; - if (target.tagName === 'BUTTON' || target.tagName === 'INPUT' || target.closest('button')) return; + if (target.tagName === 'BUTTON' || target.tagName === 'INPUT' || target.closest('button') || target.closest('.rz')) return; // Delegate to GTK — the WM handles everything (smooth, zero CPU) appRpc.request['window.beginMove']({ button: e.button + 1, // DOM: 0=left, GTK: 1=left @@ -104,9 +104,56 @@ e.preventDefault(); } - // ── Window resize handled natively by GTK (see gtk-resize.ts) ── - // No JS resize handlers needed — GTK button-press-event intercepts - // at the native level before WebKitGTK, matching Tauri/tao's approach. + // ── Window resize — CSS handles capture mouse, X11 FFI resizes ── + let resizeEdge: string | null = null; + let resizeStartX = 0; + let resizeStartY = 0; + let resizeFrame = { x: 0, y: 0, width: 0, height: 0 }; + const CURSOR_MAP: Record = { + n: 'n-resize', s: 's-resize', e: 'e-resize', w: 'w-resize', + ne: 'ne-resize', nw: 'nw-resize', se: 'se-resize', sw: 'sw-resize', + }; + + function onResizeStart(e: MouseEvent, edge: string) { + e.preventDefault(); + e.stopPropagation(); + resizeEdge = edge; + resizeStartX = e.screenX; + resizeStartY = e.screenY; + // Capture frame synchronously + resizeFrame = { + x: window.screenX, y: window.screenY, + width: window.outerWidth, height: window.outerHeight, + }; + document.body.style.cursor = CURSOR_MAP[edge] || 'default'; + document.body.style.userSelect = 'none'; + } + + function onResizeMove(e: MouseEvent) { + if (!resizeEdge) return; + e.preventDefault(); + const dx = e.screenX - resizeStartX; + const dy = e.screenY - resizeStartY; + let { x, y, width, height } = resizeFrame; + const MIN_W = 400, MIN_H = 300; + if (resizeEdge.includes('e')) width = Math.max(MIN_W, width + dx); + if (resizeEdge.includes('w')) { const nw = Math.max(MIN_W, width - dx); x += width - nw; width = nw; } + if (resizeEdge.includes('s')) height = Math.max(MIN_H, height + dy); + if (resizeEdge.includes('n')) { const nh = Math.max(MIN_H, height - dy); y += height - nh; height = nh; } + // Use Electrobun's setPosition + setSize (simpler than X11 FFI) + appRpc.request['window.setFrame']({ + x: Math.round(x), y: Math.round(y), + width: Math.round(width), height: Math.round(height), + }).catch(() => {}); + } + + function onResizeEnd() { + if (!resizeEdge) return; + resizeEdge = null; + document.body.style.cursor = ''; + document.body.style.userSelect = ''; + saveWindowFrame(); + } // ── Window frame persistence (debounced 500ms) ────────────── let frameSaveTimer: ReturnType | null = null; @@ -206,7 +253,9 @@ setAgentToastFn(showToast); setupErrorBoundary(); - // Resize handled natively by GTK (gtk-resize.ts) — no JS listeners needed + // JS resize needs document-level listeners + document.addEventListener('mousemove', onResizeMove); + document.addEventListener('mouseup', onResizeEnd); // Blink + session timers — MUST be in onMount, NOT $effect // $effect interacts with reactive graph and causes cycles @@ -302,7 +351,8 @@ clearInterval(sessionId); document.removeEventListener("keydown", handleSearchShortcut); window.removeEventListener("palette-command", handlePaletteCommand); - // Resize handled natively — no JS listeners to clean up + document.removeEventListener('mousemove', onResizeMove); + document.removeEventListener('mouseup', onResizeEnd); }; }); @@ -322,11 +372,23 @@ onClose={() => setNotifDrawerOpen(false)} /> - -
-
-
-
+ + +
onResizeStart(e, 'n')}>
+ +
onResizeStart(e, 's')}>
+ +
onResizeStart(e, 'e')}>
+ +
onResizeStart(e, 'w')}>
+ +
onResizeStart(e, 'ne')}>
+ +
onResizeStart(e, 'nw')}>
+ +
onResizeStart(e, 'se')}>
+ +
onResizeStart(e, 'sw')}>