The popup's target URL pattern was doing two jobs. As a fallback for locate requests it is now dead — every step carries its firm's pattern. As the thing deciding which tab gets scraped it was still load-bearing: without it, capture falls back to whichever tab is active, which means scraping a banking or mail tab and storing it in the dashboard's database. So the setting goes, but the scoping moves to the manifest rather than disappearing. host_permissions already lists exactly the hosts this extension is allowed to read; capture now queries those (minus localhost, which is the dashboard mirroring its own output back). The two cannot drift apart, and adding a firm — which means adding its host to the manifest anyway — scopes capture without a second place to remember. With more than one firm open, capture prefers the active tab over the first match, so it follows attention rather than tab order. That case could not arise while a single pattern matched one site. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
52 lines
1.9 KiB
JavaScript
52 lines
1.9 KiB
JavaScript
const FIELDS = ['apiBase', 'pollSeconds', 'elementSelector'];
|
|
const DEFAULTS = {
|
|
apiBase: 'http://localhost:3000',
|
|
pollSeconds: 3,
|
|
elementSelector: '',
|
|
};
|
|
|
|
async function load() {
|
|
const cfg = { ...DEFAULTS, ...(await chrome.storage.local.get(FIELDS)) };
|
|
for (const f of FIELDS) document.getElementById(f).value = cfg[f];
|
|
render();
|
|
}
|
|
|
|
async function render() {
|
|
const { state = {} } = await chrome.storage.local.get('state');
|
|
const color = !state.connected ? '#ef4444' : state.enabled ? '#22c55e' : '#94a3b8';
|
|
const label = !state.connected ? 'Disconnected' : state.enabled ? 'ON — capturing' : 'Connected — off';
|
|
|
|
const cap = state.lastCapture;
|
|
const detail = state.waiting
|
|
? `<div class="muted">${esc(state.waiting)}</div>`
|
|
: cap
|
|
? `<div class="muted">${esc(cap.title || cap.url)}<br>${(cap.bytes / 1024).toFixed(1)} KB · ${new Date(cap.at).toLocaleTimeString()}</div>`
|
|
: '';
|
|
|
|
document.getElementById('status').innerHTML =
|
|
`<span class="dot" style="background:${color}"></span><strong>${label}</strong>` +
|
|
detail +
|
|
(state.error ? `<div class="err">${esc(state.error)}</div>` : '');
|
|
}
|
|
|
|
document.getElementById('save').addEventListener('click', async () => {
|
|
const patch = {};
|
|
for (const f of FIELDS) patch[f] = document.getElementById(f).value.trim();
|
|
patch.pollSeconds = Number(patch.pollSeconds) || DEFAULTS.pollSeconds;
|
|
patch.apiBase = patch.apiBase || DEFAULTS.apiBase;
|
|
await chrome.storage.local.set(patch);
|
|
setTimeout(render, 400);
|
|
});
|
|
|
|
document.getElementById('now').addEventListener('click', () => {
|
|
chrome.runtime.sendMessage({ type: 'capture-now' }, () => setTimeout(render, 200));
|
|
});
|
|
|
|
chrome.storage.onChanged.addListener((c, area) => { if (area === 'local' && 'state' in c) render(); });
|
|
|
|
function esc(s) {
|
|
return String(s).replace(/[&<>"]/g, (ch) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[ch]));
|
|
}
|
|
|
|
load();
|