// Shared constants + storage helpers. Imported by the service worker and the popup. // Firefox exposes the promise-based APIs as `browser`; its `chrome` alias is // callback-style, which every `await` below would silently break on. const chrome = globalThis.browser ?? globalThis.chrome; export const DEFAULT_SETTINGS = { lang: 'eng', psm: '3', // Tesseract page segmentation mode enhance: true, // upscale + grayscale the crop before OCR hud: true, // show the on-page toast after each capture dedupeWarn: true, // flag a page whose text matches the previous one autoAdvance: false, advanceMode: 'click', // 'click' | 'key' advancePoint: null, // { x, y, vw, vh } in top-frame viewport CSS pixels advanceKey: 'ArrowRight', advanceDelay: 350, // ms to wait after capturing before advancing runInterval: 600, // ms to let the new page settle before the next shot runStopOnDupes: 2, // consecutive identical pages that end a run (0 = never) runMaxPages: 300, // hard cap on pages per run }; export const IDLE_RUN = { active: false, tabId: null, captured: 0, startedAt: 0, lastTick: 0, stopReason: null }; export const LANGUAGES = [ { code: 'eng', label: 'English' }, { code: 'por', label: 'Portuguese' }, { code: 'spa', label: 'Spanish' }, ]; export const PSM_MODES = [ { value: '3', label: 'Auto (mixed layout)' }, { value: '4', label: 'Single column' }, { value: '6', label: 'Single block of text' }, { value: '7', label: 'Single line' }, { value: '11', label: 'Sparse text' }, ]; const get = (keys) => chrome.storage.local.get(keys); const set = (obj) => chrome.storage.local.set(obj); export async function getSettings() { const { settings } = await get('settings'); const merged = { ...DEFAULT_SETTINGS, ...(settings || {}) }; // 0.1.0 stored a CSS selector, which cannot be converted to a point. delete merged.advanceSelector; return merged; } /** True when auto-advance is switched on but has nothing to act on. */ export function advanceIsConfigured(s) { return s.advanceMode === 'key' ? !!s.advanceKey : !!s.advancePoint; } export async function saveSettings(patch) { const next = { ...(await getSettings()), ...patch }; await set({ settings: next }); return next; } export async function getRegion() { const { region } = await get('region'); return region || null; } export async function setRegion(region) { await set({ region }); return region; } export async function getRun() { const { run } = await get('run'); return { ...IDLE_RUN, ...(run || {}) }; } export async function setRun(patch) { const next = { ...(await getRun()), ...patch }; await set({ run: next }); return next; } export async function getPages() { const { pages } = await get('pages'); return pages || []; } export async function setPages(pages) { await set({ pages }); } export const thumbKey = (id) => `thumb:${id}`; export const cropKey = (id) => `crop:${id}`; /** Remove a page and everything stored alongside it. */ export async function deletePage(id) { const pages = (await getPages()).filter((p) => p.id !== id); await setPages(renumber(pages)); await chrome.storage.local.remove([thumbKey(id), cropKey(id)]); } export async function clearAll() { const pages = await getPages(); const keys = pages.flatMap((p) => [thumbKey(p.id), cropKey(p.id)]); await chrome.storage.local.remove([...keys, 'pages']); } export function renumber(pages) { return pages.map((p, i) => ({ ...p, n: i + 1 })); } /* Up to 0.2.0 every page record also stored the tab's `url` and `title`. Nothing ever read them, and a list of pages visited with title and time of visit is Chrome's Web history disclosure category verbatim, so records made by an older version are rewritten once on update. Returns null when there is nothing to strip, so the caller can skip the write. */ export function withoutHistoryFields(pages) { if (!pages.some((p) => 'url' in p || 'title' in p)) return null; return pages.map(({ url, title, ...rest }) => rest); } /** Join captured pages into one document. */ export function joinPages(pages, { separators = true } = {}) { return pages .map((p) => { const body = (p.text || '').trim(); return separators ? `--- page ${p.n} ---\n${body}` : body; }) .join('\n\n') .trim(); } export function normalizeForCompare(text) { return (text || '').replace(/\s+/g, ' ').trim().toLowerCase(); }