feat(hooks): add setWidget API for multi-line status displays

- ctx.ui.setWidget(key, lines) for multi-line displays above editor
- Widgets appear below 'Working...' indicator, above editor
- Supports ANSI styling including strikethrough
- Added theme.strikethrough() method
- Plan-mode hook now shows todo list with checkboxes
- Completed items show checked box and strikethrough text
This commit is contained in:
Helmut Januschka 2026-01-03 16:01:05 +01:00 committed by Mario Zechner
parent 537d672f17
commit dc44816051
11 changed files with 127 additions and 6 deletions

View file

@ -141,6 +141,10 @@ export class InteractiveMode {
private hookInput: HookInputComponent | undefined = undefined;
private hookEditor: HookEditorComponent | undefined = undefined;
// Hook widgets (multi-line status displays)
private hookWidgets = new Map<string, string[]>();
private widgetContainer!: Container;
// Custom tools for custom rendering
private customTools: Map<string, LoadedCustomTool>;
@ -171,6 +175,7 @@ export class InteractiveMode {
this.chatContainer = new Container();
this.pendingMessagesContainer = new Container();
this.statusContainer = new Container();
this.widgetContainer = new Container();
this.keybindings = KeybindingsManager.create();
this.editor = new CustomEditor(getEditorTheme(), this.keybindings);
this.editorContainer = new Container();
@ -326,6 +331,7 @@ export class InteractiveMode {
this.ui.addChild(this.chatContainer);
this.ui.addChild(this.pendingMessagesContainer);
this.ui.addChild(this.statusContainer);
this.ui.addChild(this.widgetContainer);
this.ui.addChild(new Spacer(1));
this.ui.addChild(this.editorContainer);
this.ui.addChild(this.footer);
@ -614,6 +620,40 @@ export class InteractiveMode {
this.ui.requestRender();
}
/**
* Set a hook widget (multi-line status display).
*/
private setHookWidget(key: string, lines: string[] | undefined): void {
if (lines === undefined) {
this.hookWidgets.delete(key);
} else {
this.hookWidgets.set(key, lines);
}
this.renderWidgets();
}
/**
* Render all hook widgets to the widget container.
*/
private renderWidgets(): void {
if (!this.widgetContainer) return;
this.widgetContainer.clear();
if (this.hookWidgets.size === 0) {
this.ui.requestRender();
return;
}
// Render each widget
for (const [_key, lines] of this.hookWidgets) {
for (const line of lines) {
this.widgetContainer.addChild(new Text(line, 1, 0));
}
}
this.ui.requestRender();
}
/**
* Create the HookUIContext for hooks and tools.
*/
@ -624,6 +664,7 @@ export class InteractiveMode {
input: (title, placeholder) => this.showHookInput(title, placeholder),
notify: (message, type) => this.showHookNotify(message, type),
setStatus: (key, text) => this.setHookStatus(key, text),
setWidget: (key, lines) => this.setHookWidget(key, lines),
custom: (factory) => this.showHookCustom(factory),
setEditorText: (text) => this.editor.setText(text),
getEditorText: () => this.editor.getText(),