# maestro-web Browser UI for Maestro. Connects to the registry HTTP/WS API for the catalog and to individual daemons (via direct WebSocket) for 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 registry or daemon if needed. ## Layout ``` src/ main.ts # entry point, hash-route dispatch api.ts # registry HTTP API client events.ts # `/api/events` WebSocket subscriber catalog.ts # session tree view (groups by metadata.label) attach.ts # xterm.js + daemon WS attach view spawn.ts # spawn-session modal 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 registry, `?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 registry's 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:8080 pnpm build # → dist/, embedded into the registry binary at cargo build time ``` The Vite dev server proxies `/api` and the events WebSocket to a `maestro-registry`. Default is `http://127.0.0.1:8080`; point it elsewhere by exporting `MAESTRO_REGISTRY` before `pnpm dev`: ```sh MAESTRO_REGISTRY=https://maestro-registry.example.com pnpm dev # Daemon-facing URL also works — `wss://...` is rewritten to `https://...` # and a trailing `/daemon` is stripped. MAESTRO_REGISTRY=wss://maestro-registry.example.com/daemon pnpm dev ``` Direct daemon WS connections (for attach) bypass the proxy and use the URL returned by `GET /api/sessions/:id/connect-info`. ## Production `pnpm build` writes the bundle to `web/dist/`. The Rust registry crate embeds that directory via `rust-embed` at compile time and serves it from `/`. See `crates/maestro-registry/src/web_assets.rs`.