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 = []; let settings = {}; const $ = (sel) => document.querySelector(sel); document.addEventListener('DOMContentLoaded', async () => { mappings = await Storage.getMappings(); settings = await Storage.getSettings(); // Apply settings to UI $('#showHighlights').checked = settings.showHighlights || false; $('#secretScanning').checked = settings.secretScanning !== false; $('#autoDetect').checked = settings.autoDetect !== false; $('#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); $('#btnImportAll').addEventListener('click', () => $('#fileImportAll').click()); $('#fileImportAll').addEventListener('change', importAll); // Custom domains $('#btnAddDomain').addEventListener('click', addDomain); $('#newDomain').addEventListener('keydown', (e) => { if (e.key === 'Enter') addDomain(); }); // Settings listeners $('#showHighlights').addEventListener('change', async (e) => { await Storage.saveSettings({ showHighlights: e.target.checked }); }); $('#secretScanning').addEventListener('change', async (e) => { await Storage.saveSettings({ secretScanning: e.target.checked }); }); $('#autoDetect').addEventListener('change', async (e) => { await Storage.saveSettings({ autoDetect: e.target.checked }); }); $('#autoRedactDetected').addEventListener('change', async (e) => { await Storage.saveSettings({ autoRedactDetected: e.target.checked }); }); $('#autoAddDetected').addEventListener('change', async (e) => { await Storage.saveSettings({ autoAddDetected: e.target.checked }); }); $('#maxLogEntries').addEventListener('change', async (e) => { await Storage.saveSettings({ maxLogEntries: parseInt(e.target.value, 10) || 200 }); }); // Add mapping $('#btnAddMapping').addEventListener('click', addMapping); $('#newSub').addEventListener('keydown', (e) => { if (e.key === 'Enter') addMapping(); }); // Export $('#btnExport').addEventListener('click', () => { const data = JSON.stringify(mappings, null, 2); const blob = new Blob([data], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'silent-send-mappings.json'; a.click(); URL.revokeObjectURL(url); }); // Import $('#btnImport').addEventListener('click', () => $('#fileImport').click()); $('#fileImport').addEventListener('change', async (e) => { const file = e.target.files[0]; if (!file) return; try { const text = await file.text(); const imported = JSON.parse(text); if (!Array.isArray(imported)) throw new Error('Expected array'); // Merge: add IDs if missing for (const item of imported) { if (!item.id) item.id = crypto.randomUUID(); if (!item.createdAt) item.createdAt = Date.now(); if (item.enabled === undefined) item.enabled = true; } mappings = [...mappings, ...imported]; await Storage.saveMappings(mappings); renderMappings(); } catch (err) { alert('Failed to import: ' + err.message); } }); // Clear all $('#btnClearAll').addEventListener('click', async () => { if (!confirm('Delete all mappings? This cannot be undone.')) return; mappings = []; await Storage.saveMappings([]); renderMappings(); }); // Clear log $('#btnClearLog').addEventListener('click', async () => { await Storage.clearLog(); renderLog(); }); // Reset all data $('#btnResetAll').addEventListener('click', async () => { if (!confirm('This will delete ALL your identities, mappings, settings, and logs.\n\nAre you sure?')) return; if (!confirm('Really? This cannot be undone.')) return; await api.storage.local.clear(); mappings = []; settings = {}; renderMappings(); renderDomains(); renderLog(); alert('All data cleared. Reload the extension to start fresh.'); }); }); async function addMapping() { const real = $('#newReal').value.trim(); const sub = $('#newSub').value.trim(); if (!real || !sub) return; const mapping = await Storage.addMapping({ real, substitute: sub, category: $('#newCategory').value, caseSensitive: $('#newCaseSensitive').checked, }); mappings.push(mapping); renderMappings(); $('#newReal').value = ''; $('#newSub').value = ''; $('#newCaseSensitive').checked = false; $('#newReal').focus(); } function renderMappings() { const tbody = $('#mappingTableBody'); if (mappings.length === 0) { tbody.innerHTML = '