/** * 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, , ) }