返回 DeepSeek-Reasonix
TurnCheckDetails.tsx
根目录 / desktop / frontend / src / components / TurnCheckDetails.tsx
1 import { useEffect, useState } from "react";
2 import { app } from "../lib/bridge";
3 import { useT } from "../lib/i18n";
4 import { turnCheckText } from "../lib/turnResult";
5 import type { WireCompletionSummary } from "../lib/types";
6
7 function CheckLog({ tabId, sessionPath, toolCallId, toolResultId, liveOutput }: { tabId: string; sessionPath: string; toolCallId?: string; toolResultId?: string; liveOutput?: string }) {
8 const t = useT();
9 const [open, setOpen] = useState(false);
10 const [loaded, setLoaded] = useState<{ key: string; output?: string; loading: boolean; truncated?: boolean }>();
11 const key = `${tabId}\u0000${sessionPath}\u0000${toolCallId ?? ""}\u0000${toolResultId ?? ""}`;
12 useEffect(() => {
13 if (!open || liveOutput !== undefined) return;
14 let active = true;
15 if (!toolCallId || !toolResultId || !sessionPath) return;
16 setLoaded({ key, loading: true });
17 void app.TurnCheckLog(tabId, sessionPath, toolCallId, toolResultId).then(result => {
18 if (active) setLoaded({ key, loading: false, output: result?.output, truncated: result?.truncated });
19 }).catch(() => { if (active) setLoaded({ key, loading: false }); });
20 return () => { active = false; };
21 }, [key, liveOutput, open, sessionPath, tabId, toolCallId, toolResultId]);
22 const value = liveOutput ?? (loaded?.key === key ? loaded.output : undefined);
23 const loading = liveOutput === undefined && loaded?.key === key && loaded.loading;
24 return <details className="turn-check-log" onToggle={e => setOpen(e.currentTarget.open)}>
25 <summary>{t("completion.viewLogs")}</summary>
26 {open && (loading ? <p role="status">{t("completion.loadingResult")}</p> : value !== undefined ? <>{loaded?.key === key && loaded.truncated && <p>{t("completion.logsTruncated")}</p>}<pre>{value}</pre></> : <p>{t("completion.logsUnavailable")}</p>)}
27 </details>;
28 }
29
30 export function TurnCheckDetails({ summary, tabId = "", sessionPath = "" }: { summary: WireCompletionSummary; tabId?: string; sessionPath?: string }) {
31 const t = useT();
32 const checks = summary.receipt?.verifications ?? [];
33 return <div className="turn-check-details">
34 <p className="turn-check-details__status">{turnCheckText(summary, t)}</p>
35 {checks.map((check, index) => <div className="turn-check-details__entry" key={check.toolCallId ?? `${index}:${check.command}`}>
36 <div className="turn-check-details__head"><code>{check.command}</code><span className={check.interrupted || check.stale ? "turn-result-warning" : check.passed ? "turn-result-added" : "turn-result-removed"}>
37 {t(check.interrupted ? "completion.checkInterruptedItem" : check.passed ? "completion.checkPassedItem" : "completion.checkFailedItem")}
38 {check.stale && ` · ${t("completion.checkStaleItem")}`}
39 </span></div>
40 {check.exitCode !== undefined && <div>{t("completion.exitCode", { code: check.exitCode })}</div>}
41 <CheckLog tabId={tabId} sessionPath={sessionPath} toolCallId={check.toolCallId} toolResultId={check.toolResultId} />
42 </div>)}
43 {summary.checking && summary.liveChecks?.map(check => <div className="turn-check-details__entry" key={check.toolCallId}>
44 <div className="turn-check-details__head"><code>{check.command}</code><span>{t("completion.checkRunning")}</span></div>
45 <CheckLog tabId={tabId} sessionPath={sessionPath} toolCallId={check.toolCallId} liveOutput={check.output} />
46 </div>)}
47 {summary.receipt?.assessmentKind === "facts" && Boolean(summary.receipt.gaps?.length || summary.receipt.risks?.length) && <p>{t("completion.modelDeclarations")}</p>}
48 {(summary.receipt?.gaps?.length ?? 0) > 0 && <ul className="turn-check-details__gaps">{summary.receipt!.gaps!.map((gap, index) => <li key={`${index}:${gap.kind}`}>{gap.detail || t("completion.checkUnknown")}</li>)}</ul>}
49 {(summary.receipt?.risks?.length ?? 0) > 0 && <ul className="turn-check-details__gaps">{summary.receipt!.risks!.map((risk, index) => <li key={index}>{risk}</li>)}</ul>}
50 {checks.length > 0 && <p className="turn-check-details__footnote">{t("completion.checksOnly")}</p>}
51 </div>;
52 }
53
53 lines Plain Text