From 2075eb4fb5997367a331bf4999983e2c5f7b2b5d Mon Sep 17 00:00:00 2001 From: Data-Wan <74923891+Data-Wan@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:27:19 -0300 Subject: [PATCH] fix: show read tool content preview by default Previously ReadToolGroupComponent completely ignored the expanded state and discarded result content, making read tool output invisible in the TUI (only showed a one-line path summary with no file content). Changes: - Store expanded state in #expanded field (was discarded with _ prefix) - Store result content text in ReadEntry.contentText (was thrown away) - Show a small code preview (3 lines) by default in collapsed mode - Show full file content when expanded via Ctrl+O - Uses renderCodeCell with PREVIEW_LIMITS.OUTPUT_COLLAPSED for consistent truncation with "... N more lines (Ctrl+O for more)" hint This brings read tool output in line with bash and grep tools which already show previews when collapsed. --- .../src/modes/components/read-tool-group.ts | 75 ++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/packages/coding-agent/src/modes/components/read-tool-group.ts b/packages/coding-agent/src/modes/components/read-tool-group.ts index e7ddc5b9d..abb0e1e84 100644 --- a/packages/coding-agent/src/modes/components/read-tool-group.ts +++ b/packages/coding-agent/src/modes/components/read-tool-group.ts @@ -1,7 +1,8 @@ import type { Component } from "@oh-my-pi/pi-tui"; import { Container, Text } from "@oh-my-pi/pi-tui"; -import { theme } from "../../modes/theme/theme"; -import { shortenPath } from "../../tools/render-utils"; +import { getLanguageFromPath, theme } from "../../modes/theme/theme"; +import { PREVIEW_LIMITS, shortenPath } from "../../tools/render-utils"; +import { renderCodeCell } from "../../tui"; import type { ToolExecutionHandle } from "./tool-execution"; type ReadRenderArgs = { @@ -35,11 +36,16 @@ type ReadEntry = { sel?: string; status: "pending" | "success" | "warning" | "error"; correctedFrom?: string; + contentText?: string; }; +/** Number of code lines to show in collapsed preview mode */ +const COLLAPSED_PREVIEW_LINES = PREVIEW_LIMITS.OUTPUT_COLLAPSED; + export class ReadToolGroupComponent extends Container implements ToolExecutionHandle { #entries = new Map(); #text: Text; + #expanded = false; constructor() { super(); @@ -81,6 +87,11 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa entry.correctedFrom = undefined; } entry.status = result.isError ? "error" : suffixResolution ? "warning" : "success"; + // Store the text content for preview/expanded display + const textContent = result.content?.find(c => c.type === "text")?.text; + if (textContent !== undefined) { + entry.contentText = textContent; + } this.#updateDisplay(); } @@ -88,7 +99,8 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa this.#updateDisplay(); } - setExpanded(_expanded: boolean): void { + setExpanded(expanded: boolean): void { + this.#expanded = expanded; this.#updateDisplay(); } @@ -99,8 +111,13 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa #updateDisplay(): void { const entries = [...this.#entries.values()]; + // Clear previous children and re-add the text node + this.clear(); + this.#text = new Text("", 0, 0); + if (entries.length === 0) { this.#text.setText(` ${theme.format.bullet} ${theme.fg("toolTitle", theme.bold("Read"))}`); + this.addChild(this.#text); return; } @@ -109,6 +126,12 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa const statusSymbol = this.#formatStatus(entry.status); const pathDisplay = this.#formatPath(entry); this.#text.setText(` ${statusSymbol} ${theme.fg("toolTitle", theme.bold("Read"))} ${pathDisplay}`.trimEnd()); + this.addChild(this.#text); + + // Show content preview (collapsed) or full content (expanded) + if (entry.contentText !== undefined) { + this.#addContentPreview(entry); + } return; } @@ -123,6 +146,52 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa } this.#text.setText(lines.join("\n")); + this.addChild(this.#text); + + // Show content for each entry: full when expanded, preview when collapsed + for (const entry of entries) { + if (entry.contentText !== undefined) { + this.#addContentPreview(entry); + } + } + } + + /** + * Add a code-cell content preview below the entry summary. + * When collapsed: shows first COLLAPSED_PREVIEW_LINES lines with "… N more lines (Ctrl+O for more)" hint. + * When expanded: shows full content. + */ + #addContentPreview(entry: ReadEntry): void { + const lang = getLanguageFromPath(entry.path); + const filePath = shortenPath(entry.path); + const title = filePath ? `Read ${filePath}` : "Read"; + let cachedWidth: number | undefined; + let cachedLines: string[] | undefined; + const expanded = this.#expanded; + const component: Component = { + render: (width: number) => { + if (cachedLines && cachedWidth === width) return cachedLines; + cachedLines = renderCodeCell( + { + code: entry.contentText ?? "", + language: lang, + title, + status: entry.status === "error" ? "error" : entry.status === "pending" ? "pending" : "complete", + expanded, + codeMaxLines: expanded ? undefined : COLLAPSED_PREVIEW_LINES, + width, + }, + theme, + ); + cachedWidth = width; + return cachedLines; + }, + invalidate: () => { + cachedWidth = undefined; + cachedLines = undefined; + }, + }; + this.addChild(component); } #formatPath(entry: ReadEntry): string {