diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0d3c20..40fc6bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,9 +76,69 @@ jobs: path: ~/.bun/install/cache key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} - run: bun install --frozen-lockfile - - run: bun dev/screenshots.ts ${{ matrix.wallpaper }} - - if: failure() + # Compares fresh renders with the published ones on the assets branch. A changed scene only warns: its renders + # are uploaded for review, and the publish job below puts them on the assets branch once the change is on main. + - id: compare + continue-on-error: true + run: bun dev/screenshots.ts ${{ matrix.wallpaper }} + - if: steps.compare.outcome == 'failure' + run: echo "::warning title=Screenshots changed::${{ matrix.wallpaper }} renders differently from the README; they publish automatically once on main. See the fresh-screenshots-${{ matrix.wallpaper }} artifact." + - if: steps.compare.outcome == 'failure' uses: actions/upload-artifact@v7 with: name: fresh-screenshots-${{ matrix.wallpaper }} - path: dev/out/${{ matrix.wallpaper }}-*-45s.png + path: dev/out/screenshots/ + + # Commits the changed screenshots to the assets branch, which the README's images point at. + publish: + needs: screenshots + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v7 + with: + ref: assets + - uses: actions/download-artifact@v8 + with: + pattern: fresh-screenshots-* + merge-multiple: true + - run: | + git add -A + git diff --cached --quiet && exit 0 + git -c user.name="github-actions[bot]" -c user.email="41898273+github-actions[bot]@users.noreply.github.com" commit -m "Update screenshots for ${GITHUB_SHA::7}" + git push + + # Updates the release branch that `opencode plugin add vaprdev/opencode-wallpapers#release` installs: the built plugin + # with a package.json that has no build scripts, so installing doesn't build it, and no sources, dev tools or images. + release: + needs: [build, check] + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v7 + - uses: actions/checkout@v7 + with: + ref: release + path: release + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.14 + - uses: actions/cache@v6 + with: + path: ~/.bun/install/cache + key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} + - run: bun install --frozen-lockfile + - run: bun run build + - working-directory: release + run: | + git rm -rq --ignore-unmatch . + cp -R ../dist ../README.md ../LICENSE ../CHANGELOG.md . + jq 'del(.scripts, .devDependencies)' ../package.json > package.json + git add -A + git diff --cached --quiet && exit 0 + git -c user.name="github-actions[bot]" -c user.email="41898273+github-actions[bot]@users.noreply.github.com" commit -m "Release ${GITHUB_SHA::7}" + git push diff --git a/.gitignore b/.gitignore index 42ede1c..db0c3c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ node_modules dev/out dist +# The assets branch, checked out by dev/assets.ts +screenshots/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3d8830..b13300e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -347,9 +347,9 @@ bun dev/pixel-check.ts [id] [activity] [time] # real-pixel mode against a mo bun dev/poke.ts [activity] [time] [seconds...] # click at a point (fractions of the screen) bun dev/fade.ts :day :night 50 # a frame halfway through a crossfade, and its cost bun dev/gif.ts [activity] [time] # a looping GIF at 3x speed in dev/out -bun run screenshots # fails if the README gallery is out of date -bun run screenshots:update # re-renders it (teeming at 45 s) -bun dev/gif.ts all # re-renders the README's GIFs +bun run screenshots # fails if the README gallery differs from the published one +bun run screenshots:update # re-renders it into screenshots/ (teeming at 45 s) +bun dev/gif.ts all # re-renders the README's GIFs into screenshots/ ``` - `check.ts` runs the real engine on a mock 160x45 screen. "cells change per frame" is flicker: lower is calmer. @@ -379,8 +379,34 @@ bun dev/gif.ts all # re-renders the README's GIFs 1. Put it in `wallpapers/.ts` as a named export, and add it to `wallpapers/index.ts`. 2. Run `bun run typecheck` and `bun dev/check.ts `, and look at `dev/snap.ts` and `dev/preview.ts` output at every time of day. -3. Add it to the README's wallpaper list, gallery and GIF grid. Add its best-looking time of day to `BEST` in `dev/gif.ts`, then run `bun run screenshots:update` and - `bun dev/gif.ts all`. +3. Add it to the README's wallpaper list, gallery and GIF grid. Add its best-looking time of day to `BEST` in + `dev/gif.ts`. CI publishes its screenshots once it's merged; a maintainer adds its GIF (see below). + +## README images + +The README's images live on the orphan `assets` branch and are linked by URL, so installing the plugin doesn't +download them. Don't commit images to `main`. The dev tools check out that branch at `screenshots/` (a git worktree, +ignored on `main`) the first time they need it. + +- **Screenshots** update themselves: CI renders every wallpaper, and when a pull request changes one it warns and + attaches the fresh renders as an artifact for review. Once the change is on `main`, CI commits them to `assets`. +- **GIFs and the rest** (`hero.gif`, `menu.png`, the farm's seasons and rain) are updated by hand, by anyone who can + push to `assets`: + + ```sh + git -C screenshots pull origin assets + bun dev/gif.ts all # or copy the new image into screenshots/ + git -C screenshots commit -am "Update GIFs" && git -C screenshots push origin assets + ``` + + Push after the change they show is on `main`. A new file needs `git -C screenshots add` first. + +## Releases + +`opencode plugin add vaprdev/opencode-wallpapers#release` installs the `release` branch, which CI rebuilds from every +push to `main` once the checks pass: `dist/`, the README, the license, the changelog and a `package.json` without +scripts, so installing doesn't build anything. Users get it with `opencode plugin update`. To run your checkout in +OpenCode instead, put its absolute path in `plugins` in `~/.config/opencode/cli.json`. ## Your own package diff --git a/README.md b/README.md index 4f8ce81..3a5dcf8 100644 --- a/README.md +++ b/README.md @@ -2,25 +2,26 @@ Animated wallpapers for the OpenCode terminal UI: slow, quiet scenes behind everything, kept readable behind text. -![Beach, tundra, jungle, desert and space wallpapers in motion](screenshots/hero.gif) +![Beach, tundra, jungle, desert and space wallpapers in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/hero.gif) > This project is not affiliated with, endorsed by, or sponsored by OpenCode or its maintainers. ## Install -Requires OpenCode 2. Clone into OpenCode's plugin directory and restart OpenCode: +Requires OpenCode 2: ```sh -git clone https://github.com/vaprdev/opencode-wallpapers.git ~/.config/opencode/plugins/wallpapers +opencode plugin add vaprdev/opencode-wallpapers#release ``` -Update with `git pull`. +Update with `opencode plugin update`. Restart OpenCode after either. If you installed it with `git clone` before, delete +`~/.config/opencode/plugins/wallpapers`. ## Use -Type `/wallpaper` to open the menu. ↑/↓ picks a setting, Enter or ←/→ changes it, Escape closes it. +Type `/wallpaper` to open the menu and pick a wallpaper; none shows until you do. ↑/↓ picks a setting, Enter or ←/→ changes it, Escape closes it. -![The wallpaper menu](screenshots/menu.png) +![The wallpaper menu](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/menu.png) - **Activity:** calm, lively or teeming: how much moves behind your work. - **Time:** day, sunset or night, or auto to follow your clock. @@ -38,9 +39,9 @@ an error. With mouse support, clicking empty background nudges the scene, and ea | Spring | Summer | Autumn | Winter | | --- | --- | --- | --- | -| ![Farm in spring](screenshots/farm-spring.png) | ![Farm in summer](screenshots/farm-summer.png) | ![Farm in autumn](screenshots/farm-autumn.png) | ![Farm in winter](screenshots/farm-winter.png) | +| ![Farm in spring](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/farm-spring.png) | ![Farm in summer](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/farm-summer.png) | ![Farm in autumn](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/farm-autumn.png) | ![Farm in winter](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/farm-winter.png) | -![Farm in the rain](screenshots/farm-rain.png) +![Farm in the rain](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/farm-rain.png) ## Terminals @@ -64,32 +65,32 @@ At teeming, played back at 3x speed: | | | | --- | --- | -| ![Ocean in motion](screenshots/ocean.gif) | ![Desert in motion](screenshots/desert.gif) | -| ![Jungle in motion](screenshots/jungle.gif) | ![Space in motion](screenshots/space.gif) | -| ![Farm in motion](screenshots/farm.gif) | ![Tundra in motion](screenshots/tundra.gif) | -| ![Beach in motion](screenshots/beach.gif) | ![City in motion](screenshots/city.gif) | -| ![Zen garden in motion](screenshots/zen.gif) | ![Prehistoric in motion](screenshots/prehistoric.gif) | -| ![Rider in motion](screenshots/rider.gif) | ![Megacity in motion](screenshots/megacity.gif) | -| ![Dome in motion](screenshots/dome.gif) | ![Haunted house in motion](screenshots/haunted-house.gif) | -| ![Flower Box in motion](screenshots/flowerbox.gif) | | +| ![Ocean in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/ocean.gif) | ![Desert in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/desert.gif) | +| ![Jungle in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/jungle.gif) | ![Space in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/space.gif) | +| ![Farm in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/farm.gif) | ![Tundra in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/tundra.gif) | +| ![Beach in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/beach.gif) | ![City in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/city.gif) | +| ![Zen garden in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/zen.gif) | ![Prehistoric in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/prehistoric.gif) | +| ![Rider in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/rider.gif) | ![Megacity in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/megacity.gif) | +| ![Dome in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/dome.gif) | ![Haunted house in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/haunted-house.gif) | +| ![Flower Box in motion](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/flowerbox.gif) | | | | Day | Sunset | Night | | --- | --- | --- | --- | -| Ocean | ![Ocean by day](screenshots/ocean.png) | ![Ocean at sunset](screenshots/ocean-sunset.png) | ![Ocean at night](screenshots/ocean-night.png) | -| Desert | ![Desert by day](screenshots/desert.png) | ![Desert at sunset](screenshots/desert-sunset.png) | ![Desert at night](screenshots/desert-night.png) | -| Jungle | ![Jungle by day](screenshots/jungle.png) | ![Jungle at sunset](screenshots/jungle-sunset.png) | ![Jungle at night](screenshots/jungle-night.png) | -| Space | ![Space by day](screenshots/space.png) | ![Space at sunset](screenshots/space-sunset.png) | ![Space at night](screenshots/space-night.png) | -| Farm | ![Farm by day](screenshots/farm.png) | ![Farm at sunset](screenshots/farm-sunset.png) | ![Farm at night](screenshots/farm-night.png) | -| Tundra | ![Tundra by day](screenshots/tundra.png) | ![Tundra at sunset](screenshots/tundra-sunset.png) | ![Tundra at night](screenshots/tundra-night.png) | -| Beach | ![Beach by day](screenshots/beach.png) | ![Beach at sunset](screenshots/beach-sunset.png) | ![Beach at night](screenshots/beach-night.png) | -| City | ![City by day](screenshots/city.png) | ![City at sunset](screenshots/city-sunset.png) | ![City at night](screenshots/city-night.png) | -| Zen garden | ![Zen garden by day](screenshots/zen.png) | ![Zen garden at sunset](screenshots/zen-sunset.png) | ![Zen garden at night](screenshots/zen-night.png) | -| Prehistoric | ![Prehistoric by day](screenshots/prehistoric.png) | ![Prehistoric at sunset](screenshots/prehistoric-sunset.png) | ![Prehistoric at night](screenshots/prehistoric-night.png) | -| Rider | ![Rider by day](screenshots/rider.png) | ![Rider at sunset](screenshots/rider-sunset.png) | ![Rider at night](screenshots/rider-night.png) | -| Megacity | ![Megacity by day](screenshots/megacity.png) | ![Megacity at sunset](screenshots/megacity-sunset.png) | ![Megacity at night](screenshots/megacity-night.png) | -| Dome | ![Dome by day](screenshots/dome.png) | ![Dome at sunset](screenshots/dome-sunset.png) | ![Dome at night](screenshots/dome-night.png) | -| Haunted house | ![Haunted house by day](screenshots/haunted-house.png) | ![Haunted house at sunset](screenshots/haunted-house-sunset.png) | ![Haunted house at night](screenshots/haunted-house-night.png) | -| Flower Box | ![Flower Box by day](screenshots/flowerbox.png) | ![Flower Box at sunset](screenshots/flowerbox-sunset.png) | ![Flower Box at night](screenshots/flowerbox-night.png) | +| Ocean | ![Ocean by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/ocean.png) | ![Ocean at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/ocean-sunset.png) | ![Ocean at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/ocean-night.png) | +| Desert | ![Desert by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/desert.png) | ![Desert at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/desert-sunset.png) | ![Desert at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/desert-night.png) | +| Jungle | ![Jungle by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/jungle.png) | ![Jungle at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/jungle-sunset.png) | ![Jungle at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/jungle-night.png) | +| Space | ![Space by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/space.png) | ![Space at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/space-sunset.png) | ![Space at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/space-night.png) | +| Farm | ![Farm by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/farm.png) | ![Farm at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/farm-sunset.png) | ![Farm at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/farm-night.png) | +| Tundra | ![Tundra by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/tundra.png) | ![Tundra at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/tundra-sunset.png) | ![Tundra at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/tundra-night.png) | +| Beach | ![Beach by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/beach.png) | ![Beach at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/beach-sunset.png) | ![Beach at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/beach-night.png) | +| City | ![City by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/city.png) | ![City at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/city-sunset.png) | ![City at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/city-night.png) | +| Zen garden | ![Zen garden by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/zen.png) | ![Zen garden at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/zen-sunset.png) | ![Zen garden at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/zen-night.png) | +| Prehistoric | ![Prehistoric by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/prehistoric.png) | ![Prehistoric at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/prehistoric-sunset.png) | ![Prehistoric at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/prehistoric-night.png) | +| Rider | ![Rider by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/rider.png) | ![Rider at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/rider-sunset.png) | ![Rider at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/rider-night.png) | +| Megacity | ![Megacity by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/megacity.png) | ![Megacity at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/megacity-sunset.png) | ![Megacity at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/megacity-night.png) | +| Dome | ![Dome by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/dome.png) | ![Dome at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/dome-sunset.png) | ![Dome at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/dome-night.png) | +| Haunted house | ![Haunted house by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/haunted-house.png) | ![Haunted house at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/haunted-house-sunset.png) | ![Haunted house at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/haunted-house-night.png) | +| Flower Box | ![Flower Box by day](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/flowerbox.png) | ![Flower Box at sunset](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/flowerbox-sunset.png) | ![Flower Box at night](https://raw.githubusercontent.com/vaprdev/opencode-wallpapers/assets/flowerbox-night.png) | ## License diff --git a/dev/assets.ts b/dev/assets.ts new file mode 100644 index 0000000..edcffae --- /dev/null +++ b/dev/assets.ts @@ -0,0 +1,14 @@ +// The README's images live on the orphan assets branch, so installing the plugin doesn't download them. The dev tools +// read and write them in screenshots/, a worktree of that branch, created here on first use. +import { existsSync } from "node:fs" + +export function assets() { + if (existsSync("screenshots/.git")) return + if (existsSync("screenshots")) throw new Error("screenshots/ isn't a checkout of the assets branch: move it away and rerun") + if (Bun.spawnSync(["git", "rev-parse", "--verify", "--quiet", "refs/heads/assets"]).exitCode) git("fetch", "--depth=1", "origin", "assets:assets") + git("worktree", "add", "screenshots", "assets") +} + +function git(...args: string[]) { + if (Bun.spawnSync(["git", ...args], { stdout: "ignore", stderr: "inherit" }).exitCode) throw new Error(`git ${args.join(" ")} failed`) +} diff --git a/dev/build.ts b/dev/build.ts index 8e425c7..972f5c8 100644 --- a/dev/build.ts +++ b/dev/build.ts @@ -1,5 +1,5 @@ -// Compiles the plugin to dist/tui.js and the wallpaper API to dist/api.js with its type declarations, for the npm -// package: bun dev/build.ts +// Compiles the plugin to dist/tui.js and the wallpaper API to dist/api.js with its type declarations, for the release +// branch that CI publishes (see CONTRIBUTING.md) and the npm package: bun dev/build.ts // OpenTUI's Solid JSX transform skips files under node_modules, so an installed package must ship compiled JSX. // A git clone loads tui.tsx directly and never needs this. import { rmSync } from "node:fs" diff --git a/dev/gif.ts b/dev/gif.ts index b2aac52..23c24a8 100644 --- a/dev/gif.ts +++ b/dev/gif.ts @@ -1,11 +1,13 @@ // Renders a wallpaper's scene directly (no terminal conversion) to a looping animated GIF: // bun dev/gif.ts [calm|lively|teeming] [day|sunset|night] [WxH] [seconds] → dev/out/--