diff --git a/__tests__/ui/screens/InstallPluginsScreen.test.tsx b/__tests__/ui/screens/InstallPluginsScreen.test.tsx index b7b91dd..9a5f5cd 100644 --- a/__tests__/ui/screens/InstallPluginsScreen.test.tsx +++ b/__tests__/ui/screens/InstallPluginsScreen.test.tsx @@ -96,8 +96,8 @@ describe('InstallPluginsScreen', () => { await waitFor(() => { const frame = sut.lastFrame()!; - const codexPos = frame.indexOf('Codex'); - const cursorPos = frame.indexOf('Cursor'); + const codexPos = frame.lastIndexOf('Codex'); + const cursorPos = frame.lastIndexOf('Cursor'); expect(codexPos).toBeGreaterThan(-1); expect(cursorPos).toBeGreaterThan(-1); expect(codexPos).toBeLessThan(cursorPos); diff --git a/src/ui/tui/screens/install-plugins/InstallPluginsScreen.tsx b/src/ui/tui/screens/install-plugins/InstallPluginsScreen.tsx index 6356bdb..ba65b77 100644 --- a/src/ui/tui/screens/install-plugins/InstallPluginsScreen.tsx +++ b/src/ui/tui/screens/install-plugins/InstallPluginsScreen.tsx @@ -1,5 +1,7 @@ -import { getIntegrations } from '@integrations/index.js'; import { ScreenId } from '@lib/session.js'; +import { track } from '@lib/telemetry.js'; +import type { IdeId } from '@shared-kernel/types.js'; + import { MainLayout } from '../../components/MainLayout.js'; import { TaskList } from '../../components/TaskList.js'; import { useAutoAdvance } from '../../hooks/useAutoAdvance.js'; @@ -8,18 +10,15 @@ import { useNavigation } from '../../hooks/useNavigation.js'; import { buildWizardTasks } from '../../lib/wizard-tasks.js'; import { $session, store } from '../../store.js'; import { usePluginInstall } from './usePluginInstall.js'; -import { track } from '@lib/telemetry.js'; import { pluginsAlreadyInstalled, pluginInstalled, pluginExitedAfterError, } from './log-messages.js'; import * as te from './telemetry-events.js'; -import { type IdeSelectValue, type DetectedSelectValue, type ErrorAction } from './actions.js'; +import { type ErrorAction } from './actions.js'; import { BottomPrompt, MainContent } from './components/index.js'; -const ALL_INTEGRATIONS = getIntegrations(); - export function InstallPluginsScreen() { const navigate = useNavigation(ScreenId.InstallPlugins); const log = useLogger(ScreenId.InstallPlugins); @@ -35,24 +34,18 @@ export function InstallPluginsScreen() { }, }); - function handleIdeSelect(value: IdeSelectValue) { - track(te.pluginIdeSelected(value)); - selectIde(value); - } - - const preferredIndex = ALL_INTEGRATIONS.map((i) => i.id).find((id) => detected.includes(id)); - - function handleDetectedSelect(value: DetectedSelectValue) { - if (value === 'continue') { - if (!preferredIndex) return; + function handleIdeSelect(value: IdeId) { + if (detected.includes(value)) { + store.setIde(value); - store.setIde(preferredIndex); log(pluginsAlreadyInstalled(detected)); track(te.pluginsAlreadyDetected()); navigate.to('next'); return; } - handleIdeSelect(value); + + track(te.pluginIdeSelected(value)); + selectIde(value); } function handleError(value: ErrorAction) { @@ -83,8 +76,7 @@ export function InstallPluginsScreen() { } diff --git a/src/ui/tui/screens/install-plugins/actions.ts b/src/ui/tui/screens/install-plugins/actions.ts index 65d69f4..e34afc6 100644 --- a/src/ui/tui/screens/install-plugins/actions.ts +++ b/src/ui/tui/screens/install-plugins/actions.ts @@ -1,10 +1,6 @@ import type { IdeId } from '@shared-kernel/types.js'; import type { PromptOption } from '../../components/PromptPanel.js'; -export type IdeSelectValue = IdeId; - -export type DetectedSelectValue = IdeSelectValue | 'continue'; - export const IDE_SELECT_OPTIONS: PromptOption[] = [ { label: 'Claude Code', value: 'claude' }, { label: 'Cursor', value: 'cursor' }, diff --git a/src/ui/tui/screens/install-plugins/components/BottomPrompt.tsx b/src/ui/tui/screens/install-plugins/components/BottomPrompt.tsx index 65e4018..0e0a087 100644 --- a/src/ui/tui/screens/install-plugins/components/BottomPrompt.tsx +++ b/src/ui/tui/screens/install-plugins/components/BottomPrompt.tsx @@ -2,41 +2,23 @@ import type { IdeId } from '@shared-kernel/types.js'; import { getIntegrations } from '@integrations/index.js'; import { PromptPanel } from '../../../components/PromptPanel.js'; import type { PluginPhase } from '../usePluginInstall.js'; -import { - IDE_SELECT_OPTIONS, - ERROR_OPTIONS, - type IdeSelectValue, - type DetectedSelectValue, - type ErrorAction, -} from '../actions.js'; +import { ERROR_OPTIONS, type ErrorAction } from '../actions.js'; +import { DefaultPrompt } from './DefaultPrompt.js'; const ALL_INTEGRATIONS = getIntegrations(); type BottomPromptProps = { phase: PluginPhase; detected: IdeId[]; - onIdeSelect: (value: IdeSelectValue) => void; - onDetectedSelect: (value: DetectedSelectValue) => void; + onSelect: (value: IdeId) => void; onError: (value: ErrorAction) => void; }; -export function BottomPrompt({ - phase, - detected, - onIdeSelect, - onDetectedSelect, - onError, -}: BottomPromptProps) { +export function BottomPrompt({ phase, detected, onSelect, onError }: BottomPromptProps) { switch (phase) { case 'choose-ide': - return ( - - ); + return ; + case 'error': return ( ); + case 'already-installed': { const detectedSet = new Set(detected); const preferred = ALL_INTEGRATIONS.find((i) => detectedSet.has(i.id)); - const otherOptions = ALL_INTEGRATIONS.filter((i) => i.id !== preferred?.id) + + if (!preferred) { + return ; + } + + const rest = ALL_INTEGRATIONS.filter((i) => i.id !== preferred.id) .toSorted((a, b) => Number(detectedSet.has(b.id)) - Number(detectedSet.has(a.id))) - .map((i) => ({ - label: i.name, - value: i.id, - })); + .map((i) => ({ label: i.name, value: i.id })); return ( ); } + case 'detecting': case 'installing': case 'installed': return null; + default: { const _exhaustive: never = phase satisfies never; throw new Error(`Unhandled phase: ${_exhaustive}`); diff --git a/src/ui/tui/screens/install-plugins/components/DefaultPrompt.tsx b/src/ui/tui/screens/install-plugins/components/DefaultPrompt.tsx new file mode 100644 index 0000000..2b0b010 --- /dev/null +++ b/src/ui/tui/screens/install-plugins/components/DefaultPrompt.tsx @@ -0,0 +1,18 @@ +import { PromptPanel } from '@ui/tui/components/index.js'; +import type { IdeId } from '@shared-kernel/types.js'; +import { IDE_SELECT_OPTIONS } from '../actions.js'; + +type DefaultPromptProps = { + onSelect: (value: IdeId) => void; +}; + +export function DefaultPrompt({ onSelect }: DefaultPromptProps) { + return ( + + ); +}