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>
This commit is contained in:
2026-06-20 12:11:43 +00:00
co-authored by Claude Opus 4.8
parent eb2162889e
commit c02ff02849
9 changed files with 1130 additions and 164 deletions
+38 -1
View File
@@ -2,7 +2,20 @@ import { createSession } from "./api.js";
import { attachDirAutocomplete } from "./dirAutocomplete.js";
import type { CreateSessionRequest, DaemonInfo } from "./types.js";
export function openSpawnModal(daemons: Map<string, DaemonInfo>): void {
/** Seed values for the spawn form — used by "clone" to reopen the dialog with
* the same daemon, cwd, command, name, and label as an existing session. */
export interface SpawnPrefill {
daemon_id?: string;
cwd?: string;
command?: string;
name?: string;
label?: string;
}
export function openSpawnModal(
daemons: Map<string, DaemonInfo>,
prefill?: SpawnPrefill,
): void {
const existing = document.getElementById("modal-host");
if (existing) existing.remove();
@@ -98,6 +111,30 @@ export function openSpawnModal(daemons: Map<string, DaemonInfo>): void {
select.addEventListener("change", applyHomeDir);
applyHomeDir();
// Clone: seed the form from an existing session. cwd is marked edited so the
// home-dir sync won't clobber it on daemon change.
if (prefill) {
(host.querySelector("#spawn-title") as HTMLElement).textContent =
"Clone session";
const set = (name: string, value: string | undefined) => {
if (value == null) return;
const field = host.querySelector(
`[name=${name}]`,
) as HTMLInputElement | HTMLSelectElement | null;
if (field) field.value = value;
};
if (prefill.daemon_id && daemons.has(prefill.daemon_id)) {
select.value = prefill.daemon_id;
}
set("command", prefill.command);
set("name", prefill.name);
set("label", prefill.label);
if (prefill.cwd) {
cwdInput.value = prefill.cwd;
cwdEdited = true;
}
}
// Live directory autocomplete (NAP-329) on the path fields, scoped to the
// currently-selected daemon — re-reading `select.value` per query means it
// tracks host changes for free.