From cc17e7bab41d4cec5e996513b0c72cc3a20d0ebb Mon Sep 17 00:00:00 2001 From: funman300 Date: Mon, 11 May 2026 11:09:40 -0700 Subject: [PATCH] docs: implementation plan for volume notification Co-Authored-By: Claude Opus 4.7 (1M context) --- .../plans/2026-05-11-volume-notification.md | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-11-volume-notification.md diff --git a/docs/superpowers/plans/2026-05-11-volume-notification.md b/docs/superpowers/plans/2026-05-11-volume-notification.md new file mode 100644 index 0000000..6826cb3 --- /dev/null +++ b/docs/superpowers/plans/2026-05-11-volume-notification.md @@ -0,0 +1,227 @@ +# Volume Notification Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the three `pamixer`-direct media-key bindings with calls to a wrapper script that also fires a mako notification showing the new volume level (or "Muted") with a progress bar. + +**Architecture:** New `scripts/volume.sh` wrapper accepting `up|down|mute`. Adjusts master sink via `pamixer`, then calls `notify-send` with mako's `x-canonical-private-synchronous` and `int:value` hints so successive notifications replace each other and render as a progress bar. niri keybinds call the wrapper through its `~/.local/bin/volume` symlink. install.sh symlinks the script alongside the existing entries. + +**Tech Stack:** bash, pamixer, notify-send, mako (notification daemon), niri. + +**Spec:** `docs/superpowers/specs/2026-05-11-volume-notification-design.md` + +**Verification model:** Bash syntax check + dry-run script invocation with each subcommand. niri config validity via `niri validate`. End-to-end (actually pressing the volume key and seeing a mako bubble) requires the live desktop — the controller verifies after both task commits. + +--- + +## File Structure + +| File | Role | +|---|---| +| `scripts/volume.sh` | New wrapper: pamixer adjust + notify-send. Three subcommands: `up`, `down`, `mute`. | +| `install.sh` | One new `ln -sf` line in the `==> Installing scripts` block. | +| `niri/config.kdl` | Three lines (97–99) modified to call `volume up/down/mute` instead of `pamixer` directly. | + +--- + +## Task 1: Wrapper script + install.sh symlink + +After this task, `~/.local/bin/volume up|down|mute` works from any shell and produces both the volume change and the mako notification. niri keybinds still call `pamixer` directly — that swap happens in Task 2. + +**Files:** +- Create: `scripts/volume.sh` +- Modify: `install.sh` (append one line in the scripts symlink block) + +- [ ] **Step 1: Create `scripts/volume.sh`** + +Write `scripts/volume.sh` with this exact content: + +```bash +#!/bin/bash + +case "${1:-}" in + up) pamixer -i 5 ;; + down) pamixer -d 5 ;; + mute) pamixer -t ;; + *) echo "usage: $0 up|down|mute" >&2; exit 1 ;; +esac + +if [ "$(pamixer --get-mute)" = "true" ]; then + notify-send -h string:x-canonical-private-synchronous:volume \ + -h int:value:0 \ + -t 1500 \ + "Muted" +else + LEVEL=$(pamixer --get-volume) + notify-send -h string:x-canonical-private-synchronous:volume \ + -h int:value:"$LEVEL" \ + -t 1500 \ + "Volume: ${LEVEL}%" +fi +``` + +- [ ] **Step 2: Make it executable** + +```bash +chmod +x scripts/volume.sh +ls -la scripts/volume.sh +``` + +Expected: `-rwxr-xr-x` mode. + +- [ ] **Step 3: Syntax-check** + +```bash +bash -n scripts/volume.sh && echo "syntax ok" +``` + +Expected: `syntax ok`, exit 0. + +- [ ] **Step 4: Dry-run the usage-error branch** + +The unrecognised-arg branch is safe to exercise without side effects (pamixer never called): + +```bash +bash scripts/volume.sh 2>&1 +echo "exit=$?" +``` + +Expected: +``` +usage: scripts/volume.sh up|down|mute +exit=1 +``` + +- [ ] **Step 5: Symlink it into `~/.local/bin`** + +```bash +ln -sf "$PWD/scripts/volume.sh" ~/.local/bin/volume +ls -la ~/.local/bin/volume +``` + +Expected: symlink pointing to `$PWD/scripts/volume.sh` (absolute path of repo's script). + +- [ ] **Step 6: Add the symlink line to `install.sh`** + +Find the `==> Installing scripts` block in `install.sh`. It currently ends like this: + +```bash +ln -sf "$(pwd)/scripts/waybar-restart.sh" ~/.local/bin/waybar-restart +ln -sf "$(pwd)/scripts/screenshot.sh" ~/.local/bin/screenshot +``` + +Append one line after `screenshot.sh`: + +```bash +ln -sf "$(pwd)/scripts/volume.sh" ~/.local/bin/volume +``` + +The block then ends with three `ln -sf` lines (waybar-restart, screenshot, volume). + +- [ ] **Step 7: Sanity-check install.sh parses** + +```bash +bash -n install.sh && echo "syntax ok" +``` + +Expected: `syntax ok`, exit 0. + +- [ ] **Step 8: Functional smoke test (controller verifies; subagent skip if no audio session)** + +If you have an audio session, run: + +```bash +~/.local/bin/volume up +``` + +Expected: volume rises 5%, a mako bubble appears with `Volume: NN%` and a progress bar, the bubble auto-dismisses in 1.5 s. Then run `~/.local/bin/volume down` and `~/.local/bin/volume mute` and confirm each behaves. + +If your sandbox has no audio session or no notification daemon, skip; the controller will verify after the commit. + +- [ ] **Step 9: Commit** + +```bash +git add scripts/volume.sh install.sh +git commit -m "volume: add notification wrapper script" +``` + +--- + +## Task 2: Point niri keybinds at the wrapper + +After this task, pressing the three XF86Audio keys runs the wrapper instead of `pamixer` directly. Volume changes now produce notifications. + +**Files:** +- Modify: `niri/config.kdl` (lines 97–99) + +- [ ] **Step 1: Replace the three keybind lines** + +Find lines 97–99 in `niri/config.kdl`: + +```kdl + XF86AudioRaiseVolume allow-when-locked=true { spawn "pamixer" "-i" "5"; } + XF86AudioLowerVolume allow-when-locked=true { spawn "pamixer" "-d" "5"; } + XF86AudioMute allow-when-locked=true { spawn "pamixer" "-t"; } +``` + +Replace with: + +```kdl + XF86AudioRaiseVolume allow-when-locked=true { spawn "volume" "up"; } + XF86AudioLowerVolume allow-when-locked=true { spawn "volume" "down"; } + XF86AudioMute allow-when-locked=true { spawn "volume" "mute"; } +``` + +`allow-when-locked=true` stays — same behaviour, just routed through the wrapper. + +- [ ] **Step 2: Validate niri config** + +```bash +niri validate 2>&1 | tail -2 +``` + +Expected: a final line `INFO niri: config is valid`. Non-zero exit or `ERROR` means a syntax issue. + +- [ ] **Step 3: Confirm pamixer is no longer referenced in keybinds** + +```bash +grep -n "pamixer" niri/config.kdl +``` + +Expected: zero matches (the wrapper is invoked via `volume`, pamixer is implementation detail). + +- [ ] **Step 4: Confirm the three new lines are in place** + +```bash +grep -n 'spawn "volume"' niri/config.kdl +``` + +Expected: three lines, one each for `"up"`, `"down"`, `"mute"`: +``` +97: XF86AudioRaiseVolume allow-when-locked=true { spawn "volume" "up"; } +98: XF86AudioLowerVolume allow-when-locked=true { spawn "volume" "down"; } +99: XF86AudioMute allow-when-locked=true { spawn "volume" "mute"; } +``` + +- [ ] **Step 5: Functional smoke test (controller verifies)** + +niri picks up `config.kdl` changes live; no reload needed. Press the volume up key — a mako notification should appear within ~100 ms showing the new level + progress bar. Press mute — notification shows `Muted` with empty bar. Press volume up again — notification returns to `Volume: NN%`. + +Each key press should replace the previous notification (no stacking), thanks to the `x-canonical-private-synchronous` hint. + +- [ ] **Step 6: Commit** + +```bash +git add niri/config.kdl +git commit -m "niri: route volume keys through notification wrapper" +``` + +--- + +## Final verification (controller, post-merge) + +- [ ] Pressing XF86AudioRaiseVolume / XF86AudioLowerVolume produces a mako notification with the new percentage and a progress bar. +- [ ] Pressing XF86AudioMute toggles the mute state; the notification shows `Muted` (with empty bar) when muting and `Volume: NN%` when unmuting. +- [ ] Holding the key (rapid repeat) produces a single updating bubble, not a stack. +- [ ] Volume changes still work on the lock screen (gtklock). The bubble may not be visible while locked depending on mako's layer ordering relative to gtklock; the volume change itself fires regardless. +- [ ] `~/.local/bin/volume bogus` prints the usage message and exits 1.