From 935f074d9a0031c5a337421f81b2080ca34ba419 Mon Sep 17 00:00:00 2001 From: ikma Date: Wed, 15 Apr 2026 16:57:59 -0400 Subject: [PATCH 1/8] feat: add lib/common.sh with shared constants and helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract repeated patterns from task scripts: - Path constants (WALLPAPERS_OUTPUT_DIR, CONFIG_FILE, STATE_DIR) - detect_screen_resolution() — replaces 5 copies of system_profiler pipeline - resolution_width/height() — replaces cut -d'x' pattern - require_command() — replaces ad-hoc command -v checks - require_config() — replaces ad-hoc config file checks --- lib/common.sh | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 lib/common.sh diff --git a/lib/common.sh b/lib/common.sh new file mode 100644 index 0000000..00ecc03 --- /dev/null +++ b/lib/common.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Shared constants and helpers for wallpapers tasks. + +# Paths +WALLPAPERS_OUTPUT_DIR="$HOME/.local/share/wallpapers" +WALLPAPERS_CONFIG_DIR="$HOME/.config/wallpapers" +WALLPAPERS_CONFIG_FILE="$WALLPAPERS_CONFIG_DIR/config.json" +WALLPAPERS_STATE_DIR="$HOME/.local/state/wallpapers" + +# Detect the primary screen resolution. +# Outputs WxH (e.g. "3024x1964"). Returns 1 if detection fails. +detect_screen_resolution() { + local res + res=$(system_profiler SPDisplaysDataType 2>/dev/null \ + | grep -i "Resolution:" \ + | head -1 \ + | sed 's/.*: //' \ + | sed 's/ Retina//' \ + | sed 's/ //g') + if [[ -z "$res" ]]; then + echo "error: could not detect screen resolution" >&2 + return 1 + fi + echo "$res" +} + +# Extract width from a WxH resolution string. +resolution_width() { + echo "${1%%x*}" +} + +# Extract height from a WxH resolution string. +resolution_height() { + echo "${1#*x}" +} + +# Check that a command exists, exit with a styled error if not. +# Usage: require_command [install hint] +require_command() { + local cmd="$1" + local hint="${2:-}" + if ! command -v "$cmd" &>/dev/null; then + if command -v gum &>/dev/null; then + gum style --foreground 196 "❌ $cmd not found.${hint:+ $hint}" + else + echo "error: $cmd not found.${hint:+ $hint}" >&2 + fi + exit 1 + fi +} + +# Check that the config file exists, exit with error if not. +require_config() { + if [[ ! -f "$WALLPAPERS_CONFIG_FILE" ]]; then + if command -v gum &>/dev/null; then + gum style --foreground 196 "❌ Config not found. Run: wallpapers config init" + else + echo "error: config not found at $WALLPAPERS_CONFIG_FILE" >&2 + fi + exit 1 + fi +} From 1efddf0c46a879677894f34aac008475ff41a9d8 Mon Sep 17 00:00:00 2001 From: ikma Date: Wed, 15 Apr 2026 16:58:06 -0400 Subject: [PATCH 2/8] refactor: use lib/common.sh across all task scripts - Source common.sh for shared paths and helpers - Replace inline system_profiler with detect_screen_resolution() - Replace ad-hoc command/config checks with require_command/require_config - Replace BASH_SOURCE gymnastics with $MISE_CONFIG_ROOT - Add #USAGE headers to cli task - Update ai task reference from CLAUDE.md to README.md --- .mise/tasks/ai | 2 +- .mise/tasks/apply/_default | 28 +++++++++------------------- .mise/tasks/apply/undo | 8 +++----- .mise/tasks/clean | 7 +++---- .mise/tasks/cli | 8 ++++++++ .mise/tasks/config/edit | 12 +++--------- .mise/tasks/config/init | 16 +++++++--------- .mise/tasks/generate | 11 +++++------ .mise/tasks/goto | 23 +++++++---------------- .mise/tasks/hammerspoon/config | 2 +- .mise/tasks/info/list | 9 ++++----- .mise/tasks/info/resolution | 6 ++++-- .mise/tasks/open | 7 +++---- .mise/tasks/quick | 13 ++++++------- .mise/tasks/shell | 3 +-- .mise/tasks/tutorial/apply | 5 +++-- .mise/tasks/tutorial/generate | 5 +++-- .mise/tasks/tutorial/navigate | 5 +++-- 18 files changed, 74 insertions(+), 96 deletions(-) diff --git a/.mise/tasks/ai b/.mise/tasks/ai index 02016f9..b26f70d 100755 --- a/.mise/tasks/ai +++ b/.mise/tasks/ai @@ -5,7 +5,7 @@ cat << 'EOF' # Wallpapers > llms.txt for AI agents helping users USE this tool. -> For agents working ON the codebase, see CLAUDE.md. +> For agents working ON the codebase, see the Development section in README.md. ## Overview diff --git a/.mise/tasks/apply/_default b/.mise/tasks/apply/_default index c2ddbe5..62ed58a 100755 --- a/.mise/tasks/apply/_default +++ b/.mise/tasks/apply/_default @@ -7,20 +7,10 @@ #USAGE flag "--apps" help="Only launch and position apps" #USAGE flag "--clean" help="Close previous app windows before applying" set -e +source "${MISE_CONFIG_ROOT}/lib/common.sh" -OUTPUT_DIR="$HOME/.local/share/wallpapers" -CONFIG_FILE="$HOME/.config/wallpapers/config.json" - -if [ ! -f "$CONFIG_FILE" ]; then - gum style --foreground 196 "❌ Config not found. Run: mise run config:init" - exit 1 -fi - -# Require butthair (manages Hammerspoon backend) -if ! command -v butthair &>/dev/null; then - gum style --foreground 196 "❌ butthair not found. Install with: shiv install butthair" - exit 1 -fi +require_config +require_command butthair "Install with: shiv install butthair" # Require Hammerspoon via butthair APP_PATH="/Applications/Hammerspoon.app" @@ -51,13 +41,13 @@ cache_bust() { } # Auto-detect screen resolution -SCREEN_RES=$(system_profiler SPDisplaysDataType | grep -i "Resolution:" | head -1 | sed 's/.*: //' | sed 's/ Retina//' | sed 's/ //g') -SCREEN_W=$(echo "$SCREEN_RES" | cut -d'x' -f1) -SCREEN_H=$(echo "$SCREEN_RES" | cut -d'x' -f2) +SCREEN_RES=$(detect_screen_resolution) +SCREEN_W=$(resolution_width "$SCREEN_RES") +SCREEN_H=$(resolution_height "$SCREEN_RES") # Apply to all spaces { - CONFIG_COUNT=$(jq 'if .spaces then .spaces | length elif .workspaces then .workspaces | length else 0 end' "$CONFIG_FILE") + CONFIG_COUNT=$(jq 'if .spaces then .spaces | length elif .workspaces then .workspaces | length else 0 end' "$WALLPAPERS_CONFIG_FILE") SPACE_COUNT=$(butthair spaces:list -- -j 2>/dev/null | jq 'length') if [ -z "$SPACE_COUNT" ] || [ "$SPACE_COUNT" = "0" ]; then @@ -131,7 +121,7 @@ SCREEN_H=$(echo "$SCREEN_RES" | cut -d'x' -f2) USABLE_H=$(echo "$SCREEN_JSON" | jq '.usable.h') USABLE_Y=$(echo "$SCREEN_JSON" | jq '.usable.y') - GAP=$(jq '.defaults.gap // 8' "$CONFIG_FILE") + GAP=$(jq '.defaults.gap // 8' "$WALLPAPERS_CONFIG_FILE") STATE_FILE="$HOME/.local/share/wallpapers/.apply-state" mkdir -p "$(dirname "$STATE_FILE")" @@ -165,7 +155,7 @@ SCREEN_H=$(echo "$SCREEN_RES" | cut -d'x' -f2) for s in $(seq 0 $((CONFIG_COUNT - 1))); do SPACE_NUM=$((s + 1)) - ZONES_JSON=$(jq -c ".spaces[$s].zones" "$CONFIG_FILE") + ZONES_JSON=$(jq -c ".spaces[$s].zones" "$WALLPAPERS_CONFIG_FILE") ZONE_COUNT=$(echo "$ZONES_JSON" | jq 'length') # Skip spaces with no apps diff --git a/.mise/tasks/apply/undo b/.mise/tasks/apply/undo index 40d6fe6..85e596e 100755 --- a/.mise/tasks/apply/undo +++ b/.mise/tasks/apply/undo @@ -1,12 +1,10 @@ #!/usr/bin/env bash #MISE description="Close windows created by the last 'apply --apps'" +source "${MISE_CONFIG_ROOT}/lib/common.sh" -STATE_FILE="$HOME/.local/share/wallpapers/.apply-state" +STATE_FILE="$WALLPAPERS_OUTPUT_DIR/.apply-state" -if ! command -v butthair &>/dev/null; then - gum style --foreground 196 "❌ butthair not found. Install with: shiv install butthair" - exit 1 -fi +require_command butthair "Install with: shiv install butthair" if [ ! -f "$STATE_FILE" ] || [ ! -s "$STATE_FILE" ]; then echo "Nothing to undo." diff --git a/.mise/tasks/clean b/.mise/tasks/clean index 6ff79f3..bd09888 100755 --- a/.mise/tasks/clean +++ b/.mise/tasks/clean @@ -1,10 +1,9 @@ #!/usr/bin/env bash #MISE description="Remove all generated wallpapers" +source "${MISE_CONFIG_ROOT}/lib/common.sh" -OUTPUT_DIR="$HOME/.local/share/wallpapers" - -if gum confirm "Delete all generated wallpapers from $OUTPUT_DIR?"; then - rm -rf "$OUTPUT_DIR"/*.png +if gum confirm "Delete all generated wallpapers from $WALLPAPERS_OUTPUT_DIR?"; then + rm -rf "$WALLPAPERS_OUTPUT_DIR"/*.png gum style --foreground 46 "✅ Cleaned wallpapers directory" else gum style --foreground 244 "Cancelled" diff --git a/.mise/tasks/cli b/.mise/tasks/cli index 46c3337..21432b2 100755 --- a/.mise/tasks/cli +++ b/.mise/tasks/cli @@ -1,3 +1,11 @@ #!/usr/bin/env bash #MISE description="Run generator directly with arguments" +#USAGE arg "" help="Workspace name" +#USAGE flag "-d --description " help="Subtitle text" +#USAGE flag "-r --resolution " help="Preset: 1080p, 1440p, 4k, macbook-14, macbook-16, imac-24, studio-display" +#USAGE flag "--width " help="Custom width" +#USAGE flag "--height " help="Custom height" +#USAGE flag "--bg-color " help="Background hex color" +#USAGE flag "--text-color " help="Text hex color" +#USAGE flag "--style