th-a59af5: Big Smooth desktop shell (Electron MVP) - #328
Conversation
🦋 Changeset detectedLatest commit: 4c8d59b The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The browser PWA and the macOS-only menu bar are two half-answers to the same question: where does Big Smooth live when you're not in a terminal. This is the answer — an Electron app that IS the desktop app. The daemon stays the engine. The shell resolves and spawns `smooth-daemon` (same resolution order as `th daemon`, plus a bundled copy for packaged builds), waits on `/health`, and opens a window on the daemon's `/` — which already serves smooth-web with the local auth token injected, so there is no renderer, preload, or IPC code here at all. If a daemon is already answering on the port (`th up`, launchd) it attaches instead, and only ever terminates a child it spawned itself. The tray carries the `th` mark and three things: Open, a Set Up submenu that hands the interactive `th doctor --setup-*` flows a terminal, and Quit. Closing the window hides to the tray; only Quit exits. Packaging and signing are a separate slice — electron-builder.yml is a working skeleton with no certificates wired. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The desktop app now ships the engine rather than borrowing whatever the host has: `pnpm stage-daemon` stages `smooth-daemon` into the bundle, electron-builder packages it (signed .dmg/.zip with the hardened runtime on macOS, NSIS on Windows), and the nested Mach-O gets its own signature so notarization — reusing scripts/macos/notarize-and-staple.sh — has nothing to reject. Verified: the packaged app spawns its bundled daemon, health-checks it, and opens on it. The tray's Set Up items stopped being a Terminal shell-out. Calendar and Reminders drive a new `smooth-daemon tcc <what>` as a one-shot child (the long-running daemon is the wrong process to ask from — it may have been started by launchd, and TCC would attribute the prompt to that); Messages pokes Apple Events; Full Disk Access opens the pane and reveals the app. `SMOOTH_MENUBAR` now decides in both directions. An env var that can only turn a thing ON is a footgun, and the off direction is load-bearing the moment a daemon sits inside an app bundle that owns its own tray. WHAT DOESN'T WORK, measured rather than assumed: the EventKit prompt never appears. `smooth-daemon tcc calendar` spawned from the signed app — every usage string present in its Info.plist — returns not-determined silently, while the identical binary with the identical signature prompts correctly when it is an app bundle's CFBundleExecutable launched via `open`. Being a bundled app's child is enough to USE a grant and not enough to ASK for one. desktop/README.md has the comparison and the two candidate fixes (nested helper .app, or a native module); neither is in this commit, because both change how the daemon is embedded and that's a decision, not a detail. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
eafaa5e to
2721d3f
Compare
Level-up: the Electron app is now the installable — plus one blockerShipped
The blocker: TCC does not flow to a spawned childThis is the thing worth stopping on. Measured, not assumed:
I also tried moving the daemon to Two candidate fixes, neither in this PR because both change how the daemon is embedded:
Full write-up with the comparison table lives in Also verified / not verified
One gotcha found while testing: the web SPA is compiled into the daemon binary, so staging a daemon built without |
`hiddenInset` floats the traffic lights over the page, and smooth-web puts its sidebar toggle in exactly that corner — the two landed on top of each other. Going frameless properly means insetting the SPA's own header, which is smooth-web's business and would move the browser PWA with it. A normal title bar is one deleted line and correct on Windows too. Packaged builds already take the Dock icon from BigSmooth.icns; an unpackaged `pnpm dev` showed the stock Electron atom, which is what made this look broken. Set it explicitly there so dev matches what ships. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Polish pass on the two things Brent hit: Title bar / hamburger overlap — fixed. Going frameless properly would mean insetting smooth-web's own header, which lives in another crate and would move the browser PWA with it. Noted as the upgrade path in a Dock — was already correct in packaged builds and I confirmed rather than assumed: Gates green ( |
Problem
Big Smooth has no desktop app. You reach him through a browser PWA on the daemon's port or through a macOS-only menu-bar extra (
SMOOTH_MENUBAR) — two half-answers to the same question.Solution
A new
desktop/package: an Electron app that IS the Big Smooth desktop app. Electron over Tauri deliberately — one Chromium everywhere beats Tauri's per-OS webview fragmentation.The daemon stays the engine; this is only the shell.
src/daemon.ts— resolve + start + stop the nativesmooth-daemon. Resolution order mirrorsth daemon(crates/smooth-cli/src/daemon_launcher.rs) with the packaged copy first: bundled resources →$SMOOTH_DAEMON_BIN→~/.smooth/bin→PATH→ cargo target dir (viacargo metadata, so a globalbuild.target-dirworks). It probes/healthfirst and attaches to an already-running daemon (th up, launchd) rather than fighting it for the port — and only ever terminates a child it spawned itself.src/main.ts— window + tray. The window is aBrowserWindowon the daemon's/, which already serves smooth-web with the local auth token injected intoindex.html, so there is no renderer, preload, or IPC code in this package. Close hides to tray, Quit exits, single-instance lock, off-origin links open in the real browser.thmark (rendered fromimages/smooth-icon.svg), with Open / Set Up (Calendar · Reminders · Messages · Full Disk Access, each handing the interactiveth doctor --setup-*flow a terminal) / Quit.Cross-platform throughout; the one mac-only branch is the Terminal.app shell-out, which falls back to a dialog elsewhere.
Verification
Smoke-tested on macOS with
pnpm devagainst the daemon already running on :8787:Big Smooth — your always-on AI, signed in, model + status live) — confirmed by screenshot, not just by the process existing.smooth-daemonchild), and quitting the app left that daemon up — the intended asymmetry.pnpm test(2 node:test cases over addr parsing + binary resolution),tsc,oxfmt,oxlintall clean.Not verified by me: the tray menu items under click (Set Up flows, Quit-from-tray) and any non-macOS run.
Deferred
Packaging and signing.
electron-builder.ymlis a working skeleton (mac/win/linux targets,extraResourcespointing at a per-platformresources/<os>/smooth-daemonthat nothing populates yet) — no certificates, notarization, or CI job. Windows/Linux are a config step, not a code change.Pearl th-a59af5.
🤖 Generated with Claude Code