From 922395d2b4cca7ff504475c2fac8a303e3a995ae Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Mon, 17 Aug 2026 13:01:42 -0400 Subject: [PATCH] Design the Panama Control Center --- ...2026-08-17-panama-control-center-design.md | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-17-panama-control-center-design.md diff --git a/docs/superpowers/specs/2026-08-17-panama-control-center-design.md b/docs/superpowers/specs/2026-08-17-panama-control-center-design.md new file mode 100644 index 0000000..582b7f3 --- /dev/null +++ b/docs/superpowers/specs/2026-08-17-panama-control-center-design.md @@ -0,0 +1,269 @@ +# Panama Control Center design + +## Summary + +Panama will expand the existing top-right Quick Settings popover into a complete +Control Center. Wi-Fi, Bluetooth, Focus, Caffeine, Night Light, audio, account, +settings, and power remain in place. Home Assistant favourites and lightweight +iPhone continuity join that same surface instead of creating separate windows +or permanent bar widgets. + +The approved visual direction is **A ยท Continuity Card**, integrated into the +Control Center. The panel keeps Panama's Prism glass and Tokyo Night Moon +language, grows to 430 logical pixels, and begins two pixels below the bar. Home +is a first-class control section; KDE Connect is deliberately smaller because +the paired iPhone exposes only a useful subset of KDE Connect. + +## Goals + +- Make the top-right popover feel like a complete desktop Control Center rather + than a small collection of connectivity toggles. +- Toggle the existing Home Assistant favourites without opening a browser. +- Preserve the user's seven enabled Home Assistant lights and use Home + Assistant friendly names and live states. +- Show the paired iPhone's real reachability and expose Send File, Send + Clipboard, and Ring actions. +- Represent phone transfers in Ongoing while active and as quiet recent + exchanges after completion. +- Keep credentials, Home Assistant responses, clipboard contents, and local + file paths out of Git and shell logs. +- Leave BlueBubbles as the messaging implementation rather than manufacturing + an inferior KDE Connect messaging surface. + +## Non-goals + +- Panama will not add SMS or iMessage controls. BlueBubbles already owns that + experience. +- Panama will not show an iPhone battery percentage until the device reports a + real value. The current KDE Connect battery interface returns `-1`. +- Panama will not expose remote input, device locking, remote commands, or phone + notifications when the paired iPhone does not advertise those capabilities. +- Panama will not become a full Home Assistant dashboard or entity editor. +- Panama will not store a Home Assistant token in tracked files, QML, fixtures, + process arguments, or logs. +- The first release does not pair or unpair KDE Connect devices inside Panama. + KDE Connect's own pairing UI remains the safe handoff. + +## Verified system state + +KDE Connect currently reports one paired and reachable device: `Gib's iPhone`. +Its live D-Bus object exposes clipboard, find-my-phone, ping, share, and battery +plugins. The battery plugin has no value, and the remote-input object exposes no +callable control methods. Panama must derive action visibility from advertised +capabilities rather than from generic KDE Connect marketing claims. + +The existing GNOME Home Assistant extension points to the user's Home Assistant +instance and has seven enabled light entities: all lights, kitchen lights, two +individual lights, bedroom, living room, and hall. It stores the long-lived +access token in Secret Service under `token_string=user_token`; it does not keep +the token in dconf. + +## Interaction model + +Clicking the existing right-side status cluster or pressing `Super+S` opens the +same Control Center. No new global shortcut is added. + +The resting panel is ordered by frequency and scope: + +1. Wi-Fi, Bluetooth, Caffeine, and Night Light toggle grid. +2. Focus and audio controls. +3. Home favourites. +4. Phone continuity. +5. User, Panama Settings, and power footer. + +The panel starts two logical pixels beneath the bar instead of using the current +double bar-gap offset. It stays right-aligned and retains outside-click and +Escape dismissal. Its content scrolls when an expanded device list exceeds the +usable monitor height; the layer-shell window itself never extends beyond the +work area. + +Only one detail section is expanded at a time. Wi-Fi, Bluetooth, sink, source, +Home, and Phone all participate in the existing `expandedSection` state so the +panel remains predictable. + +## Home section + +The resting Home card shows the first four enabled favourites in their saved +order. Each tile contains a domain-appropriate glyph, a friendly name, and a +plain state such as `On`, `Off`, or `Unavailable`. Enabled lights use a quiet +amber light treatment; Panama blue remains reserved for desktop selection and +focus. + +Clicking a toggleable tile calls Home Assistant and temporarily disables only +that tile. The state is refreshed from Home Assistant after the request rather +than being permanently changed optimistically. Success is reflected visually +without a toast or Signal Glass event. A failure restores the last known state +and displays a concise inline error inside the Home section. + +The trailing `N accessories` action expands a full list of configured +favourites inside the Control Center. No unconfigured Home Assistant entity is +shown. An `Open Home Assistant` action launches the configured base URL. + +If Home Assistant is unreachable, Panama retains the last good states for the +current shell session, marks them stale, and offers Retry. If no URL or token is +available, the section becomes one setup row rather than disappearing or +breaking the rest of Control Center. + +## Phone section + +The Phone card follows approved mock A at a smaller scale: + +- device silhouette, friendly name, and `Nearby on Wi-Fi` or `Not nearby`; +- Send File, Send Clipboard, and Ring as compact icon actions; +- one recent transfer row only when a real exchange occurred in the current + shell session; and +- a Device Settings handoff to Panama Settings connectivity controls. + +The bar adds a small phone glyph only while at least one paired phone is +reachable. It is status, not a second click target; the existing status cluster +continues to open Control Center. + +Send File opens a graphical file chooser. Confirming a file starts a KDE +Connect share process. Canceling the chooser has no side effect and produces no +event. The action accepts regular local files only; URLs and directories are +not inferred from arbitrary text. + +Send Clipboard tells KDE Connect to send the current clipboard through its own +clipboard plugin. Panama never reads, stores, previews, or logs clipboard text +for this action. Ring invokes find-my-phone only when that plugin is advertised. + +Phone actions are disabled while the device is unreachable. Connection and +disconnection remain ambient Status Events under the existing global curation +policy. A completed transfer may appear as a quiet recent row and an important +Signal Glass event; canceling or failing a transfer produces a concise warning. +Ring and clipboard success do not produce global events. + +## Architecture + +### KDE Connect service + +Add `services/KdeConnect.qml` as the shell-facing state owner. It exposes the +paired devices, preferred phone, reachability, advertised actions, current +transfer, and recent exchange. It supervises small allow-listed helper +commands; UI components never assemble shell command strings. + +Add `scripts/panama-kdeconnect` with a JSON contract: + +- `status` reports paired devices, reachability, type, and supported actions; +- `send-file DEVICE FILE` validates a regular file and shares it; +- `send-clipboard DEVICE` invokes the KDE Connect clipboard plugin; and +- `ring DEVICE` invokes find-my-phone. + +The helper uses `kdeconnect-cli` and the session D-Bus service. It emits no +certificate fingerprints, IP addresses, clipboard contents, or file contents. +Status refreshes when Control Center opens and on a low-frequency fallback +timer. The existing `DeviceEvents.qml` reads this service instead of launching +its own independent KDE Connect probe. + +Transfer state is contributed to `Ongoing.qml`. A running share shows filename, +device name, and a Cancel action when the child process can still be stopped. +No fake percentage is shown because KDE Connect's public CLI does not provide +reliable byte progress for this device. + +### Home Assistant bridge + +Add `scripts/panama-home-assistant`, a small local bridge with four commands: + +- `probe` reports configuration and API availability without entity details; +- `snapshot` returns only configured favourites and normalized state; +- `toggle ENTITY_ID` calls the Home Assistant service for an allow-listed + configured entity; and +- `open` launches the configured Home Assistant URL. + +Configuration resolution is intentionally layered: + +1. `PANAMA_HOME_ASSISTANT_URL`, `PANAMA_HOME_ASSISTANT_TOKEN`, and optional + `PANAMA_HOME_ASSISTANT_ENTITIES` from the gitignored `config/bash/env`; +2. the existing GNOME extension's `hass-url` and enabled-entity dconf values; +3. the existing Secret Service item with `token_string=user_token`. + +This preserves the current working setup without copying a token. Explicit +Panama environment variables can replace the compatibility fallbacks later. +The bridge sends the token only in the Home Assistant Authorization header and +redacts request failures before returning them to QML. + +Add `services/HomeAssistant.qml` to own normalized entities, loading and stale +state, per-entity busy state, and inline errors. It refreshes when Control +Center opens, after a toggle, and at a bounded interval while the panel is +visible. It does not continuously poll while the panel is closed. + +### Control Center components + +Keep `QuickSettings.qml` as the only layer-shell window and +`QuickSettingsPanel.qml` as composition. Add focused presentation components +under `modules/quicksettings/`: + +- `ControlSectionHeader.qml` for consistent quiet labels and trailing actions; +- `HomeControls.qml` and `HomeTile.qml`; +- `PhoneControls.qml`; and +- `RecentExchange.qml`. + +The panel width becomes a named Control Center geometry token rather than +changing the shared popover width used by unrelated surfaces. Existing Wi-Fi, +Bluetooth, audio, Focus, Caffeine, Night Light, footer, keyboard focus, and +outside-click behavior remain intact. + +## Security and privacy + +- The existing Secret Service token remains the preferred compatibility + source; no migration requires printing or copying it. +- Environment configuration is allowed only in the already gitignored + `config/bash/env` file. +- Helpers never echo tokens, Authorization headers, clipboard text, entity + response bodies, IP addresses, or full local file paths to logs. +- Home Assistant commands accept only entity IDs present in the configured + favourites snapshot. +- KDE Connect device IDs and file paths are passed as separate process + arguments, never interpolated into a shell command. +- A user click is required for every Home toggle, phone ring, clipboard send, + file chooser, and file transfer. + +## Failure behavior + +- KDE Connect absent: Phone shows `KDE Connect unavailable` with a Settings + handoff; Control Center remains usable. +- Paired phone offline: identity remains visible, actions disable, and no + repeated disconnect toast is emitted. +- File chooser canceled: no error and no history entry. +- Transfer failed: Ongoing clears, recent history does not claim success, and a + concise warning is shown. +- Home Assistant unavailable: last good state remains visibly stale and Retry + is offered. +- Invalid or expired token: Home shows `Authentication required` without + exposing the response body. +- One malformed entity is omitted without suppressing healthy favourites. + +## Testing and verification + +Focused automated coverage will prove: + +1. KDE Connect status normalization and capability filtering through synthetic + command fixtures. +2. File validation, allow-listed device actions, canceled chooser behavior, and + transfer lifecycle without sending a real file. +3. Home Assistant configuration precedence, response normalization, + allow-listed toggles, authentication redaction, stale-state retention, and + malformed-entity isolation against a local fake API. +4. Control Center structure, exclusive detail expansion, Home/Phone empty and + failure states, Ongoing contribution, status glyph visibility, and the + two-pixel top attachment through contract tests. +5. Fresh Quickshell construction with no QML errors or warnings and valid + Hyprland configuration. +6. A live read-only smoke test that reports only the paired/reachable phone + count, supported-action names, Home Assistant availability, and configured + entity count. +7. Visual captures of resting, expanded Home, phone offline, transfer active, + and Home Assistant failure states. + +A real Ring, clipboard send, Home light toggle, or file transfer is not part of +automated verification because each changes an external device. Those final +checks require an explicit user action from the finished Control Center. + +## Documentation updates + +- Update `config/dot/hypr/DESKTOP-PARITY.md` to mark Home Assistant favourites + and KDE Connect phone integration live. +- Document Control Center behavior, credential resolution, and iOS limitations + in `config/dot/hypr/README.md`. +- Keep BlueBubbles listed as the messaging application and KDE Connect as the + continuity/file-transfer layer.