juicyjuicy 0.4.2 → 0.5.1
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
CHANGED
|
@@ -19821,7 +19821,6 @@ var LIMITS = {
|
|
|
19821
19821
|
* whatever draws one (the link-preview card, players' browsers) decodes every pixel.
|
|
19822
19822
|
*/
|
|
19823
19823
|
maxThumbnailSide: 4096,
|
|
19824
|
-
maxHowItWasMadeBytes: 20 * 1024,
|
|
19825
19824
|
maxApiTokensPerUser: 20,
|
|
19826
19825
|
maxTokenNameLength: 60,
|
|
19827
19826
|
/** Characters in a comment (plain text, ARCHITECTURE §14). */
|
|
@@ -19983,6 +19982,15 @@ var relativePath = (what) => external_exports.string().transform(normalizePath).
|
|
|
19983
19982
|
`${what} must be a relative path inside the project`
|
|
19984
19983
|
);
|
|
19985
19984
|
var tagSchema = external_exports.string().min(2).max(24).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "tags are lowercase words, e.g. puzzle or tower-defense");
|
|
19985
|
+
var languageNames = new Intl.DisplayNames(["en"], { type: "language", fallback: "none" });
|
|
19986
|
+
function languageName(code) {
|
|
19987
|
+
if (!/^[a-z]{2,3}$/.test(code)) return void 0;
|
|
19988
|
+
return languageNames.of(code);
|
|
19989
|
+
}
|
|
19990
|
+
var languageSchema = external_exports.string().trim().toLowerCase().refine(
|
|
19991
|
+
(code) => languageName(code) !== void 0,
|
|
19992
|
+
'use a two-letter language code, like "en" or "pl"'
|
|
19993
|
+
);
|
|
19986
19994
|
var SOURCE_URL_PATTERN = /^https:\/\/[a-z0-9.-]+(:\d{1,5})?(\/[\w.~%/@+=:-]*)?$/i;
|
|
19987
19995
|
var sourceUrlSchema = external_exports.string().max(300).regex(
|
|
19988
19996
|
SOURCE_URL_PATTERN,
|
|
@@ -20002,6 +20010,8 @@ var gameFieldSchemas = {
|
|
|
20002
20010
|
engine: external_exports.enum(ENGINES),
|
|
20003
20011
|
madeWith: external_exports.array(memberText().min(1).max(40)).max(5),
|
|
20004
20012
|
tags: external_exports.array(tagSchema).max(5),
|
|
20013
|
+
/** The languages a player can play it in, the default first. Each one counts once. */
|
|
20014
|
+
languages: external_exports.array(languageSchema).min(1, "list at least one language").max(5).transform((codes) => [...new Set(codes)]),
|
|
20005
20015
|
input: external_exports.array(external_exports.enum(INPUTS)),
|
|
20006
20016
|
orientation: external_exports.enum(ORIENTATIONS),
|
|
20007
20017
|
aspectRatio: external_exports.string().regex(/^(fill|\d{1,2}:\d{1,2})$/, 'use "W:H" like "16:9", or "fill"'),
|
|
@@ -20021,6 +20031,7 @@ var manifestSchema = external_exports.strictObject({
|
|
|
20021
20031
|
engine: gameFieldSchemas.engine.default("other"),
|
|
20022
20032
|
madeWith: gameFieldSchemas.madeWith.default([]),
|
|
20023
20033
|
tags: gameFieldSchemas.tags.default([]),
|
|
20034
|
+
languages: gameFieldSchemas.languages.default(["en"]),
|
|
20024
20035
|
input: gameFieldSchemas.input.default([]),
|
|
20025
20036
|
orientation: gameFieldSchemas.orientation.default("any"),
|
|
20026
20037
|
aspectRatio: gameFieldSchemas.aspectRatio.default("16:9"),
|
|
@@ -20028,7 +20039,8 @@ var manifestSchema = external_exports.strictObject({
|
|
|
20028
20039
|
(path2) => THUMBNAIL_EXTENSIONS.some((ext) => path2.toLowerCase().endsWith(`.${ext}`)),
|
|
20029
20040
|
`thumbnail must be one of: ${THUMBNAIL_EXTENSIONS.join(", ")}`
|
|
20030
20041
|
).optional(),
|
|
20031
|
-
|
|
20042
|
+
/** No longer used (D50); still accepted so older juicy.json files keep publishing. */
|
|
20043
|
+
howItWasMade: external_exports.string().optional().meta({ deprecated: true }),
|
|
20032
20044
|
remixable: gameFieldSchemas.remixable.default(false),
|
|
20033
20045
|
sourceUrl: gameFieldSchemas.sourceUrl.optional(),
|
|
20034
20046
|
remixedFrom: slugSchema.nullable().optional()
|
|
@@ -20064,8 +20076,6 @@ var startVersionRequestSchema = external_exports.object({
|
|
|
20064
20076
|
* when the game's current thumbnail was set on the dashboard. Older CLIs leave it out.
|
|
20065
20077
|
*/
|
|
20066
20078
|
thumbnailAuto: external_exports.boolean().optional(),
|
|
20067
|
-
/** Contents of the manifest's `howItWasMade` markdown file. */
|
|
20068
|
-
howItWasMade: memberText().optional(),
|
|
20069
20079
|
/** A note for the version history, e.g. what changed or a commit SHA. */
|
|
20070
20080
|
message: memberText().max(200).optional(),
|
|
20071
20081
|
source: external_exports.enum(PUBLISH_SOURCES).default("cli"),
|
|
@@ -20084,13 +20094,12 @@ var updateGameRequestSchema = external_exports.object({
|
|
|
20084
20094
|
engine: gameFieldSchemas.engine.optional(),
|
|
20085
20095
|
madeWith: gameFieldSchemas.madeWith.optional(),
|
|
20086
20096
|
tags: gameFieldSchemas.tags.optional(),
|
|
20097
|
+
languages: gameFieldSchemas.languages.optional(),
|
|
20087
20098
|
input: gameFieldSchemas.input.optional(),
|
|
20088
20099
|
orientation: gameFieldSchemas.orientation.optional(),
|
|
20089
20100
|
aspectRatio: gameFieldSchemas.aspectRatio.optional(),
|
|
20090
20101
|
remixable: gameFieldSchemas.remixable.optional(),
|
|
20091
|
-
sourceUrl: gameFieldSchemas.sourceUrl.nullable().optional()
|
|
20092
|
-
/** Markdown, ≤ 20 KB. */
|
|
20093
|
-
howItWasMade: memberText().nullable().optional()
|
|
20102
|
+
sourceUrl: gameFieldSchemas.sourceUrl.nullable().optional()
|
|
20094
20103
|
});
|
|
20095
20104
|
var rollbackRequestSchema = external_exports.object({ version: external_exports.number().int().positive() });
|
|
20096
20105
|
var voteRequestSchema = external_exports.object({
|
|
@@ -20785,8 +20794,16 @@ var REMIX_LICENSE_NAME = "JuicyJuicy remix license";
|
|
|
20785
20794
|
var REMIX_LICENSE = "Members may download this game's files and publish a changed version of it on JuicyJuicy, credited to the original. It doesn't allow publishing the game or a remix of it anywhere else, or passing the files on. Turning Remixable off stops new remix downloads; remixes already published stay up.";
|
|
20786
20795
|
|
|
20787
20796
|
// ../shared/src/listing.ts
|
|
20788
|
-
var GAME_SORTS = [
|
|
20797
|
+
var GAME_SORTS = [
|
|
20798
|
+
"newest",
|
|
20799
|
+
"oldest",
|
|
20800
|
+
"trending",
|
|
20801
|
+
"upvoted",
|
|
20802
|
+
"downvoted",
|
|
20803
|
+
"played"
|
|
20804
|
+
];
|
|
20789
20805
|
var GAME_PERIODS = ["24h", "week", "month", "all"];
|
|
20806
|
+
var GAME_LISTS = ["featured", "favorites", "unplayed"];
|
|
20790
20807
|
var DEFAULT_GAME_LIST_QUERY = {
|
|
20791
20808
|
sort: "newest",
|
|
20792
20809
|
period: "all",
|
|
@@ -20801,8 +20818,10 @@ var paramSchemas = {
|
|
|
20801
20818
|
period: external_exports.preprocess(lowercase2, external_exports.enum(GAME_PERIODS)),
|
|
20802
20819
|
engine: external_exports.preprocess(lowercase2, external_exports.enum(ENGINES)),
|
|
20803
20820
|
tag: external_exports.preprocess(lowercase2, gameFieldSchemas.tags.element),
|
|
20821
|
+
lang: languageSchema,
|
|
20804
20822
|
q: external_exports.string().max(MAX_GAME_SEARCH_LENGTH),
|
|
20805
20823
|
remixes: external_exports.enum(["0", "1"]),
|
|
20824
|
+
list: external_exports.preprocess(lowercase2, external_exports.enum(GAME_LISTS)),
|
|
20806
20825
|
page: external_exports.string().regex(/^\d{1,4}$/, "use a whole number").transform(Number).pipe(external_exports.number().min(1).max(MAX_GAME_LIST_PAGE))
|
|
20807
20826
|
};
|
|
20808
20827
|
function remixFilter(param) {
|
|
@@ -20814,8 +20833,10 @@ var gameListRequestSchema = external_exports.object({
|
|
|
20814
20833
|
period: paramSchemas.period.default(DEFAULT_GAME_LIST_QUERY.period),
|
|
20815
20834
|
engine: paramSchemas.engine.optional(),
|
|
20816
20835
|
tag: paramSchemas.tag.optional(),
|
|
20836
|
+
lang: paramSchemas.lang.optional(),
|
|
20817
20837
|
q: paramSchemas.q.optional(),
|
|
20818
20838
|
remixes: paramSchemas.remixes.optional(),
|
|
20839
|
+
list: paramSchemas.list.optional(),
|
|
20819
20840
|
page: paramSchemas.page.default(DEFAULT_GAME_LIST_QUERY.page)
|
|
20820
20841
|
}).transform(({ remixes, ...rest }) => ({ ...rest, remixes: remixFilter(remixes) }));
|
|
20821
20842
|
|
|
@@ -24346,7 +24367,7 @@ var program = new Command();
|
|
|
24346
24367
|
// package.json
|
|
24347
24368
|
var package_default = {
|
|
24348
24369
|
name: "juicyjuicy",
|
|
24349
|
-
version: "0.
|
|
24370
|
+
version: "0.5.1",
|
|
24350
24371
|
description: "Publish AI-made browser games to juicyjuicy.games",
|
|
24351
24372
|
type: "module",
|
|
24352
24373
|
license: "MIT",
|
|
@@ -26085,6 +26106,7 @@ async function walkFiles(root) {
|
|
|
26085
26106
|
await visit2(root);
|
|
26086
26107
|
return result;
|
|
26087
26108
|
}
|
|
26109
|
+
var HOW_IT_WAS_MADE_WARNING = '"howItWasMade" is no longer used and its file is not sent; remove it from juicy.json (juicyjuicy init does)';
|
|
26088
26110
|
var NO_THUMBNAIL_WARNING = `no thumbnail: the game's tile shows a plain gradient. Add a screenshot as thumbnail.png and set "thumbnail" in juicy.json`;
|
|
26089
26111
|
function isInside(root, path2) {
|
|
26090
26112
|
const rel = relative(root, path2);
|
|
@@ -26105,7 +26127,6 @@ async function checkProject(dir, options = {}) {
|
|
|
26105
26127
|
files: [],
|
|
26106
26128
|
totalBytes: 0,
|
|
26107
26129
|
thumbnail: null,
|
|
26108
|
-
howItWasMade: null,
|
|
26109
26130
|
issues: [],
|
|
26110
26131
|
warnings: [],
|
|
26111
26132
|
findings: [],
|
|
@@ -26140,9 +26161,8 @@ async function checkProject(dir, options = {}) {
|
|
|
26140
26161
|
}
|
|
26141
26162
|
check3.files = plan.files;
|
|
26142
26163
|
check3.totalBytes = plan.files.reduce((sum, file2) => sum + file2.size, 0);
|
|
26143
|
-
const story = manifest.howItWasMade ? relative(buildDir, resolve(realDir, manifest.howItWasMade)).split(sep).join("/") : null;
|
|
26144
26164
|
for (const { path: path2 } of plan.files) {
|
|
26145
|
-
const reason =
|
|
26165
|
+
const reason = unusualFileReason(path2);
|
|
26146
26166
|
if (reason) check3.unusualFiles.push({ path: path2, reason });
|
|
26147
26167
|
}
|
|
26148
26168
|
if (check3.unusualFiles.length > 0) {
|
|
@@ -26188,23 +26208,8 @@ async function checkProject(dir, options = {}) {
|
|
|
26188
26208
|
} else {
|
|
26189
26209
|
check3.warnings.push(NO_THUMBNAIL_WARNING);
|
|
26190
26210
|
}
|
|
26191
|
-
if (manifest.howItWasMade) {
|
|
26192
|
-
|
|
26193
|
-
if (!file2) {
|
|
26194
|
-
check3.issues.push({
|
|
26195
|
-
code: "VALIDATION_FAILED",
|
|
26196
|
-
message: "file not found in the project folder",
|
|
26197
|
-
path: manifest.howItWasMade
|
|
26198
|
-
});
|
|
26199
|
-
} else if (file2.size > LIMITS.maxHowItWasMadeBytes) {
|
|
26200
|
-
check3.issues.push({
|
|
26201
|
-
code: "TOO_LARGE",
|
|
26202
|
-
message: `${(file2.size / 1024).toFixed(1)} KB; at most ${LIMITS.maxHowItWasMadeBytes / 1024} KB`,
|
|
26203
|
-
path: manifest.howItWasMade
|
|
26204
|
-
});
|
|
26205
|
-
} else {
|
|
26206
|
-
check3.howItWasMade = { ...file2, text: await readFile2(file2.absolute, "utf8") };
|
|
26207
|
-
}
|
|
26211
|
+
if (manifest.howItWasMade !== void 0) {
|
|
26212
|
+
check3.warnings.push(HOW_IT_WAS_MADE_WARNING);
|
|
26208
26213
|
}
|
|
26209
26214
|
const viteWarning = await viteBaseWarning(dir);
|
|
26210
26215
|
if (viteWarning) check3.warnings.push(viteWarning);
|
|
@@ -26298,7 +26303,6 @@ async function detectProject(dir) {
|
|
|
26298
26303
|
entry,
|
|
26299
26304
|
engine: await detectEngine(dir, buildDir, deps, Boolean(pkg)),
|
|
26300
26305
|
thumbnail: await firstExisting(dir, ["thumbnail.png", "thumbnail.jpg", "thumbnail.webp"]),
|
|
26301
|
-
howItWasMade: await firstExisting(dir, ["HOW_I_MADE_IT.md"]),
|
|
26302
26306
|
warnings
|
|
26303
26307
|
};
|
|
26304
26308
|
}
|
|
@@ -26331,11 +26335,11 @@ var KEY_ORDER = [
|
|
|
26331
26335
|
"engine",
|
|
26332
26336
|
"madeWith",
|
|
26333
26337
|
"tags",
|
|
26338
|
+
"languages",
|
|
26334
26339
|
"input",
|
|
26335
26340
|
"orientation",
|
|
26336
26341
|
"aspectRatio",
|
|
26337
26342
|
"thumbnail",
|
|
26338
|
-
"howItWasMade",
|
|
26339
26343
|
"remixable",
|
|
26340
26344
|
"sourceUrl",
|
|
26341
26345
|
"remixedFrom"
|
|
@@ -26353,12 +26357,13 @@ async function buildManifest(dir, flags, ask) {
|
|
|
26353
26357
|
orientation: pick2("orientation", "any"),
|
|
26354
26358
|
aspectRatio: pick2("aspectRatio", "16:9"),
|
|
26355
26359
|
tags: pick2("tags", []),
|
|
26360
|
+
languages: pick2("languages", ["en"]),
|
|
26356
26361
|
input: pick2("input", []),
|
|
26357
26362
|
remixable: pick2("remixable", false)
|
|
26358
26363
|
};
|
|
26359
26364
|
const thumbnail = pick2("thumbnail", detected.thumbnail);
|
|
26360
26365
|
if (thumbnail) values.thumbnail = thumbnail;
|
|
26361
|
-
|
|
26366
|
+
delete values.howItWasMade;
|
|
26362
26367
|
if (flags.sourceUrl) values.sourceUrl = flags.sourceUrl;
|
|
26363
26368
|
const fieldSchema = manifestSchema.shape;
|
|
26364
26369
|
const check3 = (field) => (value) => fieldSchema[field].safeParse(value).error?.issues[0]?.message;
|
|
@@ -26877,7 +26882,6 @@ async function check2(out, dir, options) {
|
|
|
26877
26882
|
unusualFiles: result.unusualFiles,
|
|
26878
26883
|
thumbnail: result.thumbnail?.path ?? null,
|
|
26879
26884
|
thumbnailAuto: Boolean(result.thumbnail?.auto),
|
|
26880
|
-
howItWasMade: result.howItWasMade?.path ?? null,
|
|
26881
26885
|
warnings: result.warnings,
|
|
26882
26886
|
findings: result.findings,
|
|
26883
26887
|
scriptHosts: result.scriptHosts
|
|
@@ -27046,7 +27050,7 @@ async function publish(out, dir, options) {
|
|
|
27046
27050
|
const server = apiUrl();
|
|
27047
27051
|
const auth = await ensureLogin(out, server);
|
|
27048
27052
|
const api = new ApiClient(server, auth.token);
|
|
27049
|
-
const { thumbnail
|
|
27053
|
+
const { thumbnail } = checked;
|
|
27050
27054
|
const request = {
|
|
27051
27055
|
manifest: checked.rawManifest,
|
|
27052
27056
|
files: await hashFiles(checked.buildDir, checked.files),
|
|
@@ -27057,7 +27061,6 @@ async function publish(out, dir, options) {
|
|
|
27057
27061
|
} : void 0,
|
|
27058
27062
|
// A screenshot doesn't replace a thumbnail uploaded on the dashboard (D34).
|
|
27059
27063
|
...thumbnail?.auto ? { thumbnailAuto: true } : {},
|
|
27060
|
-
howItWasMade: howItWasMade?.text,
|
|
27061
27064
|
message: options.message,
|
|
27062
27065
|
source: options.source ?? (process.env.GITHUB_ACTIONS === "true" ? "github" : "cli"),
|
|
27063
27066
|
...options.draft ? { draft: true } : {}
|
|
@@ -27740,7 +27743,11 @@ function createProgram() {
|
|
|
27740
27743
|
"--made-with <list>",
|
|
27741
27744
|
'models and tools, e.g. "Claude Opus 5.5, Claude Code"',
|
|
27742
27745
|
splitList
|
|
27743
|
-
).option("--tags <list>", "up to 5 tags, e.g. arcade,space", splitList).
|
|
27746
|
+
).option("--tags <list>", "up to 5 tags, e.g. arcade,space", splitList).option(
|
|
27747
|
+
"--languages <list>",
|
|
27748
|
+
"language codes players can pick, the default first, e.g. en,pl",
|
|
27749
|
+
splitList
|
|
27750
|
+
).addOption(new Option("--input <list>", `controls: ${INPUTS.join(", ")}`).argParser(splitList)).addOption(new Option("--orientation <orientation>").choices(ORIENTATIONS)).option("--aspect-ratio <ratio>", 'like 16:9, or "fill"').option("--thumbnail <file>", "png/jpg/webp in the game folder").option("--remixable", `let other members remix it on JuicyJuicy only (${REMIX_LICENSE_NAME})`).option("--source-url <url>", "public source code, e.g. a GitHub repo").addHelpText(
|
|
27744
27751
|
"after",
|
|
27745
27752
|
`
|
|
27746
27753
|
${REMIX_LICENSE_NAME} (--remixable): ${REMIX_LICENSE}`
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -53,15 +53,21 @@ npx -y juicyjuicy@0 init --yes --json \
|
|
|
53
53
|
- `init` keeps what an existing `juicy.json` already says (flags override), and detects `buildDir`, `entry` and `engine`. Check what it wrote.
|
|
54
54
|
- `--made-with`: your real model and the tool you run in, e.g. `"Claude Opus 5.5, Claude Code"` or `"Gemini 3 Pro, Antigravity"`.
|
|
55
55
|
- The slug (the `/g/<slug>` URL name) defaults to one made from the title. Once published it belongs to this user; reuse it to publish updates.
|
|
56
|
-
-
|
|
56
|
+
- `--languages`: the languages a player can switch between, English first, e.g. `en,pl`. Leave it out for a game that's only in English (the default is `en`).
|
|
57
57
|
|
|
58
58
|
All fields: [reference/manifest.md](reference/manifest.md).
|
|
59
59
|
|
|
60
|
+
**English first.** Every game on JuicyJuicy plays in English by default, so players anywhere can pick it up:
|
|
61
|
+
|
|
62
|
+
- If the game's text (menus, buttons, instructions, dialogue, text drawn in images) is in another language, build an English version before publishing: keep the player-facing text in one table per language, start in English, and add a visible switch (like `EN | PL`) to the language the game was made in. Tell the user in a sentence that you did. Pass both with `--languages en,pl`.
|
|
63
|
+
- `title` and `description` are always in English. A title that is a name (`Kasztanek`) stays as it is; one made of words in another language gets translated (`Wielka Ucieczka` → `The Great Escape`). Tell the user the English title you chose.
|
|
64
|
+
- Text in the thumbnail (step 4) is in English too.
|
|
65
|
+
|
|
60
66
|
### 4. Add a thumbnail
|
|
61
67
|
|
|
62
68
|
A `thumbnail.png` (or `.jpg`/`.webp`, up to 2 MB and 4096×4096 pixels, ideally the game's shape, like 1280×720 for 16:9) in the project folder becomes the game's tile.
|
|
63
69
|
|
|
64
|
-
- **If you can run the game in a browser and take a screenshot**, make a deliberate one: get past any title or "click to start" screen to real gameplay, then save it as `thumbnail.png` and pass `--thumbnail thumbnail.png` to `init`.
|
|
70
|
+
- **If you can run the game in a browser and take a screenshot**, make a deliberate one, with the game in English: get past any title or "click to start" screen to real gameplay, then save it as `thumbnail.png` and pass `--thumbnail thumbnail.png` to `init`.
|
|
65
71
|
- **Otherwise the CLI takes one itself.** When `juicy.json` names no thumbnail, `check` and `publish` open the built game in Chrome, Edge, Chromium or Brave on this machine (headless, at the game's `aspectRatio`, sandboxed and offline: only the game's own files load) and use a screenshot taken a few seconds after it loads. A game that loads its engine, fonts or images from another site is captured without them. Don't block publishing on a thumbnail.
|
|
66
72
|
- **Not for a remix**, unless the user agrees: the screenshot runs the other creator's code. Ask first; only if they say yes, pass `--auto-thumbnail` to `check` and `publish`. Without it you get a warning starting `no thumbnail: this is a remix`.
|
|
67
73
|
|
|
@@ -13,11 +13,11 @@ The manifest in the project folder. `juicyjuicy init` writes it; edit it by hand
|
|
|
13
13
|
"engine": "phaser",
|
|
14
14
|
"madeWith": ["Claude Opus 5.5", "Claude Code"],
|
|
15
15
|
"tags": ["arcade", "space"],
|
|
16
|
+
"languages": ["en", "pl"],
|
|
16
17
|
"input": ["keyboard", "touch"],
|
|
17
18
|
"orientation": "landscape",
|
|
18
19
|
"aspectRatio": "16:9",
|
|
19
20
|
"thumbnail": "thumbnail.png",
|
|
20
|
-
"howItWasMade": "HOW_I_MADE_IT.md",
|
|
21
21
|
"remixable": true,
|
|
22
22
|
"sourceUrl": "https://github.com/someone/space-goose"
|
|
23
23
|
}
|
|
@@ -33,11 +33,12 @@ The manifest in the project folder. `juicyjuicy init` writes it; edit it by hand
|
|
|
33
33
|
| `engine` | | `vanilla`, `phaser`, `threejs`, `pixi`, `godot`, `unity` or `other`. | `--engine` |
|
|
34
34
|
| `madeWith` | | Up to 5 models/tools, e.g. `["Claude Opus 5.5", "Claude Code"]`. | `--made-with "a, b"` |
|
|
35
35
|
| `tags` | | Up to 5 lowercase words or `word-word`, e.g. `puzzle`, `tower-defense`. | `--tags a,b` |
|
|
36
|
+
| `languages` | | Up to 5 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` |
|
|
36
37
|
| `input` | | Any of `keyboard`, `mouse`, `touch`, `gamepad`. | `--input a,b` |
|
|
37
38
|
| `orientation` | | `landscape`, `portrait` or `any` (default). | `--orientation` |
|
|
38
39
|
| `aspectRatio` | | `"W:H"` like `"16:9"` or `"4:3"`, or `"fill"` to fill the player. Default `16:9`. | `--aspect-ratio` |
|
|
39
40
|
| `thumbnail` | | png/jpg/webp in the project folder (not the build folder), up to 2 MB and 4096×4096 pixels. Without one, `check` and `publish` screenshot the game in a local Chrome or Edge, offline (`--no-auto-thumbnail` turns that off; a remix only with `--auto-thumbnail`). | `--thumbnail` |
|
|
40
|
-
| `howItWasMade` | |
|
|
41
|
+
| `howItWasMade` | | No longer used; `check` warns about it and `init` removes it. | |
|
|
41
42
|
| `remixable` | | `true` grants the JuicyJuicy remix license: other members may remix the game on JuicyJuicy only (see SKILL.md, "Remix a game"). Default `false`. | `--remixable` |
|
|
42
43
|
| `sourceUrl` | | Plain `https://` link to the source, e.g. a GitHub repo (no `?`, `#`, spaces or quotes; at most 300 characters). | `--source-url` |
|
|
43
44
|
| `remixedFrom` | | Set by `juicyjuicy remix`; don't edit. | |
|
|
@@ -17,7 +17,7 @@ With `--json`, a failed command's last line is `{"ok": false, "error": {"code",
|
|
|
17
17
|
| `INVALID_MANIFEST` | `juicy.json` is missing, isn't JSON, or a field breaks a rule (`details` says which). | No file: run `init`. Otherwise fix the listed fields (see manifest.md). |
|
|
18
18
|
| `ENTRY_MISSING` | The build folder doesn't exist, or has no entry file. | Run the build. Check `buildDir` and `entry`. |
|
|
19
19
|
| `INVALID_PATH` | A file name is unsafe (backslash, control character, too long, …). | Rename the file (POSIX names, under 256 characters). |
|
|
20
|
-
| `TOO_LARGE` | A file is over 100 MB, the whole build over 150 MB, the thumbnail over 2 MB or 4096 pixels wide or high
|
|
20
|
+
| `TOO_LARGE` | A file is over 100 MB, the whole build over 150 MB, or the thumbnail over 2 MB or 4096 pixels wide or high. Or the user's games together take up more than their 2 GB of storage (the message says "your games already take up"). | Compress or remove assets; resize the thumbnail. For storage: the user deletes a game they don't need on the site; only the newest 5 versions of each game are kept, and old ones are cleaned up daily. |
|
|
21
21
|
| `TOO_MANY_FILES` | More than 3,000 files. | Bundle or pack assets (sprite sheets, atlases). |
|
|
22
22
|
| `VALIDATION_FAILED` | Something else is malformed, e.g. a thumbnail that isn't png/jpg/webp or a file that's missing. | Follow the message. |
|
|
23
23
|
| `SLUG_TAKEN` | Another creator owns this slug. | Choose a new slug: `init --slug <new-slug>`. |
|