Files
stack/packages/cli/src/tui/components/command-autocomplete.tsx
Jarvis 774b76447d
Some checks failed
ci/woodpecker/pr/ci Pipeline failed
ci/woodpecker/push/ci Pipeline failed
fix: rename all packages from @mosaic/* to @mosaicstack/*
- Updated all package.json name fields and dependency references
- Updated all TypeScript/JavaScript imports
- Updated .woodpecker/publish.yml filters and registry paths
- Updated tools/install.sh scope default
- Updated .npmrc registry paths (worktree + host)
- Enhanced update-checker.ts with checkForAllUpdates() multi-package support
- Updated CLI update command to show table of all packages
- Added KNOWN_PACKAGES, formatAllPackagesTable, getInstallAllCommand
- Marked checkForUpdate() with @deprecated JSDoc

Closes #391
2026-04-04 21:43:23 -05:00

67 lines
2.1 KiB
TypeScript

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 (
<Box flexDirection="column" borderStyle="round" borderColor="gray" paddingX={1}>
{filtered.slice(0, 8).map((cmd, i) => (
<Box key={`${cmd.execution}-${cmd.name}`}>
<Text color={i === clampedIndex ? 'cyan' : 'white'} bold={i === clampedIndex}>
{i === clampedIndex ? '▶ ' : ' '}/{cmd.name}
</Text>
{cmd.aliases.length > 0 && (
<Text color="gray"> ({cmd.aliases.map((a) => `/${a}`).join(', ')})</Text>
)}
<Text color="gray"> {cmd.description}</Text>
</Box>
))}
{selected && selected.args && selected.args.length > 0 && (
<Box marginTop={1} borderStyle="single" borderColor="gray" paddingX={1}>
<Text color="yellow">
/{selected.name} {getArgHint(selected.args)}
</Text>
<Text color="gray"> {selected.description}</Text>
</Box>
)}
</Box>
);
}
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(' ');
}