import React, { useCallback, useEffect, useMemo, useState } from 'react' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, } from 'src/services/analytics/index.js' import { setupTerminal, shouldOfferTerminalSetup, } from '../commands/terminalSetup/terminalSetup.js' import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js' import { Box, Link, Newline, Text, useTheme } from '../ink.js' import { useKeybindings } from '../keybindings/useKeybinding.js' import { isAnthropicAuthEnabled } from '../utils/auth.js' import { normalizeApiKeyForConfig } from '../utils/authPortable.js' import { getCustomApiKeyStatus } from '../utils/config.js' import { env } from '../utils/env.js' import { isRunningOnHomespace } from '../utils/envUtils.js' import { PreflightStep } from '../utils/preflightChecks.js' import type { ThemeSetting } from '../utils/theme.js' import { ApproveApiKey } from './ApproveApiKey.js' import { ConsoleOAuthFlow } from './ConsoleOAuthFlow.js' import { Select } from './CustomSelect/select.js' import { WelcomeV2 } from './LogoV2/WelcomeV2.js' import { PressEnterToContinue } from './PressEnterToContinue.js' import { ThemePicker } from './ThemePicker.js' import { OrderedList } from './ui/OrderedList.js' type StepId = | 'preflight' | 'theme' | 'oauth' | 'api-key' | 'security' | 'terminal-setup' interface OnboardingStep { id: StepId component: React.ReactNode } type Props = { onDone(): void } export function Onboarding({ onDone }: Props): React.ReactNode { const [currentStepIndex, setCurrentStepIndex] = useState(0) const [skipOAuth, setSkipOAuth] = useState(false) const [oauthEnabled] = useState(() => isAnthropicAuthEnabled()) const [theme, setTheme] = useTheme() useEffect(() => { logEvent('tengu_began_setup', { oauthEnabled, }) }, [oauthEnabled]) function goToNextStep() { if (currentStepIndex < steps.length - 1) { const nextIndex = currentStepIndex + 1 setCurrentStepIndex(nextIndex) logEvent('tengu_onboarding_step', { oauthEnabled, stepId: steps[nextIndex] ?.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, }) } else { onDone() } } function handleThemeSelection(newTheme: ThemeSetting) { setTheme(newTheme) goToNextStep() } const exitState = useExitOnCtrlCDWithKeybindings() // Define all onboarding steps const themeStep = ( ) const securityStep = ( Security notes: {/** * OrderedList misnumbers items when rendering conditionally, * so put all items in the if/else */} Claude can make mistakes You should always review Claude's responses, especially when running code. Due to prompt injection risks, only use it with code you trust For more details see: ) const preflightStep = // Create the steps array - determine which steps to include based on reAuth and oauthEnabled const apiKeyNeedingApproval = useMemo(() => { // Add API key step if needed // On homespace, ANTHROPIC_API_KEY is preserved in process.env for child // processes but ignored by Claude Code itself (see auth.ts). if (!process.env.ANTHROPIC_API_KEY || isRunningOnHomespace()) { return '' } const customApiKeyTruncated = normalizeApiKeyForConfig( process.env.ANTHROPIC_API_KEY, ) if (getCustomApiKeyStatus(customApiKeyTruncated) === 'new') { return customApiKeyTruncated } }, []) function handleApiKeyDone(approved: boolean) { if (approved) { setSkipOAuth(true) } goToNextStep() } const steps: OnboardingStep[] = [] if (oauthEnabled) { steps.push({ id: 'preflight', component: preflightStep }) } steps.push({ id: 'theme', component: themeStep }) if (apiKeyNeedingApproval) { steps.push({ id: 'api-key', component: ( ), }) } if (oauthEnabled) { steps.push({ id: 'oauth', component: ( ), }) } steps.push({ id: 'security', component: securityStep }) if (shouldOfferTerminalSetup()) { steps.push({ id: 'terminal-setup', component: ( Use Claude Code's terminal setup? For the optimal coding experience, enable the recommended settings for your terminal:{' '} {env.terminal === 'Apple_Terminal' ? 'Option+Enter for newlines and visual bell' : 'Shift+Enter for newlines'}