From 0b9e51d6ccefcf50675ca2fe040167577335d7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Liu=20=E5=AE=9D=E7=8E=89?= Date: Wed, 18 Mar 2026 02:24:15 -0500 Subject: [PATCH] feat(baoyu-danger-x-to-markdown): add MARKDOWN entity support for X articles --- .../scripts/markdown.test.ts | 55 +++++++++++++++++++ .../scripts/markdown.ts | 31 +++++++++++ .../scripts/types.ts | 1 + 3 files changed, 87 insertions(+) create mode 100644 skills/baoyu-danger-x-to-markdown/scripts/markdown.test.ts diff --git a/skills/baoyu-danger-x-to-markdown/scripts/markdown.test.ts b/skills/baoyu-danger-x-to-markdown/scripts/markdown.test.ts new file mode 100644 index 0000000..23a3b6c --- /dev/null +++ b/skills/baoyu-danger-x-to-markdown/scripts/markdown.test.ts @@ -0,0 +1,55 @@ +import { expect, test } from "bun:test"; + +import { formatArticleMarkdown } from "./markdown.js"; + +test("formatArticleMarkdown renders MARKDOWN entities from atomic blocks", () => { + const article = { + title: "Atomic Markdown Example", + content_state: { + blocks: [ + { + type: "unstyled", + text: "Before the snippet.", + entityRanges: [], + }, + { + type: "atomic", + text: " ", + entityRanges: [{ key: 0, offset: 0, length: 1 }], + }, + { + type: "unstyled", + text: "After the snippet.", + entityRanges: [], + }, + ], + entityMap: { + "0": { + key: "5", + value: { + type: "MARKDOWN", + mutability: "Mutable", + data: { + markdown: "```python\nprint('hello from x article')\n```\n", + }, + }, + }, + }, + }, + }; + + const { markdown } = formatArticleMarkdown(article); + + expect(markdown).toContain("Before the snippet."); + expect(markdown).toContain("```python\nprint('hello from x article')\n```"); + expect(markdown).toContain("After the snippet."); + expect(markdown).toBe(`# Atomic Markdown Example + +Before the snippet. + +\`\`\`python +print('hello from x article') +\`\`\` + +After the snippet.`); +}); diff --git a/skills/baoyu-danger-x-to-markdown/scripts/markdown.ts b/skills/baoyu-danger-x-to-markdown/scripts/markdown.ts index 946e708..2886a81 100644 --- a/skills/baoyu-danger-x-to-markdown/scripts/markdown.ts +++ b/skills/baoyu-danger-x-to-markdown/scripts/markdown.ts @@ -237,6 +237,22 @@ function resolveEntityTweetLines( return lines; } +function resolveEntityMarkdownLines( + entityKey: number | undefined, + entityMap: ArticleContentState["entityMap"] | undefined, + entityLookup: EntityLookup +): string[] { + if (entityKey === undefined) return []; + const entry = resolveEntityEntry(entityKey, entityMap, entityLookup); + const value = entry?.value; + if (!value || value.type !== "MARKDOWN") return []; + + const markdown = typeof value.data?.markdown === "string" ? value.data.markdown : ""; + const normalized = markdown.replace(/\r\n/g, "\n").trimEnd(); + if (!normalized) return []; + return normalized.split("\n"); +} + function buildMediaLinkMap( entityMap: ArticleContentState["entityMap"] | undefined ): Map { @@ -397,6 +413,16 @@ function renderContentBlocks( return [...new Set(linkLines)]; }; + const collectMarkdownLines = (block: ArticleBlock): string[] => { + const ranges = Array.isArray(block.entityRanges) ? block.entityRanges : []; + const markdownLines: string[] = []; + for (const range of ranges) { + if (typeof range?.key !== "number") continue; + markdownLines.push(...resolveEntityMarkdownLines(range.key, entityMap, entityLookup)); + } + return markdownLines; + }; + const pushTrailingMedia = (mediaLines: string[]) => { if (mediaLines.length > 0) { pushBlock(mediaLines, "media"); @@ -441,6 +467,11 @@ function renderContentBlocks( pushBlock(tweetLines, "quote"); } + const markdownLines = collectMarkdownLines(block); + if (markdownLines.length > 0) { + pushBlock(markdownLines, "text"); + } + const mediaLines = collectMediaLines(block); if (mediaLines.length > 0) { pushBlock(mediaLines, "media"); diff --git a/skills/baoyu-danger-x-to-markdown/scripts/types.ts b/skills/baoyu-danger-x-to-markdown/scripts/types.ts index dbdec2a..7cd0acb 100644 --- a/skills/baoyu-danger-x-to-markdown/scripts/types.ts +++ b/skills/baoyu-danger-x-to-markdown/scripts/types.ts @@ -38,6 +38,7 @@ export type ArticleEntityMapEntry = { mutability?: string; data?: { caption?: string; + markdown?: string; mediaItems?: ArticleEntityMapMediaItem[]; url?: string; tweetId?: string;