返回 DeepSeek-Reasonix
MarkdownImage.tsx
根目录 / desktop / frontend / src / components / MarkdownImage.tsx
1 import { useContext, useEffect, useMemo, useState } from "react";
2 import { app } from "../lib/bridge";
3 import { hasMarkdownImageResolver, markdownImageSource, type MarkdownImageView } from "../lib/markdownImage";
4 import { RichMarkdownLink } from "./githubLink";
5 import { MarkdownImageTabContext } from "./MarkdownImageContext";
6
7 export function MarkdownImage({ src, alt, title }: { src?: string; alt?: string; title?: string }) {
8 const tabId = useContext(MarkdownImageTabContext);
9 const source = src?.trim() ?? "";
10 const resolverAvailable = hasMarkdownImageResolver();
11 const legacyView = useMemo<MarkdownImageView>(
12 () => ({ url: markdownImageSource(source), openHref: /^https?:\/\//i.test(source) ? source : undefined }),
13 [source],
14 );
15 const [resolved, setResolved] = useState<MarkdownImageView | null>(() => resolverAvailable ? null : legacyView);
16 const [loadFailed, setLoadFailed] = useState(false);
17
18 useEffect(() => {
19 let live = true;
20 setLoadFailed(false);
21 if (!hasMarkdownImageResolver()) {
22 setResolved(legacyView);
23 return () => { live = false; };
24 }
25 setResolved(null);
26 void app.ResolveMarkdownImageForTab(tabId, source).then((view) => {
27 if (live) setResolved(view);
28 }).catch(() => {
29 if (live) setResolved({ url: "", errorCode: "resolve-failed" });
30 });
31 return () => { live = false; };
32 }, [legacyView, source, tabId]);
33
34 const unavailable = loadFailed || Boolean(resolved?.errorCode) || (resolved !== null && !resolved.url);
35 if (unavailable) {
36 const label = alt?.trim() || resolved?.filename || "Image unavailable";
37 return (
38 <span className="md-image-fallback" role="img" aria-label={label} title={resolved?.errorCode || title}>
39 <span>{label}</span>
40 {resolved?.openHref && <RichMarkdownLink href={resolved.openHref}>Open image</RichMarkdownLink>}
41 </span>
42 );
43 }
44 if (!resolved) {
45 return <span className="md-image-placeholder" role="status" aria-label={alt?.trim() || "Loading image"} />;
46 }
47 return (
48 <img
49 src={resolved.url}
50 alt={alt ?? ""}
51 title={title}
52 loading="lazy"
53 decoding="async"
54 referrerPolicy="no-referrer"
55 onError={() => setLoadFailed(true)}
56 />
57 );
58 }
59
59 lines Plain Text