From ec2c2b8a8fbac38b3ab707d18cd9541873c8b032 Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Wed, 4 Jun 2025 10:45:03 -0600 Subject: [PATCH] Fix tab complete --- src/components/Terminal.tsx | 155 ++++++++++++------------------------ 1 file changed, 49 insertions(+), 106 deletions(-) diff --git a/src/components/Terminal.tsx b/src/components/Terminal.tsx index 9eb8d17..bb86908 100644 --- a/src/components/Terminal.tsx +++ b/src/components/Terminal.tsx @@ -42,11 +42,9 @@ const Terminal = () => { const [currentInput, setCurrentInput] = useState(''); const [historyIndex, setHistoryIndex] = useState(-1); const [fileSystem, setFileSystem] = useState<{ [key: string]: FileSystemNode }>({}); - const [lastTabInput, setLastTabInput] = useState(''); - const [showCompletions, setShowCompletions] = useState(false); - const [completionsList, setCompletionsList] = useState([]); const [isTrainRunning, setIsTrainRunning] = useState(false); const [trainPosition, setTrainPosition] = useState(100); + const [persistentHistory, setPersistentHistory] = useState([]); const inputRef = useRef(null); const terminalRef = useRef(null); @@ -62,6 +60,28 @@ const Terminal = () => { } }, []); + // Load persistent command history from localStorage + useEffect(() => { + const savedHistory = localStorage.getItem('terminal-history'); + if (savedHistory) { + try { + const parsed = JSON.parse(savedHistory); + setPersistentHistory(parsed); + } catch (error) { + console.error('Error loading command history:', error); + } + } + }, []); + + // Save command history to localStorage + const saveCommandToHistory = (command: string) => { + if (command.trim()) { + const updatedHistory = [...persistentHistory, command].slice(-100); // Keep last 100 commands + setPersistentHistory(updatedHistory); + localStorage.setItem('terminal-history', JSON.stringify(updatedHistory)); + } + }; + useEffect(() => { const initializeFileSystem = async () => { try { @@ -269,35 +289,13 @@ ${post.content}`; return '/' + currentParts.join('/'); }; - const getCommonPrefix = (strings: string[]): string => { - if (strings.length === 0) return ''; - if (strings.length === 1) return strings[0]; - - let commonPrefix = strings[0]; - for (let i = 1; i < strings.length; i++) { - let j = 0; - while (j < commonPrefix.length && j < strings[i].length && commonPrefix[j] === strings[i][j]) { - j++; - } - commonPrefix = commonPrefix.substring(0, j); - if (commonPrefix === '') break; - } - return commonPrefix; - }; - - const getCompletions = (input: string): { completions: string[], prefix: string, replaceFrom: number } => { + const getCompletions = (input: string): { completion: string | null, replaceFrom: number } => { const parts = input.trim().split(' '); const command = parts[0]; const partialPath = parts[parts.length - 1] || ''; - if (parts.length === 1) { - // Command completion - const commands = ['ls', 'cd', 'cat', 'pwd', 'clear', 'tree', 'whoami', 'open', 'help']; - const matches = commands.filter(cmd => cmd.startsWith(command)); - return { completions: matches, prefix: command, replaceFrom: 0 }; - } - - if (['ls', 'cd', 'cat', 'open'].includes(command)) { + // Only complete paths for these commands, not the commands themselves + if (parts.length > 1 && ['ls', 'cd', 'cat', 'open'].includes(command)) { // Path completion const isAbsolute = partialPath.startsWith('/'); const pathToComplete = isAbsolute ? partialPath : resolvePath(partialPath); @@ -334,26 +332,26 @@ ${post.content}`; if (current?.children && current.children[part] && current.children[part].type === 'directory') { current = current.children[part]; } else { - return { completions: [], prefix: searchPrefix, replaceFrom }; + return { completion: null, replaceFrom }; } } if (!current?.children) { - return { completions: [], prefix: searchPrefix, replaceFrom }; + return { completion: null, replaceFrom }; } - // Get matching items - const matches = Object.keys(current.children) - .filter(name => name.startsWith(searchPrefix)) - .map(name => { - const item = current.children![name]; - return item.type === 'directory' ? `${name}/` : name; - }); + // Get first matching item + const match = Object.keys(current.children) + .find(name => name.startsWith(searchPrefix)); - return { completions: matches, prefix: searchPrefix, replaceFrom }; + if (match) { + const item = current.children[match]; + const completion = item.type === 'directory' ? `${match}/` : match; + return { completion, replaceFrom }; + } } - return { completions: [], prefix: '', replaceFrom: input.length }; + return { completion: null, replaceFrom: input.length }; }; const executeCommand = (input: string): string => { @@ -556,6 +554,9 @@ ${post.content}`; timestamp: new Date() }; + // Save command to persistent history + saveCommandToHistory(currentInput); + if (currentInput.trim().toLowerCase() === 'clear') { setCommandHistory([]); } else { @@ -564,91 +565,39 @@ ${post.content}`; setCurrentInput(''); setHistoryIndex(-1); - setShowCompletions(false); - setLastTabInput(''); }; const handleKeyDown = (e: JSX.TargetedKeyboardEvent) => { if (e.key === 'Tab') { e.preventDefault(); - const { completions, prefix, replaceFrom } = getCompletions(currentInput); + const { completion, replaceFrom } = getCompletions(currentInput); - if (completions.length === 0) { - return; - } - - if (completions.length === 1) { - // Single completion - replace from the correct position + if (completion) { + // Replace from the correct position with the completion const beforeReplacement = currentInput.substring(0, replaceFrom); - const completion = completions[0]; const newInput = beforeReplacement + completion; - setCurrentInput(newInput + (completion.endsWith('/') ? '' : ' ')); - setShowCompletions(false); - setLastTabInput(''); - } else { - // Multiple completions - find common prefix - const commonPrefix = getCommonPrefix(completions); - - if (commonPrefix.length > prefix.length) { - // Complete to common prefix - const beforeReplacement = currentInput.substring(0, replaceFrom); - setCurrentInput(beforeReplacement + commonPrefix); - setShowCompletions(false); - setLastTabInput(currentInput); - } else { - // Show completions if we hit tab twice on the same input - if (currentInput === lastTabInput && !showCompletions) { - setCompletionsList(completions); - setShowCompletions(true); - - // Add completions to command history for display - const output = completions.length > 8 - ? completions.slice(0, 8).join(' ') + ` ... (${completions.length - 8} more)` - : completions.join(' '); - - const newCommand: Command = { - input: '', - output, - timestamp: new Date() - }; - setCommandHistory((prev: Command[]) => [...prev, newCommand]); - } else { - setLastTabInput(currentInput); - setShowCompletions(false); - } - } } } else if (e.key === 'ArrowUp') { e.preventDefault(); - const inputCommands = commandHistory.filter((cmd: Command) => cmd.input.trim() !== ''); - if (inputCommands.length > 0) { - const newIndex = historyIndex === -1 ? inputCommands.length - 1 : Math.max(0, historyIndex - 1); + if (persistentHistory.length > 0) { + const newIndex = historyIndex === -1 ? persistentHistory.length - 1 : Math.max(0, historyIndex - 1); setHistoryIndex(newIndex); - setCurrentInput(inputCommands[newIndex].input); + setCurrentInput(persistentHistory[newIndex]); } - setShowCompletions(false); - setLastTabInput(''); } else if (e.key === 'ArrowDown') { e.preventDefault(); - const inputCommands = commandHistory.filter((cmd: Command) => cmd.input.trim() !== ''); if (historyIndex !== -1) { - const newIndex = Math.min(inputCommands.length - 1, historyIndex + 1); - if (newIndex === inputCommands.length - 1 && historyIndex === newIndex) { + const newIndex = Math.min(persistentHistory.length - 1, historyIndex + 1); + if (newIndex === persistentHistory.length - 1 && historyIndex === newIndex) { setHistoryIndex(-1); setCurrentInput(''); } else { setHistoryIndex(newIndex); - setCurrentInput(inputCommands[newIndex].input); + setCurrentInput(persistentHistory[newIndex]); } } - setShowCompletions(false); - setLastTabInput(''); - } else { - // Reset completion state on any other key - setShowCompletions(false); - setLastTabInput(''); } }; @@ -718,12 +667,6 @@ __/ =| o |=-O=====O=====O=====O \\ ____Y___________|__|_________________________ ))} - {showCompletions && ( -
- Tab completions: {completionsList.join(' ')} -
- )} -
guest@atri.dad :