Sweep across the three script directories: features/scripts/utils,
core/system/scripts, themes/engine/scripts. 142 of 169 bash scripts
gained `set -e`; 27 already had it; the one Python helper
(nomarchy-haptic-touchpad) was skipped via shebang detection.
Why: bash's default behavior is to continue past a failed command,
which means a script that does "do A; do B; do C" leaves the system
in a half-applied state when B fails - and the user gets no signal.
Several recent fix commits (theme partial-apply, waybar reload race,
installer prewipe silent failures) all trace back to this. set -e
turns silent corruption into a loud abort the user can act on.
The 11 scripts with explicit `|| true` markers stay safe under set -e
because || true coerces the exit to zero; the markers continue to
mean "I deliberately tolerate this failure here."
Deliberate exception: nomarchy-menu runs WITHOUT set -e. It is an
interactive UX loop where action branches do `cmd; back_to <self>`
so a failed action would abort the script under set -e and the menu
would disappear without feedback. Soft-failure - menu re-displays,
user picks again - is the right semantic. Documented inline.
Validation: bash -n on every modified script (zero failures). The
new pre-commit hook (27f5663) was just updated to filter by shebang
so it doesn't try to bash-syntax-check the Python helper - that
filter was uncovered by this sweep.
Risk: set -e can surface latent bugs in scripts that previously
relied on silent continuation. If anything breaks, it's a real bug
that was already broken and is now visible. Easy per-script revert
if any UX glitches show up.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
135 lines
3.9 KiB
Bash
Executable File
135 lines
3.9 KiB
Bash
Executable File
#!/bin/bash
|
|
set -e
|
|
|
|
# Take a screenshot of the whole screen, a specific window, or a user-drawn region.
|
|
# Saves to ~/Pictures by default, but that can be changed via NOMARCHY_SCREENSHOT_DIR or XDG_PICTURES_DIR ENVs.
|
|
# Editor defaults to Satty but can be changed via --editor=<name> or NOMARCHY_SCREENSHOT_EDITOR env
|
|
|
|
[[ -f ~/.config/user-dirs.dirs ]] && source ~/.config/user-dirs.dirs
|
|
OUTPUT_DIR="${NOMARCHY_SCREENSHOT_DIR:-${XDG_PICTURES_DIR:-$HOME/Pictures}}"
|
|
|
|
if [[ ! -d $OUTPUT_DIR ]]; then
|
|
notify-send "Screenshot directory does not exist: $OUTPUT_DIR" -u critical -t 3000
|
|
exit 1
|
|
fi
|
|
|
|
pkill slurp && exit 0
|
|
|
|
SCREENSHOT_EDITOR="${NOMARCHY_SCREENSHOT_EDITOR:-satty}"
|
|
|
|
# Parse --editor flag from any position
|
|
ARGS=()
|
|
for arg in "$@"; do
|
|
if [[ $arg == --editor=* ]]; then
|
|
SCREENSHOT_EDITOR="${arg#--editor=}"
|
|
else
|
|
ARGS+=("$arg")
|
|
fi
|
|
done
|
|
set -- "${ARGS[@]}"
|
|
|
|
open_editor() {
|
|
local filepath="$1"
|
|
if [[ $SCREENSHOT_EDITOR == "satty" ]]; then
|
|
satty --filename "$filepath" \
|
|
--output-filename "$filepath" \
|
|
--actions-on-enter save-to-clipboard \
|
|
--save-after-copy \
|
|
--copy-command 'wl-copy'
|
|
else
|
|
$SCREENSHOT_EDITOR "$filepath"
|
|
fi
|
|
}
|
|
|
|
MODE="${1:-smart}"
|
|
PROCESSING="${2:-slurp}"
|
|
|
|
# accounting for portrait/transformed displays
|
|
JQ_MONITOR_GEO='
|
|
def format_geo:
|
|
.x as $x | .y as $y |
|
|
(.width / .scale | floor) as $w |
|
|
(.height / .scale | floor) as $h |
|
|
.transform as $t |
|
|
if $t == 1 or $t == 3 then
|
|
"\($x),\($y) \($h)x\($w)"
|
|
else
|
|
"\($x),\($y) \($w)x\($h)"
|
|
end;
|
|
'
|
|
|
|
get_rectangles() {
|
|
local active_workspace=$(hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .activeWorkspace.id')
|
|
hyprctl monitors -j | jq -r --arg ws "$active_workspace" "${JQ_MONITOR_GEO} .[] | select(.activeWorkspace.id == (\$ws | tonumber)) | format_geo"
|
|
hyprctl clients -j | jq -r --arg ws "$active_workspace" '.[] | select(.workspace.id == ($ws | tonumber)) | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"'
|
|
}
|
|
|
|
# Select based on mode
|
|
case "$MODE" in
|
|
region)
|
|
hyprpicker -r -z >/dev/null 2>&1 &
|
|
PID=$!
|
|
sleep .1
|
|
SELECTION=$(slurp 2>/dev/null)
|
|
kill $PID 2>/dev/null
|
|
;;
|
|
windows)
|
|
hyprpicker -r -z >/dev/null 2>&1 &
|
|
PID=$!
|
|
sleep .1
|
|
SELECTION=$(get_rectangles | slurp -r 2>/dev/null)
|
|
kill $PID 2>/dev/null
|
|
;;
|
|
fullscreen)
|
|
SELECTION=$(hyprctl monitors -j | jq -r "${JQ_MONITOR_GEO} .[] | select(.focused == true) | format_geo")
|
|
;;
|
|
smart | *)
|
|
RECTS=$(get_rectangles)
|
|
hyprpicker -r -z >/dev/null 2>&1 &
|
|
PID=$!
|
|
sleep .1
|
|
SELECTION=$(echo "$RECTS" | slurp 2>/dev/null)
|
|
kill $PID 2>/dev/null
|
|
|
|
# If the selection area is L * W < 20, we'll assume you were trying to select whichever
|
|
# window or output it was inside of to prevent accidental 2px snapshots
|
|
if [[ $SELECTION =~ ^([0-9]+),([0-9]+)[[:space:]]([0-9]+)x([0-9]+)$ ]]; then
|
|
if ((${BASH_REMATCH[3]} * ${BASH_REMATCH[4]} < 20)); then
|
|
click_x="${BASH_REMATCH[1]}"
|
|
click_y="${BASH_REMATCH[2]}"
|
|
|
|
while IFS= read -r rect; do
|
|
if [[ $rect =~ ^([0-9]+),([0-9]+)[[:space:]]([0-9]+)x([0-9]+) ]]; then
|
|
rect_x="${BASH_REMATCH[1]}"
|
|
rect_y="${BASH_REMATCH[2]}"
|
|
rect_width="${BASH_REMATCH[3]}"
|
|
rect_height="${BASH_REMATCH[4]}"
|
|
|
|
if ((click_x >= rect_x && click_x < rect_x + rect_width && click_y >= rect_y && click_y < rect_y + rect_height)); then
|
|
SELECTION="${rect_x},${rect_y} ${rect_width}x${rect_height}"
|
|
break
|
|
fi
|
|
fi
|
|
done <<<"$RECTS"
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
[[ -z $SELECTION ]] && exit 0
|
|
|
|
FILENAME="screenshot-$(date +'%Y-%m-%d_%H-%M-%S').png"
|
|
FILEPATH="$OUTPUT_DIR/$FILENAME"
|
|
|
|
if [[ $PROCESSING == "slurp" ]]; then
|
|
grim -g "$SELECTION" "$FILEPATH" || exit 1
|
|
wl-copy <"$FILEPATH"
|
|
|
|
(
|
|
ACTION=$(notify-send "Screenshot saved to clipboard and file" "Edit with Super + Alt + , (or click this)" -t 10000 -i "$FILEPATH" -A "default=edit")
|
|
[[ $ACTION == "default" ]] && open_editor "$FILEPATH"
|
|
) &
|
|
else
|
|
grim -g "$SELECTION" - | wl-copy
|
|
fi
|