feat(desktop): resolve media output paths through XDG user-dirs

Add a modules.desktop.userdirs module that declares the XDG user directories,
and route the recorder and screenshot tools through xdg-user-dir so their
output folders (Videos/Recordings, Pictures/Screenshots) follow one relocatable
source instead of a hardcoded $HOME path.
This commit was merged in pull request #21.
This commit is contained in:
2026-07-22 23:11:16 -04:00
parent 0e92de7eea
commit ab89ba8391
5 changed files with 28 additions and 9 deletions

View File

@@ -34,7 +34,9 @@ No audio capture and no full-screen recording variant.
- **No waybar `window-rewrite` icon.** - **No waybar `window-rewrite` icon.**
wf-recorder is headless and slurp is a transient selection overlay, so neither owns a workspace window and the per-application icon convention does not apply. wf-recorder is headless and slurp is a transient selection overlay, so neither owns a workspace window and the per-application icon convention does not apply.
- **Recordings land in `~/Videos/Recordings`**, timestamped `recording-<date>.mp4`, with the directory created on first capture. - **Output paths follow XDG user-dirs.**
A new `modules.desktop.userdirs` declares the XDG user directories (home-manager `xdg.userDirs`), and the recorder resolves its base with `xdg-user-dir VIDEOS`, writing timestamped `recording-<date>.mp4` under `<Videos>/Recordings` (created on first capture).
The screenshot module (task 0028) was aligned to the same convention (`xdg-user-dir PICTURES``<Pictures>/Screenshots`), so relocating a directory is a one-line change to `xdg.userDirs` rather than an edit in each tool.
- **Live capture is the irreducible manual step.** - **Live capture is the irreducible manual step.**
The build is green, the config parses under `Hyprland --verify-config`, and the indicator's idle/recording transitions are verified against a stand-in process; exercising a real slurp selection and wf-recorder capture needs a running session. The build is green, the config parses under `Hyprland --verify-config`, and the indicator's idle/recording transitions are verified against a stand-in process; exercising a real slurp selection and wf-recorder capture needs a running session.

View File

@@ -19,6 +19,7 @@ in
modules.desktop.rofi.enable = lib.mkDefault true; modules.desktop.rofi.enable = lib.mkDefault true;
modules.desktop.screenshot.enable = lib.mkDefault true; modules.desktop.screenshot.enable = lib.mkDefault true;
modules.desktop.terminal.enable = lib.mkDefault true; modules.desktop.terminal.enable = lib.mkDefault true;
modules.desktop.userdirs.enable = lib.mkDefault true;
modules.desktop.theming.enable = lib.mkDefault true; modules.desktop.theming.enable = lib.mkDefault true;
modules.desktop.waybar.enable = lib.mkDefault true; modules.desktop.waybar.enable = lib.mkDefault true;
modules.desktop.audio.enable = lib.mkDefault true; modules.desktop.audio.enable = lib.mkDefault true;

View File

@@ -16,8 +16,7 @@ let
pkill = "${pkgs.procps}/bin/pkill"; pkill = "${pkgs.procps}/bin/pkill";
date = "${pkgs.coreutils}/bin/date"; date = "${pkgs.coreutils}/bin/date";
mkdir = "${pkgs.coreutils}/bin/mkdir"; mkdir = "${pkgs.coreutils}/bin/mkdir";
xdgUserDir = "${pkgs.xdg-user-dirs}/bin/xdg-user-dir";
recDir = "$HOME/Videos/Recordings";
# A single key both starts and stops. # A single key both starts and stops.
# A running wf-recorder is stopped with SIGINT so it finalises the file. # A running wf-recorder is stopped with SIGINT so it finalises the file.
@@ -32,8 +31,9 @@ let
fi fi
region=$(${slurp}) || exit 0 region=$(${slurp}) || exit 0
${mkdir} -p "${recDir}" dir="$(${xdgUserDir} VIDEOS)/Recordings"
file="${recDir}/recording-$(${date} +%Y%m%d-%H%M%S).mp4" ${mkdir} -p "$dir"
file="$dir/recording-$(${date} +%Y%m%d-%H%M%S).mp4"
${notify-send} -a "Screen recording" "Recording started" "Region capture, no audio." ${notify-send} -a "Screen recording" "Recording started" "Region capture, no audio."
${wf-recorder} -g "$region" -f "$file" ${wf-recorder} -g "$region" -f "$file"

View File

@@ -13,8 +13,7 @@ let
grimblast = "${pkgs.grimblast}/bin/grimblast"; grimblast = "${pkgs.grimblast}/bin/grimblast";
satty = "${pkgs.satty}/bin/satty"; satty = "${pkgs.satty}/bin/satty";
wl-copy = "${pkgs.wl-clipboard}/bin/wl-copy"; wl-copy = "${pkgs.wl-clipboard}/bin/wl-copy";
xdgUserDir = "${pkgs.xdg-user-dirs}/bin/xdg-user-dir";
shotDir = "$HOME/Pictures/Screenshots";
# satty is the annotation step, and its copy action is set to save as well, # 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 # so a single keystroke through it lands the shot in both the clipboard and a
@@ -22,10 +21,11 @@ let
capture = capture =
target: target:
pkgs.writeShellScript "screenshot-${target}" '' pkgs.writeShellScript "screenshot-${target}" ''
mkdir -p ${shotDir} dir="$(${xdgUserDir} PICTURES)/Screenshots"
mkdir -p "$dir"
${grimblast} save ${target} - \ ${grimblast} save ${target} - \
| ${satty} --filename - \ | ${satty} --filename - \
--output-filename "${shotDir}/screenshot-%Y%m%d-%H%M%S.png" \ --output-filename "$dir/screenshot-%Y%m%d-%H%M%S.png" \
--copy-command ${wl-copy} \ --copy-command ${wl-copy} \
--save-after-copy \ --save-after-copy \
--early-exit \ --early-exit \

View File

@@ -0,0 +1,16 @@
{ config, lib, ... }:
# Declares the XDG user directories, so a tool that queries them lands in a
# well-known folder a host can relocate from one place.
let
cfg = config.modules.desktop.userdirs;
user = config.user.name;
in
{
options.modules.desktop.userdirs.enable = lib.mkEnableOption "XDG user directories";
config = lib.mkIf cfg.enable {
# enable writes ~/.config/user-dirs.dirs from the option defaults, which
# xdg-user-dir then reads.
home-manager.users.${user}.xdg.userDirs.enable = true;
};
}