A browser extension that lets you summarize any webpage and ask questions using AI.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: Firefox MV3 background, Ollama connectivity, and defaults

- Load config.js before background on Firefox; use importScripts only in SW
- Firefox CSP omits upgrade-insecure-requests so http://localhost Ollama works
- DNR rules cover 127.0.0.1:11434 and all resource types; extra host perms
- Add declarativeNetRequestWithHostAccess on Firefox manifest
- Hint when fetch fails (NetworkError) that Ollama may not be running
- Default disable thinking mode to on in shared config

+63 -14
+1
manifest-chrome.json
··· 22 22 }, 23 23 "host_permissions": [ 24 24 "http://localhost/*", 25 + "http://127.0.0.1/*", 25 26 "http://*/*", 26 27 "https://*/*" 27 28 ],
+6 -1
manifest-firefox.json
··· 15 15 "storage", 16 16 "scripting", 17 17 "declarativeNetRequest", 18 + "declarativeNetRequestWithHostAccess", 18 19 "contextMenus" 19 20 ], 20 21 "commands": { ··· 28 29 }, 29 30 "host_permissions": [ 30 31 "http://localhost/*", 32 + "http://127.0.0.1/*", 31 33 "http://*/*", 32 34 "https://*/*" 33 35 ], ··· 40 42 } 41 43 ] 42 44 }, 45 + "content_security_policy": { 46 + "extension_pages": "script-src 'self'; object-src 'self'" 47 + }, 43 48 "action": { 44 49 "default_popup": "popup/popup.html", 45 50 "default_icon": { ··· 50 55 } 51 56 }, 52 57 "background": { 53 - "scripts": ["scripts/background.js"] 58 + "scripts": ["scripts/config.js", "scripts/background.js"] 54 59 }, 55 60 "options_page": "options/options.html", 56 61 "icons": {
+1
manifest.json
··· 22 22 }, 23 23 "host_permissions": [ 24 24 "http://localhost/*", 25 + "http://127.0.0.1/*", 25 26 "http://*/*", 26 27 "https://*/*" 27 28 ],
+7 -2
options/options.js
··· 159 159 // Provide helpful troubleshooting 160 160 if ( 161 161 errorMsg.includes("Cannot connect") || 162 - errorMsg.includes("Failed to fetch") 162 + errorMsg.includes("Failed to fetch") || 163 + errorMsg.includes("NetworkError") 163 164 ) { 165 + errorMsg += 166 + "\n\nThis error often means nothing is listening at your API URL. If you use Ollama, it may not be running."; 164 167 errorMsg += "\n\n💡 Troubleshooting:\n"; 165 168 errorMsg += "1. Check if Ollama is running: run `ollama serve`\n"; 166 169 errorMsg += 167 170 "2. Check if the model is pulled: run `ollama pull " + 168 171 settings.model + 169 172 "`\n"; 170 - errorMsg += "3. Try switching API Mode (Native vs OpenAI-Compatible)"; 173 + errorMsg += "3. Try switching API Mode (Native vs OpenAI-Compatible)\n"; 174 + errorMsg += 175 + "4. If using Ollama in Firefox: restart with OLLAMA_ORIGINS=* (e.g. OLLAMA_ORIGINS=* ollama serve)"; 171 176 } 172 177 173 178 showStatus("❌ " + errorMsg, "error");
+2
popup/popup.css
··· 553 553 border-radius: 6px; 554 554 font-size: 12px; 555 555 line-height: 1.5; 556 + white-space: pre-wrap; 556 557 } 557 558 558 559 /* ── PDF Error ── */ ··· 755 756 color: var(--error-text); 756 757 padding: 10px 14px; 757 758 border-radius: 6px; 759 + white-space: pre-wrap; 758 760 } 759 761 760 762 .chat-spinner {
+22 -6
popup/popup.js
··· 189 189 if (!streamTarget) return; // Nothing was streaming 190 190 191 191 if (error) { 192 - showToast("Error: " + error); 192 + showToast("Error: " + withOllamaFetchHint(error)); 193 193 if (streamElement) { 194 - streamElement.innerHTML = `<div class="error-message">${escapeHtml(error)}</div>`; 194 + streamElement.innerHTML = `<div class="error-message">${escapeHtml(withOllamaFetchHint(error))}</div>`; 195 195 } 196 196 // Still finalize to reset states 197 197 if (streamTarget === "chat") { ··· 801 801 // Show error in chat 802 802 const errorEl = document.createElement("div"); 803 803 errorEl.className = "chat-message assistant error"; 804 - errorEl.textContent = "Error: " + error.message; 804 + errorEl.textContent = "Error: " + withOllamaFetchHint(error.message); 805 805 chatSection.appendChild(errorEl); 806 806 807 807 // Scroll to show error ··· 823 823 chatInput.focus(); 824 824 825 825 if (error) { 826 - showToast("Error: " + error); 826 + showToast("Error: " + withOllamaFetchHint(error)); 827 827 return; 828 828 } 829 829 ··· 923 923 // The response will come via postMessage listener 924 924 } catch (error) { 925 925 console.error("API Error:", error); 926 - resultContainer.innerHTML = `<div class="error-message">${escapeHtml(error.message)}</div>`; 926 + resultContainer.innerHTML = `<div class="error-message">${escapeHtml(withOllamaFetchHint(error.message))}</div>`; 927 927 setLoading(false); 928 928 } 929 929 } ··· 932 932 if (error) { 933 933 setLoading(false); 934 934 if (currentStreamElement) { 935 - currentStreamElement.innerHTML = `<div class="error-message">${escapeHtml(error)}</div>`; 935 + currentStreamElement.innerHTML = `<div class="error-message">${escapeHtml(withOllamaFetchHint(error))}</div>`; 936 936 } 937 937 return; 938 938 } ··· 1283 1283 // When done loading, show "Regenerate" if we have a summary, otherwise "Quick Summary" 1284 1284 setSummarizeLabel(quickSummary ? "Regenerate" : "Quick Summary"); 1285 1285 } 1286 + } 1287 + 1288 + function looksLikeNetworkFetchFailure(message) { 1289 + const m = String(message ?? "").toLowerCase(); 1290 + return ( 1291 + m.includes("networkerror") || 1292 + m.includes("failed to fetch") || 1293 + m.includes("network error when attempting to fetch") 1294 + ); 1295 + } 1296 + 1297 + /** Explains that Ollama may be stopped when the error looks like a failed fetch. */ 1298 + function withOllamaFetchHint(message) { 1299 + const base = String(message ?? ""); 1300 + if (!looksLikeNetworkFetchFailure(base)) return base; 1301 + return `${base}\n\nIf you're using Ollama, it may not be running. Try \`ollama serve\`.`; 1286 1302 } 1287 1303 1288 1304 function escapeHtml(text) {
+18 -2
rules.json
··· 13 13 ] 14 14 }, 15 15 "condition": { 16 - "urlFilter": "http://localhost:11434/*", 17 - "resourceTypes": ["xmlhttprequest"] 16 + "urlFilter": "http://localhost:11434/*" 17 + } 18 + }, 19 + { 20 + "id": 2, 21 + "priority": 1, 22 + "action": { 23 + "type": "modifyHeaders", 24 + "requestHeaders": [ 25 + { 26 + "header": "Origin", 27 + "operation": "set", 28 + "value": "http://127.0.0.1" 29 + } 30 + ] 31 + }, 32 + "condition": { 33 + "urlFilter": "http://127.0.0.1:11434/*" 18 34 } 19 35 } 20 36 ]
+5 -2
scripts/background.js
··· 1 1 // Background script - handles API communication 2 2 // Uses centralized CONFIG from config.js 3 3 4 - // Import shared configuration (service worker context) 5 - importScripts("config.js"); 4 + // Chrome MV3: service worker — load config via importScripts. 5 + // Firefox MV3: event-page scripts — config.js is listed first in manifest "scripts". 6 + if (typeof importScripts === "function") { 7 + importScripts("config.js"); 8 + } 6 9 7 10 // Cache key prefixes from CONFIG 8 11 const QUICK_SUMMARY_CACHE_PREFIX = CONFIG.CACHE.QUICK_SUMMARY;
+1 -1
scripts/config.js
··· 19 19 BASE_URL: "http://localhost:11434", 20 20 MODEL: "gpt-oss:20b-cloud", 21 21 KEY: "", 22 - DISABLE_THINKING: false, 22 + DISABLE_THINKING: true, 23 23 AUTO_SUMMARIZE: false, 24 24 MAX_TOKENS: 2048, 25 25 TEMPERATURE: 0.7,