A browser extension that lets you summarize any webpage and ask questions using AI.
1/**
2 * Shared configuration for the Lede extension
3 * Centralizes all defaults, constants, and settings
4 */
5
6const CONFIG = {
7 // Cache prefixes (must match across all files)
8 CACHE: {
9 QUICK_SUMMARY: "quick_summary_cache_",
10 DETAILED_SUMMARY: "detailed_summary_cache_",
11 CONTENT: "content_cache_",
12 CHAT: "chat_cache_",
13 SUGGESTIONS: "suggestions_cache_",
14 },
15
16 // Default API settings
17 API: {
18 MODE: "ollama", // "ollama" or "openai"
19 BASE_URL: "http://localhost:11434",
20 MODEL: "gpt-oss:20b-cloud",
21 KEY: "",
22 DISABLE_THINKING: true,
23 AUTO_SUMMARIZE: false,
24 MAX_TOKENS: 2048,
25 /** Hard cap on streamed assistant text (chars) to stop runaway output if the model misbehaves. */
26 STREAM_MAX_OUTPUT_CHARS: 16000,
27 /** Non-streaming follow-up suggestion calls use a small budget so nonsense stays short. */
28 SUGGESTIONS_MAX_TOKENS: 256,
29 /** Only parse suggestions from the first N chars of the model reply. */
30 SUGGESTIONS_MAX_PARSE_CHARS: 1000,
31 TEMPERATURE: 0.7,
32 TIMEOUT_MS: 30000,
33 /**
34 * Ollama only: how long the model stays loaded after each request (e.g. "0", "15s", "1m").
35 * Must match an option value in settings; "0" = unload immediately.
36 */
37 KEEP_ALIVE: "0",
38 },
39
40 // Content extraction settings (scripts/content.js — Readability + paragraph fallback)
41 EXTRACTION: {
42 MAX_LENGTH: 50000,
43 /** Not used by content.js today (fallback uses `p` nodes); kept for future richer fallback. */
44 FALLBACK_SELECTORS: [
45 "article p",
46 "article div",
47 ".content p",
48 ".content div",
49 ".post-content p",
50 ".entry-content p",
51 ".article-body p",
52 "main p",
53 "main div",
54 '[role="main"] p',
55 ".story p",
56 ".story-body p",
57 "#story p",
58 ],
59 },
60
61 // UI Themes
62 THEMES: {
63 OPTIONS: ["light", "dark", "system"],
64 DEFAULT: "system",
65 },
66
67 // Accent colors
68 ACCENTS: {
69 DEFAULT_PRESET: "orange",
70 DEFAULT_COLOR: "#F15B2F",
71 PRESETS: {
72 orange: "#F15B2F",
73 blue: "#2F80ED",
74 green: "#2FA36B",
75 purple: "#7E57C2",
76 teal: "#14B8A6",
77 pink: "#EC4899",
78 indigo: "#4F46E5",
79 },
80 },
81
82 // Fallback chips when AI suggestion generation fails (see popup `renderSuggestions`)
83 SUGGESTIONS: {
84 LIST: ["What are some key quotes?", "Explain this simply"],
85 },
86
87 // Prompt templates
88 PROMPTS: {
89 SYSTEM_SUMMARIZER:
90 "You are Lede, a helpful assistant that summarizes webpages concisely. Foreground the main point (the lede) before supporting detail.",
91 SYSTEM_CHAT:
92 "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.",
93 QUICK_SUMMARY: `You are to provide a "Quick Summary" of this webpage.
94
95 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.
96
97 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.
98
99 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.`,
100 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:
101- What are some key quotes?
102- Explain this simply
103- What information is the most important?
104
105Return only the 2 questions, one per line, no numbering or bullet points.`,
106 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.`,
107 },
108
109 // Ollama template strings (background native `/api/generate` prompt assembly)
110 OLLAMA: {
111 CONTEXT_TEMPLATE: "Context:\n${context}\n\nUser: ${userMessage}",
112 /** Single-turn prompts use the raw user string in code; kept here for parity / edits. */
113 SINGLE_MESSAGE_TEMPLATE: "${userMessage}",
114 },
115
116 /**
117 * Display strings wired in `options/options.js` and `popup/popup.js` (titles, taglines, empty state, summarize button).
118 * Name / tagline: keep aligned with manifest `name` and `description`. Shortcuts: same chord as manifest `commands.summarize-page.suggested_key`.
119 * Extension version lives only in the manifest(s); use `chrome.runtime.getManifest().version` in code if needed.
120 */
121 EXTENSION: {
122 NAME: "Lede",
123 /** Under the wordmark in popup and settings */
124 TAGLINE: "Page summarizer & chat",
125 /** One-line for manifest description and similar */
126 FULL_TAGLINE: "Lede — page summarizer & chat",
127 /** Popup empty state (before summarize) */
128 EMPTY_HEADLINE: "Don't bury the lede",
129 EMPTY_SUB:
130 "Put the main point first—then ask anything about the page—with AI.",
131 /** Settings page, under the header block */
132 SETTINGS_MOTTO: "Let Lede lead the way.",
133 SHORTCUT: "Ctrl+Shift+U",
134 SHORTCUT_MAC: "Cmd+Shift+U",
135 /** Footer primary button when no summary yet (and after errors / reset). */
136 QUICK_SUMMARY_LABEL: "Summarize",
137 },
138
139 /**
140 * Popup: pages where summarization is blocked (streaming, banking, in-app UIs).
141 * First matching rule wins. See `getUnsupportedExtractionInfo` in `popup/popup.js`.
142 *
143 * - `url`: match hostname/path (e.g. YouTube watch only).
144 * - `host`: `hostSuffix` matches exact host or `*.suffix` (e.g. `bsky.app`, `mail.proton.me`).
145 * - `labeled`: `title` is `${label} isn't supported` per site row `[hostSuffix, label]`.
146 */
147 UNSUPPORTED_EXTRACTION: {
148 RULES: [
149 {
150 kind: "url",
151 title: "YouTube videos aren't supported",
152 message:
153 "This extension can't extract text from YouTube videos at the moment.",
154 anyOf: [
155 { hostEquals: "youtu.be" },
156 {
157 hostIn: ["youtube.com", "www.youtube.com"],
158 pathnameEquals: "/watch",
159 },
160 ],
161 },
162 {
163 kind: "labeled",
164 message:
165 "Hopefully 🤞 integration with AT Protocol will allow you to summarize Bluesky posts in the future.",
166 sites: [
167 ["bsky.app", "Bluesky"],
168 ["witchsky.app", "Bluesky"],
169 ["deer.social", "Bluesky"],
170 ["anisota.net", "Bluesky"],
171 ],
172 },
173 {
174 kind: "labeled",
175 message:
176 "Social media feeds and in-app posts aren't supported at this time.",
177 sites: [
178 ["x.com", "X"],
179 ["twitter.com", "X"],
180 ["instagram.com", "Instagram"],
181 ["facebook.com", "Facebook"],
182 ["threads.net", "Threads"],
183 ["tiktok.com", "TikTok"],
184 ["snapchat.com", "Snapchat"],
185 ["pinterest.com", "Pinterest"],
186 ["linkedin.com", "LinkedIn"],
187 ],
188 },
189 {
190 kind: "host",
191 hostSuffix: "nebula.tv",
192 title: "Nebula isn't supported",
193 message:
194 "Video pages don't provide readable article text in the way this extension expects.",
195 },
196 {
197 kind: "host",
198 hostSuffix: "vimeo.com",
199 title: "Vimeo isn't supported",
200 message:
201 "Video pages don't provide readable article text in the way this extension expects.",
202 },
203 {
204 kind: "host",
205 hostSuffix: "disneyplus.com",
206 title: "Disney+ isn't supported",
207 message:
208 "Streaming apps don't expose summarizable text like a normal webpage.",
209 },
210 {
211 kind: "host",
212 hostSuffix: "netflix.com",
213 title: "Netflix isn't supported",
214 message:
215 "Streaming apps don't expose summarizable text like a normal webpage.",
216 },
217 {
218 kind: "labeled",
219 message:
220 "Streaming and media players don't expose readable article text to the browser.",
221 sites: [
222 ["hulu.com", "Hulu"],
223 ["max.com", "Max"],
224 ["hbomax.com", "HBO Max"],
225 ["primevideo.com", "Prime Video"],
226 ["peacocktv.com", "Peacock"],
227 ["paramountplus.com", "Paramount+"],
228 ["crunchyroll.com", "Crunchyroll"],
229 ["discoveryplus.com", "Discovery+"],
230 ["tubi.tv", "Tubi"],
231 ["pluto.tv", "Pluto TV"],
232 ["mubi.com", "Mubi"],
233 ["showtime.com", "Showtime"],
234 ["starz.com", "Starz"],
235 ["tv.apple.com", "Apple TV"],
236 ["twitch.tv", "Twitch"],
237 ["spotify.com", "Spotify"],
238 ],
239 },
240 {
241 kind: "host",
242 hostSuffix: "proton.me",
243 title: "Proton Mail isn't supported",
244 message:
245 "Proton apps are not supported at this time. Sorry!",
246 },
247 {
248 kind: "labeled",
249 message:
250 "Music and audio apps don't expose readable article text to the browser.",
251 sites: [
252 ["music.apple.com", "Apple Music"],
253 ["music.youtube.com", "YouTube Music"],
254 ["soundcloud.com", "SoundCloud"],
255 ["audible.com", "Audible"],
256 ["audible.co.uk", "Audible"],
257 ["audible.de", "Audible"],
258 ["pocketcasts.com", "Pocket Casts"],
259 ],
260 },
261 {
262 kind: "labeled",
263 message:
264 "This kind of video site doesn't provide summarizable text like a normal article.",
265 sites: [
266 ["dailymotion.com", "Dailymotion"],
267 ["rumble.com", "Rumble"],
268 ["odysee.com", "Odysee"],
269 ],
270 },
271 {
272 kind: "labeled",
273 message:
274 "We hope you don't want to put your banking information into an AI, anyways.",
275 sites: [
276 ["chase.com", "Chase"],
277 ["bankofamerica.com", "Bank of America"],
278 ["wellsfargo.com", "Wells Fargo"],
279 ["citi.com", "Citi"],
280 ["usbank.com", "U.S. Bank"],
281 ["capitalone.com", "Capital One"],
282 ["pnc.com", "PNC"],
283 ["td.com", "TD Bank"],
284 ["schwab.com", "Schwab"],
285 ["fidelity.com", "Fidelity"],
286 ["barclays.co.uk", "Barclays"],
287 ["hsbc.co.uk", "HSBC UK"],
288 ["rbc.com", "RBC"],
289 ["bmo.com", "BMO"],
290 ],
291 },
292 {
293 kind: "labeled",
294 message:
295 "Documents and canvases in this app aren't available to summarize like a normal webpage.",
296 sites: [
297 ["docs.google.com", "Google Docs"],
298 ["sheets.google.com", "Google Sheets"],
299 ["slides.google.com", "Google Slides"],
300 ["notion.so", "Notion"],
301 ["figma.com", "Figma"],
302 ["miro.com", "Miro"],
303 ["airtable.com", "Airtable"],
304 ["coda.io", "Coda"],
305 ["paper.dropbox.com", "Dropbox Paper"],
306 ["officeapps.live.com", "Office for the web"],
307 ],
308 },
309 {
310 kind: "labeled",
311 message:
312 "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.",
313 sites: [
314 ["zoom.us", "Zoom"],
315 ["zoom.com", "Zoom"],
316 ["meet.google.com", "Google Meet"],
317 ["teams.microsoft.com", "Microsoft Teams"],
318 ["teams.live.com", "Microsoft Teams"],
319 ["webex.com", "Webex"],
320 ["gotomeeting.com", "GoTo Meeting"],
321 ["global.gotomeeting.com", "GoTo Meeting"],
322 ],
323 },
324 ],
325 },
326};
327
328// Make available for both Chrome extension contexts and tests
329if (typeof module !== "undefined" && module.exports) {
330 module.exports = CONFIG;
331}