Files
Hellion-NewTab/src/js/state.js
T

118 lines
3.9 KiB
JavaScript

/* =============================================
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, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
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
}
];
}
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 }) {
trash.push({
item: structuredClone(item),
type,
originBoardId: originBoardId ?? null,
deletedAt: Date.now()
});
// 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);
}
}
// 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();
}
}