返回 DeepSeek-Reasonix
ModelApplicationRecovery.tsx
根目录 / desktop / frontend / src / components / ModelApplicationRecovery.tsx
1 import { ErrorMessage } from "./ErrorMessage";
2 import { useEffect, useState } from "react";
3 import { app } from "../lib/bridge";
4 import { useT } from "../lib/i18n";
5 import { appliedOnce, type ModelApplicationChoice, type ModelApplicationDetails } from "../lib/modelApplication";
6 import { useModelApplicationStatus } from "../lib/useModelApplicationStatus";
7
8 type RemoteApplication = { key: string; text: string; details: ModelApplicationDetails };
9 export function ComposerModelApplicationRecovery({ tabId, local, remote, setRemote, draftKey, text, running, onUseApplied }: {
10 tabId?: string;
11 local: { application?: ModelApplicationDetails; setApplication(value: ModelApplicationDetails | undefined): void; blocked: boolean };
12 remote?: RemoteApplication;
13 setRemote(value: RemoteApplication | undefined): void;
14 draftKey: string; text: string; running: boolean;
15 onUseApplied(choice: ModelApplicationChoice): void;
16 }) {
17 const currentRemote = remote?.key === draftKey && remote.text === text ? remote : undefined;
18 const details = local.application ?? currentRemote?.details;
19 if (!tabId || !details) return null;
20 const change = local.application ? local.setApplication : (value: ModelApplicationDetails | undefined) => setRemote(value && currentRemote ? { ...currentRemote, details: value } : undefined);
21 return <ModelApplicationRecovery key={details.runtimeIdentity + details.desiredRevision} tabId={tabId} details={details} onChange={change} blocked={local.blocked || running} onUseApplied={onUseApplied} />;
22 }
23
24 export function ModelApplicationRecovery({ tabId, details, onChange, onUseApplied, blocked }: {
25 tabId: string;
26 details: ModelApplicationDetails;
27 onChange: (value: ModelApplicationDetails | undefined) => void;
28 onUseApplied: (choice: ModelApplicationChoice) => void;
29 blocked: boolean;
30 }) {
31 const status = useModelApplicationStatus(tabId);
32 useEffect(() => {
33 if (status?.application === "applied") onChange(undefined);
34 }, [status, onChange]);
35 const t = useT();
36 const [busy, setBusy] = useState(false);
37 const [expanded, setExpanded] = useState(false);
38 const [selected, setSelected] = useState<string[]>([]);
39 const [error, setError] = useState("");
40 const applying = busy || details.applying;
41 const allows = (action: string) => !details.availableActions || details.availableActions.includes(action);
42 const retry = async (stop = false) => {
43 setBusy(true);
44 setError("");
45 try {
46 if (stop) await app.CancelModelApplicationBlockers?.(tabId, appliedOnce(details), selected);
47 const result = await app.RetryModelSettingsApplication(tabId);
48 const target = result.targets.find(target => target.tabId === tabId);
49 if (target?.application === "applied") onChange(undefined);
50 else if (target?.details) onChange(target.details);
51 else setError(t("modelApply.failed"));
52 } catch {
53 setError(t("modelApply.failed"));
54 } finally {
55 setBusy(false);
56 }
57 };
58 return <div role="alert" className="model-application-recovery">
59 <span>{t(details.code === "model_settings_pending" ? "modelApply.pending" : "modelApply.failed", { n: details.blockingJobs.length })}</span>
60 <span>{t("modelApply.current", { model: details.model })}</span>
61 {details.connectionTarget && <span>{details.connectionTarget}</span>}
62 {details.blockingJobs.length > 0 && <button type="button" onClick={() => setExpanded(!expanded)}>{t("modelApply.jobs")}</button>}
63 {allows("retry") && <button type="button" disabled={applying} onClick={() => void retry()}>{t("modelApply.retry")}</button>}
64 {details.canUseApplied && allows("applied_once") && app.StartTurnWithModelApplication &&
65 <button type="button" disabled={blocked || applying} onClick={() => onUseApplied(appliedOnce(details))}>{t("modelApply.useCurrent")}</button>}
66 {expanded && <div className="model-application-recovery__jobs">
67 {details.blockingJobs.map(job => <label key={job.id}>
68 <input type="checkbox" checked={selected.includes(job.id)} onChange={event => setSelected(event.target.checked ? [...selected, job.id] : selected.filter(id => id !== job.id))} />
69 {job.label || job.id}
70 </label>)}
71 {app.CancelModelApplicationBlockers && allows("stop_selected") &&
72 <button type="button" disabled={applying || selected.length === 0} onClick={() => void retry(true)}>{t("modelApply.stopSelected")}</button>}
73 </div>}
74 {!details.canUseApplied && details.continuationUnavailable &&
75 <details><summary>{t("modelApply.details")}</summary>{details.continuationUnavailable}</details>}
76 {error && <span><ErrorMessage error={error} /></span>}
77 </div>;
78 }
79
79 lines Plain Text