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
This commit is contained in:
2026-08-18 23:49:49 -05:00
parent 84ce19b2b3
commit d7ef0624f4
5 changed files with 537 additions and 148 deletions
@@ -301,23 +301,39 @@ html.dark .sel { background: #424242; border-color: #545454; color: #fff; }
html.dark .muted { color: #bdbdbd; }
/* ---- Recon (Mark VII parity) ---- */
.recon-title-card-container { display: flex; width: 100%; flex-wrap: wrap; justify-content: space-between; gap: 10px; margin: 8px 0 16px; }
.recon-title-card { flex: 1 1 220px; min-width: 220px; margin-bottom: 1em; }
.recon-card { background: var(--surface); border-radius: 2px; box-shadow: var(--shadow); height: 200px; padding: 12px 16px; display: flex; flex-direction: column; }
.recon-title-card-title { font-size: 20px; margin-bottom: 15px; display: flex; align-items: center; color: var(--text); }
.recon-title-card-container { display: flex; width: 100%; flex-wrap: wrap; gap: 10px; margin: 8px 0 16px; }
.recon-card {
flex: 1 1 0; min-width: 170px; height: 190px;
background: var(--surface); border-radius: 2px; box-shadow: var(--shadow);
padding: 12px 14px; display: flex; flex-direction: column;
}
.recon-card-title {
font-size: 12px; text-transform: uppercase; letter-spacing: .05em;
color: var(--muted); margin-bottom: 6px; display: flex; align-items: center;
}
.recon-card-title-link { color: inherit; text-decoration: none; }
.recon-card-title-link:visited { color: inherit; }
.recon-card-title-link:hover { text-decoration: underline; }
.recon-title-card-content { display: flex; justify-content: center; align-items: center; height: 70%; }
.recon-chart-box { width: 100%; height: 150px; position: relative; }
.recon-chart-box canvas { width: 100%; height: 100%; }
.recon-no-data { font-style: italic; color: #787878; display: flex; justify-content: center; padding: 12px; }
.recon-hs-col { display: flex; flex-direction: column; justify-content: center; align-items: center; }
.recon-card-title-link:hover { color: var(--primary); text-decoration: underline; }
.recon-card-body { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.recon-card-value {
font-size: 26px; font-weight: 700; line-height: 1.15; color: var(--text);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.recon-card-sub {
font-size: 12px; color: var(--muted); margin: 1px 0 6px; min-height: 16px;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.recon-chart-box { position: relative; flex: 1; min-height: 0; margin-top: auto; }
.recon-chart-box canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.recon-no-data { font-style: italic; color: #787878; display: flex; justify-content: center; align-items: center; padding: 8px; text-align: center; }
.recon-hs-count { font-size: 32px; font-weight: 700; line-height: 1.1; }
.recon-hs-label { color: grey; margin: 2px 0 10px; }
.recon-toggle { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text); margin: 0; cursor: pointer; }
.recon-ps-row { display: flex; align-items: center; width: 100%; gap: 4px; }
.recon-ps-row .sel { width: 100%; }
.recon-hs-label { color: var(--muted); font-size: 12px; margin: 1px 0 8px; }
.recon-toggle { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text); margin: 0; cursor: pointer; }
.recon-ps-row { display: flex; align-items: center; width: 100%; gap: 4px; margin-top: 2px; }
.recon-ps-row .sel { width: 100%; font-size: 12px; padding: 4px 6px; }
.recon-ps-actions { display: flex; align-items: center; gap: 2px; margin-top: 4px; }
.recon-ps-actions .icon-btn { width: 28px; height: 28px; }
.recon-ps-actions .icon-btn svg { width: 18px; height: 18px; }
.icon-btn { background: transparent; color: var(--muted); border: 0; border-radius: 50%; width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; padding: 0; }
.icon-btn:hover { background: var(--surface-alt); color: var(--text); }
.icon-btn:disabled { opacity: .38; cursor: default; }
@@ -362,6 +378,8 @@ html.dark .recon-row-compare td { background: rgba(25, 118, 210, .18); }
.recon-focus-body { margin-top: 18px; }
.recon-focus-body-title { font-size: 16px; margin-bottom: 8px; color: var(--text); }
.recon-focus-action-button { width: 100%; margin-bottom: 5px; }
.recon-focus-twin { background: var(--ok); }
.recon-focus-twin:hover { background: #689f38; }
.recon-focus-detail { display: flex; justify-content: space-between; gap: 10px; padding: 3px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.recon-focus-detail-label { color: var(--muted); flex: none; }
.recon-sort-arrow { color: var(--muted); font-size: 11px; }
@@ -410,6 +428,16 @@ html.dark .recon-pill.on { background: #1b3a23; color: #81c784; }
.recon-map-box { position: relative; }
.recon-map-box canvas { display: block; }
.recon-map-box .recon-no-data { min-height: 60px; }
.recon-map-tip {
position: absolute; z-index: 20; min-width: 220px; max-width: 260px;
background: var(--surface); border: 1px solid var(--border); border-radius: 3px;
box-shadow: var(--shadow); padding: 8px 10px; pointer-events: none; font-size: 12px;
color: var(--text);
}
.recon-map-tip-row { padding: 3px 0; border-bottom: 1px solid var(--border); }
.recon-map-tip-row:last-child { border-bottom: 0; }
.recon-map-tip-ssid { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.recon-map-tip-meta { color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ---- Reports view ---- */
.wigle-warn { color: #ef6c00; font-size: 12px; }