/** * Shared configuration for the Lede extension * Centralizes all defaults, constants, and settings */ const CONFIG = { // Cache prefixes (must match across all files) CACHE: { QUICK_SUMMARY: "quick_summary_cache_", DETAILED_SUMMARY: "detailed_summary_cache_", CONTENT: "content_cache_", CHAT: "chat_cache_", SUGGESTIONS: "suggestions_cache_", }, // Default API settings API: { MODE: "ollama", // "ollama" or "openai" BASE_URL: "http://localhost:11434", MODEL: "gpt-oss:20b-cloud", KEY: "", DISABLE_THINKING: true, AUTO_SUMMARIZE: false, MAX_TOKENS: 2048, /** Hard cap on streamed assistant text (chars) to stop runaway output if the model misbehaves. */ STREAM_MAX_OUTPUT_CHARS: 16000, /** Non-streaming follow-up suggestion calls use a small budget so nonsense stays short. */ SUGGESTIONS_MAX_TOKENS: 256, /** Only parse suggestions from the first N chars of the model reply. */ SUGGESTIONS_MAX_PARSE_CHARS: 1000, TEMPERATURE: 0.7, TIMEOUT_MS: 30000, /** * Ollama only: how long the model stays loaded after each request (e.g. "0", "15s", "1m"). * Must match an option value in settings; "0" = unload immediately. */ KEEP_ALIVE: "0", }, // Content extraction settings (scripts/content.js — Readability + paragraph fallback) EXTRACTION: { MAX_LENGTH: 50000, /** Not used by content.js today (fallback uses `p` nodes); kept for future richer fallback. */ FALLBACK_SELECTORS: [ "article p", "article div", ".content p", ".content div", ".post-content p", ".entry-content p", ".article-body p", "main p", "main div", '[role="main"] p', ".story p", ".story-body p", "#story p", ], }, // UI Themes THEMES: { OPTIONS: ["light", "dark", "system"], DEFAULT: "system", }, // Accent colors ACCENTS: { DEFAULT_PRESET: "orange", DEFAULT_COLOR: "#F15B2F", PRESETS: { orange: "#F15B2F", blue: "#2F80ED", green: "#2FA36B", purple: "#7E57C2", teal: "#14B8A6", pink: "#EC4899", indigo: "#4F46E5", }, }, // Fallback chips when AI suggestion generation fails (see popup `renderSuggestions`) SUGGESTIONS: { LIST: ["What are some key quotes?", "Explain this simply"], }, // Prompt templates PROMPTS: { SYSTEM_SUMMARIZER: "You are Lede, a helpful assistant that summarizes webpages concisely. Foreground the main point (the lede) before supporting detail.", SYSTEM_CHAT: "You are Lede, a helpful assistant answering questions about a webpage. Use the provided page content and summary to give accurate, concise answers. You may use short sentences, sections, and bullet points to answer. Avoid long paragraphs and tables. ONLY answer based on the provided page content and summary, not any external knowledge or information.", QUICK_SUMMARY: `You are to provide a "Quick Summary" of this webpage. Start with very brief sentence that provides the most important takeaway or piece of information. This section should have no title. A few key words should be **bolded** so people can quickly scan. Then, create a section called "Key Points". Focus on the main points and key takeaways. Use markdown formatting (headings, bullet points, etc.). Avoid repeating the same information from previous sections. The "Key Points" should be 3-4 **short** one-sentence bullet points. Each of these bullet points should have key points/takeaways **bolded** so people can quickly scan.`, SUGGESTIONS: `Based on the summary provided, generate 2 natural follow-up questions that a reader might want to ask (besides "Why would this be worth reading?"). Keep questions short (5-8 words), like these examples: - What are some key quotes? - Explain this simply - What information is the most important? Return only the 2 questions, one per line, no numbering or bullet points.`, CHAT_SUGGESTIONS: `Based on this chat response, generate 2 natural follow-up questions the reader might want to ask next. Keep questions short (5-8 words). Make them specific to what was just discussed. Return only the 2 questions, one per line, no numbering or bullet points.`, }, // Ollama template strings (background native `/api/generate` prompt assembly) OLLAMA: { CONTEXT_TEMPLATE: "Context:\n${context}\n\nUser: ${userMessage}", /** Single-turn prompts use the raw user string in code; kept here for parity / edits. */ SINGLE_MESSAGE_TEMPLATE: "${userMessage}", }, /** * Display strings wired in `options/options.js` and `popup/popup.js` (titles, taglines, empty state, summarize button). * Name / tagline: keep aligned with manifest `name` and `description`. Shortcuts: same chord as manifest `commands.summarize-page.suggested_key`. * Extension version lives only in the manifest(s); use `chrome.runtime.getManifest().version` in code if needed. */ EXTENSION: { NAME: "Lede", /** Under the wordmark in popup and settings */ TAGLINE: "Page summarizer & chat", /** One-line for manifest description and similar */ FULL_TAGLINE: "Lede — page summarizer & chat", /** Popup empty state (before summarize) */ EMPTY_HEADLINE: "Don't bury the lede", EMPTY_SUB: "Put the main point first—then ask anything about the page—with AI.", /** Settings page, under the header block */ SETTINGS_MOTTO: "Let Lede lead the way.", SHORTCUT: "Ctrl+Shift+U", SHORTCUT_MAC: "Cmd+Shift+U", /** Footer primary button when no summary yet (and after errors / reset). */ QUICK_SUMMARY_LABEL: "Summarize", }, /** * Popup: pages where summarization is blocked (streaming, banking, in-app UIs). * First matching rule wins. See `getUnsupportedExtractionInfo` in `popup/popup.js`. * * - `url`: match hostname/path (e.g. YouTube watch only). * - `host`: `hostSuffix` matches exact host or `*.suffix` (e.g. `bsky.app`, `mail.proton.me`). * - `labeled`: `title` is `${label} isn't supported` per site row `[hostSuffix, label]`. */ UNSUPPORTED_EXTRACTION: { RULES: [ { kind: "url", title: "YouTube videos aren't supported", message: "This extension can't extract text from YouTube videos at the moment.", anyOf: [ { hostEquals: "youtu.be" }, { hostIn: ["youtube.com", "www.youtube.com"], pathnameEquals: "/watch", }, ], }, { kind: "labeled", message: "Hopefully 🤞 integration with AT Protocol will allow you to summarize Bluesky posts in the future.", sites: [ ["bsky.app", "Bluesky"], ["witchsky.app", "Bluesky"], ["deer.social", "Bluesky"], ["anisota.net", "Bluesky"], ], }, { kind: "labeled", message: "Social media feeds and in-app posts aren't supported at this time.", sites: [ ["x.com", "X"], ["twitter.com", "X"], ["instagram.com", "Instagram"], ["facebook.com", "Facebook"], ["threads.net", "Threads"], ["tiktok.com", "TikTok"], ["snapchat.com", "Snapchat"], ["pinterest.com", "Pinterest"], ["linkedin.com", "LinkedIn"], ], }, { kind: "host", hostSuffix: "nebula.tv", title: "Nebula isn't supported", message: "Video pages don't provide readable article text in the way this extension expects.", }, { kind: "host", hostSuffix: "vimeo.com", title: "Vimeo isn't supported", message: "Video pages don't provide readable article text in the way this extension expects.", }, { kind: "host", hostSuffix: "disneyplus.com", title: "Disney+ isn't supported", message: "Streaming apps don't expose summarizable text like a normal webpage.", }, { kind: "host", hostSuffix: "netflix.com", title: "Netflix isn't supported", message: "Streaming apps don't expose summarizable text like a normal webpage.", }, { kind: "labeled", message: "Streaming and media players don't expose readable article text to the browser.", sites: [ ["hulu.com", "Hulu"], ["max.com", "Max"], ["hbomax.com", "HBO Max"], ["primevideo.com", "Prime Video"], ["peacocktv.com", "Peacock"], ["paramountplus.com", "Paramount+"], ["crunchyroll.com", "Crunchyroll"], ["discoveryplus.com", "Discovery+"], ["tubi.tv", "Tubi"], ["pluto.tv", "Pluto TV"], ["mubi.com", "Mubi"], ["showtime.com", "Showtime"], ["starz.com", "Starz"], ["tv.apple.com", "Apple TV"], ["twitch.tv", "Twitch"], ["spotify.com", "Spotify"], ], }, { kind: "host", hostSuffix: "proton.me", title: "Proton Mail isn't supported", message: "Proton apps are not supported at this time. Sorry!", }, { kind: "labeled", message: "Music and audio apps don't expose readable article text to the browser.", sites: [ ["music.apple.com", "Apple Music"], ["music.youtube.com", "YouTube Music"], ["soundcloud.com", "SoundCloud"], ["audible.com", "Audible"], ["audible.co.uk", "Audible"], ["audible.de", "Audible"], ["pocketcasts.com", "Pocket Casts"], ], }, { kind: "labeled", message: "This kind of video site doesn't provide summarizable text like a normal article.", sites: [ ["dailymotion.com", "Dailymotion"], ["rumble.com", "Rumble"], ["odysee.com", "Odysee"], ], }, { kind: "labeled", message: "We hope you don't want to put your banking information into an AI, anyways.", sites: [ ["chase.com", "Chase"], ["bankofamerica.com", "Bank of America"], ["wellsfargo.com", "Wells Fargo"], ["citi.com", "Citi"], ["usbank.com", "U.S. Bank"], ["capitalone.com", "Capital One"], ["pnc.com", "PNC"], ["td.com", "TD Bank"], ["schwab.com", "Schwab"], ["fidelity.com", "Fidelity"], ["barclays.co.uk", "Barclays"], ["hsbc.co.uk", "HSBC UK"], ["rbc.com", "RBC"], ["bmo.com", "BMO"], ], }, { kind: "labeled", message: "Documents and canvases in this app aren't available to summarize like a normal webpage.", sites: [ ["docs.google.com", "Google Docs"], ["sheets.google.com", "Google Sheets"], ["slides.google.com", "Google Slides"], ["notion.so", "Notion"], ["figma.com", "Figma"], ["miro.com", "Miro"], ["airtable.com", "Airtable"], ["coda.io", "Coda"], ["paper.dropbox.com", "Dropbox Paper"], ["officeapps.live.com", "Office for the web"], ], }, { kind: "labeled", message: "Video meetings don't provide readable page text for this extension to use. Consider getting a transcript of the meeting and putting into a tool like ChatGPT.", sites: [ ["zoom.us", "Zoom"], ["zoom.com", "Zoom"], ["meet.google.com", "Google Meet"], ["teams.microsoft.com", "Microsoft Teams"], ["teams.live.com", "Microsoft Teams"], ["webex.com", "Webex"], ["gotomeeting.com", "GoTo Meeting"], ["global.gotomeeting.com", "GoTo Meeting"], ], }, ], }, }; // Make available for both Chrome extension contexts and tests if (typeof module !== "undefined" && module.exports) { module.exports = CONFIG; }