/** * [Gitea #120] What this device stores for Lotus, from the APIs that are cheap * to ask. `estimate()` is the total the browser accounts for; Chromium adds a * per-bucket breakdown (`usageDetails`), Firefox/Safari don't. Media files are * not in here at all: they live in the browser's own HTTP cache, which manages * and evicts them itself. */ export type StorageUsage = { usage: number; quota: number; /** Chromium only: IndexedDB (sync store, crypto store, search index) bytes. */ indexedDB?: number; /** Chromium only: Cache Storage (offline app shell) bytes. */ caches?: number; /** Whether the browser granted persistent storage (won't evict under pressure). */ persisted: boolean | undefined; }; type EstimateWithDetails = StorageEstimate & { usageDetails?: { indexedDB?: number; caches?: number; serviceWorkerRegistrations?: number }; }; export const storageUsageSupported = (): boolean => typeof navigator !== 'undefined' && !!navigator.storage && !!navigator.storage.estimate; export async function readStorageUsage(): Promise { if (!storageUsageSupported()) return undefined; const est = (await navigator.storage.estimate()) as EstimateWithDetails; let persisted: boolean | undefined; try { persisted = navigator.storage.persisted ? await navigator.storage.persisted() : undefined; } catch { persisted = undefined; } return { usage: est.usage ?? 0, quota: est.quota ?? 0, indexedDB: est.usageDetails?.indexedDB, caches: est.usageDetails?.caches, persisted, }; } /** Ask the browser to keep this origin's storage; returns the new state. */ export async function requestPersistentStorage(): Promise { if (!navigator.storage?.persist) return undefined; try { return await navigator.storage.persist(); } catch { return undefined; } } export const percentOf = (usage: number, quota: number): number => quota > 0 ? Math.min(100, Math.round((usage / quota) * 100)) : 0;