61a57c1d21
- Changed render methods to return component-owned `readonly string[]` rows. - Added `RenderStablePrefix` row reuse to avoid repainting unchanged streaming content. - Stabilized streaming gutters and spinner placement to reduce preview jitter and flicker. - Finalized commit-safe transcript behavior for tool-call previews and added streaming edge-case tests.
64 lines
2.0 KiB
TypeScript
64 lines
2.0 KiB
TypeScript
/**
|
|
* Shared utility for truncating text to visual lines (accounting for line wrapping).
|
|
* Used by both tool-execution.ts and bash-execution.ts for consistent behavior.
|
|
*/
|
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
|
|
export interface VisualTruncateResult {
|
|
/** The visual lines to display */
|
|
visualLines: readonly string[];
|
|
/** Number of visual lines that were skipped (hidden) */
|
|
skippedCount: number;
|
|
}
|
|
|
|
const textCache = new Map<number, Text>();
|
|
|
|
function getCachedText(paddingX: number): Text {
|
|
let text = textCache.get(paddingX);
|
|
if (!text) {
|
|
text = new Text("", paddingX, 0);
|
|
textCache.set(paddingX, text);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
/**
|
|
* Truncate text to a maximum number of visual lines (from the end).
|
|
* This accounts for line wrapping based on terminal width.
|
|
*
|
|
* @param text - The text content (may contain newlines)
|
|
* @param maxVisualLines - Maximum number of visual lines to show
|
|
* @param width - Terminal/render width
|
|
* @param paddingX - Horizontal padding for Text component (default 0).
|
|
* Use 0 when result will be placed in a Box (Box adds its own padding).
|
|
* Use 1 when result will be placed in a plain Container.
|
|
* @returns The truncated visual lines and count of skipped lines
|
|
*/
|
|
export function truncateToVisualLines(
|
|
text: string,
|
|
maxVisualLines: number,
|
|
width: number,
|
|
paddingX: number = 0,
|
|
): VisualTruncateResult {
|
|
if (!text) {
|
|
return { visualLines: [], skippedCount: 0 };
|
|
}
|
|
|
|
// Create a temporary Text component to render and get visual lines
|
|
const tempText = getCachedText(paddingX);
|
|
if (tempText.getText() !== text) {
|
|
tempText.setText(text);
|
|
}
|
|
const allVisualLines = tempText.render(width);
|
|
|
|
if (allVisualLines.length <= maxVisualLines) {
|
|
return { visualLines: allVisualLines, skippedCount: 0 };
|
|
}
|
|
|
|
// Take the last N visual lines
|
|
const truncatedLines = allVisualLines.slice(-maxVisualLines);
|
|
const skippedCount = allVisualLines.length - maxVisualLines;
|
|
|
|
return { visualLines: truncatedLines, skippedCount };
|
|
}
|