// React port of ../../spawnModal.ts: dialog chrome around SpawnForm. Escape, // the ✕ and a click on the backdrop all close it; SpawnForm's own DirAutocomplete // swallows Escape first, so dismissing a dropdown never also discards the form. import { useEffect, useRef } from "react"; import { createPortal } from "react-dom"; import type { Session } from "../../types.js"; import { SpawnForm, type SpawnFormProps } from "./SpawnForm.js"; export interface SpawnModalProps { seed?: SpawnFormProps["seed"]; /** Called with the created session after a successful submit — the modal * has already closed by then, so the caller decides where it lands. */ onSuccess(session: Session): void; onClose(): void; } export function SpawnModal({ seed, onSuccess, onClose }: SpawnModalProps) { const filterInputRef = useRef(null); const handleSuccess = (session: Session) => { onClose(); onSuccess(session); }; // The project filter owns the keyboard on open: typing narrows the list and // Tab from here reaches the harness tiles, matching the TUI's draft pane. useEffect(() => { const host = filterInputRef.current; host?.querySelector("input[placeholder='Filter projects…']")?.focus(); }, []); return createPortal(
{ if (e.target === e.currentTarget) onClose(); }} >
{ if (e.key === "Escape" && !e.defaultPrevented) { e.preventDefault(); onClose(); } }} >

{seed ? "Spawn similar" : "Spawn session"}

, document.body, ); }