Repository navigation
fix(emoji): send messages without waiting for community emoji - #2871
johnmatthewtennant wants to merge 16 commits into
Conversation
A time such as `~19:25Z:` contains `:25Z:`, which the shared shortcode matcher treated as an emoji reference. While the community catalog was loading or had failed, any send with such text threw "Community emoji isn't ready" and kept the draft. A colon that follows a letter, digit or underscore no longer opens a shortcode, matching common emoji tokenizers. Standalone, chained and overlapping codes still match, so real emoji references keep waiting for the catalog. Co-authored-by: Thomas <ef494d9d97846d01e6a8bf964c4cf817a3303cbc4fa1e2b5a88f2a0b4055a94f@buzz.block.builderlab.xyz> Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
Co-authored-by: Thomas <ef494d9d97846d01e6a8bf964c4cf817a3303cbc4fa1e2b5a88f2a0b4055a94f@buzz.block.builderlab.xyz> Signed-off-by: John Tennant <jtennant@block.xyz>
Co-authored-by: Thomas <ef494d9d97846d01e6a8bf964c4cf817a3303cbc4fa1e2b5a88f2a0b4055a94f@buzz.block.builderlab.xyz> Signed-off-by: John Tennant <jtennant@block.xyz>
Co-authored-by: Thomas <ef494d9d97846d01e6a8bf964c4cf817a3303cbc4fa1e2b5a88f2a0b4055a94f@buzz.block.builderlab.xyz> Signed-off-by: John Tennant <jtennant@block.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The composer decorated custom emoji with its own regex, so word-adjacent codes such as nice:party: showed the emoji while the shared matcher sent them as literal text. Drive the decoration from emojiMatches instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed f5c1e8a4899d132f17f112b7edf6b4957bb9e745 against target/merge-base d3d69aaa24628cb43036cfc106f77de140b90c38. No required changes found. The optional Unicode boundary refinement is noted inline; it preserves existing behavior and does not block this fix.
Coverage: catalog loading/retry/disposal and send callers; shared shortcode parsing; edit mapping order/deletion handling; composer preview, edit and recovery paths; changed regression tests. The public description and both screenshots were inspected with no concrete disclosure finding.
Source-only: no tests, builds, app or native/browser workflows were run. The reported web reproduction was not independently verified, and the description explicitly leaves human testing outstanding. This is a COMMENT review, not approval or runtime acceptance.
The letter or digit boundary read one UTF-16 unit, so an astral letter or digit such as 𝐀 or 𝟙 left a lone low surrogate that never matched, and 𝐀:party: still tagged and rendered the emoji. Test the preceding code point instead. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
A decomposed word such as cafe followed by U+0301 ends in a combining mark, which is not a letter, so café:party: still tagged the emoji mid-word. Skip trailing marks before testing the boundary, with a sticky lookbehind that reads only as far back as it needs. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
A tagless edit adopted the newest tagged edit's mapping wholesale, so after 👋 became :party: and a cold edit restored 👋, only the original carried wave and it rendered as text. Merge mappings per shortcode, newest edit first and the original last. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed 5304a1d741a3ac1f4517e0ca87d77d2563c023d6 against target/merge-base d3d69aaa24628cb43036cfc106f77de140b90c38, focusing on the changes since reviewed f5c1e8a4899d132f17f112b7edf6b4957bb9e745. No new actionable findings.
The prior optional Unicode boundary note is addressed in source: the matcher handles full code points and trailing combining marks, with regression cases added. The per-shortcode edit fold preserves the newest valid explicit mapping in the existing authorized, non-deleted edit order, then falls back to the original for other codes. Prior unchanged catalog/retry/send coverage is retained. The current description and unchanged screenshots were checked.
Source-only: no tests, benchmarks, builds or app workflows were executed. The author’s reported testing was not independently verified; native/browser behavior and human acceptance remain unverified. This COMMENT is not approval.
|
🤖 Drip design review at This repairs sending while optional emoji loads or fails, keeps timestamps and embedded word text literal, and preserves each shortcode’s latest image mapping through edits. Both before/after screenshots were inspected; the light-theme after image shows the timestamp message and an empty composer. Not checked: tests, live/native sending, dark mode, narrow or enlarged layouts, keyboard/focus, long content, or agent-heavy conversations. Human testing remains outstanding per the PR description. |
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 One picker regression should be fixed before merge.
The picker inserts a custom emoji's literal at the caret, so picking :party: right after "nice" produced "nice:party:", which the word boundary rule reads as text and publishes without its emoji tag. The composer now adds a space before an inserted shortcode when the text before the caret ends a word. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source follow-up via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed e61db030a4d61a07800f2e585df0e804f4af847f against target/merge-base d3d69aaa24628cb43036cfc106f77de140b90c38, focusing on the insertion change since 5304a1d741a3ac1f4517e0ca87d77d2563c023d6. One P2 finding is detailed inline: the picker separator also changes clipboard text through the shared insertion path. Please preserve verbatim paste while retaining picker-after-word spacing.
The shared Unicode matcher, selection/range handling, insertion limits and undo were reviewed. Prior unchanged catalog/retry/send/edit-mapping coverage is retained. The current public description and both unchanged screenshots were checked with no new disclosure finding.
Source-only: no tests, builds or app workflows were executed. The author’s reported tests were not independently verified; native/browser behavior and human acceptance remain unverified. This is a COMMENT review, not approval.
The picker spacing from e61db03 ran on every rangeless insert, so pasting ":30:00" after "14" produced "14 :30:00", breaking the verbatim paste contract. Paste now bypasses it, and a caret inside inline code keeps an inserted shortcode literal. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
The separator read inline tokens as U+FFFC, so picking :party: right after a mention chip with no space produced @name:party:, which stays literal. It now reads each token's source text. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
A keycap such as 1️⃣ ends with an enclosing mark, so the word-end check read it as a digit word and left 1️⃣:party: literal, while the composer's completion still offered that chain. Enclosing marks no longer extend a word. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
The fold merged every edit's emoji tags, so a shortcode removed by the newest edit kept its mapping, and Send to channel republished it. The merge now keeps only shortcodes the folded content still references. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, with independent Rocket, Mantis and Groot coverage.
Reviewed 1d30546a2f58be6fd1021649c5fe77bf9406187c against target/merge-base d3d69aaa24628cb43036cfc106f77de140b90c38, focusing on the delta from e61db030a4d61a07800f2e585df0e804f4af847f and retaining unchanged prior coverage.
The prior clipboard mutation is fixed: normal paste and Paste and Match Style bypass spacing, and explicit inline-code marks now do too. One P2 remains in the code-preservation path: picker insertion inside Markdown code restored for editing still adds a space. Details inline.
Optional cost cleanup: fold.ts computes referencedEmoji(content) inside the mapping filter; computing a referenced-code set once avoids reparsing the same content per historical mapping. Runtime impact was not measured.
Source-only. We did not execute tests, builds or native/browser workflows. The PR description and both attached screenshots were inspected; author-reported tests and human acceptance remain unverified.
Editing a sent message restores its Markdown source as plain text, so inline and fenced code carry no code mark and the picker still added a space inside them. Skip the separator inside Markdown literal ranges of the projected source, as formatting already does. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
Find the referenced shortcodes once per message, and only when it has emoji mappings, instead of once per mapping. Co-authored-by: Xander <ff30c859b96bd5233ffaf5d071200458f1274bd0dab03589214cf1055198491d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: John Tennant <jtennant@block.xyz>
Why
Sending a message failed with "Community emoji isn't ready. Your draft is kept; try sending again shortly." (or "Community emoji unavailable") whenever the community's custom emoji catalog was still loading or had failed. Nothing published. Two things caused it:
emoji-directory.tstags()threw while the catalog wasn't ready, so any message with a shortcode candidate was refused, including replies, edits and attachment publications.:25Z:in19:25Z:and:30:in14:30:00as emoji references. Agents and people write times like this constantly, so ordinary status messages hit the first problem even though they reference no emoji.What
emojitags, and a send after a failed read still starts one shared retry. A loaded catalog still pins each code to its original URL. The composer's "Retry message preparation" button is gone because nothing is refused anymore.:party:), chained (:party::wave:) and overlapping (:unknown:party:) codes still match. The renderer, tagging,referencedEmojiand agent sends share the matcher, so sent tags and rendering agree. Text such asword:party:now stays plain text.nice:party:no longer previews an emoji that the send leaves literal.nice :party:), so the picked emoji still renders and is tagged.Reviewer-reproducible examples
End to end in the web build: run it against a local
ghcr.io/block/buzz:mainrelay, seed one kind 30030buzz:custom-emojiset containingparty, and hold thePOST /api/relay/<origin>/queryrequest for that set open (for example with a Playwright route). Then send from the composer and read the channel's kind 9 events back from the relay:mainSTATUS ~19:25Z: DRAFT. NEXT: check again at 14:30:00.(catalog loading)emojitag:party: hello(catalog loading)emojitag:party:(catalog loaded)["emoji", "party", <url>]nice:party:(catalog loaded)Human testing: not yet done. John authorized readiness on 2026-11-07 before running the emoji send steps himself, so
buzz-review-completedis withheld until he confirms.Screenshots
Sending timestamp prose while the community emoji catalog is still loading. Before, on
main, the send is refused:After, it publishes immediately and the composer clears:
🤖 Generated with Claude Code