diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 43a82da80..e3ecae06a 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixed +- Fixed edit tool diff rendering to wrap long diff lines with continuation gutters instead of truncating them at terminal width ([#578](https://github.com/can1357/oh-my-pi/issues/578)) - Fixed `--list-models` and `/model` provider filtering to hide models from disabled providers ([#588](https://github.com/can1357/oh-my-pi/issues/588)) - Fixed edit tool diffstats to use diff-specific add/remove theme colors instead of success/error status colors ([#589](https://github.com/can1357/oh-my-pi/issues/589)) diff --git a/packages/coding-agent/src/patch/shared.ts b/packages/coding-agent/src/patch/shared.ts index 7b877e6d9..0178629e8 100644 --- a/packages/coding-agent/src/patch/shared.ts +++ b/packages/coding-agent/src/patch/shared.ts @@ -3,7 +3,7 @@ */ import type { ToolCallContext } from "@oh-my-pi/pi-agent-core"; import type { Component } from "@oh-my-pi/pi-tui"; -import { Text } from "@oh-my-pi/pi-tui"; +import { Text, visibleWidth, wrapTextWithAnsi } from "@oh-my-pi/pi-tui"; import type { RenderResultOptions } from "../extensibility/custom-tools/types"; import type { FileDiagnosticsResult } from "../lsp"; import { renderDiff as renderDiffColored } from "../modes/components/diff"; @@ -222,6 +222,29 @@ function renderDiffSection( return text; } +function wrapEditRendererLine(line: string, width: number): string[] { + if (width <= 0) return [line]; + if (line.length === 0) return [""]; + + const startAnsi = line.match(/^((?:\x1b\[[0-9;]*m)*)/)?.[1] ?? ""; + const bodyWithReset = line.slice(startAnsi.length); + const body = bodyWithReset.endsWith("\x1b[39m") ? bodyWithReset.slice(0, -"\x1b[39m".length) : bodyWithReset; + const diffMatch = /^([+\-\s])(\s*\d+)\|(.*)$/s.exec(body); + + if (!diffMatch) { + return wrapTextWithAnsi(line, width); + } + + const [, marker, lineNum, content] = diffMatch; + const prefix = `${marker}${lineNum}|`; + const prefixWidth = visibleWidth(prefix); + const contentWidth = Math.max(1, width - prefixWidth); + const continuationPrefix = `${" ".repeat(Math.max(0, prefixWidth - 1))}|`; + const wrappedContent = wrapTextWithAnsi(content, contentWidth); + + return wrappedContent.map((segment, index) => `${startAnsi}${index === 0 ? prefix : continuationPrefix}${segment}\x1b[39m`); +} + export const editToolRenderer = { mergeCallAndResult: true, @@ -356,8 +379,7 @@ export const editToolRenderer = { ); } - const lines = - width > 0 ? text.split("\n").map(line => truncateToWidth(line, width, Ellipsis.Omit)) : text.split("\n"); + const lines = width > 0 ? text.split("\n").flatMap(line => wrapEditRendererLine(line, width)) : text.split("\n"); cached = { key, lines }; return lines; },