/** * @license * Copyright 2025 Vybestack LLC * SPDX-License-Identifier: Apache-2.0 */ import { Box, Text } from 'ink'; import { Colors } from '../colors.js'; import { PrepareLabel } from './PrepareLabel.js'; import { isSlashCommand } from '../utils/commandUtils.js'; import { CommandKind } from '../commands/types.js'; export interface Suggestion { label: string; value: string; description?: string; matchedIndex?: number; kind?: CommandKind; } interface SuggestionsDisplayProps { suggestions: Suggestion[]; activeIndex: number; isLoading: boolean; width: number; scrollOffset: number; userInput: string; activeHint?: string; } export const MAX_SUGGESTIONS_TO_SHOW = 8; interface SuggestionRowProps { suggestion: Suggestion; index: number; startIndex: number; activeIndex: number; userInput: string; isSlashCommandMode: boolean; commandNameWidth: number; } function SuggestionRow({ suggestion, index, startIndex, activeIndex, userInput, isSlashCommandMode, commandNameWidth, }: SuggestionRowProps) { const originalIndex = startIndex + index; const isActive = originalIndex === activeIndex; const textColor = isActive ? '#00ff00' : Colors.Foreground; const labelElement = ( ); const subagentBadge = suggestion.kind === CommandKind.SUBAGENT ? ( [Subagent] ) : null; return ( {isSlashCommandMode ? ( <> {labelElement} {suggestion.description && ( {suggestion.description} )} ) : ( <> {subagentBadge} {labelElement} {suggestion.description && ( {suggestion.description} )} )} ); } /** * @plan:PLAN-20251013-AUTOCOMPLETE.P08 * @requirement:REQ-002 * @requirement:REQ-003 * @requirement:REQ-004 * @pseudocode UIHintRendering.md lines 4-7 * Full implementation of hint line rendering * - Line 4: Modify SuggestionsDisplay to accept activeHint prop * - Line 5-6: Render hint in dedicated line above suggestion list * - Line 7: Ensure consistent height to avoid layout shift */ export function SuggestionsDisplay({ suggestions, activeIndex, isLoading, width, scrollOffset, userInput, activeHint, }: SuggestionsDisplayProps) { if (isLoading) { return ( Loading suggestions... ); } if (suggestions.length === 0) { return null; } const startIndex = scrollOffset; const endIndex = Math.min( scrollOffset + MAX_SUGGESTIONS_TO_SHOW, suggestions.length, ); const visibleSuggestions = suggestions.slice(startIndex, endIndex); const isSlashCommandMode = isSlashCommand(userInput); let commandNameWidth = 0; if (isSlashCommandMode) { const maxLabelLength = visibleSuggestions.length > 0 ? Math.max(...visibleSuggestions.map((s) => s.label.length)) : 0; const maxAllowedWidth = Math.floor(width * 0.35); commandNameWidth = Math.max( 15, Math.min(maxLabelLength + 2, maxAllowedWidth), ); } const hasScrollUp = scrollOffset > 0; const hasScrollDown = endIndex < suggestions.length; const hasCounter = suggestions.length > MAX_SUGGESTIONS_TO_SHOW; return ( {activeHint && ( {activeHint} )} {hasScrollUp && } {visibleSuggestions.map((suggestion, index) => ( ))} {hasScrollDown && } {hasCounter && ( ({activeIndex + 1}/{suggestions.length}) )} ); }