Deletes the vanilla shell and mounts App.tsx on #root. main.ts and the 23 views it drove are gone; App.tsx composes the ported components — sidebar, multi-view, command bar, spawn modal, help and restart overlays, daemon link, drawer — around the catalog and layout stores. The pure parts of four deleted modules moved next to the components that still use them: the spawn-form model and spawnSimilar into components/spawn, the transcript's markdown/format helpers and the diff into components/structured, the help overlay's grouping into components/dialogs. Document-level event names now live in uiEvents.ts. Deduplicated two pairs the migration left side by side: the sidebar uses dialogs/KillDialog and common/ContextMenu. It also listens for the command bar's kill/rename events, which nothing had picked up since catalog.ts went away. styles.css stays as the token and base layer, with `#app` renamed to `#root` and the pane-body rules re-keyed off the classes the React panes actually carry. Closes #253
105 lines
3.7 KiB
TypeScript
105 lines
3.7 KiB
TypeScript
// Pure spawn-form model, kept when spawnForm.ts's imperative DOM form was
|
|
// deleted (#253): the value/seed shapes, the request builder, and the preset
|
|
// fallback. SpawnForm.tsx and useFastForward.ts render from these.
|
|
|
|
import { splitCommand } from "../../toolIcon.js";
|
|
import type { CreateSessionRequest, SpawnPreset } from "../../types.js";
|
|
|
|
export const STRUCTURED_VIEW_TOOLS = new Set(["claude", "codex"]);
|
|
|
|
export function supportsStructuredView(command: string): boolean {
|
|
const tool = splitCommand(command).tool;
|
|
return tool !== null && STRUCTURED_VIEW_TOOLS.has(tool);
|
|
}
|
|
|
|
/** Where a worktree's branch comes from: freshly created (default) or picked
|
|
* from the repository's existing branches (#61). */
|
|
export type WorktreeBranchMode = "new" | "existing";
|
|
|
|
/** Seed values for the spawn form — used by "Spawn similar" to open the modal
|
|
* with the same cwd, command, and name as an existing session, and by the
|
|
* fast-forward fallback to pre-fill whatever it did resolve. */
|
|
export interface SpawnFormSeed {
|
|
command?: string;
|
|
cwd?: string;
|
|
name?: string;
|
|
worktreeEnabled?: boolean;
|
|
worktreeBranch?: string;
|
|
/** Whether the operator typed the branch themselves, rather than it having
|
|
* been auto-synced from the session name. Carried separately because the
|
|
* value alone cannot distinguish the two. */
|
|
worktreeBranchEdited?: boolean;
|
|
worktreeBranchMode?: WorktreeBranchMode;
|
|
cwdRawVisible?: boolean;
|
|
}
|
|
|
|
/** The form's current values, reported on every change. */
|
|
export interface SpawnFormValues {
|
|
command: string;
|
|
cwd: string;
|
|
name: string;
|
|
structuredView: boolean;
|
|
autoApprove: boolean;
|
|
worktreeEnabled: boolean;
|
|
worktreeBranch: string;
|
|
worktreeBranchEdited: boolean;
|
|
/** "new" creates the branch; "existing" checks out a picked one (#61). */
|
|
worktreeBranchMode: WorktreeBranchMode;
|
|
/** Whether the cwd lives in the raw text input (vs a picked recent row). */
|
|
cwdRawVisible?: boolean;
|
|
}
|
|
|
|
export function buildSpawnRequest(
|
|
values: SpawnFormValues,
|
|
baseRepo: string | null,
|
|
): { req: CreateSessionRequest } | { error: string } {
|
|
if (!values.cwd) return { error: "Pick a working directory." };
|
|
if (!values.command) return { error: "Pick a command (or fill the custom command)." };
|
|
|
|
const metadata: Record<string, unknown> = {};
|
|
const name = values.name.trim();
|
|
if (name) metadata.name = name;
|
|
|
|
const req: CreateSessionRequest = {
|
|
cwd: values.cwd,
|
|
command: values.command,
|
|
metadata,
|
|
};
|
|
|
|
if (values.structuredView && supportsStructuredView(values.command)) {
|
|
req.view = "Structured";
|
|
}
|
|
|
|
if (values.autoApprove) {
|
|
req.auto_approve = true;
|
|
}
|
|
|
|
if (values.worktreeEnabled) {
|
|
const mode = values.worktreeBranchMode ?? "new";
|
|
const branch = values.worktreeBranch.trim();
|
|
if (!baseRepo) return { error: "Worktree needs a directory inside a git repository." };
|
|
if (!branch) {
|
|
return { error: mode === "existing" ? "Pick an existing branch." : "Worktree requires a branch name." };
|
|
}
|
|
req.worktree = {
|
|
base_repo: baseRepo,
|
|
branch,
|
|
// Existing-mode picks must check out as-is; the daemon creates
|
|
// anything else with `worktree add -b` (#56/#61).
|
|
create_branch: mode !== "existing",
|
|
};
|
|
}
|
|
|
|
return { req };
|
|
}
|
|
|
|
/** Built-ins used when the daemon's `/api/info` presets can't be fetched
|
|
* (older daemon, transient error). Mirrors `maestro_config::builtin_presets`.
|
|
* Exported for the fast-forward spawn's preset fallback (#67). */
|
|
export const FALLBACK_PRESETS: SpawnPreset[] = [
|
|
{ id: "claude", label: "Claude", command: "claude" },
|
|
{ id: "opencode", label: "opencode", command: "opencode" },
|
|
{ id: "zsh", label: "Shell", command: "zsh" },
|
|
{ id: "custom", label: "Custom…", command: "" },
|
|
];
|