diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index f1cce1441..e389c9254 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -9364,14 +9364,19 @@ You are taking over this conversation. Based on the context above, provide a bri // Handle slash command autocomplete if (slashCommandOpen) { const isTerminalMode = activeSession?.inputMode === 'terminal'; - const filteredCommands = allSlashCommands.filter((cmd) => { - // Check if command is only available in terminal mode - if ('terminalOnly' in cmd && cmd.terminalOnly && !isTerminalMode) return false; - // Check if command is only available in AI mode - if ('aiOnly' in cmd && cmd.aiOnly && isTerminalMode) return false; - // Check if command matches input - return cmd.command.toLowerCase().startsWith(inputValue.toLowerCase()); - }); + const searchTerm = inputValue.toLowerCase().replace(/^\//, ''); + const scored: { cmd: typeof allSlashCommands[number]; score: number }[] = []; + for (const cmd of allSlashCommands) { + if ('terminalOnly' in cmd && cmd.terminalOnly && !isTerminalMode) continue; + if ('aiOnly' in cmd && cmd.aiOnly && isTerminalMode) continue; + if (!searchTerm) { scored.push({ cmd, score: 0 }); continue; } + const cmdName = cmd.command.toLowerCase().replace(/^\//, ''); + if (cmdName.startsWith(searchTerm)) { scored.push({ cmd, score: 3 }); continue; } + if (cmdName.includes(searchTerm)) { scored.push({ cmd, score: 2 }); continue; } + if (cmd.description && cmd.description.toLowerCase().includes(searchTerm)) { scored.push({ cmd, score: 1 }); continue; } + } + scored.sort((a, b) => b.score - a.score); + const filteredCommands = scored.map((s) => s.cmd); if (e.key === 'ArrowDown') { e.preventDefault(); diff --git a/src/renderer/components/InputArea.tsx b/src/renderer/components/InputArea.tsx index db6c09573..b4e9073b3 100644 --- a/src/renderer/components/InputArea.tsx +++ b/src/renderer/components/InputArea.tsx @@ -307,16 +307,21 @@ export const InputArea = React.memo(function InputArea(props: InputAreaProps) { // PERF: Memoize both the lowercase conversion and filtered results to avoid // recalculating on every render - inputValue changes on every keystroke const inputValueLower = useMemo(() => inputValue.toLowerCase(), [inputValue]); + const searchTerm = useMemo(() => inputValueLower.replace(/^\//, ''), [inputValueLower]); const filteredSlashCommands = useMemo(() => { - return slashCommands.filter((cmd) => { - // Check if command is only available in terminal mode - if (cmd.terminalOnly && !isTerminalMode) return false; - // Check if command is only available in AI mode - if (cmd.aiOnly && isTerminalMode) return false; - // Check if command matches input - return cmd.command.toLowerCase().startsWith(inputValueLower); - }); - }, [slashCommands, isTerminalMode, inputValueLower]); + const scored: { cmd: SlashCommand; score: number }[] = []; + for (const cmd of slashCommands) { + if (cmd.terminalOnly && !isTerminalMode) continue; + if (cmd.aiOnly && isTerminalMode) continue; + if (!searchTerm) { scored.push({ cmd, score: 0 }); continue; } + const cmdName = cmd.command.toLowerCase().replace(/^\//, ''); + if (cmdName.startsWith(searchTerm)) { scored.push({ cmd, score: 3 }); continue; } + if (cmdName.includes(searchTerm)) { scored.push({ cmd, score: 2 }); continue; } + if (cmd.description && cmd.description.toLowerCase().includes(searchTerm)) { scored.push({ cmd, score: 1 }); continue; } + } + scored.sort((a, b) => b.score - a.score); + return scored.map((s) => s.cmd); + }, [slashCommands, isTerminalMode, searchTerm]); // Ensure selectedSlashCommandIndex is valid for the filtered list const safeSelectedIndex = Math.min(