// [DEF:toasts_module:Module] // @SEMANTICS: notification, toast, feedback, state // @PURPOSE: Manages toast notifications using a Svelte writable store. // @LAYER: UI-State import { writable } from 'svelte/store'; // [DEF:toasts:Data] // @PURPOSE: Writable store containing the list of active toasts. export const toasts = writable([]); // [/DEF:toasts:Data] const TOAST_DEDUP_WINDOW_MS = 1200; const recentToastByKey = new Map(); function buildToastKey(message, type) { return `${String(type || 'info')}::${String(message || '')}`; } function shouldSkipDuplicateToast(message, type) { const key = buildToastKey(message, type); const now = Date.now(); const previousAt = recentToastByKey.get(key); if (previousAt && now - previousAt < TOAST_DEDUP_WINDOW_MS) { return true; } recentToastByKey.set(key, now); return false; } // [DEF:addToast:Function] // @PURPOSE: Adds a new toast message. // @PRE: message string is provided. // @POST: New toast is added to the store and scheduled for removal. // @PARAM: message (string) - The message text. // @PARAM: type (string) - The type of toast (info, success, error). // @PARAM: duration (number) - Duration in ms before the toast is removed. export function addToast(message, type = 'info', duration = 3000) { if (shouldSkipDuplicateToast(message, type)) { console.log(`[toasts.addToast][Action] Duplicate skipped context={{'type': '${type}', 'message': '${message}'}}`); return; } const id = Math.random().toString(36).substr(2, 9); console.log(`[toasts.addToast][Action] Adding toast context={{'id': '${id}', 'type': '${type}', 'message': '${message}'}}`); toasts.update(all => [...all, { id, message, type }]); setTimeout(() => removeToast(id), duration); } // [/DEF:addToast:Function] // [DEF:removeToast:Function] // @PURPOSE: Removes a toast message by ID. // @PRE: id is provided. // @POST: Toast is removed from the store. // @PARAM: id (string) - The ID of the toast to remove. function removeToast(id) { console.log(`[toasts.removeToast][Action] Removing toast context={{'id': '${id}'}}`); toasts.update(all => all.filter(t => t.id !== id)); } // [/DEF:removeToast:Function] // [/DEF:toasts_module:Module]