fix(tui): honor padded markdown width budgets
This commit is contained in:
@@ -12,7 +12,14 @@ import { normalizeToLF } from "../edit/normalize";
|
||||
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
||||
import type { Theme } from "../modes/theme/theme";
|
||||
import astEditDescription from "../prompts/tools/ast-edit.md" with { type: "text" };
|
||||
import { Ellipsis, fileHyperlink, framedBlock, renderStatusLine, truncateToWidth } from "../tui";
|
||||
import {
|
||||
Ellipsis,
|
||||
fileHyperlink,
|
||||
framedBlock,
|
||||
outputBlockContentWidth,
|
||||
renderStatusLine,
|
||||
truncateToWidth,
|
||||
} from "../tui";
|
||||
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
||||
import type { ToolSession } from ".";
|
||||
import { truncateForPrompt } from "./approval";
|
||||
@@ -695,7 +702,7 @@ export const astEditToolRenderer = {
|
||||
}
|
||||
return framedBlock(uiTheme, width => {
|
||||
const changeLines = buildChangeBody(changeGroups, Boolean(options.expanded), COLLAPSED_CHANGE_LIMIT, uiTheme);
|
||||
const innerWidth = Math.max(1, width - 3);
|
||||
const innerWidth = outputBlockContentWidth(width);
|
||||
const bodyLines = [...changeLines, ...extraLines].map(l => truncateToWidth(l, innerWidth, Ellipsis.Omit));
|
||||
while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type Component, padding, Text, visibleWidth } from "@oh-my-pi/pi-tui";
|
||||
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
||||
import type { Theme, ThemeColor } from "../modes/theme/theme";
|
||||
import { framedBlock, renderStatusLine } from "../tui";
|
||||
import { framedBlock, outputBlockContentWidth, renderStatusLine } from "../tui";
|
||||
import type {
|
||||
GhRunWatchFailedLogDetails,
|
||||
GhRunWatchJobDetails,
|
||||
@@ -364,7 +364,7 @@ function renderFallbackComponent(
|
||||
}
|
||||
|
||||
return framedBlock(theme, width => {
|
||||
const lineWidth = Math.max(1, (width || FALLBACK_WIDTH) - 3);
|
||||
const lineWidth = outputBlockContentWidth(width || FALLBACK_WIDTH);
|
||||
const expanded = options.expanded;
|
||||
const limit = expanded ? allLines.length : Math.min(allLines.length, PREVIEW_LIMITS.OUTPUT_EXPANDED);
|
||||
const visible = allLines.slice(0, limit);
|
||||
@@ -464,7 +464,7 @@ export const githubToolRenderer = {
|
||||
uiTheme,
|
||||
);
|
||||
return framedBlock(uiTheme, width => {
|
||||
const innerWidth = Math.max(1, (width || FALLBACK_WIDTH) - 3);
|
||||
const innerWidth = outputBlockContentWidth(width || FALLBACK_WIDTH);
|
||||
const sections = buildWatchSections(watch, uiTheme, options, innerWidth);
|
||||
return {
|
||||
header,
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
formatStatusIcon,
|
||||
replaceTabs,
|
||||
} from "../tools/render-utils";
|
||||
import { renderOutputBlock } from "./output-block";
|
||||
import { outputBlockContentWidth, renderOutputBlock } from "./output-block";
|
||||
import type { State } from "./types";
|
||||
|
||||
export interface CodeCellOptions {
|
||||
@@ -230,9 +230,9 @@ export function renderMarkdownCell(options: MarkdownCellOptions, theme: Theme):
|
||||
const { title, meta } = formatHeader(codeOptions, theme);
|
||||
const state = getState(options.status);
|
||||
|
||||
// Markdown component manages its own wrapping at the inner content width.
|
||||
// `renderOutputBlock` adds a `│ ` prefix + `│` suffix → 3 visible columns.
|
||||
const innerWidth = Math.max(20, width - 3);
|
||||
// Markdown component manages its own wrapping at the same inner width as
|
||||
// `renderOutputBlock`, so collapsed row caps are applied after final wrapping.
|
||||
const innerWidth = Math.max(20, outputBlockContentWidth(width));
|
||||
const allLines = content.trim() ? new Markdown(content, 0, 0, getMarkdownTheme()).render(innerWidth) : [];
|
||||
const maxContentLines = expanded ? allLines.length : Math.min(allLines.length, contentMaxLines);
|
||||
const contentLines = allLines.slice(0, maxContentLines);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { beforeAll, describe, expect, it } from "bun:test";
|
||||
import { stripVTControlCharacters } from "node:util";
|
||||
import { getThemeByName, initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import { renderMarkdownCell } from "@oh-my-pi/pi-coding-agent/tui/code-cell";
|
||||
import { renderOutputBlock } from "@oh-my-pi/pi-coding-agent/tui/output-block";
|
||||
|
||||
describe("renderOutputBlock", () => {
|
||||
@@ -36,4 +37,21 @@ describe("renderOutputBlock", () => {
|
||||
|
||||
expect(lines.filter(line => line.startsWith("│"))).toEqual(["│abcdefghijklmn│"]);
|
||||
});
|
||||
|
||||
it("budgets collapsed Markdown rows against the padded block width", async () => {
|
||||
const theme = (await getThemeByName("dark"))!;
|
||||
const lines = renderMarkdownCell(
|
||||
{
|
||||
content: "x".repeat(27),
|
||||
contentMaxLines: 1,
|
||||
status: "complete",
|
||||
title: "Read",
|
||||
width: 30,
|
||||
},
|
||||
theme,
|
||||
).map(line => stripVTControlCharacters(line));
|
||||
|
||||
expect(lines[1]).toBe(`│ ${"x".repeat(26)} │`);
|
||||
expect(lines[2]).toStartWith("│ … 1 more line");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2596,7 +2596,13 @@ export class Markdown implements Component, NativeScrollbackCommittedRows, Nativ
|
||||
// Use the list's start property (defaults to 1 for ordered lists)
|
||||
const startNumber = token.start ?? 1;
|
||||
const pushWrapped = (text: string, firstPrefix: string, continuationPrefix: string): void => {
|
||||
const bodyWidth = Math.max(1, width - visibleWidth(firstPrefix));
|
||||
const prefixWidth = visibleWidth(firstPrefix);
|
||||
if (prefixWidth >= width) {
|
||||
lines.push(truncateToWidth(firstPrefix, width, Ellipsis.Omit));
|
||||
lines.push(...wrapTextWithAnsi(text, Math.max(1, width)));
|
||||
return;
|
||||
}
|
||||
const bodyWidth = width - prefixWidth;
|
||||
const wrapped = wrapTextWithAnsi(text, bodyWidth);
|
||||
if (wrapped.length === 0) {
|
||||
lines.push(firstPrefix);
|
||||
|
||||
@@ -160,6 +160,15 @@ describe("Markdown component", () => {
|
||||
expect(plainLines.every(line => visibleWidth(line) <= 16)).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps list rows within width when the marker consumes the line", () => {
|
||||
const markdown = new Markdown("123456789. x", 0, 0, defaultMarkdownTheme);
|
||||
|
||||
const plainLines = markdown.render(8).map(line => stripVTControlCharacters(line).trimEnd());
|
||||
|
||||
expect(plainLines.every(line => visibleWidth(line) <= 8)).toBe(true);
|
||||
expect(plainLines.join("")).toContain("x");
|
||||
});
|
||||
|
||||
it("should maintain numbering when code blocks are not indented (LLM output)", () => {
|
||||
// When code blocks aren't indented, marked parses each item as a separate list.
|
||||
// We use token.start to preserve the original numbering.
|
||||
|
||||
Reference in New Issue
Block a user