68 lines
1.6 KiB
JavaScript
68 lines
1.6 KiB
JavaScript
import { Fragment } from 'react';
|
|
|
|
const TOKEN_RE = /(\*\*[^*\n][\s\S]*?[^*\n]\*\*|`[^`\n]+`|\[[^\]\n]+\]\(https?:\/\/[^)\s]+\))/g;
|
|
|
|
function renderToken(token, key) {
|
|
if (token.startsWith('**') && token.endsWith('**')) {
|
|
return (
|
|
<strong key={key} className="font-semibold text-foreground">
|
|
{token.slice(2, -2)}
|
|
</strong>
|
|
);
|
|
}
|
|
|
|
if (token.startsWith('`') && token.endsWith('`')) {
|
|
return (
|
|
<code key={key} className="rounded bg-muted px-1 py-0.5 font-mono text-[0.92em] text-foreground">
|
|
{token.slice(1, -1)}
|
|
</code>
|
|
);
|
|
}
|
|
|
|
const link = token.match(/^\[([^\]\n]+)\]\((https?:\/\/[^)\s]+)\)$/);
|
|
if (link) {
|
|
return (
|
|
<a
|
|
key={key}
|
|
href={link[2]}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="font-medium text-primary underline-offset-4 hover:underline"
|
|
>
|
|
{link[1]}
|
|
</a>
|
|
);
|
|
}
|
|
|
|
return token;
|
|
}
|
|
|
|
export function renderInlineMarkdown(text) {
|
|
if (!text) return null;
|
|
|
|
const parts = [];
|
|
let lastIndex = 0;
|
|
|
|
for (const match of text.matchAll(TOKEN_RE)) {
|
|
if (match.index > lastIndex) {
|
|
parts.push(text.slice(lastIndex, match.index));
|
|
}
|
|
parts.push(renderToken(match[0], `md-${match.index}`));
|
|
lastIndex = match.index + match[0].length;
|
|
}
|
|
|
|
if (lastIndex < text.length) {
|
|
parts.push(text.slice(lastIndex));
|
|
}
|
|
|
|
return parts.map((part, index) => (
|
|
<Fragment key={typeof part === 'string' ? `text-${index}` : part.key || index}>
|
|
{part}
|
|
</Fragment>
|
|
));
|
|
}
|
|
|
|
export function MarkdownText({ text }) {
|
|
return renderInlineMarkdown(text);
|
|
}
|