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.
This commit is contained in:
Miguel Palhas
2026-08-22 10:14:10 +01:00
parent 6790f41652
commit 1bb464be4b
14 changed files with 155 additions and 467 deletions
+13 -23
View File
@@ -55,7 +55,6 @@ function folderHeader(name: string, count: number, depth: number): HTMLElement {
function leafRow(opts: {
name: string;
daemon: string;
cmd: string;
status: "running" | "dead";
active?: boolean;
@@ -192,8 +191,7 @@ export function renderKit(): HTMLElement {
{
const { el, body } = section("Chips & badges", "Status-bearing pills and counts.");
body.append(
specimen("daemon-chip", h("span", "daemon-chip", "thinkpad")),
specimen("daemon-empty", h("span", "daemon-empty", "no daemons connected")),
specimen("session-tally", h("span", "session-tally", "6 sessions · 5 live")),
specimen("folder-count", h("span", "folder-count", "4")),
specimen("leaf-badge", h("span", "leaf-badge", "exited")),
specimen(
@@ -217,23 +215,20 @@ export function renderKit(): HTMLElement {
{
const { el, body } = section(
"Session rows — nested by host, then directory",
"Host is the first level of nesting; the working directory (home collapsed to ~) nests beneath it, and single-child chains fold into one node like ~/git/maestro. “Alive” is a faint row wash; exited rows recede with a badge; the open session is highlighted.",
"The working directory (home collapsed to ~) is the nesting, and single-child chains fold into one node like ~/git/maestro. “Alive” is a faint row wash; exited rows recede with a badge; the open session is highlighted.",
);
const list = h("div", "kit-tree");
list.append(
folderHeader("thinkpad", 3, 0),
folderHeader("~/git/maestro", 3, 1),
folderHeader("web", 2, 2),
leafRow({ name: "redesign", daemon: "", cmd: "claude", status: "running", indent: 3 }),
leafRow({ name: "dev-server", daemon: "", cmd: "vite dev", status: "running", indent: 3, active: true }),
folderHeader("crates/registry", 1, 2),
leafRow({ name: "registry-broker", daemon: "", cmd: "claude", status: "running", indent: 3 }),
folderHeader("tower", 2, 0),
folderHeader("~/work/acme", 1, 1),
leafRow({ name: "billing-api", daemon: "", cmd: "claude", status: "running", indent: 2 }),
leafRow({ name: "scratch", daemon: "", cmd: "bash", status: "dead", indent: 1 }),
folderHeader("pi", 1, 0),
leafRow({ name: "monitor", daemon: "", cmd: "btop", status: "running", indent: 1 }),
folderHeader("~/git/maestro", 3, 0),
folderHeader("web", 2, 1),
leafRow({ name: "redesign", cmd: "claude", status: "running", indent: 2 }),
leafRow({ name: "dev-server", cmd: "vite dev", status: "running", indent: 2, active: true }),
folderHeader("crates/daemon", 1, 1),
leafRow({ name: "daemon-work", cmd: "claude", status: "running", indent: 2 }),
folderHeader("~/work/acme", 2, 0),
leafRow({ name: "billing-api", cmd: "claude", status: "running", indent: 1 }),
leafRow({ name: "scratch", cmd: "bash", status: "dead", indent: 1 }),
leafRow({ name: "monitor", cmd: "btop", status: "running", indent: 0 }),
);
body.classList.add("kit-tree-host");
body.appendChild(list);
@@ -258,12 +253,7 @@ export function renderKit(): HTMLElement {
<p class="empty-hint">Start your first session — it'll show up here.</p>
<div class="empty-actions"></div>`;
noSessions.querySelector(".empty-actions")!.appendChild(btn("btn btn-primary", "+ Spawn session"));
const waiting = h("div", "empty connecting");
waiting.innerHTML = `
<div class="empty-glyph" aria-hidden="true">◴</div>
<div class="empty-title">Waiting for a daemon</div>
<p class="empty-hint">Start a <code>maestro daemon</code> on a host and it will appear here.</p>`;
body.append(welcome, noSessions, waiting);
body.append(welcome, noSessions);
main.appendChild(el);
}