返回 DeepSeek-Reasonix
useConfigLoadWarnings.ts
根目录 / desktop / frontend / src / lib / useConfigLoadWarnings.ts
1 import { desktopHost } from "./desktopHost";
2 import { useCallback, useEffect, useRef, useState } from "react";
3
4 export interface ConfigLoadWarningsSnapshot {
5 warnings: string[];
6 revision: number;
7 }
8
9 export function normalizeConfigLoadWarnings(payload: unknown): string[] {
10 if (!Array.isArray(payload)) return [];
11 const seen = new Set<string>();
12 const warnings: string[] = [];
13 for (const value of payload) {
14 if (typeof value !== "string") continue;
15 const warning = value.trim();
16 if (!warning || seen.has(warning)) continue;
17 seen.add(warning);
18 warnings.push(warning);
19 }
20 return warnings;
21 }
22
23 export function configLoadWarningsKey(warnings: readonly string[]): string {
24 return warnings.length > 0 ? JSON.stringify(warnings) : "";
25 }
26
27 export function normalizeConfigLoadWarningsRevision(value: unknown): number {
28 return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
29 }
30
31 export function normalizeConfigLoadWarningsEvent(payload: unknown, revisionValue?: unknown): ConfigLoadWarningsSnapshot | null {
32 if (Array.isArray(payload)) {
33 const warnings = normalizeConfigLoadWarnings(payload);
34 return warnings.length > 0 ? { warnings, revision: normalizeConfigLoadWarningsRevision(revisionValue) } : null;
35 }
36 if (!payload || typeof payload !== "object") return null;
37 const record = payload as Record<string, unknown>;
38 const warnings = normalizeConfigLoadWarnings(record.warnings);
39 if (warnings.length === 0) return null;
40 return { warnings, revision: normalizeConfigLoadWarningsRevision(record.revision) };
41 }
42
43 export function subscribeConfigLoadWarnings(cb: (snapshot: ConfigLoadWarningsSnapshot) => void): () => void {
44 const host = desktopHost();
45 if (host.kind !== "none") {
46 return host.events.on("config:load-warnings", (payload?: unknown, revision?: unknown) => {
47 const snapshot = normalizeConfigLoadWarningsEvent(payload, revision);
48 if (snapshot) cb(snapshot);
49 });
50 }
51 return () => {};
52 }
53
54 export function useConfigLoadWarnings() {
55 const [configLoadWarnings, setConfigLoadWarnings] = useState<string[]>([]);
56 const latestRevision = useRef(0);
57 const seenKeys = useRef(new Set<string>());
58 const present = useCallback((payload: unknown, revisionValue: unknown, resetSeen = false) => {
59 const revision = normalizeConfigLoadWarningsRevision(revisionValue);
60 if (revision < latestRevision.current) return;
61 latestRevision.current = revision;
62 const warnings = normalizeConfigLoadWarnings(payload);
63 if (resetSeen) seenKeys.current.clear();
64 const key = configLoadWarningsKey(warnings);
65 if (!key) {
66 seenKeys.current.clear();
67 setConfigLoadWarnings([]);
68 return;
69 }
70 if (seenKeys.current.has(key)) return;
71 seenKeys.current.add(key);
72 setConfigLoadWarnings(warnings);
73 }, []);
74
75 useEffect(() => subscribeConfigLoadWarnings((snapshot) => {
76 present(snapshot.warnings, snapshot.revision);
77 }), [present]);
78
79 const applySnapshot = useCallback((payload: unknown, revision: unknown) => {
80 present(payload, revision);
81 }, [present]);
82 const reload = useCallback((payload: unknown, revision: unknown) => present(payload, revision, true), [present]);
83 const dismiss = useCallback(() => setConfigLoadWarnings([]), []);
84
85 return { configLoadWarnings, applySnapshot, reload, dismiss };
86 }
87
87 lines TYPESCRIPT