@johnmorrisdotca/kumimoji 1.2.0 → 1.2.2

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/CHANGELOG.md CHANGED
@@ -6,6 +6,37 @@ All notable changes to this project are written down here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.2.2] - 2026-10-06
10
+
11
+ Nothing that was exported has changed.
12
+
13
+ ### Changed
14
+
15
+ - The README takes the family's one layout, fully: a hero picture of the demo on a desk and on a phone in light and dark, a picture of each kind of table (English, kana, a run that is not a word, a hand of eleven, a wild tile), an Examples section of ten examples whose output is what they print, an Install section, and a short list of the calls to learn first. Its pictures are in `docs/images` (WebP, light and dark) and are retaken with `pnpm screenshots:readme` (it replaces `pnpm pictures` and the two JPEGs `docs/desktop.jpg` and `docs/phone.jpg`); they are not in the tarball, and `pnpm test:package` fails if one is.
16
+ - To keep the README under the 64,000 characters npm can show, the long tables of exports (dealing and judging, tiles and grids, playing, the table of players, looking at the table) and the table's, the element's and the React components' options moved to `docs/API.md`, and the source tree moved to `docs/ARCHITECTURE.md`, each with a summary and a link left in the README. Nothing was removed, and the tests that hold these tables to the code read the README and `docs/API.md` together.
17
+ - `pnpm test:readme` type-checks and runs every TypeScript and JavaScript example in the README against the built package, as a CI job of its own, and `pnpm check` holds the README to the family's lint.
18
+ - "The command line" says there is none, and now sits after the languages, where the other sections of the standard's order leave room for it.
19
+ - Repository only: the package and everything it exports are unchanged. `CONTRIBUTING.md` is the family's one text with a section of its own for Kumimoji, held to the master in johnmorrisdotca/.github by `src/family.test.js`; `ci.yml` and `pages.yml` are the family's one text (`pnpm check`, the demo, and the package on Linux, macOS and Windows), and any jobs of the package's own after them.
20
+
21
+ ### Fixed
22
+
23
+ - The API reference page wraps a long entry path instead of running about 2 px wider than a 360 px screen. Nothing the package exports has changed.
24
+
25
+ ## [1.2.1] - 2026-10-05
26
+
27
+ Nothing that was exported has changed.
28
+
29
+ ### Added
30
+
31
+ - A test holds every `@johnmorrisdotca/kumimoji@N` version pin in the README to this package's major version.
32
+
33
+ ### Changed
34
+
35
+ - The family's list, in the README and in the demo's footer, names all twenty-four packages, Karakuri and Houseki included.
36
+ - The npm description is one sentence of 250 characters or fewer, so npm and its search show it whole; it is also the repository's About text. `homepage` is the demo site and `author` is `"John Morris"`, the same in every package.
37
+ - The GitHub Actions workflows use the current versions of the actions (checkout 7, setup-node 7, pnpm/action-setup 6; configure-pages 6, upload-pages-artifact 5 and deploy-pages 5 for Pages), which clears GitHub's Node 20 deprecation warning.
38
+ - Every entry has an `import` condition beside `default`.
39
+
9
40
  ## [1.2.0] - 2026-10-01
10
41
 
11
42
  Nothing that was exported has changed; the rules, the word lists, every deal and every saved game are exactly as they were.
@@ -126,7 +157,8 @@ The first release.
126
157
  `@johnmorrisdotca/kumimoji/react`.
127
158
  - A static demo for GitHub Pages.
128
159
 
129
- [Unreleased]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.1.1...HEAD
160
+ [Unreleased]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.2.1...HEAD
161
+ [1.2.1]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.2.0...v1.2.1
130
162
  [1.1.1]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.1.0...v1.1.1
131
163
  [1.1.0]: https://github.com/johnmorrisdotca/kumimoji/releases/tag/v1.1.0
132
164
  [1.0.1]: https://github.com/johnmorrisdotca/kumimoji/releases/tag/v1.0.1
package/CONTRIBUTING.md CHANGED
@@ -1,28 +1,100 @@
1
- # Contributing to Kumimoji
1
+ # Contributing
2
2
 
3
- Thank you for helping. Bug reports, rule questions, corrections to the
4
- Japanese and pull requests are all welcome.
3
+ Thank you for helping. Bug reports, ideas, corrections to the Japanese and pull
4
+ requests are all welcome.
5
5
 
6
- ## Reporting a bug
6
+ This first part is the same in every package of the family. It is the master
7
+ text kept in
8
+ [johnmorrisdotca/.github](https://github.com/johnmorrisdotca/.github/blob/main/CONTRIBUTING.md),
9
+ copied unchanged into `scripts/community/CONTRIBUTING.md`, and a test holds
10
+ this file to that copy. What is particular to the package follows it, under
11
+ the heading "Particular to" and the package's name.
12
+
13
+ ## Before you start
14
+
15
+ Open an issue first for anything bigger than a typo, so that we can agree on the
16
+ shape before you spend time on it. Taking part follows the
17
+ [Code of Conduct](CODE_OF_CONDUCT.md); report a security concern privately, as
18
+ [SECURITY.md](SECURITY.md) says.
19
+
20
+ ## Making a change
21
+
22
+ ```sh
23
+ pnpm install
24
+ pnpm check # lint, types and tests: the same as CI
25
+ pnpm test:package # pack it as npm does, install it in an empty project, import every entry
26
+ pnpm site # build the demo into ./site, as GitHub Pages publishes it
27
+ ```
28
+
29
+ The package's own further commands (its browser tests, its command line, its
30
+ data scripts) are listed under its own heading below.
31
+
32
+ ## House rules, shared by every package of the family
33
+
34
+ - **No runtime dependencies.** Development dependencies are for tests, builds and
35
+ documentation only.
36
+ - **The core is pure.** Every function in it returns new values and never
37
+ changes what it was given.
38
+ - **Test what you change.** Tests sit beside the code they test. A rule you
39
+ change has a test that would have caught it.
40
+ - **Words a person reads come in English and Japanese.** If you cannot write the
41
+ Japanese, say so in the pull request and someone will.
42
+ - **Option values and names are kebab case.**
43
+ - **Art and sound are CC0 or public domain only**, checked at the source and
44
+ credited. Data and word lists may be under another licence that lets them be
45
+ shipped, with its notice kept in `NOTICE.md`. No GPL or LGPL code.
46
+ - **Needs Node 22 or later.**
47
+ - **A README table, example or count that a test holds to the code** changes
48
+ together with the code.
49
+ - **The family's own files are the same in every package**: `demo/family.css`,
50
+ `scripts/family-template.mjs`, `scripts/family-readme.mjs`,
51
+ `scripts/release-notes.mjs`, the files in `scripts/community/` and
52
+ `family.test.js` (in `src/`, or in `test/`). Do not edit one here. To change
53
+ one, change it in every repository at once, bump `FAMILY_TEMPLATE_VERSION` for
54
+ the template, and record the new hash in `family.test.js`. What is the
55
+ package's own goes in its own stylesheet, `demo/<name>.css`, and its page
56
+ builder, `scripts/site.mjs`.
57
+ - **The list of the family in the README is made, not written.**
58
+ `pnpm family:readme` writes it between its markers from
59
+ `scripts/family-template.mjs`.
60
+ - **The workflows are the family's too.** `ci.yml` runs `pnpm check`, the demo's
61
+ browser tests and the packed package on Linux, macOS and Windows; `pages.yml`
62
+ is the same text in every package. A package adds jobs of its own after those.
63
+
64
+ ## Pull requests
65
+
66
+ One change per pull request. Say what changed and how you checked it, and add a
67
+ line to `CHANGELOG.md` under **Unreleased**: for a change a user would notice,
68
+ and for one to the repository alone.
69
+
70
+ ## Releasing
71
+
72
+ Maintainers bump the version in `package.json` (and in `src/version.ts`, where
73
+ the package has one), move *Unreleased* to the new version in `CHANGELOG.md`,
74
+ dated, push, wait for CI and tag `vX.Y.Z`, the same as `package.json`'s version.
75
+ The Release workflow (`.github/workflows/release.yml`) checks and builds the
76
+ package, attaches the tarball to a GitHub release and publishes it to npm by
77
+ trusted publishing, with provenance and no token. A version already on npm is
78
+ not published again.
79
+
80
+ ## Particular to Kumimoji
81
+
82
+ ### Reporting a bug
7
83
 
8
84
  Open an [issue](https://github.com/johnmorrisdotca/kumimoji/issues/new?template=report-a-bug.md)
9
85
  with what you did, what you expected and what happened. A seed deals the same
10
86
  bag every time, so the table's *Save as JSON* file, or the seed with the
11
87
  language, hand and level, makes almost any bug reproducible.
12
88
 
13
- ## Making a change
89
+ ### Commands and rules
14
90
 
15
91
  ```sh
16
- git clone https://github.com/johnmorrisdotca/kumimoji
17
- cd kumimoji
18
- pnpm install
19
92
  pnpm check # lint, types and tests: the same as CI
20
93
  pnpm test:table # the demo in real browsers: builds it, then taps it
21
94
  pnpm test:package # npm pack, install the tarball, import and require every entry, read both lists
22
95
  pnpm test:frameworks # the README's examples built in React, Vue, Svelte, Angular and a plain page
23
96
  pnpm site # builds the demo into ./site
24
97
  pnpm pictures # re-takes the README's two pictures from the built demo
25
- pnpm dlx serve site # or any static server
26
98
  ```
27
99
 
28
100
  - **The rules are pure.** Everything outside `src/ui` and `src/react.tsx` is
@@ -44,7 +116,6 @@ pnpm dlx serve site # or any static server
44
116
  - **How the lists load is not to be changed lightly.** They are fetched by a
45
117
  dynamic import behind `typeof window`, so that a page's server code never
46
118
  carries them; Itsutsu relies on it.
47
- - **Test what you change.** Tests sit beside their source as `*.test.ts`.
48
119
  - **The table is tested by tapping it.** `table/*.table.mjs` are Playwright
49
120
  tests that open the built demo in Chromium and WebKit, at a phone's width by
50
121
  touch and at a desktop's by mouse, and do what a person does. After every
@@ -61,34 +132,4 @@ pnpm dlx serve site # or any static server
61
132
  each from the packed tarball and lays a tile.
62
133
  - **Every export gets a doc comment**, which a test holds, and the README's
63
134
  tables (options, theming, limits) are checked against the code.
64
- - **The demo wears the family's look.** `demo/family.css` is the same file in
65
- every sibling package's demo, and a test fails if it differs from its
66
- recorded hash. What is Kumimoji's own goes in `demo/kumimoji.css`.
67
- - **No dependencies.** The package has none at run time and should stay so.
68
135
  - **No name, art or wording of a published game.**
69
- - One change per pull request, with a line in `CHANGELOG.md` under
70
- *Unreleased*.
71
-
72
- ## House rules, shared by every package of the family
73
-
74
- - Open an issue first for anything bigger than a typo, so that we can agree on the shape before you spend time on it.
75
- - No runtime dependencies. Every function that plays or checks a game is pure: it returns new values and never changes what it was given.
76
- - Tests sit beside the code they test. A rule you change has a test that would have caught it.
77
- - Words a player reads come in English and Japanese. If you cannot write the Japanese, say so in the pull request and someone will.
78
- - Option values and names are kebab case.
79
- - Art and sound are CC0 or public domain only, checked at the source, and credited in the README. No GPL or LGPL code.
80
- - Needs Node 22 or later. A change a user would notice gets a line in `CHANGELOG.md`.
81
-
82
- ## Releasing
83
-
84
- Maintainers bump the version in `package.json` and `src/version.ts` and move
85
- *Unreleased* to the new version in `CHANGELOG.md`, dated. Pushing the tag
86
- `vX.Y.Z` runs the Release workflow, which checks that the tag matches
87
- `package.json`, runs the checks, builds the package, installs and uses it as
88
- npm packs it, attaches the tarball to a GitHub release, and publishes it to
89
- npm with provenance, through npm's trusted publishing (no token is kept). A
90
- version already on npm is not published again. The workflow can also be run
91
- by hand.
92
-
93
- By contributing you agree that your code is released under the
94
- [MIT licence](LICENSE), and to follow the [code of conduct](CODE_OF_CONDUCT.md).
package/README.md CHANGED
@@ -12,12 +12,26 @@ In English and in Japanese kana. The rules as pure, seeded TypeScript, two word
12
12
  <img alt="TypeScript" src="https://img.shields.io/badge/types-TypeScript-3178c6">
13
13
  </p>
14
14
 
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>
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> · <a href="docs/API.md">The API in full</a></p>
16
+
17
+ <table align="center">
18
+ <tr>
19
+ <td align="center" valign="top">
20
+ <picture>
21
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/hero-desk-dark.webp">
22
+ <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">
23
+ </picture>
24
+ <br><em>An English game half way, on a desk: a crossword of SYNDIC and the words that cross it.</em>
25
+ </td>
26
+ <td align="center" valign="top">
27
+ <picture>
28
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/hero-phone-dark.webp">
29
+ <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">
30
+ </picture>
31
+ <br><em>A game in kana on a phone, in the device's light or dark.</em>
32
+ </td>
33
+ </tr>
34
+ </table>
21
35
 
22
36
  A word game engine for the race to build a crossword. Every player lays their
23
37
  own tiles as words that cross, draws another when the hand is used and every
@@ -69,7 +83,7 @@ isFinished(play, judgeWithWords(play.tiles, words)); // → true
69
83
 
70
84
  NOW, then SNOW, then SNOWY. And a table to play on, in a page:
71
85
 
72
- ```js
86
+ ```js no-run
73
87
  import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
74
88
 
75
89
  mountKumimoji(document.getElementById("table"), { language: "english", hand: 7 });
@@ -90,8 +104,108 @@ Or with nothing to install, [play a game in the demo](https://johnmorrisdotca.gi
90
104
  on the や tile, and a list of 163,461 readings.
91
105
  - **Learners and teachers** of English or Japanese spelling.
92
106
 
107
+ ## Features
108
+
109
+ - **Dealt from a seed.** `generateKumimoji` lays a crossword first and deals
110
+ its tiles, so every bag can be finished.
111
+ - **English and Japanese.** 144 tiles in each: the English letters, or 45 base
112
+ kana where が plays as か, ゃ as や and を as お. 110,316 English words and
113
+ 163,461 Japanese readings, of 2 to 15 tiles.
114
+ - **Wild tiles** that take any letter or kana, and are read as that in every
115
+ word they are part of.
116
+ - **Trades.** A tile you cannot use goes back into the bag for three.
117
+ - **Diagonals**, if chosen: every diagonal run of three or more must be a word
118
+ too.
119
+ - **Three lengths.** A short game, half the set, or all 144 tiles; and two
120
+ English sets at once.
121
+ - **The rules as plain functions.** A game in progress is a value (`TilePlay`):
122
+ `deal`, `placeFromHand`, `moveOnTable`, `liftToHand`, `draw` and `trade` each
123
+ return a new one.
124
+ - **A table of two to eight** sharing one bag, with a last round, resigning,
125
+ joining and leaving, kept as text, and a computer player for any seat.
126
+ - **Help**, if chosen: the words the hand's own tiles spell.
127
+ - **A table to play on.** `mountKumimoji` plays a whole game alone in plain
128
+ DOM, saves it and loads it back; `KumimojiBoard` draws a grid in React.
129
+ - **A tag.** `<kumimoji-table>` is the same game in one element, with no
130
+ framework.
131
+ - **One bag a day.** `kumimojiDailySeed(new Date())` is the same seed for
132
+ everybody, worldwide.
133
+ - **Played from a keyboard.** Arrow keys between squares and tiles, Enter or
134
+ Space to pick up and lay; see [Accessibility](#accessibility).
135
+ - **The table's words in English and Japanese**, and any other language by a
136
+ table of your own. **Themeable**: every colour is a CSS variable.
137
+
138
+ ### What's in it
139
+
140
+ 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.
141
+
142
+ <table>
143
+ <tr>
144
+ <td align="center" valign="top" width="50%">
145
+ <picture>
146
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/english-desk-dark.webp">
147
+ <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">
148
+ </picture>
149
+ <br><em><strong>English.</strong> 144 tiles, 110,316 words.</em>
150
+ </td>
151
+ <td align="center" valign="top" width="50%">
152
+ <picture>
153
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/kana-desk-dark.webp">
154
+ <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">
155
+ </picture>
156
+ <br><em><strong>Japanese kana.</strong> 45 base kana tiles, 163,461 readings.</em>
157
+ </td>
158
+ </tr>
159
+ <tr>
160
+ <td align="center" valign="top" width="50%">
161
+ <picture>
162
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/not-a-word-desk-dark.webp">
163
+ <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">
164
+ </picture>
165
+ <br><em><strong>A run that is not a word</strong> is ringed, and named under the table.</em>
166
+ </td>
167
+ <td align="center" valign="top" width="50%">
168
+ <picture>
169
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/hand-of-eleven-desk-dark.webp">
170
+ <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">
171
+ </picture>
172
+ <br><em><strong>A hand of eleven.</strong> Hands are 3, 7 or 11.</em>
173
+ </td>
174
+ </tr>
175
+ </table>
176
+
177
+ <p align="center">
178
+ <picture>
179
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/wild-tiles-desk-dark.webp">
180
+ <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">
181
+ </picture>
182
+ <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>
183
+ </p>
184
+
93
185
  ## Use it in your project
94
186
 
187
+ ### Install
188
+
189
+ ```sh
190
+ npm install @johnmorrisdotca/kumimoji
191
+ ```
192
+
193
+ ```sh
194
+ pnpm add @johnmorrisdotca/kumimoji
195
+ ```
196
+
197
+ ```sh
198
+ yarn add @johnmorrisdotca/kumimoji
199
+ ```
200
+
201
+ 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:
202
+
203
+ ```html
204
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@johnmorrisdotca/kumimoji@1/dist/element-define.js"></script>
205
+ ```
206
+
207
+ On a server or in a script, `import "@johnmorrisdotca/kumimoji/words"` first, so that the word lists are read from the package's files.
208
+
95
209
  Kumimoji is five things, each usable without the others: **the rules**, plain
96
210
  functions over plain values; **the word lists**, loaded when first needed;
97
211
  **a table** you mount into any element; **the same table as a tag**,
@@ -217,7 +331,7 @@ onBeforeUnmount(() => table?.destroy());
217
331
 
218
332
  ### 7. Angular
219
333
 
220
- ```typescript
334
+ ```ts no-check
221
335
  import { Component, ElementRef, OnDestroy, afterNextRender, provideZonelessChangeDetection, signal, viewChild } from "@angular/core";
222
336
  import { bootstrapApplication } from "@angular/platform-browser";
223
337
  import { mountKumimoji, type KumimojiTableHandle } from "@johnmorrisdotca/kumimoji/ui";
@@ -268,146 +382,178 @@ fetches when a game is first dealt.
268
382
  phone. The rules have no DOM in them and run in Node 22 and later, Deno,
269
383
  Bun and web workers.
270
384
 
271
- ## Architecture
385
+ The cookbook, with the output of each example, is under [Examples](#examples).
272
386
 
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.
387
+ ## Examples
388
+
389
+ 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.
279
390
 
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
391
+ ### A page with nothing else
392
+
393
+ Save this as a file and open it. The tag is the whole table: set-up, tiles, hand, clock, saving and loading.
394
+
395
+ ```html
396
+ <!doctype html>
397
+ <meta charset="utf-8">
398
+ <meta name="viewport" content="width=device-width, initial-scale=1">
399
+ <title>A crossword race</title>
400
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@johnmorrisdotca/kumimoji@1/dist/element-define.js"></script>
401
+ <kumimoji-table language="english" hand="7" seed="daily"></kumimoji-table>
325
402
  ```
326
403
 
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.
404
+ ### Listen to the tag
330
405
 
331
- ## The name
406
+ 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
407
 
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.
408
+ ```html
409
+ <kumimoji-table id="game" language="japanese" hand="7" seed="2026"></kumimoji-table>
410
+ <p id="note"></p>
411
+ <script type="module">
412
+ import "https://cdn.jsdelivr.net/npm/@johnmorrisdotca/kumimoji@1/dist/element-define.js";
339
413
 
340
- ## Where it comes from, and where it is used
414
+ const game = document.getElementById("game");
415
+ game.addEventListener("kumimoji-change", (event) => {
416
+ localStorage.setItem("kumimoji", JSON.stringify(event.detail.saved));
417
+ });
418
+ game.addEventListener("kumimoji-finish", (event) => {
419
+ document.getElementById("note").textContent = `Done in ${Math.round(event.detail.elapsedMs / 1000)} seconds.`;
420
+ });
421
+ </script>
422
+ ```
341
423
 
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.
424
+ ### Deal the day's bag
347
425
 
348
- ### Used by
426
+ `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
427
 
350
- - [Itsutsu](https://itsutsu.com), for its game of Kumimoji: its rules, its
351
- word lists and its computer player.
428
+ ```ts
429
+ import "@johnmorrisdotca/kumimoji/words";
430
+ import { generateKumimoji, kumimojiDay, kumimojiDailySeed, loadTileWords } from "@johnmorrisdotca/kumimoji";
352
431
 
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.
432
+ await loadTileWords("english");
433
+ const now = new Date("2026-10-06T10:00:00Z");
434
+ kumimojiDay(now); // → "2026-10-06"
435
+ kumimojiDailySeed(now); // → 20261006
436
+ const dealt = generateKumimoji(7, "medium", kumimojiDailySeed(now));
437
+ dealt.givens.length; // → 40
438
+ dealt.givens.slice(0, 10); // → "ltlekac*rm"
439
+ ```
356
440
 
357
- ### The family
441
+ The `*` is a wild tile, which takes any letter. The bag is one character a tile, in the order the tiles come out.
358
442
 
359
- Kumimoji is one of sixteen packages, each made for the same site, each MIT, each at
360
- [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca):
361
-
362
- - [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ, the sound of something small rolling): dice, with notation, exact odds and games.
363
- - [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ, how Japanese says "cube"): a turning cube for the browser, 2×2 to 7×7.
364
- - [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ, "one"): a colour-card game, named for the call a player makes with one card left.
365
- - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ, the everyday Japanese word for a deck of playing cards): card games as pure rules.
366
- - [Tane](https://github.com/johnmorrisdotca/tane) (種, a seed, the kind you plant): seeded random numbers and daily seeds.
367
- - [Narabe](https://github.com/johnmorrisdotca/narabe) (並べ, "line them up"): a rules engine for gomoku, Reversi, Go, checkers and many more.
368
- - [Tenka](https://github.com/johnmorrisdotca/tenka) (天下, "under heaven"): a world-conquest game for two to six.
369
- - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字, "letters put together"): a crossword tile race in English and Japanese.
370
- - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ, "joining"): a line-joining puzzle.
371
- - [Jarajara](https://github.com/johnmorrisdotca/jarajara) (ジャラジャラ, the rattle of mahjong tiles being shuffled): mahjong tiles and a matching solitaire.
372
- - [Suido](https://github.com/johnmorrisdotca/suido) (水道, "waterworks"): a pipe puzzle.
373
- - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ, the Japanese word for dominoes): dominoes and Mexican Train.
374
- - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉, "words"): word lists and word-game rules.
375
- - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六, backgammon's Japanese name): backgammon and its variants.
376
- - [Kazu](https://github.com/johnmorrisdotca/kazu) (数, "number"): grid number puzzles, Sudoku and five more.
377
- - [Meikyuu](https://github.com/johnmorrisdotca/meikyuu) (迷宮, "labyrinth"): mazes to draw a line through.
378
-
379
- **This package is Kumimoji.** The demos of all sixteen share one header and footer, so each links the rest.
443
+ ### Judge a grid
380
444
 
381
- ## Features
445
+ `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:
382
446
 
383
- - **Dealt from a seed.** `generateKumimoji` lays a crossword first and deals
384
- its tiles, so every bag can be finished.
385
- - **English and Japanese.** 144 tiles in each: the English letters, or 45 base
386
- kana where が plays as か, ゃ as や and を as お. 110,316 English words and
387
- 163,461 Japanese readings, of 2 to 15 tiles.
388
- - **Wild tiles** that take any letter or kana, and are read as that in every
389
- word they are part of.
390
- - **Trades.** A tile you cannot use goes back into the bag for three.
391
- - **Diagonals**, if chosen: every diagonal run of three or more must be a word
392
- too.
393
- - **Three lengths.** A short game, half the set, or all 144 tiles; and two
394
- English sets at once.
395
- - **The rules as plain functions.** A game in progress is a value (`TilePlay`):
396
- `deal`, `placeFromHand`, `moveOnTable`, `liftToHand`, `draw` and `trade` each
397
- return a new one.
398
- - **A table of two to eight** sharing one bag, with a last round, resigning,
399
- joining and leaving, kept as text, and a computer player for any seat.
400
- - **Help**, if chosen: the words the hand's own tiles spell.
401
- - **A table to play on.** `mountKumimoji` plays a whole game alone in plain
402
- DOM, saves it and loads it back; `KumimojiBoard` draws a grid in React.
403
- - **A tag.** `<kumimoji-table>` is the same game in one element, with no
404
- framework.
405
- - **One bag a day.** `kumimojiDailySeed(new Date())` is the same seed for
406
- everybody, worldwide.
407
- - **Played from a keyboard.** Arrow keys between squares and tiles, Enter or
408
- Space to pick up and lay; see [Accessibility](#accessibility).
409
- - **The table's words in English and Japanese**, and any other language by a
410
- table of your own. **Themeable**: every colour is a CSS variable.
447
+ ```ts
448
+ import "@johnmorrisdotca/kumimoji/words";
449
+ import { decodeGrid, judgeWithWords, loadTileWords, squareAt } from "@johnmorrisdotca/kumimoji";
450
+
451
+ const words = await loadTileWords("english");
452
+ const crossword = decodeGrid("cat/2o/2w")!;
453
+ judgeWithWords(crossword, words).sound; // → true
454
+
455
+ const scattered = new Map(crossword);
456
+ scattered.set(squareAt(4, 4), "x"); // a tile that touches nothing
457
+ const verdict = judgeWithWords(scattered, words);
458
+ verdict.sound; // → false
459
+ [...verdict.apart]; // → ["4,4"]
460
+ ```
461
+
462
+ ### What the computer player would lay
463
+
464
+ `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:
465
+
466
+ ```ts
467
+ import "@johnmorrisdotca/kumimoji/words";
468
+ import { bestLaying, deal, generateKumimoji, loadTileWords, wordsInHand } from "@johnmorrisdotca/kumimoji";
469
+
470
+ const words = await loadTileWords("english");
471
+ const play = deal(generateKumimoji(7, "hard", 2026).givens, 7);
472
+ play.hand.join(""); // → "xbsnnny"
473
+ bestLaying(play, words)?.word; // → "by"
474
+ wordsInHand(play.hand, words); // → ["by"]
475
+ ```
476
+
477
+ `wordsInHand` is the same list a player is offered as help when the game was set up with it.
478
+
479
+ ### A computer's turn, step by step
480
+
481
+ `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:
482
+
483
+ ```ts
484
+ import "@johnmorrisdotca/kumimoji/words";
485
+ import { afterComputerTurn, endTurn, generateKumimoji, handCanSpell, judgeWithWords, loadTileWords, nameOf, planComputerTurn, seatPlay, startParty } from "@johnmorrisdotca/kumimoji";
486
+
487
+ const words = await loadTileWords("english");
488
+ const settings = { size: 7, level: "hard", seed: 2026, gameLength: "short", language: "english", doubleSet: false, diagonals: false, hints: false } as const;
489
+ let game = startParty(settings, generateKumimoji(7, "hard", 2026).givens, ["Ann", { name: "Computer", computer: true }]);
490
+ game = endTurn(game, judgeWithWords(seatPlay(game).tiles, words), (hand) => handCanSpell(hand, words)); // Ann passes
491
+ nameOf(game, game.turn); // → "Computer 1"
492
+
493
+ const steps = planComputerTurn(game, words);
494
+ steps.map(({ said }) => (said.kind === "laid" ? `laid ${said.word}` : said.kind)); // → ["laid enamor", "traded", "laid cate", "drew", "done"]
495
+ nameOf(afterComputerTurn(game, words), 0); // → "Ann"
496
+ ```
497
+
498
+ ### Points for a finished game
499
+
500
+ 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:
501
+
502
+ ```ts
503
+ import "@johnmorrisdotca/kumimoji/words";
504
+ import { generateKumimoji, kumimojiPoints, loadTileWords } from "@johnmorrisdotca/kumimoji";
505
+
506
+ await loadTileWords("english");
507
+ const dealt = generateKumimoji(7, "hard", 2026);
508
+ kumimojiPoints(dealt.givens, 90_000); // → 770
509
+ kumimojiPoints(dealt.givens, 900_000); // → 500
510
+ ```
511
+
512
+ ### Check a race on a server
513
+
514
+ 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:
515
+
516
+ ```ts
517
+ import "@johnmorrisdotca/kumimoji/words";
518
+ import { checkKumimoji, generateKumimoji, loadTileWords } from "@johnmorrisdotca/kumimoji";
519
+
520
+ await loadTileWords("english");
521
+ const dealt = generateKumimoji(3, "hard", 44);
522
+ checkKumimoji(3, dealt.givens, "snowy", { level: "hard" }); // → { ok: true }
523
+ checkKumimoji(3, dealt.givens, "snows", { level: "hard" }); // → { ok: false, reason: "the grid does not use exactly the tiles of the bag" }
524
+ ```
525
+
526
+ ### Mount the table with options
527
+
528
+ `mountKumimoji` plays a whole game alone in one element, and hands back the game as plain data after each change:
529
+
530
+ ```ts no-run
531
+ import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
532
+
533
+ const table = mountKumimoji(document.getElementById("table")!, {
534
+ language: "japanese",
535
+ hand: 11,
536
+ level: "easy", // the most wild tiles
537
+ gameLength: "medium", // half the set
538
+ seed: 2026,
539
+ locale: "ja", // the table's own words
540
+ onFinish: (result) => console.log("done", result),
541
+ });
542
+ await table.newGame({ language: "english", hand: 7 }); // another game, on the same table
543
+ table.destroy();
544
+ ```
545
+
546
+ ### A look of your own
547
+
548
+ Every colour is a CSS variable on `.km-root`, so a stylesheet or the `theme` option can restyle the table:
549
+
550
+ ```ts no-run
551
+ import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
552
+
553
+ mountKumimoji(document.getElementById("table")!, {
554
+ theme: { "--km-felt": "#23405a", "--km-tile": "#fffdf7", "--km-wild": "#d4a017", "--km-square": "40px" },
555
+ });
556
+ ```
411
557
 
412
558
  ## How to play
413
559
 
@@ -601,7 +747,7 @@ The JSON, as `kumimojiToJSON` writes it:
601
747
  {
602
748
  "format": 1,
603
749
  "game": "kumimoji",
604
- "generator": "kumimoji 1.2.0",
750
+ "generator": "kumimoji 1.2.2",
605
751
  "size": 3,
606
752
  "level": "hard",
607
753
  "seed": 44,
@@ -711,76 +857,22 @@ as you type. The entries:
711
857
  | `@johnmorrisdotca/kumimoji/element/define` | Defines `<kumimoji-table>` on the page by being imported; exports nothing |
712
858
  | `@johnmorrisdotca/kumimoji/react` | `KumimojiBoard` and `KumimojiTable` |
713
859
 
714
- ### Dealing and judging
860
+ ### The calls to learn first
715
861
 
716
- | Export | What it does |
862
+ | Call | What it does |
717
863
  | --- | --- |
718
864
  | `generateKumimoji(hand, level, seed, options?)` | A game dealt from a seed: the bag, and one grid that finishes it |
719
- | `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 |
720
- | `judgeGrid(tiles, isWord, readable?, rules?)` | The same against any test of a word |
721
- | `runsOf(tiles, rules?)`, `groupsOf(tiles, links?)` | Every run the grid is read by; the tiles in groups that touch |
722
- | `checkKumimoji(size, givens, answer, options?)` | Whether a grid finishes a game: the one check a server runs |
723
- | `kumimojiPoints(givens, elapsedMs)` | A finished game's points |
724
- | `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 |
725
- | `wordsInHand(hand, words)`, `handSpelling(play, word)` | Help: the words a hand spells, and the hand arranged as one |
726
- | `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 |
727
- | `seededRandom(seed)`, `shuffled(items, random)` | The seeded random a deal is drawn from |
728
-
729
- ### Tiles and grids
865
+ | `deal(bag, handSize)` and `placeFromHand`, `moveOnTable`, `liftToHand`, `draw`, `trade` | The opening hand, and each move, every one returning a new game |
866
+ | `judgeWithWords(tiles, words)` | What is wrong with a grid, or that it is sound |
867
+ | `isFinished(play, verdict)` | Whether the bag is empty, the hand used and every run a word in one piece |
868
+ | `checkKumimoji(size, givens, answer, options?)` | The one check a server runs on a finished grid |
869
+ | `loadTileWords(language)` | A word list, fetched when first needed |
870
+ | `mountKumimoji(element, options?)` and `<kumimoji-table>` | The table, in one call or one tag |
871
+ | `kumimojiToJSON`, `kumimojiFromJSON` | A game kept, and read back without trusting it |
730
872
 
731
- | Export | What it does |
732
- | --- | --- |
733
- | `squareAt(row, col)`, `placeOf(square)` | A square's key, and back |
734
- | `encodeGrid(tiles)`, `decodeGrid(code)`, `boundsOf(tiles)` | A grid as a string, and back; the rows and columns it stands within |
735
- | `lettersOf(letters)`, `sameLetters(a, b)` | How many of each tile, and whether two tallies match |
736
- | `tileFace(tile)`, `tileDescription(tile)`, `kanaTileCode(kana, wild?)` | What a tile shows; what a screen reader says; a kana's tile |
737
- | `tileKana(kana)`, `BASE_KANA`, `CORNER_FORMS`, `formsOfTile(kana)` | The tile a kana is played with, the 45 tiles, and the forms each plays as |
738
- | `familyKeyOf(language)` | Which tile of the set a code is, without the word list |
739
- | `loadTileWords`, `tileWords`, `tileWordsReady` | The word list: fetched, handed back, asked after |
740
- | `unpackTileWords`, `unpackLength`, `tileWordsFrom`, `readTileWordsWith` | How a list is read from its packed data, for the loader |
741
- | `mixShown(language)`, `lengthRows(hand?)`, `LENGTHS`, `TRY_IT` | A set and the lengths of game as a page shows them |
742
-
743
- ### Playing, and a game kept
744
-
745
- The moves are in [Playing](#playing) above. With them:
746
-
747
- | Export | What it does |
748
- | --- | --- |
749
- | `tilesLeft(play)` | How many tiles are still in the bag |
750
- | `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 |
751
- | `kumimojiToJSON(saved)`, `kumimojiFromJSON(text)`, `kumimojiExported(saved)` | A game as JSON, and back, or `null` |
752
- | `kumimojiToText(saved, strings?)`, `gridToText(tiles, language?)`, `kumimojiClock(ms)` | A game, a grid and a time as text |
753
- | `KUMIMOJI_EXPORT_FORMAT`, `KUMIMOJI_VERSION` | The JSON's format number, and this package's version |
754
- | `KUMIMOJI_STRINGS`, `kumimojiStrings(locale, own?)`, `kumimojiSay(line, values)` | The table's words in English and Japanese |
873
+ ### The long tables
755
874
 
756
- ### A table of players
757
-
758
- | Export | What it does |
759
- | --- | --- |
760
- | `startParty(settings, bag, seats)` | A table dealt: two to eight seats, each a name or `{ name, computer }` |
761
- | `seatPlay(game)`, `withSeatPlay(game, play)` | The player to move's hand and table as a `TilePlay`, and the game with it put back |
762
- | `endTurn(game, verdict, handSpells)`, `doneRefused(…)` | End the turn; why Done is refused, if it is |
763
- | `drawAll(game)`, `mayDrawAll(game, verdict)` | Everyone draws together |
764
- | `resign(game)`, `mayResign(game)` | Give up the game |
765
- | `joinParty(game, seat)`, `joinRefused(game)`, `leaveParty(game, at)`, `leaveRefused(game, at)` | Joining and leaving a game under way |
766
- | `isOver(game)`, `winnersOf(game)`, `goesOut(game, verdict)`, `isLastTurn(game)`, `lastStanding(game)` | How the game stands and ends |
767
- | `mayTradeThisTurn(game)`, `mustTradeFirst(game, handSpells)`, `laidThisTurn(game)`, `handCanSpell(hand, words)` | What this turn may and must do |
768
- | `nameOf(game, at)`, `tidyName(name)`, `computerName(players)`, `isComputer(game, at)` | Names, and whether a seat is a computer's |
769
- | `stillIn(game)`, `nextIn(game, from)`, `partyTilesLeft(game)`, `tilesHeldBy(game, at)`, `tableTally(tiles)` | Who is in, whose turn is next, and the tiles |
770
- | `partyTilesNeeded`, `partyFits`, `partyLength`, `partyPlayersAsked` | Whether a bag is big enough for a table, for a set-up screen |
771
- | `passViewStart(game)`, `stepView(game, from, by)` | Whose table a shared screen shows |
772
- | `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 |
773
- | `afterComputerTurn(game, words)`, `planComputerTurn(game, words)`, `COMPUTER_STEPS_MOST` | A computer seat's turn, whole or step by step |
774
- | `bestLaying(play, words, rules?)`, `tradeChoice(hand, words)`, `judgeTiles(tiles, words, rules?)` | What the computer would lay, and trade |
775
-
776
- ### Looking at the table
777
-
778
- A table with no edges is looked at through a view, which the package keeps
779
- apart from the game: `tableArea`, `fitView`, `fittedMost`, `zoomView`,
780
- `panView`, `keepInReach`, `overflows`, `squareUnder`, `edgePan` and the
781
- numbers in `TABLE`. A player may turn the table a quarter at a time with every
782
- tile kept upright: `nextTurn`, `turnPlace`, `unturnPlace`, `turnArea`,
783
- `turnView`, `typingWay` and `arrowStep`.
875
+ 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.
784
876
 
785
877
  The types are `TilePlay`, `Tiles`, `GridVerdict`, `GridRules`, `Run`,
786
878
  `RunLine`, `Bounds`, `Square`, `CrossingFit`, `KumimojiDeal`,
@@ -793,65 +885,11 @@ The types are `TilePlay`, `Tiles`, `GridVerdict`, `GridRules`, `Run`,
793
885
 
794
886
  ### The table
795
887
 
796
- `mountKumimoji(element, options?)` from `@johnmorrisdotca/kumimoji/ui` deals
797
- and plays a whole game alone in the element, and returns a handle.
798
-
799
- | Option | Default | What it does |
800
- | --- | --- | --- |
801
- | `language` | `"english"` | The language of the tiles: `"english"` or `"japanese"` |
802
- | `hand` | `7` | The opening hand: 3, 7 or 11 tiles |
803
- | `level` | `"medium"` | Wild tiles: `"easy"` the most, `"medium"` some, `"hard"` none |
804
- | `gameLength` | `"short"` | `"short"`, `"medium"`, or `"full"`, all 144 tiles |
805
- | `diagonals` | `false` | Every diagonal run of three or more must be a word too |
806
- | `seed` | a new one each game | The seed the bag is dealt from |
807
- | `onFinish` | | Called when the last tile is laid and every run is a word, with the time it took |
808
- | `onChange` | | Called after every change, with the game as it would be saved |
809
- | `locale` | the page's `lang` | The language of the table's words: `"en"` or `"ja"` |
810
- | `strings` | | Words of your own, laid over the locale's |
811
- | `theme` | | CSS variables set on the table itself |
812
- | `keep` | `true` | Whether the table offers saving and loading |
813
-
814
- | Handle | What it does |
815
- | --- | --- |
816
- | `play()` | The game as it stands, or `null` while it is being dealt |
817
- | `saved()` | The game as it would be saved, or `null` while it is being dealt |
818
- | `newGame(options?)` | A new game; any of the first six options may change |
819
- | `setGame(saved)` | Put a game on the table: one read back by `kumimojiFromJSON` |
820
- | `setLocale(locale, strings?)` | Change the language of the table's words |
821
- | `destroy()` | Take the table off the page and stop its clock |
822
-
823
- To play it: tap a tile in your hand and then a square to lay it; tap a tile on
824
- the table and then a square to move it, or tap it again to take it back. A
825
- wild is given its letter before it is laid. To draw a board yourself, the same
826
- entry has `boardModel(tiles, verdict, glyphOf?)`, which works out the squares
827
- to draw and how each tile stands, with `BOARD_MARGIN` and `BOARD_LEAST`, and
828
- `KUMIMOJI_STYLE`, the table's stylesheet as a string.
888
+ `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?)`, `destroy()`). Its options are `language`, `hand`, `level`, `gameLength`, `diagonals`, `seed`, `onFinish`, `onChange`, `locale`, `strings`, `theme` and `keep`; each, with its default, is in [docs/API.md](docs/API.md#the-table).
829
889
 
830
890
  ### The element
831
891
 
832
- `<kumimoji-table>` is `mountKumimoji` as a tag: `@johnmorrisdotca/kumimoji/element/define`
833
- defines it, and `@johnmorrisdotca/kumimoji/element` holds the class alone. Each
834
- attribute is read again when it changes, and a change to any but `lang` deals a
835
- new game. A table the rules do not offer (a hand of 5, say) draws nothing.
836
-
837
- | Attribute | Default | What it does |
838
- | --- | --- | --- |
839
- | `language` | `english` | The tiles: `english` or `japanese` kana |
840
- | `hand` | `7` | The opening hand: 3, 7 or 11 |
841
- | `level` | `medium` | Wild tiles: `easy` the most, `medium` some, `hard` none |
842
- | `length` | `short` | `short`, `medium` or `full`, all 144 tiles |
843
- | `diagonals` | off | `on` to read every diagonal run of three or more too |
844
- | `seed` | a new one each game | A whole number to deal from, or `daily` for the day's seed |
845
- | `lang` | the page's | The language of the table's words: `en` or `ja` |
846
- | `keep` | on | `off` leaves saving and loading out |
847
-
848
- It fires `kumimoji-change` after every change to the game, with the game as it
849
- would be saved as `event.detail.saved`, and `kumimoji-finish` when the last
850
- tile is laid and every run is a word, with the deal, the play and the time it
851
- took as `event.detail`; both bubble. It has `saved` (the game as it would be
852
- saved), `table` (the handle `mountKumimoji` returns), `newGame(options?)` and
853
- `setGame(saved)`. To style it, set the table's variables on `kumimoji-table
854
- .km-root`: see [Theming](#theming).
892
+ `<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).
855
893
 
856
894
  ### The React components
857
895
 
@@ -890,26 +928,6 @@ export function Board() {
890
928
  else for its `<div>`. Options are read when it mounts; give it a new `key` to
891
929
  start over with different ones.
892
930
 
893
- ## Accessibility
894
-
895
- - **A keyboard plays it.** Every square and every tile in the hand is a button
896
- with a name. Tab lands on one square of the table and on one tile of the
897
- hand, the arrow keys move between squares (and Left, Right, Home and End
898
- between the hand's tiles), Enter or Space picks a tile up and lays it, and
899
- Escape lets go of a tile you have picked up. The keyboard stays where it was
900
- after each move, including on the buttons under the table, and the table's
901
- description says so.
902
- - **What is happening is said.** The line above the table, with the tiles left
903
- and the time, and the line saying which runs are not words, are polite live
904
- regions, and so is the note after saving or loading.
905
- - **Targets are big enough.** Every button is at least 44 pixels each way,
906
- which a test holds at a phone's width.
907
- - **Not by colour alone.** A run that is not a word is ringed and listed in
908
- words; a tile apart from the crossword is faded and said so.
909
- - **Motion.** There is none to reduce: the table has no animation.
910
- - **Not yet.** Letters cannot be typed to lay a tile, and a screen reader hears
911
- the table as a grid of squares, not as the words they make.
912
-
913
931
  ## Theming
914
932
 
915
933
  Every colour is a CSS variable on `.km-root`. Set them in your stylesheet on
@@ -935,7 +953,7 @@ on the table itself and so wins in light and dark alike.
935
953
 
936
954
  A slate table with ivory tiles, a little smaller:
937
955
 
938
- ```js
956
+ ```js no-run
939
957
  import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
940
958
 
941
959
  mountKumimoji(document.getElementById("table"), {
@@ -946,6 +964,47 @@ mountKumimoji(document.getElementById("table"), {
946
964
  `KumimojiBoard` draws with colours of its own; style it through its `style`
947
965
  and `className`.
948
966
 
967
+ ## Limits
968
+
969
+ | Limit | Value | Constant |
970
+ | --- | --- | --- |
971
+ | Opening hand | 3, 7 or 11 | `KUMIMOJI_HANDS` |
972
+ | A word | 2 to 15 tiles | |
973
+ | A finished grid, either way | 60 squares | `KUMIMOJI_GRID_MOST` |
974
+ | Players at a table | 2 to 8 | `KUMIMOJI_PARTY` |
975
+ | A player's name | 20 characters | `KUMIMOJI_PARTY` |
976
+ | Words one hand is offered as help | 40 | `HELP_WORDS_MOST` |
977
+ | A seed | a whole number from 0 to 4,294,967,295 | |
978
+ | Two sets at once | English only | |
979
+
980
+ ## Accessibility
981
+
982
+ - **A keyboard plays it.** Every square and every tile in the hand is a button
983
+ with a name. Tab lands on one square of the table and on one tile of the
984
+ hand, the arrow keys move between squares (and Left, Right, Home and End
985
+ between the hand's tiles), Enter or Space picks a tile up and lays it, and
986
+ Escape lets go of a tile you have picked up. The keyboard stays where it was
987
+ after each move, including on the buttons under the table, and the table's
988
+ description says so.
989
+ - **What is happening is said.** The line above the table, with the tiles left
990
+ and the time, and the line saying which runs are not words, are polite live
991
+ regions, and so is the note after saving or loading.
992
+ - **Targets are big enough.** Every button is at least 44 pixels each way,
993
+ which a test holds at a phone's width.
994
+ - **Not by colour alone.** A run that is not a word is ringed and listed in
995
+ words; a tile apart from the crossword is faded and said so.
996
+ - **Motion.** There is none to reduce: the table has no animation.
997
+ - **Not yet.** Letters cannot be typed to lay a tile, and a screen reader hears
998
+ the table as a grid of squares, not as the words they make.
999
+
1000
+ ## Browser support
1001
+
1002
+ Any current browser: Chrome, Edge, Firefox and Safari, on a desk or a phone.
1003
+ It needs ES2020 and dynamic `import()`. The table is tested in Chromium and in
1004
+ WebKit, Safari's engine, at phone size with touch and on a desktop. The rules
1005
+ run in Node 22 and later, Deno, Bun and web workers, and load by `import` and
1006
+ by `require()`.
1007
+
949
1008
  ## Languages
950
1009
 
951
1010
  Two things here have a language, and they are chosen apart. **The tiles** are
@@ -971,27 +1030,6 @@ kumimojiStrings("en", { draw: "Robar" }).draw; // → "Robar"
971
1030
  A new language for the tiles is another matter: it needs a real dictionary of
972
1031
  that language, whose licence lets its words be shipped.
973
1032
 
974
- ## Limits
975
-
976
- | Limit | Value | Constant |
977
- | --- | --- | --- |
978
- | Opening hand | 3, 7 or 11 | `KUMIMOJI_HANDS` |
979
- | A word | 2 to 15 tiles | |
980
- | A finished grid, either way | 60 squares | `KUMIMOJI_GRID_MOST` |
981
- | Players at a table | 2 to 8 | `KUMIMOJI_PARTY` |
982
- | A player's name | 20 characters | `KUMIMOJI_PARTY` |
983
- | Words one hand is offered as help | 40 | `HELP_WORDS_MOST` |
984
- | A seed | a whole number from 0 to 4,294,967,295 | |
985
- | Two sets at once | English only | |
986
-
987
- ## Browser support
988
-
989
- Any current browser: Chrome, Edge, Firefox and Safari, on a desk or a phone.
990
- It needs ES2020 and dynamic `import()`. The table is tested in Chromium and in
991
- WebKit, Safari's engine, at phone size with touch and on a desktop. The rules
992
- run in Node 22 and later, Deno, Bun and web workers, and load by `import` and
993
- by `require()`.
994
-
995
1033
  ## The command line
996
1034
 
997
1035
  There is none, on purpose. Checking a word or seeing a deal is a few lines of
@@ -1024,24 +1062,106 @@ runs from a static page and costs nothing to host.
1024
1062
 
1025
1063
  Ideas and pull requests are welcome.
1026
1064
 
1027
- ## Contributing
1065
+ ## Architecture
1066
+
1067
+ The rules are plain functions over plain data with no DOM: a game is a value,
1068
+ every move returns the next one, and the same seed deals the same bag in every
1069
+ browser. Word lists are loaded only when a game opens, and the table is its own
1070
+ entry (`/ui` for plain DOM, `/element` for a tag, `/react` for React), so a page that wants only the
1071
+ rules carries none of it. A computer player is pure too, so its turn replays
1072
+ exactly after a reload.
1028
1073
 
1029
- See [CONTRIBUTING.md](./CONTRIBUTING.md). In short:
1074
+ 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
1075
+ and checks the package as npm packs it, `demo/` is the page published on
1076
+ GitHub Pages, and `table/` taps it in real browsers.
1077
+
1078
+ ## The name
1079
+
1080
+ *Kumimoji* (組み文字) is made of *kumi* (組み, from 組む, to put together or
1081
+ assemble) and *moji* (文字, a letter or character): letters put together,
1082
+ which is what a player does with the tiles. Say it in four even beats:
1083
+ ku-mi-mo-ji. In Japanese printing the same word names something else, several
1084
+ characters set together in the space of one; the game borrows only its plain
1085
+ sense.
1086
+
1087
+ ## Where it comes from, and where it is used
1088
+
1089
+ Kumimoji was built for [Itsutsu](https://itsutsu.com), a site for board games,
1090
+ puzzles, card games and dice games played at your own pace. *Itsutsu* (五つ) is
1091
+ Japanese for "five", after five in a row, the game the site began with. It is
1092
+ [the Kumimoji played there](https://itsutsu.com/games/kumimoji): alone, in
1093
+ races, round one device and across several.
1094
+
1095
+ ### Used by
1096
+
1097
+ - [Itsutsu](https://itsutsu.com), for its game of Kumimoji: its rules, its
1098
+ word lists and its computer player.
1099
+
1100
+ That is the whole list so far. Using Kumimoji in something? Open an
1101
+ [*Add my project*](https://github.com/johnmorrisdotca/kumimoji/issues/new?template=add-my-project.md)
1102
+ issue and we will add you.
1103
+
1104
+ ### The family
1105
+
1106
+ <!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
1107
+ Kumimoji is one of twenty-four packages, each made for the same site, each at
1108
+ [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
1109
+
1110
+ - [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/).
1111
+ - [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/).
1112
+ - [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/).
1113
+ - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
1114
+ - [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/).
1115
+ - [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/).
1116
+ - [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/).
1117
+ - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
1118
+ - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
1119
+ - [Jarajara](https://github.com/johnmorrisdotca/jarajara) (ジャラジャラ): mahjong tiles drawn as SVG, stacked layouts, and the matching solitaire Awase. [Demo](https://johnmorrisdotca.github.io/jarajara/).
1120
+ - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
1121
+ - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
1122
+ - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
1123
+ - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
1124
+ - [Kazu](https://github.com/johnmorrisdotca/kazu) (数): grid number puzzles: Sudoku and its variants, Futoshiki and Skyscrapers. [Demo](https://johnmorrisdotca.github.io/kazu/).
1125
+ - [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/).
1126
+ - [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/).
1127
+ - [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/).
1128
+ - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
1129
+ - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
1130
+ - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
1131
+ - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
1132
+ - [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/).
1133
+ - [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/).
1134
+
1135
+ **This package is Kumimoji.** The demos of all twenty-four share one header and footer, so each links the rest.
1136
+ <!-- family:end -->
1137
+
1138
+ ## Development
1030
1139
 
1031
1140
  ```sh
1032
- pnpm install
1033
- pnpm check # lint, types and tests
1034
- pnpm test:table # the demo in real browsers, by taps
1035
- pnpm site # build the demo into ./site, then serve it
1036
- pnpm pictures # take the README's two pictures from the built demo
1141
+ pnpm install --frozen-lockfile
1142
+ pnpm check # lint, types and tests, including the checks on the README's tables and examples
1143
+ pnpm test:table # the demo in real browsers, by taps
1144
+ pnpm test:package # pack it as npm does, install it and import every entry
1145
+ pnpm test:frameworks # the six framework examples, built from the packed tarball and played
1146
+ pnpm test:readme # every TypeScript and JavaScript example in this README, type-checked and run
1147
+ pnpm site # build the demo into ./site
1148
+ pnpm screenshots:readme # retake the README's pictures into docs/images (builds the demo first)
1037
1149
  ```
1038
1150
 
1151
+ 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.
1152
+
1153
+ ## Contributing
1154
+
1155
+ See [CONTRIBUTING.md](./CONTRIBUTING.md). In short: run `pnpm check` before you push (see [Development](#development)).
1156
+
1039
1157
  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.
1040
1158
 
1041
1159
  ## Changes
1042
1160
 
1043
1161
  See [CHANGELOG.md](./CHANGELOG.md).
1044
1162
 
1163
+ The latest release is 1.2.2: the README takes the family's full layout, with pictures of the table and examples that are run.
1164
+
1045
1165
  ## Licence
1046
1166
 
1047
1167
  The code is [MIT](./LICENSE) © John Morris.
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The version of this package, as `package.json` has it. A test holds the two together. */
2
- export declare const KUMIMOJI_VERSION = "1.2.0";
2
+ export declare const KUMIMOJI_VERSION = "1.2.2";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The version of this package, as `package.json` has it. A test holds the two together. */
2
- export const KUMIMOJI_VERSION = "1.2.0";
2
+ export const KUMIMOJI_VERSION = "1.2.2";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@johnmorrisdotca/kumimoji",
3
- "version": "1.2.0",
4
- "description": "Crossword tile race: a word game engine where each player builds a crossword from a bag of letter tiles, in English or Japanese kana, alone against the clock or at a table of two to eight. Seeded deals, word checking, wild tiles, a computer player, and a playable table for React, Vue, Svelte, Angular or plain HTML. Zero dependencies.",
3
+ "version": "1.2.2",
4
+ "description": "Crossword tile race: each player builds a crossword from a bag of letter tiles, in English or Japanese kana, alone against the clock or at a table of two to eight. Seeded deals, a computer player and a playable table. Zero dependencies.",
5
5
  "keywords": [
6
6
  "word-game",
7
7
  "custom-element",
@@ -79,11 +79,7 @@
79
79
  "url": "git+https://github.com/johnmorrisdotca/kumimoji.git"
80
80
  },
81
81
  "license": "MIT AND CC-BY-SA-4.0",
82
- "author": {
83
- "name": "John Morris",
84
- "email": "john@johnmorris.ca",
85
- "url": "https://johnmorris.ca"
86
- },
82
+ "author": "John Morris",
87
83
  "type": "module",
88
84
  "main": "./dist/index.js",
89
85
  "module": "./dist/index.js",
@@ -91,26 +87,32 @@
91
87
  "exports": {
92
88
  ".": {
93
89
  "types": "./dist/index.d.ts",
90
+ "import": "./dist/index.js",
94
91
  "default": "./dist/index.js"
95
92
  },
96
93
  "./words": {
97
94
  "types": "./dist/words.d.ts",
95
+ "import": "./dist/words.js",
98
96
  "default": "./dist/words.js"
99
97
  },
100
98
  "./ui": {
101
99
  "types": "./dist/ui.d.ts",
100
+ "import": "./dist/ui.js",
102
101
  "default": "./dist/ui.js"
103
102
  },
104
103
  "./react": {
105
104
  "types": "./dist/react.d.ts",
105
+ "import": "./dist/react.js",
106
106
  "default": "./dist/react.js"
107
107
  },
108
108
  "./element": {
109
109
  "types": "./dist/element.d.ts",
110
+ "import": "./dist/element.js",
110
111
  "default": "./dist/element.js"
111
112
  },
112
113
  "./element/define": {
113
114
  "types": "./dist/element-define.d.ts",
115
+ "import": "./dist/element-define.js",
114
116
  "default": "./dist/element-define.js"
115
117
  }
116
118
  },
@@ -137,12 +139,14 @@
137
139
  "test": "vitest run",
138
140
  "test:table": "pnpm site && playwright test",
139
141
  "test:package": "pnpm build && node scripts/check-package.mjs",
142
+ "family:readme": "node scripts/family-readme.mjs",
140
143
  "test:frameworks": "pnpm build && node scripts/check-frameworks.mjs",
141
144
  "check": "pnpm lint && pnpm typecheck && pnpm test",
142
145
  "site": "pnpm build && node scripts/site.mjs",
143
- "pictures": "pnpm site && node scripts/readme-pictures.mjs",
144
146
  "docs:make": "node scripts/docs.mjs",
145
- "prepublishOnly": "pnpm check && pnpm build"
147
+ "prepublishOnly": "pnpm check && pnpm build",
148
+ "screenshots:readme": "pnpm site && node scripts/readme-pictures.mjs",
149
+ "test:readme": "pnpm build && node scripts/check-readme-examples.mjs"
146
150
  },
147
151
  "peerDependencies": {
148
152
  "react": ">=18"