From a084790ab7e3171264560c29d47479f942b303ec Mon Sep 17 00:00:00 2001 From: c4ch3c4d3 Date: Wed, 19 Aug 2026 06:52:49 -0500 Subject: [PATCH] =?UTF-8?q?docs:=20implementation=20plan=20=E2=80=94=20enc?= =?UTF-8?q?ryption=20landscape=20card=20ring=20+=20key=20redesign?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-08-19-encryption-card-redesign.md | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-19-encryption-card-redesign.md diff --git a/docs/superpowers/plans/2026-08-19-encryption-card-redesign.md b/docs/superpowers/plans/2026-08-19-encryption-card-redesign.md new file mode 100644 index 0000000..83ec694 --- /dev/null +++ b/docs/superpowers/plans/2026-08-19-encryption-card-redesign.md @@ -0,0 +1,179 @@ +# Encryption Landscape Card — Ring + Key Redesign Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the Encryption Landscape recon card's text headline + clipped canvas legend with a full-size plain-hole ring graph and a wrapping HTML legend (dot + label + count per encryption bucket). + +**Architecture:** Single-file client-side change: the recon view in `views.js` drops the `encValue`/`encSub` text nodes, draws the doughnut with `legend:false` and a taller height, and populates a new HTML legend container from the existing `encCounts` bucket map. `chart.js`'s `MiniChart.doughnut` is unchanged (its canvas `legend` option is simply no longer used by the enc card). CSS adds flex-wrap legend styles. + +**Tech Stack:** Vanilla JS (no framework), canvas via `MiniChart.doughnut` in `chart.js`, plain CSS in `app.css`. Device deploy via `scripts/deploy.sh --password 'Bryce9205'`. Tests: none exist for the frontend; verification is via the deployed device + backend test suite (must stay green). + +## Global Constraints + +- Do not alter data source, `reconEncBucket`, per-scan bucketing, re-sync, or the other four recon cards. +- Legend entries: colored dot + label + count, format `● WPA2 54`; buckets with zero APs hidden. +- Ring center hole stays plain (no text). +- Keep `MiniChart.doughnut`'s `legend` option in `chart.js` (used by no caller after this change, but harmless). +- Empty state text stays "No encryption data yet — run a scan." +- No comments added to code unless already present in the surrounding style. +- Deploy and verify on the device; backend `tests/` suite must stay green (291 tests). + +--- + +### Task 1: Ring + HTML legend for Encryption Landscape card + +**Files:** +- Modify: `payload/user/remote_access/pager-webui/www/js/views.js:1497-1507` (card markup) +- Modify: `payload/user/remote_access/pager-webui/www/js/views.js:2256-2322` (drawCharts enc block) +- Modify: `payload/user/remote_access/pager-webui/www/css/app.css` (after line 328) +- Test: none (frontend); verify via device sweep + +**Interfaces:** +- Consumes: `RECON_ENC_BUCKETS` (6 names, `views.js:852`), `RECON_ENC_COLORS` (6 colors, `views.js:851`), `encCounts` object `{bucketName: count}`, `reconEncBucket(a.encryption)`. +- Produces: DOM `div#recon-enc-legend` under the enc card body, populated by `drawCharts`; `canvas#recon-encryption` redrawn with `{ legend: false, height: 120 }`. `encValue`/`encSub` variables are removed. + +- [ ] **Step 1: Edit the enc card markup in views.js** + +Replace lines 1497-1507: + +```js + const encBody = titleCard('Encryption Landscape', null); + const encValue = h('div', { class: 'recon-card-value', text: '—' }); + const encSub = h('div', { class: 'recon-card-sub', text: '' }); + encBody.appendChild(encValue); + encBody.appendChild(encSub); + const encBox = h('div', { class: 'recon-chart-box' }); + encBody.appendChild(encBox); + const encCanvas = h('canvas', { id: 'recon-encryption' }); + encBox.appendChild(encCanvas); + const encEmpty = h('div', { class: 'recon-no-data', text: 'No encryption data yet — run a scan.' }); + encBox.appendChild(encEmpty); +``` + +with: + +```js + const encBody = titleCard('Encryption Landscape', null); + const encBox = h('div', { class: 'recon-chart-box' }); + encBody.appendChild(encBox); + const encCanvas = h('canvas', { id: 'recon-encryption' }); + encBox.appendChild(encCanvas); + const encEmpty = h('div', { class: 'recon-no-data', text: 'No encryption data yet — run a scan.' }); + encBox.appendChild(encEmpty); + const encLegend = h('div', { class: 'recon-enc-legend' }); + encBody.appendChild(encLegend); +``` + +- [ ] **Step 2: Remove the enc text-headline computation in drawCharts** + +Replace lines 2256-2266 (the `encCounts` loop stays — only the `topEnc` block goes): + +```js + let topEnc = null, topEncN = 0; + Object.keys(encCounts).forEach((k) => { + if (encCounts[k] > topEncN) { topEnc = k; topEncN = encCounts[k]; } + }); + encValue.textContent = topEnc || '—'; + encSub.textContent = topEnc ? topEncN + ' of ' + n + ' APs' : ''; +``` + +with nothing (delete those lines). The `encCounts` computation immediately above must remain. + +- [ ] **Step 3: Rewrite the enc chart block in drawCharts** + +Replace lines 2308-2322: + +```js + const enc = document.getElementById('recon-encryption'); + if (enc && typeof MiniChart !== 'undefined' && MiniChart.doughnut) { + try { + if (aps.length) { + MiniChart.doughnut(enc, RECON_ENC_BUCKETS.map((k, i) => ({ + label: k, value: encCounts[k] || 0, color: RECON_ENC_COLORS[i] + })), { legend: true, height: 66 }); + enc.classList.remove('hidden'); + encEmpty.classList.add('hidden'); + } else { + enc.classList.add('hidden'); + encEmpty.classList.remove('hidden'); + } + } catch (e) {} + } +``` + +with: + +```js + const enc = document.getElementById('recon-encryption'); + if (enc && typeof MiniChart !== 'undefined' && MiniChart.doughnut) { + try { + if (aps.length) { + MiniChart.doughnut(enc, RECON_ENC_BUCKETS.map((k, i) => ({ + label: k, value: encCounts[k] || 0, color: RECON_ENC_COLORS[i] + })), { legend: false, height: 120 }); + enc.classList.remove('hidden'); + encEmpty.classList.add('hidden'); + } else { + enc.classList.add('hidden'); + encEmpty.classList.remove('hidden'); + } + } catch (e) {} + } + const encLegend = document.getElementById('recon-enc-legend'); + if (encLegend) { + encLegend.innerHTML = ''; + RECON_ENC_BUCKETS.forEach((k, i) => { + const c = encCounts[k] || 0; + if (!c) return; + const entry = h('div', { class: 'recon-enc-entry' }); + const dot = h('span', { class: 'recon-enc-dot' }); + dot.style.background = RECON_ENC_COLORS[i]; + entry.appendChild(dot); + entry.appendChild(h('span', { class: 'recon-enc-label', text: k })); + entry.appendChild(h('span', { class: 'recon-enc-count', text: String(c) })); + encLegend.appendChild(entry); + }); + } +``` + +- [ ] **Step 4: Add the legend CSS to app.css** + +Insert after line 328 (the `.recon-no-data` rule): + +```css +.recon-enc-legend { display: flex; flex-wrap: wrap; gap: 2px 10px; margin-top: 4px; align-items: baseline; } +.recon-enc-entry { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--text); } +.recon-enc-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; } +.recon-enc-label { color: var(--muted); } +.recon-enc-count { font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; } +``` + +- [ ] **Step 5: Syntax check both JS files** + +Run: `node --check payload/user/remote_access/pager-webui/www/js/views.js` +Run: `node --check payload/user/remote_access/pager-webui/www/js/chart.js` +Expected: exit 0, no output. + +- [ ] **Step 6: Run the backend test suite** + +Run: `cd /Users/bzuccaro/Documents/AI/Mark-VIII && python3 -m pytest tests/ -q 2>&1 | tail -3` +Expected: `291 passed` (or the current passing count) — no regressions from unrelated files. + +- [ ] **Step 7: Deploy to the device** + +Run: `cd /Users/bzuccaro/Documents/AI/Mark-VIII && ./scripts/deploy.sh --password 'Bryce9205'` +Expected: deploy completes with `EXTRACT_OK` / success output. + +- [ ] **Step 8: Verify the enc card on the device** + +Recreate the CDP venv if absent (`python3 -m venv /tmp/cdpenv2 && /tmp/cdpenv2/bin/pip install -q websocket-client`), then drive headless Chrome against http://172.16.52.1:8080 (login `Bryce9205`, go to `#/recon`, wait ~20s) and assert: +1. `document.getElementById('recon-encryption')` canvas has non-zero `width` attribute and the card is visible (not `.hidden`). +2. `document.getElementById('recon-enc-legend')` contains entries whose text matches `/WPA2/` and `/\d+/`, and no `recon-card-value` element exists inside the enc card. +3. Zero `Runtime.exceptionThrown` events. +Expected: all three pass; screenshots unavailable, text-state assertions only. + +- [ ] **Step 9: Commit** + +```bash +cd /Users/bzuccaro/Documents/AI/Mark-VIII && git add payload/user/remote_access/pager-webui/www/js/views.js payload/user/remote_access/pager-webui/www/css/app.css && git commit -m "ui: encryption landscape card — ring + HTML legend with counts" +```