feat: add Firefox support with cross-browser compatibility

Add manifest.firefox.json for Firefox MV3 (gecko ID, background
scripts instead of service_worker, options_ui). Introduce
browser-polyfill.js shim so all modules use whichever API is
available (browser.* or chrome.*). Add build.sh to target
chrome, firefox, or both.

https://claude.ai/code/session_01Dvgwe7XMoSxnWXkih8p1Cw
This commit is contained in:
Claude
2026-03-25 23:36:59 +00:00
parent 4576597e64
commit 3ef5df7efb
8 changed files with 191 additions and 36 deletions
+11 -9
View File
@@ -2,9 +2,11 @@
* Silent Send - Background Service Worker
*
* Manages badge count, coordinates between popup and content scripts.
* Uses `api` alias for cross-browser compatibility (Chrome + Firefox).
*/
import Storage from '../lib/storage.js';
import api from '../lib/browser-polyfill.js';
// Track substitution counts per tab
const tabCounts = new Map();
@@ -15,12 +17,12 @@ function updateBadge(tabId) {
const count = tabCounts.get(tabId) || 0;
const text = count > 0 ? String(count) : '';
chrome.action.setBadgeText({ text, tabId });
chrome.action.setBadgeBackgroundColor({ color: count > 0 ? '#10b981' : '#6b7280', tabId });
api.action.setBadgeText({ text, tabId });
api.action.setBadgeBackgroundColor({ color: count > 0 ? '#10b981' : '#6b7280', tabId });
}
// Reset count when tab navigates
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
api.tabs.onUpdated.addListener((tabId, changeInfo) => {
if (changeInfo.status === 'loading') {
tabCounts.set(tabId, 0);
updateBadge(tabId);
@@ -28,13 +30,13 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
});
// Cleanup when tab closes
chrome.tabs.onRemoved.addListener((tabId) => {
api.tabs.onRemoved.addListener((tabId) => {
tabCounts.delete(tabId);
});
// --- Message Handling ---
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
api.runtime.onMessage.addListener((message, sender, sendResponse) => {
const handler = messageHandlers[message.type];
if (handler) {
handler(message, sender, sendResponse);
@@ -87,9 +89,9 @@ const messageHandlers = {
async 'update:settings'(message) {
await Storage.saveSettings(message.settings);
// Broadcast to content scripts
const tabs = await chrome.tabs.query({ url: 'https://claude.ai/*' });
const tabs = await api.tabs.query({ url: 'https://claude.ai/*' });
for (const tab of tabs) {
chrome.tabs.sendMessage(tab.id, {
api.tabs.sendMessage(tab.id, {
type: 'settings:updated',
settings: message.settings,
}).catch(() => {});
@@ -98,6 +100,6 @@ const messageHandlers = {
};
// --- Set initial badge state ---
chrome.runtime.onInstalled.addListener(() => {
chrome.action.setBadgeBackgroundColor({ color: '#6b7280' });
api.runtime.onInstalled.addListener(() => {
api.action.setBadgeBackgroundColor({ color: '#6b7280' });
});