Compare commits

..

1 Commits

Author SHA1 Message Date
NimBold 242f161d46 test(startup): validate visible Windows renderer (#37)
- Issue #37: test the full 1.4.0 renderer with normal native window activation.\n- Remove only the Windows hide/focusability gate used by the current main branch.\n\nRefs #37.
2026-08-27 21:48:49 +03:30
3 changed files with 12 additions and 72 deletions
+1 -13
View File
@@ -18553,17 +18553,6 @@ pub fn run() {
main_window_builder = main_window_builder
.inner_size(startup_size.width as f64, startup_size.height as f64)
.prevent_overflow();
#[cfg(target_os = "windows")]
{
// Wry installs its parent WM_SETFOCUS handler while WebView2
// is still being initialized. Keep the host hidden and
// non-activatable until RunEvent::Ready so Windows cannot
// re-enter that handler during native construction.
main_window_builder = main_window_builder
.visible(false)
.focused(false)
.focusable(false);
}
main_window_builder
.build()
.map_err(|error| format!("failed to create main window: {error}"))?;
@@ -19901,8 +19890,7 @@ pub fn run() {
.run(|app_handle, event| match event {
tauri::RunEvent::Ready => {
mark_main_window_startup_complete(app_handle);
#[cfg(target_os = "windows")]
reveal_main_window(app_handle);
#[cfg(not(target_os = "windows"))]
restore_pending_main_window(app_handle);
}
#[cfg(target_os = "macos")]
+2
View File
@@ -20,6 +20,7 @@ import {
} from "./store/useSettingsStore";
import { isPermissionGranted, requestPermission, sendNotification } from '@tauri-apps/plugin-notification';
import { WindowControls } from "./components/WindowControls";
import { PropertiesWindowBridgeHost } from "./components/PropertiesWindowBridgeHost";
import { useToast } from "./contexts/ToastContext";
import { setLogStreamActive } from './utils/logger';
import { updateDockBadge } from './utils/dockBadge';
@@ -1286,6 +1287,7 @@ function App() {
{isAddModalOpen && <AddDownloadsModal />}
<PropertiesWindowBridgeHost />
{isDeleteModalOpen && (
<Suspense fallback={null}>
<DeleteConfirmationModal />
+9 -59
View File
@@ -16,29 +16,7 @@ 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<void>((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();
});
void initLogger();
const serializeConsoleArguments = (values: unknown[]) => values.map(value => {
if (value instanceof Error) return `${value.name}: ${value.message}\n${value.stack || ''}`;
@@ -58,13 +36,11 @@ 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);
void logError(redactConsoleMessage(serializeConsoleArguments(values))).catch(() => undefined);
};
console.warn = (...values: unknown[]) => {
originalConsoleWarn(...values);
const message = redactConsoleMessage(serializeConsoleArguments(values));
void documentLoaded.then(() => logWarn(message)).catch(() => undefined);
void logWarn(redactConsoleMessage(serializeConsoleArguments(values))).catch(() => undefined);
};
const rootElement = document.getElementById("root");
@@ -99,46 +75,20 @@ const PropertiesStartupFailure = () => (
</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 () => {
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);
}
// 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);
};
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