// file: kb_app_demo/frontend/ts/splash.ts // version: 4 //! Splash-screen event handling for the Tauri startup window. import { listen } from "@tauri-apps/api/event"; import type { SplashOrder } from "./bindings/kb_app_demo/splash/SplashOrder"; import { frontendError, frontendDebug, installFrontendConsoleBridge } from "./frontend_log"; const splashTarget = "kb_app_demo.frontend.splash"; const defaultFadeDurationMs = 3000; let activeOpacityFrame: number | null = null; function normalizeDurationMs(value: number | null | undefined): number { if (typeof value !== "number") { return defaultFadeDurationMs; } if (!Number.isFinite(value) || value <= 0) { return defaultFadeDurationMs; } return value; } function easeInOut(progress: number): number { if (progress < 0.5) { return 2 * progress * progress; } return 1 - Math.pow(-2 * progress + 2, 2) / 2; } async function animateOpacity(element: HTMLElement, fromOpacity: number, toOpacity: number, durationMs: number): Promise { frontendDebug(splashTarget, `Animating from ${fromOpacity} to ${toOpacity} over ${durationMs}ms`); if (activeOpacityFrame !== null) { cancelAnimationFrame(activeOpacityFrame); activeOpacityFrame = null; } element.style.opacity = fromOpacity.toString(); element.style.willChange = "opacity"; await new Promise(resolve => requestAnimationFrame(() => resolve())); await new Promise(resolve => { const startedAt = performance.now(); const opacityDelta = toOpacity - fromOpacity; const updateOpacity = (currentTime: number): void => { const elapsedMs = currentTime - startedAt; const rawProgress = Math.min(elapsedMs / durationMs, 1); const easedProgress = easeInOut(rawProgress); const nextOpacity = fromOpacity + opacityDelta * easedProgress; element.style.opacity = nextOpacity.toString(); if (rawProgress >= 1) { element.style.opacity = toOpacity.toString(); element.style.willChange = "auto"; activeOpacityFrame = null; resolve(); return; } activeOpacityFrame = requestAnimationFrame(updateOpacity); }; activeOpacityFrame = requestAnimationFrame(updateOpacity); }); } function addLogMessage(message: string): void { const debugInfo = document.getElementById("debug-info"); if (!debugInfo) { return; } const time = new Date().toLocaleTimeString(); debugInfo.innerHTML += `${time}: ${message}
`; frontendDebug(splashTarget, `addLogMessage: ${message}`); } function addMessage(message: string, status: string): void { const messagesContainer = document.getElementById("messages-container"); if (!messagesContainer) { return; } const messageElement = document.createElement("div"); messageElement.className = `splash-message ${status}`; messageElement.textContent = message; messagesContainer.appendChild(messageElement); messagesContainer.scrollTop = messagesContainer.scrollHeight; frontendDebug(splashTarget, `addMessage: ${message} - ${status}`); } async function handleSplashOrder(order: SplashOrder): Promise { const durationMs = normalizeDurationMs(order.duration_ms); const container = document.getElementById("splash-container"); if (order.order === "fadein" && container) { frontendDebug(splashTarget, `fadein`); await animateOpacity(container, 0, 1, durationMs); return; } if (order.order === "fadeout" && container) { frontendDebug(splashTarget, `fadeout`); await animateOpacity(container, 1, 0, durationMs); return; } if (order.order === "add_msg" && order.msg && order.status) { addMessage(order.msg, order.status); return; } if (order.order === "add_log" && order.msg) { addLogMessage(order.msg); return; } frontendError(splashTarget, `unknown splash order: ${order.order}`); } function initializeSplashDom(): void { const container = document.getElementById("splash-container"); if (container) { container.style.opacity = "0"; container.style.willChange = "opacity"; } installFrontendConsoleBridge(splashTarget); } async function initializeSplashListeners(): Promise { await listen("splash", event => { void handleSplashOrder(event.payload); }); } document.addEventListener("DOMContentLoaded", () => { initializeSplashDom(); void initializeSplashListeners(); frontendDebug(splashTarget, "splash window loaded"); });