aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-14 13:38:07 +0200
committerDanilo M. <danix@danix.xyz>2026-09-14 13:38:07 +0200
commit3d840257c0138751478ddd685ae7afbcb3644608 (patch)
treebd0e40cbe856e7108a138c2bfb98363f474eb35c /docs
parent3dee371ed7e460d68018839e0ff17b436e2c6b52 (diff)
downloadquickshell-3d840257c0138751478ddd685ae7afbcb3644608.tar.gz
quickshell-3d840257c0138751478ddd685ae7afbcb3644608.zip
docs: design for network and bluetooth modules
Two drawer modules covering wired, wifi and bluetooth, using the native Quickshell.Networking and Quickshell.Bluetooth backends rather than nmcli or bluetoothctl. The one exception is pairing: quickshell ships no BlueZ agent and no generic D-Bus module, so a device needing passkey confirmation is paired through bluetoothctl, whose own agent handles it. The wifi module became Network so a connected eth0 does not mask the wifi state; the tile shows both links when both are up because the API exposes no route metric. Replaces waybar's network and bluetooth indicators, per the drawer being the single place they are managed.
Diffstat (limited to 'docs')
-rw-r--r--docs/superpowers/specs/2026-09-14-network-bluetooth-design.md160
1 files changed, 160 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-09-14-network-bluetooth-design.md b/docs/superpowers/specs/2026-09-14-network-bluetooth-design.md
new file mode 100644
index 0000000..53c79ac
--- /dev/null
+++ b/docs/superpowers/specs/2026-09-14-network-bluetooth-design.md
@@ -0,0 +1,160 @@
+# Network and Bluetooth modules for the desktop shell
+
+Two new modules under the `desktop/` drawer: `network`, covering wired and
+wifi, and `bluetooth`. They follow the module contract the desktop shell
+already defines, and they use the native Quickshell backends rather than
+shelling out to `nmcli` or `bluetoothctl`, with one deliberate exception for
+Bluetooth pairing.
+
+This is the first of the projects the desktop shell design named but left
+unbuilt. Do not disturb, breaktimer and kdeconnect remain later projects.
+
+## Why
+
+The drawer is where a desktop's adjustable things are visible together. Wifi
+and Bluetooth are the two most-used of those and are currently not in it. The
+design also chose to replace waybar's own network and Bluetooth indicators, so
+the drawer becomes the single place both are managed.
+
+## Scope
+
+In:
+
+- `network`: wired devices and wifi, radio switch, scan, join a network with a
+ password, join a hidden network, connect, disconnect, forget.
+- `bluetooth`: adapter power, discoverable, scan, pair, connect, disconnect,
+ forget, trust, device battery where reported.
+- Removing waybar's network and Bluetooth indicators from the live config.
+
+Out, and noted as ceilings:
+
+- Enterprise / 802.1x wifi. The profile model is far heavier than the drawer
+ wants, and nothing here uses it.
+- Joining a hidden network, if `NMSettings` turns out not to be constructible
+ in QML. Nothing else can create a profile from scratch, so this one is
+ conditional on a feasibility check during implementation. Dropped with a
+ note if it does not hold.
+
+## Backends
+
+Quickshell 0.3.1 ships the two APIs built in; verified against the 0.3.1
+reference and against the installed binary:
+
+- `Quickshell.Networking` (NetworkManager D-Bus). `Networking` has
+ `connectivity`, `devices`, `wifiEnabled`, `wifiHardwareEnabled`.
+ `NetworkDevice` has `autoconnect`, `name`, `address`, `networks`, `state`,
+ `type`, `connected`, `disconnect()`. `WifiDevice` adds `scannerEnabled` and
+ `mode`. `Network` has `known`, `nmSettings`, `state`, `connected`, `device`,
+ `name`, `connect()`, `connectWithSettings()`, `disconnect()`, `forget()` and
+ the `connectionFailed` signal. `WifiNetwork` adds `security`,
+ `signalStrength` and `connectWithPsk(psk)`.
+- `Quickshell.Bluetooth` (BlueZ D-Bus). `Bluetooth` has `adapters`,
+ `defaultAdapter`, `devices`. `BluetoothAdapter` has `enabled`,
+ `discoverable`, `discoverableTimeout`, `pairable`, `pairableTimeout`,
+ `discovering`, `devices`, `name`, `state`, `adapterId`. `BluetoothDevice`
+ has `paired`, `bonded`, `connected`, `trusted`, `blocked`, `battery`,
+ `batteryAvailable`, `icon`, `address`, `name`, `deviceName`, `state`,
+ `adapter`, and `pair()`, `cancelPair()`, `connect()`, `disconnect()`,
+ `forget()`.
+
+Because Quickshell has no generic D-Bus module and no BlueZ pairing agent,
+`pair()` cannot handle a device that requires a passkey or PIN confirmation.
+That one action shells out to `bluetoothctl`, whose own agent does.
+
+## Module shape
+
+Both modules are `alwaysActive: true` and declare it truthfully. There is no
+poll to gate: the backends are push, and the tile needs live state the moment
+the drawer opens. Unlike sound and vm, neither needs a `Service.qml`: there is
+no polling loop to own, so the module root references the singleton directly,
+which is also what instantiates it at shell start.
+
+```
+modules/network/ NetworkModule.qml NetworkTile.qml NetworkPage.qml NetworkRow.qml README.md
+modules/bluetooth/ BluetoothModule.qml BluetoothTile.qml BluetoothPage.qml BluetoothRow.qml Pairing.qml README.md
+```
+
+`shell.qml` gains the two imports and two registry entries. The drawer's
+`open(<name>)` IPC reaches the pages with no further wiring; the module names
+are `network` and `bluetooth`.
+
+Grid order becomes `Sound, Network, Bluetooth, Mail, Appearance, Machines`,
+six tiles, two rows of three in the 600px panel.
+
+## The network page
+
+**Wired.** The managed `WiredDevice`s only, filtered by `type` so `docker0`,
+`virbr0` and `veth*` never appear. Each is a row: interface, connected state,
+and disconnect.
+
+**Wifi.** A radio switch on `Networking.wifiEnabled`. Off hides the list and
+shows `Wi-Fi off`. On shows a scan toggle on `device.scannerEnabled` and a
+list ordered connected first, then known, then the rest by `signalStrength`.
+A lock glyph marks a secured network. Row click connects an open or known
+network directly; a secured unknown network reveals an inline password field
+on the row and submits through `connectWithPsk()`. A connected row offers
+Disconnect, a known row offers Forget.
+
+**Hidden network.** A `Join hidden network` row taking SSID, security and
+password, subject to the ceiling above.
+
+## The Bluetooth page
+
+Adapter power switch, then when on a `Make visible` toggle (`discoverable`)
+and a scan toggle (`discovering`). Connected devices come first, each with
+icon, name and battery when `batteryAvailable`, and click disconnects. Paired
+devices follow: connect, disconnect, forget, and a trust toggle. Devices found
+while scanning offer Pair, which starts `Pairing.qml`.
+
+## Tiles
+
+**Network.** The icon swaps wired, wifi, off. With one link up the state line
+names it (`eth0`, or the SSID). With both wired and wifi up the content is two
+compact lines, one each, because the API exposes no route metric and the tile
+will not guess which link carries traffic. `active` while any link is up.
+
+**Bluetooth.** The icon swaps on and off. The state line names the connected
+device, or a count when several, `No devices` when powered but idle, `Off`
+when the adapter is down. `active` while anything is connected.
+
+## Failures
+
+An inline status line on the page beside the affected row: a rejected PSK
+arrives as `connectionFailed`, and forgetting or pairing can fail too. A
+failure that lands after the drawer has closed also fires `notify-send`,
+using the cached-`Process` pattern already in `VmModule.qml`. The error clears
+on the next action.
+
+## Files outside this repo
+
+The live waybar configuration loses its network and Bluetooth indicators,
+applied by the user. Nothing else outside the repo changes: both pages render
+inside the existing `quickshell-desktop` window, so there is no new namespace
+and no new blur rule, and no new IPC.
+
+## Verification
+
+No automated oracle reaches a D-Bus backend, so verification is visual, by the
+user, following the AGENTS.md measurement rules (the process is `qs`, a
+detached one does not survive a tool call, `pkill -f` kills the caller):
+
+- radio toggles for both wifi and the Bluetooth adapter
+- join an open network and a secured one, once with a wrong PSK and once right
+- forget a network
+- wired shows connected; with wired and wifi both up the tile shows two lines
+- Bluetooth power, discoverable, scan
+- pair a Just Works device, then forget and re-trust it
+
+If `Pairing.qml` grows a parser for `bluetoothctl` output, that parser leaves
+one runnable `assert` check fed sample lines. `test-mail-notify.sh` is
+unaffected.
+
+## Traps carried forward
+
+- The PipeWire "initial value arrives before `ready`" pattern may apply to
+ these singletons too. Verify before trusting a first paint rather than
+ assuming a guard is needed.
+- QML's JS engine has no `String.matchAll`, so any `bluetoothctl` parsing uses
+ an `exec` loop.
+- `bluetoothctl` output parsing is brittle. Mark it with a `ponytail:` comment
+ naming the ceiling and the upgrade path.