/** * Thin launchers for one-off dialog JSX sites in main.tsx. * Each launcher dynamically imports its component and wires the `done` callback * identically to the original inline call site. Zero behavior change. * * Part of the main.tsx React/JSX extraction effort. See sibling PRs * perf/extract-interactive-helpers and perf/launch-repl. */ import React from 'react'; import type { AssistantSession } from './assistant/sessionDiscovery.js'; import type { StatsStore } from './context/stats.js'; import type { Root } from '@anthropic/ink'; import { renderAndRun, showSetupDialog } from './interactiveHelpers.js'; import { KeybindingSetup } from './keybindings/KeybindingProviderSetup.js'; import type { AppState } from './state/AppStateStore.js'; import type { AgentMemoryScope } from '@claude-code-best/builtin-tools/tools/AgentTool/agentMemory.js'; import type { TeleportRemoteResponse } from './utils/conversationRecovery.js'; import type { FpsMetrics } from './utils/fpsTracker.js'; import type { ValidationError } from './utils/settings/validation.js'; // Type-only access to ResumeConversation's Props via the module type. // No runtime cost - erased at compile time. type ResumeConversationProps = React.ComponentProps< typeof import('./screens/ResumeConversation.js').ResumeConversation >; /** * Site ~3173: SnapshotUpdateDialog (agent memory snapshot update prompt). * Original callback wiring: onComplete={done}, onCancel={() => done('keep')}. */ export async function launchSnapshotUpdateDialog( root: Root, props: { agentType: string; scope: AgentMemoryScope; snapshotTimestamp: string; }, ): Promise<'merge' | 'keep' | 'replace'> { const { SnapshotUpdateDialog } = await import('./components/agents/SnapshotUpdateDialog.js'); return showSetupDialog<'merge' | 'keep' | 'replace'>(root, done => ( done('keep')} // Esc/cancel → safe default: keep current memory /> )); } /** * Site ~3250: InvalidSettingsDialog (settings validation errors). * Original callback wiring: onContinue={done}, onExit passed through from caller. */ export async function launchInvalidSettingsDialog( root: Root, props: { settingsErrors: ValidationError[]; onExit: () => void; }, ): Promise { const { InvalidSettingsDialog } = await import('./components/InvalidSettingsDialog.js'); return showSetupDialog(root, done => ( )); } /** * Site ~4229: AssistantSessionChooser (pick a bridge session to attach to). * Original callback wiring: onSelect={id => done(id)}, onCancel={() => done(null)}. */ export async function launchAssistantSessionChooser( root: Root, props: { sessions: AssistantSession[] }, ): Promise { const { AssistantSessionChooser } = await import('./assistant/AssistantSessionChooser.js'); return showSetupDialog(root, done => ( done(id)} onCancel={() => done(null)} /> )); } /** * `claude assistant` found zero sessions — show the same install wizard * as `/assistant` when daemon.json is empty. Resolves to the installed dir on * success, null on cancel. Rejects on install failure so the caller can * distinguish errors from user cancellation. */ export async function launchAssistantInstallWizard(root: Root): Promise { const { NewInstallWizard, computeDefaultInstallDir } = await import('./commands/assistant/assistant.js'); const defaultDir = await computeDefaultInstallDir(); let rejectWithError: (reason: Error) => void; const errorPromise = new Promise((_, reject) => { rejectWithError = reject; }); const resultPromise = showSetupDialog(root, done => ( done(dir)} onCancel={() => done(null)} onError={message => rejectWithError(new Error(`Installation failed: ${message}`))} /> )); return Promise.race([resultPromise, errorPromise]); } /** * Site ~4549: TeleportResumeWrapper (interactive teleport session picker). * Original callback wiring: onComplete={done}, onCancel={() => done(null)}, source="cliArg". */ export async function launchTeleportResumeWrapper(root: Root): Promise { const { TeleportResumeWrapper } = await import('./components/TeleportResumeWrapper.js'); return showSetupDialog(root, done => ( done(null)} source="cliArg" /> )); } /** * Site ~4597: TeleportRepoMismatchDialog (pick a local checkout of the target repo). * Original callback wiring: onSelectPath={done}, onCancel={() => done(null)}. */ export async function launchTeleportRepoMismatchDialog( root: Root, props: { targetRepo: string; initialPaths: string[]; }, ): Promise { const { TeleportRepoMismatchDialog } = await import('./components/TeleportRepoMismatchDialog.js'); return showSetupDialog(root, done => ( done(null)} /> )); } /** * Site ~4903: ResumeConversation mount (interactive session picker). * Wraps in and uses renderAndRun. * Preserves original Promise.all parallelism between getWorktreePaths and imports. */ export async function launchResumeChooser( root: Root, appProps: { getFpsMetrics: () => FpsMetrics | undefined; stats: StatsStore; initialState: AppState; }, worktreePathsPromise: Promise, resumeProps: Omit, ): Promise { const [worktreePaths, { ResumeConversation }, { App }] = await Promise.all([ worktreePathsPromise, import('./screens/ResumeConversation.js'), import('./components/App.js'), ]); await renderAndRun( root, , ); }