From 7744b386d28d71594d264e44baec69d5faa81466 Mon Sep 17 00:00:00 2001 From: NimBold Date: Thu, 27 Aug 2026 23:43:50 +0330 Subject: [PATCH] test(startup): isolate post-load log read (#37) - Issue #37: compare the post-load bool IPC command with the failing startup control.\n- Keep this diagnostic limited to the Windows/Linux packaged smoke workflow.\n\nRefs #37. --- src/main.tsx | 185 +++------------------------------------------------ 1 file changed, 11 insertions(+), 174 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index d3b0b54..83c59da 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,178 +1,15 @@ -import { StrictMode, type ComponentType } from "react"; -import { createRoot } from "react-dom/client"; -import "@fontsource-variable/inter/wght.css"; -import "@fontsource-variable/noto-sans-hebrew/wght.css"; -import "@fontsource-variable/noto-sans-sc/wght.css"; -import "@fontsource-variable/outfit/wght.css"; -import "@fontsource-variable/roboto/wght.css"; -import "@fontsource-variable/vazirmatn/wght.css"; -import "./index.css"; -import { i18nReady } from "./i18n"; -import { ErrorBoundary } from "./components/ErrorBoundary"; -import { ToastProvider } from "./contexts/ToastContext"; -import { error as logError, warn as logWarn, initLogger } from "./utils/logger"; -import { getCurrentWindow } from '@tauri-apps/api/window'; -import { invokeCommand as invoke } from './ipc'; - -const isPropertiesWindow = getCurrentWindow().label.startsWith('properties-'); - -// WebView2 can overflow its native call stack when the renderer sends IPC -// during document bootstrapping. Keep all renderer-to-native startup work -// behind the document load boundary, not just the first logger query. This is -// also needed for Zustand persistence, whose module initialization reads the -// native database before React mounts. -const documentLoaded = new Promise((resolve) => { - const releaseAfterNativeLoad = () => { - // The load event is dispatched from WebView2's navigation callback. Move - // renderer startup to the next task so its first IPC cannot re-enter that - // native callback stack. - window.setTimeout(resolve, 0); - }; - - if (document.readyState === 'complete') { - releaseAfterNativeLoad(); - return; - } - window.addEventListener('load', releaseAfterNativeLoad, { once: true }); -}); - -void documentLoaded.then(() => { - void initLogger(); -}); - -const serializeConsoleArguments = (values: unknown[]) => values.map(value => { - if (value instanceof Error) return `${value.name}: ${value.message}\n${value.stack || ''}`; - if (typeof value === 'string') return value; - try { - return JSON.stringify(value); - } catch { - return String(value); - } -}).join(' '); - -const redactConsoleMessage = (message: string) => message - .replace(/(authorization|cookie|password|token|secret)\s*[:=]\s*([^\s,;]+)/gi, '$1=[redacted]') - .replace(/(https?:\/\/[^\s?]+)\?[^\s]+/g, '$1?[redacted]'); - -const originalConsoleError = console.error.bind(console); -const originalConsoleWarn = console.warn.bind(console); -console.error = (...values: unknown[]) => { - originalConsoleError(...values); - const message = redactConsoleMessage(serializeConsoleArguments(values)); - void documentLoaded.then(() => logError(message)).catch(() => undefined); -}; -console.warn = (...values: unknown[]) => { - originalConsoleWarn(...values); - const message = redactConsoleMessage(serializeConsoleArguments(values)); - void documentLoaded.then(() => logWarn(message)).catch(() => undefined); -}; +import { invoke } from "@tauri-apps/api/core"; const rootElement = document.getElementById("root"); -const renderRoot = (RootComponent: ComponentType) => { - if (!rootElement) return; +if (rootElement) rootElement.textContent = "Firelink startup control"; - createRoot(rootElement).render( - - - - - - - , - ); -}; - -const PropertiesStartupFailure = () => ( -
-

Download Properties could not be loaded.

- -
-); - -const MainStartupFailure = () => ( -
-

Firelink could not be loaded.

- -
-); - -const renderMainApp = async () => { - if (!rootElement) return; - - await documentLoaded; - - try { - // Keep the child entrypoint isolated from the main application module. App - // imports the persistent Zustand stores, whose module initialization issues - // main-window-only IPC commands. Loading it in a Properties child creates a - // second persistence owner and can race the bridge handshake. - const RootComponent = (await import('./App')).default; - renderRoot(RootComponent); - } catch (error) { - console.error('Failed to initialize Firelink:', error); - renderRoot(MainStartupFailure); - } -}; - -const renderPropertiesApp = async () => { - if (!rootElement) return; - - await documentLoaded; - - try { - // Properties starts with the synchronous English catalog and changes locale - // after its first paint. Waiting for a lazy locale chunk here delays the - // loading shell and makes native window startup visible to the user. - const RootComponent = (await import('./components/PropertiesWindowApp')).PropertiesWindowApp; - renderRoot(RootComponent); - } catch (error) { - // A failed lazy chunk must not leave the native window hidden forever. Show - // a styled, closable failure state and use the same caller-validated native - // reveal command as the normal child path. - console.error('Failed to initialize the Properties window:', error); - renderRoot(PropertiesStartupFailure); - const fallbackSessionId = crypto.randomUUID(); - void invoke('properties_window_send_ready', { sessionId: fallbackSessionId }) - .then(() => invoke('properties_window_reveal', { sessionId: fallbackSessionId })) - .catch(revealError => { - console.error('Failed to reveal the Properties startup error:', revealError); - }); - } -}; - -if (isPropertiesWindow) { - void renderPropertiesApp(); -} else { - void i18nReady.then(renderMainApp).catch(error => { - console.error('Failed to initialize localization:', error); - void renderMainApp(); - }); -} - -// Prevent the webview's default context menu ("Reload", etc.) on right-click. -// Individual components that provide custom context menus call preventDefault() -// in their own onContextMenu handlers, which fires before this document-level -// listener and is unaffected. -document.addEventListener('contextmenu', (e) => { - e.preventDefault(); +const documentLoaded = new Promise((resolve) => { + const releaseAfterNativeLoad = () => window.setTimeout(resolve, 0); + if (document.readyState === "complete") releaseAfterNativeLoad(); + else window.addEventListener("load", releaseAfterNativeLoad, { once: true }); +}); + +void documentLoaded.then(async () => { + await invoke("is_log_paused"); + if (rootElement) rootElement.textContent = "Firelink post-load log read"; });