capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
179 lines • 6.01 kB
JavaScript
import chalk from 'chalk';
import { TextProcessor } from './text-processor.js';
export class SuggestionEngine {
commands = [];
currentSuggestions = [];
selectedIndex = 0;
isShowing = false;
setCommands(commands) {
this.commands = commands;
}
updateSuggestions(input, minLength = 1) {
if (!input || input.length < minLength) {
this.clearSuggestions();
return;
}
const search = input.toLowerCase();
let matches = this.commands.filter(cmd => cmd.cmd.toLowerCase().startsWith(search));
matches = matches.sort((a, b) => a.cmd.localeCompare(b.cmd));
this.currentSuggestions = matches.map(match => ({
...match,
score: this.calculateMatchScore(match.cmd, search)
}));
if (this.currentSuggestions.length > 0) {
this.isShowing = true;
if (this.selectedIndex >= this.currentSuggestions.length) {
this.selectedIndex = this.currentSuggestions.length - 1;
}
}
else {
this.clearSuggestions();
}
}
calculateMatchScore(cmd, search) {
if (cmd.toLowerCase() === search) {
return 100;
}
if (cmd.toLowerCase().startsWith(search)) {
return 90 - (cmd.length - search.length);
}
const index = cmd.toLowerCase().indexOf(search);
if (index > 0) {
return 50 - index;
}
return 0;
}
clearSuggestions() {
this.currentSuggestions = [];
this.selectedIndex = 0;
this.isShowing = false;
}
selectPrevious() {
if (!this.isShowing || this.currentSuggestions.length === 0) {
return false;
}
if (this.selectedIndex > 0) {
this.selectedIndex--;
return true;
}
return false;
}
selectNext() {
if (!this.isShowing || this.currentSuggestions.length === 0) {
return false;
}
if (this.selectedIndex < this.currentSuggestions.length - 1) {
this.selectedIndex++;
return true;
}
return false;
}
getSelectedSuggestion() {
if (!this.isShowing || this.currentSuggestions.length === 0) {
return null;
}
return this.currentSuggestions[this.selectedIndex];
}
acceptSuggestion() {
const selected = this.getSelectedSuggestion();
if (selected) {
this.clearSuggestions();
return selected.cmd;
}
return null;
}
getCommonPrefix(input) {
const matches = this.currentSuggestions;
if (matches.length === 0) {
return null;
}
if (matches.length === 1) {
return matches[0].cmd;
}
let commonPrefix = matches[0].cmd;
for (let i = 1; i < matches.length; i++) {
const cmd = matches[i].cmd;
let j = 0;
while (j < commonPrefix.length &&
j < cmd.length &&
commonPrefix[j].toLowerCase() === cmd[j].toLowerCase()) {
j++;
}
commonPrefix = commonPrefix.substring(0, j);
}
return commonPrefix.length > input.length ? commonPrefix : null;
}
renderSuggestions(maxSuggestions, terminalWidth, searchTerm) {
const lines = [];
const search = searchTerm.toLowerCase();
const suggestionsToShow = Math.min(maxSuggestions, this.currentSuggestions.length);
for (let i = 0; i < suggestionsToShow; i++) {
const suggestion = this.currentSuggestions[i];
const isSelected = i === this.selectedIndex;
const cmd = suggestion.cmd;
const desc = suggestion.desc;
const matchIndex = cmd.toLowerCase().indexOf(search);
let formattedCmd;
if (matchIndex === 0) {
const matchedPart = cmd.substring(0, search.length);
const remainingPart = cmd.substring(search.length);
if (isSelected) {
formattedCmd = chalk.bold.yellow(matchedPart) + chalk.bold.cyan(remainingPart);
}
else {
formattedCmd = chalk.yellow(matchedPart) + chalk.gray(remainingPart);
}
}
else {
formattedCmd = isSelected ? chalk.bold.cyan(cmd) : chalk.gray(cmd);
}
let line;
if (isSelected) {
line = chalk.cyan(' ▶ ') + formattedCmd + chalk.gray(' - ' + desc);
}
else {
line = chalk.dim(' ') + formattedCmd + chalk.dim(' - ' + desc);
}
line = TextProcessor.fitText(line, terminalWidth);
lines.push(line);
}
return lines;
}
getState() {
return {
isShowing: this.isShowing,
suggestions: [...this.currentSuggestions],
selectedIndex: this.selectedIndex,
count: this.currentSuggestions.length
};
}
isShowingSuggestions() {
return this.isShowing;
}
getSuggestionCount() {
return this.currentSuggestions.length;
}
reset() {
this.clearSuggestions();
}
handleInputChange(input) {
if (input.startsWith('/')) {
this.updateSuggestions(input);
}
else {
this.clearSuggestions();
}
}
calculateSuggestionHeight(availableSpace, preferredMin = 3) {
if (!this.isShowing || this.currentSuggestions.length === 0) {
return 0;
}
const count = this.currentSuggestions.length;
if (count <= availableSpace) {
return count;
}
return Math.min(count, Math.max(preferredMin, availableSpace));
}
}
export const suggestionEngine = new SuggestionEngine();
//# sourceMappingURL=suggestion-engine.js.map