返回 DeepSeek-Reasonix
main.tsx
根目录 / desktop / frontend / src / main.tsx
1 import "./lib/compat";
2 import { StrictMode } from "react";
3 import { createRoot } from "react-dom/client";
4 import App from "./App";
5 import { ErrorBoundary } from "./components/ErrorBoundary";
6 import { installPerformancePressureMonitor } from "./lib/crash";
7 import { installGlobalCrashHandlers } from "./lib/globalCrashHandlers";
8 import { desktopHost } from "./lib/desktopHost";
9 import { installBreadcrumbConsoleHook } from "./lib/breadcrumbs";
10 import { installPerfDebugHook } from "./lib/perfDebug";
11 import { LocaleProvider, preloadDetectedLocale } from "./lib/i18n";
12 import { ToastProvider } from "./lib/toast";
13 import { initFontFamily } from "./lib/fontFamily";
14 import { initTextSize } from "./lib/textSize";
15 import { initTypographyPreferences } from "./lib/typographyPreferences";
16 import { initTheme } from "./lib/theme";
17 import { initConversationWidth } from "./lib/conversationWidth";
18 import appShellStylesheetURL from "./styles.css?url";
19
20 // Install first so startup/runtime failures paint a useful error instead of a
21 // featureless webview background, with the recent console trail attached.
22 installGlobalCrashHandlers();
23 installBreadcrumbConsoleHook();
24 installPerformancePressureMonitor();
25 installPerfDebugHook();
26
27 // Apply the saved appearance (auto/light/dark) before the first paint.
28 function initTypographyPlatform() {
29 if (typeof document === "undefined" || typeof navigator === "undefined") return;
30 const params = new URLSearchParams(window.location.search);
31 const override = params.get("platform");
32 const marker = `${navigator.platform} ${navigator.userAgent}`;
33 const platform =
34 override === "darwin" || override === "windows" || override === "linux"
35 ? override
36 : /Win/i.test(marker)
37 ? "windows"
38 : /Mac/i.test(marker)
39 ? "darwin"
40 : "linux";
41 document.documentElement.setAttribute("data-platform", platform);
42 }
43
44 initTypographyPlatform();
45 initTheme();
46 initConversationWidth();
47 initTextSize();
48 initFontFamily();
49 initTypographyPreferences();
50
51 // Pre-warm font fallback stacks so the first frame doesn't flicker between the
52 // browser default font and the app's configured typeface. Inserting a hidden span
53 // with CJK + emoji + math glyphs forces the OS font subsystem to resolve and
54 // cache the fallback chains before React mounts.
55 function prewarmFontFallbacks() {
56 const span = document.createElement("span");
57 span.style.cssText = "position:absolute;visibility:hidden;font-size:1px;pointer-events:none";
58 span.textContent = "中文日本語한국어 математика 😀🎉✓⚠∑∏∫";
59 document.body.appendChild(span);
60 // Force layout so the browser resolves font fallback chains.
61 void span.offsetHeight;
62 requestAnimationFrame(() => {
63 requestAnimationFrame(() => {
64 span.remove();
65 });
66 });
67 }
68 prewarmFontFallbacks();
69
70
71 // Inside the desktop shell, suppress the webview's default right-click menu — its
72 // Reload / Back / Inspect entries are easy to hit by accident and can reset or
73 // navigate away from the app. Text inputs keep their native Cut/Copy/Paste menu;
74 // the terminal area is exempt so its own context menu can offer copy/paste.
75 // Left alone in a plain browser (pnpm dev) so devtools stay reachable.
76 if (desktopHost().kind !== "none") {
77 window.addEventListener("contextmenu", (e) => {
78 const target = e.target as HTMLElement | null;
79 if (!target?.closest("input, textarea, .chat-transcript") && !target?.closest(".terminal-view")) e.preventDefault();
80 });
81 }
82
83 const root = document.getElementById("root");
84 if (!root) throw new Error("missing #root");
85 const rootElement = root;
86
87 async function mountApp() {
88 // The HTML boot shell paints immediately with critical inline styles. Load
89 // the full stylesheet and detected locale in parallel, then replace that
90 // shell in one React commit so users never see an unstyled application.
91 const preloadLocaleForMount = async () => {
92 await preloadDetectedLocale();
93 };
94 const stylesResult = await Promise.allSettled([
95 new Promise<void>((resolve, reject) => {
96 const link = document.createElement("link");
97 link.rel = "stylesheet";
98 link.href = appShellStylesheetURL;
99 link.onload = () => resolve();
100 link.onerror = () => reject(new Error(`failed to load desktop stylesheet: ${appShellStylesheetURL}`));
101 document.head.appendChild(link);
102 }),
103 preloadLocaleForMount(),
104 ]);
105 const [styleResult, localeResult] = stylesResult;
106 if (styleResult.status === "rejected") {
107 console.error("failed to load desktop stylesheet", styleResult.reason);
108 return;
109 }
110 if (localeResult.status === "rejected") console.error("failed to preload desktop locale", localeResult.reason);
111 createRoot(rootElement).render(
112 <StrictMode>
113 <ErrorBoundary>
114 <LocaleProvider>
115 <ToastProvider>
116 <App />
117 </ToastProvider>
118 </LocaleProvider>
119 </ErrorBoundary>
120 </StrictMode>,
121 );
122
123 void import("./lib/desktopWebViewHeartbeat").then(({ installDesktopWebViewHeartbeat }) => {
124 installDesktopWebViewHeartbeat();
125 });
126 }
127
128 void mountApp();
129
129 lines Plain Text