juicyjuicy 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +31 -23
- package/dist/skill/SKILL.md +19 -20
- package/dist/skill/reference/languages.md +1 -1
- package/dist/skill/reference/manifest.md +1 -1
- package/dist/skill/reference/moderation.md +1 -1
- package/dist/skill/reference/mouse-lock.md +2 -2
- package/dist/skill/reference/touch.md +31 -10
- package/dist/skill/reference/troubleshooting.md +1 -1
- package/dist/skill/reference/update-all-games.md +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19807,7 +19807,7 @@ function date4(params) {
|
|
|
19807
19807
|
}
|
|
19808
19808
|
|
|
19809
19809
|
// ../shared/src/cli.ts
|
|
19810
|
-
var CLI_RELEASE = "0.
|
|
19810
|
+
var CLI_RELEASE = "0.16.0";
|
|
19811
19811
|
function cliCommand(args) {
|
|
19812
19812
|
return `npx -y juicyjuicy@${CLI_RELEASE} ${args}`;
|
|
19813
19813
|
}
|
|
@@ -19924,7 +19924,7 @@ var LIMITS = {
|
|
|
19924
19924
|
var PUBLISH_LIMIT_WINDOW_HOURS = 24;
|
|
19925
19925
|
var RATE_LIMITS = {
|
|
19926
19926
|
/** New accounts, site-wide. */
|
|
19927
|
-
signUp: { limit:
|
|
19927
|
+
signUp: { limit: 60, windowSeconds: 60 * 60 },
|
|
19928
19928
|
/** New accounts per client IP (hashed). */
|
|
19929
19929
|
signUpNetwork: { limit: 3, windowSeconds: 24 * 60 * 60 },
|
|
19930
19930
|
/** Verification and password-reset mails per address (D88), keyed by a hash of it. */
|
|
@@ -20012,6 +20012,16 @@ var UPLOAD_BUDGET = {
|
|
|
20012
20012
|
/** Files one member may upload in the last 24 hours: 5 publishes at `LIMITS.maxFiles`. */
|
|
20013
20013
|
memberFilesPerDay: 15e3
|
|
20014
20014
|
};
|
|
20015
|
+
var PROVIDER_ALLOWANCES = {
|
|
20016
|
+
/** Neon Free: past it the compute is suspended until the next billing period. */
|
|
20017
|
+
neonComputeHoursPerMonth: 100,
|
|
20018
|
+
/** Neon Free: past it writes fail. */
|
|
20019
|
+
neonStorageBytes: 1024 * MB,
|
|
20020
|
+
/** Neon Free: past it the compute is suspended, as for CU-hours. */
|
|
20021
|
+
neonEgressBytesPerMonth: 5 * 1024 * MB,
|
|
20022
|
+
/** Cloudflare Email Service on Workers Paid; past it $0.35 per 1,000. */
|
|
20023
|
+
mailIncludedPerMonth: 3e3
|
|
20024
|
+
};
|
|
20015
20025
|
var THUMBNAIL_EXTENSIONS = ["png", "jpg", "jpeg", "webp"];
|
|
20016
20026
|
|
|
20017
20027
|
// ../shared/src/genres.ts
|
|
@@ -20774,14 +20784,14 @@ var GAME_FEATURES = [
|
|
|
20774
20784
|
},
|
|
20775
20785
|
{
|
|
20776
20786
|
id: "touch",
|
|
20777
|
-
name: "
|
|
20778
|
-
summary: "The game plays on phones and tablets
|
|
20779
|
-
adds: "
|
|
20780
|
-
why: "Many players open links on their phone; without
|
|
20781
|
-
detection: { kind: "declared", input: "touch" },
|
|
20787
|
+
name: "Mobile optimization",
|
|
20788
|
+
summary: "The game plays well on phones and tablets: touch controls, screen fit, sound, speed.",
|
|
20789
|
+
adds: "Touch controls that fit the game, a layout that fits a phone screen in either orientation and survives rotating, menus that work by tap, and sound and speed that work on phones.",
|
|
20790
|
+
why: "Many players open links on their phone; without this they may only be able to watch, or get a game that's cut off, tiny or silent.",
|
|
20791
|
+
detection: { kind: "declared", input: "touch", addUntilDeclared: true },
|
|
20782
20792
|
status: "available",
|
|
20783
20793
|
recipe: "reference/touch.md",
|
|
20784
|
-
task: "
|
|
20794
|
+
task: "optimize the game for mobile so it plays well on phones and tablets: touch controls, screen fit in both orientations, sound and speed",
|
|
20785
20795
|
docs: "/docs/publish#touch"
|
|
20786
20796
|
},
|
|
20787
20797
|
{
|
|
@@ -20796,18 +20806,6 @@ var GAME_FEATURES = [
|
|
|
20796
20806
|
task: "add gamepad support so the game plays with a controller",
|
|
20797
20807
|
docs: "/docs/publish#gamepad"
|
|
20798
20808
|
},
|
|
20799
|
-
{
|
|
20800
|
-
id: "mouse-lock",
|
|
20801
|
-
name: "Mouse lock",
|
|
20802
|
-
summary: "Aiming or steering with the mouse keeps the cursor in the game.",
|
|
20803
|
-
adds: "Games that aim or steer with the mouse lock the cursor (Pointer Lock), so it can't leave the game.",
|
|
20804
|
-
why: "Without it, turning fast drags the cursor out of the frame or onto another screen, and the game stops responding.",
|
|
20805
|
-
detection: { kind: "declared" },
|
|
20806
|
-
status: "available",
|
|
20807
|
-
recipe: "reference/mouse-lock.md",
|
|
20808
|
-
task: "add mouse lock so aiming or steering with the mouse keeps the cursor in the game",
|
|
20809
|
-
docs: "/docs/publish#mouse-lock"
|
|
20810
|
-
},
|
|
20811
20809
|
{
|
|
20812
20810
|
id: "auto-pause",
|
|
20813
20811
|
name: "Auto-pause",
|
|
@@ -20821,6 +20819,7 @@ var GAME_FEATURES = [
|
|
|
20821
20819
|
docs: "/docs/publish#auto-pause"
|
|
20822
20820
|
}
|
|
20823
20821
|
];
|
|
20822
|
+
var RETIRED_FEATURES = ["mouse-lock"];
|
|
20824
20823
|
function gameFeature(id) {
|
|
20825
20824
|
return GAME_FEATURES.find((feature) => feature.id === id);
|
|
20826
20825
|
}
|
|
@@ -20850,9 +20849,9 @@ function featureState(feature, input2) {
|
|
|
20850
20849
|
};
|
|
20851
20850
|
}
|
|
20852
20851
|
case "declared": {
|
|
20853
|
-
const { input: inputKind } = feature.detection;
|
|
20852
|
+
const { input: inputKind, addUntilDeclared } = feature.detection;
|
|
20854
20853
|
if (inputKind && input2.input?.includes(inputKind)) {
|
|
20855
|
-
return { feature, status: "on", canAdd:
|
|
20854
|
+
return { feature, status: "on", canAdd: Boolean(addUntilDeclared) && !declared };
|
|
20856
20855
|
}
|
|
20857
20856
|
if (input2.declared === null) return { feature, status: "unknown", canAdd: false };
|
|
20858
20857
|
return { feature, status: declared ? "on" : "off", canAdd: !declared };
|
|
@@ -20867,6 +20866,7 @@ function featureState(feature, input2) {
|
|
|
20867
20866
|
function featureFindings(input2) {
|
|
20868
20867
|
const findings = [];
|
|
20869
20868
|
for (const id of input2.declared) {
|
|
20869
|
+
if (RETIRED_FEATURES.includes(id)) continue;
|
|
20870
20870
|
const feature = gameFeature(id);
|
|
20871
20871
|
if (!feature) {
|
|
20872
20872
|
findings.push({
|
|
@@ -27792,7 +27792,7 @@ var program = new Command();
|
|
|
27792
27792
|
// package.json
|
|
27793
27793
|
var package_default = {
|
|
27794
27794
|
name: "juicyjuicy",
|
|
27795
|
-
version: "0.
|
|
27795
|
+
version: "0.16.0",
|
|
27796
27796
|
description: "Publish AI-made browser games to juicyjuicy.games",
|
|
27797
27797
|
type: "module",
|
|
27798
27798
|
license: "MIT",
|
|
@@ -27882,6 +27882,14 @@ The sign-in page says why and how to contest it.`;
|
|
|
27882
27882
|
It won't be shown. Upload a different image to replace it.`;
|
|
27883
27883
|
case "profile_moderated":
|
|
27884
27884
|
return `JuicyJuicy's moderators removed ${note.subject ?? "something from your profile"}:${quoted(note.comment)}`;
|
|
27885
|
+
// Only the site's banner shows these (D108); a publish never sends them.
|
|
27886
|
+
case "version_held":
|
|
27887
|
+
case "report_held":
|
|
27888
|
+
case "image_held":
|
|
27889
|
+
case "score_removed":
|
|
27890
|
+
case "account_changed":
|
|
27891
|
+
case "game_edited":
|
|
27892
|
+
return `A notice about ${game} is waiting on the JuicyJuicy site:${quoted(note.comment ?? "see the banner there")}`;
|
|
27885
27893
|
}
|
|
27886
27894
|
}
|
|
27887
27895
|
function noteMessages(moderation, warnings, slug) {
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -9,22 +9,22 @@ JuicyJuicy hosts **static browser games** (HTML/JS/CSS/assets, WebAssembly) and
|
|
|
9
9
|
|
|
10
10
|
Run every CLI command with `--json`. Each output line is a JSON object; the **last** line is the result, either `{"ok": true, …}` or `{"ok": false, "error": {"code", "message", "details"?, "hint"?}}`. The exit code is `0` (ok), `1` (fix the game or `juicy.json`), `2` (log in) or `3` (network or server problem, so retry).
|
|
11
11
|
|
|
12
|
-
Run the CLI exactly as written here, `npx -y juicyjuicy@0.
|
|
12
|
+
Run the CLI exactly as written here, `npx -y juicyjuicy@0.16.0 …`: this skill is made for that release. If JuicyJuicy answers that the CLI is out of date, run the command its message gives. It updates the CLI and this skill together; read this skill again afterwards and use the release it names from then on.
|
|
13
13
|
|
|
14
14
|
## Procedure
|
|
15
15
|
|
|
16
16
|
### 1. Make sure the CLI is logged in
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npx -y juicyjuicy@0.
|
|
19
|
+
npx -y juicyjuicy@0.16.0 whoami --json
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
If it exits with `2`, log in. `login` opens the user's browser and **waits up to 10 minutes** for them to approve:
|
|
23
23
|
|
|
24
|
-
- Run `npx -y juicyjuicy@0.
|
|
24
|
+
- Run `npx -y juicyjuicy@0.16.0 login --json` in the background if your shell can, and read its first output line (`{"event": "device_code", "userCode", "verificationUrl"}`) right away, and show the user the code and link without waiting for them to ask: the page shows the same code, and that's how they know the login is theirs.
|
|
25
25
|
- Then tell the user, with the real code and link: *"A JuicyJuicy page opened in your browser. Check it shows the code **WXTZ-BCDF** and click **Approve**. If no page opened, go to <verificationUrl>."*
|
|
26
26
|
- If your shell can't run commands in the background, say that first (without a code: it doesn't exist yet), then run it in the foreground with a timeout of at least 10 minutes. The code is also printed as `Your code: …`.
|
|
27
|
-
- The user needs a JuicyJuicy account
|
|
27
|
+
- The user needs a JuicyJuicy account. If they don't have one yet, they can make one on the sign-in page that opens.
|
|
28
28
|
|
|
29
29
|
In CI there's no browser: the `JUICY_TOKEN` environment variable holds a token instead.
|
|
30
30
|
|
|
@@ -45,7 +45,7 @@ Files and folders starting with `.` (`.env`, `.git/`, …) and `node_modules/` a
|
|
|
45
45
|
`juicy.json` sits in the project folder and describes the game. Create or update it with `init`, filling in what you know from the game you built:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
npx -y juicyjuicy@0.
|
|
48
|
+
npx -y juicyjuicy@0.16.0 init --yes --json \
|
|
49
49
|
--title "Snake Deluxe" \
|
|
50
50
|
--description "Eat the apples, don't bite your tail. Arrow keys or swipe." \
|
|
51
51
|
--made-with "<your model name>, <the tool you run in>" \
|
|
@@ -127,7 +127,7 @@ To replace it, the user saves a screenshot as `thumbnail.png` in the project fol
|
|
|
127
127
|
|
|
128
128
|
Do this **every time**, also for updates. It protects the user and their players; JuicyJuicy's server runs its own checks as well, so this is help, not a formality to get past.
|
|
129
129
|
|
|
130
|
-
1. **Check.** Run `npx -y juicyjuicy@0.
|
|
130
|
+
1. **Check.** Run `npx -y juicyjuicy@0.16.0 check --json`. Errors (a failed result) must be fixed: explain each one (`details` has a `rule` and a `path`), and offer to fix it. If the user says no, **stop and don't publish**: the server would refuse it anyway. Warnings in the result (`findings`) go into step 2.
|
|
131
131
|
Every file in the result's `files` will be public. If `unusualFiles` isn't empty (names that look like keys or private data, project files like `package.json`, notes and other files a web game doesn't load), **show the user that list and ask whether those files should be public** before publishing; offer to move them out of the build folder or set `buildDir` to a folder with just the game.
|
|
132
132
|
2. **Security review.** Follow [reference/review.md](reference/review.md): keys and tokens, `innerHTML`/`eval` on network or multiplayer data, `http://` loads, trackers, login or payment forms, anything that leaves the page. Same rule: for what it marks as must-fix, fix it or don't publish.
|
|
133
133
|
3. **Standards.** Check the game against these and tell the user, in a sentence or two, what falls short; offer to fix it. A "no" still publishes: not every game can have touch controls.
|
|
@@ -142,7 +142,7 @@ Do this **every time**, also for updates. It protects the user and their players
|
|
|
142
142
|
- Saves progress with JuicyJuicy's window.juicy snippet, if it saves at all, so it follows signed-in players to any browser (not cookies: the play domain clears them).
|
|
143
143
|
- A Quit or Exit button, if it has one, sends JuicyJuicy's quit message instead of calling window.close().
|
|
144
144
|
|
|
145
|
-
The game runs in a frame on JuicyJuicy, inside a page. The frame can start at 0×0 or be hidden: when `innerWidth` or `innerHeight` is 0, pause and wait for a `resize` instead of restarting, and give every loop that searches (a free cell for food, a spawn point) a way out. A game stuck in a loop stops every other game in the player's tab from loading. Mouse events stop when the cursor leaves the frame: call `canvas.setPointerCapture(event.pointerId)` on `pointerdown` so a drag keeps getting `pointermove` and `pointerup` outside it. A game that aims or steers with the mouse (a shooter, a camera, a racer) calls `canvas.requestPointerLock()` on a click and reads `event.movementX`/`movementY`: the cursor hides and can't leave the game, not even onto a second screen in fullscreen, and Esc gives it back. Ask for the lock again on the next click.
|
|
145
|
+
The game runs in a frame on JuicyJuicy, inside a page. The frame can start at 0×0 or be hidden: when `innerWidth` or `innerHeight` is 0, pause and wait for a `resize` instead of restarting, and give every loop that searches (a free cell for food, a spawn point) a way out. A game stuck in a loop stops every other game in the player's tab from loading. Mouse events stop when the cursor leaves the frame: call `canvas.setPointerCapture(event.pointerId)` on `pointerdown` so a drag keeps getting `pointermove` and `pointerup` outside it. A game that aims or steers with the mouse (a shooter, a camera, a racer) calls `canvas.requestPointerLock()` on a click and reads `event.movementX`/`movementY`: the cursor hides and can't leave the game, not even onto a second screen in fullscreen, and Esc gives it back. Ask for the lock again on the next click. [reference/mouse-lock.md](reference/mouse-lock.md) has the whole recipe (pausing when the lock ends, menus, engines).
|
|
146
146
|
|
|
147
147
|
Esc belongs to the browser: it leaves fullscreen and Pointer Lock before the game sees it. `navigator.keyboard.lock()`, which hands Esc to a page in fullscreen, fails inside a frame, so don't rely on Esc alone: give pause or the menu another key too (like P).
|
|
148
148
|
|
|
@@ -156,7 +156,7 @@ Keep it short when there's nothing to say: "Checked: no problems found" is enoug
|
|
|
156
156
|
### 6. Publish
|
|
157
157
|
|
|
158
158
|
```bash
|
|
159
|
-
npx -y juicyjuicy@0.
|
|
159
|
+
npx -y juicyjuicy@0.16.0 publish --json --message "<what changed, in a few words>"
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
This makes the new version live: when the user says "publish", that's what they want. Don't add `--draft` (see "Other commands") unless they asked in words for it not to go live yet. Not for a first publish, a big game or one you're unsure of either: the user would have to find the dashboard and make it live themselves.
|
|
@@ -186,14 +186,13 @@ The game's page under My games on JuicyJuicy has an **Upgrades** panel; its Add
|
|
|
186
186
|
| Cloud saves | [reference/cloud-saves.md](reference/cloud-saves.md) | `"features": ["cloud-saves"]` |
|
|
187
187
|
| Gamepad support | [reference/gamepad.md](reference/gamepad.md) | `"input": [… "gamepad"]` and `"features": ["gamepad"]` |
|
|
188
188
|
| Global highscores | [reference/highscores.md](reference/highscores.md) | `"features": ["highscores"]` |
|
|
189
|
-
|
|
|
189
|
+
| Mobile optimization | [reference/touch.md](reference/touch.md) | `"input": [… "touch"]` and `"features": ["touch"]` |
|
|
190
190
|
| Multi-language | [reference/languages.md](reference/languages.md) | `"languages": ["en", …]` |
|
|
191
191
|
| Multiplayer | [reference/multiplayer.md](reference/multiplayer.md) | `"features": ["multiplayer"]` |
|
|
192
|
-
| Touch controls | [reference/touch.md](reference/touch.md) | `"input": [… "touch"]` and `"features": ["touch"]` |
|
|
193
192
|
|
|
194
193
|
An upgrade that doesn't fit the game (mouse lock for a game played by clicking, auto-pause for a turn-based one) is the recipe's to say: tell the user why, and change nothing unless they still want it.
|
|
195
194
|
|
|
196
|
-
1. **Find the project.** Log in first (step 1). The project is the folder whose `juicy.json` has the game's `slug`: the current folder, or one the user points you to. **If it isn't on this computer** (or you're in a chat app or sandbox), download the live version: `npx -y juicyjuicy@0.
|
|
195
|
+
1. **Find the project.** Log in first (step 1). The project is the folder whose `juicy.json` has the game's `slug`: the current folder, or one the user points you to. **If it isn't on this computer** (or you're in a chat app or sandbox), download the live version: `npx -y juicyjuicy@0.16.0 pull <slug> --json` writes it into a new folder named after the slug (`dir` in the result) with its `juicy.json`, and you work there. Read the result's `next`: the download is the built game, so for a bundled one (minified code), ask whether the user has the source somewhere, or use `sourceUrl`, before changing the bundle itself. `pull` only works for the user's own games (`NOT_FOUND` otherwise).
|
|
197
196
|
2. **Read the recipe** for the upgrade and make the change. Keep everything else about the game as it is.
|
|
198
197
|
3. **Build and try it** (step 2), then **declare it** in `juicy.json` as the table says, adding to what's already listed.
|
|
199
198
|
4. **Review and publish** as steps 5–7 say, with `--message "Add <upgrade>"`. In step 5's `check` result, `features.detected` lists what JuicyJuicy found in the files; a `feature-missing` warning means a declared upgrade isn't there yet: fix it before publishing.
|
|
@@ -201,14 +200,14 @@ An upgrade that doesn't fit the game (mouse lock for a game played by clicking,
|
|
|
201
200
|
|
|
202
201
|
## Bring all my games up to date
|
|
203
202
|
|
|
204
|
-
When the user asks to bring their JuicyJuicy games up to date or fix a **Needs update** badge, when `check` or `publish` warns `requirement-missing`, or after updating the CLI: follow [reference/update-all-games.md](reference/update-all-games.md). It starts with `npx -y juicyjuicy@0.
|
|
203
|
+
When the user asks to bring their JuicyJuicy games up to date or fix a **Needs update** badge, when `check` or `publish` warns `requirement-missing`, or after updating the CLI: follow [reference/update-all-games.md](reference/update-all-games.md). It starts with `npx -y juicyjuicy@0.16.0 check-games --json`, which checks every game of the user's at once and settles what a script can; the rest are code changes, one game at a time.
|
|
205
204
|
|
|
206
205
|
## Remix a game
|
|
207
206
|
|
|
208
207
|
A game marked remixable on JuicyJuicy can be downloaded, changed and published as a new game that links back to it. Log in first (step 1), then:
|
|
209
208
|
|
|
210
209
|
```bash
|
|
211
|
-
npx -y juicyjuicy@0.
|
|
210
|
+
npx -y juicyjuicy@0.16.0 remix <slug> --json
|
|
212
211
|
```
|
|
213
212
|
|
|
214
213
|
- The first line is `{"event": "license", "name", "text"}`: the remix license. Tell the user in a sentence what it says. In short: the remix may be published **on JuicyJuicy only**, never anywhere else, and the files aren't to be passed on. The full text is:
|
|
@@ -244,10 +243,10 @@ Everything else is in [reference/troubleshooting.md](reference/troubleshooting.m
|
|
|
244
243
|
|
|
245
244
|
## Other commands
|
|
246
245
|
|
|
247
|
-
- `npx -y juicyjuicy@0.
|
|
248
|
-
- `npx -y juicyjuicy@0.
|
|
249
|
-
- `npx -y juicyjuicy@0.
|
|
250
|
-
- `npx -y juicyjuicy@0.
|
|
251
|
-
- `npx -y juicyjuicy@0.
|
|
252
|
-
- `npx -y juicyjuicy@0.
|
|
253
|
-
- `npx -y juicyjuicy@0.
|
|
246
|
+
- `npx -y juicyjuicy@0.16.0 list --json`: the user's games; `list <slug>` shows one game's versions. `--all` lists every game on JuicyJuicy instead (60 a page, `--page 2` and on; `nextPage` in the result), `--owner <username>` another creator's. Narrow and order any list with `--search <title text>`, `--genre` (one of the genre ids above, or several: `board,cards-casino`), `--tag` (a theme), `--engine`, `--remixes`, `--multiplayer` (games with rooms), `--highscores` (games with a leaderboard), `--sort newest|upvoted|downvoted|played` and `--period 24h|week|month|all`, e.g. to find a game to remix.
|
|
247
|
+
- `npx -y juicyjuicy@0.16.0 publish --draft --json`: uploads a new version without making it live (players keep the current one). Only when the user asks for that in words (e.g. "upload it but don't put it live yet"), never on your own judgment. The result has `draft: true`; when they say so, make it live with `go-live`, or they can on the dashboard. Only the newest draft can go live.
|
|
248
|
+
- `npx -y juicyjuicy@0.16.0 go-live --slug <slug> --json`: makes the game's newest draft live, only when the user says so. Pass the draft's number (`go-live <version>`) when they named one, so a newer draft never goes live by surprise. A draft waiting for an admin's review can't go live yet (`FORBIDDEN`), and nor can one a moderator rejected (`VERSION_REJECTED`) or asked changes to (`CHANGES_REQUESTED`). There's no command to go back to an earlier version: when the user asks for one, explain that JuicyJuicy doesn't keep version history, and offer to fix the game or publish their own earlier files as a new version.
|
|
249
|
+
- `npx -y juicyjuicy@0.16.0 recheck <slug> --reply "<what changed>" --json`: asks JuicyJuicy's moderators for another look at a game they blocked, once the user fixed it (see [reference/moderation.md](reference/moderation.md)). Only with the user's OK.
|
|
250
|
+
- `npx -y juicyjuicy@0.16.0 pull <slug> --json`: downloads the live version of one of the user's own games into a new folder with its `juicy.json`, to change it when the project isn't on this computer (see "Add a JuicyJuicy upgrade").
|
|
251
|
+
- `npx -y juicyjuicy@0.16.0 check-games --json`: checks all the user's games against JuicyJuicy's features and required updates, declares the features it finds where a game never declared them, and lists what still needs a change (see "Bring all my games up to date").
|
|
252
|
+
- `npx -y juicyjuicy@0.16.0 open <slug>`: opens the game page in the browser.
|
|
@@ -16,4 +16,4 @@ JuicyJuicy offers English, French, Italian, German, Spanish and Polish (`en`, `f
|
|
|
16
16
|
Run the game, switch to each language and go through the menus and the first minute of play: no missing keys (an untranslated key shows up as the key's name), no clipped text, and the choice is still there after a reload.
|
|
17
17
|
|
|
18
18
|
## Declare it
|
|
19
|
-
Set `languages` in `juicy.json` to every language the game now has, English first, e.g. `"languages": ["en", "fr", "pl"]` (or `npx -y juicyjuicy@0.
|
|
19
|
+
Set `languages` in `juicy.json` to every language the game now has, English first, e.g. `"languages": ["en", "fr", "pl"]` (or `npx -y juicyjuicy@0.16.0 init --yes --json --languages en,fr,pl`). Don't add it to `features`: JuicyJuicy reads `languages`, and shows the game in each language's filter.
|
|
@@ -40,7 +40,7 @@ The manifest in the project folder. `juicyjuicy init` writes it; edit it by hand
|
|
|
40
40
|
| `tags` | | Themes: up to 3 lowercase words or `word-word` for the setting or subject, e.g. `space`, `pirates`, `blackjack`. Not genres or features (touch, remixable: JuicyJuicy shows those by itself), except `multiplayer`, not counted among the 3, for a game players play together without JuicyJuicy's rooms (its own netcode, or one screen). JuicyJuicy cleans them up when saving (spellings fixed, `co-op`, `pvp` and the like saved as `multiplayer`, genre and other feature words moved out, at most 3 themes kept; `check` warns with what it will save). | `--tags a,b` |
|
|
41
41
|
| `ageRating` | | Who the game is for: `everyone`, `12+` or `16+`, from its content (SKILL.md step 3 says how to pick). Rate every game you publish. Left out, a publish keeps the game's rating; a rating set by JuicyJuicy's moderators wins over this one, and `publish` writes it here. | `--age-rating` |
|
|
42
42
|
| `languages` | | Up to 10 language codes a player can switch between, English first, e.g. `["en", "pl"]`. Default `["en"]`. Shown as a language filter on JuicyJuicy. | `--languages a,b` |
|
|
43
|
-
| `features` | yes | The JuicyJuicy upgrades the game has, by id: `auto-pause`, `browser-menu`, `cloud-saves`, `gamepad`, `highscores`, `
|
|
43
|
+
| `features` | yes | The JuicyJuicy upgrades the game has, by id: `auto-pause`, `browser-menu`, `cloud-saves`, `gamepad`, `highscores`, `multiplayer`, `touch`; `[]` for none. Required: `check` and `publish` stop without it, and `init` adds `[]`. Add an id only once the game has the feature (see SKILL.md, "Add a JuicyJuicy upgrade"); `check` warns when one is listed but missing. Multi-language is `languages`, not a feature id; `touch` and `gamepad` go in `input` too. | |
|
|
44
44
|
| `input` | | Any of `keyboard`, `mouse`, `touch`, `gamepad`. | `--input a,b` |
|
|
45
45
|
| `orientation` | | `landscape`, `portrait` or `any` (default). | `--orientation` |
|
|
46
46
|
| `aspectRatio` | | `"W:H"` like `"16:9"` or `"4:3"`, or `"fill"` to fill the player. Default `16:9`. | `--aspect-ratio` |
|
|
@@ -25,7 +25,7 @@ JuicyJuicy's moderators (its admins) can leave the user a written comment about
|
|
|
25
25
|
- after a change request or a rejection: run the review steps and publish a new version (`publish --json`, with a `--message` that says what changed). Tell the user it goes through the safety check again and may wait for a moderator.
|
|
26
26
|
- after a block: publish if the game's files changed (a new title or description in `juicy.json` also goes out with a publish), then ask for another look:
|
|
27
27
|
```bash
|
|
28
|
-
npx -y juicyjuicy@0.
|
|
28
|
+
npx -y juicyjuicy@0.16.0 recheck <slug> --reply "<what the user changed, in their words>" --json
|
|
29
29
|
```
|
|
30
30
|
The game stays unlisted until an admin lists it again; tell the user that. Write the reply with the user, and keep it to what was changed.
|
|
31
31
|
4. **If the user doesn't want to change it**, stop. A version with changes requested or rejected never goes live, and a blocked game stays unlisted. Don't try to get around the note: don't republish the same thing to clear it, rename the game, or publish it under a new slug.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Upgrade: mouse lock
|
|
2
2
|
|
|
3
|
-
Aiming or steering with the mouse keeps the cursor in the game. Read this when the user asks for mouse lock
|
|
3
|
+
Aiming or steering with the mouse keeps the cursor in the game. Read this when the publish standards in SKILL.md find a game that aims or steers with the mouse without locking it, or when the user asks for mouse lock. JuicyJuicy doesn't lock the mouse for a game: the frame only allows it, and the game has to ask. If the user's game needs it, follow "Add a JuicyJuicy upgrade" in SKILL.md to publish the change.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
In a game that turns a camera or aims with the mouse, turning fast drags the cursor out of the game's frame, onto the page or another screen, and the game stops getting the movement. Pointer Lock hides the cursor and keeps sending movement however far the mouse goes. JuicyJuicy's frame allows it.
|
|
@@ -19,4 +19,4 @@ In a game that turns a camera or aims with the mouse, turning fast drags the cur
|
|
|
19
19
|
Run the game: a click locks the cursor and turning works far past the frame's edges; Esc gives the mouse back and the game pauses with "Click to play"; a click resumes; menus can be clicked.
|
|
20
20
|
|
|
21
21
|
## Declare it
|
|
22
|
-
|
|
22
|
+
Nothing to declare: mouse lock isn't a row in the Upgrades panel, so leave `features` as it is. A `juicy.json` that already lists `mouse-lock` is fine as it is too.
|
|
@@ -1,23 +1,44 @@
|
|
|
1
|
-
# Upgrade:
|
|
1
|
+
# Upgrade: mobile optimization
|
|
2
2
|
|
|
3
|
-
The game plays on phones and tablets. Read this when the user asks for touch controls (the JuicyJuicy Upgrades panel's prompt names this file), then follow "Add a JuicyJuicy upgrade" in SKILL.md to publish it.
|
|
3
|
+
The game plays well on phones and tablets: controls, screen, sound and speed. Read this when the user asks for mobile optimization or touch controls (the JuicyJuicy Upgrades panel's prompt names this file), then follow "Add a JuicyJuicy upgrade" in SKILL.md to publish it.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
|
-
Many players open a shared link on their phone. JuicyJuicy shows the game there
|
|
6
|
+
Many players open a shared link on their phone. JuicyJuicy shows the game there in a frame that fills the screen below a slim bar. Players without a keyboard or mouse can only watch unless the game takes touches, and a game laid out for a desktop window ends up cut off or tiny. Games with touch also get the **Touch controls** chip and filter on JuicyJuicy.
|
|
7
7
|
|
|
8
8
|
## What to change
|
|
9
|
+
Go through every part, also when the game already takes touches: fix what falls short and leave what's already right. Keyboard and mouse players keep the game as it was.
|
|
10
|
+
|
|
11
|
+
### Controls
|
|
9
12
|
1. **Pick the controls that fit the game**, and say which to the user before building, in a sentence:
|
|
10
13
|
- **Taps and drags** for games already played with the mouse (clicks, aiming, dragging pieces): make the pointer code work for touch. Use Pointer Events (`pointerdown`, `pointermove`, `pointerup`) instead of mouse events, so one handler takes both; for multi-touch, track each `pointerId`.
|
|
11
14
|
- **Swipes** for games with four directions (snake, 2048, tile puzzles): a swipe longer than about 30 CSS pixels in its main direction is the arrow key.
|
|
12
|
-
- **On-screen buttons** for games with keys held down (platformers, shooters, racing): a d-pad or a virtual stick on the left, action buttons on the right. Each button is at least 44×44 CSS pixels with space between, semi-transparent over the game, and holding it acts like holding the key (press on `pointerdown`, release on `pointerup`, `pointercancel` and `pointerleave`).
|
|
15
|
+
- **On-screen buttons** for games with keys held down (platformers, shooters, racing): a d-pad or a virtual stick on the left, action buttons on the right. Each button is at least 44×44 CSS pixels with space between, semi-transparent over the game, and holding it acts like holding the key (press on `pointerdown`, release on `pointerup`, `pointercancel` and `pointerleave`). Moving and jumping at once must work: track each `pointerId`.
|
|
13
16
|
Engines: Phaser has `this.input.on("pointerdown", …)` and `this.input.addPointer()` for more fingers; Godot has `InputEventScreenTouch`/`ScreenDrag` and a `TouchScreenButton` node that triggers an input action; Unity uses its Input System's on-screen controls.
|
|
14
|
-
2. **Show on-screen buttons only on touch screens**: when `matchMedia("(pointer: coarse)").matches`, or from the first `touchstart`/`pointerdown` with `pointerType === "touch"`.
|
|
15
|
-
3. **Stop the browser taking the gestures.** On the game's canvas or container, set the CSS `touch-action: none` and `user-select: none; -webkit-user-select: none; -webkit-touch-callout: none`, so drags don't scroll or zoom the page and a long press doesn't select or open a menu. Keep `<meta name="viewport" content="width=device-width, initial-scale=1">`.
|
|
16
|
-
4. **
|
|
17
|
-
|
|
17
|
+
2. **Show on-screen buttons only on touch screens**: when `matchMedia("(pointer: coarse)").matches`, or from the first `touchstart`/`pointerdown` with `pointerType === "touch"`.
|
|
18
|
+
3. **Stop the browser taking the gestures.** On the game's canvas or container, set the CSS `touch-action: none` and `user-select: none; -webkit-user-select: none; -webkit-touch-callout: none`, so drags don't scroll or zoom the page and a long press doesn't select or open a menu. On `html, body` set `overscroll-behavior: none; overflow: hidden`, unless the game is a page meant to scroll. Keep `<meta name="viewport" content="width=device-width, initial-scale=1">`.
|
|
19
|
+
4. **Menus and dialogs work by tap**: every key-only action ("press Enter to start", "Space to restart") gets a tap target too, and the help text says "tap" on touch screens. Anything shown only on hover also needs a tap.
|
|
20
|
+
|
|
21
|
+
### Screen
|
|
22
|
+
5. **Fit the frame.** Size the game from the frame (`innerWidth` and `innerHeight`, or CSS `100%`): no fixed 1280×720 page with scrollbars, and nothing important cut off at a narrow width.
|
|
23
|
+
6. **Survive every resize.** Rotating the phone, the browser's address bar sliding away and the on-screen keyboard all resize the frame. Listen for `resize`, lay the game out again and keep playing: never restart the level or lose the score because of it.
|
|
24
|
+
7. **Stay readable.** Size text and buttons in CSS pixels from the frame, not from the game's internal resolution, so a 1920-wide canvas shrunk to a phone doesn't shrink its text with it: body text at least 14 CSS pixels, tap targets at least 44.
|
|
25
|
+
8. **Work both ways round if you can**, with a layout for tall and for wide frames, as the shapes are very different. If the game only works one way, set `orientation` in `juicy.json` (`landscape` or `portrait`) and, when the frame is the other way round, pause and show "Rotate your phone" over the game until a `resize` puts it right. Nothing rotates the game for you: `screen.orientation.lock()` doesn't work on iPhones.
|
|
26
|
+
9. **Keep controls clear.** On-screen controls sit at least 16 CSS pixels from the frame's edges, under the thumbs, and don't cover the player or the action. In a tall frame, put them below the play area when there's room.
|
|
27
|
+
10. **Never need fullscreen.** iPhone Safari can't put a game in fullscreen: hide the game's own fullscreen button when `document.fullscreenEnabled` is false.
|
|
28
|
+
|
|
29
|
+
### Phone browsers
|
|
30
|
+
11. **Sound starts on a tap.** Phones keep audio silent until the player touches the game: create or `resume()` the `AudioContext` in a `pointerup` or `click` handler (iPhones don't accept `pointerdown`), and `resume()` again when the page becomes visible, since a call or switching apps suspends it.
|
|
31
|
+
12. **Nothing depends on the mouse or keyboard being there.** Phones have no Pointer Lock: when `requestPointerLock` is missing or fails, aim by dragging or with a stick. Only a real `<input>` brings up the on-screen keyboard: focus it inside the tap's handler and give it `font-size: 16px`, or iPhones zoom the page.
|
|
32
|
+
13. **Run smoothly.** Cap the drawing resolution on touch screens at `Math.min(devicePixelRatio, 2)`, and use fewer particles or effects there if a round drops frames. Desktop keeps its full quality.
|
|
33
|
+
14. **Stop when hidden.** Phones switch apps often: on `visibilitychange` to hidden, pause a real-time game and stop its sound.
|
|
18
34
|
|
|
19
35
|
## Check it
|
|
20
|
-
Run the game in the browser's device mode (a phone size, touch on) and play a full round with touch only: start, play, pause, game over, restart. Then check the keyboard and mouse still work as before.
|
|
36
|
+
Run the game in the browser's device mode (a phone size, touch on), upright and on its side (about 390×844 and 844×390), and play a full round with touch only: start, play, pause, game over, restart. Rotate in the middle of a round and check it carries on. Check the page never scrolls or zooms and nothing is cut off. Then check the keyboard and mouse still work as before.
|
|
37
|
+
|
|
38
|
+
Device mode can't show everything, sound on iPhones above all: tell the user, in a sentence, to try the game on a real phone after publishing.
|
|
39
|
+
|
|
40
|
+
## When it doesn't fit
|
|
41
|
+
Some games can't work on a phone, such as one that needs a full keyboard all the time. Tell the user why, do what still helps (screen fit, readable text) if they want it, and don't declare it.
|
|
21
42
|
|
|
22
43
|
## Declare it
|
|
23
|
-
Add `"touch"` to `input` in `juicy.json` (`"input": ["keyboard", "touch"]`, keeping what's there), and `"touch"` to `features` (`"features": ["touch"]`, adding to the list). JuicyJuicy can't detect this one from the files: `input` is what the site's chip and the Upgrades panel
|
|
44
|
+
Add `"touch"` to `input` in `juicy.json` (`"input": ["keyboard", "touch"]`, keeping what's there), and `"touch"` to `features` (`"features": ["touch"]`, adding to the list). JuicyJuicy can't detect this one from the files: `input` is what the site's chip reads and `features` is what ends the Upgrades panel's offer, so only add them once a full round plays by touch.
|
|
@@ -6,7 +6,7 @@ With `--json`, a failed command's last line is `{"ok": false, "error": {"code",
|
|
|
6
6
|
|
|
7
7
|
| Code | Meaning | Fix |
|
|
8
8
|
|---|---|---|
|
|
9
|
-
| `UNAUTHORIZED` | No saved login, or the token was revoked. | `npx -y juicyjuicy@0.
|
|
9
|
+
| `UNAUTHORIZED` | No saved login, or the token was revoked. | `npx -y juicyjuicy@0.16.0 login --json` (the user approves in the browser). In CI, set `JUICY_TOKEN`. |
|
|
10
10
|
| `EXPIRED_TOKEN` | The login code expired (10 minutes) or was used. | Run `login` again. |
|
|
11
11
|
| `ACCESS_DENIED` | The user clicked Deny. | Ask them, then run `login` again. |
|
|
12
12
|
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
For "bring my JuicyJuicy games up to date", "fix the required updates", a **Needs update** badge on My games, or a `check` or `publish` that warns `requirement-missing`. Sometimes JuicyJuicy makes a rule every game must follow (say, `juicy.json` listing its `features`). A game that doesn't follow it, or that JuicyJuicy hasn't checked yet, leaves the public lists after the deadline; its link keeps working.
|
|
4
4
|
|
|
5
|
-
To only say which games need an update, without checking or changing anything: `npx -y juicyjuicy@0.
|
|
5
|
+
To only say which games need an update, without checking or changing anything: `npx -y juicyjuicy@0.16.0 list --json` gives each of the user's games `needsUpdate`, one entry per required update (`title`, `deadline`, `phase` `enforced` once it's past the deadline, `notChecked` when JuicyJuicy hasn't checked the game yet, which step 1 settles); `[]` means none.
|
|
6
6
|
|
|
7
7
|
1. **Check every game at once.** Log in first (SKILL.md step 1), then run:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx -y juicyjuicy@0.
|
|
10
|
+
npx -y juicyjuicy@0.16.0 check-games --json
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
JuicyJuicy checks the live version of each of the user's games on its side. It records which features each one has and, for a game whose `juicy.json` never listed its `features`, declares what it finds in the files (like cloud saves). Nothing is uploaded and no new version is made. The result has one entry per game in `games` (`features` with `on`/`off`/`unknown`, `requirements` with `passed`), `toFix` (the games that still need a code change, each with its `prompts`), `notReached` and `next`.
|
|
@@ -15,7 +15,7 @@ To only say which games need an update, without checking or changing anything: `
|
|
|
15
15
|
2. **If `notReached` isn't empty**, run `check-games` again: it had no time for those games.
|
|
16
16
|
|
|
17
17
|
3. **For each game in `toFix`**, one at a time:
|
|
18
|
-
- Find its project, or download the live version with `npx -y juicyjuicy@0.
|
|
18
|
+
- Find its project, or download the live version with `npx -y juicyjuicy@0.16.0 pull <slug> --json` (SKILL.md, "Add a JuicyJuicy upgrade", step 1).
|
|
19
19
|
- Do what each prompt says, with the recipe it names when it names one.
|
|
20
20
|
- Make sure `juicy.json` has `features` (`[]` if the game has none of the upgrades).
|
|
21
21
|
- Check and publish an update as SKILL.md steps 5–7 say, with `--message "Required update: <what>"`.
|