Migrate to Electrobun 2.0.2 (Hutch)
Electrobun 2.x replaces the vendored CLI with Hutch: the npm package is now a
bootstrap whose exports are a throwing stub, and the SDK is projected into
.hutch/devkit. The app stays on the Bun main process — `mainProcess: "bun"` is
load-bearing, since the 2.x default (Cottontail) ignores build.bun and with it
the ssh2 externals — and the bundle now ships the pinned Bun 1.4.0.
- electrobun 2.0.2 as an exact devDependency. `electrobun prepare` takes the
old CLI re-sign step's slot in postinstall and every dev/build/start/release
script: tsc (root tsconfig extends the devkit's) and the Angular build
(electrobun/view mapped into the devkit) resolve the SDK there.
- Imports move to electrobun/main. The CLI re-sign script and the v1 "three"
type shim are gone (Hutch's binaries are validly signed).
- UI: Electroview's bunSocket is hostSocket; noImplicitReturns is off because
the devkit's raw-TS browser SDK compiles inside the UI program.
- Updater.applyUpdate() now consults before-quit, so the transfers/unsaved
guard's "Quit Anyway" re-runs the apply instead of quitting without it.
- 2.x applies trafficLightOffset from macOS's default button position (1.18.1
clamped it inside the titlebar); {12, 4} centres the lights in the header.
- Releases: mac-x64 dropped (2.x publishes no macOS x64 core, so no Rosetta
path). artifacts/ is emptied before each build and published verbatim.
Installers lost the stable- prefix (the update feed kept it), so release.ts,
deploy.sh and the site links follow. Linux ships Electrobun's Setup
installer instead of the portable tarball and its miniSCP.sh wrapper.
- Docs cover all of the above, plus re-checks against 2.0.2: still no native
modal windows; window close is now cancellable through will-close.
Verified: tsc, build:ui, a stable mac-arm64 release (dmg + the v1-compatible
stable-macos-arm64 feed) and dev launches (RPC, Settings window, traffic
lights). Windows/Linux builds and the v1 -> 2.x Windows update bridge still
need their first run on native machines.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+26
-14
@@ -6,15 +6,21 @@ Dense reference for development and debugging. Companion files:
|
||||
|
||||
## Process model
|
||||
|
||||
- **Bun main process** (`src/bun/`): electrobun runs it inside a WebWorker of
|
||||
its native launcher. Owns every privileged capability: filesystem, SSH,
|
||||
window management, native menus/dialogs/tray, the connection/settings stores.
|
||||
- **Bun main process** (`src/bun/`): the native launcher runs electrobun's
|
||||
`main.js` on the Bun the 2.x devkit pins (1.4.0, shipped in the bundle —
|
||||
`build.mainProcess: "bun"`), which starts our bundle in a Worker. Imports the
|
||||
SDK as `electrobun/main` (projected into `.hutch/devkit/`). Owns every
|
||||
privileged capability: filesystem, SSH, window management, native
|
||||
menus/dialogs/tray, the connection/settings stores.
|
||||
- **Webviews** (`ui/` Angular): one per window, system WebKit on macOS /
|
||||
WebView2 on Windows / WebKitGTK on Linux. Zero privileged access — everything
|
||||
goes through RPC.
|
||||
- **RPC transport**: one WebSocket server in the bun process, multiplexed by
|
||||
`webviewId` (each webview auto-connects using natively injected globals).
|
||||
Requests are typed by the `AppRPC` schema in `src/shared/rpc.ts`.
|
||||
`webviewId` (each webview auto-connects using natively injected globals;
|
||||
packets AES-GCM-encrypted per webview). 2.x Electroview queues sends while
|
||||
its `hostSocket` connects and falls back to a native bridge when the socket
|
||||
is unavailable. Requests are typed by the `AppRPC` schema in
|
||||
`src/shared/rpc.ts`.
|
||||
|
||||
### The one-schema rule
|
||||
|
||||
@@ -39,7 +45,9 @@ Settings window). `rpc.send.*` on an instance targets exactly its own webview.
|
||||
exist until after construction). `AppDeps.events` is a mutable object of
|
||||
no-op functions, reassigned in `src/bun/index.ts` after everything exists:
|
||||
`sessionClosed`, `connectionsChanged`, `openEditor`, `settingsChanged`,
|
||||
`openConnectionManager`, `openSettings`, `showAbout`.
|
||||
`openConnectionManager`, `openSettings`, `showAbout`, `applyUpdate` (the
|
||||
update restart lives in index.ts beside the before-quit guard it must get past —
|
||||
see build-and-release.md "Auto-updater").
|
||||
|
||||
## Window kinds
|
||||
|
||||
@@ -50,9 +58,9 @@ scale so the first paint is already right. `ui/src/main.ts` picks the root
|
||||
component from `APP_MODE`; all four roots use selector `app-root` (index.html
|
||||
hosts only that tag). `chromeOptions()` is the ONLY place the native frame is
|
||||
decided, spread by the main window and `openSecondaryWindow`: macOS `hiddenInset` +
|
||||
`trafficLightOffset {x:12,y:13}` (the UI renders an `h-10 pl-24` drag-region
|
||||
header via `IS_MAC_CHROME`); other platforms keep the native titlebar and a
|
||||
compact `h-9` non-drag header.
|
||||
`trafficLightOffset {x:12,y:4}` (2.x semantics — see gotchas; the UI renders an
|
||||
`h-10 pl-24` drag-region header via `IS_MAC_CHROME`); other platforms keep the
|
||||
native titlebar and a compact `h-9` non-drag header.
|
||||
|
||||
`WindowRegistry<Rec>` (same file): keyed window map with dedupe/activate,
|
||||
cascade placement off an anchor frame, focus tracking. electrobun's emitter
|
||||
@@ -67,8 +75,9 @@ through a concrete session, and `local` is one shared session anyway).
|
||||
Re-opening focuses the existing window. The webview fetches its own content
|
||||
(`readFile`) — `openEditor` just creates the window with ctx in the preload.
|
||||
|
||||
**Close safety** (electrobun window `close` is NOT cancellable — verified, no
|
||||
`allow` response like `before-quit` has):
|
||||
**Close safety** (designed when electrobun window `close` was NOT cancellable —
|
||||
1.18.1 had no `allow` response like `before-quit`; 2.x adds a cancellable
|
||||
`will-close` for the titlebar close, not used yet — see gotchas):
|
||||
1. ⌘W (routed via the app menu → `editorCloseRequest`) / Close button →
|
||||
styled in-window Save/Don't Save/Cancel; the confirm path calls
|
||||
`editorClose` which sets `suppressPrompt` before `win.close()`.
|
||||
@@ -92,7 +101,9 @@ not `findInPage`, which is a shim on WebView2).
|
||||
### Connection-manager window (`src/bun/manager-window.ts`)
|
||||
|
||||
Single instance, centered over main, **modal to main only** (editors stay
|
||||
usable). No native modality exists, so three layers approximate it: bun
|
||||
usable). No native modality exists (re-checked on 2.0.2: no parent/owner/sheet
|
||||
option or input lock in any SDK or the core's exports; `styleMask.DocModalWindow`
|
||||
is only the cosmetic NSWindow bit), so three layers approximate it: bun
|
||||
bounces main's `focus` event back to the manager; the main webview renders a
|
||||
click-swallowing overlay (`managerState` message); main drops its keyboard
|
||||
shortcuts and bun drops main-targeted menu actions while open. Connect /
|
||||
@@ -208,8 +219,9 @@ component; tokens → `ui/src/styles.css`.
|
||||
through plain callback registries, never signals**: a signal consumed from
|
||||
an effect that also writes signals re-triggers itself (this froze the whole
|
||||
app once), and signal coalescing drops progress snapshots. All requests are
|
||||
gated on the socket-open promise — calls fired during boot would otherwise
|
||||
be silently lost. `request` and `send` are ready-gated Proxies.
|
||||
gated on the socket-open promise (`electroview.hostSocket`) — on 1.18.1 calls
|
||||
fired during boot were silently lost; 2.x Electroview queues them itself, so
|
||||
the gate is belt-and-braces now. `request` and `send` are ready-gated Proxies.
|
||||
- `panels.service.ts` — `PanelState` (per panel): sessionId, fsKey, cwd,
|
||||
entries, sort, cursor, selection, type-to-filter. `sorted` computed:
|
||||
filter → dirs-first sort (name/size/mtime numeric, kind/rights/owner/group
|
||||
|
||||
Reference in New Issue
Block a user