返回 DeepSeek-Reasonix
HljsCode.tsx
根目录 / desktop / frontend / src / components / editors / HljsCode.tsx
1 import { memo, useEffect, useMemo, useState } from "react";
2 import type { EditorProps } from "../CodeViewer";
3 import {
4 escapeHtml,
5 highlightToHtml,
6 IDLE_HIGHLIGHT_MIN_BYTES,
7 shouldHighlightSource,
8 } from "../../lib/highlight";
9 import { scheduleIdleTask } from "../../lib/idleTask";
10 import { CopyButton } from "../CopyButton";
11
12 // HljsCode is the syntax-highlighted default behind the code editor seam. It
13 // renders highlight.js token markup into a <pre>; token colors live in styles.css
14 // (.hljs-*). To upgrade to a full editor, point CodeViewer.tsx's lazy import at a
15 // Monaco/CodeMirror module honoring the same EditorProps.
16 //
17 // Large blocks (≥ IDLE_HIGHLIGHT_MIN_BYTES, still under the skip caps) mount
18 // the COMPLETE plain text immediately and swap in highlighted HTML from an
19 // idle callback; the skip caps (MAX_HIGHLIGHT_BYTES / MAX_HIGHLIGHT_LINES)
20 // remain the plain-forever policy. Either way nothing is ever truncated.
21 const HljsCode = memo(function HljsCode({ value, copyValue, language, scrollMode, maxHeight, sourceSize }: EditorProps) {
22 const syntaxHighlight = useMemo(
23 () => shouldHighlightSource(value, sourceSize),
24 [sourceSize, value],
25 );
26 const deferToIdle = syntaxHighlight && (sourceSize ?? value.length) >= IDLE_HIGHLIGHT_MIN_BYTES;
27 const [idleResult, setIdleResult] = useState<{ value: string; language?: string; html: string } | null>(null);
28
29 useEffect(() => {
30 if (!deferToIdle) return;
31 let cancelled = false;
32 const cancelIdle = scheduleIdleTask(() => {
33 if (cancelled) return;
34 const html = highlightToHtml(value, language);
35 setIdleResult({ value, language, html });
36 });
37 return () => {
38 cancelled = true;
39 cancelIdle();
40 };
41 }, [deferToIdle, language, value]);
42
43 const idleHtml = idleResult && idleResult.value === value && idleResult.language === language
44 ? idleResult.html
45 : null;
46 const html = useMemo(() => {
47 if (!deferToIdle) return highlightToHtml(value, syntaxHighlight ? language : undefined);
48 return idleHtml ?? escapeHtml(value);
49 }, [deferToIdle, idleHtml, language, syntaxHighlight, value]);
50 const highlighted = syntaxHighlight && (!deferToIdle || idleHtml !== null);
51 const bounded = scrollMode === "bounded" || (scrollMode !== "expand" && maxHeight != null);
52 return (
53 <div className="code-block__wrap">
54 <pre
55 className={`code hljs${bounded ? " code--scroll-y" : ""}`}
56 data-nested-scroll={bounded ? "" : undefined}
57 data-highlight-mode={highlighted ? "syntax" : "plain"}
58 data-lang={language}
59 style={maxHeight != null ? { maxHeight } : undefined}
60 >
61 <code dangerouslySetInnerHTML={{ __html: html }} />
62 </pre>
63 <CopyButton text={copyValue ?? value} className="code-block__copy" />
64 </div>
65 );
66 });
67
68 export default HljsCode;
69
69 lines Plain Text