const FirstRunSetup = (function() { 'use strict'; const COMPLETE_KEY = 'intercept.setup.complete.v1'; const DEFAULT_MODE_KEY = 'intercept.default_mode'; let overlayEl = null; let depsStatusEl = null; let locationStatusEl = null; let notifyStatusEl = null; let modeStatusEl = null; let modeSelectEl = null; let uiTierStatusEl = null; let dependencyReady = null; function init() { buildDOM(); maybeShow(); } function maybeShow() { if (localStorage.getItem(COMPLETE_KEY) === 'true') return; if (localStorage.getItem('disclaimerAccepted') === 'true') { open(); refreshStatuses(); return; } let attempts = 0; const waitTimer = setInterval(() => { attempts += 1; if (localStorage.getItem(COMPLETE_KEY) === 'true') { clearInterval(waitTimer); return; } if (localStorage.getItem('disclaimerAccepted') === 'true') { clearInterval(waitTimer); open(); refreshStatuses(); } if (attempts > 30) { clearInterval(waitTimer); } }, 1000); } function buildDOM() { overlayEl = document.createElement('div'); overlayEl.id = 'firstRunSetupOverlay'; overlayEl.className = 'setup-overlay'; const modal = document.createElement('div'); modal.className = 'setup-modal'; const header = document.createElement('div'); header.className = 'setup-header'; const headingWrap = document.createElement('div'); const title = document.createElement('h2'); title.className = 'setup-title'; title.textContent = 'Quick Setup'; headingWrap.appendChild(title); const subtitle = document.createElement('p'); subtitle.className = 'setup-subtitle'; subtitle.textContent = 'Complete these checks once so all modes work reliably.'; headingWrap.appendChild(subtitle); const closeBtn = document.createElement('button'); closeBtn.type = 'button'; closeBtn.className = 'setup-close'; closeBtn.textContent = '×'; closeBtn.setAttribute('aria-label', 'Close setup assistant'); closeBtn.addEventListener('click', close); header.appendChild(headingWrap); header.appendChild(closeBtn); const content = document.createElement('div'); content.className = 'setup-content'; const depsStep = createStep( 'Dependencies', 'Verify required tools are installed for enabled modes.', (statusEl, actionsEl) => { depsStatusEl = statusEl; const checkBtn = buildButton('Recheck', () => checkDependencies()); const openToolsBtn = buildButton('Open Tools', () => { if (typeof showSettings === 'function') showSettings(); if (typeof switchSettingsTab === 'function') switchSettingsTab('tools'); }); actionsEl.appendChild(checkBtn); actionsEl.appendChild(openToolsBtn); } ); const locationStep = createStep( 'Observer Location', 'Set latitude/longitude for pass prediction and mapping features.', (statusEl, actionsEl) => { locationStatusEl = statusEl; actionsEl.appendChild(buildButton('Open Location', () => { if (typeof showSettings === 'function') showSettings(); if (typeof switchSettingsTab === 'function') switchSettingsTab('location'); })); actionsEl.appendChild(buildButton('Recheck', refreshStatuses)); } ); const notifyStep = createStep( 'Desktop Alerts', 'Allow notifications so high-priority alerts are visible when the tab is hidden.', (statusEl, actionsEl) => { notifyStatusEl = statusEl; actionsEl.appendChild(buildButton('Enable Notifications', requestNotifications)); } ); const modeStep = createStep( 'Default Start Mode', 'Choose which mode should be selected by default.', (statusEl, actionsEl) => { modeStatusEl = statusEl; modeSelectEl = document.createElement('select'); modeSelectEl.className = 'setup-btn'; const modes = [ ['pager', 'Pager'], ['sensor', '433MHz'], ['rtlamr', 'Meters'], ['waterfall', 'Waterfall'], ['wifi', 'WiFi'], ['bluetooth', 'Bluetooth'], ['bt_locate', 'BT Locate'], ['aprs', 'APRS'], ['satellite', 'Satellite'], ['sstv', 'ISS SSTV'], ['weathersat', 'Weather Sat'], ['sstv_general', 'HF SSTV'], ]; for (const [value, label] of modes) { const opt = document.createElement('option'); opt.value = value; opt.textContent = label; modeSelectEl.appendChild(opt); } const savedDefaultMode = localStorage.getItem(DEFAULT_MODE_KEY); if (savedDefaultMode) { const normalizedMode = savedDefaultMode === 'listening' ? 'waterfall' : savedDefaultMode; modeSelectEl.value = normalizedMode; if (normalizedMode !== savedDefaultMode) { localStorage.setItem(DEFAULT_MODE_KEY, normalizedMode); } } actionsEl.appendChild(modeSelectEl); actionsEl.appendChild(buildButton('Save', () => { const selected = modeSelectEl.value || 'pager'; localStorage.setItem(DEFAULT_MODE_KEY, selected); refreshStatuses(); if (typeof showAppToast === 'function') { showAppToast('Default Mode Saved', `New sessions will default to ${selected}.`, 'info'); } })); } ); const tierStep = createStep( 'Display Mode', 'Choose how the interface looks. You can change this later with the toggle in the nav bar.', (statusEl, actionsEl) => { uiTierStatusEl = statusEl; const btnWrap = document.createElement('div'); btnWrap.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:4px;'; function makeTierBtn(tier, title, desc, previewFn) { const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'setup-btn'; btn.style.cssText = 'display:flex;flex-direction:column;align-items:flex-start;height:auto;padding:10px;text-align:left;gap:4px;'; const preview = document.createElement('div'); preview.style.cssText = 'width:100%;height:48px;border-radius:3px;margin-bottom:4px;overflow:hidden;'; previewFn(preview); const titleEl = document.createElement('strong'); titleEl.style.cssText = 'font-size:12px;'; titleEl.textContent = title; const descEl = document.createElement('span'); descEl.style.cssText = 'font-size:10px;color:var(--text-dim);white-space:normal;line-height:1.3;'; descEl.textContent = desc; btn.appendChild(preview); btn.appendChild(titleEl); btn.appendChild(descEl); btn.addEventListener('click', () => { document.documentElement.setAttribute('data-ui-tier', tier); localStorage.setItem('intercept-ui-tier', tier); refreshStatuses(); if (typeof showAppToast === 'function') { showAppToast('Display Mode Set', `Switched to ${title} mode.`, 'info'); } }); return btn; } const leanBtn = makeTierBtn('lean', 'Lean', 'No effects — for Raspberry Pi or older hardware.', (el) => { el.style.cssText += 'background:#111;border:1px solid #222;display:flex;flex-direction:column;justify-content:center;padding:6px;gap:3px;'; el.innerHTML = '