From 416813452eafd4b9497fe7f4b36d5000b51c969d Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 14 May 2025 16:01:29 -0700 Subject: [PATCH] Improvements to suggestions & slash commands (#344) Co-authored-by: N. Taylor Mullen --- packages/cli/src/ui/App.tsx | 2 +- .../cli/src/ui/components/InputPrompt.tsx | 1 + .../src/ui/components/SuggestionsDisplay.tsx | 28 +++++++++----- .../cli/src/ui/hooks/slashCommandProcessor.ts | 2 +- packages/cli/src/ui/hooks/useCompletion.ts | 38 ++++++++++++------- 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index 5fb36480..758ddb28 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -314,7 +314,7 @@ export const App = ({ resetCompletion={completion.resetCompletionState} /> {completion.showSuggestions && ( - + = ({ const newValue = base + selectedSuggestion.value; onChangeAndMoveCursor(newValue); onSubmit(newValue); // Execute the command + onChangeAndMoveCursor(''); // Clear query after submit } else { // Handle @ command completion const atIndex = query.lastIndexOf('@'); diff --git a/packages/cli/src/ui/components/SuggestionsDisplay.tsx b/packages/cli/src/ui/components/SuggestionsDisplay.tsx index ba25f2b6..3f2094bb 100644 --- a/packages/cli/src/ui/components/SuggestionsDisplay.tsx +++ b/packages/cli/src/ui/components/SuggestionsDisplay.tsx @@ -9,6 +9,7 @@ import { Colors } from '../colors.js'; export interface Suggestion { label: string; value: string; + description?: string; } interface SuggestionsDisplayProps { suggestions: Suggestion[]; @@ -29,7 +30,7 @@ export function SuggestionsDisplay({ }: SuggestionsDisplayProps) { if (isLoading) { return ( - + Loading suggestions... ); @@ -48,20 +49,29 @@ export function SuggestionsDisplay({ const visibleSuggestions = suggestions.slice(startIndex, endIndex); return ( - + {scrollOffset > 0 && } {visibleSuggestions.map((suggestion, index) => { const originalIndex = startIndex + index; const isActive = originalIndex === activeIndex; + const textColor = isActive ? Colors.AccentPurple : Colors.SubtleComment; + return ( - - {suggestion.label} - + + + + {suggestion.label} + + {suggestion.description ? ( + + + {suggestion.description} + + + ) : null} + + ); })} {endIndex < suggestions.length && } diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index e2b478e2..7ce3ae86 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -94,7 +94,7 @@ export const useSlashCommandProcessor = ( { name: 'quit', altName: 'exit', - description: '', + description: 'exit the cli', action: (_value: PartListUnion | string) => { onDebugMessage('Quitting. Good-bye.'); process.exit(0); diff --git a/packages/cli/src/ui/hooks/useCompletion.ts b/packages/cli/src/ui/hooks/useCompletion.ts index 1c693f71..622dc4c4 100644 --- a/packages/cli/src/ui/hooks/useCompletion.ts +++ b/packages/cli/src/ui/hooks/useCompletion.ts @@ -119,20 +119,30 @@ export function useCompletion( // --- Handle Slash Command Completion --- if (trimmedQuery.startsWith('/')) { const partialCommand = trimmedQuery.substring(1); - const commands = slashCommands - .map((cmd) => cmd.name) - .concat( - slashCommands - .map((cmd) => cmd.altName) - .filter((cmd) => cmd !== undefined), - ); - - const filteredSuggestions = commands - .filter((name) => name.startsWith(partialCommand)) - // Filter out ? and any other single character commands - .filter((name) => name.length > 1) - .map((name) => ({ label: name, value: name })) - .sort(); + const filteredSuggestions = slashCommands + .filter( + (cmd) => + cmd.name.startsWith(partialCommand) || + cmd.altName?.startsWith(partialCommand), + ) + // Filter out ? and any other single character commands unless it's the only char + .filter((cmd) => { + const nameMatch = cmd.name.startsWith(partialCommand); + const altNameMatch = cmd.altName?.startsWith(partialCommand); + if (partialCommand.length === 1) { + return nameMatch || altNameMatch; // Allow single char match if query is single char + } + return ( + (nameMatch && cmd.name.length > 1) || + (altNameMatch && cmd.altName && cmd.altName.length > 1) + ); + }) + .map((cmd) => ({ + label: cmd.name, // Always show the main name as label + value: cmd.name, // Value should be the main command name for execution + description: cmd.description, + })) + .sort((a, b) => a.label.localeCompare(b.label)); setSuggestions(filteredSuggestions); setShowSuggestions(filteredSuggestions.length > 0);