返回 DeepSeek-Reasonix
CodeViewer.tsx
根目录 / desktop / frontend / src / components / CodeViewer.tsx
1 import { lazy, Suspense, type CSSProperties } from "react";
2 import { CopyButton } from "./CopyButton";
3 import { useT } from "../lib/i18n";
4
5 export type CodeScrollMode = "expand" | "bounded";
6
7 export interface EditorProps {
8 value: string;
9 /** Complete source when the displayed code is a folded preview. */
10 copyValue?: string;
11 language?: string;
12 readOnly?: boolean;
13 scrollMode?: CodeScrollMode;
14 maxHeight?: CSSProperties["maxHeight"];
15 /** Original source size in bytes when the caller already has it. */
16 sourceSize?: number;
17 /** Opt in to the workspace-oriented viewer with line numbers and search. */
18 showLineNumbers?: boolean;
19 /** Markdown fences keep language and copy controls outside the scrolling source. */
20 showHeader?: boolean;
21 /** Request that the workspace-oriented viewer opens search after mounting. */
22 searchRequestPending?: boolean;
23 /** Called once the viewer has consumed a pending search request. */
24 onSearchRequestConsumed?: () => void;
25 }
26
27 // ── EDITOR SEAM (code) ───────────────────────────────────────────────────────
28 // Keep the established highlighted viewer for existing chat, diff, and tool
29 // surfaces. Workspace previews explicitly opt into the heavier searchable
30 // viewer, so this feature cannot silently change every code block in the app.
31 const HljsImpl = lazy(() => Promise.all([import("./editors/HljsCode"), import("./CodeSyntax.css")]).then(([module]) => module));
32 const LineNumberImpl = lazy(() => Promise.all([import("./editors/LineNumberCode"), import("./CodeSyntax.css")]).then(([module]) => module));
33
34 function CodeBlockHeader({ value, copyValue, language }: EditorProps) {
35 const t = useT();
36 return <div className="code-block__header">
37 <span className="code-block__language" title={language}>{language || t("code.plainText")}</span>
38 <CopyButton text={copyValue ?? value} className="code-block__header-copy" />
39 </div>;
40 }
41
42 export function CodeViewer(props: EditorProps) {
43 const Impl = props.showLineNumbers ? LineNumberImpl : HljsImpl;
44 const framed = props.showHeader && !props.showLineNumbers;
45 const bounded = props.scrollMode === "bounded" || (props.scrollMode !== "expand" && props.maxHeight != null);
46 return (
47 <div className={`code-block${framed ? " code-block--framed" : ""}`}>
48 {framed && <CodeBlockHeader {...props} />}
49 <Suspense
50 fallback={
51 <pre className={`code code--loading${bounded ? " code--scroll-y" : ""}`} data-nested-scroll={bounded ? "" : undefined}>
52 <code>{props.value}</code>
53 </pre>
54 }
55 >
56 <Impl {...props} />
57 </Suspense>
58 </div>
59 );
60 }
61
61 lines Plain Text