报告语言改成分段页签,切换后不留下上一种语言的正文。导出从右侧或底部滑出,不再显示文件大小和说明句。星历用箭头按钮按方向翻页,卡片边框不被裁掉。登录的输入、按钮和六位验证码换成组件库。星星汇聚不改。 自测:tsc 退出码 0;lint 0 error、122 个警告;相关测试 185 条里 184 通过,另 1 条是既有的 Windows 管理路径问题;英文版 10 条和删除 5 条重跑通过;build 后首页仍是 Static;首页 JS gzip-9 为 642174,相对 639609 多 0.40%。隐私标记测试通过。 Co-Authored-By: Grok (agent loop)
61 lines
2.0 KiB
TypeScript
61 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
import { Check, Copy } from "lucide-react";
|
|
import { isValidElement, useEffect, useRef, useState, type ReactNode } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
/** The fenced language, from the `language-xxx` class react-markdown puts on <code>. */
|
|
export function fencedLanguage(node: ReactNode): string {
|
|
if (!isValidElement<{ className?: string }>(node)) return "";
|
|
return /language-([\w+-]+)/.exec(node.props.className ?? "")?.[1] ?? "";
|
|
}
|
|
|
|
/** The raw source inside the fence, for the copy button. */
|
|
export function fencedSource(node: ReactNode): string {
|
|
if (typeof node === "string") return node;
|
|
if (Array.isArray(node)) return node.map(fencedSource).join("");
|
|
if (isValidElement<{ children?: ReactNode }>(node)) return fencedSource(node.props.children);
|
|
return "";
|
|
}
|
|
|
|
export function MarkdownCodeBlock({ children }: { readonly children?: ReactNode }) {
|
|
const [copied, setCopied] = useState(false);
|
|
const resetTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
const language = fencedLanguage(children);
|
|
const source = fencedSource(children);
|
|
|
|
useEffect(() => () => {
|
|
if (resetTimer.current) clearTimeout(resetTimer.current);
|
|
}, []);
|
|
|
|
async function copy() {
|
|
try {
|
|
await navigator.clipboard.writeText(source);
|
|
} catch {
|
|
return;
|
|
}
|
|
setCopied(true);
|
|
if (resetTimer.current) clearTimeout(resetTimer.current);
|
|
resetTimer.current = setTimeout(() => setCopied(false), 1600);
|
|
}
|
|
|
|
return (
|
|
<div className="markdown-code">
|
|
<div className="markdown-code-bar">
|
|
<span className="markdown-code-language">{language || "代码"}</span>
|
|
<Button
|
|
aria-label={copied ? "已复制代码" : "复制代码"}
|
|
className="markdown-code-copy"
|
|
onClick={() => void copy()}
|
|
type="button"
|
|
variant="ghost"
|
|
>
|
|
{copied ? <Check aria-hidden="true" /> : <Copy aria-hidden="true" />}
|
|
<span>{copied ? "已复制" : "复制"}</span>
|
|
</Button>
|
|
</div>
|
|
<pre>{children}</pre>
|
|
</div>
|
|
);
|
|
}
|