From 9963a0dbe41602dfa0683583f80294f7b74c4c30 Mon Sep 17 00:00:00 2001 From: alexion Date: Wed, 22 Jul 2026 22:04:38 -0400 Subject: [PATCH] feat(desktop): add screenshot capture (task 0028) Add a screenshot module wiring grimblast (grim + slurp) through the satty annotation editor, enabled by the desktop aggregator. Region, active-window, and full-screen captures each open in satty and, on confirm, land in both the clipboard and a dated file under ~/Pictures/Screenshots. Bound to Print / Shift+Print / Ctrl+Print rather than the spec's Super+L family, whose keys task 0021 already holds for hjkl focus and window movement. --- .claude/tasks/0028-screenshot-capture.md | 24 ++++++++++-- modules/desktop/desktop.nix | 1 + modules/desktop/screenshot.nix | 50 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 modules/desktop/screenshot.nix diff --git a/.claude/tasks/0028-screenshot-capture.md b/.claude/tasks/0028-screenshot-capture.md index cc0dadd..a5a4839 100644 --- a/.claude/tasks/0028-screenshot-capture.md +++ b/.claude/tasks/0028-screenshot-capture.md @@ -13,7 +13,23 @@ Bind region on `Super+L`, active window on `Super+Shift+L`, and full screen on ` ## Acceptance criteria -- [ ] A screenshot module exists in the desktop group and is enabled by the aggregator. -- [ ] Region, active-window, and full-screen captures work, each opening in satty and exporting to both clipboard and file. -- [ ] Captures are bound on `Super+L`, `Super+Shift+L`, and `Super+Ctrl+L`. -- [ ] neogaia builds green under `nix flake check`. +- [x] A screenshot module exists in the desktop group and is enabled by the aggregator. +- [x] Region, active-window, and full-screen captures work, each opening in satty and exporting to both clipboard and file. +- [-] Captures are bound on `Super+L`, `Super+Shift+L`, and `Super+Ctrl+L`. +- [x] neogaia builds green under `nix flake check`. + +## Implementation Notes + +- **Screenshot binds moved off `Super+L` to the Print key family.** + Task 0021 already binds `Super+L`, `Super+Shift+L`, and `Super+Alt+L` to the `hjkl` focus, window-move, and resize actions for the right direction, so the spec's literal `Super+L` / `Super+Shift+L` / `Super+Ctrl+L` screenshot binds are a direct three-way collision with core navigation. + Two `bind=` lines for one combo don't coexist in Hyprland (one silently shadows the other, and the winner across modules isn't even deterministic), so the collision had to be broken. + With the operator's confirmation, the region/window/full captures are bound to `Print` / `Shift+Print` / `Ctrl+Print`, preserving the plain/Shift/Ctrl modifier pattern while leaving the `hjkl` scheme intact. + The spec's own keybind table is internally inconsistent here (it lists `Super+L` for both movement and screenshots), so this resolves a contradiction in the source rather than departing from a settled design. + +- **Capture pipeline.** + `grimblast save -` captures to stdout and pipes into satty, whose copy action is configured with `--copy-command wl-copy --save-after-copy`, so one confirmation lands the shot in both the clipboard and a dated file under `~/Pictures/Screenshots`. + `--actions-on-enter save-to-clipboard` makes Enter trigger that path and `--early-exit` closes satty afterwards. + The full end-to-end capture is the irreducible manual step the spec calls out (exercised in a live session); the module builds green and the pipeline and flags are verified against satty 0.21.1. + +- **No waybar icon for satty.** + The repo convention adds a `window-rewrite` mapping for each graphical application, but satty is a transient floating annotation window rather than a window that lives on a workspace, so at the operator's direction it gets no workspace glyph. diff --git a/modules/desktop/desktop.nix b/modules/desktop/desktop.nix index 18c9235..c46578f 100644 --- a/modules/desktop/desktop.nix +++ b/modules/desktop/desktop.nix @@ -16,6 +16,7 @@ in modules.desktop.login.enable = lib.mkDefault true; modules.desktop.mako.enable = lib.mkDefault true; modules.desktop.rofi.enable = lib.mkDefault true; + modules.desktop.screenshot.enable = lib.mkDefault true; modules.desktop.terminal.enable = lib.mkDefault true; modules.desktop.theming.enable = lib.mkDefault true; modules.desktop.waybar.enable = lib.mkDefault true; diff --git a/modules/desktop/screenshot.nix b/modules/desktop/screenshot.nix new file mode 100644 index 0000000..1cef31d --- /dev/null +++ b/modules/desktop/screenshot.nix @@ -0,0 +1,50 @@ +{ + config, + lib, + pkgs, + ... +}: +# Keyboard-driven screenshots that open in satty for annotation, then land in +# both the clipboard and a dated file. +let + cfg = config.modules.desktop.screenshot; + user = config.user.name; + + grimblast = "${pkgs.grimblast}/bin/grimblast"; + satty = "${pkgs.satty}/bin/satty"; + wl-copy = "${pkgs.wl-clipboard}/bin/wl-copy"; + + shotDir = "$HOME/Pictures/Screenshots"; + + # satty is the annotation step, and its copy action is set to save as well, + # so a single keystroke through it lands the shot in both the clipboard and a + # file. + capture = + target: + pkgs.writeShellScript "screenshot-${target}" '' + mkdir -p ${shotDir} + ${grimblast} save ${target} - \ + | ${satty} --filename - \ + --output-filename "${shotDir}/screenshot-%Y%m%d-%H%M%S.png" \ + --copy-command ${wl-copy} \ + --save-after-copy \ + --early-exit \ + --actions-on-enter save-to-clipboard + ''; +in +{ + options.modules.desktop.screenshot.enable = lib.mkEnableOption "screenshots via grimblast and satty"; + + config = lib.mkIf cfg.enable { + home-manager.users.${user} = { + # Print with plain/Shift/Ctrl for region/window/full. + # Super+L, the spec's chosen key, is already the hjkl focus and movement + # bind, so screenshots take the Print key instead. + wayland.windowManager.hyprland.settings.bind = [ + ", Print, exec, ${capture "area"}" + "SHIFT, Print, exec, ${capture "active"}" + "CTRL, Print, exec, ${capture "screen"}" + ]; + }; + }; +}