/** * @license * Copyright 2025 Vybestack LLC * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { useState, useCallback } from 'react'; import { Box, Text } from 'ink'; import { Colors } from '../../colors.js'; import { RadioButtonSelect } from '../shared/RadioButtonSelect.js'; import { WizardStep, type WizardState } from './types.js'; import { getNextStep, getPreviousStep } from './utils.js'; import { ProviderSelectStep } from './ProviderSelectStep.js'; import { BaseUrlConfigStep } from './BaseUrlConfigStep.js'; import { ModelSelectStep } from './ModelSelectStep.js'; import { AuthenticationStep } from './AuthenticationStep.js'; import { AdvancedParamsStep } from './AdvancedParamsStep.js'; import { ProfileSaveStep } from './ProfileSaveStep.js'; import { ProfileSuccessSummary } from './ProfileSuccessSummary.js'; import { getBorderStyle } from '../../contexts/UnicodeRenderingContext.js'; const INITIAL_STATE: WizardState = { currentStep: WizardStep.PROVIDER_SELECT, stepHistory: [WizardStep.PROVIDER_SELECT], config: { provider: null, model: null, auth: { type: null } }, validationErrors: {}, skipValidation: false, }; const CancelConfirmDialog: React.FC<{ state: WizardState; handleCancelDialogSelect: (value: string) => void; }> = ({ state, handleCancelDialogSelect }) => ( Cancel Profile Creation? Your configuration will be lost: {state.config.provider && ( • Provider: {state.config.provider} )} {state.config.model && ( • Model: {state.config.model} )} {state.config.baseUrl && ( • Base URL: {state.config.baseUrl} )} Are you sure you want to cancel? ); interface StepHandlers { state: WizardState; handleContinue: () => void; goBack: () => void; handleCancel: () => void; handleUpdateProvider: (provider: string) => void; handleUpdateBaseUrl: (baseUrl: string) => void; handleUpdateModel: (model: string) => void; handleUpdateAuth: (auth: WizardState['config']['auth']) => void; handleUpdateParams: (params: WizardState['config']['params']) => void; handleUpdateProfileName: (name: string) => void; onClose: () => void; onLoadProfile?: (profileName: string) => void; availableProviders?: string[]; } const STEP_RENDERERS: Record | null> = { [WizardStep.PROVIDER_SELECT]: (h) => ( ), [WizardStep.BASE_URL_CONFIG]: (h) => ( ), [WizardStep.MODEL_SELECT]: (h) => ( ), [WizardStep.AUTHENTICATION]: (h) => ( ), [WizardStep.ADVANCED_PARAMS]: (h) => ( ), [WizardStep.SAVE_PROFILE]: (h) => ( ), [WizardStep.SUCCESS_SUMMARY]: (h) => ( ), }; const renderStep = (handlers: StepHandlers): React.ReactNode => { const renderer = STEP_RENDERERS[handlers.state.currentStep]; if (renderer) { return renderer(handlers) as React.ReactNode; } return Unknown step; }; const useConfigUpdaters = ( setState: React.Dispatch>, ) => { const updateConfig = useCallback( ( key: K, value: WizardState['config'][K], ) => { setState((prev) => ({ ...prev, config: { ...prev.config, [key]: value }, })); }, [setState], ); const handleUpdateProvider = useCallback( (provider: string) => { setState((prev) => { const newConfig = { ...prev.config, provider }; const nextStep = getNextStep(WizardStep.PROVIDER_SELECT, { ...prev, config: newConfig, }); return { ...prev, config: newConfig, currentStep: nextStep, stepHistory: [...prev.stepHistory, nextStep], }; }); }, [setState], ); const handleUpdateProfileName = useCallback( (name: string) => { setState((prev) => ({ ...prev, profileName: name })); }, [setState], ); return { updateConfig, handleUpdateProvider, handleUpdateProfileName }; }; const buildHandlers = ( state: WizardState, handleContinue: () => void, goBack: () => void, handleCancel: () => void, updateConfig: ( key: K, value: WizardState['config'][K], ) => void, handleUpdateProvider: (provider: string) => void, handleUpdateProfileName: (name: string) => void, onClose: () => void, onLoadProfile?: (profileName: string) => void, availableProviders?: string[], ): StepHandlers => ({ state, handleContinue, goBack, handleCancel, handleUpdateProvider, handleUpdateProfileName, handleUpdateBaseUrl: (b: string) => updateConfig('baseUrl', b), handleUpdateModel: (m: string) => updateConfig('model', m), handleUpdateAuth: (a: WizardState['config']['auth']) => updateConfig('auth', a), handleUpdateParams: (p: WizardState['config']['params']) => updateConfig('params', p), onClose, onLoadProfile, availableProviders, }); interface ProfileCreateWizardProps { onClose: () => void; onLoadProfile?: (profileName: string) => void; availableProviders?: string[]; } export const ProfileCreateWizard: React.FC = ({ onClose, onLoadProfile, availableProviders, }) => { const [state, setState] = useState(INITIAL_STATE); const [showCancelConfirm, setShowCancelConfirm] = useState(false); const navigateToStep = useCallback((nextStep: WizardStep) => { setState((prev) => ({ ...prev, currentStep: nextStep, stepHistory: [...prev.stepHistory, nextStep], })); }, []); const goBack = useCallback(() => { setState((prev) => ({ ...prev, currentStep: getPreviousStep(prev), stepHistory: prev.stepHistory.slice(0, -1), })); }, []); const handleContinue = useCallback(() => { navigateToStep(getNextStep(state.currentStep, state)); }, [state, navigateToStep]); const handleCancel = useCallback(() => { if ( state.currentStep === WizardStep.PROVIDER_SELECT && !state.config.provider ) { onClose(); return; } setShowCancelConfirm(true); }, [state.currentStep, state.config.provider, onClose]); const handleCancelDialogSelect = useCallback( (value: string) => { if (value === 'confirm') { onClose(); } else { setShowCancelConfirm(false); } }, [onClose], ); const { updateConfig, handleUpdateProvider, handleUpdateProfileName } = useConfigUpdaters(setState); const handlers = buildHandlers( state, handleContinue, goBack, handleCancel, updateConfig, handleUpdateProvider, handleUpdateProfileName, onClose, onLoadProfile, availableProviders, ); if (showCancelConfirm) { return ( ); } return ( {renderStep(handlers)} ); };