From 03325797bf995a10de9434b21349c0deb79cec70 Mon Sep 17 00:00:00 2001 From: Alois Date: Mon, 10 Aug 2026 00:07:22 +0200 Subject: [PATCH] fix(markdown): replace annotation with edited indicator --- package.json | 2 +- src/markdown/markdown.tsx | 48 ++++-------------------------- src/markdown/remark-small-muted.ts | 45 ---------------------------- src/markdown/text.tsx | 18 ++++++++++- 4 files changed, 23 insertions(+), 90 deletions(-) delete mode 100644 src/markdown/remark-small-muted.ts diff --git a/package.json b/package.json index 1682bf8..914b420 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@methanium/ui", - "version": "0.0.27", + "version": "0.0.28", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/markdown/markdown.tsx b/src/markdown/markdown.tsx index 1176c38..a7848bb 100644 --- a/src/markdown/markdown.tsx +++ b/src/markdown/markdown.tsx @@ -6,7 +6,7 @@ import rehypeSlug from "rehype-slug"; import remarkBreaks from "remark-breaks"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; -import type { ComponentProps } from "react"; +import type { ComponentProps, ReactNode } from "react"; import { cn } from "../lib/utils"; import { CodeBlock } from "./code-block"; @@ -38,7 +38,6 @@ import { EmojiProvider, useEmojiRegistry } from "./emoji-context"; import type { EmojiRegistry } from "./emoji-data"; import { parseCodeMeta, remarkCodeMeta } from "./markdown-meta"; import { remarkEmoji } from "./remark-emoji"; -import { remarkSmallMuted } from "./remark-small-muted"; export type MarkdownTheme = "light" | "dark"; export type MarkdownVariant = "article" | "compact"; @@ -48,6 +47,7 @@ export type MarkdownProps = Omit, "children"> & { components?: Partial; registry?: EmojiRegistry; theme?: MarkdownTheme; + trailingContent?: ReactNode; variant?: MarkdownVariant; }; @@ -81,45 +81,6 @@ 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, @@ -176,6 +137,7 @@ function Markdown({ components, registry: registryOverride, theme, + trailingContent, variant = "article", ...props }: MarkdownProps) { @@ -187,7 +149,6 @@ function Markdown({ remarkMath, remarkBreaks, remarkCodeMeta, - remarkSmallMuted, [remarkEmoji, { registry }], ]; @@ -210,8 +171,9 @@ function Markdown({ rehypePlugins={rehypePlugins} components={{ ...defaultComponents, ...components }} > - {moveFenceNotes(content)} + {content} + {trailingContent} ); diff --git a/src/markdown/remark-small-muted.ts b/src/markdown/remark-small-muted.ts deleted file mode 100644 index 6beadd1..0000000 --- a/src/markdown/remark-small-muted.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { Emphasis, Parent, Root, Text } from "mdast"; -import { SKIP, visit } from "unist-util-visit"; - -const delimiter = "??++"; - -export function remarkSmallMuted() { - return (tree: Root) => { - visit(tree, "text", (node: Text, index, parent: Parent | undefined) => { - if (index === undefined || !parent) return; - - const children: Parent["children"] = []; - let cursor = 0; - let opening = node.value.indexOf(delimiter); - - while (opening !== -1) { - const contentStart = opening + delimiter.length; - const closing = node.value.indexOf(delimiter, contentStart); - if (closing === -1) break; - - if (opening > cursor) { - children.push({ type: "text", value: node.value.slice(cursor, opening) }); - } - children.push({ - type: "emphasis", - children: [{ type: "text", value: node.value.slice(contentStart, closing) }], - data: { - hName: "span", - hProperties: { className: ["text-xs", "text-muted-foreground"] }, - }, - } satisfies Emphasis); - - cursor = closing + delimiter.length; - opening = node.value.indexOf(delimiter, cursor); - } - - if (children.length === 0) return; - if (cursor < node.value.length) { - children.push({ type: "text", value: node.value.slice(cursor) }); - } - - parent.children.splice(index, 1, ...children); - return [SKIP, index + children.length]; - }); - }; -} diff --git a/src/markdown/text.tsx b/src/markdown/text.tsx index 8615b39..bae9b5a 100644 --- a/src/markdown/text.tsx +++ b/src/markdown/text.tsx @@ -6,14 +6,30 @@ import { Markdown } from "./markdown"; export type TextProps = { fontSize?: CSSProperties["fontSize"]; registry?: EmojiRegistry; + showEditedIndicator?: boolean; value: string; }; -export default function Text({ fontSize, registry, value }: TextProps) { +export default function Text({ + fontSize, + registry, + showEditedIndicator = false, + value, +}: TextProps) { return ( p:nth-last-child(2)]:my-0 [&>p:nth-last-child(2)]:inline" + : undefined + } content={value} registry={registry} + trailingContent={ + showEditedIndicator ? ( + (edited) + ) : undefined + } variant="compact" style={{ fontSize: fontSize ?? "1rem" }} />