@indiegems/gem-web-sdk 16.2.0 → 16.3.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/docs/brand-art.md CHANGED
@@ -5,6 +5,13 @@ The SDK ships the platform's own art for its two currencies, **Gems** and **Coin
5
5
  §1 before you pick a file: each currency has two kinds of art, and which one you use depends on what
6
6
  the screen is for.
7
7
 
8
+ > **Draw the platform's current marks with `gem.branding`, and keep these files as the fallback.**
9
+ > `gem.branding.currencyMark('GEMS', { size: 20 })` and `gem.branding.trophy()` hand you the same
10
+ > renders, served by the platform, so a changed mark reaches your game with no SDK update and no
11
+ > republish ([branding.md](branding.md)). The files in this package are the marks as they were when
12
+ > this SDK version was released: draw them only when a lookup answers `unavailable`. Every size,
13
+ > role and rule on this page applies to both.
14
+
8
15
  **Currency display belongs to the arcade.** The arcade draws the player's Gems and Coins totals in
9
16
  its own chrome while your game runs, and it animates every payout of either into its total. Your game
10
17
  draws Gems or Coins only in the three places §5 allows — an amount on offer or just paid, a reward
@@ -142,6 +149,9 @@ and the art softens as it is stretched. Below 64 px, use the icon (§2).
142
149
 
143
150
  ## 4. Loading and drawing the art
144
151
 
152
+ The platform serves every one of these files through `gem.branding` ([branding.md](branding.md)),
153
+ which is what to draw first. The copies below are the fallback for when it answers `unavailable`.
154
+
145
155
  All of it is in the package you already install, under `brand/`, and published beside it on the CDN —
146
156
  the same bytes either way, for the version you are on.
147
157
 
@@ -264,9 +274,10 @@ The CDN lets any origin read these files, so a canvas or a WebGL texture can use
264
274
  path never changes once published, so a URL you ship keeps working, and `artifact-manifest.json` in
265
275
  the same version folder lists each file's `sha384` hash under `brand`.
266
276
 
267
- **Prefer copying the file into your build over loading it by URL at play time.** A load at play time
268
- is one more request that can be slow or fail, and your screen then shows a gap where the art should
269
- be. If you do load it live, lay the screen out so it reads correctly without the image.
277
+ **Prefer `gem.branding` over a fixed URL here.** A URL in this folder is the art of the version you
278
+ pinned, forever; the platform's is the art as it is now. Either way, a load at play time can be slow
279
+ or fail, so lay the screen out so it reads correctly without the image, and keep a bundled copy for
280
+ `unavailable`.
270
281
 
271
282
  ### The platform currency's `icon_url`
272
283
 
@@ -0,0 +1,160 @@
1
+ # The platform's branding: the logo, the wordmark and the platform marks
2
+
3
+ Show the Indie Gems logo on your main menu, the Gem Arcade wordmark on a credits screen, or the Gems
4
+ mark beside a price, with the files **the platform serves now**. `gem.branding` reads them from the
5
+ platform's branding manifest, so when a mark changes, your game draws the new one on its next
6
+ session. You do not update the SDK or republish.
7
+
8
+ ```js
9
+ const logo = await gem.branding.logo();
10
+ if (logo.outcome === 'ok') {
11
+ menuLogo.src = logo.url;
12
+ } else {
13
+ menuLogo.hidden = true; // the platform's branding could not be read: draw no logo
14
+ }
15
+ ```
16
+
17
+ - [1. What you can ask for](#1-what-you-can-ask-for)
18
+ - [2. What a lookup answers](#2-what-a-lookup-answers)
19
+ - [3. Picking a file](#3-picking-a-file)
20
+ - [4. When the manifest cannot be read: the bundled copy](#4-when-the-manifest-cannot-be-read-the-bundled-copy)
21
+ - [5. Your page's Content-Security-Policy](#5-your-pages-content-security-policy)
22
+ - [6. Using the logo and the wordmark](#6-using-the-logo-and-the-wordmark)
23
+
24
+ ## 1. What you can ask for
25
+
26
+ | Call | Asset id | What it is |
27
+ | --------------------------------------------- | -------------- | --------------------------------------------------------------------------------------------------- |
28
+ | `gem.branding.logo()` | `gem-logo` | The Indie Gems logo, the brand gem. Scalable (SVG). |
29
+ | `gem.branding.wordmark()` | `gem-wordmark` | The Gem Arcade wordmark, 336 and 672 px wide. |
30
+ | `gem.branding.currencyMark('GEMS')` | `gems-mark` | The Gems mark: the 20 and 32 CSS px icons at 1, 1.5, 2 and 3x, and hero renders 128 to 768 px wide. |
31
+ | `gem.branding.currencyMark('ARCADE_TICKETS')` | `ticket-mark` | The Coins mark, in the same sizes. |
32
+ | `gem.branding.trophy()` | `trophy-mark` | The trophy: 20, 28, 56 and 72 CSS px renders and hero renders 128 to 768 px wide. |
33
+
34
+ **Any asset, by id and kind:** `gem.branding.get(id, { kind })`. The named calls are `get` with the
35
+ id filled in. When the platform adds an asset (another image, a sound, a font), you reach it with
36
+ `get` from the SDK you already have:
37
+
38
+ ```js
39
+ // 'arcade-sting' is an example of an id the platform could add; today it answers not_found.
40
+ const sting = await gem.branding.get('arcade-sting', { kind: 'audio' });
41
+ if (sting.outcome === 'ok') new Audio(sting.url).play();
42
+ ```
43
+
44
+ `gem.branding.list()` resolves every asset the platform serves today, each with its `id`, `kind`
45
+ and every file (`variants`). Compare `kind` rather than switching on every value: kinds other than
46
+ `image`, `audio` and `font` may appear.
47
+
48
+ The currency marks follow the rules in [brand-art.md](brand-art.md): **the icon** (`size: 20` or
49
+ `32`) beside an amount, **the hero** (no size, or a size of 64 CSS px or more) on a reward screen,
50
+ and never a balance of your own.
51
+
52
+ ## 2. What a lookup answers
53
+
54
+ Every call resolves. None rejects, except for an argument that is not one: an empty id, a `size`
55
+ or `density` that is not a positive number, a `theme` other than `'light'` or `'dark'`, or a
56
+ currency other than `'GEMS'` or `'ARCADE_TICKETS'`.
57
+
58
+ | `outcome` | Means | Do |
59
+ | ------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
60
+ | `ok` | The platform has it. `url` is the file picked for you, `variant` describes it, and `asset` holds every file. | Draw `url`. |
61
+ | `not_found` | The manifest was read, and has no asset with that id and kind, or none in the theme you asked for. | Draw nothing for it. |
62
+ | `unavailable` | The manifest could not be read: the player is offline, the request failed, or your page's policy refused it (§5). | Draw your bundled copy (§4), or nothing. |
63
+
64
+ An id or a kind the SDK does not know is `not_found`, never an error, so a newer platform cannot
65
+ break an older game.
66
+
67
+ **Every URL is on the platform's own host**, absolute, and content-hashed: a changed mark is a new
68
+ URL. Use it as it is in an `<img src>`, a CSS `background-image` or a texture loader. Look it up
69
+ each session; do not store one in a save or your build.
70
+
71
+ The SDK reads the manifest once, on your first `gem.branding` call, and every later call shares
72
+ that read. A game that never calls it makes no request. If the read fails, calls answer
73
+ `unavailable` for a minute, then the next call tries again. Call `gem.branding.list()` during
74
+ start-up to have it ready before your menu draws.
75
+
76
+ ## 3. Picking a file
77
+
78
+ Pass the box you draw in, in CSS px, as `size`. The SDK picks:
79
+
80
+ 1. the file **made for that box**, at the nearest density at or above the screen's
81
+ `devicePixelRatio` (else the densest there is);
82
+ 2. else a **scalable** file (the logo's SVG);
83
+ 3. else the **narrowest** file at least `size × density` pixels wide, else the widest.
84
+
85
+ With no `size`, you get the scalable file, or else the widest. Pass `density` to override the
86
+ screen's ratio, for example `density: 3` for a canvas or a texture you scale down yourself. Pass
87
+ `theme: 'light'` or `'dark'` to prefer a file drawn for that theme; files for the other theme are
88
+ never picked, and a file rendered for your exact `size` still beats one drawn for your theme at
89
+ another size. Every file today serves both.
90
+
91
+ ```js
92
+ // A 20 CSS px Gems icon in an <img>, with every density for the browser to choose from.
93
+ const gems = await gem.branding.currencyMark('GEMS', { size: 20 });
94
+ if (gems.outcome === 'ok') {
95
+ const files = gems.asset.variants.filter((v) => v.size === 20);
96
+ icon.src = gems.url;
97
+ icon.srcset = files.map((v) => `${v.url} ${v.width}w`).join(', ');
98
+ icon.sizes = '20px';
99
+ }
100
+ ```
101
+
102
+ ```js
103
+ // The wordmark on a canvas or a WebGL texture: the platform's host allows any origin to read it.
104
+ const wordmark = await gem.branding.wordmark({ size: 168, density: 3 });
105
+ if (wordmark.outcome === 'ok') {
106
+ const img = new Image();
107
+ img.crossOrigin = 'anonymous'; // before src, or the canvas is tainted
108
+ img.src = wordmark.url;
109
+ await img.decode();
110
+ ctx.drawImage(img, x, y, 168, 88);
111
+ }
112
+ ```
113
+
114
+ ## 4. When the manifest cannot be read: the bundled copy
115
+
116
+ The SDK package still ships the Gems, Coins and trophy marks under `brand/`
117
+ ([brand-art.md](brand-art.md) §4). **They are the fallback for `unavailable`**, not the art to draw
118
+ first: a file bundled in your build is the mark as it was when you built, and the platform's is the
119
+ mark as it is now.
120
+
121
+ ```js
122
+ import gemsIcon from '@indiegems/gem-web-sdk/brand/gem-icon-20-2x.png';
123
+
124
+ const gems = await gem.branding.currencyMark('GEMS', { size: 20 });
125
+ if (gems.outcome === 'ok') icon.src = gems.url;
126
+ else if (gems.outcome === 'unavailable') icon.src = gemsIcon; // the platform could not be reached
127
+ ```
128
+
129
+ There is no bundled logo or wordmark. When `logo()` or `wordmark()` is `unavailable`, lay the screen
130
+ out so it reads correctly without it.
131
+
132
+ ## 5. Your page's Content-Security-Policy
133
+
134
+ The platform does not restrict what your game fetches. If your own page sets a
135
+ Content-Security-Policy (a `<meta>` tag in your `index.html`), allow `https://gemarcade.com`,
136
+ where the manifest and every file it lists are served:
137
+
138
+ - in **`connect-src`**, for the manifest. Without it every lookup is `unavailable`, and the SDK says
139
+ so once on the console;
140
+ - in each kind's own directive for the files you draw: **`img-src`** for images, **`media-src`**
141
+ for audio, **`font-src`** for fonts.
142
+
143
+ [arcade-and-store.md](arcade-and-store.md#the-media-hosts) lists the hosts the Arcade's card art comes
144
+ from, beside this one.
145
+
146
+ ## 6. Using the logo and the wordmark
147
+
148
+ - **Draw them as served:** never recoloured, stretched, rotated, cropped, outlined or given effects,
149
+ and never rebuilt from another font or file.
150
+ - **Keep clear space** around each, at least a quarter of its height, free of text, buttons and
151
+ other art.
152
+ - **Not smaller than the Arcade draws them:** the wordmark 60 CSS px wide, the gem logo 32 CSS px.
153
+ Below that the gem's facets blur together.
154
+ - **On a busy background,** put them on a plain panel rather than over art.
155
+ - **The wordmark is the product's name written as a logo.** Never set "Gem Arcade" in a text face
156
+ beside the gem in its place.
157
+ - **The currency marks stand for the platform's currencies only:** the Gems mark beside a Gems
158
+ amount, never for your own currency ([brand-art.md](brand-art.md) §5).
159
+ - **Never inside a panel dressed as the Arcade's own screens.** A player must always be able to tell
160
+ your game from the platform.
package/docs/changelog.md CHANGED
@@ -16,6 +16,14 @@ Its entries are grouped under up to five headings:
16
16
  - **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
17
17
  - **New capability**: something a game can now do. Nothing changes unless you use it.
18
18
 
19
+ ## 16.3.0
20
+
21
+ Upgrading from 16.2.0. Impact: none.
22
+
23
+ ### New capability
24
+
25
+ - Games can now read the platform's branding manifest at run time through Gem.branding. A game can show the platform logo, the Arcade wordmark, the currency marks and the trophy, and a branding change on the platform reaches the game with no SDK update or republish. The manifest is read once on the first call and all callers share that read. A lookup by id and kind resolves to one of three outcomes: ok, with the URL, the chosen variant and the whole asset; not_found, when the manifest does not list that id or kind; or unavailable, when the manifest could not be read. In the unavailable case, draw your bundled brand copy instead. Only a bad argument rejects. A list call resolves every asset. You can pass the size you will draw and an optional theme, and the SDK picks the best-matching variant. Nothing is required of existing games.
26
+
19
27
  ## 16.2.0
20
28
 
21
29
  Upgrading from 16.1.0. Impact: required.
@@ -50,7 +50,7 @@ either way.
50
50
  ```jsonc
51
51
  // package.json
52
52
  "dependencies": {
53
- "@indiegems/gem-web-sdk": "16.2.0"
53
+ "@indiegems/gem-web-sdk": "16.3.0"
54
54
  }
55
55
  ```
56
56
 
@@ -18,8 +18,8 @@
18
18
  },
19
19
  {
20
20
  "file": "arcade-and-store.md",
21
- "sha256": "35d71f81775c0e7b05c84626bbe8365b624a60fb751abe2a2b3126ba554f33ab",
22
- "updated": "2026-10-28T21:07:26Z"
21
+ "sha256": "d792225c078f9488e3c67f2fe4af1128f7f742ee829173dcd014b03e11eb32f0",
22
+ "updated": "2026-10-29T20:35:07Z"
23
23
  },
24
24
  {
25
25
  "file": "arcade-avatars.md",
@@ -38,13 +38,18 @@
38
38
  },
39
39
  {
40
40
  "file": "brand-art.md",
41
- "sha256": "6d51ae596f6b43fb79058412ab8b10922829dd4e37631ca42f9063d5f43b5029",
42
- "updated": "2026-10-25T16:18:07Z"
41
+ "sha256": "99ef1d5703bca12128b38db8352a102e11444de82903a73973942f074da4fc74",
42
+ "updated": "2026-10-29T20:35:07Z"
43
+ },
44
+ {
45
+ "file": "branding.md",
46
+ "sha256": "f22bd82245e6c4a74025884bfda54e35405e990661c84e5857d5e424e182e61d",
47
+ "updated": "2026-10-29T20:56:53Z"
43
48
  },
44
49
  {
45
50
  "file": "changelog.md",
46
- "sha256": "b2791239b695b7830c97ccb7ef1370c317b92070a9a3f17d8488d02d78965094",
47
- "updated": "2026-10-29T20:57:56Z"
51
+ "sha256": "600e6a0c79c48d4fa82e0993eac822df6629ab737ec6053eafab19c743ea19b8",
52
+ "updated": "2026-10-29T21:40:20Z"
48
53
  },
49
54
  {
50
55
  "file": "daily-challenges.md",
@@ -73,8 +78,8 @@
73
78
  },
74
79
  {
75
80
  "file": "getting-started.md",
76
- "sha256": "0699d1d9283ec251416a8068a6d1dfa050d795c58c55656cc5ce83f98e5bf132",
77
- "updated": "2026-10-29T20:57:56Z"
81
+ "sha256": "50f47be4de5a38a04e49cf1e864f66f8eab4739d8658d6984bb85c61400a0893",
82
+ "updated": "2026-10-29T21:40:20Z"
78
83
  },
79
84
  {
80
85
  "file": "inventory-and-catalogs.md",
@@ -133,8 +138,8 @@
133
138
  },
134
139
  {
135
140
  "file": "server-bundles.md",
136
- "sha256": "3d628942136f6dbf2dc0dd25a825f4520fe9b54ffc88a62e2d9fed11039a5151",
137
- "updated": "2026-10-29T20:57:56Z"
141
+ "sha256": "721aeb9ac64b9564477a5dffb3b5cc70d6d0c1b222d79ab7090413a0f9b4ef5a",
142
+ "updated": "2026-10-29T21:40:20Z"
138
143
  },
139
144
  {
140
145
  "file": "serverless-functions-js.md",
@@ -80,8 +80,8 @@ To check the download yourself: the package npm serves is, byte for byte, the fi
80
80
  also publishes on its download host, with the hash beside it:
81
81
 
82
82
  ```bash
83
- curl -fsSL -O https://downloads.gemarcade.com/sdk/v16.2.0/gem-web-sdk.tgz
84
- curl -fsSL https://downloads.gemarcade.com/sdk/v16.2.0/gem-web-sdk.tgz.sri
83
+ curl -fsSL -O https://downloads.gemarcade.com/sdk/v16.3.0/gem-web-sdk.tgz
84
+ curl -fsSL https://downloads.gemarcade.com/sdk/v16.3.0/gem-web-sdk.tgz.sri
85
85
  openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
86
86
  ```
87
87
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indiegems/gem-web-sdk",
3
- "version": "16.2.0",
3
+ "version": "16.3.0",
4
4
  "description": "postMessage bridge contract between gem-web and browser-playable games",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",