aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md118
-rw-r--r--CLAUDE.md7
2 files changed, 125 insertions, 0 deletions
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..e7e7f65
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,118 @@
+# AGENTS.md
+
+Guidance for agents working in this repository.
+
+## What this is
+
+Quickshell components for a Hyprland desktop, one per directory, each a
+complete shell in its own right. They are not modules of a single bar: any of
+them runs alone, and running one does not require the others.
+
+ volume-osd/ volume for output and input, plus what is playing
+ vm-manager/ libvirt drawer: state, live stats, snapshots
+
+Both are started from `~/.config/hypr/sections/autostart.lua` and keep running
+for the whole session.
+
+## The rule that bites first
+
+**A quickshell config with no visible window exits.** Both components here are
+hidden most of the time, so each holds itself open with a 1x1 transparent
+`PanelWindow` with `mask: Region {}`, which is click-through and draws nothing.
+
+Without it the shell loads, logs `Configuration Loaded`, reports no error, and
+quits. The symptom is never an error message: it is a keybind that appears to
+do nothing, or a panel that never paints. This was removed once during
+development because a process check was measuring the wrong thing, and both
+components broke in exactly that way. Any new component needs the same window.
+
+## Verifying, and how not to
+
+Two traps cost real time in this repo, both about measurement rather than code.
+
+**A detached `qs` does not survive an agent's tool call.** Starting one with
+`&`, `nohup` or `setsid -f` and then checking `pgrep` in a later call reports
+`DEAD` regardless of whether the config is sound. That reads exactly like the
+no-visible-window exit above and sent a debugging session in the wrong
+direction for a dozen calls. Start it so the harness owns the process, and
+confirm with the log rather than a later `pgrep`.
+
+**`pkill -f` matches the agent's own shell.** The working directory is in the
+command line, so `pkill -f quickshell` or `pkill -f "foo.qml"` kills the caller
+and returns 144. Use `pkill -x quickshell`.
+
+For anything visual, ask. Screenshots of a transient OSD are a race, and the
+user has the screen.
+
+## Per-component notes
+
+Each directory's README carries its own, and they are worth reading before
+changing that component. The ones that generalise:
+
+- **PipeWire nodes report their initial volume before `ready` goes true.** The
+ `ready` check alone suppresses the startup values. An extra guard on top of
+ it ate the user's first keypress instead.
+- **`PwObjectTracker` is not optional.** Node properties only stay current
+ while something binds the node; without it the volume reads once and goes
+ stale.
+- **playerctld publishes a duplicate of every MPRIS player** under its own bus
+ name. Filter by `dbusName`, not by identity.
+- **Key events reach a focused item, not a window.** Setting
+ `WlrLayershell.keyboardFocus` is necessary but not sufficient:
+ `Keys.onEscapePressed` on a `PanelWindow` never fires.
+- **A `Row` sizes to its children, not its parent.** Fixed child widths inside
+ one overflowed the panel and pushed a button past its edge.
+- **libvirt's own memory and disk figures are not what they look like.**
+ `balloon.current` is memory allocated to the VM and reads full forever;
+ `block.allocation` is qcow2 growth on the host, not usage inside the guest.
+ The real numbers come from qemu-guest-agent, and the panel shows a dash
+ rather than substituting the host-side ones.
+
+## Theme
+
+No component defines a palette. `udt-accent`, in the
+`unified-desktop-theme` repo alongside this one, writes
+`~/.cache/wal/udt-palette.qml` from its `palette.rasi`, carrying the whole
+Catppuccin Macchiato palette plus the accent snapped from the current
+wallpaper. Each `Theme.qml` parses and watches that file, so a palette edit
+recolours a running shell with no restart.
+
+What is left in `Theme.qml` is a fallback for before the file is read, and for
+a machine without unified-desktop-theme. Do not grow it into a second palette:
+that duplication is what generating the file removed.
+
+The file is parsed rather than imported because a generated QML singleton
+cannot be imported without a `qmldir` beside it, and the wal cache has no
+reason to carry one.
+
+## Blur
+
+Translucency is set in QML; the frosting is the compositor's. Hyprland blurs a
+layer surface only when a rule names it, matched on the namespace the window
+sets, in `~/.config/hypr/sections/decorations.lua`. A new component that wants
+frosting needs its own `hl.layer_rule` and a distinct
+`WlrLayershell.namespace`. Without the rule it still works, rendering flat
+translucent.
+
+## Reloading
+
+| | How |
+| --- | --- |
+| a component's QML | quickshell hot-reloads on save; no restart |
+| palette | regenerate with `udt-accent <wallpaper>`; watched, no restart |
+| Hyprland binds, layer rules, autostart | `hyprctl reload` |
+
+Hot reload covers adding and removing windows too: the keepalive window above
+was added to a running shell and took effect on save.
+
+`qs -p <dir>` runs one directly. `qs -p <dir> ipc call <target> <fn>` reaches a
+running one, which is how `SUPER+v` opens the VM drawer.
+
+## Conventions
+
+- GPLv2 only, with the header notice in every source file.
+- Commits carry the reasoning, not just the change: several of the notes above
+ exist because a commit message explained why something was the way it was.
+- No home paths in committed files. A gitleaks hook blocks them and has been
+ right every time; `~` in documentation, absolute paths only in the live
+ Hyprland config, which is not in this repo.
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..3599d0e
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,7 @@
+# CLAUDE.md
+
+This file is intentionally thin. AGENTS.md is the single source of truth for
+this project, shared across every agent tool. Do not duplicate content here,
+edit AGENTS.md instead.
+
+@AGENTS.md