返回 DeepSeek-Reasonix
ReasoningSummary.tsx
根目录 / desktop / frontend / src / components / ReasoningSummary.tsx
1 import { memo, useEffect, useMemo, useRef } from "react";
2 import { reasoningSummaryText } from "../lib/reasoningSummary";
3
4 export const ReasoningSummary = memo(function ReasoningSummary({
5 text,
6 streaming = false,
7 className = "",
8 onOpen,
9 maxChars,
10 }: {
11 text: string;
12 streaming?: boolean;
13 className?: string;
14 onOpen?: () => void;
15 maxChars?: number;
16 }) {
17 const summary = useMemo(() => reasoningSummaryText(text, { streaming, maxChars }), [text, streaming, maxChars]);
18 const ref = useRef<HTMLElement>(null);
19
20 // While streaming, keep the single-line summary pinned to the line tail so
21 // the newest text stays visible; rAF coalesces rapid token updates. A
22 // settled summary resets to the line start.
23 useEffect(() => {
24 const el = ref.current;
25 if (!el) return;
26 if (!streaming) {
27 el.scrollLeft = 0;
28 return;
29 }
30 const frame = requestAnimationFrame(() => {
31 el.scrollLeft = el.scrollWidth;
32 });
33 return () => cancelAnimationFrame(frame);
34 }, [summary, streaming]);
35
36 if (!summary) return null;
37 const cls = `reasoning-summary${className ? ` ${className}` : ""}`;
38 const followEnd = streaming ? { "data-follow-end": "" } : {};
39 if (onOpen) {
40 return <button ref={ref as React.RefObject<HTMLButtonElement>} type="button" className={cls} onClick={onOpen} {...followEnd}>{summary}</button>;
41 }
42 return <span ref={ref as React.RefObject<HTMLSpanElement>} className={cls} {...followEnd}>{summary}</span>;
43 });
44
44 lines Plain Text