diff --git a/src/main/index.ts b/src/main/index.ts index 92fb73a..b7164a7 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -167,6 +167,18 @@ process.on('uncaughtException', (err) => { console.error('[main] uncaughtException:', err) }) +// Diagnostics for "app froze then recovered" reports: WER logs an +// AppHangTransient but keeps no stack. A stall of THIS (UI) thread is what +// Windows flags as a hang, so measure event-loop lag and say so loudly when +// it happens — that distinguishes a main-process block from a renderer one. +let lastTick = Date.now() +setInterval(() => { + const now = Date.now() + const lag = now - lastTick - 1000 + if (lag > 2000) console.error(`[main] event loop stalled ~${lag}ms`) + lastTick = now +}, 1000).unref() + app.on('before-quit', () => { // Let window 'close' events pass through so teardown completes. markQuitting() diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 771f065..1bf0d34 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -11,6 +11,19 @@ import { useSettingsStore } from './settings/store' import { mix } from './theme/chrome' import './global.css' +// Diagnostics: report renderer main-thread freezes after recovery. console.error +// is surfaced into the dev terminal by the main process's console-message hook, +// so a "卡死又恢复" report tells us which process stalled and for how long. +{ + let lastTick = performance.now() + setInterval(() => { + const now = performance.now() + const lag = now - lastTick - 2000 + if (lag > 3000) console.error(`[renderer] main thread stalled ~${Math.round(lag)}ms`) + lastTick = now + }, 2000) +} + // Dev convenience: allow opening the renderer in a plain browser (vite page) // so layout/UX work doesn't require the electron shell. No-op under electron. if (typeof window !== 'undefined' && !window.api) {