Describe project snapshots and their API routes (#231)

The editor can now keep named snapshots of a project. The wiki
explains how to use them and where they live.

- Concepts: new section "Snapshots" on create, restore with the
  automatic "Before restoring" copy, and delete.
- API: the four routes under projects/{id}/snapshots.
- Architecture: SnapshotStore on server and client, and the dialog.
- Development: e2e module 50, the PHP test and the check sum 1393.
admin committed 2026-09-25 18:22:07 +02:00
1 parent e77acc5a07
commit 0e42e2fecb
4 files changed
+25 -3

No files matched your search

+5 -1
@@ -10,7 +10,11 @@ All routes go through `api/index.php?r=…` (the site's nginx does not pass `PAT
| POST | `projects` | `{name}` → project document (201) |
| GET | `projects/{id}` | `{id,name,revision,updatedAt,project}` |
| PUT | `projects/{id}` | `{revision, project}` → `{revision,updatedAt}` or **409** `{error,revision}` when stale |
| DELETE | `projects/{id}` | 204 |
| DELETE | `projects/{id}` | 204, and the snapshots of the project go with it |
| GET | `projects/{id}/snapshots` | `[{id,name,revision,createdAt}]`, newest first — see [Concepts](Concepts) |
| POST | `projects/{id}/snapshots` | `{name?}` → `{id,name,revision,createdAt}` (201). Copies the project as stored; a blank name becomes the UTC date and time. **422** over 120 characters |
| DELETE | `projects/{id}/snapshots/{sid}` | 204 |
| POST | `projects/{id}/snapshots/{sid}/restore` | `{revision}` → `{revision,updatedAt,before}`. Keeps the stored project as the snapshot `before` (`Before restoring <name>`), then saves the snapshot as the next revision. **409** `{error,revision}` when `revision` is stale |
| GET | `textures` | `[{file,name,width,height,bytes,updatedAt}]` — what the image picker offers |
| POST | `textures&name=hero.png` | **raw image bytes** → `{file,name,width,height,bytes}` (201) |
| DELETE | `textures/{file}` | 204 |
+4
@@ -90,6 +90,7 @@ src/core/particles/ nodes (the catalogue), compile (a graph → one function
src/core/store/ serialize (untrusted JSON → Project), projectPlugins (which plugins a project switches on, read off the raw
file before it is deserialized, #119), tilemapCells (the compact form of a layer's cells in the file),
http (one API call), ProjectStore/HttpProjectStore (load takes a `prepare` that sees the raw JSON first),
SnapshotStore/HttpSnapshotStore (the snapshots of a project, #231),
AuthStore/HttpAuthStore (signing in and out, #177; changing the password, #182),
FileStore (what the server holds of one kind) + HttpFileStore (one generic client over the three
file collections) — TextureStore, FontStore and SoundStore are its type parameters, nothing else
@@ -136,6 +137,8 @@ src/viewport/ view (transforms), items (what to draw), symbols (how to
src/editor/ App → Editor (layout + ticker), PlaySession (the run),
ProjectSession (load/create/delete plus the top bar's one menu, #183),
OpenProjectDialog (the project picker behind "Open project…"),
SnapshotsDialog + snapshots (the list behind "Snapshots…" and the rule of when a
snapshot may be made: only with everything saved, #231),
ProjectSettingsDialog + settingsChanges (name, the start-scene switch and the collision
layers in one dialog; the pure rule of what Apply has to carry out),
EditorRun (run in editor), state, actions, shortcuts, debugHook (installs `window.__gm`;
@@ -238,6 +241,7 @@ src/editor/console/ log (ring buffer, argument formatting, the console.* capt
api/ PHP backend: index.php (routing and the sign-in gate),
lib/{Store,TextureStore,FontStore,SoundStore,PluginStore,OriginalName,Response}.php,
lib/{Auth,UserStore,SessionStore}.php (#177: accounts in data/users.json, sessions in data/sessions/)
lib/SnapshotStore.php (#231: data/snapshots/{projectId}/, one copy of the project per file)
data/projects/ persisted projects, data/textures/ + data/fonts/ + data/sounds/ content-addressed uploads,
data/plugins/ editor plugins, filled from plugins/ by the build and the seeder; which of them run is written
in each project (#119)
+14 -1
@@ -57,8 +57,21 @@ Fixed steps start on a whole screen pixel, so pixel art at 100% stays sharp. The
pixel, and Fit then names the factor in screen pixels (`Fit · 156%`). The choice is saved with
the project, like *Play starts*, but it is no undo step: Ctrl+Z undoes edits, not a look at the game.
**Snapshots** (#231). *Menu → Snapshots…* lists the snapshots of the open project, newest first,
with the local time each was made. **Create snapshot** copies the project as the server has it
stored, under the name typed or, left blank, under the date and time; the autosave is written
first, and while anything is still unsaved (a run with edits waiting, a failed save, a conflict)
no snapshot is made. **Restore** asks first, then keeps the project as it is now as a snapshot of
its own, `Before restoring <name>`, and writes the snapshot back as a new revision — so a restore
can itself be taken back. The project is loaded again, which starts the undo history over, and a
second tab still on the old revision is told the project was changed elsewhere. **Delete** asks,
then removes the one picked. A snapshot is only a copy of the project document: images, fonts,
sounds and pixel art are stored by their content and never removed, so everything it names is
still there. Deleting the project deletes its snapshots. Whoever can open a project can use its
snapshots, an administrator included.
**Toolbar and panels.** The toolbar carries five **icon buttons** and no captions (#183): the
**menu** (three bars) on the left with *New project*, *Open project…* and *Project settings…*,
**menu** (three bars) on the left with *New project*, *Open project…*, *Snapshots…* and *Project settings…*,
**undo** and **redo** beside it, and **play** and **pause** in the middle — on the middle of the
window, not of the space the row has left over — where play turns into a stop square while a run is
on, and pause into a second play sign while it is held. On the right stand the
+2 -1
@@ -14,7 +14,8 @@ npm run build # writes dist/ and regenerates index.html
./build.sh # the same under `set -o pipefail`, for callers that pipe the output
php tools/make-user.php <name> <password> [admin|user] # an account; every tool signs in (see Accounts)
php tests/php/accounts.test.php # the API's account rules, no server needed (CI's gate job)
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1385 editor checks, screenshots in /tmp/gm-e2e-*.png
php tests/php/snapshots.test.php # the snapshot store (#231), the same way
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1393 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result