Motion handler crashes in Bun's JSCallback FFI bridge — disabled.
CSS handles provide cursor feedback. GTK button-press-event handles
actual resize via begin_resize_drag with real event timestamp.
Bundler issue resolved: syntax error in /* */ comment was preventing
Electrobun's bundler from compiling gtk-resize.ts.
Fixed malformed block comment that prevented bun bundler from compiling.
Switched from TypedArray.buffer to Buffer.alloc for FFI output pointers.
Disabled motion handler to isolate button-press resize.
NOTE: Electrobun's dev command rebuilds the bundle on every run, overwriting
manual builds. To test changes, run: bun build src/bun/index.ts --outfile
build/.../app/bun/index.js --target=bun --external=electrobun AFTER
electrobun dev finishes building, then restart the app.
All previous approaches failed because they initiated resize from JS
(too late, wrong timestamp, WebKit steals grab). The correct approach
(proven by Tauri/tao) is to connect button-press-event DIRECTLY on the
GtkWindow at the GTK level, BEFORE WebKitGTK processes events.
New: gtk-resize.ts
- JSCallback for button-press-event + motion-notify-event
- Hit-test in 8px border zone (same as tao's 5*scale_factor)
- begin_resize_drag with REAL event timestamp (not GDK_CURRENT_TIME)
- Returns TRUE to STOP propagation (WebKit never sees the press)
- Cursor updates on motion-notify in border zone
Removed: all JS resize handles (divs, mousemove, mouseup, RPC calls)
begin_resize_drag + XUngrabPointer still fails because GTK's layout
cycle re-asserts WebView preferred size, fighting the WM resize.
New approach: JS mousemove → XMoveResizeWindow via libX11.so.6 FFI.
Completely bypasses GTK size negotiation. GTK only receives
ConfigureNotify after the X server has already resized the window.
Added: x11SetFrame() using gdk_x11_display_get_xdisplay +
gdk_x11_window_get_xid + XMoveResizeWindow.
Root cause confirmed via X11 research: WebKitGTK holds an implicit X11
pointer grab from button-press. GTK's gdk_seat_ungrab targets the wrong
device, leaving the grab active. Mutter gets AlreadyGrabbed and gives up.
Fix: call XUngrabPointer(xdisplay, CurrentTime) + XFlush directly via
libX11.so.6 FFI before begin_resize_drag. This releases ALL grabs on
the display connection, matching SDL2's proven approach.
Removed failed approaches: WebView sensitivity toggle, findWebView cache.
Codex review #3 identified: the issue is NOT min-size but pointer grab
conflict. WebKitGTK steals the WM's X11 pointer grab when cursor enters
the WebView during inward resize.
Fix: gtk_widget_set_sensitive(webview, false) before begin_resize_drag,
re-enable after 5s. Also added findWebView() to cache the deepest
GtkBin child pointer for fast access.
Codex review #2 found: set_size_request only controls minimum, not
natural/preferred size. WebView still reports content size as natural,
causing rubber-band effect during shrink.
Fix: reparent WebKitWebView into GtkScrolledWindow via FFI with
propagate-natural-width/height=FALSE and min-content=1x1. This
decouples WebView content size from window size negotiation.
Also reverted to native begin_resize_drag (WM handles everything).
Electrobun's setSize respects WebView min-size constraint. Bypass it
with direct gtk_window_resize() + gtk_window_move() FFI calls.
clearMinSizeTree() runs on every resize frame to suppress WebView
re-propagation. gtkSetFrame() exported as new RPC endpoint.
GTK begin_resize_drag loses grip when cursor moves inward past the 6px
handle zone. Replaced with document-level mousemove/mouseup listeners
that compute delta from initial frame and call setPosition+setSize.
- clearMinSize RPC clears WebView min-size before resize starts
- Cursor locks to resize direction during drag (body.style.cursor)
- user-select disabled during drag to prevent text selection
- Frame captured async before resize starts (no race condition)
WebKitWebView re-propagates content size as minimum on every layout cycle,
overriding the one-time init fix. Now clearMinSizeTree() runs right before
each gtk_window_begin_resize_drag call, allowing resize-in (shrink).
Also removed red debug background from resize handles.
Root cause: WebKitWebView requests min_size = content_size (5120x1387 on
ultrawide), which GTK propagates to WM_NORMAL_HINTS, blocking all resize.
Fix: recursively walk the GTK widget tree (GtkWindow → GtkBin → WebView)
and call gtk_widget_set_size_request(-1, -1) on every widget. Then set
gtk_window_set_geometry_hints with min=400x300.
Result: WM_NORMAL_HINTS now shows min=10x10, window is fully resizable.
- Native dialog: resolve to nearest existing parent dir, detect user cancel
(exit code 1) vs actual error, add createIfMissing option
- Claude models: fallback to KNOWN_CLAUDE_MODELS (6 models) when API key
unavailable. Adds Opus 4.6, Sonnet 4.6, Opus 4.5, Sonnet 4, Haiku 4.5,
Sonnet 3.7. Live API paginated to limit=100.
- PathBrowser: Select button moved to sticky header (always visible).
Current path shown compact in header with RTL ellipsis.
- files.ensureDir RPC: creates directory recursively before project creation
- files.ensureDir added to RPC schema
Root cause: WebKitGTK reports stale modifier state (0x14=Ctrl+Alt) after
SIGTERM of previous instance. Electrobun interprets this as Cmd+click and
opens a new window, which closes the main window.
Finding: when modifier state is clean (0x10, isCtrlHeld=0), the window
opens correctly. The event emitter API isn't publicly exported from
electrobun/bun — needs upstream fix or different approach.
- files.browse: new RPC handler — unguarded directory listing, returns
only directories (no file content access). Used by PathBrowser wizard.
- PathBrowser: uses files.browse instead of files.list (was blocked by
guardPath "access denied: path outside allowed project directories")
- Home dir resolved via files.homeDir RPC (not process.env.HOME)
Note: GTK native dialog title/theme/sort controlled by Electrobun's
native wrapper — canChooseFiles:false should set SELECT_FOLDER action
but may need upstream fix for correct title.
- SidecarManager: spawns claude/codex/ollama runners via Bun.spawn(),
NDJSON stdio protocol, Claude CLI auto-detection, env stripping,
AbortController stop, Deno/Node runtime detection
- MessageAdapter: parses Claude stream-json, Codex ThreadEvent, Ollama
chunks into common AgentMessage format
- agent-store.svelte.ts: per-project reactive session state, RPC event
listeners for agent.message/status/cost
- AgentPane: wired to real sessions (start/stop/prompt), stop button,
thinking/system message rendering
- ProjectCard: status dot from real agent status, cost/tokens from store
- 5 new RPC types (agent.start/stop/prompt/list + events)
All settings wired to SQLite persistence:
- AgentSettings: shell, CWD, permissions, providers (JSON blob)
- SecuritySettings: branch policies (JSON array)
- ProjectSettings: per-project via setProject RPC
- OrchestrationSettings: wake, anchors, notifications
- AdvancedSettings: logging, OTLP, plugins, import/export JSON
Theme Editor:
- 26 color pickers (14 Accents + 12 Neutrals)
- Live CSS var preview as you pick colors
- Save custom theme to SQLite, cancel reverts
- Import/export theme as JSON
- Custom themes in dropdown with delete button
Extensions Marketplace:
- 8-plugin demo catalog (Browse/Installed tabs)
- Search/filter by name or tag
- Install/uninstall with SQLite persistence
- Plugin cards with emoji icons, tags, version
Terminal font hot-swap:
- fontStore.onTermFontChange() → xterm.js options update + fitAddon.fit()
- Resize notification to PTY daemon after font change
All 7 settings categories functional. Every control persists and takes effect.
- Bun process connects to agor-ptyd via PtyClient (5 retries, exponential backoff)
- RPC bridge: 5 request handlers (create/write/resize/unsubscribe/close)
- Daemon output forwarded to WebView as pty.output messages (base64 passthrough)
- Terminal.svelte: real PTY sessions via RPC instead of echo mode
- Shared RPC schema at src/shared/pty-rpc-schema.ts
- Fixed pty-client.ts protocol: base64 string for data (was number array)
- TerminalTabs passes sessionId to Terminal component