feat(tui): add placeholder support to Input component

The Input component had no placeholder text support — when empty, it
showed only "> " with a blinking cursor and no hint of expected input.

The ExtensionInputComponent received a placeholder parameter but
discarded it (_placeholder with underscore = intentionally unused).

Fix: Input now has a public placeholder property. When value is empty,
renders the placeholder in dim text. ExtensionInputComponent passes
the placeholder through to Input.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
deseltrus 2026-03-15 07:23:55 +01:00
parent be2492b48d
commit b97a47db42
2 changed files with 15 additions and 1 deletions

View file

@ -33,7 +33,7 @@ export class ExtensionInputComponent extends Container implements Focusable {
constructor(
title: string,
_placeholder: string | undefined,
placeholder: string | undefined,
onSubmit: (value: string) => void,
onCancel: () => void,
opts?: ExtensionInputOptions,
@ -61,6 +61,9 @@ export class ExtensionInputComponent extends Container implements Focusable {
}
this.input = new Input();
if (placeholder) {
this.input.placeholder = placeholder;
}
this.addChild(this.input);
this.addChild(new Spacer(1));
this.addChild(new Text(`${keyHint("selectConfirm", "submit")} ${keyHint("selectCancel", "cancel")}`, 1, 0));

View file

@ -20,6 +20,7 @@ export class Input implements Component, Focusable {
private cursor: number = 0; // Cursor position in the value
public onSubmit?: (value: string) => void;
public onEscape?: () => void;
public placeholder: string = "";
/** Focusable interface - set by TUI when focus changes */
focused: boolean = false;
@ -440,6 +441,16 @@ export class Input implements Component, Focusable {
return [prompt];
}
// Show placeholder when value is empty
if (this.value === "" && this.placeholder) {
const placeholderText = this.placeholder.slice(0, availableWidth - 1);
const marker = this.focused ? CURSOR_MARKER : "";
const cursorChar = "\x1b[7m \x1b[27m"; // inverse space for cursor
const dimPlaceholder = `\x1b[2m${placeholderText}\x1b[22m`; // dim text
const padding = " ".repeat(Math.max(0, availableWidth - visibleWidth(placeholderText) - 1));
return [prompt + marker + cursorChar + dimPlaceholder + padding];
}
let visibleText = "";
let cursorDisplay = this.cursor;