Repository navigation
feat(composer): ":" emoji suggestions with plain-text items - #917
Draft
pasqualevitiello wants to merge 2 commits into
Draft
pasqualevitiello wants to merge 2 commits into
pasqualevitiello wants to merge 2 commits into
Conversation
- Add `type: "text"` to ComposerItemValue: selecting such an item replaces the query with the item's `text` (or `label`) as ordinary characters instead of inserting a token, so backspace, copy/paste, undo, caret placement and screen readers treat it like typed text - Add the p-composer-12 particle and an "Emoji" docs section: typing ":thu" suggests 👍 from a curated list of 137 emoji ranked in onQueryChange (shortcode prefix, keyword prefix, substring; at most 8, at least 2 characters); each row shows the emoji on a blurred glow - Add the ":" trigger to the chat demo composer - Test the ranking, and the query, selection, keyboard and undo behavior in a happy-dom test that runs in its own bun process Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
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.
What
Slack-style emoji suggestions for the Composer. Typing
:plus at least two letters (:thu) opens a popover of matching emoji. Picking one (arrows + Enter, or click) replaces:thuwith the character, 👍. Each row shows the emoji (20px), then:shortcode:in muted text.Recording of typing
Ship it :thu→ Enter → 👍 → send: typing-thu.mp4 (raw: download).Mechanism: B, a small core addition
ComposerItemValue.typegains"text". Selecting such an item replaces the query with the item'stext(orlabel) as a plain text node, with the same trailing-space and caret handling as tokens. It's a few lines ininsertItem(lib/composer-document.ts) and a type change.ComposerPartis unchanged, so the draft never contains anything except text for an emoji.Why not A (an emoji token with
text: "👍"drawn byrenderToken):{ type: "mention", trigger: ":" }parts. Anything that reads mentions (notifications, Cal Video) would have to filter emoji out, which contradicts "plain text, not a chip".renderTokenbranch, the defaultComposerTokendraws:thumbsupin primary text. Every consumer would need that branch, the chat demo included.contenteditable="false"island: word-wise caret movement and selection skip it, IME composition next to it is fragile, and a pasted 👍 (plain text) would behave differently from a picked one.With B, backspace, copy/paste, undo (one history step back to
:thu), caret position and screen readers all treat the emoji like a typed character.Lighter route for the trigger rules (no core change)
The two-character minimum, ranking (shortcode prefix → keyword prefix → substring) and the 8-result limit live in user land. The demo ranks inside
onQueryChangeand passes the result withfilter: null:Trade-off: every consumer writes this state + callback, and the ranked list arrives one commit after the query changes. I measured it in the browser frame by frame while typing and backspacing (
:thu→:thumbsu→:thum): the popup stayed open every frame, with no close/reopen or fade. If more consumers need it, a follow-up could letitemsbe a function of the query.The existing core rules already cover the rest:
:opens only at the start or after whitespace (so10:30andhttps://don't open it), a space right after:closes it, and so does Escape.Data
No dataset dependency.
apps/ui/registry/default/chat-demo/chat-demo-emoji.tsholds 137 curated{ emoji, shortcode, keywords }entries (including 👍 ❤️ 😂 😮 👏 🎉) plussearchEmoji. It sits next to the chat demo and is registered as a second file of thechat-demoblock and of the newp-composer-12particle. shadcn rewrites@/registry/default/chat-demo/…to@/components/chat-demo/…, which matches the file's target.Changes
lib/composer-document.ts(registry + syncedpackages/ui):type: "text"items.p-composer-12particle + "Emoji" section under "Mentions and actions" incomposer.mdx; theComposerItemValuedocs now mention"text".chat-demo-composer.tsx)::trigger next to@.apps/ui/public/r/*.jsonregenerated withbun run registry:build. The build also rewrote ~30 unrelatedp-*.jsonfiles because the registry had drifted from earlier commits. I left those out to keep this PR focused.Tests
apps/ui/tests/chat/chat-demo-emoji.test.ts: two-character minimum, ranking order, 8-result limit, case/space handling, item shape, common reactions present, unique shortcodes.packages/ui/test/components/composer.dom.tsx(happy-dom + real React render): opens after:+ 2 characters; stays closed inat 10:30,https://thumbsand: th; Escape closes; ↑/↓ wrap; Enter insertsnice 👍as a single text part,getComposerTextreturns it, the caret lands after the space, and no token element exists; undo restores:tada.@happy-dom/global-registratoras apackages/uidev dependency. The DOM test runs in its ownbun testprocess from the roottestscript, becausebutton.test.tsxmocks@base-ui/react/use-renderandmerge-propsprocess-wide, which breaks a real render. It's named.dom.tsxso the main run doesn't pick it up.bun run test,apps/uitypecheck (app + tests) and biome all pass. I also checked in the browser: docs demo (light/dark), chat showcase (Congrats :ta→ 🎉, then@mastill suggests Maya).Follow-ups (out of scope)
:thumbsup:) to insert it directly. Not tiny: it needs an input-time hook in the core, because rewriting the controlled value would reset undo history and the caret.🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.