diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f0ca349 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +jobs: + test: + runs-on: macos-15 + steps: + - uses: actions/checkout@v6 + - uses: jdx/mise-action@v4 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Verify shell tasks + run: | + mise run test + codebase lint:mise-settings "$PWD" + codebase lint:bats-test-task "$PWD" + codebase lint:bats-test-helper "$PWD" + codebase lint:mcr-scope "$PWD" + codebase lint:or-true "$PWD" + codebase lint:shellcheck "$PWD" + - name: Verify Swift package + run: swift test + - name: Verify generated README + run: | + mise run readme + git diff --exit-code README.md diff --git a/.mise/tasks/ai b/.mise/tasks/ai index 02016f9..af8b6a5 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 @@ -411,6 +411,7 @@ if [ -d "$OUTPUT_DIR" ] && [ "$(ls -A "$OUTPUT_DIR"/*.png 2>/dev/null)" ]; then echo "" echo "| File | Size | Modified |" echo "|------|------|----------|" + # shellcheck disable=SC2012 # Human-readable listing of tool-generated wallpaper files. ls -lh "$OUTPUT_DIR"/*.png 2>/dev/null | awk '{print "| " $NF " | " $5 " | " $6 " " $7 " |"}' | sed "s|$OUTPUT_DIR/||" echo "" else diff --git a/.mise/tasks/apply/_default b/.mise/tasks/apply/_default index c2ddbe5..e49f45b 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 @@ -77,7 +67,7 @@ SCREEN_H=$(echo "$SCREEN_RES" | cut -d'x' -f2) else DIFF=$((SPACE_COUNT - CONFIG_COUNT)) gum style --foreground 245 "You have $DIFF extra space(s). Either:" - echo " • Add more workspaces to your config: mise run config:edit" + echo " • Add more workspaces to your config: wp config edit" echo " • Or remove spaces: Mission Control → hover space → click X" fi echo "" @@ -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")" @@ -152,11 +142,15 @@ SCREEN_H=$(echo "$SCREEN_RES" | cut -d'x' -f2) PREV_LAST_SPACE="" for i in "${!PREV_WINS[@]}"; do if [ "${PREV_SPACES[$i]}" != "$PREV_LAST_SPACE" ]; then - butthair spaces:goto -- "${PREV_SPACES[$i]}" < /dev/null 2>/dev/null || true + if ! butthair spaces:goto -- "${PREV_SPACES[$i]}" < /dev/null 2>/dev/null; then + : # Window cleanup is best-effort; continue closing what we can. + fi sleep 0.3 PREV_LAST_SPACE="${PREV_SPACES[$i]}" fi - butthair windows:close -- "${PREV_WINS[$i]}" < /dev/null 2>/dev/null || true + if ! butthair windows:close -- "${PREV_WINS[$i]}" < /dev/null 2>/dev/null; then + : # Window may already be gone. + fi done fi : > "$STATE_FILE" @@ -165,7 +159,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..3024c94 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." @@ -32,16 +30,22 @@ for i in "${!WINS[@]}"; do WIN_ID="${WINS[$i]}" if [ "$SPACE_NUM" != "$LAST_SPACE" ]; then - butthair spaces:goto -- "$SPACE_NUM" < /dev/null 2>/dev/null || true + if ! butthair spaces:goto -- "$SPACE_NUM" < /dev/null 2>/dev/null; then + : # Window may already be gone; continue best-effort cleanup. + fi sleep 0.3 LAST_SPACE="$SPACE_NUM" fi - RESULT=$(butthair windows:close -- "$WIN_ID" < /dev/null 2>&1) || true + if ! RESULT=$(butthair windows:close -- "$WIN_ID" < /dev/null 2>&1); then + : # Window may already be closed; print butthair's response below. + fi echo " $RESULT" done -butthair spaces:goto -- "$CURRENT_SPACE" < /dev/null 2>/dev/null || true +if ! butthair spaces:goto -- "$CURRENT_SPACE" < /dev/null 2>/dev/null; then + : # Restore is best-effort; cleanup state below regardless. +fi rm -f "$STATE_FILE" echo "Done." 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