import * as React from 'react' import { OrderedList } from '@inkjs/ui' import { Box, Text } from 'ink' import { getCurrentProjectConfig, getGlobalConfig, saveCurrentProjectConfig, saveGlobalConfig, } from './utils/config.js' import { existsSync } from 'fs' import { join } from 'path' import { homedir } from 'os' import terminalSetup from './commands/terminalSetup' import { getTheme } from './utils/theme' import { RELEASE_NOTES } from './constants/releaseNotes' import { gt } from 'semver' import { isDirEmpty } from './utils/file' import { MACRO } from './constants/macros' import { PROJECT_FILE, PRODUCT_NAME } from './constants/product' // Function to mark onboarding as complete export function markProjectOnboardingComplete(): void { const projectConfig = getCurrentProjectConfig() if (!projectConfig.hasCompletedProjectOnboarding) { saveCurrentProjectConfig({ ...projectConfig, hasCompletedProjectOnboarding: true, }) } } function markReleaseNotesSeen(): void { const config = getGlobalConfig() saveGlobalConfig({ ...config, lastReleaseNotesSeen: MACRO.VERSION, }) } type Props = { workspaceDir: string } export default function ProjectOnboarding({ workspaceDir, }: Props): React.ReactNode { // Check if project onboarding has already been completed const projectConfig = getCurrentProjectConfig() const showOnboarding = !projectConfig.hasCompletedProjectOnboarding // Get previous version from config const config = getGlobalConfig() const previousVersion = config.lastReleaseNotesSeen // Get release notes to show let releaseNotesToShow: string[] = [] if (!previousVersion || gt(MACRO.VERSION, previousVersion)) { releaseNotesToShow = RELEASE_NOTES[MACRO.VERSION] || [] } const hasReleaseNotes = releaseNotesToShow.length > 0 // Mark release notes as seen when they're displayed without onboarding React.useEffect(() => { if (hasReleaseNotes && !showOnboarding) { markReleaseNotesSeen() } }, [hasReleaseNotes, showOnboarding]) // We only want to show either onboarding OR release notes (with preference for onboarding) // If there's no onboarding to show and no release notes, return null if (!showOnboarding && !hasReleaseNotes) { return null } // Load what we need for onboarding // NOTE: This whole component is statically rendered Once const hasClaudeMd = existsSync(join(workspaceDir, PROJECT_FILE)) const isWorkspaceDirEmpty = isDirEmpty(workspaceDir) const needsClaudeMd = !hasClaudeMd && !isWorkspaceDirEmpty const showTerminalTip = terminalSetup.isEnabled && !getGlobalConfig().shiftEnterKeyBindingInstalled const theme = getTheme() return ( {showOnboarding && ( <> Tips for getting started: {/* @ts-expect-error - OrderedList children prop issue */} {/* Collect all the items that should be displayed */} {(() => { const items = [] if (isWorkspaceDirEmpty) { items.push( {/* @ts-expect-error - OrderedList.Item children prop issue */} Ask {PRODUCT_NAME} to create a new app or clone a repository. , ) } if (needsClaudeMd) { items.push( {/* @ts-expect-error - OrderedList.Item children prop issue */} Run /init to create a  {PROJECT_FILE} file with instructions for {PRODUCT_NAME}. , ) } if (showTerminalTip) { items.push( {/* @ts-expect-error - OrderedList.Item children prop issue */} Run /terminal-setup to set up terminal integration , ) } items.push( {/* @ts-expect-error - OrderedList.Item children prop issue */} Ask {PRODUCT_NAME} questions about your codebase. , ) items.push( {/* @ts-expect-error - OrderedList.Item children prop issue */} Ask {PRODUCT_NAME} to implement changes to your codebase. , ) return items })()} )} {!showOnboarding && hasReleaseNotes && ( 🆕 What's new in v{MACRO.VERSION}: {releaseNotesToShow.map((note, noteIndex) => ( • {note} ))} )} {workspaceDir === homedir() && ( Note: You have launched anon-code in your home directory. For the best experience, launch it in a project directory instead. )} ) }