Design the Panama Control Center
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user