toast.ts RawHistoryFolder
1/**
2 * Toast System Module
3 *
4 * Provides toast notification functionality using Svelte 5 components
5 * with proper DOM ready checking and theme support.
6 */
7
8import { mount, flushSync } from 'svelte';
9import ToastContainer from '$components/toast/ToastContainer.svelte';
10import { ensureShadowMount } from './shadowHost';
11
12export interface ToastConfig {
13 theme?: 'light' | 'dark';
14}
15
16// Type definitions for window globals
17declare global {
18 interface Window {
19 __dumber_toast_loaded?: boolean;
20 __dumber_initial_theme?: string;
21 }
22}
23
24let toastInitialized = false;
25
26// Apply theme based on current system/GTK preference
27function updateTheme(isDark: boolean): void {
28 if (isDark) {
29 document.documentElement.classList.add('dark');
30 } else {
31 document.documentElement.classList.remove('dark');
32 }
33}
34
35export async function initializeToast(config?: ToastConfig): Promise<void> {
36 if (toastInitialized || window.__dumber_toast_loaded) {
37 // Verify functions still exist - if not, reinitialize
38 if (typeof window.__dumber_showToast === 'function' &&
39 typeof window.__dumber_showZoomToast === 'function') {
40 console.log('✅ Toast system already initialized and functional');
41 return;
42 }
43 // If functions are missing, reinitialize
44 console.log('⚠️ Toast functions missing, reinitializing...');
45 toastInitialized = false;
46 window.__dumber_toast_loaded = false;
47 }
48
49 console.log('🚀 Initializing Svelte toast system at:', document.readyState);
50 console.log('📊 DOM state check - head:', !!document.head, 'body:', !!document.body);
51 window.__dumber_toast_loaded = true;
52
53 // Ensure DOM is ready
54 if (document.readyState === 'loading') {
55 return new Promise((resolve) => {
56 document.addEventListener('DOMContentLoaded', async () => {
57 await initializeToast(config);
58 resolve();
59 });
60 });
61 }
62
63 if (!document.head || !document.body) {
64 console.warn('❌ DOM structure not ready, deferring toast initialization. Retrying...');
65 window.__dumber_toast_loaded = false; // Reset the flag so we can try again
66 return new Promise((resolve) => {
67 const checkDOM = () => {
68 if (document.head && document.body) {
69 console.log('✅ DOM structure now ready, initializing toast system');
70 initializeToast(config).then(() => resolve());
71 } else {
72 console.log('⏳ DOM still not ready, retrying in 50ms...');
73 setTimeout(checkDOM, 50);
74 }
75 };
76 setTimeout(checkDOM, 100);
77 });
78 }
79
80 try {
81 // Use the global Shadow DOM host for isolation
82 const rootElement = ensureShadowMount('dumber-toast');
83
84 // Initialize theme
85 const initialTheme = config?.theme || window.__dumber_initial_theme;
86 if (initialTheme !== undefined) {
87 updateTheme(initialTheme === 'dark');
88 }
89
90 // Mount the Svelte toast container (Svelte 5 syntax)
91 mount(ToastContainer, {
92 target: rootElement as unknown as Element
93 });
94
95 // Signal isolated GUI readiness for page-world bridge
96 try {
97 document.documentElement.setAttribute('data-dumber-gui', 'ready');
98 document.dispatchEvent(new CustomEvent('dumber:gui-ready'));
99 } catch {
100 // Ignore errors in GUI readiness signaling
101 }
102
103 // Listen for page-world bridge events and forward to internal toast functions
104 const handleShowToast = (e: Event) => {
105 const detail = (e as CustomEvent).detail || {};
106 if (typeof window.__dumber_showToast === 'function') {
107 window.__dumber_showToast(detail.message, detail.duration, detail.type);
108 }
109 };
110 document.addEventListener('dumber:showToast', handleShowToast);
111 document.addEventListener('dumber:toast:show', handleShowToast);
112
113 const handleZoomToast = (e: Event) => {
114 const detail = (e as CustomEvent).detail || {};
115 if (typeof window.__dumber_showZoomToast === 'function') {
116 window.__dumber_showZoomToast(detail.level);
117 }
118 };
119 document.addEventListener('dumber:showZoomToast', handleZoomToast);
120 document.addEventListener('dumber:toast:zoom', handleZoomToast);
121
122 // Force immediate effect execution to ensure onMount callbacks run
123 flushSync();
124
125 // Toast functions are now exposed by the component itself
126 console.log('✅ Toast functions exposed via component instantiation');
127
128 toastInitialized = true;
129 console.log('✅ Toast system initialized with Svelte 5');
130
131 } catch (error) {
132 console.error('❌ Failed to initialize toast system:', error);
133
134 // Fallback to basic toast system if Svelte fails
135 initializeFallbackToast();
136 }
137}
138
139function exposeToastFunctions(): void {
140 // The Svelte ToastContainer component already exposes these functions
141 // We just need to make sure they exist, they're handled by the component
142 console.log('✅ Toast functions will be exposed by ToastContainer component');
143}
144
145function initializeFallbackToast(): void {
146 console.warn('🔄 Falling back to basic toast system');
147
148 // Create basic toast container for fallback
149 const fallbackContainer = document.createElement('div');
150 fallbackContainer.id = 'dumber-fallback-toast';
151 fallbackContainer.style.cssText = `
152 position: fixed;
153 top: 20px;
154 right: 20px;
155 z-index: 10000;
156 pointer-events: none;
157 `;
158 document.documentElement.appendChild(fallbackContainer);
159
160 // Expose toast functions for fallback too
161 exposeToastFunctions();
162
163 toastInitialized = true;
164 console.log('✅ Fallback toast system ready');
165}