// React port of main.ts's `requestFastForward` (#67): one keystroke, every // spawn default resolved — most recent project (or the one the operator is // on), its last command, a fresh generated branch handle, worktree on with a // new branch. A failed POST, or any missing default, falls back to the same // pre-filled form `onFallback` opens, rather than a dead keystroke. import { useCallback } from "react"; import { createSession, fetchNames, getDaemonInfo, projects as fetchProjects } from "../../api.js"; import { buildFastForwardRequest, fastForwardSeed, resolveFastForward } from "../../fastForward.js"; import { FALLBACK_PRESETS, type SpawnFormSeed } from "../../spawnForm.js"; import type { Project, Session, SpawnPreset } from "../../types.js"; export interface UseFastForwardOptions { /** The project of the session the operator is currently on, if any — its * base repo for a worktree session, else its cwd. Same rule the plain * spawn modal's seed uses, so `N` lands where the operator currently is. */ focusedCwd?: string; onSpawned(session: Session): void; onFallback(seed: SpawnFormSeed): void; } async function resolvePresets(): Promise { try { const info = await getDaemonInfo(); if (info.presets?.length) return info.presets; } catch { // keep fallbacks } return FALLBACK_PRESETS; } /** Returns a function to call on the fast-forward keystroke. */ export function useFastForward(opts: UseFastForwardOptions): () => Promise { const { focusedCwd, onSpawned, onFallback } = opts; return useCallback(async () => { const [discovered, handle] = await Promise.all([ fetchProjects().catch(() => [] as Project[]), fetchNames(1) .then((batch) => batch.names[0]) .catch(() => undefined), ]); const presets = await resolvePresets(); const resolved = resolveFastForward(discovered, presets, handle, focusedCwd); const req = buildFastForwardRequest(resolved); if (req) { try { onSpawned(await createSession(req)); return; } catch { // degrades to the pre-filled form below, never a silent dead key } } onFallback(fastForwardSeed(resolved)); }, [focusedCwd, onSpawned, onFallback]); }