aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/superpowers/specs/2026-10-13-performance-panel-design.md198
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.