// Dev-only design kit. Lazily imported behind `import.meta.env.DEV` from // main.ts (`?kit`), so it never ships in a production bundle. Renders every // component specimen on one scrollable page so the visual system can be // critiqued as a whole — buttons (including terminal-native variants for // review), inputs, chips, badges, session rows, and empty states. import "./kit.css"; import { createThemeToggle } from "./theme.js"; function h(tag: string, className?: string, html?: string): HTMLElement { const el = document.createElement(tag); if (className) el.className = className; if (html != null) el.innerHTML = html; return el; } /** A titled section with an optional note and a content host. */ function section(title: string, note?: string): { el: HTMLElement; body: HTMLElement } { const el = h("section", "kit-section"); const head = h("div", "kit-section-head"); head.appendChild(h("h2", "kit-section-title", title)); if (note) head.appendChild(h("p", "kit-section-note", note)); el.appendChild(head); const body = h("div", "kit-row"); el.appendChild(body); return { el, body }; } /** A labelled specimen cell so each variant is named for critique. */ function specimen(label: string, node: Node): HTMLElement { const cell = h("div", "kit-cell"); const slot = h("div", "kit-slot"); slot.appendChild(node); cell.append(slot, h("span", "kit-label", label)); return cell; } function btn(className: string, label: string, disabled = false): HTMLButtonElement { const b = document.createElement("button"); b.type = "button"; b.className = className; b.textContent = label; b.disabled = disabled; return b; } /** A collapsible folder header, matching the real sidebar tree markup. */ function folderHeader(name: string, count: number, depth: number): HTMLElement { const b = h("button", "tree-folder-header"); (b as HTMLButtonElement).type = "button"; b.style.paddingLeft = `${depth * 16}px`; b.setAttribute("aria-expanded", "true"); b.innerHTML = `▾${name}${count}`; return b; } function leafRow(opts: { name: string; cmd: string; status: "running" | "dead"; active?: boolean; indent?: number; }): HTMLElement { const row = h("a", "tree-leaf"); (row as HTMLAnchorElement).href = "#/kit"; row.dataset.status = opts.status; if (opts.active) row.classList.add("is-active"); row.style.paddingLeft = `${(opts.indent ?? 0) * 16 + 8}px`; row.innerHTML = ` ${opts.name} ${opts.cmd !== opts.name ? `${opts.cmd}` : ""} ${opts.status === "dead" ? `exited` : ""} `; return row; } export function renderKit(): HTMLElement { const page = h("div", "kit"); // --- header --- const header = h("header", "kit-header"); header.appendChild( h( "div", "kit-brand", `

maestro · design kit

`, ), ); const headerActions = h("div", "kit-header-actions"); const back = document.createElement("a"); back.className = "btn btn-ghost"; back.href = "?mock"; back.textContent = "← back to app"; headerActions.append(back, createThemeToggle()); header.appendChild(headerActions); page.appendChild(header); const main = h("div", "kit-main"); page.appendChild(main); // --- color tokens --- { const { el, body } = section( "Color tokens", "Slate neutrals (hue ~256) + signal-green = alive. Swap by theme toggle.", ); body.classList.add("kit-swatches"); const tokens = [ "--bg", "--surface", "--surface-2", "--surface-3", "--border", "--ink", "--muted", "--faint", "--accent", "--danger", "--warn", ]; for (const t of tokens) { const sw = h("div", "kit-swatch"); const chip = h("div", "kit-swatch-chip"); chip.style.background = `var(${t})`; sw.append(chip, h("span", "kit-label", t)); body.appendChild(sw); } main.appendChild(el); } // --- buttons: current system --- { const { el, body } = section( "Buttons — current", "The shipping button styles — now the bracket terminal style, as used across the header, panes, and empty states.", ); body.append( specimen("btn-primary", btn("btn btn-primary", "+ Spawn")), specimen("btn (default)", btn("btn", "Cancel")), specimen("btn-ghost", btn("btn btn-ghost", "Dismiss")), specimen("btn-icon", btn("btn btn-icon", "✕")), specimen("btn:disabled", btn("btn btn-primary", "+ Spawn", true)), ); main.appendChild(el); } // --- buttons: terminal-native variants for critique --- { const { el, body } = section( "Buttons — other explorations", "The bracket variant was adopted as the app style (above). These remain as alternates — not used in the app.", ); body.append( specimen("bracket", btn("kit-btn-bracket", "+ Spawn")), specimen("bracket primary", btn("kit-btn-bracket is-primary", "+ Spawn")), specimen("prompt caret", btn("kit-btn-prompt", "spawn")), specimen("block", btn("kit-btn-block", "SPAWN")), specimen("underline", btn("kit-btn-underline", "spawn")), ); main.appendChild(el); } // --- inputs --- { const { el, body } = section("Inputs", "Mono inputs with a green focus ring."); const search = h("input") as HTMLInputElement; search.type = "search"; search.className = "search"; search.placeholder = "Search sessions…"; const text = h("div", "spawn-form"); const ti = document.createElement("input"); ti.type = "text"; ti.placeholder = "branch-name"; text.appendChild(ti); const sel = h("div", "spawn-form"); const s = document.createElement("select"); s.innerHTML = ``; sel.appendChild(s); body.append( specimen("search", search), specimen("text input", text), specimen("select", sel), ); main.appendChild(el); } // --- chips & badges --- { const { el, body } = section("Chips & badges", "Status-bearing pills and counts."); body.append( 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( "attach-state · open", h("span", "attach-state", "live"), ), ); (body.lastElementChild?.querySelector(".attach-state") as HTMLElement)?.setAttribute( "data-state", "open", ); const connecting = specimen("attach-state · connecting", h("span", "attach-state", "connecting")); (connecting.querySelector(".attach-state") as HTMLElement).setAttribute("data-state", "connecting"); const closed = specimen("attach-state · closed", h("span", "attach-state", "closed")); (closed.querySelector(".attach-state") as HTMLElement).setAttribute("data-state", "closed"); body.append(connecting, closed); main.appendChild(el); } // --- session rows, nested by working directory --- { const { el, body } = section( "Session rows — nested by host, then directory", "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("~/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); main.appendChild(el); } // --- empty states --- { const { el, body } = section("Empty states", "Each one teaches the next action."); body.classList.add("kit-empties"); const welcome = h("div", "empty"); welcome.innerHTML = `
No session open

Pick a session from the list, or start a new one.

`; welcome.querySelector(".empty-actions")!.appendChild(btn("btn btn-primary", "+ Spawn session")); const noSessions = h("div", "empty"); noSessions.innerHTML = `
No sessions yet

Start your first session — it'll show up here.

`; noSessions.querySelector(".empty-actions")!.appendChild(btn("btn btn-primary", "+ Spawn session")); body.append(welcome, noSessions); main.appendChild(el); } return page; }