import { useCallback, useState } from 'react';
import TextInput from '../../components/TextInput.js';
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
import { Box, color, Text, useTheme } from '@anthropic/ink';
import { useKeybindings } from '../../keybindings/useKeybinding.js';
interface ApiKeyStepProps {
existingApiKey: string | null;
useExistingKey: boolean;
apiKeyOrOAuthToken: string;
onApiKeyChange: (value: string) => void;
onToggleUseExistingKey: (useExisting: boolean) => void;
onSubmit: () => void;
onCreateOAuthToken?: () => void;
selectedOption?: 'existing' | 'new' | 'oauth';
onSelectOption?: (option: 'existing' | 'new' | 'oauth') => void;
}
export function ApiKeyStep({
existingApiKey,
apiKeyOrOAuthToken,
onApiKeyChange,
onSubmit,
onToggleUseExistingKey,
onCreateOAuthToken,
selectedOption = existingApiKey ? 'existing' : onCreateOAuthToken ? 'oauth' : 'new',
onSelectOption,
}: ApiKeyStepProps) {
const [cursorOffset, setCursorOffset] = useState(0);
const terminalSize = useTerminalSize();
const [theme] = useTheme();
const handlePrevious = useCallback(() => {
if (selectedOption === 'new' && onCreateOAuthToken) {
// From 'new' go up to 'oauth'
onSelectOption?.('oauth');
} else if (selectedOption === 'oauth' && existingApiKey) {
// From 'oauth' go up to 'existing' (only if it exists)
onSelectOption?.('existing');
onToggleUseExistingKey(true);
}
}, [selectedOption, onCreateOAuthToken, existingApiKey, onSelectOption, onToggleUseExistingKey]);
const handleNext = useCallback(() => {
if (selectedOption === 'existing') {
// From 'existing' go down to 'oauth' (if available) or 'new'
onSelectOption?.(onCreateOAuthToken ? 'oauth' : 'new');
onToggleUseExistingKey(false);
} else if (selectedOption === 'oauth') {
// From 'oauth' go down to 'new'
onSelectOption?.('new');
}
}, [selectedOption, onCreateOAuthToken, onSelectOption, onToggleUseExistingKey]);
const handleConfirm = useCallback(() => {
if (selectedOption === 'oauth' && onCreateOAuthToken) {
onCreateOAuthToken();
} else {
onSubmit();
}
}, [selectedOption, onCreateOAuthToken, onSubmit]);
// When the text input is visible, omit confirm:yes so bare 'y' passes
// through to the input instead of submitting. TextInput's onSubmit handles
// Enter. Keep the Confirmation context (not Settings) to avoid j/k bindings.
const isTextInputVisible = selectedOption === 'new';
useKeybindings(
{
'confirm:previous': handlePrevious,
'confirm:next': handleNext,
'confirm:yes': handleConfirm,
},
{ context: 'Confirmation', isActive: !isTextInputVisible },
);
useKeybindings(
{
'confirm:previous': handlePrevious,
'confirm:next': handleNext,
},
{ context: 'Confirmation', isActive: isTextInputVisible },
);
return (
<>
Install GitHub App
Choose API key
{existingApiKey && (
{selectedOption === 'existing' ? color('success', theme)('> ') : ' '}
Use your existing Claude Code API key
)}
{onCreateOAuthToken && (
{selectedOption === 'oauth' ? color('success', theme)('> ') : ' '}
Create a long-lived token with your Claude subscription
)}
{selectedOption === 'new' ? color('success', theme)('> ') : ' '}
Enter a new API key
{selectedOption === 'new' && (
)}
↑/↓ to select · Enter to continue
>
);
}