mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 20:01:24 +00:00
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:
parent
537d672f17
commit
dc44816051
11 changed files with 127 additions and 6 deletions
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -376,6 +376,10 @@ export class Theme {
|
|||
return chalk.inverse(text);
|
||||
}
|
||||
|
||||
strikethrough(text: string): string {
|
||||
return chalk.strikethrough(text);
|
||||
}
|
||||
|
||||
getFgAnsi(color: ThemeColor): string {
|
||||
const ansi = this.fgColors.get(color);
|
||||
if (!ansi) throw new Error(`Unknown theme color: ${color}`);
|
||||
|
|
|
|||
|
|
@ -131,6 +131,17 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|||
} as RpcHookUIRequest);
|
||||
},
|
||||
|
||||
setWidget(key: string, lines: string[] | undefined): void {
|
||||
// Fire and forget - host can implement widget display
|
||||
output({
|
||||
type: "hook_ui_request",
|
||||
id: crypto.randomUUID(),
|
||||
method: "setWidget",
|
||||
widgetKey: key,
|
||||
widgetLines: lines,
|
||||
} as RpcHookUIRequest);
|
||||
},
|
||||
|
||||
async custom() {
|
||||
// Custom UI not supported in RPC mode
|
||||
return undefined as never;
|
||||
|
|
|
|||
|
|
@ -189,6 +189,13 @@ export type RpcHookUIRequest =
|
|||
notifyType?: "info" | "warning" | "error";
|
||||
}
|
||||
| { type: "hook_ui_request"; id: string; method: "setStatus"; statusKey: string; statusText: string | undefined }
|
||||
| {
|
||||
type: "hook_ui_request";
|
||||
id: string;
|
||||
method: "setWidget";
|
||||
widgetKey: string;
|
||||
widgetLines: string[] | undefined;
|
||||
}
|
||||
| { type: "hook_ui_request"; id: string; method: "set_editor_text"; text: string };
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue