Files
Panama/docs/superpowers/specs/2026-08-17-panama-control-center-design.md
T

270 lines
13 KiB
Markdown

# 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 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.