# Design: Open AP tab — Mark VII "PineAP Settings" card Date: 2026-08-11 Status: Approved ## Problem The PineAP **Open AP** tab (`#/pineap/open`) renders as a flat list of nine plain checkboxes inside a single `.pineap-title-card`. The Mark VII PineAP page's settings card ("PineAP") uses a titled card with a subtitle, iOS-style slide toggles (`mat-slide-toggle`) grouped into sections, and a Save button. The Open AP tab should be restyled to that anatomy, with behavior switched from immediate-apply to a batched Mk7-style Save. ## Reference (Mark VII) The Mk7 "PineAP" settings card (from the firmware bundle): - 20px card title **"PineAP"** + muted subtitle **"Quickly set the general behavior of PineAP"** - The Passive/Active/Advanced button group (already on our Overview tab — not duplicated here) - Settings as `mat-slide-toggle` iOS switches - A small accent **save** button (`savePineAPSettings`) ## Design ### Layout A full-width `.pineap-card-settings` card containing: - **Title row** (`.pineap-card-title-flex`): title **"PineAP"**, muted subtitle **"Quickly set the general behavior of PineAP"**, and a **Save** button on the right (accent/primary `.btn`). - **Grouped slide-toggle settings** using the repo's existing `.switch` CSS (iOS-style slider; defined in `app.css` but currently unused): - **Karma**: Enable PineAP, Karma - **SSID Pool**: Capture SSIDs to Pool, Advertise AP Impersonation Pool - **Logging**: Log Handshakes, Log Partial Handshakes, Log PCAP, Log WiGLE, Log Recon - **Footer** (muted): `PineAP MAC: X Target MAC: Y` ### Behavior (Mk7-style Save, batched) - Flipping a switch **stages** the value locally and marks the field dirty — no API call is made until Save. - **Save** applies only the dirty fields, batched per backend route: - Logging + Capture SSIDs (set_config fields `loghandshake`, `logpartialhandshake`, `logpcap`, `logwigle`, `logrecon`, `autossidpool`) → one `POST /api/pineap/set_config` with the dirty fields. The backend merges over the current daemon config, so untouched settings keep their values. - `pineap_disabled` (Enable PineAP) → `POST /api/pineap/enable {enable}` if dirty. - `karma` → `POST /api/pineap/mimic {enable}` if dirty. - `advertise` → `POST /api/pineap/ssidpool/advertise {enable}` if dirty. - All dirty routes fire together (`Promise.allSettled`); toast "Settings saved" if all fulfilled, else "Some settings failed" (error). - Dirty-only apply preserves the unreadable live Karma state: the Pager daemon cannot report Karma, so leaving the switch untouched means Save does not change it. - `load()` runs once on entry and after a successful Save to populate the switches from the daemon (`get_config`, `hostapd`, `wifi/get_ap`). No polling on this tab (unchanged), so staged values are never clobbered. - If nothing is dirty, Save toasts "No changes". ### Mapping table (field → route, dirty-only) | Switch | Backend call | Dirty flag | |---|---|---| | Enable PineAP | `POST /api/pineap/enable {enable}` | `pineap_disabled` | | Karma | `POST /api/pineap/mimic {enable}` | `karma` | | Capture SSIDs to Pool | `POST /api/pineap/set_config {autossidpool}` | `autossidpool` | | Advertise AP Impersonation Pool | `POST /api/pineap/ssidpool/advertise {enable}` | `advertise` | | Log Handshakes | `POST /api/pineap/set_config {loghandshake}` | `loghandshake` | | Log Partial Handshakes | `POST /api/pineap/set_config {logpartialhandshake}` | `logpartialhandshake` | | Log PCAP | `POST /api/pineap/set_config {logpcap}` | `logpcap` | | Log WiGLE | `POST /api/pineap/set_config {logwigle}` | `logwigle` | | Log Recon | `POST /api/pineap/set_config {logrecon}` | `logrecon` | ## CSS additions (`app.css`) - `.pineap-card-subtitle` — muted, ~13px subtitle under the card title. - `.pineap-settings-section` — small muted group label (e.g. "Karma", "SSID Pool", "Logging"), 13px, with margin, used between the toggle groups. - Reuse the existing `.switch` slider CSS (no change to it). ## Files touched - `payload/user/general/pager-webui/www/css/app.css` — two new classes. - `payload/user/general/pager-webui/www/js/views.js` — rewrite `views.pineap_open` (layout + staged-save behavior). No backend changes. No test-module changes. ## Verification 1. JS delimiter balance check (Python checker; no node available). 2. All 13 backend unittest modules pass (unchanged). 3. Deploy via `scripts/deploy.ps1` (password supplied securely), restart `/etc/init.d/pagerwebui`. 4. On-device: Open AP tab shows the titled settings card with grouped slide switches and Save; flipping a switch then Save applies the values (verify via `get_config`/`hostapd` after Save); untouched toggles are not sent; user walks the tab visually.