From 9f6996dcdbd934601ddb66eea0ddeef1e097e2f1 Mon Sep 17 00:00:00 2001 From: frozenKelp Date: Tue, 9 Jun 2026 15:04:18 +0530 Subject: [PATCH] fix: read idleTimeoutMin from settings; clean up idle + splash canvas fixes --- src/lib/components/chrome/SplashScreen.svelte | 16 ++++++++ src/routes/+layout.svelte | 40 +++++++++++++++++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/lib/components/chrome/SplashScreen.svelte b/src/lib/components/chrome/SplashScreen.svelte index 9a4c06c..31bd9be 100644 --- a/src/lib/components/chrome/SplashScreen.svelte +++ b/src/lib/components/chrome/SplashScreen.svelte @@ -290,6 +290,7 @@ if (logW <= 0 || logH <= 0) return if (logW === lastLogW && logH === lastLogH && scale === lastScale) return lastLogW = logW; lastLogH = logH; lastScale = scale + if (live) cleanup() // release old offscreen canvases before rebuilding at new size const built = buildCards(logW, logH) const stamps = built.cards.map(c => buildStamp(c, scale)) const vig = buildVignette(logW, logH, scale) @@ -339,10 +340,25 @@ function resume() { if (!paused) return; paused = false; raf = requestAnimationFrame(frame) } function onVis() { document.hidden ? pause() : resume() } + // clears all canvas contexts and nulls live state so the GC can collect the offscreen bitmaps + function cleanup() { + if (live) { + live.stamps.forEach(canvas => { + const c = canvas.getContext('2d') + if (c) c.clearRect(0, 0, canvas.width, canvas.height) + }) + const vigCtx = live.vignette.getContext('2d') + if (vigCtx) vigCtx.clearRect(0, 0, live.vignette.width, live.vignette.height) + } + ctx.clearRect(0, 0, el.width, el.height) + live = null + } + document.addEventListener('visibilitychange', onVis) raf = requestAnimationFrame(frame) return () => { cancelAnimationFrame(raf) + cleanup() extraCleanup?.() document.removeEventListener('visibilitychange', onVis) } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 029069a..d0efd2d 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -24,6 +24,7 @@ const POLL_MS = 1500 let pollTimer: ReturnType | null = null + let idleTimer: ReturnType | null = null let polling = false async function pollLoop() { @@ -117,11 +118,14 @@ polling = true pollLoop() + resetIdleTimer() + return () => { polling = false if (pollTimer !== null) { clearTimeout(pollTimer); pollTimer = null } - if (discordInitialized) destroyRpc() - platformService.destroy() + if (idleTimer !== null) { clearTimeout(idleTimer); idleTimer = null } + if (discordInitialized) destroyRpc().catch(() => {}) + platformService.destroy().catch(() => {}) } }) @@ -146,7 +150,37 @@ if (appState.status === 'booting') _splashDismissed = false }) - function onIdleDismiss() { appState.idleSplash = false } + $effect(() => { + if (appState.status === 'ready') resetIdleTimer() + }) + + $effect(() => { + if (appState.status !== 'ready') return + // capture phase so events from any component — including modals — reset the timer + const onActivity = () => resetIdleTimer() + document.addEventListener('mousemove', onActivity, true) + document.addEventListener('keydown', onActivity, true) + document.addEventListener('touchstart', onActivity, true) + document.addEventListener('click', onActivity, true) + return () => { + document.removeEventListener('mousemove', onActivity, true) + document.removeEventListener('keydown', onActivity, true) + document.removeEventListener('touchstart', onActivity, true) + document.removeEventListener('click', onActivity, true) + } + }) + + function resetIdleTimer() { + if (idleTimer) clearTimeout(idleTimer) + appState.idleSplash = false + // read the setting live so changes take effect without a restart + const timeoutMs = (settingsState.settings.idleTimeoutMin ?? 5) * 60_000 + idleTimer = setTimeout(() => { + if (appState.status === 'ready') appState.idleSplash = true + }, timeoutMs) + } + + function onIdleDismiss() { appState.idleSplash = false; resetIdleTimer() } function onSplashRetry() { import('$lib/state/boot.svelte').then(({ retryBoot }) => {