@johnmorrisdotca/kumimoji 1.2.1 → 1.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +37 -1
  2. package/CONTRIBUTING.md +81 -44
  3. package/README.md +478 -327
  4. package/dist/check.d.ts +26 -1
  5. package/dist/check.js +26 -1
  6. package/dist/computer.types.d.ts +24 -2
  7. package/dist/computerPlay.d.ts +45 -2
  8. package/dist/computerPlay.js +45 -2
  9. package/dist/computerTurn.d.ts +42 -3
  10. package/dist/computerTurn.js +42 -3
  11. package/dist/daily.d.ts +22 -2
  12. package/dist/daily.js +22 -2
  13. package/dist/element-define.d.ts +1 -1
  14. package/dist/element-define.js +3 -13
  15. package/dist/element.d.ts +85 -4
  16. package/dist/element.js +176 -45
  17. package/dist/embed-day.d.ts +4 -0
  18. package/dist/embed-day.js +23 -0
  19. package/dist/export.d.ts +123 -6
  20. package/dist/export.js +91 -4
  21. package/dist/generate.d.ts +14 -0
  22. package/dist/generate.js +14 -0
  23. package/dist/grid.d.ts +181 -10
  24. package/dist/grid.js +122 -7
  25. package/dist/help.d.ts +36 -3
  26. package/dist/help.js +36 -3
  27. package/dist/judge.d.ts +14 -0
  28. package/dist/judge.js +14 -0
  29. package/dist/kana.d.ts +23 -1
  30. package/dist/kana.js +23 -1
  31. package/dist/kumimoji.types.d.ts +70 -5
  32. package/dist/party.d.ts +268 -14
  33. package/dist/party.js +268 -14
  34. package/dist/party.types.d.ts +50 -2
  35. package/dist/partyKept.d.ts +74 -4
  36. package/dist/partyKept.js +74 -4
  37. package/dist/partySeats.d.ts +92 -6
  38. package/dist/partySeats.js +72 -4
  39. package/dist/partyTurns.d.ts +171 -5
  40. package/dist/partyTurns.js +171 -5
  41. package/dist/placement.d.ts +50 -2
  42. package/dist/placement.js +30 -0
  43. package/dist/play.d.ts +247 -15
  44. package/dist/play.js +237 -15
  45. package/dist/random.d.ts +34 -3
  46. package/dist/random.js +23 -2
  47. package/dist/react.d.ts +42 -4
  48. package/dist/react.js +22 -5
  49. package/dist/showcase.d.ts +83 -7
  50. package/dist/showcase.js +49 -4
  51. package/dist/strings.d.ts +54 -5
  52. package/dist/strings.js +34 -3
  53. package/dist/tableView.d.ts +151 -8
  54. package/dist/tableView.js +129 -6
  55. package/dist/tileFace.d.ts +59 -5
  56. package/dist/tileFace.js +48 -4
  57. package/dist/tileFamily.d.ts +9 -0
  58. package/dist/tileFamily.js +9 -0
  59. package/dist/tileWords.d.ts +93 -6
  60. package/dist/tileWords.js +83 -6
  61. package/dist/tileWordsModule.d.ts +11 -1
  62. package/dist/tileWordsModule.js +11 -1
  63. package/dist/tiles.constants.d.ts +99 -5
  64. package/dist/tiles.constants.js +99 -5
  65. package/dist/turn.d.ts +82 -4
  66. package/dist/turn.js +82 -4
  67. package/dist/ui/board.d.ts +60 -5
  68. package/dist/ui/board.js +28 -2
  69. package/dist/ui/mount.d.ts +41 -2
  70. package/dist/ui/mount.js +38 -1
  71. package/dist/ui/style.d.ts +6 -1
  72. package/dist/ui/style.js +6 -0
  73. package/dist/version.d.ts +9 -2
  74. package/dist/version.js +9 -2
  75. package/dist/wilds.d.ts +22 -1
  76. package/dist/wilds.js +22 -1
  77. package/package.json +66 -57
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- <h1 align="center">Kumimoji <sub>組み文字</sub></h1>
1
+ <h1 align="center">Kumimoji <sub>組み文字</sub> — Crossword tile game in English and Japanese</h1>
2
2
 
3
3
  <p align="center"><strong>The crossword tile race: a word game where you build one crossword from a bag of letter tiles.</strong><br>
4
4
  In English and in Japanese kana. The rules as pure, seeded TypeScript, two word lists, a computer player, and a table to play on in React, Vue, Svelte, Angular or plain HTML.</p>
@@ -10,14 +10,30 @@ In English and in Japanese kana. The rules as pure, seeded TypeScript, two word
10
10
  <a href="./NOTICE.md"><img alt="Japanese word list: CC BY-SA 4.0" src="https://img.shields.io/badge/Japanese%20words-CC%20BY--SA%204.0-b5452c"></a>
11
11
  <img alt="No dependencies" src="https://img.shields.io/badge/dependencies-0-2f5d4a">
12
12
  <img alt="TypeScript" src="https://img.shields.io/badge/types-TypeScript-3178c6">
13
+ <a href="https://johnmorrisdotca.github.io/kumimoji/docs/api/main/index.html"><img alt="679.1 kB minified and gzipped" src="https://img.shields.io/badge/min%2Bgzip-679.1%20kB-2f5d4a"></a>
14
+ <a href="https://www.npmjs.com/package/@johnmorrisdotca/kumimoji#provenance"><img alt="Published with npm provenance" src="https://img.shields.io/badge/npm-provenance-2f5d4a"></a>
13
15
  </p>
14
16
 
15
- <p align="center"><a href="https://johnmorrisdotca.github.io/kumimoji/"><strong>Play a game →</strong></a> · <a href="https://johnmorrisdotca.github.io/kumimoji/api.html">API reference</a></p>
16
-
17
- <p align="center">
18
- <img src="docs/desktop.jpg" alt="An English game half way, under the demo's header with its language chooser, five cloth patches and Help switch: the set-up choices, a crossword of SYNDIC and the words that cross it on the green table, a tile in hand under it, and the buttons to draw, trade and sort" width="720">
19
- <img src="docs/phone.jpg" alt="A game in Japanese kana on a phone in dark mode, in Japanese: a crossword half built on the green table, with a tile in hand and the buttons to draw, trade and sort" width="220">
20
- </p>
17
+ <p align="center"><a href="https://johnmorrisdotca.github.io/kumimoji/"><strong>Play a game →</strong></a> · <a href="https://johnmorrisdotca.github.io/kumimoji/docs/">Documentation</a> · <a href="https://johnmorrisdotca.github.io/kumimoji/docs/guides/getting-started.html">Getting started</a> · <a href="https://johnmorrisdotca.github.io/kumimoji/api.html">API reference</a> · <a href="docs/API.md">The API in full</a></p>
18
+
19
+ <table align="center">
20
+ <tr>
21
+ <td align="center" valign="top">
22
+ <picture>
23
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/hero-desk-dark.webp">
24
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/hero-desk-light.webp" alt="The demo on a desk, in English: the page's header with its language chooser and cloth swatches, the choices for tiles, hand and wild tiles, and a green table with a crossword half built from the words SYNDIC, MAUP and PA crossing, one tile left in the hand and the buttons to draw, trade, sort and lift all." width="720">
25
+ </picture>
26
+ <br><em>An English game half way, on a desk: a crossword of SYNDIC and the words that cross it.</em>
27
+ </td>
28
+ <td align="center" valign="top">
29
+ <picture>
30
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/hero-phone-dark.webp">
31
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/hero-phone-light.webp" alt="The demo on a phone, in Japanese kana: a green table with a crossword built from kana tiles, and a hand and buttons under it." width="220">
32
+ </picture>
33
+ <br><em>A game in kana on a phone, in the device's light or dark.</em>
34
+ </td>
35
+ </tr>
36
+ </table>
21
37
 
22
38
  A word game engine for the race to build a crossword. Every player lays their
23
39
  own tiles as words that cross, draws another when the hand is used and every
@@ -69,7 +85,7 @@ isFinished(play, judgeWithWords(play.tiles, words)); // → true
69
85
 
70
86
  NOW, then SNOW, then SNOWY. And a table to play on, in a page:
71
87
 
72
- ```js
88
+ ```js no-run
73
89
  import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
74
90
 
75
91
  mountKumimoji(document.getElementById("table"), { language: "english", hand: 7 });
@@ -90,8 +106,121 @@ Or with nothing to install, [play a game in the demo](https://johnmorrisdotca.gi
90
106
  on the や tile, and a list of 163,461 readings.
91
107
  - **Learners and teachers** of English or Japanese spelling.
92
108
 
109
+ ## Features
110
+
111
+ - **Dealt from a seed.** `generateKumimoji` lays a crossword first and deals
112
+ its tiles, so every bag can be finished.
113
+ - **English and Japanese.** 144 tiles in each: the English letters, or 45 base
114
+ kana where が plays as か, ゃ as や and を as お. 110,316 English words and
115
+ 163,461 Japanese readings, of 2 to 15 tiles.
116
+ - **Wild tiles** that take any letter or kana, and are read as that in every
117
+ word they are part of.
118
+ - **Trades.** A tile you cannot use goes back into the bag for three.
119
+ - **Diagonals**, if chosen: every diagonal run of three or more must be a word
120
+ too.
121
+ - **Three lengths.** A short game, half the set, or all 144 tiles; and two
122
+ English sets at once.
123
+ - **The rules as plain functions.** A game in progress is a value (`TilePlay`):
124
+ `deal`, `placeFromHand`, `moveOnTable`, `liftToHand`, `draw` and `trade` each
125
+ return a new one.
126
+ - **A table of two to eight** sharing one bag, with a last round, resigning,
127
+ joining and leaving, kept as text, and a computer player for any seat.
128
+ - **Help**, if chosen: the words the hand's own tiles spell.
129
+ - **A table to play on.** `mountKumimoji` plays a whole game alone in plain
130
+ DOM, saves it and loads it back; `KumimojiBoard` draws a grid in React.
131
+ - **A tag.** `<kumimoji-table>` is the same game in one element, with no
132
+ framework.
133
+ - **One bag a day.** `kumimojiDailySeed(new Date())` is the same seed for
134
+ everybody, worldwide.
135
+ - **Played from a keyboard.** Arrow keys between squares and tiles, Enter or
136
+ Space to pick up and lay; see [Accessibility](#accessibility).
137
+ - **The table's words in English and Japanese**, and any other language by a
138
+ table of your own. **Themeable**: every colour is a CSS variable.
139
+
140
+ ### What's in it
141
+
142
+ Each picture is the table the package draws, taken from [the demo](https://johnmorrisdotca.github.io/kumimoji/) with `pnpm screenshots:readme`, in light and dark. The game is dealt from a seed, and the words are laid by the package's own computer player, so the same pictures come again.
143
+
144
+ <table>
145
+ <tr>
146
+ <td align="center" valign="top" width="50%">
147
+ <picture>
148
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/english-desk-dark.webp">
149
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/english-desk-light.webp" alt="An English board on the green table: SYNDIC across with MAUP and PA crossing it, and U hanging under the N, drawn on a grid of empty squares that has no edge." width="400">
150
+ </picture>
151
+ <br><em><strong>English.</strong> 144 tiles, 110,316 words.</em>
152
+ </td>
153
+ <td align="center" valign="top" width="50%">
154
+ <picture>
155
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/kana-desk-dark.webp">
156
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/kana-desk-light.webp" alt="A Japanese board of kana tiles on the green table, a crossword of hiragana laid across and down on the grid of empty squares." width="400">
157
+ </picture>
158
+ <br><em><strong>Japanese kana.</strong> 45 base kana tiles, 163,461 readings.</em>
159
+ </td>
160
+ </tr>
161
+ <tr>
162
+ <td align="center" valign="top" width="50%">
163
+ <picture>
164
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/not-a-word-desk-dark.webp">
165
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/not-a-word-desk-light.webp" alt="A board with two tiles side by side, N and W, each ringed in red, because the two make the run NW, which is not a word." width="400">
166
+ </picture>
167
+ <br><em><strong>A run that is not a word</strong> is ringed, and named under the table.</em>
168
+ </td>
169
+ <td align="center" valign="top" width="50%">
170
+ <picture>
171
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/hand-of-eleven-desk-dark.webp">
172
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/hand-of-eleven-desk-light.webp" alt="A board partly built from a hand of eleven tiles, a larger crossword than a hand of seven makes, on the same green table." width="400">
173
+ </picture>
174
+ <br><em><strong>A hand of eleven.</strong> Hands are 3, 7 or 11.</em>
175
+ </td>
176
+ </tr>
177
+ </table>
178
+
179
+ <p align="center">
180
+ <picture>
181
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/wild-tiles-desk-dark.webp">
182
+ <img src="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/wild-tiles-desk-light.webp" alt="A hand of seven tiles, E, T, U, P, L and L in cream and one gold tile with a black star, a wild tile that is any letter or kana the player gives it." width="500">
183
+ </picture>
184
+ <br><em><strong>A wild tile</strong> is the gold star in the hand: the player gives it a letter before it is laid.</em>
185
+ </p>
186
+
93
187
  ## Use it in your project
94
188
 
189
+ ### Install under another name
190
+
191
+ ```sh
192
+ npm install word-game@npm:@johnmorrisdotca/kumimoji
193
+ ```
194
+
195
+ ```ts no-check
196
+ import "word-game/words";
197
+ import { loadTileWords, generateKumimoji } from "word-game";
198
+ await loadTileWords("english");
199
+ console.log(generateKumimoji(7,"easy",42).seed);
200
+ ```
201
+
202
+ ### Install
203
+
204
+ ```sh
205
+ npm install @johnmorrisdotca/kumimoji
206
+ ```
207
+
208
+ ```sh
209
+ pnpm add @johnmorrisdotca/kumimoji
210
+ ```
211
+
212
+ ```sh
213
+ yarn add @johnmorrisdotca/kumimoji
214
+ ```
215
+
216
+ A page with no bundler loads the table as a tag from a CDN, naming the major version so that a release that changes what you use is one you choose:
217
+
218
+ ```html
219
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@johnmorrisdotca/kumimoji@1/dist/element-define.js"></script>
220
+ ```
221
+
222
+ On a server or in a script, `import "@johnmorrisdotca/kumimoji/words"` first, so that the word lists are read from the package's files.
223
+
95
224
  Kumimoji is five things, each usable without the others: **the rules**, plain
96
225
  functions over plain values; **the word lists**, loaded when first needed;
97
226
  **a table** you mount into any element; **the same table as a tag**,
@@ -217,7 +346,7 @@ onBeforeUnmount(() => table?.destroy());
217
346
 
218
347
  ### 7. Angular
219
348
 
220
- ```typescript
349
+ ```ts no-check
221
350
  import { Component, ElementRef, OnDestroy, afterNextRender, provideZonelessChangeDetection, signal, viewChild } from "@angular/core";
222
351
  import { bootstrapApplication } from "@angular/platform-browser";
223
352
  import { mountKumimoji, type KumimojiTableHandle } from "@johnmorrisdotca/kumimoji/ui";
@@ -268,156 +397,184 @@ fetches when a game is first dealt.
268
397
  phone. The rules have no DOM in them and run in Node 22 and later, Deno,
269
398
  Bun and web workers.
270
399
 
271
- ## Architecture
400
+ The cookbook, with the output of each example, is under [Examples](#examples).
272
401
 
273
- The rules are plain functions over plain data with no DOM: a game is a value,
274
- every move returns the next one, and the same seed deals the same bag in every
275
- browser. Word lists are loaded only when a game opens, and the table is its own
276
- entry (`/ui` for plain DOM, `/element` for a tag, `/react` for React), so a page that wants only the
277
- rules carries none of it. A computer player is pure too, so its turn replays
278
- exactly after a reload.
402
+ ### Embed builder and reflected settings
403
+
404
+ [Build a crossword-table embed](https://johnmorrisdotca.github.io/kumimoji/builder.html), preview English letters or Japanese kana and copy element, iframe, module, React 19, Vue, Svelte, Angular, HTML or URL code. It saves no play automatically. The builder/frame defaults `keep` off; the existing element default remains on for compatibility.
405
+
406
+ Every attribute reflects to a property. New `daily` accepts today or an explicit UTC date (the `seed="daily"` alias remains); `controls`, device/light/dark `theme`, `size` (small 320px, medium 480px, large 640px), pixel `width` and spoken `label` are available. `table`, `saved`, `newGame()` and `setGame()` remain. `options` accepts every native table choice, including initial saved state and callbacks. Readiness is available as `table.ready`; `table.set(...)` updates presentation without redealing. Load errors are visible and reported through `onError`/`kumimoji-error`. `defineTable` accepts a custom tag and is server-safe. See [the embed guide](docs/embeds.md).
407
+
408
+ ## Examples
279
409
 
280
- ```text
281
- src/
282
- ├── check.ts whether a finished grid completes a game: the one check a server also runs
283
- ├── computer.types.ts what a computer did in one step of its turn
284
- ├── computerPlay.ts the word a computer player lays: from its own hand, with no randomness
285
- ├── computerTurn.ts a computer's whole turn, planned as steps the page can show one at a time
286
- ├── daily.ts one seed a day, the same for everybody
287
- ├── element-define.ts the "/element/define" entry: defines <kumimoji-table> on the page by being imported
288
- ├── element.ts the "/element" entry: the <kumimoji-table> element, a game alone in a tag
289
- ├── export.ts a game written out as JSON or plain text, and read back
290
- ├── generate.ts making a game from a seed: a crossword laid first, then its tiles become the bag
291
- ├── grid.ts the grid of tiles on a table with no edges, and how it is written as a string
292
- ├── help.ts arranging the hand into a word, for a player who asks for help
293
- ├── index.ts the main entry: the rules, the generator, the computer player and the table's pure helpers
294
- ├── judge.ts judging a grid against its language's word list, run by run
295
- ├── kana.ts Japanese tiles: the 45 base kana, and how every other kana is played as one of them
296
- ├── kumimoji.types.ts the game's types: how much of the set a bag holds, and the rest
297
- ├── party.ts pass and play: two to eight people round one device, one bag, a hand and a table each
298
- ├── party.types.ts what a pass-and-play game was set up as
299
- ├── partyKept.ts a pass-and-play game kept in the browser as one string of JSON
300
- ├── partySeats.ts people and computers joining and leaving a pass-and-play game between turns
301
- ├── partyTurns.ts how a pass-and-play turn ends, and how the game does, and who won
302
- ├── placement.ts where a word can cross a crossword without spoiling it
303
- ├── play.ts a game being played: the bag, the hand and the table, and every move on them
304
- ├── random.ts a seeded random, so one seed deals one game in every browser
305
- ├── react.tsx the "/react" entry: a grid to draw in React
306
- ├── showcase.ts what the rules page shows of the game, worked out from the game's own tables
307
- ├── strings.ts every word Kumimoji shows a person, in English and Japanese
308
- ├── tableView.ts how the table is looked at: which squares are shown, and how big a tile is drawn
309
- ├── tileFace.ts what a tile code shows, without loading a word list
310
- ├── tileFamily.ts which tile of the set a tile is, without loading a word list
311
- ├── tileWords.ts the word list, loaded once when a game opens
312
- ├── tileWordsModule.ts the same lists where there is no browser, imported only by a server or a test
313
- ├── tiles.constants.ts every number the game is played by: tile mixes, hand sizes, lengths of a game
314
- ├── turn.ts turning the table while every tile stays upright
315
- ├── ui.ts the "/ui" entry: Kumimoji played in the browser, in plain DOM
316
- ├── version.ts the version of this package, as package.json has it
317
- ├── wilds.ts a wild tile with no letter read as whatever makes the grid words
318
- ├── words.en.data.ts the English word list
319
- ├── words.ja.data.ts the Japanese word list and tile mix
320
- ├── words.ts the "/words" entry: the word lists, for a place with no browser, such as a server or a test
321
- └── ui/ the table that draws and plays a game
322
- ├── board.ts how a tile on the table is drawn: sound, in a run that is not a word, or apart
323
- ├── mount.ts the table itself: mounting it on a page, the options it takes, and playing it from a keyboard
324
- └── style.ts the table's own styles, every colour and size a CSS variable so a page can restyle it
410
+ Every TypeScript and JavaScript block that can run is type-checked against the built package and run by `pnpm test:readme`, so the output after `// →` is what the code prints. The word lists are read from the package's files in Node, so nothing here uses a network.
411
+
412
+ ### A page with nothing else
413
+
414
+ Save this as a file and open it. The tag is the whole table: set-up, tiles, hand, clock, saving and loading.
415
+
416
+ ```html
417
+ <!doctype html>
418
+ <meta charset="utf-8">
419
+ <meta name="viewport" content="width=device-width, initial-scale=1">
420
+ <title>A crossword race</title>
421
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@johnmorrisdotca/kumimoji@1/dist/element-define.js"></script>
422
+ <kumimoji-table language="english" hand="7" seed="daily"></kumimoji-table>
325
423
  ```
326
424
 
327
- Tests sit beside the code they test (`*.test.ts`). `scripts/` builds the demo
328
- and checks the package as npm packs it, `demo/` is the page published on
329
- GitHub Pages, and `table/` taps it in real browsers.
425
+ ### Listen to the tag
330
426
 
331
- ## The name
427
+ The tag fires `kumimoji-change` after every change, with the game as it would be saved, and `kumimoji-finish` when the last tile is laid and every run is a word:
332
428
 
333
- *Kumimoji* (組み文字) is made of *kumi* (組み, from 組む, to put together or
334
- assemble) and *moji* (文字, a letter or character): letters put together,
335
- which is what a player does with the tiles. Say it in four even beats:
336
- ku-mi-mo-ji. In Japanese printing the same word names something else, several
337
- characters set together in the space of one; the game borrows only its plain
338
- sense.
429
+ ```html
430
+ <kumimoji-table id="game" language="japanese" hand="7" seed="2026"></kumimoji-table>
431
+ <p id="note"></p>
432
+ <script type="module">
433
+ import "https://cdn.jsdelivr.net/npm/@johnmorrisdotca/kumimoji@1/dist/element-define.js";
339
434
 
340
- ## Where it comes from, and where it is used
435
+ const game = document.getElementById("game");
436
+ game.addEventListener("kumimoji-change", (event) => {
437
+ localStorage.setItem("kumimoji", JSON.stringify(event.detail.saved));
438
+ });
439
+ game.addEventListener("kumimoji-finish", (event) => {
440
+ document.getElementById("note").textContent = `Done in ${Math.round(event.detail.elapsedMs / 1000)} seconds.`;
441
+ });
442
+ </script>
443
+ ```
341
444
 
342
- Kumimoji was built for [Itsutsu](https://itsutsu.com), a site for board games,
343
- puzzles, card games and dice games played at your own pace. *Itsutsu* (五つ) is
344
- Japanese for "five", after five in a row, the game the site began with. It is
345
- [the Kumimoji played there](https://itsutsu.com/games/kumimoji): alone, in
346
- races, round one device and across several.
445
+ ### Deal the day's bag
347
446
 
348
- ### Used by
447
+ `kumimojiDailySeed(date)` is the UTC date as a number, the same for everybody on earth, and the same number as Tane's `dailySeed`. Everybody who deals from it with the same settings gets the same bag in the same order:
349
448
 
350
- - [Itsutsu](https://itsutsu.com), for its game of Kumimoji: its rules, its
351
- word lists and its computer player.
449
+ ```ts
450
+ import "@johnmorrisdotca/kumimoji/words";
451
+ import { generateKumimoji, kumimojiDay, kumimojiDailySeed, loadTileWords } from "@johnmorrisdotca/kumimoji";
352
452
 
353
- That is the whole list so far. Using Kumimoji in something? Open an
354
- [*Add my project*](https://github.com/johnmorrisdotca/kumimoji/issues/new?template=add-my-project.md)
355
- issue and we will add you.
453
+ await loadTileWords("english");
454
+ const now = new Date("2026-10-06T10:00:00Z");
455
+ kumimojiDay(now); // → "2026-10-06"
456
+ kumimojiDailySeed(now); // → 20261006
457
+ const dealt = generateKumimoji(7, "medium", kumimojiDailySeed(now));
458
+ dealt.givens.length; // → 40
459
+ dealt.givens.slice(0, 10); // → "ltlekac*rm"
460
+ ```
356
461
 
357
- ### The family
462
+ The `*` is a wild tile, which takes any letter. The bag is one character a tile, in the order the tiles come out.
358
463
 
359
- <!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
360
- Kumimoji is one of twenty-four packages, each made for the same site, each at
361
- [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
464
+ ### Judge a grid
362
465
 
363
- - [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
364
- - [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ): a turning cube for the browser, 2×2 to 7×7, with record solves to replay. [Demo](https://johnmorrisdotca.github.io/kyuubu/).
365
- - [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ): a colour-card shedding game for two to eight, with the house rules people play. [Demo](https://johnmorrisdotca.github.io/hitotsu/).
366
- - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
367
- - [Tane](https://github.com/johnmorrisdotca/tane) (種): seeded random numbers and daily seeds, the same in every browser and on every server. [Demo](https://johnmorrisdotca.github.io/tane/).
368
- - [Narabe](https://github.com/johnmorrisdotca/narabe) (並べ): one rules engine for abstract board games, from gomoku and Reversi to Go and checkers. [Demo](https://johnmorrisdotca.github.io/narabe/).
369
- - [Tenka](https://github.com/johnmorrisdotca/tenka) (天下): world conquest for two to six, on a map of the real world. [Demo](https://johnmorrisdotca.github.io/tenka/).
370
- - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
371
- - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
372
- - [Jarajara](https://github.com/johnmorrisdotca/jarajara) (ジャラジャラ): mahjong tiles drawn as SVG, stacked layouts, and the matching solitaire Awase. [Demo](https://johnmorrisdotca.github.io/jarajara/).
373
- - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
374
- - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
375
- - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
376
- - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
377
- - [Kazu](https://github.com/johnmorrisdotca/kazu) (数): grid number puzzles: Sudoku and its variants, Futoshiki and Skyscrapers. [Demo](https://johnmorrisdotca.github.io/kazu/).
378
- - [Meikyuu](https://github.com/johnmorrisdotca/meikyuu) (迷宮): mazes on squares, hexagons, triangles and circles, made from a seed and drawn through with a finger or the mouse. [Demo](https://johnmorrisdotca.github.io/meikyuu/).
379
- - [Hikidashi](https://github.com/johnmorrisdotca/hikidashi) (引き出し): a drawer of small Japanese text tools: era dates, kanji numerals, readings and sentence difficulty. [Demo](https://johnmorrisdotca.github.io/hikidashi/).
380
- - [Chizu](https://github.com/johnmorrisdotca/chizu) (地図): maps of the world and of countries' regions, in English and Japanese, with a quiz and callouts. [Demo](https://johnmorrisdotca.github.io/chizu/).
381
- - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
382
- - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
383
- - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
384
- - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
385
- - [Karakuri](https://github.com/johnmorrisdotca/karakuri) (からくり): eight hyper-casual puzzle games, some of them physics: draw a shield, pull pins, cut ropes, slide blocks, pour tubes. [Demo](https://johnmorrisdotca.github.io/karakuri/).
386
- - [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
466
+ `judgeWithWords(tiles, words)` says what is wrong with a grid: the tiles in runs that are not words, and the tiles standing apart from the crossword. A table draws the first as a red ring and fades the second:
387
467
 
388
- **This package is Kumimoji.** The demos of all twenty-four share one header and footer, so each links the rest.
389
- <!-- family:end -->
468
+ ```ts
469
+ import "@johnmorrisdotca/kumimoji/words";
470
+ import { decodeGrid, judgeWithWords, loadTileWords, squareAt } from "@johnmorrisdotca/kumimoji";
390
471
 
391
- ## Features
472
+ const words = await loadTileWords("english");
473
+ const crossword = decodeGrid("cat/2o/2w")!;
474
+ judgeWithWords(crossword, words).sound; // → true
475
+
476
+ const scattered = new Map(crossword);
477
+ scattered.set(squareAt(4, 4), "x"); // a tile that touches nothing
478
+ const verdict = judgeWithWords(scattered, words);
479
+ verdict.sound; // → false
480
+ [...verdict.apart]; // → ["4,4"]
481
+ ```
392
482
 
393
- - **Dealt from a seed.** `generateKumimoji` lays a crossword first and deals
394
- its tiles, so every bag can be finished.
395
- - **English and Japanese.** 144 tiles in each: the English letters, or 45 base
396
- kana where が plays as か, ゃ as や and を as お. 110,316 English words and
397
- 163,461 Japanese readings, of 2 to 15 tiles.
398
- - **Wild tiles** that take any letter or kana, and are read as that in every
399
- word they are part of.
400
- - **Trades.** A tile you cannot use goes back into the bag for three.
401
- - **Diagonals**, if chosen: every diagonal run of three or more must be a word
402
- too.
403
- - **Three lengths.** A short game, half the set, or all 144 tiles; and two
404
- English sets at once.
405
- - **The rules as plain functions.** A game in progress is a value (`TilePlay`):
406
- `deal`, `placeFromHand`, `moveOnTable`, `liftToHand`, `draw` and `trade` each
407
- return a new one.
408
- - **A table of two to eight** sharing one bag, with a last round, resigning,
409
- joining and leaving, kept as text, and a computer player for any seat.
410
- - **Help**, if chosen: the words the hand's own tiles spell.
411
- - **A table to play on.** `mountKumimoji` plays a whole game alone in plain
412
- DOM, saves it and loads it back; `KumimojiBoard` draws a grid in React.
413
- - **A tag.** `<kumimoji-table>` is the same game in one element, with no
414
- framework.
415
- - **One bag a day.** `kumimojiDailySeed(new Date())` is the same seed for
416
- everybody, worldwide.
417
- - **Played from a keyboard.** Arrow keys between squares and tiles, Enter or
418
- Space to pick up and lay; see [Accessibility](#accessibility).
419
- - **The table's words in English and Japanese**, and any other language by a
420
- table of your own. **Themeable**: every colour is a CSS variable.
483
+ ### What the computer player would lay
484
+
485
+ `bestLaying(play, words)` finds the word the computer player lays from a hand and a table, with no randomness, so a turn replays exactly after a reload:
486
+
487
+ ```ts
488
+ import "@johnmorrisdotca/kumimoji/words";
489
+ import { bestLaying, deal, generateKumimoji, loadTileWords, wordsInHand } from "@johnmorrisdotca/kumimoji";
490
+
491
+ const words = await loadTileWords("english");
492
+ const play = deal(generateKumimoji(7, "hard", 2026).givens, 7);
493
+ play.hand.join(""); // → "xbsnnny"
494
+ bestLaying(play, words)?.word; // → "by"
495
+ wordsInHand(play.hand, words); // → ["by"]
496
+ ```
497
+
498
+ `wordsInHand` is the same list a player is offered as help when the game was set up with it.
499
+
500
+ ### A computer's turn, step by step
501
+
502
+ `planComputerTurn(game, words)` lists a computer seat's whole turn as steps, in order, for a page to show one at a time; `afterComputerTurn` is the game after all of them:
503
+
504
+ ```ts
505
+ import "@johnmorrisdotca/kumimoji/words";
506
+ import { afterComputerTurn, endTurn, generateKumimoji, handCanSpell, judgeWithWords, loadTileWords, nameOf, planComputerTurn, seatPlay, startParty } from "@johnmorrisdotca/kumimoji";
507
+
508
+ const words = await loadTileWords("english");
509
+ const settings = { size: 7, level: "hard", seed: 2026, gameLength: "short", language: "english", doubleSet: false, diagonals: false, hints: false } as const;
510
+ let game = startParty(settings, generateKumimoji(7, "hard", 2026).givens, ["Ann", { name: "Computer", computer: true }]);
511
+ game = endTurn(game, judgeWithWords(seatPlay(game).tiles, words), (hand) => handCanSpell(hand, words)); // Ann passes
512
+ nameOf(game, game.turn); // → "Computer 1"
513
+
514
+ const steps = planComputerTurn(game, words);
515
+ steps.map(({ said }) => (said.kind === "laid" ? `laid ${said.word}` : said.kind)); // → ["laid enamor", "traded", "laid cate", "drew", "done"]
516
+ nameOf(afterComputerTurn(game, words), 0); // → "Ann"
517
+ ```
518
+
519
+ ### Points for a finished game
520
+
521
+ A finished game scores 10 a tile, and up to as much again for speed (this one is 770 points at a minute and a half, and 500 at fifteen minutes, when no speed bonus is left). A server that has checked a grid can award the same points the table shows:
522
+
523
+ ```ts
524
+ import "@johnmorrisdotca/kumimoji/words";
525
+ import { generateKumimoji, kumimojiPoints, loadTileWords } from "@johnmorrisdotca/kumimoji";
526
+
527
+ await loadTileWords("english");
528
+ const dealt = generateKumimoji(7, "hard", 2026);
529
+ kumimojiPoints(dealt.givens, 90_000); // → 770
530
+ kumimojiPoints(dealt.givens, 900_000); // → 500
531
+ ```
532
+
533
+ ### Check a race on a server
534
+
535
+ Players racing a seed are dealt the same bag, so a server that is handed only the seed and a finished grid checks the whole game in time proportional to the grid, with no search:
536
+
537
+ ```ts
538
+ import "@johnmorrisdotca/kumimoji/words";
539
+ import { checkKumimoji, generateKumimoji, loadTileWords } from "@johnmorrisdotca/kumimoji";
540
+
541
+ await loadTileWords("english");
542
+ const dealt = generateKumimoji(3, "hard", 44);
543
+ checkKumimoji(3, dealt.givens, "snowy", { level: "hard" }); // → { ok: true }
544
+ checkKumimoji(3, dealt.givens, "snows", { level: "hard" }); // → { ok: false, reason: "the grid does not use exactly the tiles of the bag" }
545
+ ```
546
+
547
+ ### Mount the table with options
548
+
549
+ `mountKumimoji` plays a whole game alone in one element, and hands back the game as plain data after each change:
550
+
551
+ ```ts no-run
552
+ import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
553
+
554
+ const table = mountKumimoji(document.getElementById("table")!, {
555
+ language: "japanese",
556
+ hand: 11,
557
+ level: "easy", // the most wild tiles
558
+ gameLength: "medium", // half the set
559
+ seed: 2026,
560
+ locale: "ja", // the table's own words
561
+ onFinish: (result) => console.log("done", result),
562
+ });
563
+ await table.newGame({ language: "english", hand: 7 }); // another game, on the same table
564
+ table.destroy();
565
+ ```
566
+
567
+ ### A look of your own
568
+
569
+ Every colour is a CSS variable on `.km-root`, so a stylesheet or the `theme` option can restyle the table:
570
+
571
+ ```ts no-run
572
+ import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
573
+
574
+ mountKumimoji(document.getElementById("table")!, {
575
+ theme: { "--km-felt": "#23405a", "--km-tile": "#fffdf7", "--km-wild": "#d4a017", "--km-square": "40px" },
576
+ });
577
+ ```
421
578
 
422
579
  ## How to play
423
580
 
@@ -611,7 +768,7 @@ The JSON, as `kumimojiToJSON` writes it:
611
768
  {
612
769
  "format": 1,
613
770
  "game": "kumimoji",
614
- "generator": "kumimoji 1.2.1",
771
+ "generator": "kumimoji 1.3.0",
615
772
  "size": 3,
616
773
  "level": "hard",
617
774
  "seed": 44,
@@ -707,7 +864,7 @@ turn tiles into words: `glyphOf`, `soundOf`, `wordOf`, `wildFor`, `isWild`,
707
864
 
708
865
  ## API
709
866
 
710
- The [API reference](https://johnmorrisdotca.github.io/kumimoji/api.html) lists every export of every entry point with its signature and its doc comment. It is made from the source by `pnpm site`, so it cannot fall behind the code.
867
+ The [documentation](https://johnmorrisdotca.github.io/kumimoji/docs/) includes bilingual guides, local search, signatures, parameters, return values and checked examples, built from source by `pnpm docs:site`. The [API index](https://johnmorrisdotca.github.io/kumimoji/api.html) preserves export anchors.
711
868
 
712
869
  Every function, type and constant has a doc comment, so an editor shows this
713
870
  as you type. The entries:
@@ -721,76 +878,22 @@ as you type. The entries:
721
878
  | `@johnmorrisdotca/kumimoji/element/define` | Defines `<kumimoji-table>` on the page by being imported; exports nothing |
722
879
  | `@johnmorrisdotca/kumimoji/react` | `KumimojiBoard` and `KumimojiTable` |
723
880
 
724
- ### Dealing and judging
881
+ ### The calls to learn first
725
882
 
726
- | Export | What it does |
883
+ | Call | What it does |
727
884
  | --- | --- |
728
885
  | `generateKumimoji(hand, level, seed, options?)` | A game dealt from a seed: the bag, and one grid that finishes it |
729
- | `judgeWithWords(tiles, words, rules?)` | What is wrong with a grid: the tiles in runs that are not words, the tiles apart, and whether it is sound |
730
- | `judgeGrid(tiles, isWord, readable?, rules?)` | The same against any test of a word |
731
- | `runsOf(tiles, rules?)`, `groupsOf(tiles, links?)` | Every run the grid is read by; the tiles in groups that touch |
732
- | `checkKumimoji(size, givens, answer, options?)` | Whether a grid finishes a game: the one check a server runs |
733
- | `kumimojiPoints(givens, elapsedMs)` | A finished game's points |
734
- | `readWilds(tiles, words, rules?)`, `WILD_SEARCH_MOST` | A grid with each wild that has no letter read as the one that makes its runs words, or `null`; and how far it looks before giving up |
735
- | `wordsInHand(hand, words)`, `handSpelling(play, word)` | Help: the words a hand spells, and the hand arranged as one |
736
- | `crossingFit`, `diagonalsRead` | Where a word may be laid crossing a tile, and the diagonal runs a new tile would make, for a generator or a computer player |
737
- | `seededRandom(seed)`, `shuffled(items, random)` | The seeded random a deal is drawn from |
738
-
739
- ### Tiles and grids
886
+ | `deal(bag, handSize)` and `placeFromHand`, `moveOnTable`, `liftToHand`, `draw`, `trade` | The opening hand, and each move, every one returning a new game |
887
+ | `judgeWithWords(tiles, words)` | What is wrong with a grid, or that it is sound |
888
+ | `isFinished(play, verdict)` | Whether the bag is empty, the hand used and every run a word in one piece |
889
+ | `checkKumimoji(size, givens, answer, options?)` | The one check a server runs on a finished grid |
890
+ | `loadTileWords(language)` | A word list, fetched when first needed |
891
+ | `mountKumimoji(element, options?)` and `<kumimoji-table>` | The table, in one call or one tag |
892
+ | `kumimojiToJSON`, `kumimojiFromJSON` | A game kept, and read back without trusting it |
740
893
 
741
- | Export | What it does |
742
- | --- | --- |
743
- | `squareAt(row, col)`, `placeOf(square)` | A square's key, and back |
744
- | `encodeGrid(tiles)`, `decodeGrid(code)`, `boundsOf(tiles)` | A grid as a string, and back; the rows and columns it stands within |
745
- | `lettersOf(letters)`, `sameLetters(a, b)` | How many of each tile, and whether two tallies match |
746
- | `tileFace(tile)`, `tileDescription(tile)`, `kanaTileCode(kana, wild?)` | What a tile shows; what a screen reader says; a kana's tile |
747
- | `tileKana(kana)`, `BASE_KANA`, `CORNER_FORMS`, `formsOfTile(kana)` | The tile a kana is played with, the 45 tiles, and the forms each plays as |
748
- | `familyKeyOf(language)` | Which tile of the set a code is, without the word list |
749
- | `loadTileWords`, `tileWords`, `tileWordsReady` | The word list: fetched, handed back, asked after |
750
- | `unpackTileWords`, `unpackLength`, `tileWordsFrom`, `readTileWordsWith` | How a list is read from its packed data, for the loader |
751
- | `mixShown(language)`, `lengthRows(hand?)`, `LENGTHS`, `TRY_IT` | A set and the lengths of game as a page shows them |
894
+ ### The long tables
752
895
 
753
- ### Playing, and a game kept
754
-
755
- The moves are in [Playing](#playing) above. With them:
756
-
757
- | Export | What it does |
758
- | --- | --- |
759
- | `tilesLeft(play)` | How many tiles are still in the bag |
760
- | `encodeTileProgress(play)`, `readTileProgress(code)`, `decodeTileProgress(code, bag, language?)` | A game half way as one string; its shape read; and opened again on its own bag |
761
- | `kumimojiToJSON(saved)`, `kumimojiFromJSON(text)`, `kumimojiExported(saved)` | A game as JSON, and back, or `null` |
762
- | `kumimojiToText(saved, strings?)`, `gridToText(tiles, language?)`, `kumimojiClock(ms)` | A game, a grid and a time as text |
763
- | `KUMIMOJI_EXPORT_FORMAT`, `KUMIMOJI_VERSION` | The JSON's format number, and this package's version |
764
- | `KUMIMOJI_STRINGS`, `kumimojiStrings(locale, own?)`, `kumimojiSay(line, values)` | The table's words in English and Japanese |
765
-
766
- ### A table of players
767
-
768
- | Export | What it does |
769
- | --- | --- |
770
- | `startParty(settings, bag, seats)` | A table dealt: two to eight seats, each a name or `{ name, computer }` |
771
- | `seatPlay(game)`, `withSeatPlay(game, play)` | The player to move's hand and table as a `TilePlay`, and the game with it put back |
772
- | `endTurn(game, verdict, handSpells)`, `doneRefused(…)` | End the turn; why Done is refused, if it is |
773
- | `drawAll(game)`, `mayDrawAll(game, verdict)` | Everyone draws together |
774
- | `resign(game)`, `mayResign(game)` | Give up the game |
775
- | `joinParty(game, seat)`, `joinRefused(game)`, `leaveParty(game, at)`, `leaveRefused(game, at)` | Joining and leaving a game under way |
776
- | `isOver(game)`, `winnersOf(game)`, `goesOut(game, verdict)`, `isLastTurn(game)`, `lastStanding(game)` | How the game stands and ends |
777
- | `mayTradeThisTurn(game)`, `mustTradeFirst(game, handSpells)`, `laidThisTurn(game)`, `handCanSpell(hand, words)` | What this turn may and must do |
778
- | `nameOf(game, at)`, `tidyName(name)`, `computerName(players)`, `isComputer(game, at)` | Names, and whether a seat is a computer's |
779
- | `stillIn(game)`, `nextIn(game, from)`, `partyTilesLeft(game)`, `tilesHeldBy(game, at)`, `tableTally(tiles)` | Who is in, whose turn is next, and the tiles |
780
- | `partyTilesNeeded`, `partyFits`, `partyLength`, `partyPlayersAsked` | Whether a bag is big enough for a table, for a set-up screen |
781
- | `passViewStart(game)`, `stepView(game, from, by)` | Whose table a shared screen shows |
782
- | `encodeParty(game)`, `decodeParty(code, familyKey?)`, `readSettings(value)`, `holdsItsBag(game, familyKey)`, `isPartyFor` | A table kept as text, read back and checked, and matched to the set-up it was dealt for |
783
- | `afterComputerTurn(game, words)`, `planComputerTurn(game, words)`, `COMPUTER_STEPS_MOST` | A computer seat's turn, whole or step by step |
784
- | `bestLaying(play, words, rules?)`, `tradeChoice(hand, words)`, `judgeTiles(tiles, words, rules?)` | What the computer would lay, and trade |
785
-
786
- ### Looking at the table
787
-
788
- A table with no edges is looked at through a view, which the package keeps
789
- apart from the game: `tableArea`, `fitView`, `fittedMost`, `zoomView`,
790
- `panView`, `keepInReach`, `overflows`, `squareUnder`, `edgePan` and the
791
- numbers in `TABLE`. A player may turn the table a quarter at a time with every
792
- tile kept upright: `nextTurn`, `turnPlace`, `unturnPlace`, `turnArea`,
793
- `turnView`, `typingWay` and `arrowStep`.
896
+ The tables of every export for dealing and judging, tiles and grids, playing and keeping a game, a table of players and looking at the table are in [docs/API.md](docs/API.md), and every export of every entry point, with its signature and doc comment, is in the [API reference](https://johnmorrisdotca.github.io/kumimoji/api.html). The types the package exports are listed there too.
794
897
 
795
898
  The types are `TilePlay`, `Tiles`, `GridVerdict`, `GridRules`, `Run`,
796
899
  `RunLine`, `Bounds`, `Square`, `CrossingFit`, `KumimojiDeal`,
@@ -803,65 +906,11 @@ The types are `TilePlay`, `Tiles`, `GridVerdict`, `GridRules`, `Run`,
803
906
 
804
907
  ### The table
805
908
 
806
- `mountKumimoji(element, options?)` from `@johnmorrisdotca/kumimoji/ui` deals
807
- and plays a whole game alone in the element, and returns a handle.
808
-
809
- | Option | Default | What it does |
810
- | --- | --- | --- |
811
- | `language` | `"english"` | The language of the tiles: `"english"` or `"japanese"` |
812
- | `hand` | `7` | The opening hand: 3, 7 or 11 tiles |
813
- | `level` | `"medium"` | Wild tiles: `"easy"` the most, `"medium"` some, `"hard"` none |
814
- | `gameLength` | `"short"` | `"short"`, `"medium"`, or `"full"`, all 144 tiles |
815
- | `diagonals` | `false` | Every diagonal run of three or more must be a word too |
816
- | `seed` | a new one each game | The seed the bag is dealt from |
817
- | `onFinish` | | Called when the last tile is laid and every run is a word, with the time it took |
818
- | `onChange` | | Called after every change, with the game as it would be saved |
819
- | `locale` | the page's `lang` | The language of the table's words: `"en"` or `"ja"` |
820
- | `strings` | | Words of your own, laid over the locale's |
821
- | `theme` | | CSS variables set on the table itself |
822
- | `keep` | `true` | Whether the table offers saving and loading |
823
-
824
- | Handle | What it does |
825
- | --- | --- |
826
- | `play()` | The game as it stands, or `null` while it is being dealt |
827
- | `saved()` | The game as it would be saved, or `null` while it is being dealt |
828
- | `newGame(options?)` | A new game; any of the first six options may change |
829
- | `setGame(saved)` | Put a game on the table: one read back by `kumimojiFromJSON` |
830
- | `setLocale(locale, strings?)` | Change the language of the table's words |
831
- | `destroy()` | Take the table off the page and stop its clock |
832
-
833
- To play it: tap a tile in your hand and then a square to lay it; tap a tile on
834
- the table and then a square to move it, or tap it again to take it back. A
835
- wild is given its letter before it is laid. To draw a board yourself, the same
836
- entry has `boardModel(tiles, verdict, glyphOf?)`, which works out the squares
837
- to draw and how each tile stands, with `BOARD_MARGIN` and `BOARD_LEAST`, and
838
- `KUMIMOJI_STYLE`, the table's stylesheet as a string.
909
+ `mountKumimoji(element, options?)` from `@johnmorrisdotca/kumimoji/ui` deals and plays a whole game alone in the element and returns a handle (`play()`, `saved()`, `newGame(options?)`, `setGame(saved)`, `setLocale(locale, strings?)`, `ready`, `set(options)`, `destroy()`). Its options are `language`, `hand`, `level`, `gameLength`, `diagonals`, `seed`, `onFinish`, `onChange`, `locale`, `strings`, `theme`, `keep`, `controls`, `saved` and `onError`; each, with its default, is in [docs/API.md](docs/API.md#the-table).
839
910
 
840
911
  ### The element
841
912
 
842
- `<kumimoji-table>` is `mountKumimoji` as a tag: `@johnmorrisdotca/kumimoji/element/define`
843
- defines it, and `@johnmorrisdotca/kumimoji/element` holds the class alone. Each
844
- attribute is read again when it changes, and a change to any but `lang` deals a
845
- new game. A table the rules do not offer (a hand of 5, say) draws nothing.
846
-
847
- | Attribute | Default | What it does |
848
- | --- | --- | --- |
849
- | `language` | `english` | The tiles: `english` or `japanese` kana |
850
- | `hand` | `7` | The opening hand: 3, 7 or 11 |
851
- | `level` | `medium` | Wild tiles: `easy` the most, `medium` some, `hard` none |
852
- | `length` | `short` | `short`, `medium` or `full`, all 144 tiles |
853
- | `diagonals` | off | `on` to read every diagonal run of three or more too |
854
- | `seed` | a new one each game | A whole number to deal from, or `daily` for the day's seed |
855
- | `lang` | the page's | The language of the table's words: `en` or `ja` |
856
- | `keep` | on | `off` leaves saving and loading out |
857
-
858
- It fires `kumimoji-change` after every change to the game, with the game as it
859
- would be saved as `event.detail.saved`, and `kumimoji-finish` when the last
860
- tile is laid and every run is a word, with the deal, the play and the time it
861
- took as `event.detail`; both bubble. It has `saved` (the game as it would be
862
- saved), `table` (the handle `mountKumimoji` returns), `newGame(options?)` and
863
- `setGame(saved)`. To style it, set the table's variables on `kumimoji-table
864
- .km-root`: see [Theming](#theming).
913
+ `<kumimoji-table>` is `mountKumimoji` as a tag: `@johnmorrisdotca/kumimoji/element/define` defines it. Its attributes are `language`, `hand`, `level`, `length`, `diagonals`, `seed` (a number, or `daily`), `lang` and `keep`; it fires `kumimoji-change` and `kumimoji-finish`, which bubble. Every attribute, event and property is in [docs/API.md](docs/API.md#the-element).
865
914
 
866
915
  ### The React components
867
916
 
@@ -886,39 +935,7 @@ export function Board() {
886
935
  }
887
936
  ```
888
937
 
889
- | `KumimojiBoard` prop | What it does |
890
- | --- | --- |
891
- | `tiles` | The tiles on the table: `play.tiles` |
892
- | `verdict` | From `judgeWithWords`: rings the runs that are not words and fades the tiles apart |
893
- | `glyphOf` | What to print on a tile: `words.glyphOf` for the language played |
894
- | `onSquare` | Called with a square's key when it is pressed, empty or not |
895
- | `held` | A square to ring, such as the one holding a tile picked up |
896
- | `square` | The side of a square in pixels; 36 by default |
897
- | anything else | Passed to the `<div>` |
898
-
899
- `KumimojiTable` takes every option of `mountKumimoji` as a prop, and anything
900
- else for its `<div>`. Options are read when it mounts; give it a new `key` to
901
- start over with different ones.
902
-
903
- ## Accessibility
904
-
905
- - **A keyboard plays it.** Every square and every tile in the hand is a button
906
- with a name. Tab lands on one square of the table and on one tile of the
907
- hand, the arrow keys move between squares (and Left, Right, Home and End
908
- between the hand's tiles), Enter or Space picks a tile up and lays it, and
909
- Escape lets go of a tile you have picked up. The keyboard stays where it was
910
- after each move, including on the buttons under the table, and the table's
911
- description says so.
912
- - **What is happening is said.** The line above the table, with the tiles left
913
- and the time, and the line saying which runs are not words, are polite live
914
- regions, and so is the note after saving or loading.
915
- - **Targets are big enough.** Every button is at least 44 pixels each way,
916
- which a test holds at a phone's width.
917
- - **Not by colour alone.** A run that is not a word is ringed and listed in
918
- words; a tile apart from the crossword is faded and said so.
919
- - **Motion.** There is none to reduce: the table has no animation.
920
- - **Not yet.** Letters cannot be typed to lay a tile, and a screen reader hears
921
- the table as a grid of squares, not as the words they make.
938
+ The props of `KumimojiBoard` (`tiles`, `verdict`, `glyphOf`, `onSquare`, `held`, `square`, and anything else for its `<div>`) and of `KumimojiTable` are in [docs/react.md](docs/react.md).
922
939
 
923
940
  ## Theming
924
941
 
@@ -945,7 +962,7 @@ on the table itself and so wins in light and dark alike.
945
962
 
946
963
  A slate table with ivory tiles, a little smaller:
947
964
 
948
- ```js
965
+ ```js no-run
949
966
  import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
950
967
 
951
968
  mountKumimoji(document.getElementById("table"), {
@@ -956,6 +973,47 @@ mountKumimoji(document.getElementById("table"), {
956
973
  `KumimojiBoard` draws with colours of its own; style it through its `style`
957
974
  and `className`.
958
975
 
976
+ ## Limits
977
+
978
+ | Limit | Value | Constant |
979
+ | --- | --- | --- |
980
+ | Opening hand | 3, 7 or 11 | `KUMIMOJI_HANDS` |
981
+ | A word | 2 to 15 tiles | |
982
+ | A finished grid, either way | 60 squares | `KUMIMOJI_GRID_MOST` |
983
+ | Players at a table | 2 to 8 | `KUMIMOJI_PARTY` |
984
+ | A player's name | 20 characters | `KUMIMOJI_PARTY` |
985
+ | Words one hand is offered as help | 40 | `HELP_WORDS_MOST` |
986
+ | A seed | a whole number from 0 to 4,294,967,295 | |
987
+ | Two sets at once | English only | |
988
+
989
+ ## Accessibility
990
+
991
+ - **A keyboard plays it.** Every square and every tile in the hand is a button
992
+ with a name. Tab lands on one square of the table and on one tile of the
993
+ hand, the arrow keys move between squares (and Left, Right, Home and End
994
+ between the hand's tiles), Enter or Space picks a tile up and lays it, and
995
+ Escape lets go of a tile you have picked up. The keyboard stays where it was
996
+ after each move, including on the buttons under the table, and the table's
997
+ description says so.
998
+ - **What is happening is said.** The line above the table, with the tiles left
999
+ and the time, and the line saying which runs are not words, are polite live
1000
+ regions, and so is the note after saving or loading.
1001
+ - **Targets are big enough.** Every button is at least 44 pixels each way,
1002
+ which a test holds at a phone's width.
1003
+ - **Not by colour alone.** A run that is not a word is ringed and listed in
1004
+ words; a tile apart from the crossword is faded and said so.
1005
+ - **Motion.** There is none to reduce: the table has no animation.
1006
+ - **Not yet.** Letters cannot be typed to lay a tile, and a screen reader hears
1007
+ the table as a grid of squares, not as the words they make.
1008
+
1009
+ ## Browser support
1010
+
1011
+ Any current browser: Chrome, Edge, Firefox and Safari, on a desk or a phone.
1012
+ It needs ES2020 and dynamic `import()`. The table is tested in Chromium and in
1013
+ WebKit, Safari's engine, at phone size with touch and on a desktop. The rules
1014
+ run in Node 22 and later, Deno, Bun and web workers, and load by `import` and
1015
+ by `require()`.
1016
+
959
1017
  ## Languages
960
1018
 
961
1019
  Two things here have a language, and they are chosen apart. **The tiles** are
@@ -981,27 +1039,6 @@ kumimojiStrings("en", { draw: "Robar" }).draw; // → "Robar"
981
1039
  A new language for the tiles is another matter: it needs a real dictionary of
982
1040
  that language, whose licence lets its words be shipped.
983
1041
 
984
- ## Limits
985
-
986
- | Limit | Value | Constant |
987
- | --- | --- | --- |
988
- | Opening hand | 3, 7 or 11 | `KUMIMOJI_HANDS` |
989
- | A word | 2 to 15 tiles | |
990
- | A finished grid, either way | 60 squares | `KUMIMOJI_GRID_MOST` |
991
- | Players at a table | 2 to 8 | `KUMIMOJI_PARTY` |
992
- | A player's name | 20 characters | `KUMIMOJI_PARTY` |
993
- | Words one hand is offered as help | 40 | `HELP_WORDS_MOST` |
994
- | A seed | a whole number from 0 to 4,294,967,295 | |
995
- | Two sets at once | English only | |
996
-
997
- ## Browser support
998
-
999
- Any current browser: Chrome, Edge, Firefox and Safari, on a desk or a phone.
1000
- It needs ES2020 and dynamic `import()`. The table is tested in Chromium and in
1001
- WebKit, Safari's engine, at phone size with touch and on a desktop. The rules
1002
- run in Node 22 and later, Deno, Bun and web workers, and load by `import` and
1003
- by `require()`.
1004
-
1005
1042
  ## The command line
1006
1043
 
1007
1044
  There is none, on purpose. Checking a word or seeing a deal is a few lines of
@@ -1034,24 +1071,138 @@ runs from a static page and costs nothing to host.
1034
1071
 
1035
1072
  Ideas and pull requests are welcome.
1036
1073
 
1037
- ## Contributing
1074
+ ## Architecture
1075
+
1076
+ The rules are plain functions over plain data with no DOM: a game is a value,
1077
+ every move returns the next one, and the same seed deals the same bag in every
1078
+ browser. Word lists are loaded only when a game opens, and the table is its own
1079
+ entry (`/ui` for plain DOM, `/element` for a tag, `/react` for React), so a page that wants only the
1080
+ rules carries none of it. A computer player is pure too, so its turn replays
1081
+ exactly after a reload.
1082
+
1083
+ The file-by-file tree, with a line on each source file, is in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): the rules (`grid`, `judge`, `play`, `generate`, `check`), the table of players (`party*`), the computer player (`computer*`), the Japanese tiles (`kana`), the word lists (`words*`), and the three faces (`ui`, `element`, `react`). Tests sit beside the code they test (`*.test.ts`). `scripts/` builds the demo
1084
+ and checks the package as npm packs it, `demo/` is the page published on
1085
+ GitHub Pages, and `table/` taps it in real browsers.
1086
+
1087
+ ## The name
1088
+
1089
+ *Kumimoji* (組み文字) is made of *kumi* (組み, from 組む, to put together or
1090
+ assemble) and *moji* (文字, a letter or character): letters put together,
1091
+ which is what a player does with the tiles. Say it in four even beats:
1092
+ ku-mi-mo-ji. In Japanese printing the same word names something else, several
1093
+ characters set together in the space of one; the game borrows only its plain
1094
+ sense.
1095
+
1096
+ ## Where it comes from, and where it is used
1097
+
1098
+ Kumimoji was built for [Itsutsu](https://itsutsu.com), a site for board games,
1099
+ puzzles, card games and dice games played at your own pace. *Itsutsu* (五つ) is
1100
+ Japanese for "five", after five in a row, the game the site began with. It is
1101
+ [the Kumimoji played there](https://itsutsu.com/games/kumimoji): alone, in
1102
+ races, round one device and across several.
1103
+
1104
+ ### Used by
1105
+
1106
+ - [Itsutsu](https://itsutsu.com), for its game of Kumimoji: its rules, its
1107
+ word lists and its computer player.
1108
+
1109
+ That is the whole list so far. Using Kumimoji in something? Open an
1110
+ [*Add my project*](https://github.com/johnmorrisdotca/kumimoji/issues/new?template=add-my-project.md)
1111
+ issue and we will add you.
1112
+
1113
+ ### The family
1114
+
1115
+ <!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
1116
+ Kumimoji is one of thirty-two packages, each made for the same site, each at
1117
+ [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca) or [github.com/spxis](https://github.com/spxis). The code of every one is MIT.
1118
+
1119
+ **Games — board**
1120
+
1121
+ - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
1122
+ - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
1123
+ - [Narabe](https://github.com/johnmorrisdotca/narabe) (並べ): one rules engine for abstract board games, from gomoku and Reversi to Go and checkers. [Demo](https://johnmorrisdotca.github.io/narabe/).
1124
+ - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
1125
+ - [Tenka](https://github.com/johnmorrisdotca/tenka) (天下): world conquest for two to six, on a map of the real world. [Demo](https://johnmorrisdotca.github.io/tenka/).
1126
+
1127
+ **Games — cards & tiles**
1128
+
1129
+ - [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ): a colour-card shedding game for two to eight, with the house rules people play. [Demo](https://johnmorrisdotca.github.io/hitotsu/).
1130
+ - [Jarajara](https://github.com/johnmorrisdotca/jarajara) (ジャラジャラ): mahjong tiles drawn as SVG, stacked layouts, and the matching solitaire Awase. [Demo](https://johnmorrisdotca.github.io/jarajara/).
1131
+ - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
1132
+ - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
1133
+
1134
+ **Games — puzzles**
1038
1135
 
1039
- See [CONTRIBUTING.md](./CONTRIBUTING.md). In short:
1136
+ - [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
1137
+ - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
1138
+ - [Karakuri](https://github.com/johnmorrisdotca/karakuri) (からくり): eight hyper-casual puzzle games, some of them physics: draw a shield, pull pins, cut ropes, slide blocks, pour tubes. [Demo](https://johnmorrisdotca.github.io/karakuri/).
1139
+ - [Kazu](https://github.com/johnmorrisdotca/kazu) (数): grid number puzzles: Sudoku and its variants, Futoshiki and Skyscrapers. [Demo](https://johnmorrisdotca.github.io/kazu/).
1140
+ - [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ): a turning cube for the browser, 2×2 to 7×7, with record solves to replay. [Demo](https://johnmorrisdotca.github.io/kyuubu/).
1141
+ - [Meikyuu](https://github.com/johnmorrisdotca/meikyuu) (迷宮): mazes on squares, hexagons, triangles and circles, made from a seed and drawn through with a finger or the mouse. [Demo](https://johnmorrisdotca.github.io/meikyuu/).
1142
+ - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
1143
+ - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
1144
+ - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
1145
+
1146
+ **Games — dice & random**
1147
+
1148
+ - [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
1149
+ - [Tane](https://github.com/johnmorrisdotca/tane) (種): seeded random numbers and daily seeds, the same in every browser and on every server. [Demo](https://johnmorrisdotca.github.io/tane/).
1150
+
1151
+ **Language**
1152
+
1153
+ - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
1154
+ - [Hikidashi](https://github.com/johnmorrisdotca/hikidashi) (引き出し): a drawer of small Japanese text tools: era dates, kanji numerals, readings and sentence difficulty. [Demo](https://johnmorrisdotca.github.io/hikidashi/).
1155
+ - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
1156
+
1157
+ **Geography & data**
1158
+
1159
+ - [address-plus](https://github.com/johnmorrisdotca/address-plus) (アドレスプラス): parse and format addresses in the United States, Canada, Japan, Australia, France, Germany and the United Kingdom. [Demo](https://johnmorrisdotca.github.io/address-plus/).
1160
+ - [Chizu](https://github.com/johnmorrisdotca/chizu) (地図): maps of the world and of countries' regions, in English and Japanese, with a quiz and callouts. [Demo](https://johnmorrisdotca.github.io/chizu/).
1161
+ - [Hata](https://github.com/johnmorrisdotca/hata) (旗): country and regional flags as SVG, at their true proportions and keyed by ISO code. [Demo](https://johnmorrisdotca.github.io/hata/).
1162
+ - [Kuni](https://github.com/johnmorrisdotca/kuni) (国): countries and their regions, with ISO codes and names in English and Japanese. [Demo](https://johnmorrisdotca.github.io/kuni/).
1163
+
1164
+ **Sports**
1165
+
1166
+ - [Dougu Sports](https://github.com/johnmorrisdotca/dougu-sports) (道具): sports emblems as SVG: trophies, medals, crests, and the ball and gear of every sport, with optional 3D turning. [Demo](https://johnmorrisdotca.github.io/dougu-sports/).
1167
+ - [Kyogijo Stadiums](https://github.com/johnmorrisdotca/kyogijo-stadiums) (競技場): sports fields, rinks, courts and ballparks drawn to scale as SVG, with each team and the score on them. [Demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/).
1168
+ - [Senshu Jerseys](https://github.com/johnmorrisdotca/senshu-jerseys) (選手): sports jerseys, sweaters, kits, caps, helmets and retired-number banners as SVG, front and back, with optional 2.5D turning. [Demo](https://johnmorrisdotca.github.io/senshu-jerseys/).
1169
+ - [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets) (対戦): a sport-agnostic tournament bracket, drawn as SVG, from a list of ties. [Demo](https://johnmorrisdotca.github.io/taisen-brackets/).
1170
+
1171
+ **Developer tools**
1172
+
1173
+ - [REST in Pieces](https://github.com/spxis/rest-in-pieces) (レスト・イン・ピーシーズ): seeded test data and REST APIs for frontend development, with latency and error drills. [Demo](https://spxis.github.io/rest-in-pieces/).
1174
+
1175
+
1176
+ **This package is Kumimoji.** The demos of all thirty-two share one header and footer, so each links the rest.
1177
+ <!-- family:end -->
1178
+
1179
+ ## Development
1040
1180
 
1041
1181
  ```sh
1042
- pnpm install
1043
- pnpm check # lint, types and tests
1044
- pnpm test:table # the demo in real browsers, by taps
1045
- pnpm site # build the demo into ./site, then serve it
1046
- pnpm pictures # take the README's two pictures from the built demo
1182
+ pnpm install --frozen-lockfile
1183
+ pnpm check # lint, types and tests, including the checks on the README's tables and examples
1184
+ pnpm test:table # the demo in real browsers, by taps
1185
+ pnpm test:package # pack it as npm does, install it and import every entry
1186
+ pnpm test:frameworks # the six framework examples, built from the packed tarball and played
1187
+ pnpm test:readme # every TypeScript and JavaScript example in this README, type-checked and run
1188
+ pnpm site # build the demo into ./site
1189
+ pnpm screenshots:readme # retake the README's pictures into docs/images (builds the demo first)
1047
1190
  ```
1048
1191
 
1192
+ The pictures are taken on the maintainer's Mac and are retaken only when the look changes; they are in `docs/images` and are not in the package that npm installs.
1193
+
1194
+ ## Contributing
1195
+
1196
+ See [CONTRIBUTING.md](./CONTRIBUTING.md). In short: run `pnpm check` before you push (see [Development](#development)).
1197
+
1049
1198
  Please follow the [code of conduct](./CODE_OF_CONDUCT.md). A way to make a check or a deal run for long, or markup that gets out of the drawing, is for the [security policy](./SECURITY.md), not a public issue.
1050
1199
 
1051
1200
  ## Changes
1052
1201
 
1053
1202
  See [CHANGELOG.md](./CHANGELOG.md).
1054
1203
 
1204
+ The latest release is 1.3.0: the table's standard embed attributes, the shared embed builder, and the documentation site with a checked example for every export.
1205
+
1055
1206
  ## Licence
1056
1207
 
1057
1208
  The code is [MIT](./LICENSE) © John Morris.