refactor(gui): simplify toast system to single source of truth
Fixes regression where zoom keyboard shortcuts (Ctrl+minus, Ctrl+equal,
Ctrl+0) were not showing toast notifications.
Changes:
- Remove all fallback toast implementations following KISS/DRY principles
- Eliminate toast bridge layer from main-world.ts that was overwriting functions
- Simplify toast.ts to only mount ToastContainer.svelte
- Make ToastContainer.svelte the single source of truth for toast functions
- Fix bootstrap initialization check to verify both showToast and showZoomToast
- Add ensureGUIInActivePane("toast") before showing zoom notifications
- Clean up verbose debug logging from zoom handler
The refactored architecture has ToastContainer.svelte expose
window.__dumber_showToast and window.__dumber_showZoomToast directly
when it mounts. No bridges, no events, no fallbacks.- commit
- 95fab16d83c17627a0137eb7a777961137daa984
- author
- bamen · · 2025-10-13 17:49 UTC
- committer
- bamen · · 2025-10-13 19:00 UTC
- parent
- 8c61237
- signature
- unsigned
4 files · +12 -194
M gui/src/injected/bootstrap.ts +2 -62
| @@ -181,7 +181,7 @@ export function bootstrapGUI(): void { | |||
| 181 | 181 | console.log("[GUI] DOM ready, starting initialization"); | |
| 182 | 182 | let toastInitialized = false; | |
| 183 | 183 | ||
| 184 | - | if (typeof window.__dumber_showToast === "function") { | |
| 184 | + | if (typeof window.__dumber_showToast === "function" && typeof window.__dumber_showZoomToast === "function") { | |
| 185 | 185 | console.log("[GUI] Toast already initialized, skipping"); | |
| 186 | 186 | toastInitialized = true; | |
| 187 | 187 | } else { | |
| @@ -195,67 +195,7 @@ export function bootstrapGUI(): void { | |||
| 195 | 195 | console.log("[GUI] Toast initialized successfully"); | |
| 196 | 196 | } catch (e) { | |
| 197 | 197 | console.error("❌ Failed to initialize Svelte toast system:", e); | |
| 198 | - | window.__dumber_showToast = ( | |
| 199 | - | message: string, | |
| 200 | - | duration: number = 2500, | |
| 201 | - | ) => { | |
| 202 | - | document | |
| 203 | - | .querySelectorAll(".dumber-fallback-toast") | |
| 204 | - | .forEach((el) => el.remove()); | |
| 205 | - | ||
| 206 | - | const toast = document.createElement("div"); | |
| 207 | - | toast.className = "dumber-fallback-toast"; | |
| 208 | - | toast.textContent = message; | |
| 209 | - | toast.style.cssText = ` | |
| 210 | - | position: fixed; | |
| 211 | - | bottom: 20px; | |
| 212 | - | right: 20px; | |
| 213 | - | background: rgba(0,0,0,0.9); | |
| 214 | - | color: white; | |
| 215 | - | padding: 12px 16px; | |
| 216 | - | border-radius: 6px; | |
| 217 | - | z-index: 2147483647; | |
| 218 | - | font-family: system-ui, -apple-system, sans-serif; | |
| 219 | - | font-size: 14px; | |
| 220 | - | opacity: 0; | |
| 221 | - | transform: translateX(100%); | |
| 222 | - | transition: all 0.3s ease; | |
| 223 | - | max-width: 300px; | |
| 224 | - | word-wrap: break-word; | |
| 225 | - | `; | |
| 226 | - | ||
| 227 | - | if (document.body) { | |
| 228 | - | document.body.appendChild(toast); | |
| 229 | - | ||
| 230 | - | requestAnimationFrame(() => { | |
| 231 | - | toast.style.opacity = "1"; | |
| 232 | - | toast.style.transform = "translateX(0)"; | |
| 233 | - | }); | |
| 234 | - | ||
| 235 | - | setTimeout(() => { | |
| 236 | - | toast.style.opacity = "0"; | |
| 237 | - | toast.style.transform = "translateX(100%)"; | |
| 238 | - | setTimeout(() => toast.remove(), 300); | |
| 239 | - | }, duration); | |
| 240 | - | } | |
| 241 | - | }; | |
| 242 | - | ||
| 243 | - | window.__dumber_showZoomToast = (level: number) => { | |
| 244 | - | const percentage = Math.round(level * 100); | |
| 245 | - | window.__dumber_showToast!(`Zoom: ${percentage}%`, 1500); | |
| 246 | - | }; | |
| 247 | - | ||
| 248 | - | window.__dumber_dismissToast = (_id?: number) => { | |
| 249 | - | document | |
| 250 | - | .querySelectorAll(".dumber-fallback-toast") | |
| 251 | - | .forEach((el) => el.remove()); | |
| 252 | - | }; | |
| 253 | - | ||
| 254 | - | window.__dumber_clearToasts = () => { | |
| 255 | - | document | |
| 256 | - | .querySelectorAll(".dumber-fallback-toast") | |
| 257 | - | .forEach((el) => el.remove()); | |
| 258 | - | }; | |
| 198 | + | throw e; | |
| 259 | 199 | } | |
| 260 | 200 | } | |
| 261 | 201 | ||
| @@ -181,7 +181,7 @@ export function bootstrapGUI(): void { | |||||
| 181 | console.log("[GUI] DOM ready, starting initialization"); | 181 | console.log("[GUI] DOM ready, starting initialization"); | ||
| 182 | let toastInitialized = false; | 182 | let toastInitialized = false; | ||
| 183 | 183 | ||||
| 184 | - | if (typeof window.__dumber_showToast === "function") { | 184 | + | if (typeof window.__dumber_showToast === "function" && typeof window.__dumber_showZoomToast === "function") { |
| 185 | console.log("[GUI] Toast already initialized, skipping"); | 185 | console.log("[GUI] Toast already initialized, skipping"); | ||
| 186 | toastInitialized = true; | 186 | toastInitialized = true; | ||
| 187 | } else { | 187 | } else { | ||
| @@ -195,67 +195,7 @@ export function bootstrapGUI(): void { | |||||
| 195 | console.log("[GUI] Toast initialized successfully"); | 195 | console.log("[GUI] Toast initialized successfully"); | ||
| 196 | } catch (e) { | 196 | } catch (e) { | ||
| 197 | console.error("❌ Failed to initialize Svelte toast system:", e); | 197 | console.error("❌ Failed to initialize Svelte toast system:", e); | ||
| 198 | - | window.__dumber_showToast = ( | 198 | + | throw e; |
| 199 | - | message: string, | |||
| 200 | - | duration: number = 2500, | |||
| 201 | - | ) => { | |||
| 202 | - | document | |||
| 203 | - | .querySelectorAll(".dumber-fallback-toast") | |||
| 204 | - | .forEach((el) => el.remove()); | |||
| 205 | - | ||||
| 206 | - | const toast = document.createElement("div"); | |||
| 207 | - | toast.className = "dumber-fallback-toast"; | |||
| 208 | - | toast.textContent = message; | |||
| 209 | - | toast.style.cssText = ` | |||
| 210 | - | position: fixed; | |||
| 211 | - | bottom: 20px; | |||
| 212 | - | right: 20px; | |||
| 213 | - | background: rgba(0,0,0,0.9); | |||
| 214 | - | color: white; | |||
| 215 | - | padding: 12px 16px; | |||
| 216 | - | border-radius: 6px; | |||
| 217 | - | z-index: 2147483647; | |||
| 218 | - | font-family: system-ui, -apple-system, sans-serif; | |||
| 219 | - | font-size: 14px; | |||
| 220 | - | opacity: 0; | |||
| 221 | - | transform: translateX(100%); | |||
| 222 | - | transition: all 0.3s ease; | |||
| 223 | - | max-width: 300px; | |||
| 224 | - | word-wrap: break-word; | |||
| 225 | - | `; | |||
| 226 | - | ||||
| 227 | - | if (document.body) { | |||
| 228 | - | document.body.appendChild(toast); | |||
| 229 | - | ||||
| 230 | - | requestAnimationFrame(() => { | |||
| 231 | - | toast.style.opacity = "1"; | |||
| 232 | - | toast.style.transform = "translateX(0)"; | |||
| 233 | - | }); | |||
| 234 | - | ||||
| 235 | - | setTimeout(() => { | |||
| 236 | - | toast.style.opacity = "0"; | |||
| 237 | - | toast.style.transform = "translateX(100%)"; | |||
| 238 | - | setTimeout(() => toast.remove(), 300); | |||
| 239 | - | }, duration); | |||
| 240 | - | } | |||
| 241 | - | }; | |||
| 242 | - | ||||
| 243 | - | window.__dumber_showZoomToast = (level: number) => { | |||
| 244 | - | const percentage = Math.round(level * 100); | |||
| 245 | - | window.__dumber_showToast!(`Zoom: ${percentage}%`, 1500); | |||
| 246 | - | }; | |||
| 247 | - | ||||
| 248 | - | window.__dumber_dismissToast = (_id?: number) => { | |||
| 249 | - | document | |||
| 250 | - | .querySelectorAll(".dumber-fallback-toast") | |||
| 251 | - | .forEach((el) => el.remove()); | |||
| 252 | - | }; | |||
| 253 | - | ||||
| 254 | - | window.__dumber_clearToasts = () => { | |||
| 255 | - | document | |||
| 256 | - | .querySelectorAll(".dumber-fallback-toast") | |||
| 257 | - | .forEach((el) => el.remove()); | |||
| 258 | - | }; | |||
| 259 | } | 199 | } | ||
| 260 | } | 200 | } | ||
| 261 | 201 | ||||
M gui/src/injected/main-world.ts +1 -60
| @@ -51,14 +51,6 @@ interface Suggestion { | |||
| 51 | 51 | } | |
| 52 | 52 | ||
| 53 | 53 | interface DumberAPI { | |
| 54 | - | toast: { | |
| 55 | - | show: ( | |
| 56 | - | message: string, | |
| 57 | - | duration?: number, | |
| 58 | - | type?: "info" | "success" | "error", | |
| 59 | - | ) => void; | |
| 60 | - | zoom: (level: number) => void; | |
| 61 | - | }; | |
| 62 | 54 | omnibox: { | |
| 63 | 55 | suggestions: (suggestions: Suggestion[]) => void; | |
| 64 | 56 | }; | |
| @@ -237,59 +229,9 @@ interface DumberAPI { | |||
| 237 | 229 | } | |
| 238 | 230 | }; | |
| 239 | 231 | ||
| 240 | - | // Initialize unified API object | |
| 232 | + | // Initialize unified API object (toast functions are exposed by ToastContainer.svelte) | |
| 241 | 233 | window.__dumber = window.__dumber || ({} as DumberAPI); | |
| 242 | 234 | ||
| 243 | - | // Toast bridge | |
| 244 | - | window.__dumber.toast = window.__dumber.toast || { | |
| 245 | - | show: (message: string, duration?: number, type?: string) => { | |
| 246 | - | try { | |
| 247 | - | document.dispatchEvent( | |
| 248 | - | new CustomEvent("dumber:toast:show", { | |
| 249 | - | detail: { message, duration, type }, | |
| 250 | - | }), | |
| 251 | - | ); | |
| 252 | - | // Legacy compatibility | |
| 253 | - | document.dispatchEvent( | |
| 254 | - | new CustomEvent("dumber:showToast", { | |
| 255 | - | detail: { message, duration, type }, | |
| 256 | - | }), | |
| 257 | - | ); | |
| 258 | - | } catch { | |
| 259 | - | // ignore | |
| 260 | - | } | |
| 261 | - | }, | |
| 262 | - | zoom: (level: number) => { | |
| 263 | - | try { | |
| 264 | - | document.dispatchEvent( | |
| 265 | - | new CustomEvent("dumber:toast:zoom", { | |
| 266 | - | detail: { level }, | |
| 267 | - | }), | |
| 268 | - | ); | |
| 269 | - | // Legacy compatibility | |
| 270 | - | document.dispatchEvent( | |
| 271 | - | new CustomEvent("dumber:showZoomToast", { | |
| 272 | - | detail: { level }, | |
| 273 | - | }), | |
| 274 | - | ); | |
| 275 | - | } catch { | |
| 276 | - | // ignore | |
| 277 | - | } | |
| 278 | - | }, | |
| 279 | - | }; | |
| 280 | - | ||
| 281 | - | // Legacy toast helpers | |
| 282 | - | window.__dumber_showToast = ( | |
| 283 | - | message: string, | |
| 284 | - | duration?: number, | |
| 285 | - | type?: "info" | "success" | "error", | |
| 286 | - | ) => { | |
| 287 | - | window.__dumber.toast.show(message, duration, type); | |
| 288 | - | }; | |
| 289 | - | window.__dumber_showZoomToast = (level: number) => { | |
| 290 | - | window.__dumber.toast.zoom(level); | |
| 291 | - | }; | |
| 292 | - | ||
| 293 | 235 | // DOM zoom functionality | |
| 294 | 236 | if (typeof window.__dumber_dom_zoom_level !== "number") { | |
| 295 | 237 | window.__dumber_dom_zoom_level = initialZoom; | |
| @@ -890,7 +832,6 @@ interface DumberAPI { | |||
| 890 | 832 | if (globalState) { | |
| 891 | 833 | const mutableState = globalState as unknown as Record<string, unknown>; | |
| 892 | 834 | delete mutableState.omnibox; | |
| 893 | - | delete mutableState.toast; | |
| 894 | 835 | } | |
| 895 | 836 | }; | |
| 896 | 837 | ||
| @@ -51,14 +51,6 @@ interface Suggestion { | |||||
| 51 | } | 51 | } | ||
| 52 | 52 | ||||
| 53 | interface DumberAPI { | 53 | interface DumberAPI { | ||
| 54 | - | toast: { | |||
| 55 | - | show: ( | |||
| 56 | - | message: string, | |||
| 57 | - | duration?: number, | |||
| 58 | - | type?: "info" | "success" | "error", | |||
| 59 | - | ) => void; | |||
| 60 | - | zoom: (level: number) => void; | |||
| 61 | - | }; | |||
| 62 | omnibox: { | 54 | omnibox: { | ||
| 63 | suggestions: (suggestions: Suggestion[]) => void; | 55 | suggestions: (suggestions: Suggestion[]) => void; | ||
| 64 | }; | 56 | }; | ||
| @@ -237,59 +229,9 @@ interface DumberAPI { | |||||
| 237 | } | 229 | } | ||
| 238 | }; | 230 | }; | ||
| 239 | 231 | ||||
| 240 | - | // Initialize unified API object | 232 | + | // Initialize unified API object (toast functions are exposed by ToastContainer.svelte) |
| 241 | window.__dumber = window.__dumber || ({} as DumberAPI); | 233 | window.__dumber = window.__dumber || ({} as DumberAPI); | ||
| 242 | 234 | ||||
| 243 | - | // Toast bridge | |||
| 244 | - | window.__dumber.toast = window.__dumber.toast || { | |||
| 245 | - | show: (message: string, duration?: number, type?: string) => { | |||
| 246 | - | try { | |||
| 247 | - | document.dispatchEvent( | |||
| 248 | - | new CustomEvent("dumber:toast:show", { | |||
| 249 | - | detail: { message, duration, type }, | |||
| 250 | - | }), | |||
| 251 | - | ); | |||
| 252 | - | // Legacy compatibility | |||
| 253 | - | document.dispatchEvent( | |||
| 254 | - | new CustomEvent("dumber:showToast", { | |||
| 255 | - | detail: { message, duration, type }, | |||
| 256 | - | }), | |||
| 257 | - | ); | |||
| 258 | - | } catch { | |||
| 259 | - | // ignore | |||
| 260 | - | } | |||
| 261 | - | }, | |||
| 262 | - | zoom: (level: number) => { | |||
| 263 | - | try { | |||
| 264 | - | document.dispatchEvent( | |||
| 265 | - | new CustomEvent("dumber:toast:zoom", { | |||
| 266 | - | detail: { level }, | |||
| 267 | - | }), | |||
| 268 | - | ); | |||
| 269 | - | // Legacy compatibility | |||
| 270 | - | document.dispatchEvent( | |||
| 271 | - | new CustomEvent("dumber:showZoomToast", { | |||
| 272 | - | detail: { level }, | |||
| 273 | - | }), | |||
| 274 | - | ); | |||
| 275 | - | } catch { | |||
| 276 | - | // ignore | |||
| 277 | - | } | |||
| 278 | - | }, | |||
| 279 | - | }; | |||
| 280 | - | ||||
| 281 | - | // Legacy toast helpers | |||
| 282 | - | window.__dumber_showToast = ( | |||
| 283 | - | message: string, | |||
| 284 | - | duration?: number, | |||
| 285 | - | type?: "info" | "success" | "error", | |||
| 286 | - | ) => { | |||
| 287 | - | window.__dumber.toast.show(message, duration, type); | |||
| 288 | - | }; | |||
| 289 | - | window.__dumber_showZoomToast = (level: number) => { | |||
| 290 | - | window.__dumber.toast.zoom(level); | |||
| 291 | - | }; | |||
| 292 | - | ||||
| 293 | // DOM zoom functionality | 235 | // DOM zoom functionality | ||
| 294 | if (typeof window.__dumber_dom_zoom_level !== "number") { | 236 | if (typeof window.__dumber_dom_zoom_level !== "number") { | ||
| 295 | window.__dumber_dom_zoom_level = initialZoom; | 237 | window.__dumber_dom_zoom_level = initialZoom; | ||
| @@ -890,7 +832,6 @@ interface DumberAPI { | |||||
| 890 | if (globalState) { | 832 | if (globalState) { | ||
| 891 | const mutableState = globalState as unknown as Record<string, unknown>; | 833 | const mutableState = globalState as unknown as Record<string, unknown>; | ||
| 892 | delete mutableState.omnibox; | 834 | delete mutableState.omnibox; | ||
| 893 | - | delete mutableState.toast; | |||
| 894 | } | 835 | } | ||
| 895 | }; | 836 | }; | ||
| 896 | 837 | ||||
M gui/src/injected/modules/toast.ts +4 -64
| @@ -96,78 +96,18 @@ export async function initializeToast(config?: ToastConfig): Promise<void> { | |||
| 96 | 96 | updateTheme(initialTheme === "dark"); | |
| 97 | 97 | } | |
| 98 | 98 | ||
| 99 | - | // Mount the Svelte toast container (Svelte 5 syntax) | |
| 99 | + | // Mount the Svelte toast container - this exposes all toast functions to window | |
| 100 | 100 | mount(ToastContainer, { | |
| 101 | 101 | target: rootElement as unknown as Element, | |
| 102 | 102 | }); | |
| 103 | 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 | |
| 104 | + | // Force immediate effect execution to ensure component functions are exposed | |
| 132 | 105 | flushSync(); | |
| 133 | 106 | ||
| 134 | - | // Toast functions are now exposed by the component itself | |
| 135 | - | console.log("✅ Toast functions exposed via component instantiation"); | |
| 136 | - | ||
| 137 | 107 | toastInitialized = true; | |
| 138 | - | console.log("✅ Toast system initialized with Svelte 5"); | |
| 108 | + | console.log("✅ Toast system initialized - functions exposed by ToastContainer"); | |
| 139 | 109 | } catch (error) { | |
| 140 | 110 | console.error("❌ Failed to initialize toast system:", error); | |
| 141 | - | ||
| 142 | - | // Fallback to basic toast system if Svelte fails | |
| 143 | - | initializeFallbackToast(); | |
| 111 | + | throw error; | |
| 144 | 112 | } | |
| 145 | 113 | } | |
| 146 | - | ||
| 147 | - | function 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 | - | ||
| 153 | - | function 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 | - | } | |
| @@ -96,78 +96,18 @@ export async function initializeToast(config?: ToastConfig): Promise<void> { | |||||
| 96 | updateTheme(initialTheme === "dark"); | 96 | updateTheme(initialTheme === "dark"); | ||
| 97 | } | 97 | } | ||
| 98 | 98 | ||||
| 99 | - | // Mount the Svelte toast container (Svelte 5 syntax) | 99 | + | // Mount the Svelte toast container - this exposes all toast functions to window |
| 100 | mount(ToastContainer, { | 100 | mount(ToastContainer, { | ||
| 101 | target: rootElement as unknown as Element, | 101 | target: rootElement as unknown as Element, | ||
| 102 | }); | 102 | }); | ||
| 103 | 103 | ||||
| 104 | - | // Signal isolated GUI readiness for page-world bridge | 104 | + | // Force immediate effect execution to ensure component functions are exposed |
| 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(); | 105 | flushSync(); | ||
| 133 | 106 | ||||
| 134 | - | // Toast functions are now exposed by the component itself | |||
| 135 | - | console.log("✅ Toast functions exposed via component instantiation"); | |||
| 136 | - | ||||
| 137 | toastInitialized = true; | 107 | toastInitialized = true; | ||
| 138 | - | console.log("✅ Toast system initialized with Svelte 5"); | 108 | + | console.log("✅ Toast system initialized - functions exposed by ToastContainer"); |
| 139 | } catch (error) { | 109 | } catch (error) { | ||
| 140 | console.error("❌ Failed to initialize toast system:", error); | 110 | console.error("❌ Failed to initialize toast system:", error); | ||
| 141 | - | 111 | + | throw error; | |
| 142 | - | // Fallback to basic toast system if Svelte fails | |||
| 143 | - | initializeFallbackToast(); | |||
| 144 | } | 112 | } | ||
| 145 | } | 113 | } | ||
| 146 | - | ||||
| 147 | - | function 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 | - | ||||
| 153 | - | function 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 | - | } | |||
M internal/app/browser/window_shortcuts.go +5 -8
| @@ -230,24 +230,21 @@ func (h *WindowShortcutHandler) handleZoom(action string, multiplier float64) { | |||
| 230 | 230 | return | |
| 231 | 231 | } | |
| 232 | 232 | ||
| 233 | + | // Ensure GUI/toast component is loaded before showing toast | |
| 234 | + | h.ensureGUIInActivePane("toast") | |
| 235 | + | ||
| 233 | 236 | // Show zoom toast notification | |
| 234 | - | zoomPercent := int(newZoom * 100) | |
| 235 | 237 | toastScript := fmt.Sprintf(` | |
| 236 | 238 | (function() { | |
| 237 | 239 | try { | |
| 238 | 240 | if (typeof window.__dumber_showZoomToast === 'function') { | |
| 239 | 241 | window.__dumber_showZoomToast(%f); | |
| 240 | - | } else { | |
| 241 | - | // Fallback: dispatch toast event directly | |
| 242 | - | document.dispatchEvent(new CustomEvent('dumber:showToast', { | |
| 243 | - | detail: { message: 'Zoom: %d%%', duration: 1500, type: 'info' } | |
| 244 | - | })); | |
| 245 | 242 | } | |
| 246 | 243 | } catch (e) { | |
| 247 | - | console.error('[zoom] Failed to show toast:', e); | |
| 244 | + | console.error('[window-shortcuts] Failed to show zoom toast:', e); | |
| 248 | 245 | } | |
| 249 | 246 | })(); | |
| 250 | - | `, newZoom, zoomPercent) | |
| 247 | + | `, newZoom) | |
| 251 | 248 | ||
| 252 | 249 | if err := activeWebView.InjectScript(toastScript); err != nil { | |
| 253 | 250 | log.Printf("[window-shortcuts] Failed to show zoom toast: %v", err) | |
| @@ -230,24 +230,21 @@ func (h *WindowShortcutHandler) handleZoom(action string, multiplier float64) { | |||||
| 230 | return | 230 | return | ||
| 231 | } | 231 | } | ||
| 232 | 232 | ||||
| 233 | + | // Ensure GUI/toast component is loaded before showing toast | |||
| 234 | + | h.ensureGUIInActivePane("toast") | |||
| 235 | + | ||||
| 233 | // Show zoom toast notification | 236 | // Show zoom toast notification | ||
| 234 | - | zoomPercent := int(newZoom * 100) | |||
| 235 | toastScript := fmt.Sprintf(` | 237 | toastScript := fmt.Sprintf(` | ||
| 236 | (function() { | 238 | (function() { | ||
| 237 | try { | 239 | try { | ||
| 238 | if (typeof window.__dumber_showZoomToast === 'function') { | 240 | if (typeof window.__dumber_showZoomToast === 'function') { | ||
| 239 | window.__dumber_showZoomToast(%f); | 241 | window.__dumber_showZoomToast(%f); | ||
| 240 | - | } else { | |||
| 241 | - | // Fallback: dispatch toast event directly | |||
| 242 | - | document.dispatchEvent(new CustomEvent('dumber:showToast', { | |||
| 243 | - | detail: { message: 'Zoom: %d%%', duration: 1500, type: 'info' } | |||
| 244 | - | })); | |||
| 245 | } | 242 | } | ||
| 246 | } catch (e) { | 243 | } catch (e) { | ||
| 247 | - | console.error('[zoom] Failed to show toast:', e); | 244 | + | console.error('[window-shortcuts] Failed to show zoom toast:', e); |
| 248 | } | 245 | } | ||
| 249 | })(); | 246 | })(); | ||
| 250 | - | `, newZoom, zoomPercent) | 247 | + | `, newZoom) |
| 251 | 248 | ||||
| 252 | if err := activeWebView.InjectScript(toastScript); err != nil { | 249 | if err := activeWebView.InjectScript(toastScript); err != nil { | ||
| 253 | log.Printf("[window-shortcuts] Failed to show zoom toast: %v", err) | 250 | log.Printf("[window-shortcuts] Failed to show zoom toast: %v", err) | ||