dialogLaunchers.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /**
  2. * Thin launchers for one-off dialog JSX sites in main.tsx.
  3. * Each launcher dynamically imports its component and wires the `done` callback
  4. * identically to the original inline call site. Zero behavior change.
  5. *
  6. * Part of the main.tsx React/JSX extraction effort. See sibling PRs
  7. * perf/extract-interactive-helpers and perf/launch-repl.
  8. */
  9. import React from 'react';
  10. import type { AssistantSession } from './assistant/sessionDiscovery.js';
  11. import type { StatsStore } from './context/stats.js';
  12. import type { Root } from './ink.js';
  13. import { renderAndRun, showSetupDialog } from './interactiveHelpers.js';
  14. import { KeybindingSetup } from './keybindings/KeybindingProviderSetup.js';
  15. import type { AppState } from './state/AppStateStore.js';
  16. import type { AgentMemoryScope } from './tools/AgentTool/agentMemory.js';
  17. import type { TeleportRemoteResponse } from './utils/conversationRecovery.js';
  18. import type { FpsMetrics } from './utils/fpsTracker.js';
  19. import type { ValidationError } from './utils/settings/validation.js';
  20. // Type-only access to ResumeConversation's Props via the module type.
  21. // No runtime cost - erased at compile time.
  22. type ResumeConversationProps = React.ComponentProps<typeof import('./screens/ResumeConversation.js').ResumeConversation>;
  23. /**
  24. * Site ~3173: SnapshotUpdateDialog (agent memory snapshot update prompt).
  25. * Original callback wiring: onComplete={done}, onCancel={() => done('keep')}.
  26. */
  27. export async function launchSnapshotUpdateDialog(root: Root, props: {
  28. agentType: string;
  29. scope: AgentMemoryScope;
  30. snapshotTimestamp: string;
  31. }): Promise<'merge' | 'keep' | 'replace'> {
  32. const {
  33. SnapshotUpdateDialog
  34. } = await import('./components/agents/SnapshotUpdateDialog.js');
  35. return showSetupDialog<'merge' | 'keep' | 'replace'>(root, done => <SnapshotUpdateDialog agentType={props.agentType} scope={props.scope} snapshotTimestamp={props.snapshotTimestamp} onComplete={done} onCancel={() => done('keep')} />);
  36. }
  37. /**
  38. * Site ~3250: InvalidSettingsDialog (settings validation errors).
  39. * Original callback wiring: onContinue={done}, onExit passed through from caller.
  40. */
  41. export async function launchInvalidSettingsDialog(root: Root, props: {
  42. settingsErrors: ValidationError[];
  43. onExit: () => void;
  44. }): Promise<void> {
  45. const {
  46. InvalidSettingsDialog
  47. } = await import('./components/InvalidSettingsDialog.js');
  48. return showSetupDialog(root, done => <InvalidSettingsDialog settingsErrors={props.settingsErrors} onContinue={done} onExit={props.onExit} />);
  49. }
  50. /**
  51. * Site ~4229: AssistantSessionChooser (pick a bridge session to attach to).
  52. * Original callback wiring: onSelect={id => done(id)}, onCancel={() => done(null)}.
  53. */
  54. export async function launchAssistantSessionChooser(root: Root, props: {
  55. sessions: AssistantSession[];
  56. }): Promise<string | null> {
  57. const {
  58. AssistantSessionChooser
  59. } = await import('./assistant/AssistantSessionChooser.js');
  60. return showSetupDialog<string | null>(root, done => <AssistantSessionChooser sessions={props.sessions} onSelect={id => done(id)} onCancel={() => done(null)} />);
  61. }
  62. /**
  63. * `claude assistant` found zero sessions — show the same install wizard
  64. * as `/assistant` when daemon.json is empty. Resolves to the installed dir on
  65. * success, null on cancel. Rejects on install failure so the caller can
  66. * distinguish errors from user cancellation.
  67. */
  68. export async function launchAssistantInstallWizard(root: Root): Promise<string | null> {
  69. const {
  70. NewInstallWizard,
  71. computeDefaultInstallDir
  72. } = await import('./commands/assistant/assistant.js');
  73. const defaultDir = await computeDefaultInstallDir();
  74. let rejectWithError: (reason: Error) => void;
  75. const errorPromise = new Promise<never>((_, reject) => {
  76. rejectWithError = reject;
  77. });
  78. const resultPromise = showSetupDialog<string | null>(root, done => <NewInstallWizard defaultDir={defaultDir} onInstalled={dir => done(dir)} onCancel={() => done(null)} onError={message => rejectWithError(new Error(`Installation failed: ${message}`))} />);
  79. return Promise.race([resultPromise, errorPromise]);
  80. }
  81. /**
  82. * Site ~4549: TeleportResumeWrapper (interactive teleport session picker).
  83. * Original callback wiring: onComplete={done}, onCancel={() => done(null)}, source="cliArg".
  84. */
  85. export async function launchTeleportResumeWrapper(root: Root): Promise<TeleportRemoteResponse | null> {
  86. const {
  87. TeleportResumeWrapper
  88. } = await import('./components/TeleportResumeWrapper.js');
  89. return showSetupDialog<TeleportRemoteResponse | null>(root, done => <TeleportResumeWrapper onComplete={done} onCancel={() => done(null)} source="cliArg" />);
  90. }
  91. /**
  92. * Site ~4597: TeleportRepoMismatchDialog (pick a local checkout of the target repo).
  93. * Original callback wiring: onSelectPath={done}, onCancel={() => done(null)}.
  94. */
  95. export async function launchTeleportRepoMismatchDialog(root: Root, props: {
  96. targetRepo: string;
  97. initialPaths: string[];
  98. }): Promise<string | null> {
  99. const {
  100. TeleportRepoMismatchDialog
  101. } = await import('./components/TeleportRepoMismatchDialog.js');
  102. return showSetupDialog<string | null>(root, done => <TeleportRepoMismatchDialog targetRepo={props.targetRepo} initialPaths={props.initialPaths} onSelectPath={done} onCancel={() => done(null)} />);
  103. }
  104. /**
  105. * Site ~4903: ResumeConversation mount (interactive session picker).
  106. * Uses renderAndRun, NOT showSetupDialog. Wraps in <App><KeybindingSetup>.
  107. * Preserves original Promise.all parallelism between getWorktreePaths and imports.
  108. */
  109. export async function launchResumeChooser(root: Root, appProps: {
  110. getFpsMetrics: () => FpsMetrics | undefined;
  111. stats: StatsStore;
  112. initialState: AppState;
  113. }, worktreePathsPromise: Promise<string[]>, resumeProps: Omit<ResumeConversationProps, 'worktreePaths'>): Promise<void> {
  114. const [worktreePaths, {
  115. ResumeConversation
  116. }, {
  117. App
  118. }] = await Promise.all([worktreePathsPromise, import('./screens/ResumeConversation.js'), import('./components/App.js')]);
  119. await renderAndRun(root, <App getFpsMetrics={appProps.getFpsMetrics} stats={appProps.stats} initialState={appProps.initialState}>
  120. <KeybindingSetup>
  121. <ResumeConversation {...resumeProps} worktreePaths={worktreePaths} />
  122. </KeybindingSetup>
  123. </App>);
  124. }