"use client"; import * as React from "react"; import ReactMarkdown, { defaultUrlTransform, type Components } from "react-markdown"; import remarkGfm from "remark-gfm"; import { RotateCcwIcon, ZoomInIcon, ZoomOutIcon } from "lucide-react"; import { Dialog, DialogContent } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { auth } from "@/lib/auth"; function AuthenticatedImage({ src, alt, ...props }: React.ImgHTMLAttributes) { const [objectURL, setObjectURL] = React.useState(null); const [captureTimestamp, setCaptureTimestamp] = React.useState(null); const [zoomed, setZoomed] = React.useState(false); const [zoomScale, setZoomScale] = React.useState(1); const imageSrc = typeof src === "string" ? src : undefined; React.useEffect(() => { let cancelled = false; let url: string | null = null; if (!imageSrc || !imageSrc.startsWith("/api/workspace/preview")) { setObjectURL(null); setCaptureTimestamp(null); setZoomed(false); setZoomScale(1); return () => { cancelled = true; }; } const token = auth.getToken(); setCaptureTimestamp(null); fetch(imageSrc, { credentials: "same-origin", headers: token ? { Authorization: `Bearer ${token}` } : undefined, }) .then((response) => { if (!response.ok) throw new Error(`图片读取失败: ${response.status}`); setCaptureTimestamp(response.headers.get("X-Report-Screenshot-Timestamp")); return response.blob(); }) .then((blob) => { if (cancelled) return; url = URL.createObjectURL(blob); setObjectURL(url); }) .catch(() => { if (!cancelled) setObjectURL(null); }); return () => { cancelled = true; if (url) URL.revokeObjectURL(url); }; }, [imageSrc]); const renderedSrc = objectURL ?? imageSrc; const timestamp = captureTimestamp ? ( {captureTimestamp} ) : null; return ( <> { setZoomed(next); if (!next) setZoomScale(1); }} >
{alt {timestamp}
); } // Tailwind-styled element overrides (no typography plugin in this project, so we // style each element). `node` is stripped — it's not a valid DOM attribute. const components: Components = { h1: ({ node, ...p }) =>
, h2: ({ node, ...p }) =>
, h3: ({ node, ...p }) =>
, h4: ({ node, ...p }) =>
, p: ({ node, ...p }) =>

, ul: ({ node, ...p }) =>