import figures from 'figures' import * as React from 'react' import { useCallback, useEffect, useMemo, useState } from 'react' import { useDebounceCallback } from 'usehooks-ts' import { addDirHelpMessage, validateDirectoryForWorkspace, } from '../../../commands/add-dir/validation.js' import TextInput from '../../../components/TextInput.js' import { type KeyboardEvent, Box, Text } from '@anthropic/ink' import { useKeybinding } from '../../../keybindings/useKeybinding.js' import type { ToolPermissionContext } from '../../../Tool.js' import { getDirectoryCompletions } from '../../../utils/suggestions/directoryCompletion.js' import { ConfigurableShortcutHint } from '../../ConfigurableShortcutHint.js' import { Select } from '../../CustomSelect/select.js' import { Byline, Dialog, KeyboardShortcutHint } from '@anthropic/ink' import { PromptInputFooterSuggestions, type SuggestionItem, } from '../../PromptInput/PromptInputFooterSuggestions.js' type Props = { onAddDirectory: (path: string, remember?: boolean) => void onCancel: () => void permissionContext: ToolPermissionContext directoryPath?: string // When directoryPath is provided, show selection options instead of input } type RememberDirectoryOption = 'yes-session' | 'yes-remember' | 'no' const REMEMBER_DIRECTORY_OPTIONS: Array<{ value: RememberDirectoryOption label: string }> = [ { value: 'yes-session', label: 'Yes, for this session', }, { value: 'yes-remember', label: 'Yes, and remember this directory', }, { value: 'no', label: 'No', }, ] function PermissionDescription(): React.ReactNode { return ( Claude Code will be able to read files in this directory and make edits when auto-accept edits is on. ) } function DirectoryDisplay({ path }: { path: string }): React.ReactNode { return ( {path} ) } function DirectoryInput({ value, onChange, onSubmit, error, suggestions, selectedSuggestion, }: { value: string onChange: (value: string) => void onSubmit: (value: string) => void error: string | null suggestions: SuggestionItem[] selectedSuggestion: number }): React.ReactNode { return ( Enter the path to the directory: {}} /> {suggestions.length > 0 && ( )} {error && {error}} ) } export function AddWorkspaceDirectory({ onAddDirectory, onCancel, permissionContext, directoryPath, }: Props): React.ReactNode { const [directoryInput, setDirectoryInput] = useState('') const [error, setError] = useState(null) const [suggestions, setSuggestions] = useState([]) const [selectedSuggestion, setSelectedSuggestion] = useState(0) const options = useMemo(() => REMEMBER_DIRECTORY_OPTIONS, []) // Fetch directory completions const fetchSuggestions = useCallback(async (path: string) => { if (!path) { setSuggestions([]) setSelectedSuggestion(0) return } const completions = await getDirectoryCompletions(path) setSuggestions(completions) setSelectedSuggestion(0) }, []) const debouncedFetchSuggestions = useDebounceCallback(fetchSuggestions, 100) useEffect(() => { void debouncedFetchSuggestions(directoryInput) }, [directoryInput, debouncedFetchSuggestions]) const applySuggestion = useCallback((suggestion: SuggestionItem) => { const newPath = suggestion.id + '/' setDirectoryInput(newPath) setError(null) // Suggestions will update via the useEffect }, []) // Handle directory submission from input const handleSubmit = useCallback( async (newPath: string) => { const result = await validateDirectoryForWorkspace( newPath, permissionContext, ) if (result.resultType === 'success') { onAddDirectory(result.absolutePath, false) } else { setError(addDirHelpMessage(result)) } }, [permissionContext, onAddDirectory], ) // Handle Esc to cancel (Ctrl+C handled by global keybindings) // Use Settings context so 'n' key doesn't cancel (allows typing 'n' in input) useKeybinding('confirm:no', onCancel, { context: 'Settings' }) const handleKeyDown = useCallback( (e: KeyboardEvent) => { if (suggestions.length > 0) { // Tab: accept selected suggestion and continue (for drilling into subdirs) if (e.key === 'tab') { e.preventDefault() const suggestion = suggestions[selectedSuggestion] if (suggestion) { applySuggestion(suggestion) } return } // Enter: apply selected suggestion and submit if (e.key === 'return') { e.preventDefault() const suggestion = suggestions[selectedSuggestion] if (suggestion) { void handleSubmit(suggestion.id + '/') } return } if (e.key === 'up' || (e.ctrl && e.key === 'p')) { e.preventDefault() setSelectedSuggestion(prev => prev <= 0 ? suggestions.length - 1 : prev - 1, ) return } if (e.key === 'down' || (e.ctrl && e.key === 'n')) { e.preventDefault() setSelectedSuggestion(prev => prev >= suggestions.length - 1 ? 0 : prev + 1, ) return } } }, [suggestions, selectedSuggestion, applySuggestion, handleSubmit], ) const handleSelect = useCallback( (value: string) => { if (!directoryPath) return const selectionValue = value as RememberDirectoryOption switch (selectionValue) { case 'yes-session': onAddDirectory(directoryPath, false) break case 'yes-remember': onAddDirectory(directoryPath, true) break case 'no': onCancel() break } }, [directoryPath, onAddDirectory, onCancel], ) return ( exitState.pending ? ( Press {exitState.keyName} again to exit ) : ( ) } > {directoryPath ? (