Skip to content

fix(emoji): send messages without waiting for community emoji - #2871

Draft
johnmatthewtennant wants to merge 16 commits into
mainfrom
jtennant/martha/timestamp-emoji
Draft

johnmatthewtennant wants to merge 16 commits into
mainfrom
jtennant/martha/timestamp-emoji

Conversation

@johnmatthewtennant

@johnmatthewtennant johnmatthewtennant commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

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:

  • The send waited for optional enrichment. emoji-directory.ts tags() threw while the catalog wasn't ready, so any message with a shortcode candidate was refused, including replies, edits and attachment publications.
  • Timestamps looked like shortcodes. The shared matcher read :25Z: in 19:25Z: and :30: in 14:30:00 as 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

  • Sending never waits for the catalog. While it is loading or failed, shortcodes are sent as literal text without emoji tags, 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.
  • A colon that directly follows a letter, digit or underscore no longer opens a shortcode, as in other emoji tokenizers such as Mastodon's. Standalone (:party:), chained (:party::wave:) and overlapping (:unknown:party:) codes still match. The renderer, tagging, referencedEmoji and agent sends share the matcher, so sent tags and rendering agree. Text such as word:party: now stays plain text.
  • Each shortcode on an edited message keeps its latest explicit mapping, from the newest edit or the original that tagged it. An edit sent without emoji tags (for example while the catalog loads) no longer falls back to the original's mapping alone.
  • The composer highlights custom emoji with the same matcher, so nice:party: no longer previews an emoji that the send leaves literal.
  • Picking a custom emoji from the composer's Emoji tool right after a word inserts a space first (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:main relay, seed one kind 30030 buzz:custom-emoji set containing party, and hold the POST /api/relay/<origin>/query request 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:

Send main This PR
STATUS ~19:25Z: DRAFT. NEXT: check again at 14:30:00. (catalog loading) "Community emoji isn't ready", nothing published Published with that content, no emoji tag
:party: hello (catalog loading) Same error, draft kept Published literally, no emoji tag
:party: (catalog loaded) ["emoji", "party", <url>]
nice:party: (catalog loaded) Literal, no tag; the composer shows no emoji image

Human testing: not yet done. John authorized readiness on 2026-11-07 before running the emoji send steps himself, so buzz-review-completed is withheld until he confirms.

Screenshots

Sending timestamp prose while the community emoji catalog is still loading. Before, on main, the send is refused:

Before: the composer refuses the send with "Community emoji isn't ready"

After, it publishes immediately and the composer clears:

After: the timestamp message is sent and the composer is empty

🤖 Generated with Claude Code

johnmatthewtennant and others added 5 commits November 6, 2026 14:50
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>
@johnmatthewtennant johnmatthewtennant changed the title fix(emoji): send timestamp prose without waiting for community emoji fix(emoji): send messages without waiting for community emoji Nov 6, 2026
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>
@johnmatthewtennant
johnmatthewtennant marked this pull request as ready for review November 7, 2026 11:58
@johnmatthewtennant
johnmatthewtennant requested a review from a team as a code owner November 7, 2026 11:58

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 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.

Comment thread src/features/relay/emoji.ts Resolved
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>
@johnmatthewtennant
johnmatthewtennant marked this pull request as draft November 7, 2026 12:23
johnmatthewtennant and others added 2 commits November 7, 2026 07:29
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>
@johnmatthewtennant
johnmatthewtennant marked this pull request as ready for review November 7, 2026 12:50

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 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.

@tellaho

tellaho commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

🤖 Drip design review at 5304a1d741a3ac1f4517e0ca87d77d2563c023d6: no design blockers found in the source and supplied screenshots.

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 kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 One picker regression should be fixed before merge.

Comment thread src/features/relay/emoji.ts Resolved
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>
@johnmatthewtennant
johnmatthewtennant marked this pull request as draft November 7, 2026 21:19
@johnmatthewtennant
johnmatthewtennant marked this pull request as ready for review November 7, 2026 21:33

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 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.

Comment thread src/features/messages/EditableInput.tsx Resolved
@johnmatthewtennant
johnmatthewtennant marked this pull request as draft November 7, 2026 22:17
johnmatthewtennant and others added 4 commits November 7, 2026 17:19
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>
@johnmatthewtennant
johnmatthewtennant marked this pull request as ready for review November 7, 2026 22:54

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 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.

Comment thread src/features/messages/EditableInput.tsx Resolved
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>
@johnmatthewtennant
johnmatthewtennant marked this pull request as draft November 7, 2026 23:12
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.

4 participants