diff --git a/README.md b/README.md
index 007f2e7..9952463 100644
--- a/README.md
+++ b/README.md
@@ -6,20 +6,21 @@ clipboard when you click it. Built to be dropped onto an nginx server as plain
static files.
```
-index.html the page
-assets/styles.css styling (auto light/dark)
-assets/app.js search, rendering, clipboard logic
-scripts/generate-manifest.mjs scans img/ -> images.json
-images.json generated manifest the page fetches
-img/ put your images here
+index.html the page
+assets/styles.css styling (auto light/dark)
+assets/app.js search, rendering, clipboard logic
+generate-manifest.py scans img/ -> images.json, then commits
+pushAll regenerate + commit + push, in one command
+images.json generated manifest the page fetches
+img/ put your images here
```
## Why a manifest?
A browser cannot list a directory on its own, and "no backend" rules out an API
that would do it. So the file list is produced by a tiny build step:
-`scripts/generate-manifest.mjs` walks `img/` and writes `images.json`. The page
-fetches that file at runtime. Everything the server does is serve static files.
+`generate-manifest.py` walks `img/` and writes `images.json`. The page fetches
+that file at runtime. Everything the server does is serve static files.
## Quick start
@@ -27,8 +28,8 @@ fetches that file at runtime. Everything the server does is serve static files.
# 1. Add images (subfolders are fine)
cp ~/pictures/*.png img/
-# 2. Build the manifest
-node scripts/generate-manifest.mjs
+# 2. Build the manifest (and commit it)
+./generate-manifest.py
# 3. Preview locally
python3 -m http.server 8080
@@ -37,6 +38,32 @@ python3 -m http.server 8080
Re-run step 2 whenever you add, rename, or delete images.
+## Publishing with `pushAll`
+
+`pushAll` does the whole publish flow in one command: regenerate the manifest,
+commit the changes, and push to the remote.
+
+```sh
+./pushAll
+```
+
+To run it as `pushAll` from anywhere, either add this folder to your `PATH`, or
+create a git alias once:
+
+```sh
+git config alias.pushAll '!./pushAll'
+# then:
+git pushAll
+```
+
+### `generate-manifest.py` options
+
+```sh
+./generate-manifest.py # generate + commit
+./generate-manifest.py --no-commit # generate only
+./generate-manifest.py -m "Add logos" # custom commit message
+```
+
## Features
- **Search** — case-insensitive, multi-word with AND semantics. Every word must
@@ -54,13 +81,13 @@ and point a server block at it. See [`nginx.conf.example`](nginx.conf.example).
```sh
# build the manifest locally, then upload everything
-node scripts/generate-manifest.mjs
+./generate-manifest.py --no-commit
rsync -av --delete ./ user@server:/var/www/image-library/
```
-If you'd rather generate the manifest on the server, install Node there and run
-the script after uploading images. Node is only needed for that step — nginx
-never runs it.
+If you'd rather generate the manifest on the server, install Python 3 there and
+run the script after uploading images. Python is only needed for that step —
+nginx never runs it.
### Clipboard over plain HTTP
diff --git a/assets/app.js b/assets/app.js
index 363be9b..95df084 100644
--- a/assets/app.js
+++ b/assets/app.js
@@ -1,6 +1,6 @@
/* Image Library — single-page app, no backend.
*
- * Reads `images.json` (generated by scripts/generate-manifest.mjs), renders a
+ * Reads `images.json` (generated by ./generate-manifest.py), renders a
* searchable grid, and copies an image's absolute URL to the clipboard on click.
*/
(() => {
@@ -42,7 +42,7 @@
els.manifestInfo.textContent = "";
showEmpty(
"Could not load images.json",
- `Run node scripts/generate-manifest.mjs to build the manifest, then reload.`
+ `Run ./generate-manifest.py to build the manifest, then reload.`
);
els.loading.hidden = true;
return;
@@ -96,7 +96,7 @@
if (images.length === 0) {
showEmpty(
"No images yet",
- `Drop files into img/, then run node scripts/generate-manifest.mjs.`
+ `Drop files into img/, then run ./generate-manifest.py.`
);
updateCount(0, 0);
return;
diff --git a/images.json b/images.json
index 0fc03b0..0c98138 100644
--- a/images.json
+++ b/images.json
@@ -1,5 +1,5 @@
{
- "generatedAt": "2026-09-27T14:23:41.878271Z",
+ "generatedAt": "2026-09-27T14:25:20.447348Z",
"count": 1,
"images": [
{