Repository navigation
fix(ui): truncate previews by rune, not byte, so CJK output stays readable - #40
Merged
Merged
Conversation
Owner
|
The fix is correct, the tests check real behaviour, and every change is an improvement on master. Nothing blocks it. |
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.
Summary
/skillslist, and the "last 5 messages" section of an MCPsession_load— cut its text with a byte slice (s[:max]), so non-ASCII text was sliced inside a character. The fragment carried a half-finished UTF-8 sequence and the terminal rendered�.internal/textutil.Truncatekeeps the output valid UTF-8, keeps the full rune budget, and marks a cut with.... The three call sites use it, and the three duplicate byte-slicing helpers are gone.Reproduction
A tool result of 160 Chinese characters (480 bytes), rendered by
RenderToolOutput("Bash", out, false).Before (this branch's parent):
\xe6\x96is the first two bytes of the next中: the line ends in a replacement character and holds 26 characters where the 80-rune budget promised 80.After:
80 runes on the line: 77 characters plus the ellipsis.
The same byte cut appeared in the skill list (
truncateSkillDesc, 80 bytes of a description) and in the MCP session summary (truncate(msg.Content, 200), which also overshot its own limit by the three ellipsis bytes).Tests
internal/textutil/textutil_test.go— ASCII, Chinese, Japanese and emoji cases; the result is always valid UTF-8, never exceeds the rune budget, and a limit at or below the ellipsis length cannot overflow either.internal/repl/render_test.go— the operator-visible boundary: a 160-character Chinese tool result and a Chinese error message both render as valid UTF-8 with noU+FFFD, and the preview keeps all 80 runes.The two boundary tests fail on
masterfor the intended reason (RenderToolOutput returned invalid UTF-8: "...\xe6\x96...") and pass on this branch.Notes
truncateis deliberately left alone: its message says[N more bytes]and it caps a payload budget, so it counts bytes on purpose. It is not display text.truncateLoghad no call sites and was removed with the other duplicates.