feat(maestro): tile-first web spawn form — host/command tiles, recents, worktree-by-branch, generated name (#27)
ci / rust (push) Successful in 4m29s
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>
This commit is contained in:
+18
-11
@@ -273,7 +273,8 @@ export function renderTopbar(store: CatalogStore): HTMLElement {
|
||||
spawnBtn.className = "btn btn-primary spawn-btn";
|
||||
spawnBtn.textContent = "+ Spawn";
|
||||
spawnBtn.addEventListener("click", () => {
|
||||
openSpawnModal(store.snapshot().daemons);
|
||||
const snap = store.snapshot();
|
||||
openSpawnModal(snap.daemons, undefined, snap.sessions);
|
||||
});
|
||||
actions.appendChild(spawnBtn);
|
||||
return header;
|
||||
@@ -387,9 +388,10 @@ export function renderSidebar(store: CatalogStore): SidebarHandle {
|
||||
treeHost.replaceChildren();
|
||||
if (sessions.length === 0) {
|
||||
treeHost.appendChild(
|
||||
renderEmpty(state.daemons.size === 0, filter.trim().length > 0, () =>
|
||||
openSpawnModal(store.snapshot().daemons),
|
||||
),
|
||||
renderEmpty(state.daemons.size === 0, filter.trim().length > 0, () => {
|
||||
const snap = store.snapshot();
|
||||
openSpawnModal(snap.daemons, undefined, snap.sessions);
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -406,13 +408,18 @@ export function renderSidebar(store: CatalogStore): SidebarHandle {
|
||||
const cloneHandler = (ev: Event) => {
|
||||
const s = (ev as CustomEvent<Session>).detail;
|
||||
const rawName = typeof s.metadata?.name === "string" ? s.metadata.name : "";
|
||||
openSpawnModal(store.snapshot().daemons, {
|
||||
daemon_id: s.daemon_id,
|
||||
cwd: s.cwd,
|
||||
command: s.command,
|
||||
name: rawName,
|
||||
label: sessionLabel(s) ?? "",
|
||||
});
|
||||
const snap = store.snapshot();
|
||||
openSpawnModal(
|
||||
snap.daemons,
|
||||
{
|
||||
daemon_id: s.daemon_id,
|
||||
cwd: s.cwd,
|
||||
command: s.command,
|
||||
name: rawName,
|
||||
label: sessionLabel(s) ?? "",
|
||||
},
|
||||
snap.sessions,
|
||||
);
|
||||
};
|
||||
document.addEventListener("catalog-clone", cloneHandler);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user