# Maestro web UI Browser UI for Maestro. The daemon embeds this bundle and serves it off its own listener, so the page talks back to the same origin over `/api/*` — 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 spawnForm.ts # shared spawn form (preset tiles, project picker, worktree) spawnModal.ts # dialog chrome around the spawn form 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 the spawn modal, and `?theme=light|dark` to pin a theme. Routing is hash-based: - `#/` → catalog - `#/attach/` → terminal This keeps the static handler dumb (no SPA fallback needed). Concurrent attaches: open the same `#/attach/` 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 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/`. Under its default `web` feature the daemon embeds that directory at compile time and serves it from `/`, alongside the `/api/*` routes. See `crates/maestro-daemon/src/static_bundle.rs`.