返回 DeepSeek-Reasonix
useArchivedToolData.ts
根目录 / desktop / frontend / src / lib / useArchivedToolData.ts
1 import { useEffect, useMemo, useState } from "react";
2 import { app, type AppBindings } from "./bridge";
3 import type { Item } from "./useController";
4
5 type Tool = Extract<Item, { kind: "tool" }>;
6 type Data = NonNullable<Awaited<ReturnType<AppBindings["ToolResultForTab"]>>>;
7 type Source = { item: Tool; tabId: string | undefined };
8 type Result = { source: Source; data?: Data; failed?: boolean };
9
10 /** Full payloads belong to one tab and one immutable tool snapshot. Filtering
11 * during render prevents a previous payload from flashing before effect cleanup. */
12 export function useArchivedToolData(item: Tool, tabId: string | undefined, open: boolean) {
13 const source = useMemo(() => ({ item, tabId }), [item, tabId]);
14 const [result, setResult] = useState<Result>();
15 const [attempt, setAttempt] = useState(0);
16 const current = result?.source === source ? result : undefined;
17 const data = item.dataArchived ? current?.data ?? null : null;
18 useEffect(() => {
19 if (!open || !source.item.dataArchived || !source.tabId || data) return;
20 let cancelled = false;
21 setResult({ source });
22 void app.ToolResultForTab(source.tabId, source.item.id).then(value => {
23 if (cancelled) return;
24 setResult(value && typeof value.args === "string"
25 ? { source, data: value }
26 : { source, failed: true });
27 }).catch(() => {
28 if (!cancelled) setResult({ source, failed: true });
29 });
30 return () => { cancelled = true; };
31 }, [source, open, data, attempt]);
32 const failed = Boolean(item.dataArchived && (!tabId || current?.failed));
33 return {
34 data,
35 loading: Boolean(open && item.dataArchived && !data && !failed),
36 failed,
37 retry: () => setAttempt(value => value + 1),
38 };
39 }
40
40 lines TYPESCRIPT