v2.5.0
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e35a72336a |
Upgrade from the card, behind a three-dot menu
An installed title's version line now reads `0.1 → 0.3` where the catalog has moved on, so a card answers both questions somebody brings to it: what is installed, and is there anything better. Which version is installed was already recorded — `state.json` has always carried it — what was missing was anywhere to act on it. The card leads with Play (or Open, for a hosted title) and puts the rest behind a ⋮ button: Upgrade, which fetches whatever the catalog now has, and Uninstall. Upgrade stays visible while disabled rather than appearing and disappearing — a menu whose items come and go makes a person hunt for the one they used last time, and greyed out already says "not now". Playing stays the headline even with an upgrade waiting: the build on the disk still runs, and wanting to play it is not the same as wanting to wait for a download. The menu is a `<details>`, so its open state is the DOM's and the keyboard needs no teaching. Closing it on an outside click is the grid's job, not a card's: cards are rebuilt on every render, so a listener per card would be a listener per render. Package names lose their spaces — `WarpEngineClient-2.3.0-arm64.dmg` — because a space in a release asset is a space in every curl, script and shell command that touches it. Set per target rather than globally: nsis and portable would otherwise resolve to the same .exe name and overwrite each other. `productName` is untouched, so the app is still called WarpEngine Client where a person sees it — in the Dock and in /Applications. Tested on a sandbox store by rewriting one state record to claim an older build, which is what the engine actually compares: the window then offered `Upgrade:on` for that title and `Upgrade:off` for the current one, and pressing it took the record from 0.1 to 0.2 with the old payload removed first. The self-test asserts that pairing on every installed card, because a closed menu photographs identically whether or not its items are right. In Hungarian the catalog refresh and the new Upgrade both wanted "Frissítés"; the refresh is an icon with a tooltip, and a tooltip can afford to say *Katalógus frissítése*. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
06f3f2a3b1 |
The store engine moves into the client, and Python goes with it
Reading the catalog, choosing the release that fits this machine, unpacking it,
writing the menu entry and remembering what went where all happen in process now.
There is no interpreter to find, no child process, and no JSON-lines protocol
between the two halves — `PythonEngineProcessRunner`, the runtime locator, the two
engine mappers and the version negotiation are all gone, and with them the one
unchecked cast this codebase had (engine stdout to a typed event).
What that buys a person: on Windows and on a fresh Mac the app simply works. It
used to look for `python3`, `python` and `py -3` and draw a link to python.org
where none answered.
What lands on disk is unchanged, deliberately. `config.json` and `state.json` keep
the shell engine's snake_case shape, its `<scope>:<name>` keys and its file modes,
so a machine whose library was installed by the CLI keeps it — verified against the
Python engine on the same catalog: the same 13-title listing with zero field
differences, byte-identical payloads, identical modes and an identical Info.plist,
and a re-sync over a Python-installed home that writes nothing. Remove, prune,
prune-suppression on a named sync and the v1 state migration were each exercised.
Three things worth knowing about the new code:
- the zip reader is ~150 lines over `node:zlib`, because Node has none and this
application has no runtime dependencies. It restores the executable bit from
each entry's external attributes, without which nothing installed can start,
and it refuses zip64, unknown compression and paths that escape the
destination rather than guessing;
- `SUPPORTED_WARP_ENGINE_VERSIONS` names the engine versions this client is
written against, checked against the `WarpEngine-Version` header every
response carries. `selectCatalogDialect` switches over that list exhaustively,
so adding a version fails the build — type checker and linter both — until
somebody says what its catalog reads like. An absent header is read as the
oldest version, which is what an engine before 0.4.0 is;
- refresh and the language picker are icons at the foot of the side menu now,
both named for a tooltip and a screen reader, the picker still a real
`<select>` under its glyph.
The repository is free of Python as well: the Makefile, the CI check and the
release script read package.json and the forge's JSON with Node.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
8511ccbef8 |
WarpEngine Client: the whole catalog, and a build that can point elsewhere
**The app is called WarpEngine Client.** "Store" named the thing it opens rather than the
thing you run, and the store is a catalog on a site, not a window on your machine. The
window title, the bundle, the packages and the menu entry follow; the repository already
did. The store being driven is named in the side menu, so the bar stopped repeating it as
a badge — the element stays in the page, hidden, because the window check reads it.
**Every title is listed, including the ones this machine cannot install.** They arrive
from the engine with `installable: false` and a reason, and they are drawn dimmed, with an
*unsupported platform* or *no build for this machine* badge, the engine's own sentence
underneath, and nothing to press: a disabled Install would invite a click that can never
work. They get a category of their own — *Not for this machine* — and they are kept out of
the native/hosted categories and counts, because a title with no build has no mode to be
counted under. An engine older than desktop 1.2.0 is unaffected: a missing `installable`
field reads as installable, which is what those engines mean.
**A build can be pointed at another site's registry:**
make dist STORES_API=https://games.example.org/api/stores
BuildConfiguration reads the packaged package.json, where electron-builder's
extraMetadata writes that address, so a client for somebody else's catalog needs no source
change and nothing set on the user's machine. Precedence is runtime environment, then
build, then ours — three audiences, most specific first.
Also: the scrollbars are the window's own, because the platform's light track down the
side menu of a dark window looked like a mistake.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
a25acf6e35 |
Retry a failed upload instead of losing the release
Publishing 1.2.0 put the first package up and then failed the second with "invalid username, password or token" — the same token, seconds later, and the identical command succeeded on the next run. A flake at 118 MB should not cost a rebuild, so each upload gets up to three attempts, and the attachment already on the release is dropped before every attempt so a retry cannot leave two copies. The shipped bundle was checked rather than assumed: unpacked from the release zip, `codesign --verify --deep --strict` is valid on disk and satisfies its designated requirement, and the app drives the real store — ten cards, the menu populated, the categories counted. With the quarantine flag set it is killed on launch (exit 137), which is the ad-hoc-signing limitation the README already documents. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
cd5361e222 |
A side menu, categories, and a store switcher
Everything that is not a title moved out of the bar into a menu on the left that folds away with the button in the bar; the state is remembered between runs. It carries the stores on this machine, the two actions, the categories and the language. Categories narrow the grid one at a time, with counts: the state of a title on this machine, then a row per platform and per kind. The axes are built from what the catalog contains — a WarpEngine catalog has no genre — and a category that disappears under you falls back to Everything rather than leaving a blank grid. With more than one store installed, clicking another in the menu opens it: the grid, the categories and the folders follow, the choice is remembered, and stores sharing an id are told apart by their folder. app:state now reports every store, store:use switches, and the engine is re-checked per store because two stores can be at different versions. While the CLI runs, the menu, the log drawer and the filters stay live; only what would start a second call is disabled. Fixes the grid, which was broken and passing every check: its implicit rows split the window's height evenly instead of following their content, so every card came out 94px tall with its box art collapsed to nothing and its buttons clipped away. The DOM was intact throughout — ten cards, twenty buttons — which is exactly why the counts said nothing. Rows are content-sized now, and the art is one band of one height, with the title's first letter where the catalog has no image. So the window is looked at and not only counted, `SELFTEST_SHOT` has it photograph itself; the terminal has no screen-recording permission here. The selftest also clicks the store that is not open when there are two, and checks that the bar, the grid and the categories follow. Also fixes scripts/release.sh, which could not upload a package whose name has a space in it — "WarpEngine Store-1.1.0-arm64.dmg" does — because the list was one string split on whitespace. And `make release` cleans dist/ first, so a release cannot pick up the previous version's artifacts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
85c6d33b05 |
A Makefile over the npm scripts, and one command that publishes
Building and releasing were a build followed by remembered `tea` invocations, and the second half was the part I got wrong by hand: the first release went out with assets attached through a path that half-failed. `make release` is now one command — package for this machine, then create the release and upload — and the pieces are also available separately as `make dist` and `make publish`. The Makefile adds no logic beyond that: every other target wraps an npm script, so `npm start` and `npm run dist:mac` keep working. What it does add is a Node version guard on anything that touches Electron's installer, because a Node 20 `npm install` fails deep inside a postinstall script with an ESM error that says nothing about the cause. `scripts/release.sh` holds the publishing, in POSIX sh like the other repositories' scripts: - the tag comes from package.json, so `npm version patch` is the only place a version is written; - an attachment whose name is already on the release is replaced rather than refused, which is what makes rebuild-and-upload repeatable; - the repository is read from `origin`, so a fork publishes to the fork; - `RELEASE_NOTES.md` becomes the release body when it exists. Tested against the live release with a small probe file rather than by pushing 240 MB twice: creation is skipped when the release exists, a repeat upload takes the replace path, and an empty `dist/` refuses with the command to run instead. The probe was removed afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |