AGENTS.md: control centre, tray, bar hiding and reserved space; new ground rules and test notes
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
# AGENTS.md
|
||||
|
||||
Handoff for agents continuing work on the Magic Island shell (Hermes, Claude Code, Codex, a new session). Read README.md first for what the shell does and how to use it; this file is how it's built, how to work on it, and where things stand. Last updated 2026-10-10.
|
||||
Handoff for agents continuing work on the Magic Island shell (Hermes, Claude Code, Codex, a new session). Read README.md first for what the shell does and how to use it; this file is how it's built, how to work on it, and where things stand. Last updated 2026-10-13.
|
||||
|
||||
## Where things are
|
||||
|
||||
- **This repo:** `/root/amane-config` on the dev host; remote `git@server1:fabolous005/amane-config.git`.
|
||||
- **Amane library and docs:** `/root/amane` (`docs/src/*.md`, source in `src/`). The desktop has its own copy at `~/git/amane`.
|
||||
- **Hermes skill `amane`** (`~/.hermes/skills/software-development/amane/`): distilled Amane docs plus lessons. `references/recipe-media-island.md` and `references/remote-visual-testing.md` cover this project; `scripts/` holds copies of `tools/` (keep them in sync).
|
||||
- **Desktop:** `ssh -o BatchMode=yes fabian@192.168.2.169`. Hyprland 0.56.2 with a **Lua** config (`~/.config/hypr/*.lua`; API stubs in `/usr/share/hypr/stubs/hl.meta.lua`). Intel Xe iGPU (Raptor Lake). Four monitors: eDP-1 1920×1200 at 1920,620; HDMI-A-1 at 0,620; DP-2 at 3840,0; HDMI-A-4 at 5760,0. `~/.config/amane` on the desktop is a git checkout of this repo.
|
||||
- **Desktop:** `ssh -o BatchMode=yes fabian@192.168.2.169`. Hyprland 0.56.2 with a **Lua** config (`~/.config/hypr/*.lua`; API stubs in `/usr/share/hypr/stubs/hl.meta.lua`). Intel Xe iGPU (Raptor Lake). Monitors: eDP-1 1920×1200 at 1920,620; HDMI-A-1 1920×1080 at 0,620; DP-2 1920×1080 at 3840,0; HDMI-A-4 at 5760,0 only when connected (on 2026-10-13 three were connected, all 1920 wide). `~/.config/amane` on the desktop is a git checkout of this repo.
|
||||
- **Desktop daemons the control centre uses:** OpenRC services `iwd` (Wi-Fi), `bluetooth` (bluez), `openvpn.<name>` (VPN); doas rule `permit nopass fabian` for `rc-service`, `sh` and `cat`; `wl-copy`, `ip`, `lspci`. iwd and bluetoothd are often stopped; the shell then greys their tiles.
|
||||
|
||||
## Ground rules from the user
|
||||
|
||||
@@ -17,14 +18,24 @@ Handoff for agents continuing work on the Magic Island shell (Hermes, Claude Cod
|
||||
- **Desktop access is SSH only.** Don't probe the network, SSH keys or other hosts. If SSH fails, report it and wait (the user once had to restart sshd).
|
||||
- **Leave the desktop clean** after testing: fake players and null sinks stopped, shell running without the frame log (see "After testing").
|
||||
- **Edit files with the patch/write tools, not scripts that rewrite files.** The user installs missing tools themselves. Scripts take instance values (hosts, names) as arguments.
|
||||
- Hermes approval prompts are switched off; edits to AGENTS.md may still prompt (`security.protected_instruction_files`).
|
||||
- **D-Bus only for reading status.** Actions go through the CLIs: `iwctl` to join, scan and disconnect, `bluetoothctl` to pair and connect (the user had a D-Bus agent version reverted, `afe963d`). The one exception, chosen by the user: the shell serves `org.kde.StatusNotifierWatcher` and calls tray apps' dbusmenus for the tray. Ask before serving anything else on the bus.
|
||||
- **Never wake iwd by accident:** any D-Bus call to `net.connman.iwd` starts it (it then joins Wi-Fi and adds a route). Check `org.freedesktop.DBus.NameHasOwner` first; only talk to it while it has an owner.
|
||||
- **Root only through `doas -n` with fixed command lines** (`rc-service <svc> start|stop`, `cat /var/lib/iwd/<profile>.psk`). Strings from other programs (SSIDs, device names, D-Bus metadata) go to `Command` args, never into `amane::spawn`/`output` (`sh -c`). Passwords go to `wl-copy` on stdin, never as an argument; iwctl's `--passphrase` is visible in `ps` for about a second, which the user accepted.
|
||||
- Hermes approval prompts are switched off; edits to AGENTS.md may still prompt (`security.protected_instruction_files`), and the user approves AGENTS.md rewrites first.
|
||||
- Report honestly what was and wasn't checked on the real desktop.
|
||||
|
||||
## Current state
|
||||
|
||||
**master** (pushed): the island (pill, card, CD ring, EQ, output and player switching, hover animations, instant transport buttons), an idle pill when no player is open, one island per monitor (card on one monitor, live bars only on the focused one), a workspace pill per monitor, and wallpaper colours through matugen.
|
||||
**master** (pushed): the island (pill, card, CD ring, EQ, output and player switching, hover animations, instant transport buttons), an idle pill when no player is open, one island per monitor (card on one monitor, live bars only on the focused one), a workspace pill per monitor, and wallpaper colours through matugen. Merged from `control-centre` on 2026-10-13, then extended on master:
|
||||
|
||||
**Open design branches** (pushed, not merged; the user is reviewing them):
|
||||
- **Control centre** (top right): a status pill (Wi-Fi or hotspot, VPN, Bluetooth, Ethernet, battery; icons of stopped services hidden) that opens a panel: tiles for Wi-Fi, Bluetooth, VPN, Hotspot, Ethernet; volume with the output, brightness, keyboard light, performance mode, external-GPU programs. The panel morphs into its menus as one shape: Wi-Fi and Bluetooth maps (devices floating round this computer, click to join/pair, right-click a saved network for its password, click the centre to search again), VPN list, sound outputs, Ethernet details with copyable addresses. The maps reach from the right edge to the island's pill, less 16 px. Popups (`magic-prompt`) ask for passwords, PINs and passkeys and say why something failed.
|
||||
- **System tray** right of the workspace pill.
|
||||
- **Bar hiding:** only on monitors showing a fullscreen window; the pointer resting on the top edge brings it back. Events bring one part in for a moment (a peek): the workspace pill alone on a workspace change, the island on a song change or a new player.
|
||||
- **Reserved space:** windows that aren't fullscreen stay below the bar (44 px at the top of every monitor).
|
||||
|
||||
On 2026-10-13 the user was asked to try the mouse-only parts (tray clicks, top-edge hover reveal, Copy buttons, PIN pairing) and answered "All works"; the agent itself only drove them over IPC.
|
||||
|
||||
**Open design branches** (pushed, not merged; the user is reviewing them). They branched from `036b326`, 13 commits before master's control-centre work; merging one will need a rebase (conflicts not checked yet):
|
||||
|
||||
| Branch | What it is |
|
||||
|---|---|
|
||||
@@ -45,7 +56,7 @@ Measured 2026-10-09 with the silent "Synth Test" player playing, 4 monitors (Ama
|
||||
|
||||
Hyprland's CPU stayed at about 9.5% in every run, blur rule on or off. All builds, master included, had 25–40 stutter frames (20–500 ms gaps) per 3 opens and closes, against 0–1 measured earlier with fewer monitors; the cause wasn't found (Cider and Firefox were running).
|
||||
|
||||
**On the desktop right now:** the agent-started shell runs the `design-frosted` build (pid in `~/.cache/amane-remote-test/shell.pid`, no frame log). `~/.config/amane` is on master with design-frosted's `src/` deployed over it, so `git status` shows changed files. `shell.conf` has `glass = 0.70` (set by the user). amane is not autostarted by Hyprland: after a reboot the user starts it.
|
||||
**On the desktop right now:** the agent-started shell runs a master build (`11646ca`; pid in `~/.cache/amane-remote-test/shell.pid`, no frame log). `~/.config/amane` is a checkout at `036b326` with master's `src/` deployed over it, so `git status` shows changed files. `shell.conf` has `glass = 0.70` (set by the user). amane is not autostarted by Hyprland: after a reboot the user starts it.
|
||||
|
||||
**Theme pipeline:** Skwd-wall v2's matugen integration `amane` renders `~/.config/amane/matugen/amane.colors` into `~/.config/amane/theme` (Skwd scheme: `scheme-fidelity`). `~/.config/hypr/windowrules.lua` has the glass layer rule (`^magic-.*$`, blur, `ignore_alpha = 0.5`).
|
||||
|
||||
@@ -66,7 +77,15 @@ Hyprland's CPU stayed at about 9.5% in every run, blur rule on or off. All build
|
||||
| `art.rs` `Art` | Service, 250 ms poll | Cover URL → local file (curl for https), copied to `palette-cover`, which `Palette` (opened in `main.rs`, 8 colours) watches. Only used for `cover_colors`. |
|
||||
| `theme.rs` `Theme` | Service, `amane::watch_file` | 10 colours from `~/.config/amane/theme` and `shell.conf` (`cover_colors`, `transition_ms`; `glass` on the glass branches), faded together on one `Animation<Colors>`. Every view starts with `theme::begin()` (subscribes, returns `cover_colors`) and draws with `theme::now()`. No theme file: the original pink on purple. `watch_file` watches the folder, so matugen's rename-over saves count (55–68 ms to redraw). |
|
||||
| `workspaces.rs` | view + input-only `Hover` | Workspace pill per monitor (namespace `magic-workspaces`, top left): `Workspaces` filtered by `output() == monitor.name`, special workspaces skipped. Click runs `hyprctl dispatch 'hl.dsp.focus({ workspace = N })'` on a thread. |
|
||||
| `parse.rs` | plain functions | pactl/cava/proc parsing, filter-chain config, `pw-cli` params, EQ store, middle ellipsis, `key = value` settings, `#rrggbb`. Unit-tested on its own. |
|
||||
| `parse.rs` | plain functions | pactl/cava/proc parsing, filter-chain config, `pw-cli` params, EQ store, middle ellipsis, `key = value` settings, `#rrggbb`; for the control centre also iwctl/bluetoothctl output (`plain_lines`, `bluez_reason`, `pairing_event`), iwd profile names and psk files, Wi-Fi bars, link speeds, `hyprctl monitors/clients`, `ip` addresses and routes, tray icon scoring and menu mnemonics. Unit-tested on its own. |
|
||||
| `control.rs` `Control` | view + input-only Service | The control centre (`magic-control`, top right) and its menu window (`magic-control-menu`, no shape of its own: the panel morphs to the menu's size, then the menu's content fades in). `Menu` (Wifi, Bluetooth, Vpn, Sound, Ethernet), `menu_content(menu, map_w, ..)` for both windows, `map_width(monitor_w)`, the `control` IPC handler. |
|
||||
| `system.rs` `Links`, `Power`, `Gpu` | Services, threads | iwd (D-Bus reads, only while it has an owner), openvpn services, bluez service state, wired cards from sysfs, battery, brightness, keyboard light, power profile, eGPU programs from fdinfo. Actions: `join_wifi`, `scan_wifi`, `disconnect_wifi` (iwctl), `scan_bluetooth`, `rc-service` via doas, `saved_password`, `wired_details`. |
|
||||
| `prompt.rs` `Prompt` | view + input-only Service | The popup (`magic-prompt`): password, PIN, passkey, confirm, show a code, saved password (Copy), failure reason. Runs the `bluetoothctl` pairing session. Only popups with a text field take the keyboard. `prompt::copy(text, done)` is the one wl-copy. |
|
||||
| `map.rs` | widget functions | The Wi-Fi/Bluetooth maps: nodes on an ellipse round the centre bubble (nearer = stronger), floating, lines (wobbling, running out while connecting, solid with a pulse), search rings. `AREA_H` 480, `NODES` 8 (a 9th makes labels meet at 822 px wide; the sizes were picked by simulating the drift). |
|
||||
| `glyph.rs` | shapes | Every icon as `Shape`s; maps draw all of theirs into ONE canvas (`glyph_shapes`), as each `Canvas` has a large fixed GPU cost. |
|
||||
| `edge.rs` `Edge` | Service, Hyprland sockets | Reads `.socket2.sock` events (fullscreen, workspace, focus, windows) and asks `.socket.sock` for `monitors`/`clients`; no polling. Hides the bar on fullscreen monitors (`edge.part(Part, monitor)`, `hidden_by`), the 1 px `magic-edge` strip that brings it back, peeks and the island's pop (`edge.pop`), the workspace highlight slide (`switched`), and `spacer`, the `magic-reserve` strip that reserves 44 px for the bar. |
|
||||
| `tray.rs` `Tray`, `TrayUi` | Services, bus threads | Owns `org.kde.StatusNotifierWatcher` (only if no other watcher runs) and is its host; items by bus name or path (path-only owners found by asking all connections at once); icons from themes or `IconPixmap`; dbusmenu menus in `magic-tray-menu`; the `tray` IPC handler. |
|
||||
| `png.rs` | plain functions | Stored-deflate PNG writer for pixmap tray icons (amane draws images from files). |
|
||||
|
||||
The equalizer: one `setsid -f pipewire -c eq-<key>.conf` per player with `libpipewire-module-filter-chain`, 10 biquads (low shelf 31 Hz, peaking 63 Hz–8 kHz, high shelf 16 kHz). The player's streams move into sink `magic_island_eq.<key>`; gains change live with `pw-cli set-param magic_island_eq.<key> Props '{ params = [ "b1:Gain" 6.0 ... ] }'`. A player only gets one when its curve or output first changes, or it has a saved non-flat curve.
|
||||
|
||||
@@ -90,6 +109,9 @@ The equalizer: one `setsid -f pipewire -c eq-<key>.conf` per player with `libpip
|
||||
- Layer-rule `match.namespace` is a full match: `^magic-` matches nothing, `^magic-.*$` works. Layer rules can only switch blur on; blur strength (`decoration.blur.size`, `passes`) is global.
|
||||
- `$XDG_RUNTIME_DIR/hypr/` keeps folders of dead instances; the live one has `.socket.sock` (`amane-remote.sh` picks it). A shell started with a dead signature gets an empty `Workspaces` list.
|
||||
- `hyprctl layers` lists hidden layer windows too (alpha 0); filter on `alpha > 0`.
|
||||
- **Reserved space and fullscreen:** a layer with `Zone::Reserve` reserves its whole height (amane's `measure_thickness`), and only on a window anchored to one edge (or an edge plus both sides), so corner-anchored or card-sized windows can't reserve. Hyprland keeps tiled and maximised (fullscreen 1) windows out of the reserved strip and lets fullscreen 2/3 cover it. Bar windows must keep `Zone::Ignore`, otherwise they get pushed down below the strip. Check with `hyprctl monitors` (`reserved: 0 44 0 0`) and a tiled window's `at:`.
|
||||
- **Hyprland state comes from its sockets, not polling:** `.socket2.sock` streams events, `.socket.sock` answers `monitors`/`clients`. Fullscreen 2 and 3 hide the bar; 1 (maximised) keeps it.
|
||||
- A peek (workspace or island) only shows on the **focused** monitor, and only matters on a fullscreen one; test with a workspace hop between two fullscreen workspaces there, then restore the focus.
|
||||
|
||||
## Performance rules
|
||||
|
||||
@@ -122,7 +144,7 @@ The equalizer: one `setsid -f pipewire -c eq-<key>.conf` per player with `libpip
|
||||
```
|
||||
|
||||
Then `cargo clippy` there. The first build takes minutes.
|
||||
2. Unit tests: `rustc --edition 2024 --test src/parse.rs -o /tmp/parse_test && /tmp/parse_test` (6 tests).
|
||||
2. Unit tests: `rustc --edition 2024 --test src/parse.rs -o /tmp/parse_test && /tmp/parse_test` (10 tests).
|
||||
|
||||
## Testing on the desktop
|
||||
|
||||
@@ -150,23 +172,29 @@ tools/amane-remote.sh $H sh 'COMMAND' # anything inside the graphical sessi
|
||||
- Before trusting timings, check `uptime`: a background Gentoo build once made every build look broken.
|
||||
- A fresh shell once exited with "lost the gpu (Device is lost)" (Mesa warns Xe is experimental); starting again worked.
|
||||
- To tune a visualiser, sample real levels read-only (`pw-record --target <stream> | cava`, `timeout -s KILL` on each stage; a plain `timeout` on the pipeline orphans cava) first. Typical music sits at 0.15–0.35.
|
||||
- In `pkill -f` patterns over SSH, use `[p]ython3 ...` so the pattern doesn't match the remote shell itself.
|
||||
- In `pkill -f` patterns over SSH, use `[p]ython3 ...` so the pattern doesn't match the remote shell itself (a plain `pgrep -f fake_tray.py` once killed the test script).
|
||||
- **Control centre and tray without a mouse:** `ipc control open|close|menu <wifi|bluetooth|vpn|sound>|ethernet|wifi search|status`, `ipc tray list|menu|open N|activate N|choose ID|enter ID|back|close`. `control status` ends with `prompt=<what the popup shows>`. A fake tray app: `python3 tools/fake_tray.py name|path` (by bus name, or path-only like libappindicator; it prints every call it gets). Kill it by pid.
|
||||
- **Maps with many nodes:** with Wi-Fi off the maps are empty. Deploy a scratch copy of `src/` whose `map::map` injects fake nodes (never commit it), then redeploy the real `src`. Pick sizes by simulating the drift in Python, not from single screenshots.
|
||||
- **Measure gaps and alignment from pixels**, not by asking a vision model: `ffmpeg -i shot.png -vf crop=1920:1:0:Y -f rawvideo -pix_fmt rgb24 row.raw` and look for the border pixels.
|
||||
- To see the island and the control centre together, open them on a monitor without a fullscreen window (focus one of its workspaces first, restore after).
|
||||
|
||||
### After testing
|
||||
|
||||
```sh
|
||||
tools/amane-remote.sh $H players stop
|
||||
tools/amane-remote.sh $H sinks stop
|
||||
tools/amane-remote.sh $H sh 'P=$(pgrep -f "^/usr/lib/python-exec/python3[.0-9]*/python3 fake_tray"); [ -n "$P" ] && kill $P' # fake tray apps
|
||||
tools/amane-remote.sh $H sh 'kill $(cat shell.pid); for i in $(seq 50); do pgrep -F shell.pid > /dev/null || break; sleep 0.1; done
|
||||
rm -f shell.log; setsid -f sh -c "echo \$\$ > shell.pid; exec ~/.cache/amane/amane-shell" > /dev/null 2>&1'
|
||||
```
|
||||
|
||||
## Open threads
|
||||
|
||||
- The user picks a glass design; then merge `glass-base` plus that design into master and update README/AGENTS.md.
|
||||
- The user picks a glass design; then rebase `glass-base` plus that design onto master (they predate the control centre, whose windows draw their own surfaces), merge, and update README/AGENTS.md.
|
||||
- The candle ring is still mirrored (`mirrored()`); the pill's bars aren't.
|
||||
- The Amane library could fix the two Hyprland 0.56 issues upstream (handle a configure on a hidden layer window; use the Lua dispatch in `Workspaces::focus`). Not started.
|
||||
- `island-eq` is tracked in git but rewritten live by the island, so the desktop checkout always shows it changed; untracking it was proposed, no answer yet.
|
||||
- Leftovers offered for deletion, no answer yet: `src.before-test/` on the desktop, seven merged local branches on the dev host.
|
||||
- Leftovers offered for deletion, no answer yet: `src.before-test/` on the desktop, seven merged local branches on the dev host. New since: the merged local `control-centre` branch, and old screenshots in the desktop's `~/.cache/amane-remote-test/` (`dimtest.png`, `grab.png`, `shot.png`, `ws-after.png`, `shots/`).
|
||||
- On a monitor narrower than about 1500 px the maps stop at 600 px (`MIN_MAP_W`) and run under the island.
|
||||
- The open/close stutter rise (25–40 slow frames per 3 opens and closes on every build) is unexplained.
|
||||
- See README "Known limits".
|
||||
|
||||
Reference in New Issue
Block a user