/**
* @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})
)}
);
}