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.
This commit is contained in:
Data-Wan
2026-04-18 22:30:23 +02:00
committed by can1357
parent 2921332c44
commit 2075eb4fb5
@@ -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<string, ReadEntry>();
#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 {