Repository navigation
fix(react-email): avoid splitting surrogate pairs when truncating Preview text - #3761
RaphaelFakhri wants to merge 2 commits into
Conversation
🦋 Changeset detectedLatest commit: 9b5a540 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@RaphaelFakhri is attempting to deploy a commit to the resend Team on Vercel. A member of the Team first needs to authorize it. |
dielduarte
left a comment
There was a problem hiding this comment.
Hey, @RaphaelFakhri thanks for the contributions.
Similar requests as your CLI PR
1 - Cut on whole visible characters (graphemes), not just surrogate pairs. The current check handles simple emoji but still splits emoji made of several characters. At the 200-character cut:
- 👨👩👧 becomes 👨 (a stray invisible joiner is left)
- 🇺🇸 becomes 🇺 (a lone regional indicator, shown as a boxed "U")
- 👍🏽 becomes 👍 (the skin tone is silently dropped)
Intl.Segmenter is built in and handles all of these.
-
Test the multi-part emoji at the cut point. Please add cases for a family (👨👩👧), a flag (🇺🇸) and a skin-tone emoji (👍🏽) placed so they straddle character 200. Each should be either kept
-
Check for any unpaired surrogate. not.toContain('\une high surrogate. A regex catches all of them.
-
Return early when the text fits. Checking text.length <= PREVIEW_MAX_LENGTH first reads more clearly than slicing and checking the length last, and it skips the work for short previews, which are most of them.
Cut on whole visible characters with Intl.Segmenter so families, flags and skin-tone emoji are kept or dropped whole, return early when the text fits, and test the cut point with an unpaired-surrogate check.
25bfd68 to
9b5a540
Compare
|
Thanks, done. Preview now returns early when the text fits and otherwise cuts at 200 on graphemes. The tests slide a family, a flag and a skin-tone emoji across character 200 and check for unpaired surrogates. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
|
Hey @RaphaelFakhri this is pretty much the same comment as the CLI one: resend/resend-cli#395 (comment) After digging deeper, we haven't found an approach that solves this fully without trade-offs we're not comfortable shipping yet: Code points (for...of): no more broken �, but multi-part emoji (families, flags, skin tones) can still be cut to part of themselves. Because of that, I'm going to close this PR for now. This isn't a "no" to the problem: it's real, and we want to fix it properly. If you have ideas or want to keep exploring, we'd love to keep the discussion going on feel free to open a dicussion or even new prs based on this one, and any future PRs are very welcome. Thanks again for caring about this! |
Summary
<Preview>truncates its text withsubstring(0, 200). When an emoji straddles the limit, the cut leaves a lone high surrogate, which shows up as a replacement character in the<title>and the preview text. The fix drops the dangling high surrogate after truncation.Changes
truncatehelper inpreview.tsxthat removes a trailing high surrogate when the text was cut.preview.spec.tsx.react-email.Testing
vitest run src/components/preview: 6 passed.biome checkis clean for the changed files.Checklist
Summary by cubic
Fixes
react-email's<Preview>truncation so emoji aren't cut in half at the 200-character limit. Truncation now cuts on whole graphemes withIntl.Segmenter, so families, flags, and skin-tone emoji are kept or dropped whole instead of leaving a lone surrogate that renders as a replacement character.truncatehelper inpreview.tsxand a regression test covering multiple emoji types at the cut point.Written for commit 9b5a540. Summary will update on new commits.