diff options
| -rw-r--r-- | docs/superpowers/specs/2026-10-13-performance-panel-design.md | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-10-13-performance-panel-design.md b/docs/superpowers/specs/2026-10-13-performance-panel-design.md new file mode 100644 index 0000000..a6818c3 --- /dev/null +++ b/docs/superpowers/specs/2026-10-13-performance-panel-design.md @@ -0,0 +1,198 @@ +# Performance panel design + +A performance panel that drops from the island's status pill: CPU, RAM, +temperatures, GPU, disks and network, ported from the four conky widgets that +show them today (`system`, `gpu`, `disks`, `network` in conky-theme-udt). An +icon in the status pill opens it and turns yellow or red when something needs +attention, including the public IP drifting from the address the user's VPN +depends on. This is sub-project 4 of the roadmap in +`2026-10-07-island-core-design.md`. + +Once the panel is verified, the four conky widgets are removed: two dashboards +drawing the same numbers would drift apart. + +## Scope + +In v1: + +- A 3-column card grid: CPU (per-core bars, RAM, temperatures), GPU, network + (rate graph, LAN and public IP, pin button), disks. +- A status-pill icon tinted by the worst severity of temperatures, RAM, disks + and the public IP check. +- A pinned public IP, set from the panel, and a red warning on mismatch. +- Removal of the four conky widgets and their samplers. + +Not in v1: a drawer page (sub-project 6 reuses `Perf.qml`); history beyond the +network graph; process lists; GPU utilisation (the xe driver exposes none, see +below); auto-discovered mounts; a keybind. + +## Where it lives + + desktop/island/perf/ + Perf.js pure parsing and maths, node-tested + test_perf.js node test for Perf.js + Perf.qml singleton: FileViews, Processes, Timers; exposes state + PerfPanel.qml the card grid + desktop/island/StatusPill.qml + the performance icon + desktop/island/Island.qml + panel "perf" + desktop/Prefs.qml + pinnedIp + +## Data sources + +All of these are what conky reads today; nothing new is measured. + +| Figure | Source | When | +| --- | --- | --- | +| CPU load, aggregate | `/proc/stat` deltas | always, 2s | +| CPU load, per core | `/proc/stat` `cpuN` lines | panel open, 2s | +| RAM | `/proc/meminfo`, `MemTotal - MemAvailable` | always, 2s | +| temperatures | hwmon `temp*_input` | always, 2s | +| GPU fan | hwmon `xe` `fan1_input` | panel open, 2s | +| VRAM used | `/proc/[0-9]*/fdinfo/*`, `drm-resident-vram0` summed per `drm-client-id` for `drm-driver: xe` | panel open, 2s | +| disks | `timeout 5 /usr/bin/df -P -B1 <mounts>` | always, 60s | +| network rate | `/sys/class/net/br0/statistics/{rx,tx}_bytes` | panel open, 1s | +| LAN address | `ip -4 addr show br0`, memoized on first success | panel open | +| public IP | `curl -fsS --max-time 10 https://ipinfo.io/ip` | 30 min, and on panel open if stale | + +Reads of `/proc` and `/sys` are `FileView`s reloaded by a `Timer`, no +subprocess. `df`, the fdinfo scan, `ip` and `curl` are `Process` calls; each +`Process` reused for a sequence sets `running = false` before each start. + +`Perf.detailed` is true while the panel is open and gates the rows marked +"panel open", the way `Virsh.sampling` gates the VM stats. The always rows +exist so the icon can warn while the panel is closed. + +### Sensors + +hwmon chips are found by their `name` file once at start, because `hwmonN` +numbering changes across boots. The table is conky's: + +| Chip | File | Label | Warn | Crit | +| --- | --- | --- | --- | --- | +| `k10temp` | `temp1_input` | CPU | 75 | 90 | +| `k10temp` | `temp3_input` | CCD1 | 75 | 90 | +| `nvme` | `temp1_input` | NVME | 60 | 70 | +| `gigabyte_wmi` | `temp2_input` | BOARD | 60 | 70 | +| `gigabyte_wmi` | `temp3_input` | BOARD | 60 | 70 | +| `xe` | `temp2_input` | GPU | 75 | 85 | +| `xe` | `temp3_input` | VRAM | 80 | 90 | + +The GPU has no utilisation figure: xe exposes no `gpu_busy_percent` and +`intel_gpu_top` refuses the device. The integrated AMD GPU is not shown. + +### Host constants + +Kept in `Perf.qml`, each marked `ponytail:` with its ceiling, as conky had +them: the mount list (`/`, `/home`, `/data`, `/mnt/nfs/Library`, +`/mnt/nfs/shared`, one NFS mount per server), the interface `br0` (a bridge, so +VM-to-host traffic shows in the graph), and 12 GiB of VRAM (xe puts no +capacity in sysfs). Mount icons are conky's, re-checked in Inconsolata Nerd +Font: Slackware for `/`, house for `/home`, hard disk for `/data`, network +share for NFS, where the label is the server address read from the mount's +device field, so no address is committed. + +## Public IP + +The fetch writes conky's cache format to `~/.cache/udt/pubip.txt` +(`ip <addr>` and `fetched <epoch>` lines, atomic), so the existing file is +reused. A response that is not an IPv4 address is discarded and the cache left +alone. A value older than 4 hours is shown as a dash. + +A pin button beside the address stores the current address as +`perf.pinnedIp` in `desktop.json` through `Prefs`, which gains a `pinnedIp` +property and `setPinnedIp()`, written only on change like `setControls()`. The +address lives in the user's state file, never in the repo. Pressing the pin +again with the same address does nothing; a different address replaces it. + +The check: + +| State | Result | +| --- | --- | +| no pin | ok | +| fresh fetch equals pin | ok | +| fresh fetch differs from pin | crit | +| fetch failed, or value stale | ok, address shown as a dash or with its age | + +A failed fetch is never a mismatch: a dropped connection must not raise the +alarm. + +## The icon + +`utilities-system-monitor` through `Quickshell.iconPath`, placed before the +controls icon in the status pill. It is tinted by the worst severity: + +| Input | Warn | Crit | +| --- | --- | --- | +| each temperature | its warn | its crit | +| RAM | 85% | 95% | +| each disk | 90% | 95% | +| public IP | | mismatch with the pin | + +Neutral when ok, `Theme.yellow` on warn, `Theme.red` on crit. Not the accent, +which udt can snap onto a state colour. CPU load is left out: spikes are +normal and would flicker the icon, and sustained load shows up as heat. + +Left-click toggles the panel. No right-click until a drawer page exists. The +status pill expands for `perf` as it does for `sound` and `controls`. + +## The panel + +A grid of three columns, the same palette and type as the other island panels: + +- **CPU**, spanning two rows: aggregate load as the headline, the CPU model, + one bar per core, RAM used and total with a bar, then the temperature rows + for CPU, CCD1, NVME and both BOARD sensors. +- **GPU**: package temperature as the headline, VRAM used and total with a + bar, VRAM temperature, fan rpm. +- **Network**: download and upload as two lines over a 60-point history, + current rates, LAN address, public IP with its pin button. The public IP is + red on mismatch. +- **Disks**, spanning two columns: one row per mount with icon, label, + percentage and bar. + +Colours follow conky's bands so the numbers read as before: load, per-core +bars, RAM and disk bars at 25 and 75; temperatures at each sensor's own warn +and crit. Escape closes the panel, as for the other panels. + +## Errors + +- A missing chip, file or interface shows a dash and counts as ok for the + icon. +- `df` under `timeout 5`: a hung NFS server keeps the last value, or a dash + before there is one. The island never blocks. +- VRAM counts only processes readable by the user, so a system service's GPU + use is missing. Noted as a ceiling, as in conky. +- The network history is cleared when the panel closes; it is not sampled + while closed. + +## Conky removal + +In conky-theme-udt, after the panel is verified on screen beside the conky +widgets, in that repo's own commits: + +- Delete `widgets/system.lua`, `gpu.lua`, `disks.lua`, `network.lua` and their + entries in the `dashboard.lua` layout. +- Drop `disks-sample.sh` and `pubip-sample.sh` from `conky.text` and delete + them; the island owns the public IP fetch, and two fetchers would double the + outside traffic. +- Remove readers in `lib/data.lua` used only by those widgets, each checked + against its remaining callers. +- Leave the other widgets where they are. Rearranging the freed space is the + user's call, on screen. + +## Testing + +- `node desktop/island/perf/test_perf.js`: `/proc/stat` aggregate and per-core + deltas, meminfo, `df -P -B1` parsing, the fdinfo sum with duplicate client + ids, the network rate across a counter reset, severity across every input, + the IP check (no pin, match, mismatch, failed, stale), and the cache file + format both ways. +- A smoke run of `qs -p desktop` reading the log, after the user restarts the + desktop component, since the new directory needs a reindex. +- On screen, by the user: the panel beside the conky widgets before they are + removed, the icon colours, the pin button. + +## AGENTS.md + +Add any gotcha found while building. The island's README gains the panel and +the icon thresholds. |
