// Dismissible — reusable "hide this UI element permanently" store. // // State is persisted server-side in data/ui-state.json via the authenticated // /read-file + /write-file endpoints, so a dismissal follows the user across // browsers and devices. It is a direct file read/write: no task is created // (nothing appears in the task manager) and no system scan runs. // // Usage: // await Dismissible.load(); // once, before reading state (e.g. in a page init) // if (!Dismissible.isDismissed('my-id')) { ...render the thing... } // Dismissible.dismiss('my-id'); // on close — persists, fire-and-forget // Dismissible.restore('my-id'); // bring it back window.Dismissible = (() => { const FILE = 'ui-state.json'; let dismissed = new Set(); let loaded = false; async function load() { if (loaded) return; try { const res = await fetch(`/read-file?path=${encodeURIComponent(FILE)}`); if (res.ok) { const data = JSON.parse(await res.text()); if (Array.isArray(data?.dismissed)) dismissed = new Set(data.dismissed); } // A 404 just means nothing has been dismissed yet — leave the set empty. } catch (_) { // On any error, fail open: treat nothing as dismissed so notices still show. } loaded = true; } function isDismissed(id) { return dismissed.has(id); } async function dismiss(id) { await load(); if (dismissed.has(id)) return; dismissed.add(id); await persist(); } async function restore(id) { await load(); if (!dismissed.has(id)) return; dismissed.delete(id); await persist(); } async function persist() { try { await fetch('/write-file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: FILE, content: JSON.stringify({ dismissed: [...dismissed] }, null, 2) }) }); } catch (err) { console.warn('Dismissible: failed to persist state', err); } } return { load, isDismissed, dismiss, restore }; })();