// This returns the flags passed into your Elm application export const flags = async ({ env }: ElmLand.FlagsArgs) => { // Get user's preferred font size by creating a temporary element const tempDiv = document.createElement("div"); tempDiv.style.fontSize = "1rem"; tempDiv.style.position = "absolute"; tempDiv.style.visibility = "hidden"; document.body.appendChild(tempDiv); const baseFontSize = parseFloat(window.getComputedStyle(tempDiv).fontSize); document.body.removeChild(tempDiv); return { width: window.innerWidth, height: window.innerHeight, baseFontSize: baseFontSize, }; }; // This function is called after your Elm app starts export const onReady = ({ app, env }: ElmLand.OnReadyArgs) => { console.log("Elm is ready", app); // Simple loading screen with minimum display time let loadingComplete = false; function completeLoading() { if (!loadingComplete && app.ports?.assetsLoaded?.send) { loadingComplete = true; console.log("Loading complete - starting fade out sequence"); app.ports.assetsLoaded.send(null); } } // Set up the loading completion trigger if (app.ports?.checkAssetsLoaded?.subscribe) { app.ports.checkAssetsLoaded.subscribe(() => { console.log("Elm app ready - starting loading timer"); // Minimum display time for loading screen (so users can see it) const minDisplayTime = 1200; // 1.2 seconds // Check if fonts are loaded, then wait for minimum time if (document.fonts) { document.fonts.ready.then(() => { console.log( "Fonts loaded, waiting for minimum display time of", minDisplayTime, "ms", ); setTimeout(() => { console.log("Minimum display time elapsed, triggering fade out"); completeLoading(); }, minDisplayTime); }); } else { // Fallback if fonts API not supported console.log("Fonts API not supported, using fallback timing"); setTimeout(completeLoading, minDisplayTime + 300); } }); } // Determine initial theme first let initialTheme = "dark"; // Match Elm's hardcoded default const savedTheme = localStorage.getItem("app-theme"); if (savedTheme) { initialTheme = savedTheme; } else if (window.matchMedia) { const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); initialTheme = mediaQuery.matches ? "dark" : "light"; } // Store the determined initial theme localStorage.setItem("app-theme", initialTheme); // Handle saving theme to localStorage AND toggle requests if (app.ports?.saveTheme?.subscribe) { app.ports.saveTheme.subscribe((themeOrSignal: unknown) => { if (themeOrSignal === "toggle") { // Handle toggle request - now we know localStorage has the current theme const currentTheme = localStorage.getItem("app-theme") || initialTheme; const newTheme = currentTheme === "light" ? "dark" : "light"; console.log("Toggling theme from", currentTheme, "to", newTheme); localStorage.setItem("app-theme", newTheme); // Send new theme back to Elm if (app.ports?.loadTheme?.send) { app.ports.loadTheme.send(newTheme); } } else { // Handle normal theme saving (when theme is set directly) console.log("Saving theme:", themeOrSignal); localStorage.setItem("app-theme", themeOrSignal as string); } }); } // Load theme from localStorage on startup if (app.ports?.loadTheme?.send) { console.log("Loading initial theme:", initialTheme); app.ports.loadTheme.send(initialTheme); } // Optional: Listen for system theme changes if (window.matchMedia) { const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); mediaQuery.addEventListener("change", (e) => { // Only apply if user hasn't manually saved a theme preference if (!savedTheme) { const systemTheme = e.matches ? "dark" : "light"; console.log("System theme changed:", systemTheme); localStorage.setItem("app-theme", systemTheme); if (app.ports?.loadTheme?.send) { app.ports.loadTheme.send(systemTheme); } } }); } }; // Type definitions for Elm Land namespace ElmLand { export type FlagsArgs = { env: Record; }; export type OnReadyArgs = { env: Record; app: { ports?: Record }; }; export type Port = { send?: (data: unknown) => void; subscribe?: (callback: (data: unknown) => unknown) => void; }; }