The draft pane covers every field the modal had; the modal was a second code path for the same job. The header Spawn button, sidebar empty state, welcome button, and ?mock&spawn now open a draft pane via MultiView.openDraft — reusing an empty focused pane, else splitting it. "Spawn similar" seeds a draft with the session's cwd + command. Modal-only CSS (backdrop, sheet/dialog chrome, keyframes) is deleted; .modal-body form styling folds into .spawn-form, which also picks up the select styling the worktree branch picker was borrowing from the modal.
74 lines
2.7 KiB
Markdown
74 lines
2.7 KiB
Markdown
# maestro-web
|
|
|
|
Browser UI for Maestro. Talks to a single local daemon over its `/api/*`
|
|
HTTP/WS surface — session catalog, spawn/kill, and terminal I/O.
|
|
|
|
## Framework choice: vanilla TypeScript
|
|
|
|
No frontend framework. The UI is small enough that the cost of pulling in
|
|
Preact / Solid / Leptos doesn't pay back. xterm.js does all the heavy
|
|
rendering; everything else is a session tree and a spawn form, both trivial
|
|
in plain DOM.
|
|
|
|
This decision is non-architectural — swap in a framework later without
|
|
touching the daemon if needed.
|
|
|
|
## Layout
|
|
|
|
```
|
|
src/
|
|
main.ts # entry point, hash-route dispatch
|
|
api.ts # daemon HTTP API client
|
|
events.ts # `/api/events` WebSocket subscriber
|
|
catalog.ts # session tree view (groups by working directory)
|
|
attach.ts # xterm.js + attach WS view
|
|
multiview.ts # tabs + split panes; draft panes host the spawn form
|
|
spawnForm.ts # shared spawn form (preset tiles, project picker, worktree)
|
|
theme.ts # dark/light theme toggle + persistence
|
|
types.ts # mirror of `maestro-protocol` JSON shapes
|
|
styles.css # design-system tokens + component styles
|
|
mock.ts # DEV-only review fixture (see Design)
|
|
```
|
|
|
|
## Design
|
|
|
|
The visual system (OKLCH token palette, monospace-first typography, dark+light
|
|
theming, motion, components) is documented in [`../DESIGN.md`](../DESIGN.md);
|
|
the tokens live in the `:root` block of `src/styles.css`. Strategic intent
|
|
(register, brand, principles) is in [`../PRODUCT.md`](../PRODUCT.md). Theme
|
|
follows the OS by default and can be pinned via the header toggle (persisted in
|
|
`localStorage`).
|
|
|
|
`mock.ts` is a DEV-only fixture (stripped from production builds): visit the dev
|
|
server with `?mock` to seed a representative catalog without a running daemon,
|
|
`?mock&spawn` to open a draft pane, and `?theme=light|dark` to pin a theme.
|
|
|
|
Routing is hash-based:
|
|
- `#/` → catalog
|
|
- `#/attach/<session-id>` → terminal
|
|
|
|
This keeps the static handler dumb (no SPA fallback needed). Concurrent
|
|
attaches: open the same `#/attach/<id>` URL in another tab.
|
|
|
|
## Dev
|
|
|
|
```sh
|
|
pnpm install
|
|
pnpm dev # http://localhost:5173, proxies /api → http://127.0.0.1:7070
|
|
pnpm build # → dist/, embedded into the maestro-web binary at cargo build time
|
|
```
|
|
|
|
The Vite dev server proxies `/api` and the WebSockets to a local daemon.
|
|
Default is `http://127.0.0.1:7070`; point it elsewhere by exporting
|
|
`MAESTRO_DAEMON_URL` before `pnpm dev`:
|
|
|
|
```sh
|
|
MAESTRO_DAEMON_URL=http://laptop.lan:7070 pnpm dev
|
|
```
|
|
|
|
## Production
|
|
|
|
`pnpm build` writes the bundle to `web/dist/`. The `maestro-web` crate embeds
|
|
that directory at compile time and serves it from `/`, reverse-proxying `/api/*`
|
|
to the daemon. See `crates/maestro-web/src/static_bundle.rs`.
|