Files
Mark-VIII/docs/superpowers/specs/2026-08-18-ui-hardening-design.md
T
bzuccaro d7ef0624f4 ui: recon card redesign, channel-map hover, Send to PineAP prefill, recon hardening
- Top 5 recon cards restyled as stat cards: value + sub-line + mini chart,
  consistent 190px anatomy; Handshakes links to the handshakes page;
  Previous Scans shows count/latest plus a compact picker + action row
- Channel map records lobe geometry (canvas.__reconLobes / __reconLobesHit);
  hovering lists the networks under the cursor, click pins the tooltip,
  mouseleave hides it; empty states hide the tooltip too
- Recon focus sidebar gains 'Send to PineAP — Twin this network': routes to
  Evil WPA (psk2/sae/owe from recon encryption) or OpenAP (with BSSID) and
  prefills the form; one-shot PineAPPrefill is consumed by the attack
  launcher and never auto-deploys or leaks into manual forms
- Existing Actions verified end-to-end and hardened: capture/stop handshake
  sync the auto-collect toggle, examine buttons disable while pending
- Recon resilience: loadDetail retries after failed render or 503 (no more
  permanently blank page), scan-list errors surface in the scan bar, poll
  guard prevents overlapping loads, chart draws are individually isolated
- Auto-collect toggle re-syncs from get_config on the slow poll so the UI
  tracks the pager's own settings
2026-08-18 23:49:49 -05:00

6.0 KiB

Mark VIII UI Hardening + Recon Polish — Design

Date: 2026-08-18 Status: Approved in advance by user (auto-approve; user unavailable for review)

Context

Mark VIII (web UI for the WiFi Pineapple Pager) has been through major dev changes. The user runs a live test tomorrow and needs the UI rock solid. Verified on device:

  • MCP server works: tools/list returns 16 tools; device.state, recon.aps respond correctly.
  • API endpoints used by the Recon "Actions" buttons work (/api/recon/examine, /api/pineap/set_config).
  • Recon detail endpoint is slow (~1.5 s) and can 503 when the sqlite DB is busy; the UI swallows errors silently and can stay blank.

Findings

  1. Recon top cards (5): equal 200 px cards with 20 px titles, centered content, charts sized for wide cards. "Previous Scans" crams 5 icon buttons + a 50+-option select into a ~220 px card → buttons stack into three rows. "Channel Distribution" bar chart is unreadable at card width. Handshakes toggle text wraps awkwardly.
  2. Channel map: canvas lobes render but have zero interactivity — no way to see which networks are under the cursor.
  3. Focus sidebar Actions: "Capture WPA Handshakes" / "Stop Handshake Capture" / "Examine BSSID" / "Examine Channel" work at the API level but give weak feedback; there is no path from a recon target to the PineAP evil-twin form.
  4. Robustness: loadDetail() in the recon view swallows render/network errors with .catch(() => {}); if rendering throws after detailId is set, the page stays blank forever (guard short-circuits). load() has no pending guard → overlapping polls. hsAuto (loghandshake) checkbox is only synced at view creation — it drifts from the pager's own settings when changed elsewhere (pager UI / another browser).

Changes

1. Recon title cards — redesign (views.js + app.css)

New card anatomy (all five consistent):

  • Compact header: 12 px uppercase muted title (clickable where it links).
  • Primary value: 28 px bold.
  • Sub-line: 12 px muted context text.
  • Optional mini chart: fixed 96 px tall.

Cards (flex: 1 1 0; min-width: 170px; height: 190px — five fit one row even in a ~1000 px pane):

  1. Wireless Landscape — value: total networks (APs); sub: "N clients · M unassociated"; mini doughnut (APs/Clients/Unassociated).
  2. Channel Distribution — value: busiest channel "CH 6"; sub: "N of K APs on CH 6 · channels seen"; mini bar chart of channel counts.
  3. Encryption Landscape — value: top encryption bucket; sub: "N of K APs"; mini doughnut of buckets.
  4. Handshakes — value: handshake count (links to #/recon/handshakes); sub: "captured"; auto-collect toggle (restyled).
  5. Previous Scans — value: scan count; sub: latest scan start time; compact select row + single row of icon buttons.

Empty states get copy that matches the card ("No landscape data yet — run a scan").

2. Channel map hover (chart.js + views.js + app.css)

  • MiniChart.channelMap records per-lobe geometry on the canvas: canvas.__reconLobes = [{ ap, cx, half, topPx, basePx }] (CSS px).
  • views.js attaches mousemove/mouseleave/click to the map canvas:
    • hit test: t=(mx-cx)/half, lift = 0.5+0.5·cos(πt), hovered if my >= basePx - lift·peakPx - 4 and my <= basePx + 8;
    • tooltip div (absolute, inside .recon-map-box) lists every network under the cursor: SSID/MAC/channel/freq/signal/encryption/vendor;
    • click pins the tooltip until the next move or click;
    • mouseleave hides it.
  • Lobe geometry regenerates on every renderChannelMap() (redraw), so stale geometry is impossible.

3. Focus sidebar: Actions validation + Send to PineAP (views.js)

  • Existing buttons stay; toast feedback improved (already verified working at API level; re-verified end-to-end in browser).
  • New primary button "Send to PineAP — Twin this network":
    • encryption bucket Open → navigate #/pineap/open with prefill {ssid, hidden, channel, bssid};
    • anything else → navigate #/pineap/evilwpa with prefill {ssid, hidden, channel, enctype} where enctype maps from recon encryption: SAE→sae, OWE→owe, WPA3-only→sae, else psk2;
    • Enterprise networks still go to Evil WPA (psk2) — noted in the prefill banner.
  • Prefill mechanism: window.PineAPPrefill = { set, consume } (module singleton in views.js); consume() clears after use so a stale prefill never leaks into a manually opened form.
  • attackLauncher() consumes the prefill when building the form (SSID, hidden, channel via chanSelect, enctype, BSSID) and renders a muted banner: "Prefilled from Recon — verify, set the passphrase, then Deploy." Deploy is never triggered automatically (no attacks without an explicit user action).

4. Robustness / sync hardening (views.js)

  • loadDetail(): on fetch failure keep detailId unset so the poll retries; surface "Scan data unavailable — retrying…" in the scan status line; wrap the render body so one chart's exception cannot blank the table (each chart draw also wrapped individually).
  • drawCharts(): wrap each chart section in try/catch.
  • load(): loadPending guard against overlapping polls; surface scan-list errors in the status line.
  • hsAuto re-syncs from /api/pineap/get_config on the 30 s slow poll (stays in sync with the pager's own UI/settings changes).
  • Version bumps in index.html for app.css, chart.js, views.js.

5. Verification

  • Backend unchanged → existing tests/ still pass (run the suite).
  • Deploy via scripts/deploy.sh --password (device: 172.16.52.1).
  • End-to-end in browser (device UI):
    • login; recon page: cards populated with scan data; no blank-page state;
    • click an AP row → focus sidebar → each Action button verified by reading back state (get_config) and API responses;
    • Send to PineAP → form pre-filled on the right tab (open vs WPA target);
    • channel map click → tooltip shows networks under cursor;
    • reboot-resilience spot check via service restart (pagerwebui restart).