From a844c94f1364ae472eb345a03b8e39ee538def76 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Mar 2026 02:37:04 +0000 Subject: [PATCH] fix: activity log now records from injector + dynamic icon colors Activity log fix: - Injector now writes directly to storage.local in addition to sending runtime messages to the background worker. This fixes the issue where MV3 service worker sleep caused messages to be silently dropped. Dynamic icon colors: - Black "SS" = active, normal - Blue "SS" = reveal mode on - Red "SS" = Silent Send disabled - Icons generated via OffscreenCanvas in the service worker - Updates on every settings change and keyboard shortcut toggle Also adds keyboard shortcuts section to Options page showing current bindings and how to customize them per browser. https://claude.ai/code/session_01Dvgwe7XMoSxnWXkih8p1Cw --- src/background/service-worker.js | 85 +++++++++++++++++++++++++++++--- src/content/injector.js | 24 ++++++++- src/options/options.html | 23 +++++++++ 3 files changed, 124 insertions(+), 8 deletions(-) diff --git a/src/background/service-worker.js b/src/background/service-worker.js index 3034780..e3dc304 100644 --- a/src/background/service-worker.js +++ b/src/background/service-worker.js @@ -176,10 +176,10 @@ api.commands.onCommand.addListener(async (command) => { settings.revealMode = !settings.revealMode; await Storage.saveSettings({ revealMode: settings.revealMode }); - // Broadcast to all tabs broadcastSettings(settings); + await updateIcon(settings); - // Show brief badge text + // Flash badge text briefly const [tab] = await api.tabs.query({ active: true, currentWindow: true }); if (tab) { api.action.setBadgeText({ text: settings.revealMode ? 'EYE' : '', tabId: tab.id }); @@ -188,7 +188,6 @@ api.commands.onCommand.addListener(async (command) => { tabId: tab.id, }); if (!settings.revealMode) { - // Restore normal badge after a moment setTimeout(() => updateBadge(tab.id), 1500); } } @@ -199,8 +198,9 @@ api.commands.onCommand.addListener(async (command) => { await Storage.saveSettings({ enabled: settings.enabled }); broadcastSettings(settings); + await updateIcon(settings); - // Flash badge + // Flash badge text briefly const [tab] = await api.tabs.query({ active: true, currentWindow: true }); if (tab) { api.action.setBadgeText({ text: settings.enabled ? 'ON' : 'OFF', tabId: tab.id }); @@ -230,7 +230,78 @@ async function broadcastSettings(settings) { } } -// --- Set initial badge state --- -api.runtime.onInstalled.addListener(() => { - api.action.setBadgeBackgroundColor({ color: '#6b7280' }); +// --- Dynamic Icon Colors --- +// Green = active, Blue = reveal mode, Red = disabled, Gray = unconfigured + +function generateIcon(color, size) { + const canvas = new OffscreenCanvas(size, size); + const ctx = canvas.getContext('2d'); + + // Rounded rect background + const r = size * 0.19; + ctx.beginPath(); + ctx.moveTo(r, 0); + ctx.lineTo(size - r, 0); + ctx.quadraticCurveTo(size, 0, size, r); + ctx.lineTo(size, size - r); + ctx.quadraticCurveTo(size, size, size - r, size); + ctx.lineTo(r, size); + ctx.quadraticCurveTo(0, size, 0, size - r); + ctx.lineTo(0, r); + ctx.quadraticCurveTo(0, 0, r, 0); + ctx.closePath(); + ctx.fillStyle = color; + ctx.fill(); + + // "SS" text + ctx.fillStyle = '#ffffff'; + ctx.font = `bold ${size * 0.44}px sans-serif`; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText('SS', size / 2, size / 2 + size * 0.03); + + return ctx.getImageData(0, 0, size, size); +} + +async function updateIcon(settings) { + let color; + if (!settings.enabled) { + color = '#dc2626'; // red — disabled + } else if (settings.revealMode) { + color = '#1d4ed8'; // blue — reveal mode + } else { + color = '#111111'; // dark — normal active + } + + try { + const imageData = { + 16: generateIcon(color, 16), + 32: generateIcon(color, 32), + 48: generateIcon(color, 48), + }; + await api.action.setIcon({ imageData }); + } catch (e) { + // OffscreenCanvas may not be available in all contexts + } +} + +// Update icon when settings change +api.storage.onChanged.addListener(async (changes) => { + if (changes.ss_settings) { + const settings = { ...(changes.ss_settings.newValue || {}) }; + await updateIcon(settings); + } }); + +// --- Set initial state --- +api.runtime.onInstalled.addListener(async () => { + api.action.setBadgeBackgroundColor({ color: '#6b7280' }); + const settings = await Storage.getSettings(); + await updateIcon(settings); +}); + +// Also set icon on startup (service worker wake) +(async () => { + const settings = await Storage.getSettings(); + await updateIcon(settings); +})(); diff --git a/src/content/injector.js b/src/content/injector.js index 680f2bc..f42a1c1 100644 --- a/src/content/injector.js +++ b/src/content/injector.js @@ -38,14 +38,36 @@ script.onload = () => script.remove(); // Listen for substitution events from the page script - window.addEventListener('message', (event) => { + window.addEventListener('message', async (event) => { if (event.source !== window) return; if (event.data?.type === 'ss:substitution-performed') { + // Try to notify background for badge update api.runtime.sendMessage({ type: 'substitution:performed', count: event.data.count, replacements: event.data.replacements, }).catch(() => {}); + + // Also log directly from the injector (content script world) + // in case the background worker is asleep + const replacements = event.data.replacements || []; + for (const r of replacements) { + const log = (await api.storage.local.get('ss_activity_log')).ss_activity_log || []; + log.unshift({ + id: crypto.randomUUID(), + timestamp: Date.now(), + type: 'substitution', + direction: 'outbound', + original: r.original, + replaced: r.replaced, + category: r.category || 'general', + pattern: r.pattern || '', + url: location.href, + }); + // Trim + if (log.length > 200) log.length = 200; + await api.storage.local.set({ ss_activity_log: log }); + } } }); diff --git a/src/options/options.html b/src/options/options.html index ceafb41..3c5a8ba 100644 --- a/src/options/options.html +++ b/src/options/options.html @@ -12,6 +12,29 @@

Manage your privacy substitution mappings

+
+

Keyboard Shortcuts

+
+
+ +

Show your real data in AI responses

+
+ Alt+Shift+R +
+
+
+ +

Quickly enable or disable all substitution

+
+ Alt+Shift+S +
+

+ To change these shortcuts:
+ Chrome: chrome://extensions/shortcuts
+ Firefox: about:addons → gear icon → Manage Extension Shortcuts +

+
+

Settings