"use client"; import { useState, type ReactNode } from "react"; import { Check, Copy } from "lucide-react"; import { cn } from "@/lib/utils"; /** * 代码块容器:对应 DESIGN.md 的 code-block + code-block-header + copy-code-button。 * * Shiki 已在构建期把高亮结果编译成
…
, * 这里只负责套上头部(语言标签 + 复制按钮),不重新解析代码。 * * 复制逻辑优先使用 Clipboard API,失败时回退到 execCommand, * 以便在非 HTTPS 环境下依然可用。 */ export interface CodeBlockProps { children?: ReactNode; className?: string; /** Shiki 输出的 data-language(由 rehype-pretty-code 注入到 figure 上) */ "data-language"?: string; raw?: string; } async function copyText(text: string): Promise { try { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); return true; } } catch { // 落回下面的兼容实现 } try { const textarea = document.createElement("textarea"); textarea.value = text; textarea.setAttribute("readonly", ""); textarea.style.position = "fixed"; textarea.style.opacity = "0"; document.body.appendChild(textarea); textarea.select(); const succeeded = document.execCommand("copy"); document.body.removeChild(textarea); return succeeded; } catch { return false; } } /** 从
 的 React 子节点中抽取纯文本,作为复制内容 */
function extractText(node: ReactNode): string {
	if (node === null || node === undefined || typeof node === "boolean") {
		return "";
	}
	if (typeof node === "string" || typeof node === "number") {
		return String(node);
	}
	if (Array.isArray(node)) {
		return node.map(extractText).join("");
	}
	if (typeof node === "object" && "props" in node) {
		const props = (node as { props?: { children?: ReactNode } }).props;
		return extractText(props?.children);
	}
	return "";
}

export function CodeBlock({ children, className, ...props }: CodeBlockProps) {
	const [copied, setCopied] = useState(false);

	const language =
		props["data-language"] ?? detectLanguage(className) ?? "text";

	const handleCopy = async () => {
		const text = extractText(children).replace(/\n$/, "");
		const succeeded = await copyText(text);

		if (succeeded) {
			setCopied(true);
			window.setTimeout(() => setCopied(false), 2000);
		}
	};

	return (
		
{language}
				{children}
			
); } /** 从 className 中提取语言,如 "language-ts" → "ts" */ function detectLanguage(className?: string): string | null { const match = /language-([\w-]+)/.exec(className ?? ""); return match ? match[1] : null; }