fix(tui-go): make the write/edit output-panel number meaningful

The collapsed tool row showed "tool output (N chars)" where N was len() of the
raw unified diff — i.e. the diff blob's *byte* length (headers, @@, +/-/context
all counted), labelled "chars". That number corresponded to nothing the operator
cares about, and len() is bytes not characters.

- Summarise a write/edit by the diff's row count instead — "· diff (7 rows) —
  ^x to view" — matching exactly what the ^x modal reports. Non-diff output (the
  defensive fallback) now counts runes, not bytes, so "N chars" is truthful.
  Retire itoaLen (the byte-count-as-"len" footgun).
- Harden the (+a −b) line counts in diffSummary: guard the file header with the
  trailing space ("+++ "/"--- "), so a changed line whose content starts with
  "++"/"--" is counted instead of mistaken for a header. Apply the same guard in
  parseUnifiedDiff so such a line is rendered (and counted) rather than dropped.

tool_summary_test.go covers the diff-row summary, the rune (not byte) count, and
the ++/-- content-line edge case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 10:09:56 +00:00
parent 035f7df704
commit 4e4d7c186a
4 changed files with 65 additions and 5 deletions
+12 -2
View File
@@ -589,7 +589,7 @@ func (m Model) buildTranscriptRows(w int) ([]string, []int) {
rows = append(rows, t.span(s, t.P.Faint))
}
case "tool":
rows = append(rows, t.span("· tool output ("+itoaLen(e.Content)+" chars) — ^x to view", t.P.Dim))
rows = append(rows, t.span(toolRowSummary(e.Content), t.P.Dim))
case "narration_llm":
diamond := lipgloss.NewStyle().Foreground(t.P.Accent).Background(t.P.Bg).Bold(true).Render("◆")
lines := wrap(e.Content, w-2)
@@ -856,7 +856,17 @@ func itoa(n int) string {
return string(b[i:])
}
func itoaLen(s string) string { return itoa(len(s)) }
// toolRowSummary collapses a tool-output transcript entry into a one-line pointer. A
// write/edit entry holds a unified diff, so summarise it by what ^x actually shows — the
// diff's row count — instead of the raw diff byte length (which read as a meaningless
// "N chars": it counted +/-/@@/header bytes, not anything the operator cares about, and
// len() is bytes not characters). Non-diff output falls back to a true character count.
func toolRowSummary(content string) string {
if isUnifiedDiff(content) {
return "· diff (" + itoa(previewRowCount(content)) + " rows) — ^x to view"
}
return "· tool output (" + itoa(len([]rune(content))) + " chars) — ^x to view"
}
// metricsSuffix formats the faint latency+token annotation for a ROUTER turn.
func metricsSuffix(mtr *TurnMetrics) string {