Commit Graph
24 Commits
Author SHA1 Message Date
Miguel Palhas b9829bc56f feat(maestro): project list replaces recents picker in spawn form (#59) 2026-08-23 21:06:10 +01:00
Miguel Palhas 6f8de8de7c fix(maestro): draft-pane regressions from the integration review
Six findings from reviewing the accumulated blitz/spawn-v2 diff:

- loadRecents restored the seeded cwd but never emitted it, so the leaf's
  draft kept cwd:"" and the next render reseeded the form blank.
- stripDrafts did not recurse, so every half-filled draft in a split tab
  was written to localStorage; the old test passed only because hydrate
  discards draft on the way back in.
- showRawCwd and selectPreset grabbed focus from async init paths, so a
  draft pane stole the caret seconds after mounting.
- .mv-draft inherited no layout once the form left .modal-body.
- onSuccess mounted the new session onto a leaf that may have been closed
  while the POST was in flight, stranding it in no pane.
2026-08-23 20:12:28 +01:00
Miguel Palhas cbfd5eee12 refactor(maestro): extract the spawn form so it can live outside the modal 2026-08-23 19:52:18 +01:00
Miguel Palhas 1bb464be4b refactor(maestro): flatten web UI to a single host
Session tree nests by working directory only; the daemon chips in the
header become a session tally, and the spawn form loses its host picker.
API calls move from /api/daemons/:id/* to /api/dirs, /api/recent-dirs,
/api/gitroot, and /api/info.
2026-08-22 10:14:10 +01:00
naps62-yoloandClaude Opus 4.8 7d0dc6597a feat(maestro): move mobile ☰ to the main header
ci / rust (push) Failing after 1m45s
The sidebar-drawer toggle now lives left of the logo in the global header
instead of floating over the content area / tab bar. Removes the in-canvas
fab and tab-bar menu button and their styles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 16:14:28 +00:00
naps62-yoloandClaude Opus 4.8 a1b6c85f8d fix(maestro): reliable touch scrollback on mobile
ci / rust (push) Failing after 1m49s
xterm's native touch scrolling crawled a line at a time and stalled after a
few. Map finger drags on the viewport to term.scrollLines (skipped on the alt
screen, which the app owns) and claim vertical pans via touch-action: pan-y.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:59:49 +00:00
naps62-yoloandClaude Opus 4.8 9286eb09ba feat(maestro): tighten mobile session UX
ci / rust (push) Failing after 1m42s
On phones (single tab, one pane at a time):
- drop the pane border/padding/focus-ring so the session fills the screen
- collapse the redundant tab bar; float a ☰ to keep the sidebar drawer reachable
- disable pull-to-refresh (overscroll-behavior) so terminal scroll wins

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:53:26 +00:00
naps62-yoloandClaude Opus 4.8 57d417f7a0 feat(maestro): abbreviate paths in spawn dialog recent-dir picker
ci / rust (push) Failing after 1m43s
The working-directory picker showed raw absolute paths (`/home/naps62/...`),
unlike the sidebar tree which collapses home to `~` and shrinks leading
segments to initials. Reuse the sidebar's abbreviation so both read the same:
`/home/naps62/git.naps.pt/naps62/maestro` → `~/g/n/maestro`, with the full path
kept in the row tooltip and submitted verbatim.

- types.ts: hoist `abbreviateFolderName` (+ a local `escapeHtml`) here so both
  the sidebar and spawn dialog share one implementation (catalog.ts imports
  spawn.ts, so the helper can't live in catalog without a cycle).
- catalog.ts: import the shared helper instead of its own copy.
- spawn.ts: abbreviate each recent-dir row via `abbreviateFolderName(abbreviateCwd(...))`.
- styles.css: dim the collapsed stem in the picker too (`.recent-dir .folder-stem`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:41:52 +00:00
naps62-yoloandClaude Opus 4.8 b67591695e feat(maestro): per-host session counts + abbreviated folder paths
ci / rust (push) Successful in 4m54s
Sidebar UI tweaks from a live crit review:

- Statusbar: drop the redundant global "N sessions / N live" tally; each
  daemon chip now carries its own green per-host session-count badge
  (tabular-nums, dimmed when 0).
- Session tree: deep folder paths collapse leading segments to initials and
  keep the trailing dir name (~/git.naps.pt/naps62/maestro -> ~/g/n/maestro),
  with the stem dimmed and the full path in the tooltip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 09:56:15 +00:00
naps62-yoloandClaude Opus 4.8 851006cd2c feat(maestro): mobile multi-view — tabs-only projection + sidebar drawer
ci / rust (push) Successful in 4m48s
Below 860px the split tree projects to a one-pane-at-a-time pill strip (no
splits/drag/resize — desktop only); the tree is untouched so widening restores
the splits. Only the visible pane's session stays live; the rest park. The
sidebar becomes an off-canvas drawer toggled by a ☰ in the tab bar, with a
backdrop; tapping a session opens it and closes the drawer. Re-renders on the
breakpoint crossing without losing sessions.

Closes #35. Part of epic #37.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:30:28 +00:00
naps62-yoloandClaude Opus 4.8 159b7ecd6e feat(maestro): multi-view web UI — splits, tabs, drag-dock, highlights
ci / rust (push) Successful in 5m46s
Render the content pane as tabs of nested split trees, each session leaf a live
attach mounted through the multi-mount pool:
- #30 arbitrarily-nested H/V splits with mouse-resizable dividers; per-pane
  ⏻/▥/▤/✕ tools; closing a pane collapses its parent split.
- #31 tabs, each holding its own tree; add / switch / close.
- #32 drag-to-dock: drag a pane title or a sidebar session onto a pane for a
  5-zone dock (edges split, centre swaps/replaces); a sidebar drag moves an
  already-open session.
- #33 focused pane at full strength, others dim; sidebar shows focused (loud)
  vs open-elsewhere (quiet) with a stable per-session colour mapping rows to
  panes.

Opening a session fills the focused pane (single-pane parity by default);
multi-pane is opt-in via split / drag / tabs. Panes auto-close when their
session ends (guarded against eviction and intentional close). The steady
"connected" label is hidden as noise.

Closes #30, #31, #32, #33. Part of epic #37.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:20:57 +00:00
naps62-yoloandClaude Opus 4.8 5602cf5fc8 feat(maestro): navbar debug toggle for the HUD (#20)
ci / rust (push) Failing after 1m48s
Add an always-visible "debug" button to the topbar (next to the design-kit
shortcut) that toggles the debug HUD. Reflects the current state (lit in accent
when on) and stays in sync with the Ctrl+Shift+D keybind via the shared toggle
event.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 15:24:36 +00:00
naps62-yoloandClaude Opus 4.8 f3c4784acc feat(maestro): debug HUD overlay for inferred session metrics (#20)
ci / rust (push) Failing after 1m49s
A game-HUD-style debug overlay (FPS-counter for sessions) that surfaces the
daemon-inferred fields from #18 — activity, foreground, idle time, status — on
the active session, live off the existing events WS. Lets us validate the
inferred data before designing any "real" UI, and stays useful as a permanent
debugging tool.

- Off by default; toggled via Ctrl+Shift+D (persisted in localStorage) or `?hud`
  in dev (debug.ts).
- Fixed bottom-right overlay, pointer-events:none so it never blocks the
  terminal; activity value is color-coded (active/awaiting/idle).
- Generic: renders every non-client-owned Session field, so a new daemon-derived
  field (#19 `detail`, later metrics) surfaces with no overlay change. Idle time
  re-renders on a 1s ticker; values update on SessionUpdated.
- Pure consumer-side: no daemon/protocol/registry changes. The `?mock` fixture
  seeds varied activity so the HUD can be reviewed without a daemon.

Closes #20

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 14:55:41 +00:00
naps62-yoloandClaude Opus 4.8 b3ef2e543e feat(maestro): tile-first web spawn form — host/command tiles, recents, worktree-by-branch, generated name (#27)
ci / rust (push) Successful in 4m29s
Rework web/src/spawn.ts from raw text boxes into a low-friction, tile-first
flow: pick host tile → pick command tile → pick recent dir → Spawn, name
already filled.

- Command tiles from the daemon's advertised presets (GET /api/web/info, #26),
  with built-in fallback; `custom` reveals the free-text command input.
- Host tiles replace the <select> (label hostname, sub daemon_id); most-
  recently-spawned-on host sorts first + pre-selected; overflow behind "More…".
- Recent-dirs picker per host (GET /api/daemons/:id/recent-dirs, #26), rows show
  dir + last command, re-queried on host change; picking a dir can pre-select
  its last command. Persistent "Other directory…" drops to the raw input +
  NAP-329 autocomplete; empty history prefills raw with home_dir.
- Worktree by branch alone: toggle infers base_repo via /gitroot from the
  chosen dir (#26); non-repo dir disables the toggle with an inline hint;
  WorktreeSpec.base_repo filled client-side (wire unchanged).
- Name field prefilled with a generated cyberpunk handle (names.ts mirrors the
  maestro-names wordlists); ↻ rerolls.

Adds api.ts getWebInfo/recentDirs/gitRoot, the WebInfo/SpawnPreset/RecentDirs/
GitRoot types, and stubs the new endpoints in the `?mock&spawn` review fixture.

Closes #27

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:22:25 +00:00
naps62-yoloandClaude Opus 4.8 1006651fb4 fix(maestro): hide mobile key bar when keyboard is dismissed while focused
ci / rust (push) Successful in 5m29s
Focus alone was the wrong signal: dismissing the soft keyboard (e.g. Android
back button) keeps the terminal focused, so the key bar lingered. Detect the
keyboard's actual visibility from the visual-viewport shrink — while an editable
element is focused, a height past a threshold below the focus-free baseline
means the keyboard is up — and gate the bar on a new `html.kb-open` class in
addition to the per-terminal focus class.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:04:15 +00:00
naps62-yoloandClaude Opus 4.8 6b0c92265e fix(maestro): show mobile key bar only while terminal is focused
ci / rust (push) Failing after 3h10m1s
The special-key bar previously showed on every coarse-pointer device, even
when the terminal was unfocused and the soft keyboard was down — which looked
out of place. Gate it on the terminal textarea's focus/blur (the same signal
that drives the keyboard) via a `keybar-active` class, and re-fit on toggle
since showing/hiding the bar changes the terminal's height.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:56:36 +00:00
naps62-yoloandClaude Opus 4.8 d989a40772 fix(maestro): keep mobile key bar above the soft keyboard
ci / rust (push) Successful in 8m46s
On Android the soft keyboard overlaid the layout viewport and the browser
auto-scrolled the document on focus, dragging the shell (and its special-key
bar) to the top of the screen and forcing a manual scroll.

- index.html: interactive-widget=resizes-content so Chrome resizes the layout
  viewport instead of overlaying the keyboard, killing the auto-scroll.
- viewport.ts: mirror visualViewport.offsetTop into --app-top for iOS / older
  browsers that ignore interactive-widget and still scroll under the keyboard.
- styles.css: translateY(--app-top) re-pins #app to the visible region.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:49:46 +00:00
naps62-yoloandClaude Opus 4.8 09093bd2f6 refactor(maestro): refine mobile key bar per review (NAP-342)
ci / rust (push) Successful in 7m17s
ci / rust (pull_request) Successful in 7m14s
Review feedback on the touch key bar:
- Pare to essentials: Esc, Tab, Ctrl, Alt + cursor keys (drop PgUp/PgDn
  and the symbol keys).
- Two compact Termux-style rows in a 5-col grid; cursor keys as a
  real-keyboard inverted-T with Home/End filling the top corners:
      Esc  Tab  Home  ↑   End
      Ctrl Alt   ←    ↓    →
- Add Alt as a second sticky one-shot modifier; Ctrl+Alt compose, and
  both promote cursor keys to the modified CSI form (\x1b[1;<n><final>).
- Explicit press-flash animation: the pointerdown preventDefault (kept so
  the terminal stays focused) suppresses native :active feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:22:17 +00:00
naps62-yoloandClaude Opus 4.8 4b775ec7cb feat(maestro): mobile special-key bar for web attach (NAP-342)
On touch devices the soft keyboard omits the keys terminals depend on
(Esc, Tab, Ctrl, arrows, Home/End, PgUp/PgDn). Add a Termux-style
scrollable key bar to the attach view that injects the right xterm
sequences into the existing PTY input path.

- Ctrl is a one-shot sticky modifier: arm it and the next character —
  tapped here or typed on the soft keyboard — is sent as its control
  code, so Ctrl+C and friends work on mobile.
- Taps preventDefault on pointerdown so the terminal keeps focus and the
  keyboard stays open.
- Bar is revealed only on coarse-pointer devices; a physical keyboard
  already has these keys.
- Shell height now tracks the visual viewport (installViewportSizing) so
  the bar lands above the soft keyboard instead of behind it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 13:37:51 +00:00
naps62-yoloandClaude Opus 4.8 c02ff02849 feat(maestro): terminal-native web UI pass + design kit
ci / rust (push) Successful in 4m26s
Live-review iteration on the browser SPA, driven by crit feedback.

Sidebar / session tree
- Compact single-line leaf rows; status shown as a faint "alive" row wash
  instead of per-row dots (exited rows dim + keep the "exited" badge).
- Host is now the first level of nesting (host → label path → session); the
  inline @daemon is dropped from rows.
- Flush, borderless search input spanning the sidebar; drag-to-resize grip on
  the sidebar edge (width persisted to localStorage); narrower default.
- Per-row clone action (opens the spawn modal prefilled); right-pointing
  chevron when collapsed.

Chrome / layout
- Buttons swapped to a bracket terminal style ([ + Spawn ]); ghost/icon drop
  the brackets. Old filled styles removed.
- Squared corners globally (radius tokens) and tighter, denser spacing.
- Session tally + daemon chips moved into the centered app header.
- Shorter control height; shared --bar-h aligns the search and attach bars.

Attach / terminal
- Useful attach title (name · command @ host) over a bare UUID.
- Robust fit: compute columns from the viewport's scrollbar-excluded
  clientWidth (fixes mobile under-fit and desktop overflow); ResizeObserver +
  font-load refit; full-bleed terminal on mobile.

Spawn / empty states
- Spawn modal accepts a prefill (clone); real "+ Spawn" buttons in welcome and
  no-sessions empty states.

Dev
- New ?kit design-kit page (kit.ts/kit.css, DEV-only) showcasing components.
- Richer ?mock fixture with deeper label nesting.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 12:11:43 +00:00
naps62-yoloandClaude Opus 4.8 4c49abfdf2 feat(maestro): web UI persistent sidebar + content split w/ background connection pool (NAP-338)
ci / rust (push) Successful in 5m24s
Replace the full-screen route-swap SPA with a persistent two-pane layout:

- Global top bar (brand + Spawn + theme) above a sidebar (search + status +
  session tree, highlights the active session) and a content pane (welcome or
  the attached terminal). Selecting a row swaps only the content pane; the
  sidebar and its store subscription persist.
- New `connections.ts` ConnectionPool: attach connections (terminal + WS) are
  decoupled from the UI — navigating away parks a session (keeps it streaming)
  rather than tearing it down, so switching back is instant with scrollback
  intact. LRU-bounded (MAX_LIVE=6); explicit `⏻` disconnect distinct from nav.
- `attach.ts` → mountable `createAttachConnection`; `catalog.ts` exposes
  `renderTopbar` + `renderSidebar`; `main.ts` is the shell + hash router.
- Responsive: desktop side-by-side; mobile single-pane (list ↔ terminal).
  Single session viewed at a time (split panes deferred).

Typecheck + production build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 12:49:16 +00:00
naps62-yolo 3aedd7a0a5 feat(maestro): live directory autocomplete in spawn form (NAP-329)
ci / rust (push) Failing after 5m3s
Adds api.listDir + a reusable dirAutocomplete widget (live dropdown,
Tab/Enter completion, /-drill-in, per-(daemon,dir) cache) wired onto the
cwd and worktree base-repo fields, with matching dropdown styling.
2026-06-15 21:20:36 +00:00
naps62-yoloandClaude Opus 4.8 815ad695df feat(maestro): redesign web UI — token system, dark/light, a11y status
ci / rust (pull_request) Failing after 3m32s
Rewrite styles.css as an OKLCH design-system: cool-slate neutrals with a
single committed signal-green for "alive" (running status, primary action,
focus, selection). Monospace becomes the UI typeface; dark is default with a
full light theme via tokens, OS-following with a persisted header toggle.

- catalog: pulsing wordmark, status-bar tally (N sessions / M live) + daemon
  chips, color-independent session status (pulsing dot for running, hollow
  ring + "exited" badge for dead), reveal-on-hover kill, three teaching
  empty states.
- attach: state pill (live/closed/error), framed dark xterm w/ green cursor.
- spawn: bottom-sheet/dialog form, mono inputs with green focus rings.
- a11y: AA contrast both themes, visible focus, prefers-reduced-motion paths,
  status never color-only.
- theme.ts: dark/light toggle + persistence; mock.ts: DEV-only review fixture
  (?mock / ?mock&spawn / ?theme=), tree-shaken from production builds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 18:40:42 +00:00
naps62-yoloandClaude Opus 4.7 e68c9b6d73 feat(maestro): web client (Vite + TS + xterm.js)
NAP-218. Browser UI for the prototype: catalog tree derived from
`metadata.label`, live updates over `WS /api/events`, search across
session name+label, spawn modal (with optional git worktree), and an
attach view that opens the daemon's per-session WS for xterm.js.

Framework choice: vanilla TypeScript. The interactivity surface is
small enough that pulling in a framework wouldn't pay back; xterm.js
does the heavy lifting. Easy to swap later without protocol impact.

Hash-based routing (`#/`, `#/attach/<id>`) keeps the registry's
static surface dumb — no SPA fallback needed; concurrent attaches
work by opening the same hash URL in another tab.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 09:57:11 +00:00