import React, { useState } from 'react'
import { PRODUCT_NAME } from '../constants/product'
import { Box, Newline, Text, useInput } from 'ink'
import {
getGlobalConfig,
saveGlobalConfig,
DEFAULT_GLOBAL_CONFIG,
ProviderType,
} from '../utils/config.js'
import { OrderedList } from '@inkjs/ui'
import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD'
import { MIN_LOGO_WIDTH } from './Logo'
import { Select } from './CustomSelect/select'
import { StructuredDiff } from './StructuredDiff'
import { getTheme, type ThemeNames } from '../utils/theme'
import { clearTerminal } from '../utils/terminal'
import { PressEnterToContinue } from './PressEnterToContinue'
import { ModelSelector } from './ModelSelector'
type StepId = 'theme' | 'usage' | 'providers' | 'model'
interface OnboardingStep {
id: StepId
component: React.ReactNode
}
type Props = {
onDone(): void
}
export function Onboarding({ onDone }: Props): React.ReactNode {
const [currentStepIndex, setCurrentStepIndex] = useState(0)
const [showModelSelector, setShowModelSelector] = useState(false)
const config = getGlobalConfig()
const [selectedTheme, setSelectedTheme] = useState(
DEFAULT_GLOBAL_CONFIG.theme,
)
const theme = getTheme()
function goToNextStep() {
if (currentStepIndex < steps.length - 1) {
const nextIndex = currentStepIndex + 1
setCurrentStepIndex(nextIndex)
}
}
function handleThemeSelection(newTheme: string) {
saveGlobalConfig({
...config,
theme: newTheme as ThemeNames,
})
goToNextStep()
}
function handleThemePreview(newTheme: string) {
setSelectedTheme(newTheme as ThemeNames)
}
function handleProviderSelectionDone() {
// After model selection is done, go to the next step
goToNextStep()
}
function handleModelSelectionDone() {
// After final model selection is done, complete onboarding
onDone()
}
const exitState = useExitOnCtrlCD(() => process.exit(0))
useInput(async (_, key) => {
const currentStep = steps[currentStepIndex]
if (
key.return &&
currentStep &&
['usage', 'providers', 'model'].includes(currentStep.id)
) {
if (currentStep.id === 'model') {
// Navigate to ModelSelector component
setShowModelSelector(true)
} else if (currentStepIndex === steps.length - 1) {
onDone()
} else {
// HACK: for some reason there's now a jump here otherwise :(
await clearTerminal()
goToNextStep()
}
}
})
// Define all onboarding steps
const themeStep = (
Let's get started.Choose the option that looks best when you select it:To change this later, run /config
)
const providersStep = (
Next, let's select your preferred AI provider and model.
)
const usageStep = (
Using {PRODUCT_NAME} effectively:
Start in your project directory
Files are automatically added to context when needed.
Use {PRODUCT_NAME} as a development partner
Get help with file analysis, editing, bash commands,
and git history.
Provide clear context
Be as specific as you would with another engineer.
The better the context, the better the results.
)
const modelStep = (
Configure your models:
You can customize which models {PRODUCT_NAME} uses for different
tasks.
Let's set up your preferred models for large and small tasks.
Press Enter to continue to the
model selection screen.
)
const steps: OnboardingStep[] = []
steps.push({ id: 'theme', component: themeStep })
steps.push({ id: 'usage', component: usageStep })
steps.push({ id: 'model', component: modelStep })
// If we're showing the model selector screen, render it directly
if (showModelSelector) {
return (
)
}
return (
<>
{PRODUCT_NAME}{' '}
{exitState.pending
? `(press ${exitState.keyName} again to exit)`
: ''}
{steps[currentStepIndex]?.component}
>
)
}
export function WelcomeBox(): React.ReactNode {
const theme = getTheme()
return (
✻ 欢迎来到
{PRODUCT_NAME}
)
}