83 lines
3.3 KiB
JavaScript
83 lines
3.3 KiB
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
|