This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
maestro/web
Miguel PalhasandClaude Opus 5 59ede15d2b feat(maestro): open the bar from the sidebar search field
There is no ctrl-k on a phone and the web UI is mobile-first, so the field
the operator already reaches for becomes the touch entry point: focusing or
tapping it dispatches OPEN_COMMAND_BAR_EVENT and the shell opens the bar with
an empty query. The field is readonly and blurs itself first, so no keyboard
rises over it and the bar can be closed without reopening it.

This replaces the inline substring filter — one search affordance in the
header, not two. buildTree loses its filter argument, and the tree's "No
matches" empty state goes with it; the bar's own no-results state took over.
The filter's name-and-directory matching is now covered against the bar.

The bar's phone-width sheet moves to the spawn modal's 599px break, with
thumb-sized rows and a field that clears the zoom-on-focus threshold.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 18:21:31 +01:00
..

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; the tokens live in the :root block of src/styles.css. Strategic intent (register, brand, principles) is in ../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/<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

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:

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.