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'); // There is nothing to switch on: either it can reach the dashboard or it // cannot. `waiting` below says why a tick captured nothing. const color = state.connected ? '#22c55e' : '#ef4444'; const label = state.connected ? 'Capturing' : 'Disconnected'; const cap = state.lastCapture; const detail = state.waiting ? `
${esc(state.waiting)}
` : cap ? `
${esc(cap.title || cap.url)}
${(cap.bytes / 1024).toFixed(1)} KB · ${new Date(cap.at).toLocaleTimeString()}
` : ''; document.getElementById('status').innerHTML = `${label}` + detail + (state.error ? `
${esc(state.error)}
` : ''); } 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)); }); document.getElementById('tsCheck').addEventListener('click', () => { // Kick off the scan and return. The worker writes progress and the result to // storage; renderTopstepState (below) reflects whatever is there, so the // popup can be closed and reopened mid-scan without losing the answer. The // callback only acknowledges the request was received — it carries no result. chrome.runtime.sendMessage({ type: 'topstep-check' }, () => void chrome.runtime.lastError); }); /** Paint the Topstep panel from the stored scan state. */ function renderTopstepState(state) { const btn = document.getElementById('tsCheck'); const out = document.getElementById('tsResult'); if (!state) { out.innerHTML = ''; return; } if (state.running) { btn.disabled = true; btn.textContent = 'Checking…'; out.innerHTML = '
Scanning the Topstep dashboard…
'; return; } btn.disabled = false; btn.textContent = 'Check activatable accounts'; if (state.error) { out.innerHTML = `
${esc(state.error)}
`; return; } const r = state.result; if (!r) { out.innerHTML = ''; return; } if (r.signedOut) { out.innerHTML = '
Not signed in to Topstep. Open the dashboard, sign in, then check again.
'; return; } if (r.error) { out.innerHTML = `
${esc(r.error)}
`; return; } const n = r.activatableCount || 0; const acctUrl = (id) => `https://dashboard.topstep.com/dashboard/accounts/${encodeURIComponent(id)}`; const list = (r.activatable || []) .map((a) => `
  • ${esc(a.name)}${a.type ? ` · ${esc(a.type)}` : ''}
  • `) .join(''); const when = r.checkedAt ? ` · ${new Date(r.checkedAt).toLocaleTimeString()}` : ''; out.innerHTML = `
    ${n} activatable
    ` + `
    ${r.alreadyActivated || 0} already activated · ${r.passedTotal || 0} passed total${when}
    ` + (list ? `` : '
    No passed accounts waiting on activation.
    '); } // Render whatever the last scan left behind when the popup opens… chrome.storage.local.get('topstepScan').then(({ topstepScan }) => renderTopstepState(topstepScan)); // …and keep up with a scan that finishes while the popup is open. chrome.storage.onChanged.addListener((c, area) => { if (area === 'local' && 'topstepScan' in c) renderTopstepState(c.topstepScan.newValue); }); chrome.storage.onChanged.addListener((c, area) => { if (area === 'local' && 'state' in c) render(); }); function esc(s) { return String(s).replace(/[&<>"]/g, (ch) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[ch])); } load();