juicyjuicy 0.4.2 → 0.5.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 CHANGED
@@ -19983,6 +19983,15 @@ var relativePath = (what) => external_exports.string().transform(normalizePath).
19983
19983
  `${what} must be a relative path inside the project`
19984
19984
  );
19985
19985
  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");
19986
+ var languageNames = new Intl.DisplayNames(["en"], { type: "language", fallback: "none" });
19987
+ function languageName(code) {
19988
+ if (!/^[a-z]{2,3}$/.test(code)) return void 0;
19989
+ return languageNames.of(code);
19990
+ }
19991
+ var languageSchema = external_exports.string().trim().toLowerCase().refine(
19992
+ (code) => languageName(code) !== void 0,
19993
+ 'use a two-letter language code, like "en" or "pl"'
19994
+ );
19986
19995
  var SOURCE_URL_PATTERN = /^https:\/\/[a-z0-9.-]+(:\d{1,5})?(\/[\w.~%/@+=:-]*)?$/i;
19987
19996
  var sourceUrlSchema = external_exports.string().max(300).regex(
19988
19997
  SOURCE_URL_PATTERN,
@@ -20002,6 +20011,8 @@ var gameFieldSchemas = {
20002
20011
  engine: external_exports.enum(ENGINES),
20003
20012
  madeWith: external_exports.array(memberText().min(1).max(40)).max(5),
20004
20013
  tags: external_exports.array(tagSchema).max(5),
20014
+ /** The languages a player can play it in, the default first. Each one counts once. */
20015
+ languages: external_exports.array(languageSchema).min(1, "list at least one language").max(5).transform((codes) => [...new Set(codes)]),
20005
20016
  input: external_exports.array(external_exports.enum(INPUTS)),
20006
20017
  orientation: external_exports.enum(ORIENTATIONS),
20007
20018
  aspectRatio: external_exports.string().regex(/^(fill|\d{1,2}:\d{1,2})$/, 'use "W:H" like "16:9", or "fill"'),
@@ -20021,6 +20032,7 @@ var manifestSchema = external_exports.strictObject({
20021
20032
  engine: gameFieldSchemas.engine.default("other"),
20022
20033
  madeWith: gameFieldSchemas.madeWith.default([]),
20023
20034
  tags: gameFieldSchemas.tags.default([]),
20035
+ languages: gameFieldSchemas.languages.default(["en"]),
20024
20036
  input: gameFieldSchemas.input.default([]),
20025
20037
  orientation: gameFieldSchemas.orientation.default("any"),
20026
20038
  aspectRatio: gameFieldSchemas.aspectRatio.default("16:9"),
@@ -20084,6 +20096,7 @@ var updateGameRequestSchema = external_exports.object({
20084
20096
  engine: gameFieldSchemas.engine.optional(),
20085
20097
  madeWith: gameFieldSchemas.madeWith.optional(),
20086
20098
  tags: gameFieldSchemas.tags.optional(),
20099
+ languages: gameFieldSchemas.languages.optional(),
20087
20100
  input: gameFieldSchemas.input.optional(),
20088
20101
  orientation: gameFieldSchemas.orientation.optional(),
20089
20102
  aspectRatio: gameFieldSchemas.aspectRatio.optional(),
@@ -20785,8 +20798,16 @@ var REMIX_LICENSE_NAME = "JuicyJuicy remix license";
20785
20798
  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
20799
 
20787
20800
  // ../shared/src/listing.ts
20788
- var GAME_SORTS = ["newest", "upvoted", "downvoted", "played"];
20801
+ var GAME_SORTS = [
20802
+ "newest",
20803
+ "oldest",
20804
+ "trending",
20805
+ "upvoted",
20806
+ "downvoted",
20807
+ "played"
20808
+ ];
20789
20809
  var GAME_PERIODS = ["24h", "week", "month", "all"];
20810
+ var GAME_LISTS = ["featured", "favorites", "unplayed"];
20790
20811
  var DEFAULT_GAME_LIST_QUERY = {
20791
20812
  sort: "newest",
20792
20813
  period: "all",
@@ -20801,8 +20822,10 @@ var paramSchemas = {
20801
20822
  period: external_exports.preprocess(lowercase2, external_exports.enum(GAME_PERIODS)),
20802
20823
  engine: external_exports.preprocess(lowercase2, external_exports.enum(ENGINES)),
20803
20824
  tag: external_exports.preprocess(lowercase2, gameFieldSchemas.tags.element),
20825
+ lang: languageSchema,
20804
20826
  q: external_exports.string().max(MAX_GAME_SEARCH_LENGTH),
20805
20827
  remixes: external_exports.enum(["0", "1"]),
20828
+ list: external_exports.preprocess(lowercase2, external_exports.enum(GAME_LISTS)),
20806
20829
  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
20830
  };
20808
20831
  function remixFilter(param) {
@@ -20814,8 +20837,10 @@ var gameListRequestSchema = external_exports.object({
20814
20837
  period: paramSchemas.period.default(DEFAULT_GAME_LIST_QUERY.period),
20815
20838
  engine: paramSchemas.engine.optional(),
20816
20839
  tag: paramSchemas.tag.optional(),
20840
+ lang: paramSchemas.lang.optional(),
20817
20841
  q: paramSchemas.q.optional(),
20818
20842
  remixes: paramSchemas.remixes.optional(),
20843
+ list: paramSchemas.list.optional(),
20819
20844
  page: paramSchemas.page.default(DEFAULT_GAME_LIST_QUERY.page)
20820
20845
  }).transform(({ remixes, ...rest }) => ({ ...rest, remixes: remixFilter(remixes) }));
20821
20846
 
@@ -24346,7 +24371,7 @@ var program = new Command();
24346
24371
  // package.json
24347
24372
  var package_default = {
24348
24373
  name: "juicyjuicy",
24349
- version: "0.4.2",
24374
+ version: "0.5.0",
24350
24375
  description: "Publish AI-made browser games to juicyjuicy.games",
24351
24376
  type: "module",
24352
24377
  license: "MIT",
@@ -26331,6 +26356,7 @@ var KEY_ORDER = [
26331
26356
  "engine",
26332
26357
  "madeWith",
26333
26358
  "tags",
26359
+ "languages",
26334
26360
  "input",
26335
26361
  "orientation",
26336
26362
  "aspectRatio",
@@ -26353,6 +26379,7 @@ async function buildManifest(dir, flags, ask) {
26353
26379
  orientation: pick2("orientation", "any"),
26354
26380
  aspectRatio: pick2("aspectRatio", "16:9"),
26355
26381
  tags: pick2("tags", []),
26382
+ languages: pick2("languages", ["en"]),
26356
26383
  input: pick2("input", []),
26357
26384
  remixable: pick2("remixable", false)
26358
26385
  };
@@ -27740,7 +27767,11 @@ function createProgram() {
27740
27767
  "--made-with <list>",
27741
27768
  'models and tools, e.g. "Claude Opus 5.5, Claude Code"',
27742
27769
  splitList
27743
- ).option("--tags <list>", "up to 5 tags, e.g. arcade,space", splitList).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(
27770
+ ).option("--tags <list>", "up to 5 tags, e.g. arcade,space", splitList).option(
27771
+ "--languages <list>",
27772
+ "language codes players can pick, the default first, e.g. en,pl",
27773
+ splitList
27774
+ ).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
27775
  "after",
27745
27776
  `
27746
27777
  ${REMIX_LICENSE_NAME} (--remixable): ${REMIX_LICENSE}`
@@ -53,15 +53,22 @@ 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
+ - `--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`).
56
57
  - Offer to write `HOW_I_MADE_IT.md` (a short story of how the game was made, up to 20 KB of markdown). `init` picks it up when it exists.
57
58
 
58
59
  All fields: [reference/manifest.md](reference/manifest.md).
59
60
 
61
+ **English first.** Every game on JuicyJuicy plays in English by default, so players anywhere can pick it up:
62
+
63
+ - 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`.
64
+ - `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.
65
+ - Text in the thumbnail (step 4) is in English too.
66
+
60
67
  ### 4. Add a thumbnail
61
68
 
62
69
  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
70
 
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`.
71
+ - **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
72
  - **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
73
  - **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
74
 
@@ -13,6 +13,7 @@ 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",
@@ -33,6 +34,7 @@ The manifest in the project folder. `juicyjuicy init` writes it; edit it by hand
33
34
  | `engine` | | `vanilla`, `phaser`, `threejs`, `pixi`, `godot`, `unity` or `other`. | `--engine` |
34
35
  | `madeWith` | | Up to 5 models/tools, e.g. `["Claude Opus 5.5", "Claude Code"]`. | `--made-with "a, b"` |
35
36
  | `tags` | | Up to 5 lowercase words or `word-word`, e.g. `puzzle`, `tower-defense`. | `--tags a,b` |
37
+ | `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
38
  | `input` | | Any of `keyboard`, `mouse`, `touch`, `gamepad`. | `--input a,b` |
37
39
  | `orientation` | | `landscape`, `portrait` or `any` (default). | `--orientation` |
38
40
  | `aspectRatio` | | `"W:H"` like `"16:9"` or `"4:3"`, or `"fill"` to fill the player. Default `16:9`. | `--aspect-ratio` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juicyjuicy",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Publish AI-made browser games to juicyjuicy.games",
5
5
  "type": "module",
6
6
  "license": "MIT",