@earendil-works/pi-coding-agent
Version:
Coding agent CLI with read, bash, edit, write tools and session management
83 lines • 3.35 kB
JavaScript
/**
* Generic selector component for extensions.
* Displays a list of string options with keyboard navigation.
*/
import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
import { theme } from "../theme/theme.js";
import { CountdownTimer } from "./countdown-timer.js";
import { DynamicBorder } from "./dynamic-border.js";
import { keyHint, rawKeyHint } from "./keybinding-hints.js";
export class ExtensionSelectorComponent extends Container {
options;
selectedIndex = 0;
listContainer;
onSelectCallback;
onCancelCallback;
titleText;
baseTitle;
countdown;
onToggleToolsExpanded;
constructor(title, options, onSelect, onCancel, opts) {
super();
this.options = options;
this.onSelectCallback = onSelect;
this.onCancelCallback = onCancel;
this.onToggleToolsExpanded = opts?.onToggleToolsExpanded;
this.baseTitle = title;
this.addChild(new DynamicBorder());
this.addChild(new Spacer(1));
this.titleText = new Text(theme.fg("accent", theme.bold(title)), 1, 0);
this.addChild(this.titleText);
this.addChild(new Spacer(1));
if (opts?.timeout && opts.timeout > 0 && opts.tui) {
this.countdown = new CountdownTimer(opts.timeout, opts.tui, (s) => this.titleText.setText(theme.fg("accent", theme.bold(`${this.baseTitle} (${s}s)`))), () => this.onCancelCallback());
}
this.listContainer = new Container();
this.addChild(this.listContainer);
this.addChild(new Spacer(1));
this.addChild(new Text(rawKeyHint("↑↓", "navigate") +
" " +
keyHint("tui.select.confirm", "select") +
" " +
keyHint("tui.select.cancel", "cancel"), 1, 0));
this.addChild(new Spacer(1));
this.addChild(new DynamicBorder());
this.updateList();
}
updateList() {
this.listContainer.clear();
for (let i = 0; i < this.options.length; i++) {
const isSelected = i === this.selectedIndex;
const text = isSelected
? theme.fg("accent", "→ ") + theme.fg("accent", this.options[i])
: ` ${theme.fg("text", this.options[i])}`;
this.listContainer.addChild(new Text(text, 1, 0));
}
}
handleInput(keyData) {
const kb = getKeybindings();
if (kb.matches(keyData, "app.tools.expand")) {
this.onToggleToolsExpanded?.();
}
else if (kb.matches(keyData, "tui.select.up") || keyData === "k") {
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
this.updateList();
}
else if (kb.matches(keyData, "tui.select.down") || keyData === "j") {
this.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1);
this.updateList();
}
else if (kb.matches(keyData, "tui.select.confirm") || keyData === "\n") {
const selected = this.options[this.selectedIndex];
if (selected)
this.onSelectCallback(selected);
}
else if (kb.matches(keyData, "tui.select.cancel")) {
this.onCancelCallback();
}
}
dispose() {
this.countdown?.dispose();
}
}
//# sourceMappingURL=extension-selector.js.map