diff --git a/packages/coding-agent/src/tui/tool-execution.ts b/packages/coding-agent/src/tui/tool-execution.ts index 6c4dc9d8..d33a5ed6 100644 --- a/packages/coding-agent/src/tui/tool-execution.ts +++ b/packages/coding-agent/src/tui/tool-execution.ts @@ -1,10 +1,11 @@ -import { Container, Markdown } from "@mariozechner/pi-tui"; +import { Container, Text } from "@mariozechner/pi-tui"; +import chalk from "chalk"; /** * Component that renders a tool call with its result (updateable) */ export class ToolExecutionComponent extends Container { - private markdown: Markdown; + private text: Text; private toolName: string; private args: any; private result?: { output: string; isError: boolean }; @@ -13,8 +14,8 @@ export class ToolExecutionComponent extends Container { super(); this.toolName = toolName; this.args = args; - this.markdown = new Markdown("", undefined, undefined, { r: 40, g: 40, b: 50 }); - this.addChild(this.markdown); + this.text = new Text("", 1, 1, { r: 40, g: 40, b: 50 }); + this.addChild(this.text); this.updateDisplay(); } @@ -29,17 +30,18 @@ export class ToolExecutionComponent extends Container { ? { r: 60, g: 40, b: 40 } : { r: 40, g: 50, b: 40 } : { r: 40, g: 40, b: 50 }; - this.markdown.setCustomBgRgb(bgColor); - this.markdown.setText(this.formatToolExecution()); + this.text.setCustomBgRgb(bgColor); + this.text.setText(this.formatToolExecution()); } private formatToolExecution(): string { - let text = ""; + // Start with blank line for spacing + let text = "\n"; // Format based on tool type if (this.toolName === "bash") { const command = this.args.command || ""; - text = `**$ ${command}**`; + text += chalk.bold(`$ ${command}`); if (this.result) { // Show output without code fences - more minimal const output = this.result.output.trim(); @@ -61,7 +63,7 @@ export class ToolExecutionComponent extends Container { } } else if (this.toolName === "read") { const path = this.args.path || ""; - text = `**read** \`${path}\``; + text += chalk.bold("read") + " " + path; if (this.result) { const lines = this.result.output.split("\n"); const maxLines = 10; @@ -81,25 +83,27 @@ export class ToolExecutionComponent extends Container { const path = this.args.path || ""; const content = this.args.content || ""; const lines = content.split("\n"); - text = `**write** \`${path}\``; + const totalLines = lines.length; + + text += chalk.bold("write") + " " + path; + if (totalLines > 10) { + text += ` (${totalLines} lines)`; + } + if (this.result) { - const totalLines = lines.length; - if (totalLines > 10) { - text += ` (${totalLines} lines)`; - } text += this.result.isError ? " ❌" : " ✓"; } } else if (this.toolName === "edit") { const path = this.args.path || ""; - text = `**edit** \`${path}\``; + text += chalk.bold("edit") + " " + path; if (this.result) { text += this.result.isError ? " ❌" : " ✓"; } } else { // Generic tool - text = `**${this.toolName}**\n\`\`\`json\n${JSON.stringify(this.args, null, 2)}\n\`\`\``; + text += chalk.bold(this.toolName) + "\n" + JSON.stringify(this.args, null, 2); if (this.result) { - text += `\n\`\`\`\n${this.result.output}\n\`\`\``; + text += "\n" + this.result.output; text += this.result.isError ? " ❌" : " ✓"; } } diff --git a/packages/coding-agent/src/tui/tui-renderer.ts b/packages/coding-agent/src/tui/tui-renderer.ts index 85524cfe..3a323a0a 100644 --- a/packages/coding-agent/src/tui/tui-renderer.ts +++ b/packages/coding-agent/src/tui/tui-renderer.ts @@ -1,4 +1,4 @@ -import type { Agent, AgentState, ThinkingLevel } from "@mariozechner/pi-agent"; +import type { Agent, AgentEvent, AgentState, ThinkingLevel } from "@mariozechner/pi-agent"; import type { AssistantMessage, Message } from "@mariozechner/pi-ai"; import type { SlashCommand } from "@mariozechner/pi-tui"; import { @@ -166,7 +166,7 @@ export class TuiRenderer { this.isInitialized = true; } - async handleEvent(event: import("@mariozechner/pi-agent").AgentEvent, state: AgentState): Promise { + async handleEvent(event: AgentEvent, state: AgentState): Promise { if (!this.isInitialized) { await this.init(); } diff --git a/packages/tui/src/components/text.ts b/packages/tui/src/components/text.ts index 79f805c5..b1b6946a 100644 --- a/packages/tui/src/components/text.ts +++ b/packages/tui/src/components/text.ts @@ -1,3 +1,4 @@ +import chalk from "chalk"; import type { Component } from "../tui.js"; import { visibleWidth } from "../utils.js"; @@ -8,16 +9,23 @@ export class Text implements Component { private text: string; private paddingX: number; // Left/right padding private paddingY: number; // Top/bottom padding + private customBgRgb?: { r: number; g: number; b: number }; // Cache for rendered output private cachedText?: string; private cachedWidth?: number; private cachedLines?: string[]; - constructor(text: string = "", paddingX: number = 1, paddingY: number = 1) { + constructor( + text: string = "", + paddingX: number = 1, + paddingY: number = 1, + customBgRgb?: { r: number; g: number; b: number }, + ) { this.text = text; this.paddingX = paddingX; this.paddingY = paddingY; + this.customBgRgb = customBgRgb; } setText(text: string): void { @@ -28,6 +36,14 @@ export class Text implements Component { this.cachedLines = undefined; } + setCustomBgRgb(customBgRgb?: { r: number; g: number; b: number }): void { + this.customBgRgb = customBgRgb; + // Invalidate cache when color changes + this.cachedText = undefined; + this.cachedWidth = undefined; + this.cachedLines = undefined; + } + render(width: number): string[] { // Check cache if (this.cachedLines && this.cachedText === this.text && this.cachedWidth === width) { @@ -91,20 +107,35 @@ export class Text implements Component { // Right padding to fill to width (accounting for left padding and content) const rightPadLength = Math.max(0, width - this.paddingX - visibleLength); const rightPad = " ".repeat(rightPadLength); - paddedLines.push(leftPad + line + rightPad); + let paddedLine = leftPad + line + rightPad; + + // Apply background color if specified + if (this.customBgRgb) { + paddedLine = chalk.bgRgb(this.customBgRgb.r, this.customBgRgb.g, this.customBgRgb.b)(paddedLine); + } + + paddedLines.push(paddedLine); } // Add top padding (empty lines) const emptyLine = " ".repeat(width); const topPadding: string[] = []; for (let i = 0; i < this.paddingY; i++) { - topPadding.push(emptyLine); + let emptyPaddedLine = emptyLine; + if (this.customBgRgb) { + emptyPaddedLine = chalk.bgRgb(this.customBgRgb.r, this.customBgRgb.g, this.customBgRgb.b)(emptyPaddedLine); + } + topPadding.push(emptyPaddedLine); } // Add bottom padding (empty lines) const bottomPadding: string[] = []; for (let i = 0; i < this.paddingY; i++) { - bottomPadding.push(emptyLine); + let emptyPaddedLine = emptyLine; + if (this.customBgRgb) { + emptyPaddedLine = chalk.bgRgb(this.customBgRgb.r, this.customBgRgb.g, this.customBgRgb.b)(emptyPaddedLine); + } + bottomPadding.push(emptyPaddedLine); } // Combine top padding, content, and bottom padding