Skip to content

fix(api): keep images when a post also contains emoji nodes#338

Open
kytenz wants to merge 1 commit into
QuackbackIO:mainfrom
plannie-io:fix/emoji-nodes-drop-images-in-markdown-export
Open

fix(api): keep images when a post also contains emoji nodes#338
kytenz wants to merge 1 commit into
QuackbackIO:mainfrom
plannie-io:fix/emoji-nodes-drop-images-in-markdown-export

Conversation

@kytenz

@kytenz kytenz commented Jul 24, 2026

Copy link
Copy Markdown

Problem

contentJsonToMarkdown() only re-serializes contentJson when every node is in
RESERIALIZABLE_NODE_TYPES. emoji was not in that set, so any post written with the : emoji
picker fell back to the stored content column — which is precisely the column that loses images
(the bug #317 fixed for everything else).

So a changelog entry containing both a screenshot and an emoji comes back from
GET /api/v1/changelog (and the MCP tools) with no images at all, and with raw :tada:
shortcodes instead of the emoji. Mixing an image and an emoji in one post is common in changelogs,
so this quietly undoes #317 for a large share of real entries.

Reproduce: write a changelog entry in the editor with an uploaded image and one picker emoji,
publish it, then GET /api/v1/changelog?published=true — the markdown has neither the image nor
the emoji character.

Fix

Teach normalizeForMarkdown() about the node rather than excluding the whole document:

  • emoji joins RESERIALIZABLE_NODE_TYPES
  • an emoji node becomes a text node holding its Unicode character — attrs.emoji when the editor
    stored it, else the @tiptap/extension-emoji shortcode table (the same lookup order
    rich-text-editor.tsx already uses for HTML)
  • image-backed custom emoji have no character, so they keep :name: — the text the editor
    round-trips them as

Images now survive alongside emoji, and emoji read as emoji in markdown consumers.

Tests

Three cases added to markdown-tiptap.test.ts (40 pass):

  • an image + :tada: emoji doc → keeps ![S](…) and renders 🎉, no leftover shortcode
  • attrs.emoji wins over the shortcode table
  • a custom emoji with no Unicode character stays :quackback_logo:

bun run test apps/web/src/lib/server/__tests__/markdown-tiptap.test.ts — 40/40.
Lint clean on both files. Repo-wide typecheck is red on main at 513 errors both with and
without this change (pre-existing, unrelated to these files).

`contentJsonToMarkdown` only re-serializes `contentJson` when every node is
in `RESERIALIZABLE_NODE_TYPES`. `emoji` was not, so any post written with the
`:` emoji picker fell back to the stored markdown column — which is exactly
the column that loses images (QuackbackIO#317). The result: a changelog entry with both
a screenshot and an emoji came back from `GET /api/v1/changelog` (and MCP)
with no images and with raw `:tada:` shortcodes.

Teach `normalizeForMarkdown` about the node instead: emit the emoji's Unicode
character (from `attrs.emoji` when the editor stored it, else the shortcode
table), falling back to `:name:` for image-backed custom emoji that have no
character. Images then survive alongside emoji, and the emoji read correctly
in markdown consumers rather than as shortcodes.
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