diff --git a/docs/superpowers/plans/2026-05-01-fan-profile-auto.md b/docs/superpowers/plans/2026-05-01-fan-profile-auto.md new file mode 100644 index 0000000..f3b6700 --- /dev/null +++ b/docs/superpowers/plans/2026-05-01-fan-profile-auto.md @@ -0,0 +1,373 @@ +# Fan-Profile "Auto" Mode 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:** Add a virtual `auto` strategy to `scripts/fan-profile.sh` that mirrors the active power-profiles-daemon profile to a corresponding `fw-fanctrl` strategy, reconciling on every waybar refresh. + +**Architecture:** Three additions to a single bash script — a state-file constant, a profile→strategy mapping function, and two new conditional branches (one in the `--menu` handler, one in the status emit). State lives in `~/.local/state/fan-profile-auto`. No new files, no new daemons, no fw-fanctrl config edits. + +**Tech Stack:** bash, `fw-fanctrl`, `powerprofilesctl`, wofi, waybar. + +**Spec:** `docs/superpowers/specs/2026-05-01-fan-profile-auto-design.md` + +**Verification model:** Bash script — no unit-test framework. Each task ends with manual `bash scripts/fan-profile.sh` invocations under representative state (auto-on / auto-off / power-profile mismatch / `powerprofilesctl` failure simulated by overriding `PATH`), inspecting JSON output and side-effects on `fw-fanctrl`. + +**Mapping (locked from spec):** +| power-profiles-daemon | fw-fanctrl | +|---|---| +| `power-saver` | `lazy` | +| `balanced` | `medium` | +| `performance` | `agile` | + +--- + +## File Structure + +- **Modify:** `scripts/fan-profile.sh` — add a constant, a function, and modify two existing branches. + +No other files. + +--- + +## Task 1: Add state-file constant and mapping function + +After this task, the script has a place to store auto-mode state and a pure function to map a power profile to a fan strategy. No behavior change yet — these are unused until Tasks 2 and 3 wire them in. + +**Files:** +- Modify: `scripts/fan-profile.sh` (insert after the `#!/bin/bash` shebang on line 1) + +- [ ] **Step 1: Add the constant and function at the top of the script** + +Open `scripts/fan-profile.sh`. After the shebang line (`#!/bin/bash`) and the blank line that follows, insert: + +```bash +STATE_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" + +map_profile_to_strategy() { + case "$1" in + power-saver) echo lazy ;; + balanced) echo medium ;; + performance) echo agile ;; + *) return 1 ;; + esac +} +``` + +The script's first 4 lines should now read: +```bash +#!/bin/bash + +STATE_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" + +map_profile_to_strategy() { +``` + +- [ ] **Step 2: Sanity-check the function** + +Run: +```bash +bash -c 'source scripts/fan-profile.sh; map_profile_to_strategy power-saver; map_profile_to_strategy balanced; map_profile_to_strategy performance; map_profile_to_strategy bogus; echo "exit=$?"' +``` + +Expected output: +``` +lazy +medium +agile +exit=1 +``` + +(`bogus` produces no stdout and exits 1; the other three each print one strategy name on its own line.) + +Note: sourcing the script will also execute the rest of it (the status emit). That's fine — its output gets mixed in. Only inspect that the four lines above appear in the right order somewhere. + +- [ ] **Step 3: Verify status emit still works** + +Run the existing status path: +```bash +bash scripts/fan-profile.sh +``` + +Expected: a single JSON line like `{"text": "󰈐 medium", "tooltip": "Fan strategy: medium\nSpeed: 28%\nClick to change", "class": "medium"}` — same shape as before. The script's behavior is unchanged because nothing references `STATE_FILE` or `map_profile_to_strategy` yet. + +- [ ] **Step 4: Commit** + +```bash +git add scripts/fan-profile.sh +git commit -m "fan-profile: add state path and profile→strategy mapping (unused yet)" +``` + +--- + +## Task 2: Wire `auto` into the wofi menu + +After this task, picking `auto` from the menu enables auto mode (creates the state file and applies the mapped strategy immediately). Picking any other strategy disables auto mode (deletes the state file). The status emit still behaves as before — auto-mode rendering comes in Task 3. + +**Files:** +- Modify: `scripts/fan-profile.sh` — replace the existing `--menu` block (currently lines ~3–16 of the post-Task-1 file) with the version below. + +- [ ] **Step 1: Replace the `--menu` branch** + +Find the existing block: + +```bash +if [ "$1" = "--menu" ]; then + CHOICE=$(fw-fanctrl print list 2>/dev/null \ + | grep "^-" \ + | sed 's/^- //' \ + | wofi --dmenu \ + --prompt "Fan Strategy:" \ + --width 260 \ + --height 300 \ + --hide-scroll \ + --no-actions \ + --insensitive) + [ -n "$CHOICE" ] && fw-fanctrl use "$CHOICE" && pkill -RTMIN+9 waybar + exit +fi +``` + +Replace it with: + +```bash +if [ "$1" = "--menu" ]; then + STRATS=$(fw-fanctrl print list 2>/dev/null | grep "^-" | sed 's/^- //') + CHOICE=$(printf 'auto\n%s\n' "$STRATS" \ + | wofi --dmenu \ + --prompt "Fan Strategy:" \ + --width 260 \ + --height 320 \ + --hide-scroll \ + --no-actions \ + --insensitive) + [ -z "$CHOICE" ] && exit + if [ "$CHOICE" = "auto" ]; then + mkdir -p "$(dirname "$STATE_FILE")" + touch "$STATE_FILE" + PROFILE=$(powerprofilesctl get 2>/dev/null) \ + && MAPPED=$(map_profile_to_strategy "$PROFILE") \ + && fw-fanctrl use "$MAPPED" + else + rm -f "$STATE_FILE" + fw-fanctrl use "$CHOICE" + fi + pkill -RTMIN+9 waybar + exit +fi +``` + +Three substantive changes: +1. `auto\n` is prepended to the strategy list piped into wofi. +2. Empty selection (user cancels) now exits early instead of falling through. +3. `auto` and non-`auto` selections are routed differently — auto creates the state file and applies the mapped strategy; everything else removes the state file (if present) and uses fw-fanctrl directly. + +The wofi `--height` was bumped from 300 → 320 to accommodate the extra row. + +- [ ] **Step 2: Verify the menu branch in dry-run** + +You can't fully exercise wofi without a display, but you can verify the strategy list construction: + +```bash +bash -c 'STRATS=$(fw-fanctrl print list 2>/dev/null | grep "^-" | sed "s/^- //"); printf "auto\n%s\n" "$STRATS"' +``` + +Expected output (one strategy per line, `auto` first): +``` +auto +laziest +lazy +medium +agile +very-agile +deaf +aeolus +``` + +- [ ] **Step 3: Verify state-file logic by simulating "auto" selection** + +Manually run the auto branch: +```bash +mkdir -p "${XDG_STATE_HOME:-$HOME/.local/state}" +rm -f "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +bash -c 'source scripts/fan-profile.sh; CHOICE=auto +if [ "$CHOICE" = "auto" ]; then + mkdir -p "$(dirname "$STATE_FILE")" + touch "$STATE_FILE" + echo "state file created at: $STATE_FILE" +fi' +ls -la "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +``` + +Expected: file exists. + +Now simulate selecting a real strategy: +```bash +rm -f "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +touch "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +bash -c 'source scripts/fan-profile.sh; CHOICE=lazy +if [ "$CHOICE" != "auto" ]; then + rm -f "$STATE_FILE" + echo "state file removed" +fi' +ls -la "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" 2>&1 +``` + +Expected: `cannot access ... No such file or directory` (file removed). Then clean up: +```bash +rm -f "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +``` + +- [ ] **Step 4: Commit** + +```bash +git add scripts/fan-profile.sh +git commit -m "fan-profile: route 'auto' menu choice through state file" +``` + +--- + +## Task 3: Render auto mode in the status emit + +After this task, when the state file exists the status emit reconciles `fw-fanctrl` to the power profile's mapped strategy and renders `auto` in the bar. When the state file is absent, behavior is unchanged. + +**Files:** +- Modify: `scripts/fan-profile.sh` — insert a new conditional block before the existing status emit (the `OUTPUT=$(fw-fanctrl print 2>/dev/null)` line and everything after it). + +- [ ] **Step 1: Insert the auto-mode status block** + +Find this section in the script (it's the part that runs when no `--menu` arg was passed): + +```bash +OUTPUT=$(fw-fanctrl print 2>/dev/null) +CURRENT=$(echo "$OUTPUT" | awk -F"'" '/^Strategy:/{print $2}') +SPEED=$(echo "$OUTPUT" | awk '/^Speed:/{print $2}') +``` + +Immediately *before* that `OUTPUT=` line, insert: + +```bash +if [ -f "$STATE_FILE" ]; then + PROFILE=$(powerprofilesctl get 2>/dev/null) + MAPPED=$(map_profile_to_strategy "$PROFILE" 2>/dev/null) + if [ -n "$MAPPED" ]; then + ACTIVE=$(fw-fanctrl print 2>/dev/null | awk -F"'" '/^Strategy:/{print $2}') + [ "$ACTIVE" != "$MAPPED" ] && fw-fanctrl use "$MAPPED" >/dev/null 2>&1 + printf '{"text": "󰈐 auto", "tooltip": "Auto fan strategy\\nProfile: %s → %s\\nClick to change", "class": "auto"}\n' "$PROFILE" "$MAPPED" + else + printf '{"text": "󰈐 auto (?)", "tooltip": "Auto: power-profiles-daemon unreachable or unknown profile\\nClick to change", "class": "auto"}\n' + fi + exit +fi + +``` + +(Trailing blank line included so there's separation between the new block and the existing `OUTPUT=` line.) + +- [ ] **Step 2: Verify auto-off path is unchanged** + +Make sure the state file does NOT exist: +```bash +rm -f "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +bash scripts/fan-profile.sh +``` + +Expected: same JSON as before Task 1 — `{"text": "󰈐 ", "tooltip": "Fan strategy: \nSpeed: %\nClick to change", "class": ""}`. + +- [ ] **Step 3: Verify auto-on happy path** + +Create the state file and run the script: +```bash +mkdir -p "${XDG_STATE_HOME:-$HOME/.local/state}" +touch "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +bash scripts/fan-profile.sh +``` + +Expected output (assuming `powerprofilesctl get` returns a known profile, e.g. `balanced`): +```json +{"text": "󰈐 auto", "tooltip": "Auto fan strategy\nProfile: balanced → medium\nClick to change", "class": "auto"} +``` + +Also verify the side-effect — `fw-fanctrl` should now be on the mapped strategy: +```bash +fw-fanctrl print | grep '^Strategy:' +``` + +Expected: `Strategy: 'medium'` (or `lazy`/`agile` depending on your active power profile). + +- [ ] **Step 4: Verify auto-on error path (unknown profile)** + +Simulate `powerprofilesctl` returning something the mapping doesn't know. We do this by stubbing `powerprofilesctl` via `PATH`: + +```bash +mkdir -p /tmp/fan-stub +cat >/tmp/fan-stub/powerprofilesctl <<'STUB' +#!/bin/bash +echo bogus-profile +STUB +chmod +x /tmp/fan-stub/powerprofilesctl +PATH=/tmp/fan-stub:$PATH bash scripts/fan-profile.sh +``` + +Expected: +```json +{"text": "󰈐 auto (?)", "tooltip": "Auto: power-profiles-daemon unreachable or unknown profile\nClick to change", "class": "auto"} +``` + +- [ ] **Step 5: Verify auto-on error path (powerprofilesctl missing)** + +```bash +cat >/tmp/fan-stub/powerprofilesctl <<'STUB' +#!/bin/bash +exit 1 +STUB +PATH=/tmp/fan-stub:$PATH bash scripts/fan-profile.sh +``` + +Expected: same `auto (?)` output as above (since `PROFILE` ends up empty, `map_profile_to_strategy` returns 1, `MAPPED` is empty, error branch fires). + +Clean up: +```bash +rm -rf /tmp/fan-stub +rm -f "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +``` + +- [ ] **Step 6: Reload waybar to confirm end-to-end** + +Restart waybar so it picks up the script changes (the script file itself is loaded each invocation, so this is mostly to refresh the bar widget visually): +```bash +waybar-restart +``` + +Then enable auto mode without going through wofi: +```bash +mkdir -p "${XDG_STATE_HOME:-$HOME/.local/state}" +touch "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +pkill -RTMIN+9 waybar +``` + +The bar's fan-profile widget should now read `auto` (within ~5 s — the next waybar poll). Hover the widget; the tooltip should show `Profile: `. Switch your power profile (e.g. `powerprofilesctl set performance`) and within ~5 s the tooltip should update to `Profile: performance → agile` and `fw-fanctrl print` should confirm the strategy switched. + +To turn auto off and verify the original behaviour returns: +```bash +rm "${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto" +pkill -RTMIN+9 waybar +``` + +The bar should revert to showing the underlying strategy name. + +- [ ] **Step 7: Commit** + +```bash +git add scripts/fan-profile.sh +git commit -m "fan-profile: render auto mode and reconcile fw-fanctrl on poll" +``` + +--- + +## Final verification + +- [ ] **Auto persists across waybar restarts.** With the state file present, run `waybar-restart`. The bar should come back showing `auto` (within 5 s of restart). +- [ ] **Auto persists across reboot.** Optional but the design promises it. The state file lives in `~/.local/state/` which survives reboot. +- [ ] **Wofi menu shows `auto` first.** Click the fan-profile widget; the wofi menu should list `auto` at the top followed by the seven fw-fanctrl strategies. +- [ ] **Selecting `auto` from the menu works end-to-end.** Pick `auto` → bar updates to `auto` within seconds, `fw-fanctrl print` shows the mapped strategy. +- [ ] **Selecting a non-auto strategy from the menu disables auto.** Pick e.g. `aeolus` → bar updates to `aeolus`, state file is gone (`ls ~/.local/state/fan-profile-auto` returns "No such file or directory"), and subsequent power-profile changes do NOT swap the fan strategy automatically. diff --git a/docs/superpowers/plans/2026-05-02-flameshot-screenshot.md b/docs/superpowers/plans/2026-05-02-flameshot-screenshot.md new file mode 100644 index 0000000..91d2275 --- /dev/null +++ b/docs/superpowers/plans/2026-05-02-flameshot-screenshot.md @@ -0,0 +1,340 @@ +# Flameshot Screenshot Swap 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 bespoke `scripts/screenshot.sh` pipeline with Flameshot, bound to `Mod+Print`. + +**Architecture:** Pure tool swap. Install Flameshot from `extra`, generate a small `~/.config/flameshot/flameshot.ini` from `install.sh` (mirrors the existing `mako/config` generator pattern), point `Mod+Print` at `flameshot gui`, then delete the old script + symlink + install.sh entry. Tasks ordered so `Mod+Print` keeps working at every commit boundary. + +**Tech Stack:** flameshot (Qt6, official Arch `extra`), niri, bash for `install.sh`. + +**Spec:** `docs/superpowers/specs/2026-05-02-flameshot-screenshot-design.md` + +**Verification model:** Bash + system commands. Each task ends with shell verification commands and a one-line user check (e.g. "press `Mod+Print`, confirm Flameshot toolbar appears"). The controller has a display; subagents do not — defer visual checks to the controller. + +--- + +## File Structure + +| File | Change | +|---|---| +| `packages.txt` | add `flameshot` | +| `install.sh` | remove the `ln -sf ".../screenshot.sh" ...` line; add a flameshot.ini generator block (idempotent: only writes if file doesn't exist) | +| `niri/config.kdl` | change `Mod+Print { spawn "screenshot"; }` to `Mod+Print { spawn "flameshot" "gui"; }` | +| `scripts/screenshot.sh` | deleted | +| `~/.local/bin/screenshot` | symlink removed (filesystem only, not git-tracked) | +| `~/.config/flameshot/flameshot.ini` | created on disk by the install.sh block (filesystem only, not git-tracked) | + +--- + +## Task 1: Install Flameshot and add to packages.txt + +After this task, the `flameshot` binary is on PATH and `packages.txt` declares it as a dependency. The old screenshot script and keybind are still wired up — nothing functional has changed yet. + +**Files:** +- Modify: `packages.txt` (add one line) + +- [ ] **Step 1: Install flameshot via the AUR helper** + +```bash +yay -S --needed --noconfirm flameshot +``` + +Expected: package installs (or "is up to date" if already present). If `yay` is unavailable, substitute `paru` or `sudo pacman -S --needed --noconfirm flameshot`. + +- [ ] **Step 2: Verify the binary is on PATH** + +```bash +flameshot --version +``` + +Expected: a version string like `Flameshot v13.3.0 (compiled with Qt 6.x)`. If "command not found", the install failed — escalate. + +- [ ] **Step 3: Add `flameshot` to `packages.txt`** + +Open `packages.txt` and insert `flameshot` in alphabetical-ish order. The existing list is roughly grouped by topic, not strictly sorted. Insert after `fw-fanctrl` (line 39) so it sits with the small one-off utilities cluster: + +``` +fw-fanctrl +satty +starship +flameshot +``` + +(Final order doesn't matter as long as `flameshot` appears once and on its own line. Don't reorder unrelated lines.) + +- [ ] **Step 4: Verify packages.txt is well-formed** + +```bash +grep -c "^flameshot$" packages.txt +``` + +Expected: `1` (exactly one occurrence). + +- [ ] **Step 5: Commit** + +```bash +git add packages.txt +git commit -m "packages: add flameshot" +``` + +--- + +## Task 2: Add flameshot config generator to install.sh and run it + +After this task, `~/.config/flameshot/flameshot.ini` exists with the spec's defaults. Re-running `install.sh` will not clobber the file (the generator has an `[ ! -f ]` guard so user-saved preferences like `savePath` are preserved on subsequent runs). The screenshot keybind still points at the old script. + +**Files:** +- Modify: `install.sh` (insert generator block after the existing mako python block, around line 42) + +- [ ] **Step 1: Insert the flameshot config generator into `install.sh`** + +Find the existing mako block in `install.sh`. It looks like: + +```bash +python3 - <<'PYEOF' +import json, os +c = json.load(open("theme/colors.json")) +config = f"""background-color={c['background']} +text-color={c['foreground']} +border-size=2 +border-color={c['blue']} +default-timeout=4000 +""" +open(os.path.expanduser("~/.config/mako/config"), "w").write(config) +PYEOF +``` + +Immediately *after* the `PYEOF` line that closes the mako block, insert: + +```bash +mkdir -p ~/.config/flameshot +if [ ! -f ~/.config/flameshot/flameshot.ini ]; then + cat > ~/.config/flameshot/flameshot.ini <<'INI' +[General] +disabledTrayIcon=true +showStartupLaunchMessage=false +showHelp=false +copyAndCloseAfterUpload=true +uiColor=#81a2be +contrastUiColor=#1d1f21 +contrastOpacity=190 +INI +fi +``` + +The `[ ! -f ]` guard is required: Flameshot writes runtime state (savePath, last folder) back to this file, so re-running `install.sh` must not overwrite user preferences. + +- [ ] **Step 2: Sanity-check `install.sh` parses as bash** + +```bash +bash -n install.sh +``` + +Expected: no output, exit 0. (`-n` is dry-run syntax check.) + +- [ ] **Step 3: Run the new block manually to create the live config** + +You can either run install.sh in full, or extract just the new block. Cleanest is to run the block standalone: + +```bash +mkdir -p ~/.config/flameshot +if [ ! -f ~/.config/flameshot/flameshot.ini ]; then + cat > ~/.config/flameshot/flameshot.ini <<'INI' +[General] +disabledTrayIcon=true +showStartupLaunchMessage=false +showHelp=false +copyAndCloseAfterUpload=true +uiColor=#81a2be +contrastUiColor=#1d1f21 +contrastOpacity=190 +INI +fi +``` + +- [ ] **Step 4: Verify the file was written with the right contents** + +```bash +cat ~/.config/flameshot/flameshot.ini +``` + +Expected output: +``` +[General] +disabledTrayIcon=true +showStartupLaunchMessage=false +showHelp=false +copyAndCloseAfterUpload=true +uiColor=#81a2be +contrastUiColor=#1d1f21 +contrastOpacity=190 +``` + +- [ ] **Step 5: Verify the guard works (re-run is a no-op)** + +```bash +# Mutate the file slightly +echo "savePath=/home/alex/Pictures" >> ~/.config/flameshot/flameshot.ini +# Re-run the generator block (paste from Step 3) +mkdir -p ~/.config/flameshot +if [ ! -f ~/.config/flameshot/flameshot.ini ]; then + cat > ~/.config/flameshot/flameshot.ini <<'INI' +[General] +disabledTrayIcon=true +showStartupLaunchMessage=false +showHelp=false +copyAndCloseAfterUpload=true +uiColor=#81a2be +contrastUiColor=#1d1f21 +contrastOpacity=190 +INI +fi +# Confirm the savePath line is still present (file was not regenerated) +grep "^savePath=" ~/.config/flameshot/flameshot.ini +``` + +Expected: `savePath=/home/alex/Pictures` (proves the `[ ! -f ]` guard prevented overwrite). + +Clean up the test mutation (so the file matches the spec's defaults again): + +```bash +sed -i '/^savePath=/d' ~/.config/flameshot/flameshot.ini +``` + +- [ ] **Step 6: Commit** + +```bash +git add install.sh +git commit -m "install: generate flameshot.ini with idempotent guard" +``` + +--- + +## Task 3: Repoint `Mod+Print` to Flameshot + +After this task, pressing `Mod+Print` opens Flameshot's GUI (region selector + on-canvas annotation toolbar). The old `scripts/screenshot.sh` is no longer reachable via keybind, but the script and its symlink still exist on disk — Task 4 deletes them. + +**Files:** +- Modify: `niri/config.kdl` (line 87) + +- [ ] **Step 1: Replace the keybind** + +In `niri/config.kdl`, find line 87: + +```kdl + Mod+Print { spawn "screenshot"; } +``` + +Replace with: + +```kdl + Mod+Print { spawn "flameshot" "gui"; } +``` + +Note the two-string form for `spawn` — niri's `spawn` action takes the program and each argument as separate quoted strings. `spawn "flameshot gui"` (single string) would not work; niri would try to exec a binary literally named `flameshot gui`. + +- [ ] **Step 2: Validate the niri config** + +```bash +niri validate +``` + +Expected: exit 0 and a final line `INFO niri: config is valid`. Any "ERROR" or non-zero exit means a syntax issue — fix before continuing. + +- [ ] **Step 3: User-side visual verification (controller only — subagent skip)** + +Niri picks up `config.kdl` changes live; no reload needed. Press `Mod+Print`. Expected: Flameshot's region-selector overlay appears, with a floating toolbar showing drawing tools and Save/Copy/Discard buttons. Cancel out (Esc) without taking a real screenshot. + +If a portal permission prompt appears the first time, allow it — Flameshot uses `xdg-desktop-portal-wlr` to capture under Wayland. + +If `Mod+Print` does nothing or you see "command not found" in `journalctl --user`, the binary isn't on PATH for niri — re-run Task 1, Step 1. + +- [ ] **Step 4: Commit** + +```bash +git add niri/config.kdl +git commit -m "niri: bind Mod+Print to flameshot gui" +``` + +--- + +## Task 4: Delete the old screenshot script, symlink, and install.sh entry + +After this task, all traces of `scripts/screenshot.sh` are gone — script file deleted, `~/.local/bin/screenshot` symlink removed, and the corresponding line in `install.sh` removed so re-running it doesn't recreate the dead symlink. `Mod+Print` continues to work via Flameshot. + +**Files:** +- Delete: `scripts/screenshot.sh` +- Modify: `install.sh` (remove one line) +- Filesystem: `~/.local/bin/screenshot` (remove symlink) + +- [ ] **Step 1: Remove the symlink line from `install.sh`** + +In `install.sh`, find this line (it's inside the `==> Installing scripts` section, around line 61): + +```bash +ln -sf "$(pwd)/scripts/screenshot.sh" ~/.local/bin/screenshot +``` + +Delete the entire line. The surrounding lines (other `ln -sf` calls for the remaining scripts) stay intact. + +- [ ] **Step 2: Sanity-check install.sh still parses** + +```bash +bash -n install.sh +``` + +Expected: no output, exit 0. + +- [ ] **Step 3: Delete the live symlink** + +```bash +rm ~/.local/bin/screenshot +``` + +Expected: silent success. Verify it's gone: + +```bash +ls -la ~/.local/bin/screenshot 2>&1 +``` + +Expected: `cannot access ... No such file or directory`. + +- [ ] **Step 4: Delete the script file from the repo** + +```bash +git rm scripts/screenshot.sh +``` + +Expected: `rm 'scripts/screenshot.sh'`. + +- [ ] **Step 5: Verify nothing else in the repo references the deleted script** + +```bash +grep -rn "screenshot" --include="*.kdl" --include="*.sh" --include="*.jsonc" --include="*.toml" --include="*.fish" --exclude-dir=docs /home/alex/Documents/dotfiles +``` + +Expected: zero hits, or only matches inside markdown/spec files (which the include-glob excludes anyway). If the keybind from Task 3 still says `spawn "screenshot"`, Task 3 didn't land — go back and check. + +- [ ] **Step 6: Confirm `Mod+Print` still works (controller only — subagent skip)** + +Press `Mod+Print`. Flameshot's GUI should still appear (this isn't a regression test — it's just a sanity check that nothing in the cleanup broke the live keybind). + +- [ ] **Step 7: Commit** + +```bash +git add install.sh scripts/screenshot.sh +git commit -m "screenshot: remove old grim/slurp/wofi pipeline (replaced by flameshot)" +``` + +(`git rm` from Step 4 already staged the deletion; this commit picks up both the staged deletion and the install.sh edit.) + +--- + +## Final verification + +- [ ] **Pressing `Mod+Print` opens Flameshot.** Region selector + on-canvas toolbar with drawing tools and Save/Copy/Discard buttons. +- [ ] **`flameshot gui` produces a usable capture.** Pick a region, click Copy → confirm clipboard has the image (`wl-paste --list-types | grep image/png` should show `image/png`). Click Save → file lands wherever Flameshot prompts (first-time prompt expected; pick `~/Pictures`). +- [ ] **No leftover references.** `grep -rn "screenshot\.sh\|/local/bin/screenshot" /home/alex/Documents/dotfiles --exclude-dir=.git --exclude-dir=docs` → no hits. +- [ ] **install.sh is re-runnable without breakage.** Optional: rerun `bash install.sh` and confirm flameshot.ini is preserved (the `[ ! -f ]` guard) and no `~/.local/bin/screenshot` symlink reappears (because the line was removed). +- [ ] **Toolbar colors match the theme.** The Flameshot floating toolbar should show the Tomorrow Night blue accent (`#81a2be`) on the dark background (`#1d1f21`).