From 5e2120906e3b45e62f3dc06497c9d9b91c4b5c5c Mon Sep 17 00:00:00 2001 From: Alois Date: Sun, 9 Aug 2026 23:56:58 +0200 Subject: [PATCH] feat(markdown): add code fencing for ??++ --- apps/showcase/src/MarkdownShowcase.tsx | 2 +- package.json | 2 +- src/markdown/markdown.tsx | 41 +++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/apps/showcase/src/MarkdownShowcase.tsx b/apps/showcase/src/MarkdownShowcase.tsx index ded9d7a..ddf95a1 100644 --- a/apps/showcase/src/MarkdownShowcase.tsx +++ b/apps/showcase/src/MarkdownShowcase.tsx @@ -28,7 +28,7 @@ const markdownContent = [ "", "Shortcodes use local Twemoji assets :fire: :white_check_mark:, while applications can register their own images :methanium:.", "", - "Visit the [Methanium repository](https://github.com/methanium) or jump to the [code examples](#code-examples). Bare URLs are supported too: https://example.com.", + "Visit the [Methanium](https://methanium.net) or jump to the [code examples](#code-examples). Bare URLs are supported too: https://example.com.", "", "![Methanium logo](" + methaniumLogo + ")", "", diff --git a/package.json b/package.json index adaec3d..1682bf8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@methanium/ui", - "version": "0.0.26", + "version": "0.0.27", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/markdown/markdown.tsx b/src/markdown/markdown.tsx index 99a3338..1176c38 100644 --- a/src/markdown/markdown.tsx +++ b/src/markdown/markdown.tsx @@ -81,6 +81,45 @@ function classNames(node: Element) { return value ? [String(value)] : []; } +function moveFenceNotes(content: string) { + const lines = content.split("\n"); + const fence = /^ {0,3}`{3,}/; + const closingFence = /^ {0,3}`{3,}\s*$/; + const note = /\?\?\+\+.*?\?\?\+\+/g; + let inCodeBlock = false; + let openingIndex = -1; + let openingNotes: string[] = []; + + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index]; + if (inCodeBlock) { + const closingNotes = line.match(note) ?? []; + const cleanedLine = line.replace(note, "").trimEnd(); + if (closingFence.test(cleanedLine)) { + if (openingNotes.length > 0) { + lines[openingIndex] = lines[openingIndex].replace(note, "").trimEnd(); + } + lines[index] = cleanedLine; + inCodeBlock = false; + const notes = [...openingNotes, ...closingNotes]; + if (notes.length > 0) { + lines.splice(index + 1, 0, ...notes); + index += notes.length; + } + openingNotes = []; + } + continue; + } + if (!fence.test(line)) continue; + + inCodeBlock = true; + openingIndex = index; + openingNotes = line.match(note) ?? []; + } + + return lines.join("\n"); +} + const defaultComponents: Components = { a: MarkdownLink, blockquote: MarkdownBlockquote, @@ -171,7 +210,7 @@ function Markdown({ rehypePlugins={rehypePlugins} components={{ ...defaultComponents, ...components }} > - {content} + {moveFenceNotes(content)}