Files
jiang13-bbs/frontend/components/MarkdownBodyClient.tsx
freefire 34aee91a13 feat: 论坛 emoji 表情支持
- 新增 dyEmoji 表情库与解析测试、EmojiPicker 选择器与 EmojiImg 组件
- Markdown 编辑器/渲染支持表情图片,新增 public/emoji 资源与生成脚本
- 聊天室与首页适配
2026-09-28 01:28:41 +08:00

90 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import ReactMarkdown, { type Components } from "react-markdown";
import remarkGfm from "remark-gfm";
import { mergeMarkdownComponents } from "@/components/markdown/mergeComponents";
import { splitContentSegments } from "@/lib/directiveBlocks";
import { expandDyEmojiShortcodes } from "@/lib/dyEmoji";
import type { HideKind } from "@/lib/hideBlocks";
import MdHideBlock from "@/components/markdown/MdHideBlock";
import MdTimeline from "@/components/markdown/MdTimeline";
type MarkdownBodyClientProps = {
content: string;
className?: string;
components?: Components;
postId?: number;
loginHref?: string;
loggedIn?: boolean;
};
/**
* 客户端 Markdown(发帖预览 / 评论):GFM + 隐藏块 / 时间线预览。
*/
export default function MarkdownBodyClient({
content,
className = "prose-content",
components,
postId,
loginHref,
loggedIn = false,
}: MarkdownBodyClientProps) {
const segs = splitContentSegments(expandDyEmojiShortcodes(content || ""));
const mdComponents = mergeMarkdownComponents(components);
// 时间线/隐藏块正文不套外层 heading 覆盖,避免预览与正文目录 id 错位
const nestedMd = mergeMarkdownComponents();
return (
<div className={className}>
{segs.map((seg, i) => {
if (seg.type === "md") {
if (!seg.text.trim()) return null;
return (
<ReactMarkdown key={`md-${i}`} remarkPlugins={[remarkGfm]} components={mdComponents}>
{seg.text}
</ReactMarkdown>
);
}
if (seg.type === "timeline") {
return (
<MdTimeline
key={`tl-${i}`}
items={seg.items}
renderBody={(it) =>
it.body.trim() ? (
<ReactMarkdown remarkPlugins={[remarkGfm]} components={nestedMd}>
{expandDyEmojiShortcodes(it.body)}
</ReactMarkdown>
) : null
}
/>
);
}
const kind = seg.kind as HideKind;
if (seg.locked) {
return (
<MdHideBlock
key={`hide-${i}`}
kind={kind}
points={seg.points}
locked
postId={postId}
loginHref={loginHref}
loggedIn={loggedIn}
/>
);
}
return (
<MdHideBlock key={`hide-${i}`} kind={kind} points={seg.points}>
{seg.body.trim() ? (
<ReactMarkdown remarkPlugins={[remarkGfm]} components={nestedMd}>
{expandDyEmojiShortcodes(seg.body)}
</ReactMarkdown>
) : null}
</MdHideBlock>
);
})}
</div>
);
}