fix: date false-positive, partial-word highlights, cross-browser sync

Bug fixes:
- Date (possible DOB) pattern now requires context words (born, birthday,
  dob, etc.) before firing — prevents spurious warnings on page-load API
  calls that happen to contain ISO dates in conversation history.
- Highlight regex now uses word boundaries (\b) so short substitute values
  (e.g. "aud") no longer match inside unrelated words like "Claude".
- Both TreeWalkers in content.js now skip the extension's own UI elements
  (.ss-autodetect-warning, .ss-presend-warning, .ss-reveal-badge) to
  prevent the highlight API from marking text in the extension's banners.

Settings sync:
- New src/lib/sync.js: exportSyncCode / importSyncCode (base64 JSON) for
  manual copy-paste across any browser combination. Newest lastModified
  timestamp wins; force flag available to override.
- browser.storage.sync support: when "Browser account sync" is enabled the
  extension automatically pushes/pulls via Firefox Sync or Chrome account,
  chunked to stay within per-item quota limits.
- storage.js now writes ss_lastModified on every save so conflict resolution
  has an accurate timestamp.
- service-worker.js listens for both local and sync storage changes to keep
  all copies in sync.
- New "Sync Between Browsers" section in options.html with Generate/Copy/
  Import Sync Code UI and the browser sync toggle.

https://claude.ai/code/session_01TKpSR9M8JgHLXCp5CeDsQP
This commit is contained in:
Claude
2026-03-26 14:13:22 +00:00
parent 2e79e02bd3
commit 84f6e675db
6 changed files with 340 additions and 12 deletions
+79
View File
@@ -1,5 +1,6 @@
import Storage from '../lib/storage.js';
import SilentSendCrypto from '../lib/crypto.js';
import SilentSendSync from '../lib/sync.js';
import api from '../lib/browser-polyfill.js';
let mappings = [];
@@ -18,11 +19,82 @@ document.addEventListener('DOMContentLoaded', async () => {
$('#autoRedactDetected').checked = settings.autoRedactDetected !== false;
$('#autoAddDetected').checked = settings.autoAddDetected !== false;
$('#maxLogEntries').value = settings.maxLogEntries || 200;
$('#browserSync').checked = settings.browserSync === true;
renderMappings();
renderDomains();
renderLog();
// --- Sync section ---
$('#browserSync').addEventListener('change', async (e) => {
await Storage.saveSettings({ browserSync: e.target.checked });
if (e.target.checked) {
await SilentSendSync.pushToSyncStorage();
setSyncStatus('Browser account sync enabled. Your settings will sync automatically.', 'ok');
} else {
setSyncStatus('Browser account sync disabled.', 'neutral');
}
});
$('#btnGenerateSyncCode').addEventListener('click', async () => {
const code = await SilentSendSync.exportSyncCode();
const data = await SilentSendSync._getAllData();
$('#syncCodeText').value = code;
$('#syncCodeDisplay').style.display = 'block';
$('#syncImportSection').style.display = 'none';
$('#syncCodeTime').textContent = 'Generated: ' + new Date(data.lastModified).toLocaleString();
setSyncStatus('', 'neutral');
});
$('#btnCopySyncCode').addEventListener('click', async () => {
try {
await navigator.clipboard.writeText($('#syncCodeText').value);
$('#btnCopySyncCode').textContent = 'Copied!';
setTimeout(() => { $('#btnCopySyncCode').textContent = 'Copy to Clipboard'; }, 2000);
} catch {
$('#syncCodeText').select();
document.execCommand('copy');
}
});
$('#btnImportSyncCode').addEventListener('click', () => {
$('#syncImportSection').style.display = 'block';
$('#syncCodeDisplay').style.display = 'none';
$('#syncImportText').focus();
setSyncStatus('', 'neutral');
});
$('#btnApplySyncCode').addEventListener('click', async () => {
const code = $('#syncImportText').value.trim();
if (!code) return;
const force = $('#syncForce').checked;
const result = await SilentSendSync.importSyncCode(code, { force });
if (result.success) {
setSyncStatus(`Imported successfully (data from ${result.importTime}).`, 'ok');
$('#syncImportSection').style.display = 'none';
$('#syncImportText').value = '';
mappings = await Storage.getMappings();
settings = await Storage.getSettings();
$('#browserSync').checked = settings.browserSync === true;
renderMappings();
renderDomains();
renderLog();
} else if (result.skipped) {
setSyncStatus(
`Skipped: local data is newer (local: ${result.localTime} vs import: ${result.importTime}). Check "Force" to override.`,
'warn'
);
} else {
setSyncStatus(`Failed: ${result.reason}`, 'error');
}
});
$('#btnCancelSyncImport').addEventListener('click', () => {
$('#syncImportSection').style.display = 'none';
$('#syncImportText').value = '';
setSyncStatus('', 'neutral');
});
// Transfer data
$('#btnExportAll').addEventListener('click', exportAllPlain);
$('#btnExportEncrypted').addEventListener('click', exportAllEncrypted);
@@ -393,6 +465,13 @@ async function importAll(e) {
e.target.value = '';
}
function setSyncStatus(msg, type) {
const el = $('#syncStatus');
if (!el) return;
el.textContent = msg;
el.style.color = type === 'ok' ? '#10b981' : type === 'warn' ? '#f59e0b' : type === 'error' ? '#dc2626' : '#6b7280';
}
function escapeHtml(str) {
const div = document.createElement('div');
div.textContent = str;