Extension UI dialogs (select, confirm, input) now support a timeout option that auto-dismisses with a live countdown display. Simpler alternative to manually managing AbortSignal for timed dialogs. Also adds ExtensionUIDialogOptions type export and updates RPC mode to forward timeout to clients.
75 lines
2.0 KiB
TypeScript
75 lines
2.0 KiB
TypeScript
/**
|
|
* Simple text input component for extensions.
|
|
*/
|
|
|
|
import { Container, getEditorKeybindings, Input, Spacer, Text, type TUI } from "@mariozechner/pi-tui";
|
|
import { theme } from "../theme/theme.js";
|
|
import { CountdownTimer } from "./countdown-timer.js";
|
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
|
|
export interface ExtensionInputOptions {
|
|
tui?: TUI;
|
|
timeout?: number;
|
|
}
|
|
|
|
export class ExtensionInputComponent extends Container {
|
|
private input: Input;
|
|
private onSubmitCallback: (value: string) => void;
|
|
private onCancelCallback: () => void;
|
|
private titleText: Text;
|
|
private baseTitle: string;
|
|
private countdown: CountdownTimer | undefined;
|
|
|
|
constructor(
|
|
title: string,
|
|
_placeholder: string | undefined,
|
|
onSubmit: (value: string) => void,
|
|
onCancel: () => void,
|
|
opts?: ExtensionInputOptions,
|
|
) {
|
|
super();
|
|
|
|
this.onSubmitCallback = onSubmit;
|
|
this.onCancelCallback = onCancel;
|
|
this.baseTitle = title;
|
|
|
|
this.addChild(new DynamicBorder());
|
|
this.addChild(new Spacer(1));
|
|
|
|
this.titleText = new Text(theme.fg("accent", 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", `${this.baseTitle} (${s}s)`)),
|
|
() => this.onCancelCallback(),
|
|
);
|
|
}
|
|
|
|
this.input = new Input();
|
|
this.addChild(this.input);
|
|
this.addChild(new Spacer(1));
|
|
this.addChild(new Text(theme.fg("dim", "enter submit esc cancel"), 1, 0));
|
|
this.addChild(new Spacer(1));
|
|
this.addChild(new DynamicBorder());
|
|
}
|
|
|
|
handleInput(keyData: string): void {
|
|
const kb = getEditorKeybindings();
|
|
if (kb.matches(keyData, "selectConfirm") || keyData === "\n") {
|
|
this.onSubmitCallback(this.input.getValue());
|
|
} else if (kb.matches(keyData, "selectCancel")) {
|
|
this.onCancelCallback();
|
|
} else {
|
|
this.input.handleInput(keyData);
|
|
}
|
|
}
|
|
|
|
dispose(): void {
|
|
this.countdown?.dispose();
|
|
}
|
|
}
|