|
1 |
| -import React, { useEffect, useMemo, useRef, useState } from "react" |
2 |
| -import { useTranslation } from "react-i18next" |
3 |
| - |
| 1 | +import React from "react" |
4 | 2 | import MarkdownBlock from "../common/MarkdownBlock"
|
5 |
| -import { vscode } from "@src/utils/vscode" |
6 | 3 |
|
7 | 4 | interface ReasoningBlockProps {
|
8 | 5 | content: string
|
9 |
| - ts: number |
10 |
| - isStreaming: boolean |
11 |
| - isLast: boolean |
12 |
| - metadata?: Record<string, any> |
13 | 6 | }
|
14 | 7 |
|
15 |
| -/** |
16 |
| - * Render reasoning with a heading and a persistent timer. |
17 |
| - * - Heading uses i18n key chat:reasoning.thinking |
18 |
| - * - Timer shown as "(⟲ 24s)" beside the heading and persists via message.metadata.reasoning { startedAt, elapsedMs } |
19 |
| - */ |
20 |
| -export const ReasoningBlock = ({ content, ts, isStreaming, isLast, metadata }: ReasoningBlockProps) => { |
21 |
| - const { t } = useTranslation() |
22 |
| - |
23 |
| - const persisted = (metadata?.reasoning as { startedAt?: number; elapsedMs?: number } | undefined) || {} |
24 |
| - const startedAtRef = useRef<number>(persisted.startedAt ?? Date.now()) |
25 |
| - const [elapsed, setElapsed] = useState<number>(persisted.elapsedMs ?? 0) |
26 |
| - |
27 |
| - // Initialize startedAt on first mount if missing (persist to task) |
28 |
| - useEffect(() => { |
29 |
| - if (!persisted.startedAt && isLast) { |
30 |
| - vscode.postMessage({ |
31 |
| - type: "updateMessageReasoningMeta", |
32 |
| - messageTs: ts, |
33 |
| - reasoningMeta: { startedAt: startedAtRef.current }, |
34 |
| - } as any) |
35 |
| - } |
36 |
| - // eslint-disable-next-line react-hooks/exhaustive-deps |
37 |
| - }, [ts]) |
38 |
| - |
39 |
| - // Tick while active (last row and streaming) |
40 |
| - useEffect(() => { |
41 |
| - const active = isLast && isStreaming |
42 |
| - if (!active) return |
43 |
| - |
44 |
| - const tick = () => setElapsed(Date.now() - startedAtRef.current) |
45 |
| - tick() |
46 |
| - const id = setInterval(tick, 1000) |
47 |
| - return () => clearInterval(id) |
48 |
| - }, [isLast, isStreaming]) |
49 |
| - |
50 |
| - // Persist final elapsed when streaming stops |
51 |
| - const wasActiveRef = useRef<boolean>(false) |
52 |
| - useEffect(() => { |
53 |
| - const active = isLast && isStreaming |
54 |
| - if (wasActiveRef.current && !active) { |
55 |
| - const finalMs = Date.now() - startedAtRef.current |
56 |
| - setElapsed(finalMs) |
57 |
| - vscode.postMessage({ |
58 |
| - type: "updateMessageReasoningMeta", |
59 |
| - messageTs: ts, |
60 |
| - reasoningMeta: { startedAt: startedAtRef.current, elapsedMs: finalMs }, |
61 |
| - } as any) |
62 |
| - } |
63 |
| - wasActiveRef.current = active |
64 |
| - }, [isLast, isStreaming, ts]) |
65 |
| - |
66 |
| - const displayMs = useMemo(() => { |
67 |
| - if (isLast && isStreaming) return elapsed |
68 |
| - return persisted.elapsedMs ?? elapsed |
69 |
| - }, [elapsed, isLast, isStreaming, persisted.elapsedMs]) |
70 |
| - |
71 |
| - const seconds = Math.max(0, Math.floor((displayMs || 0) / 1000)) |
72 |
| - const secondsLabel = t("chat:reasoning.seconds", { count: seconds }) |
73 |
| - |
| 8 | +export const ReasoningBlock = ({ content }: ReasoningBlockProps) => { |
| 9 | + if (!content?.trim()) return null |
74 | 10 | return (
|
75 |
| - <div className="py-1"> |
76 |
| - <div className="flex items-center justify-between mb-[10px]"> |
77 |
| - <div className="flex items-center gap-2"> |
78 |
| - <span className="codicon codicon-light-bulb" style={{ color: "var(--vscode-charts-yellow)" }} /> |
79 |
| - <span className="font-bold text-vscode-foreground">{t("chat:reasoning.thinking")}</span> |
80 |
| - </div> |
81 |
| - <span className="text-vscode-foreground tabular-nums flex items-center gap-1"> |
82 |
| - <span className="codicon codicon-clock" style={{ fontSize: "inherit" }} /> |
83 |
| - {secondsLabel} |
84 |
| - </span> |
85 |
| - </div> |
86 |
| - {(content?.trim()?.length ?? 0) > 0 && ( |
87 |
| - <div className="px-3 italic text-vscode-descriptionForeground"> |
88 |
| - <MarkdownBlock markdown={content} /> |
89 |
| - </div> |
90 |
| - )} |
| 11 | + <div className="px-3 py-1 italic text-vscode-descriptionForeground"> |
| 12 | + <MarkdownBlock markdown={content} /> |
91 | 13 | </div>
|
92 | 14 | )
|
93 | 15 | }
|
0 commit comments