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