feat(electrobun): native C resize library (Wails pattern)

New: agor-pty/native/agor_resize.c — C shared library that:
- Connects button-press-event on GtkWindow + WebView children in C
- Stores mouseButton, xroot, yroot, dragTime from real GTK events
- Exports agor_resize_start(edge) for Bun FFI

Build: gcc -shared -fPIC -o libagor-resize.so agor_resize.c $(pkg-config --cflags --libs gtk+-3.0)

New: ui-electrobun/src/bun/native-resize.ts — Bun FFI wrapper
App.svelte: simplified to just edge detection + RPC call
This commit is contained in:
Hibryda 2026-03-25 16:44:51 +01:00
parent de40bcbcac
commit 178e560068
5 changed files with 244 additions and 62 deletions

View file

@ -104,55 +104,14 @@
e.preventDefault();
}
// ── 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<string, string> = {
n: 'n-resize', s: 's-resize', e: 'e-resize', w: 'w-resize',
ne: 'ne-resize', nw: 'nw-resize', se: 'se-resize', sw: 'sw-resize',
};
// ── Window resize — JS edge detection + native C begin_resize_drag ──
// The Wails pattern: JS detects the edge, C library has real GTK event data.
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();
// Tell the native C library to start a resize drag
// It uses stored mouseButton/xroot/yroot from the real GTK button-press event
appRpc.request['window.beginResize']({ edge }).catch(() => {});
}
// ── Window frame persistence (debounced 500ms) ──────────────
@ -253,9 +212,7 @@
setAgentToastFn(showToast);
setupErrorBoundary();
// JS resize needs document-level listeners
document.addEventListener('mousemove', onResizeMove);
document.addEventListener('mouseup', onResizeEnd);
// Resize handled by native C library — no JS mousemove/mouseup needed
// Blink + session timers — MUST be in onMount, NOT $effect
// $effect interacts with reactive graph and causes cycles
@ -351,8 +308,7 @@
clearInterval(sessionId);
document.removeEventListener("keydown", handleSearchShortcut);
window.removeEventListener("palette-command", handlePaletteCommand);
document.removeEventListener('mousemove', onResizeMove);
document.removeEventListener('mouseup', onResizeEnd);
// Native resize — no JS listeners to clean
};
});
</script>