/** * Toast System Module * * Provides toast notification functionality using Svelte 5 components * with proper DOM ready checking and theme support. */ import { mount, flushSync } from 'svelte'; import ToastContainer from '$components/toast/ToastContainer.svelte'; import { ensureShadowMount } from './shadowHost'; export interface ToastConfig { theme?: 'light' | 'dark'; } // Type definitions for window globals declare global { interface Window { __dumber_toast_loaded?: boolean; __dumber_initial_theme?: string; } } let toastInitialized = false; // Apply theme based on current system/GTK preference function updateTheme(isDark: boolean): void { if (isDark) { document.documentElement.classList.add('dark'); } else { document.documentElement.classList.remove('dark'); } } export async function initializeToast(config?: ToastConfig): Promise { if (toastInitialized || window.__dumber_toast_loaded) { // Verify functions still exist - if not, reinitialize if (typeof window.__dumber_showToast === 'function' && typeof window.__dumber_showZoomToast === 'function') { console.log('✅ Toast system already initialized and functional'); return; } // If functions are missing, reinitialize console.log('⚠️ Toast functions missing, reinitializing...'); toastInitialized = false; window.__dumber_toast_loaded = false; } console.log('🚀 Initializing Svelte toast system at:', document.readyState); console.log('📊 DOM state check - head:', !!document.head, 'body:', !!document.body); window.__dumber_toast_loaded = true; // Ensure DOM is ready if (document.readyState === 'loading') { return new Promise((resolve) => { document.addEventListener('DOMContentLoaded', async () => { await initializeToast(config); resolve(); }); }); } if (!document.head || !document.body) { console.warn('❌ DOM structure not ready, deferring toast initialization. Retrying...'); window.__dumber_toast_loaded = false; // Reset the flag so we can try again return new Promise((resolve) => { const checkDOM = () => { if (document.head && document.body) { console.log('✅ DOM structure now ready, initializing toast system'); initializeToast(config).then(() => resolve()); } else { console.log('⏳ DOM still not ready, retrying in 50ms...'); setTimeout(checkDOM, 50); } }; setTimeout(checkDOM, 100); }); } try { // Use the global Shadow DOM host for isolation const rootElement = ensureShadowMount('dumber-toast'); // Initialize theme const initialTheme = config?.theme || window.__dumber_initial_theme; if (initialTheme !== undefined) { updateTheme(initialTheme === 'dark'); } // Mount the Svelte toast container (Svelte 5 syntax) mount(ToastContainer, { target: rootElement as unknown as Element }); // Signal isolated GUI readiness for page-world bridge try { document.documentElement.setAttribute('data-dumber-gui', 'ready'); document.dispatchEvent(new CustomEvent('dumber:gui-ready')); } catch { // Ignore errors in GUI readiness signaling } // Listen for page-world bridge events and forward to internal toast functions const handleShowToast = (e: Event) => { const detail = (e as CustomEvent).detail || {}; if (typeof window.__dumber_showToast === 'function') { window.__dumber_showToast(detail.message, detail.duration, detail.type); } }; document.addEventListener('dumber:showToast', handleShowToast); document.addEventListener('dumber:toast:show', handleShowToast); const handleZoomToast = (e: Event) => { const detail = (e as CustomEvent).detail || {}; if (typeof window.__dumber_showZoomToast === 'function') { window.__dumber_showZoomToast(detail.level); } }; document.addEventListener('dumber:showZoomToast', handleZoomToast); document.addEventListener('dumber:toast:zoom', handleZoomToast); // Force immediate effect execution to ensure onMount callbacks run flushSync(); // Toast functions are now exposed by the component itself console.log('✅ Toast functions exposed via component instantiation'); toastInitialized = true; console.log('✅ Toast system initialized with Svelte 5'); } catch (error) { console.error('❌ Failed to initialize toast system:', error); // Fallback to basic toast system if Svelte fails initializeFallbackToast(); } } function exposeToastFunctions(): void { // The Svelte ToastContainer component already exposes these functions // We just need to make sure they exist, they're handled by the component console.log('✅ Toast functions will be exposed by ToastContainer component'); } function initializeFallbackToast(): void { console.warn('🔄 Falling back to basic toast system'); // Create basic toast container for fallback const fallbackContainer = document.createElement('div'); fallbackContainer.id = 'dumber-fallback-toast'; fallbackContainer.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 10000; pointer-events: none; `; document.documentElement.appendChild(fallbackContainer); // Expose toast functions for fallback too exposeToastFunctions(); toastInitialized = true; console.log('✅ Fallback toast system ready'); }