import React from 'react'; import { Box, Text } from 'ink'; import type { CommandDef, CommandArgDef } from '@mosaicstack/types'; interface CommandAutocompleteProps { commands: CommandDef[]; selectedIndex: number; inputValue: string; // the current input after '/' } export function CommandAutocomplete({ commands, selectedIndex, inputValue, }: CommandAutocompleteProps) { if (commands.length === 0) return null; // Filter by inputValue prefix/fuzzy match const query = inputValue.startsWith('/') ? inputValue.slice(1) : inputValue; const filtered = filterCommands(commands, query); if (filtered.length === 0) return null; const clampedIndex = Math.min(selectedIndex, filtered.length - 1); const selected = filtered[clampedIndex]; return ( {filtered.slice(0, 8).map((cmd, i) => ( {i === clampedIndex ? '▶ ' : ' '}/{cmd.name} {cmd.aliases.length > 0 && ( ({cmd.aliases.map((a) => `/${a}`).join(', ')}) )} — {cmd.description} ))} {selected && selected.args && selected.args.length > 0 && ( /{selected.name} {getArgHint(selected.args)} — {selected.description} )} ); } function filterCommands(commands: CommandDef[], query: string): CommandDef[] { if (!query) return commands; const q = query.toLowerCase(); return commands.filter( (c) => c.name.includes(q) || c.aliases.some((a) => a.includes(q)) || c.description.toLowerCase().includes(q), ); } function getArgHint(args: CommandArgDef[]): string { if (!args || args.length === 0) return ''; return args.map((a) => (a.optional ? `[${a.name}]` : `<${a.name}>`)).join(' '); }