Repository navigation
Conversation
WrapLine and splitLongWord measured text with len() on the ANSI-stripped string, i.e. in bytes. Any non-ASCII text (accented letters, em dashes, box-drawing characters, CJK, emoji) was overcounted, so lines that fit were wrapped early, and a long word was sliced at byte offsets, cutting multibyte runes in half and emitting invalid UTF-8 that renders as replacement characters in the TUI and in headless agent output. visibleLen now uses runewidth.StringWidth (already used by TruncateWithEllipsis in the same file), and splitLongWord walks runes, accumulating by display width, so chunks always end on rune boundaries and wide characters count as two columns.
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 2 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…-width runes U+2550 is East Asian ambiguous width, so under RUNEWIDTH_EASTASIAN=1 or a CJK locale it measures 2 columns and 30 of them split into 3 chunks at width 20, not 2. Compute the expected count from runewidth.RuneWidth.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
utils.WrapLine(used by the TUI content and log panels, headless agent output, and the markdown-render fallback) measures text in bytes rather than terminal columns. Any line with non-ASCII characters is wrapped too early, and a long non-ASCII token is sliced at byte offsets, cutting UTF-8 runes in half so the panel shows�replacement characters instead of the original text.Root cause
visibleLenreturnedlen(StripANSI(s)), so a 3-byte rune such as═,—, orécounted as three columns and a 2-column CJK character as three.splitLongWorddidstripped[:maxWidth]on the byte string, which produces chunks that end in the middle of a multibyte rune (invalid UTF-8).TruncateWithEllipsisin the same file already usesrunewidth, so the two wrapping helpers were simply inconsistent with it.Fix
internal/utils/text.govisibleLennow returnsrunewidth.StringWidth(StripANSI(s)).splitLongWordcompares withrunewidth.StringWidthand splits by walking runes and accumulating display width, so chunks always end on rune boundaries and wide characters count as two columns. The two near-identical byte-splitting loops collapse into one; the existing behaviour of stripping ANSI codes from a split word is unchanged.Tests
internal/utils/text_test.goTestWrapLine_MultibyteFitsWithinWidthIsNotWrapped:"héllo wörld"(11 columns, 13 bytes) at width 12 must stay on one line. Without the fix it wraps afterhéllo.TestWrapLine_LongMultibyteWordSplitsOnRuneBoundaries: 30═at width 20 must yield 2 valid-UTF-8 chunks that rejoin to the original. Without the fix it yields 5 chunks containing�.TestWrapLine_WideCharactersCountAsTwoColumns: 10 CJK characters at width 10 must yield 2 chunks of at most 10 columns. Without the fix it yields 3 corrupted chunks.Not changed
splitLongWordstill drops ANSI codes from a word it has to split (existingTODO); this change only fixes the width measurement and the rune-boundary cut.TestServerUnixModefails locally on a clean checkout when the clone lives under a long temp path (unix socket path limit); unrelated to this change.▚▚ Shipped by breken — self-healing software. This one's on us. breken.ai