/** * Forage Chrome Extension — Content Script * * Injected into every page at document_idle. Reads page metadata and sends * it to the background service worker, which POSTs to the Forage server. * Also fires a dwell signal after 30 seconds of continuous page presence. */ (function () { const url = location.href; // Skip internal browser pages and the Forage UI itself to avoid // self-referential signals and phantom items in the feed. if ( url.startsWith('chrome://') || url.startsWith('chrome-extension://') || url.startsWith('moz-extension://') || /https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?/.test(url) ) { return; } const canonical_url = document.querySelector('link[rel="canonical"]')?.href || ''; const title = document.title || url; const words = (document.body?.innerText || '').trim().split(/\s+/).length; const reading_time_min = Math.max(1, Math.round(words / 200)); const description = document.querySelector('meta[name="description"]')?.content || document.querySelector('meta[property="og:description"]')?.content || ''; const source = (() => { try { return new URL(url).hostname.replace(/^www\./, ''); } catch { return ''; } })(); let itemId = null; // Send capture to background worker chrome.runtime.sendMessage( { type: 'capture', url, canonical_url, title, source, description, reading_time_min }, response => { if (response?.item_id) itemId = response.item_id; } ); // 30s dwell timer — cancelled if the user navigates away const DWELL_MS = 30_000; let dwellTimer = setTimeout(() => { if (itemId == null) return; chrome.runtime.sendMessage({ type: 'dwell', item_id: itemId, duration_ms: DWELL_MS }); }, DWELL_MS); window.addEventListener('beforeunload', () => { clearTimeout(dwellTimer); }); })();