pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
96 lines (95 loc) • 4.28 kB
JavaScript
// Compact tool-result rendering (refs #345).
//
// The navigable/structural tools (module_report, read_symbol, read_enclosing,
// ast_grep_search, ast_grep_dump, ast_grep_outline) return large bodies that are
// useful to the MODEL but flood the user's terminal. The pi host renders a tool's
// `content` verbatim only when the tool defines no `renderResult` (the
// createResultFallback path in tool-execution.ts). By supplying a `renderResult`
// we decouple the two surfaces entirely:
// - `content` (returned from execute) is unchanged -> the model still gets the
// full payload.
// - `renderResult` is TUI-only -> the user sees a one-line summary by default,
// and the full output when the row is expanded (options.expanded), exactly
// like the built-in read/grep/bash tools.
//
// Design borrowed from the community renderer extensions pi-tool-display and
// pi-claude-style-tools (summary-by-default + expand-on-demand), but scoped to
// pi-lens's own tools and driven off structured `details` rather than blind
// truncation. Those extensions default to respecting a tool's own renderResult
// (overrideExistingRenderers === false), so these renderers win and still coexist
// with a globally-installed renderer extension.
import { Text } from "../clients/deps/pi-tui.js";
// pi-lens brand colour: blue characters on whatever background the pi tool shell
// paints (default success/error background is left untouched). Truecolor bold
// foreground, theme-independent so the summary reads as ours regardless of the
// active pi theme. We reset only the foreground (\x1b[39m) and bold (\x1b[22m) so
// the shell background still composites.
const PI_LENS_BLUE_FG = "\x1b[1m\x1b[38;2;96;165;250m"; // bold blue
const RESET_FG = "\x1b[39m\x1b[22m";
/** Join all text content blocks into the full model-facing string. */
export function fullTextOf(result) {
return (result.content ?? [])
.filter((c) => c.type === "text" && typeof c.text === "string")
.map((c) => c.text)
.join("\n");
}
/**
* Pure selection of what to display — exported separately so it can be unit
* tested without constructing a TUI component or a Theme.
*/
export function selectCompactText(result, args, expanded, summarize) {
const text = fullTextOf(result);
if (expanded) {
return {
text: text || "(no output)",
style: result.isError ? "error" : "output",
};
}
const lineCount = text ? text.split("\n").length : 0;
let summary;
try {
summary = summarize({
details: result.details,
args,
isError: result.isError === true,
text,
lineCount,
});
}
catch {
// Never let a summarizer bug blank the row — fall back to the first line.
summary = text.split("\n")[0] ?? "";
}
// Collapsed summaries render in pi-lens blue; errors stay theme-red.
return { text: summary, style: result.isError ? "error" : "brand" };
}
/** Apply a CompactStyle to text. `brand` uses raw blue ANSI; the rest defer to
* the theme so error-red and normal output stay consistent with the host. */
export function paintCompact(style, text, theme) {
if (style === "brand") {
return `${PI_LENS_BLUE_FG}${text}${RESET_FG}`;
}
const color = style === "error" ? "error" : "toolOutput";
return theme.fg(color, text);
}
/**
* Build a `renderResult` for a tool. `summarize` produces the one-line collapsed
* view from the structured result; the expanded view shows the full payload.
*/
export function compactRenderResult(summarize) {
return (result, options, theme, context) => {
const component = context.lastComponent instanceof Text
? context.lastComponent
: new Text("", 0, 0);
const { text, style } = selectCompactText(result, (context.args ?? {}), options.expanded === true, summarize);
component.setText(paintCompact(style, text, theme));
return component;
};
}
/** Shorten an absolute/relative path to its basename for the summary line. */
export function baseName(p) {
if (typeof p !== "string" || p.length === 0)
return "";
const parts = p.split(/[\\/]/);
return parts[parts.length - 1] || p;
}