fix(markdown): replace annotation with edited indicator
Some checks failed
/ deploy-and-package (push) Has been cancelled
Some checks failed
/ deploy-and-package (push) Has been cancelled
This commit is contained in:
parent
5e2120906e
commit
03325797bf
4 changed files with 23 additions and 90 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@methanium/ui",
|
"name": "@methanium/ui",
|
||||||
"version": "0.0.27",
|
"version": "0.0.28",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import rehypeSlug from "rehype-slug";
|
||||||
import remarkBreaks from "remark-breaks";
|
import remarkBreaks from "remark-breaks";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import remarkMath from "remark-math";
|
import remarkMath from "remark-math";
|
||||||
import type { ComponentProps } from "react";
|
import type { ComponentProps, ReactNode } from "react";
|
||||||
|
|
||||||
import { cn } from "../lib/utils";
|
import { cn } from "../lib/utils";
|
||||||
import { CodeBlock } from "./code-block";
|
import { CodeBlock } from "./code-block";
|
||||||
|
|
@ -38,7 +38,6 @@ import { EmojiProvider, useEmojiRegistry } from "./emoji-context";
|
||||||
import type { EmojiRegistry } from "./emoji-data";
|
import type { EmojiRegistry } from "./emoji-data";
|
||||||
import { parseCodeMeta, remarkCodeMeta } from "./markdown-meta";
|
import { parseCodeMeta, remarkCodeMeta } from "./markdown-meta";
|
||||||
import { remarkEmoji } from "./remark-emoji";
|
import { remarkEmoji } from "./remark-emoji";
|
||||||
import { remarkSmallMuted } from "./remark-small-muted";
|
|
||||||
|
|
||||||
export type MarkdownTheme = "light" | "dark";
|
export type MarkdownTheme = "light" | "dark";
|
||||||
export type MarkdownVariant = "article" | "compact";
|
export type MarkdownVariant = "article" | "compact";
|
||||||
|
|
@ -48,6 +47,7 @@ export type MarkdownProps = Omit<ComponentProps<"div">, "children"> & {
|
||||||
components?: Partial<MarkdownComponents>;
|
components?: Partial<MarkdownComponents>;
|
||||||
registry?: EmojiRegistry;
|
registry?: EmojiRegistry;
|
||||||
theme?: MarkdownTheme;
|
theme?: MarkdownTheme;
|
||||||
|
trailingContent?: ReactNode;
|
||||||
variant?: MarkdownVariant;
|
variant?: MarkdownVariant;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -81,45 +81,6 @@ function classNames(node: Element) {
|
||||||
return value ? [String(value)] : [];
|
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 = {
|
const defaultComponents: Components = {
|
||||||
a: MarkdownLink,
|
a: MarkdownLink,
|
||||||
blockquote: MarkdownBlockquote,
|
blockquote: MarkdownBlockquote,
|
||||||
|
|
@ -176,6 +137,7 @@ function Markdown({
|
||||||
components,
|
components,
|
||||||
registry: registryOverride,
|
registry: registryOverride,
|
||||||
theme,
|
theme,
|
||||||
|
trailingContent,
|
||||||
variant = "article",
|
variant = "article",
|
||||||
...props
|
...props
|
||||||
}: MarkdownProps) {
|
}: MarkdownProps) {
|
||||||
|
|
@ -187,7 +149,6 @@ function Markdown({
|
||||||
remarkMath,
|
remarkMath,
|
||||||
remarkBreaks,
|
remarkBreaks,
|
||||||
remarkCodeMeta,
|
remarkCodeMeta,
|
||||||
remarkSmallMuted,
|
|
||||||
[remarkEmoji, { registry }],
|
[remarkEmoji, { registry }],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -210,8 +171,9 @@ function Markdown({
|
||||||
rehypePlugins={rehypePlugins}
|
rehypePlugins={rehypePlugins}
|
||||||
components={{ ...defaultComponents, ...components }}
|
components={{ ...defaultComponents, ...components }}
|
||||||
>
|
>
|
||||||
{moveFenceNotes(content)}
|
{content}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
|
{trailingContent}
|
||||||
</div>
|
</div>
|
||||||
</EmojiProvider>
|
</EmojiProvider>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -6,14 +6,30 @@ import { Markdown } from "./markdown";
|
||||||
export type TextProps = {
|
export type TextProps = {
|
||||||
fontSize?: CSSProperties["fontSize"];
|
fontSize?: CSSProperties["fontSize"];
|
||||||
registry?: EmojiRegistry;
|
registry?: EmojiRegistry;
|
||||||
|
showEditedIndicator?: boolean;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Text({ fontSize, registry, value }: TextProps) {
|
export default function Text({
|
||||||
|
fontSize,
|
||||||
|
registry,
|
||||||
|
showEditedIndicator = false,
|
||||||
|
value,
|
||||||
|
}: TextProps) {
|
||||||
return (
|
return (
|
||||||
<Markdown
|
<Markdown
|
||||||
|
className={
|
||||||
|
showEditedIndicator
|
||||||
|
? "[&>p:nth-last-child(2)]:my-0 [&>p:nth-last-child(2)]:inline"
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
content={value}
|
content={value}
|
||||||
registry={registry}
|
registry={registry}
|
||||||
|
trailingContent={
|
||||||
|
showEditedIndicator ? (
|
||||||
|
<span className="ms-1 text-xs text-muted-foreground">(edited)</span>
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
variant="compact"
|
variant="compact"
|
||||||
style={{ fontSize: fontSize ?? "1rem" }}
|
style={{ fontSize: fontSize ?? "1rem" }}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue