Skip to content

Composer draws emoji with OpenEmoji while you type (0.2.2) - #277

Merged
ralyodio merged 1 commit into
masterfrom
feat/emoji-composer
Oct 6, 2026
Merged

ralyodio merged 1 commit into
masterfrom
feat/emoji-composer

Conversation

@ralyodio

@ralyodio ralyodio commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Problem: the picker inserted our emoji, but the composer was a <textarea>. A textarea can only draw characters, so what you saw while typing was the system emoji font.

Fix: EmojiEditor, a contenteditable field that draws emoji as OpenEmoji <img> tags with the character as alt.

  • The value stays plain Unicode, so what gets encrypted is unchanged.
  • Emoji you type, enter from the OS emoji keyboard, or paste are swapped for images in place, and the caret stays where it was. IME composition is left alone.
  • Enter sends and Shift+Enter adds a new line.
  • Pasting inserts plain text only, so no foreign markup ends up in an encrypted message.
  • The caret position is remembered while the picker has focus.

Code:

  • src/lib/emoji/editor-dom.js holds the pure DOM model: render, serialize, value↔DOM offsets and caret restore.
  • src/lib/components/chat/EmojiEditor.jsx is the component.

Tried first and dropped: a chunked colour web font (WOFF2 split by unicode-range, in CBDT, sbix and SVG flavours, built from the 72px art). I rendered it in Chromium:

  • Joined emoji (ZWJ and VS16 sequences such as ❤️ 👩‍💻 🏳️‍🌈) broke or fell back to the system font, and ❤️ drew blank.
  • Any text containing VS16 pulled every chunk, about 16 MB.
  • Firefox renders neither CBDT nor sbix.

Tests: 7 new DOM-model tests, plus the picker test rewritten to insert into the editor at the caret. test:ci: 622/622. next build passes.

🤖 Generated with Claude Code

A textarea can only draw characters, so a picked emoji showed in the
system font. The composer is now a contenteditable EmojiEditor: emoji are
OpenEmoji <img>s (alt = the character), the value stays plain Unicode
(what is encrypted is unchanged), typed/OS-keyboard/pasted emoji are
swapped in place with the caret kept, Enter sends, Shift+Enter is a new
line, paste is plain text only.

A chunked web font (unicode-range WOFF2, CBDT/sbix/SVG) was tried first
and dropped: ZWJ/VS16 sequences broke or fell back to the system font, any
text with VS16 pulled every chunk (~16 MB), and Firefox renders neither
CBDT nor sbix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

13 finding(s)

MEDIUM: 9 | LOW: 4

Severity Rule Location
MEDIUM redos-nested-quantifier src/app/api/profile/update/route.js:73
MEDIUM js-unescaped-html-sink src/app/blog/[slug]/page.jsx:38
MEDIUM js-unescaped-html-sink src/app/blog/[slug]/page.jsx:66
MEDIUM js-unescaped-html-sink src/app/faq/page.jsx:57
MEDIUM js-unescaped-html-sink src/app/layout.jsx:137
MEDIUM js-unescaped-html-sink src/app/layout.jsx:141
MEDIUM js-unescaped-html-sink src/app/page.jsx:47
MEDIUM redos-nested-quantifier src/lib/auth/dns-name.js:88
MEDIUM js-unescaped-html-sink src/lib/components/chat/MessageItem.jsx:63
LOW secret-generic-credential src/app/api/auth/register-anon/route.test.js:32
LOW secret-jwt tests/debug-sms.js:10
LOW secret-generic-credential tests/private-key-import-export.test.js:252
LOW secret-generic-credential tests/private-key-import-export.test.js:264

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 85203c5 into master Oct 6, 2026
11 checks passed
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.

1 participant