Skip to content

fix(mobile): stop cutting previews and transcripts through an emoji - #6001

Open
Chessing234 wants to merge 3 commits into
block:mainfrom
Chessing234:fix/mobile-truncate-by-characters
Open

fix(mobile): stop cutting previews and transcripts through an emoji#6001
Chessing234 wants to merge 3 commits into
block:mainfrom
Chessing234:fix/mobile-truncate-by-characters

Conversation

@Chessing234

@Chessing234 Chessing234 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

The mobile half of #5996, which fixed the same bug in desktop's display truncation. No issue filed.

Three places shorten user-facing text like this:

text.length > n ? '${text.substring(0, n)}…' : text

Both halves count UTF-16 code units. Anything outside the Basic Multilingual Plane — every emoji, and CJK Extension B — is a surrogate pair, so a cut landing inside one leaves a lone surrogate: a forum post ending "…thanks 🎉" cut at 200 shows …thanks �. The agent transcript does the same to a metadata section at 500 and a tool result at 2000, where model output carrying emoji is routine.

Mobile already knows this. The reminder preview in features/channels/message_actions.dart truncates with .characters.take(...), and shared/emoji/emoji_only.dart iterates .characters. characters is a direct dependency in pubspec.yaml. These three sites are the ones that hadn't caught up, so this shares the rule rather than introducing one.

truncateWithEllipsis also fixes something subtler than the split: the decision to truncate and the cut itself now count the same units. Previously length (code units) decided and substring (code units) cut, but the reader sees characters, so a string of 200 emoji was treated as 400 long and truncated when it didn't need to be.

Limits are unchanged — only where the cut lands.

Verification note (corrected). An earlier version of this description said there was no Flutter or Dart SDK on this machine. That was wrong — the repo pins both through hermit (./bin/dart, ./bin/flutter), which I had been using all along for ./bin/cargo without noticing. I am running the mobile checks and will post the results in the thread.

Sibling PR: #5998 fixes the avatar-initial half of the same class and adds mobile/lib/shared/text/initial.dart beside this file. They touch different lines and merge in either order.

Note: I'm an outside contributor, so the workflow runs here sit at action_required until a maintainer approves them; only the DCO check reports on its own.

`substring` and `length` count UTF-16 code units. Anything outside the Basic
Multilingual Plane — every emoji, and CJK Extension B — is stored as a
surrogate pair, so a cut that lands inside one leaves a lone surrogate: not a
character, and drawn as `�` at the end of the preview.

Counting characters also makes the decision to truncate and the cut itself
agree with each other, and with what the reader sees.

The reminder preview in `features/channels/message_actions.dart` already does
this with `.characters.take(...)`. This is that rule, shared, so the next
caller doesn't have to rediscover it.

No call sites yet.

Signed-off-by: Taksh <takshkothari09@gmail.com>
Three places shortened user-facing text with
`text.length > n ? '${text.substring(0, n)}…' : text`. Both halves count
UTF-16 code units, so a forum post ending "…thanks 🎉" cut at 200 loses half
the emoji and the card shows `…thanks �`. The agent transcript does the same
to a tool result at 500 and 2000 characters, where model output carrying
emoji is routine.

Route them through `truncateWithEllipsis`. The limits are unchanged — only
where the cut lands, and that the decision to cut now counts the same units
as the cut itself.

Signed-off-by: Taksh <takshkothari09@gmail.com>
@Chessing234
Chessing234 requested a review from a team as a code owner August 16, 2026 00:51

@themiguelamador themiguelamador left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grapheme-safe truncation implementation is correct and consistently applied, but the new test file is not in canonical Dart format, so just mobile-check fails (dart format --output=none --set-exit-if-changed reports test/shared/text/truncate_test.dart changed).\n\nI applied the formatter-only fix in signed commit 8a52c9402 and published it at https://github.com/Complear/buzz/tree/review/pr-6001-fix. Please cherry-pick that commit (GitHub rejected my direct push to the fork despite the PR advertising maintainer edits).\n\nVerification after the fix: focused truncation tests 6/6, full mobile suite 1,423/1,423, flutter analyze clean, Dart format check clean, file-size check clean, and git diff --check clean. No Flutter build/run/clean/upgrade command was used.

`dart format --output=none --set-exit-if-changed .` reported the new test
changed, so `just mobile-check` failed on this branch. I had hand-wrapped one
`expect` across two lines; the formatter joins it, since it fits.

Formatter output only — no assertion or behaviour change.

Signed-off-by: Taksh <takshkothari09@gmail.com>
@Chessing234

Copy link
Copy Markdown
Contributor Author

fixed in 4f93cf19. i couldn't cherry-pick 8a52c9402https://github.com/Complear/buzz 404s for me on both git ls-remote and the api, so it's private or gone. ran the formatter myself instead, which turned out better anyway: i'd have guessed wrong.

reproduced first — dart format --output=none --set-exit-if-changed . reported 1 changed. the culprit was one hand-wrapped expect:

-    expect(truncateWithEllipsis(body, 200, '...'),
-        '${'x' * 199}\u{1F389}...');
+    expect(truncateWithEllipsis(body, 200, '...'), '${'x' * 199}\u{1F389}...');

formatter output only, no assertion changed. i'd assumed it would split the call tall-style; it joins it, since it fits in 80.

verification, run here rather than taken on trust:

  • dart format --output=none --set-exit-if-changed . — clean
  • flutter analyze — no issues found
  • flutter test1,423 passed, which matches your count

i owe you a correction on this one. i said on this pr and on #5998 that there was no flutter or dart sdk on this machine and that i therefore couldn't verify mobile work. that was wrong — the repo pins both through hermit (./bin/dart, ./bin/flutter), and i'd been using ./bin/cargo all along without checking. i've edited both pr descriptions and deleted the comment that asserted it. thanks for carrying the verification while i had that wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants