返回 DeepSeek-Reasonix
reasoningDisplayPreference.ts
根目录 / desktop / frontend / src / lib / reasoningDisplayPreference.ts
1 import { useSyncExternalStore } from "react";
2 import { applySessionExperience } from "./sessionExperience";
3
4 export type ReasoningDisplayMode = "hidden" | "summary" | "auto" | "expanded";
5 export type ResolvedReasoningDisplayMode = ReasoningDisplayMode | "legacy-collapsed" | "pending";
6
7 const LEGACY_SUMMARY_KEY = "reasonix-reasoning-summary";
8 const DISPLAY_EVENT = "reasonix:reasoning-display-mode";
9
10 let currentMode: ResolvedReasoningDisplayMode = "auto";
11 let currentModeExplicit = false;
12 const listeners = new Set<() => void>();
13
14 function emit(): void {
15 for (const listener of listeners) listener();
16 if (typeof window !== "undefined") window.dispatchEvent(new CustomEvent(DISPLAY_EVENT, { detail: currentMode }));
17 }
18
19 function validMode(value: unknown): ReasoningDisplayMode | undefined {
20 return value === "hidden" || value === "summary" || value === "auto" || value === "expanded" ? value : undefined;
21 }
22
23 function legacyValue(): boolean | undefined {
24 if (typeof localStorage === "undefined") return undefined;
25 const stored = localStorage.getItem(LEGACY_SUMMARY_KEY);
26 return stored === "1" ? true : stored === "0" ? false : undefined;
27 }
28
29 export function resolveReasoningDisplayMode(
30 configuredMode: unknown,
31 explicit: boolean,
32 ): ResolvedReasoningDisplayMode {
33 const normalized = validMode(configuredMode);
34 if (explicit && normalized) return normalized;
35 const legacy = legacyValue();
36 if (legacy !== undefined) return legacy ? "summary" : "legacy-collapsed";
37 return normalized ?? "auto";
38 }
39
40 export function getReasoningDisplayMode(): ResolvedReasoningDisplayMode {
41 if (!currentModeExplicit && currentMode !== "pending") {
42 const legacy = legacyValue();
43 if (legacy !== undefined) return legacy ? "summary" : "legacy-collapsed";
44 }
45 return currentMode;
46 }
47
48 export function setReasoningDisplayPending(): void {
49 if (currentMode === "pending") return;
50 currentMode = "pending";
51 currentModeExplicit = false;
52 emit();
53 }
54
55 /** Hydrates the frontend mirror from the authoritative desktop startup payload. */
56 export function hydrateReasoningDisplayMode(configuredMode: unknown, explicit = false): void {
57 const next = resolveReasoningDisplayMode(configuredMode, explicit);
58 // Compatibility callers still participate in the canonical two-state
59 // model. Historical configuration is intentionally normalized to Standard;
60 // only an explicit legacy "expanded" selection maps to Deep.
61 if (explicit) {
62 applySessionExperience(next === "expanded" ? "deep" : "standard");
63 }
64 if (next === currentMode && currentModeExplicit === explicit) return;
65 currentMode = next;
66 currentModeExplicit = explicit;
67 emit();
68 }
69
70 /** Applies a successfully persisted user selection and completes legacy migration. */
71 export function applyReasoningDisplayMode(mode: ReasoningDisplayMode): void {
72 applySessionExperience(mode === "expanded" ? "deep" : "standard");
73 if (typeof localStorage !== "undefined") localStorage.removeItem(LEGACY_SUMMARY_KEY);
74 if (mode === currentMode && currentModeExplicit) return;
75 currentMode = mode;
76 currentModeExplicit = true;
77 emit();
78 }
79
80 export function onReasoningDisplayModeChange(cb: () => void): () => void {
81 listeners.add(cb);
82 return () => listeners.delete(cb);
83 }
84
85 export function useReasoningDisplayMode(): ResolvedReasoningDisplayMode {
86 return useSyncExternalStore(onReasoningDisplayModeChange, getReasoningDisplayMode, getReasoningDisplayMode);
87 }
88
89 // Compatibility helpers for older frontend tests/extensions. They keep the
90 // legacy localStorage key semantics without reintroducing the old settings UI.
91 export function getReasoningSummaryEnabled(): boolean {
92 const mode = getReasoningDisplayMode();
93 return mode === "summary" || mode === "auto";
94 }
95
96 export function setReasoningSummaryEnabled(enabled: boolean): void {
97 if (typeof localStorage !== "undefined") localStorage.setItem(LEGACY_SUMMARY_KEY, enabled ? "1" : "0");
98 if (currentMode !== "summary" && currentMode !== "legacy-collapsed") return;
99 currentMode = enabled ? "summary" : "legacy-collapsed";
100 currentModeExplicit = false;
101 emit();
102 }
103
104 export function useReasoningSummaryEnabled(): boolean {
105 const mode = useReasoningDisplayMode();
106 return mode === "summary" || mode === "auto";
107 }
108
108 lines TYPESCRIPT