(data)
| 2923 | } |
| 2924 | |
| 2925 | function _wifiSetStatus(text) { |
| 2926 | const st = document.getElementById('wifi-status'); |
| 2927 | if (st) st.textContent = text; |
| 2928 | const fs = document.getElementById('wifi-fs-status'); |
| 2929 | if (fs) fs.textContent = text; |
| 2930 | } |
| 2931 | |
| 2932 | // Mirror the hardware gates (HackRF present? Huginn present?) onto the |
| 2933 | // full-screen controls — the in-page buttons are the source of truth. |
| 2934 | function _wifiFsSyncGates() { |
| 2935 | const zb = document.getElementById('wifi-fs-zb'), zbl = document.getElementById('wifi-fs-zb-label'); |
| 2936 | const zbMain = document.getElementById('wifi-zb'); |
| 2937 | if (zb && zbMain) { |
| 2938 | zb.disabled = zbMain.disabled; |
| 2939 | if (zbl) { zbl.style.opacity = zbMain.disabled ? '.4' : '1'; zbl.style.cursor = zbMain.disabled ? 'not-allowed' : 'pointer'; } |
| 2940 | } |
| 2941 | const wf = document.getElementById('wifi-fs-view-wf'), wfMain = document.getElementById('wifi-view-wf'); |
| 2942 | if (wf && wfMain) { |
| 2943 | wf.disabled = wfMain.disabled; |
| 2944 | wf.style.opacity = wfMain.disabled ? '.4' : '1'; |
| 2945 | wf.style.cursor = wfMain.disabled ? 'not-allowed' : 'pointer'; |
| 2946 | wf.title = wfMain.title; |
| 2947 | } |
| 2948 | } |
| 2949 | |
| 2950 | // Repaint everything the console owns (called after each render/scan). |
| 2951 | function _wifiFsRender() { |
| 2952 | if (!_wifiState.fs.open) return; |
| 2953 | const d = _wifiState.data; |
| 2954 | const iface = document.getElementById('wifi-fs-iface'); |
| 2955 | if (iface) iface.textContent = _wifiState.iface ? `· ${_wifiState.iface}` : ''; |
| 2956 | const st = document.getElementById('wifi-fs-status'), main = document.getElementById('wifi-status'); |
| 2957 | if (st && main) st.textContent = main.textContent; |
| 2958 | _wifiFsSyncGates(); |
| 2959 | // Band chips — clicking one filters the spectrum to that band. |
| 2960 | const summ = document.getElementById('wifi-fs-summary'); |
| 2961 | if (summ) { |
| 2962 | const ratingColor = { clear: '#22c55e', moderate: '#eab308', congested: '#ef4444' }; |
| 2963 | let chips = ''; |
| 2964 | if (d) chips = Object.keys(d.spectrum).sort().map(b => { |
| 2965 | const s = d.spectrum[b]; |
| 2966 | const on = _wifiState.band === b; |
| 2967 | const wa = s.width_advice ? ` · <span title="${_esc(s.width_advice.reason)}">→ ${s.width_advice.mhz}MHz</span>` : ''; |
| 2968 | return `<button onclick="wifiSetBand('${on ? 'all' : b}')" title="${on ? 'Show all bands' : 'Show only this band'}" |
| 2969 | style="background:${_WIFI_BAND_COLOR[b]}${on ? '33' : '18'};border:1px solid ${_WIFI_BAND_COLOR[b]}${on ? 'cc' : '55'};border-radius:6px;padding:3px 8px;cursor:pointer"> |
| 2970 | <b style="color:${_WIFI_BAND_COLOR[b]}">${b} GHz</b> · ${s.ap_count} AP · |
| 2971 | <span style="color:${ratingColor[s.rating]}">${s.rating}</span> · best ch ${s.recommend.join(', ')}${wa}</button>`; |
| 2972 | }).join(''); |
| 2973 | if (d && d.noise_floor != null) chips += `<span class="wifi-fs-tag" style="padding:3px 8px">noise floor ${d.noise_floor} dBm</span>`; |
| 2974 | summ.innerHTML = chips || '<span class="wifi-fs-meta">No APs heard yet — hit Scan.</span>'; |
| 2975 | } |
| 2976 | const leg = document.getElementById('wifi-fs-legend'); |
| 2977 | if (leg) leg.innerHTML = '<span>Signal:</span>' + |
| 2978 | [['#22c55e', 'strong ≥−55'], ['#84cc16', 'good ≥−67'], ['#eab308', 'fair ≥−75'], ['#f97316', 'weak ≥−85'], ['#ef4444', 'v.weak']] |
| 2979 | .map(([c, l]) => `<span style="display:inline-flex;align-items:center;gap:4px"><span style="width:10px;height:10px;background:${c};border-radius:2px;display:inline-block"></span>${l}</span>`).join('') + |
| 2980 | '<span style="color:#a78bfa">◆ DFS/radar</span><span style="color:#fbbf24">⚠ security issue</span>' + |
| 2981 | '<span class="wifi-fs-meta">keys: ↑↓ select · s scan · b/d view · a/2/5/6 band · Esc exit</span>'; |
| 2982 | _wifiFsRenderList(); |
no test coverage detected