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