Add the view shell and cmux preview open: the UI in real windows without the app
What
This adds cmux preview open and the view shell, Prototypes/ViewShell: cmux NEXT's UI in real, draggable, resizable macOS windows, without the app. It has no terminals, Ghostty, daemons, Cloud or Chief.
Leo (2026-11-04): "a dragabble window that is legit the app through and through without the heavy bits", and "can't you literally just ship like a bare bones version of the app ... you literally just ship the shell".
cmux preview open --size 1512x982 --size 1280x800 --size 900x700 --script streaming
cmux preview open --fixture app.window/new-tab --density compact --capture /tmp/shots --quit
The model is Expo Go: the shell is native and is built rarely, and everything else is web, loaded live.
- The window is an
NSWindowwith the system titlebar, traffic lights and anNSVisualEffectView(.sidebar, behind-window) under a clearWKWebView. It opens one window per--size, offset so all of them show. - The content is the webviews mock app (
gallery/app.html, the web PR) withchrome=native. The page draws no window frame, fills the web view and re-lays its panes out on resize. Each--KEY VALUEbecomes a mock app URL key (--fixture,--script,--density,--theme,--cpu,--slot, and so on), so new conditions never need a native build. - Hot reload. By default the shell loads the gallery dev server on
127.0.0.1:4210.launch.shstarts it when nothing answers and stops only the server it started. Vite HMR and the gallery's per-entry reload update every open window when a web file is saved, with no restart.--dist DIRserves a built gallery through acmux-preview://scheme handler instead, so nothing listens on a port. - Your look. The shell reads
appearance.themefrom~/.config/cmux/cmux-next.json(a light/dark pair takes the system's side) andappearance.accentfrom the same file. You can also point it at another file withCMUX_NEXT_CONFIG_FILE. A flag on the command line wins. - Captures for the UI judge.
--capture DIRwritesDIR/<W>x<H>.pngper window once the page posts ready (webkit.messageHandlers.cmuxPreview).--quitexits after the last capture. Each window printsready_ms=on stdout. - Size. The shell is four Swift files with no dependency on any app package.
swift build -c releasetakes seconds and runs again only when this folder changes. - Live code (Swift hot reload). In Debug the shell is linked
-interposable.inject.sh --live DIR FILE.swiftcompiles the edited file against the build and links it into a patch dylib. The running shell (--live DIR) loads the patch, interposes every Swift function it shares with the executable (dyld_dynamic_interpose) and redraws, with no relaunch. The titlebar badge (Badge.swift) is the demo view: itsbodyis anAnyView, so a patch may change what it builds.--headless --live-render DIRruns the same loop with no screen and writes each generation's picture.scripts/measure/live-code-shell.shmeasures it. - The CLI.
cmux preview openjoinscmux dev ui-hostin one launcher table incli/dev.rs. Unlikedev ui-host, it is not dev-only. The tests cover both.
This depends on the web PR (#35312) for app.html and chrome=native. The shell builds without that PR, but its windows have nothing to load until it lands.
How it differs from the gallery: the gallery shows one entry's states, each alone in a frame in a browser. The shell shows the whole app, one window per state, at real sizes, in real windows.
Not in this PR:
appearance.background(the person's picture behind the glass).- The read-only attach to the person's real acpmux. The mock host is the only model today.
- The hot-swap and first-frame numbers on a real display. The next step is a capture-mini run, with the result posted on this PR.
Testing
-
cargo test -p cmux-tui --lib -- cli::devpassed on the fleet. -
swift build -c release --package-path Prototypes/ViewShellpassed on a fleet Mac in 7.6 s. -
Live code on a fleet Mac (
scripts/measure/live-code-shell.sh, headless), at the latest head:- Debug build: 10.0 s.
- Patch load: 2 ms (4 functions interposed).
- Edit to redrawn pixels: 4.5 s for the first patch and 2.0 s for the next patch of the same file. The second patch draws a different picture from the first.
- A stored-property edit is applied as well, and flagged unsafe because the layout changed. The loader does not refuse it, so telling the user that such a change needs a rebuild is up to the tool that sends the patch.
Observed / Not run
- Observed: generation-1, -2 and -3 pictures written after each patch, with no relaunch. Both launcher tests passed (
forwards_launcher_arguments,only_curated_surface_handles_ui_layer_command), and the release build compiled. - Not run: opening the windows on a display, the capture PNGs,
ready_mson a Mac and a hot-reload save while windows are open. These need a display worker, and I have not run them yet.
No capture: the windows come from the web PR's page. Its numbers are in that PR. A real-window capture follows as a comment once a display worker runs it.
Changelog
none
š¤ Generated with Claude Code
Agent session: agent session