mirror of
https://github.com/nimbold/Firelink.git
synced 2026-09-01 13:38:01 +00:00
fix(startup): gate renderer IPC on document load (#37)
- Issue #37: Windows 1.4.0 exited with STATUS_STACK_OVERFLOW before showing the main window. - Gate lazy entrypoint imports, Zustand persistence, logger initialization, and console forwarding until WebView2 document load. - Preserve the native Ready reveal and pending-window restore lifecycle across platforms. - Show a closeable startup error if the main entrypoint cannot load. Fixes #37.
This commit is contained in:
@@ -19903,7 +19903,6 @@ pub fn run() {
|
|||||||
mark_main_window_startup_complete(app_handle);
|
mark_main_window_startup_complete(app_handle);
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
reveal_main_window(app_handle);
|
reveal_main_window(app_handle);
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
restore_pending_main_window(app_handle);
|
restore_pending_main_window(app_handle);
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
|||||||
+52
-9
@@ -16,7 +16,22 @@ import { invokeCommand as invoke } from './ipc';
|
|||||||
|
|
||||||
const isPropertiesWindow = getCurrentWindow().label.startsWith('properties-');
|
const isPropertiesWindow = getCurrentWindow().label.startsWith('properties-');
|
||||||
|
|
||||||
void initLogger();
|
// 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<void>((resolve) => {
|
||||||
|
if (document.readyState === 'complete') {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.addEventListener('load', () => resolve(), { once: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
void documentLoaded.then(() => {
|
||||||
|
void initLogger();
|
||||||
|
});
|
||||||
|
|
||||||
const serializeConsoleArguments = (values: unknown[]) => values.map(value => {
|
const serializeConsoleArguments = (values: unknown[]) => values.map(value => {
|
||||||
if (value instanceof Error) return `${value.name}: ${value.message}\n${value.stack || ''}`;
|
if (value instanceof Error) return `${value.name}: ${value.message}\n${value.stack || ''}`;
|
||||||
@@ -36,11 +51,13 @@ const originalConsoleError = console.error.bind(console);
|
|||||||
const originalConsoleWarn = console.warn.bind(console);
|
const originalConsoleWarn = console.warn.bind(console);
|
||||||
console.error = (...values: unknown[]) => {
|
console.error = (...values: unknown[]) => {
|
||||||
originalConsoleError(...values);
|
originalConsoleError(...values);
|
||||||
void logError(redactConsoleMessage(serializeConsoleArguments(values))).catch(() => undefined);
|
const message = redactConsoleMessage(serializeConsoleArguments(values));
|
||||||
|
void documentLoaded.then(() => logError(message)).catch(() => undefined);
|
||||||
};
|
};
|
||||||
console.warn = (...values: unknown[]) => {
|
console.warn = (...values: unknown[]) => {
|
||||||
originalConsoleWarn(...values);
|
originalConsoleWarn(...values);
|
||||||
void logWarn(redactConsoleMessage(serializeConsoleArguments(values))).catch(() => undefined);
|
const message = redactConsoleMessage(serializeConsoleArguments(values));
|
||||||
|
void documentLoaded.then(() => logWarn(message)).catch(() => undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const rootElement = document.getElementById("root");
|
const rootElement = document.getElementById("root");
|
||||||
@@ -75,20 +92,46 @@ const PropertiesStartupFailure = () => (
|
|||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const MainStartupFailure = () => (
|
||||||
|
<main className="flex h-screen min-h-0 flex-col items-center justify-center gap-4 bg-main-bg p-6 text-text-primary">
|
||||||
|
<p role="alert">Firelink could not be loaded.</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="app-button app-button-primary px-3 text-xs"
|
||||||
|
onClick={() => {
|
||||||
|
void getCurrentWindow().close().catch(error => {
|
||||||
|
console.error('[MainStartupFailure] close failed', error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
|
||||||
const renderMainApp = async () => {
|
const renderMainApp = async () => {
|
||||||
if (!rootElement) return;
|
if (!rootElement) return;
|
||||||
|
|
||||||
// Keep the child entrypoint isolated from the main application module. App
|
await documentLoaded;
|
||||||
// imports the persistent Zustand stores, whose module initialization issues
|
|
||||||
// main-window-only IPC commands. Loading it in a Properties child creates a
|
try {
|
||||||
// second persistence owner and can race the bridge handshake.
|
// Keep the child entrypoint isolated from the main application module. App
|
||||||
const RootComponent = (await import('./App')).default;
|
// imports the persistent Zustand stores, whose module initialization issues
|
||||||
renderRoot(RootComponent);
|
// 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 () => {
|
const renderPropertiesApp = async () => {
|
||||||
if (!rootElement) return;
|
if (!rootElement) return;
|
||||||
|
|
||||||
|
await documentLoaded;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Properties starts with the synchronous English catalog and changes locale
|
// Properties starts with the synchronous English catalog and changes locale
|
||||||
// after its first paint. Waiting for a lazy locale chunk here delays the
|
// after its first paint. Waiting for a lazy locale chunk here delays the
|
||||||
|
|||||||
Reference in New Issue
Block a user