All notable changes to gh-wallpaper are recorded here. The format is loosely based on Keep a Changelog, and the project follows semver.
- Runaway wallpaper cache. Every refresh wrote a uniquely-named PNG and handed macOS a brand-new path, which macOS's image-wallpaper extension cached a full copy of — forever — and listed under the Wallpaper picker's "Your Photos". Over months this grew without bound (hundreds of near-identical thumbnails, up to ~1 GB of orphaned cache). Renders now ping-pong between two stable image slots per display (
-a/-b): macOS still sees a path change each tick (so the set isn't a silent no-op — it caches the desktop image by path), but the history caps at two entries per display instead of growing forever. Applied consistently across the daemon, the setup wizard, and therefresh/ theme-apply paths, and the on-disk cleanup now prunes every stale wallpaper file rather than only those matching a currently-connected display.
- Creating a theme is now the bare
gh-wallpaper <name>front door. Type a name that isn't a known theme —gh-wallpaper my-theme— and it asksCreate a custom theme called 'my-theme'? [y/N], opening the visual editor on yes. This replacesgh-wallpaper themes new <name>, which is removed: on macOS it now prints a one-line redirect to the new syntax; on Linux (no visual editor) it still points at the export/edit/import JSON workflow. Known theme names still apply immediately as before, andtheme <id> --edit/editare unchanged. - Seed a new theme from any existing base directly from the command line:
gh-wallpaper my-theme --from dracula(also works asthemes new's successor). The editor's "Apply defaults from…" menu still works for re-seeding live.
- Typo suggestions. A bare name that's a near-miss of a real theme (e.g.
darcula) now surfacesDid you mean 'dracula'?before offering to create it, so mistyped applies don't silently become theme-creation prompts. - Non-interactive callers (pipes, CI) keep the previous
unknown commandbehavior — the create prompt only appears on an interactive terminal, so scripts never hang or get a surprise editor window.
gh-wallpaper edit— opens the visual editor for your active theme with no arguments needed.gh-wallpaper <theme-name>shorthand now works for custom themes too, not just built-ins.gh-wallpaper themewith no arguments prints the current theme plus the full list.gh-wallpaper dark/lightas short aliases for thegithub-*themes.
- Unknown commands now show a clear error instead of silently failing.
gh-wallpaper init— interactive Linux post-install setup. Walks through username / theme / canvas / wallpaper-setter, writes a systemd drop-in (~/.config/systemd/user/gh-wallpaper.service.d/override.conf), enables the timer, and renders once. The Linux analog of the macOS wizard. Auto-detects canvas (swaymsg / wlr-randr / xrandr) and the wallpaper-setter ($XDG_CURRENT_DESKTOP). Re-runnable;--no-enableskips systemctl for containers/CI/scripts.gh-wallpaper diagnosenow prints the override drop-in path on Linux so users can see at a glance whetherinithas been run.- Linux CI gains a smoke step that runs
init --no-enableagainst a piped input and asserts the drop-in lands on disk with the right values.
- Shim/unit path mismatch: the wallpaper-setter shims (
set-wallpaper-{gnome,kde,xfce,feh,swaybg}.sh) defaulted to~/.cache/gh-wallpaper/heatmap.png, but the systemd unit writes towallpaper.png— so the README's no-argExecStartPost=…shim.shpattern silently set a non-existent file on every DE. Unified onwallpaper.pngacross all 5 shims andheatmap.sh. Paths.linuxHome():NSHomeDirectory()reads from the passwd database (getpwuid), which can disagree with$HOMEin containers, sandboxes, and sudo-like contexts. systemd's%halways uses$HOME. Result before the fix:initcould writeoverride.confto/root/.config/...while systemd looked in$HOME/.config/...— silent mismatch. Now Linux paths prefer$HOMEand fall back toNSHomeDirectory(). macOS unchanged.
install.shtrailer +contrib/linux/README.mdnow point users atgh-wallpaper initinstead ofsystemctl --user edit gh-wallpaper.service. The "run the wizard" hint moved out of the Linux section in the top-level README (the wizard is macOS-only).
- Linux support (beta). The Swift library now compiles on Linux.
gh-wallpaper render --user X --canvas WxH --output PATHproduces the same PNG (heatmap + DESIGN BUILD SHIP headline) as the macOS app, with all 11 themes and custom JSON themes available. A newcontrib/linux/install.shhandles dependencies (resvg, Swift toolchain), builds from source, and drops the systemd user units. Existing per-DE wallpaper-setter shims (GNOME / KDE / XFCE / sway / Hyprland / X11+feh) carry over unchanged. The macOS-only daemon, visual editor, and multi-display rendering are out of scope for the Linux beta — Linux runs as a render-only binary driven by the systemd timer. gh-wallpaper renderaccepts--canvas WxHand--output PATHon both platforms — explicit overrides for users who want a one-off render at a specific size.gh-wallpaper diagnoseis now Linux-aware, emitting distro / desktop / session-type / XDG paths / systemd unit status — a copy-pasteable block intended for bug reports. Issue template at.github/ISSUE_TEMPLATE/linux-bug.mdrequires this output.- Linux CI workflow (
.github/workflows/linux-ci.yml) builds againstswift:5.10-jammy, runsswift test(snapshot byte-equality must match macOS), and smoke-renders against octocat. Paths.swiftnow follows XDG on Linux ($XDG_CONFIG_HOME/$XDG_CACHE_HOME/$XDG_STATE_HOME); macOS layout is unchanged.
contrib/linux/gh-wallpaper.servicenow invokes the Swift binary (gh-wallpaper render) instead ofheatmap.sh. The bash recipe is preserved as a no-toolchain fallback (no headline, 5 themes), demoted to a "Fallback" section in the Linux README.SVGBuilder.round3andHeadline.fmtnow passLocale(identifier: "en_US_POSIX")toString(format:)— this is a no-op on macOS (snapshot bytes unchanged) but defends againstswift-corelibs-foundationhonoringLC_NUMERICon Linux, which would emit comma-decimals and break SVG.
- Five new built-in themes:
tokyo-night,dracula,nord,gruvbox-dark, andcatppuccin-mocha. - Custom themes: drop a JSON file in
~/Library/Application Support/gh-wallpaper/themes/, then rungh-wallpaper theme <id>. Schema mirrors the built-inThemestruct (id,background,cellRamp[5 colors],headlineColor, optionalbackgroundIsGradient+gradientSVG). Validation rejects malformed files with a logged warning rather than crashing the daemon. See README for the schema. - Visual theme editor — opens a native macOS SwiftUI window with system color pickers (one per slot), a dim slider, an image-background picker, and a live preview that re-renders from the same SVG pipeline the daemon uses. Three entry points:
gh-wallpaper themes new <name>— start from scratch; the editor's "Apply defaults from…" menu can paste any existing theme's palette onto the draft live.gh-wallpaper theme <id> --edit— edit a custom in place, or fork a built-in (built-ins force a rename on save since they're immutable).- The Save button label updates as you type ("Save as custom theme 'my-theme'") so the commit moment is unambiguous. Save & apply applies the new theme as your wallpaper immediately. The editor only loads on these subcommands; the daemon stays headless.
- New
gh-wallpaper themes <verb>CRUD:delete <name>,export <name>(JSON to stdout, works for built-ins too),import(read JSON from stdin).themes export dracula > my-dracula.jsonthen editing +themes import < my-dracula.jsonis the full share-a-theme loop. - Image backgrounds — custom themes can carry
backgroundImagePath(PNG/JPEG, relative to the theme JSON or absolute) andbackgroundDimAlpha(0–1 overlay for contrast). The image is rendered via SVG<image>and the daemon hashes the file's mtime + size so swapping the photo on disk invalidates the cache automatically. The editor's image picker copies your chosen file intothemes/images/<theme-id>.<ext>so the theme stays portable. - Daily install-analytics snapshot:
docs/install-stats.ndjsonaccumulates per-day GitHub release-asset bottle download counts;docs/INSTALL_STATS.mdand a README block are regenerated from it. Server-side only — no client telemetry was added. Seedocs/INSTALL_STATS.mdfor methodology and the counting-model caveats. - Linux community recipe —
contrib/linux/ships a ~180-line shell renderer (curl+rsvg-convert), a systemd user oneshot + hourly timer, and per-DE wallpaper-setter snippets (GNOME/KDE/XFCE/Sway/Hyprland/X11). Heatmap-only (no headline), 5 curated themes (github-dark,github-light,catppuccin-mocha,dracula,tokyo-night), render-only (BYO wallpaper-set per DE). Best-effort, community-maintained — seecontrib/linux/README.md. The macOS app and release pipeline are unchanged.
- Themes
sunsetandforest— capping built-ins at 12 to keep the picker manageable. Users on those ids will fall back togithub-darkautomatically on the next refresh; switch withgh-wallpaper theme <id>to pick a replacement (paperis the closest warm-light alternative;oceancovers the cool-light slot).
- Custom theme ids cannot shadow a built-in. Built-ins always win in
Themes.byId(_:). - The
themes/directory is read on demand and cached per-process; restart the daemon (gh-wallpaper pause && gh-wallpaper start) after adding or editing custom theme files.
- Added
catppuccin-frappetheme. - Listed
catppuccin-frappeingh-wallpaper --helptheme ids.
See git history.