/* ============================================= HELLION NEWTAB — state.js Globaler State, Default-Werte, Hilfsfunktionen ============================================= */ let boards = []; // Papierkorb als EIGENER Store-Key (nicht im boards-Payload), isoliert das Quota-Risiko (CR-04/TRASH-02). // Eintrag-Schema: { item, type: 'bookmark'|'board', deletedAt, originBoardId } let trash = []; // Papierkorb: Auto-Cleanup-Fenster und harte Obergrenze (Quota-Schutz, TRASH-04). // 30 Tage in Millisekunden; ueber dieser Zeit wird ein Eintrag beim Laden auto-geloescht. const TRASH_RETENTION_MS = 30 * 24 * 60 * 60 * 1000; // Max. Anzahl trash-Eintraege. Bei Ueberlauf werden die aeltesten zuerst verworfen, // damit der Papierkorb nicht das 10-MB-Storage-Limit sprengt (kein blindes Wachstum). const TRASH_MAX_ENTRIES = 100; let settings = { compact: false, shortenTitles: false, newTab: true, showDesc: false, hideExtra: false, visibleCount: 10, bgUrl: '', theme: 'nebula', showSearch: true, searchEngine: 'google', toolbarPos: 'right', imageRefEnabled: false, language: 'auto' }; // uid() lebt jetzt in quicksave-core.js (globalThis.uid), damit Seite und // Background-Worker dieselbe ID-Erzeugung teilen. Hier bewusst KEINE eigene Deklaration. function escHtml(str) { return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function getDefaultBoards() { return [ { id: uid(), title: 'Getting Started', bookmarks: [ { id: uid(), title: 'GitHub', url: 'https://github.com', desc: '' }, { id: uid(), title: 'MDN Web Docs', url: 'https://developer.mozilla.org', desc: '' }, { id: uid(), title: 'Next.js Docs', url: 'https://nextjs.org/docs', desc: '' }, ], blurred: false, pos: { x: 40, y: 110 } } ]; } async function saveBoards() { await Store.set('boards', boards); } async function saveTrash() { await Store.set('trash', trash); } /** * Legt einen Eintrag in den Papierkorb. Klont das Objekt (structuredClone), * damit der trash-Eintrag nicht per Referenz an boards[] haengt und nach dem * Restore-Loop konsistent bleibt. Setzt deletedAt. Erzwingt die harte Obergrenze * TRASH_MAX_ENTRIES (Quota-Schutz, TRASH-04): bei Ueberlauf fallen die aeltesten * Eintraege heraus. Speichern uebernimmt der Aufrufer (saveTrash()). * @param {{ item: Object, type: 'bookmark'|'board', originBoardId: (string|null) }} entry */ function pushToTrash({ item, type, originBoardId }) { const entry = { item: structuredClone(item), type, originBoardId: originBoardId ?? null, deletedAt: Date.now() }; trash.push(entry); // Aelteste zuerst kappen, falls die Obergrenze ueberschritten ist. if (trash.length > TRASH_MAX_ENTRIES) { trash.sort((a, b) => a.deletedAt - b.deletedAt); trash = trash.slice(trash.length - TRASH_MAX_ENTRIES); } return entry; // fuer Rollback im Delete-Handler bei Save-Fehler (W-b/Quota) } // Page-seitiger Wrapper um das DOM-freie ensureInbox() aus quicksave-core.js. // ensureInbox() mutiert das globale boards-Array in-place; wir persistieren nur, // wenn die Inbox neu angelegt wurde, und geben das Inbox-Board-Objekt zurueck // (fuer Quick-Save-/Restore-Pfade). async function ensureInboxBoard() { const before = boards.length; const inbox = ensureInbox(boards); // global aus quicksave-core.js; mutiert boards in-place if (boards.length !== before) { await saveBoards(); } return inbox; } async function saveSettings() { await Store.set('settings', settings); } // ---- VIEW TRANSITIONS ---- // Fuehrt eine synchrone DOM-Mutation mit nativem View-Transition-Fade aus. // Feature-Detection-Fallback (Firefox < 144): instant. reduced-motion kappt das Fade ueber den ungeschichteten @media-Block. function withViewTransition(mutate) { if (document.startViewTransition) { document.startViewTransition(mutate); } else { mutate(); } }