@johnmorrisdotca/kumimoji 1.3.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ import { type KumimojiSaved } from "../export.ts";
2
2
  import type { KumimojiDeal, KumimojiLanguage, KumimojiLength, KumimojiLevel } from "../kumimoji.types.ts";
3
3
  import { type TilePlay } from "../play.ts";
4
4
  import { type KumimojiLocale, type KumimojiStrings } from "../strings.ts";
5
+ import { type Cloth } from "./cloth.ts";
5
6
  /**
6
7
  * Optional initial game, presentation, readiness and callback choices.
7
8
  * With nothing, it is a short English game from a hand of seven, with some wild tiles.
@@ -39,6 +40,8 @@ export type KumimojiTableOptions = {
39
40
  strings?: Partial<KumimojiStrings>;
40
41
  /** CSS variables set on the table itself, which win in light and dark alike: `{ "--km-felt": "#23405a" }`. */
41
42
  theme?: Readonly<Record<string, string>>;
43
+ /** The cloth the table is laid in: "green", "blue", "red", "black" or "wood", the family's five. `theme` is laid over it. */
44
+ cloth?: Cloth;
42
45
  /** Whether the table offers saving and loading. True by default. */
43
46
  keep?: boolean;
44
47
  /** Show native play-action buttons; a host hiding them supplies its own actions. */
@@ -63,7 +66,7 @@ export type KumimojiTableHandle = {
63
66
  /** Settles once the initial deal or restored game is ready. */
64
67
  readonly ready: Promise<void>;
65
68
  /** Change presentation and controls without redealing. */
66
- set: (options: Pick<KumimojiTableOptions, "locale" | "strings" | "theme" | "keep" | "controls">) => void;
69
+ set: (options: Pick<KumimojiTableOptions, "locale" | "strings" | "cloth" | "theme" | "keep" | "controls">) => void;
67
70
  /** The game as it stands, or null while it is being dealt. */
68
71
  play: () => TilePlay | null;
69
72
  /** The game as it would be saved (`kumimojiToJSON`), or null while it is being dealt. */
package/dist/ui/mount.js CHANGED
@@ -5,6 +5,7 @@ import { deal, isFinished, liftToHand, moveOnTable, placeFromHand, swapWithHand
5
5
  import { kumimojiStrings } from "../strings.js";
6
6
  import { KUMIMOJI_HANDS } from "../tiles.constants.js";
7
7
  import { loadTileWords } from "../tileWords.js";
8
+ import { clothVars, isCloth } from "./cloth.js";
8
9
  import { button, node, tableDrawing } from "./draw.js";
9
10
  import { KUMIMOJI_STYLE } from "./style.js";
10
11
  /** Hand a string to the person as a file of that name. */
@@ -80,8 +81,17 @@ export function mountKumimoji(target, options = {}) {
80
81
  const root = node("div", "km-root");
81
82
  root.dataset.testid = "km-root";
82
83
  root.dataset.state = "dealing";
83
- for (const [name, value] of Object.entries(current.theme ?? {}))
84
- root.style.setProperty(name, value);
84
+ const wear = (cloth, theme) => {
85
+ for (const name of Object.keys(clothVars("green")))
86
+ root.style.removeProperty(name);
87
+ for (const [name, value] of Object.entries({ ...clothVars(cloth), ...theme }))
88
+ root.style.setProperty(name, value);
89
+ if (isCloth(cloth))
90
+ root.dataset.cloth = cloth;
91
+ else
92
+ delete root.dataset.cloth;
93
+ };
94
+ wear(current.cloth, current.theme ?? {});
85
95
  const style = node("style");
86
96
  style.textContent = KUMIMOJI_STYLE;
87
97
  const status = node("p", "km-status");
@@ -311,9 +321,8 @@ export function mountKumimoji(target, options = {}) {
311
321
  for (const name of Object.keys(current.theme ?? {}))
312
322
  if (changes.theme !== undefined && !(name in changes.theme))
313
323
  root.style.removeProperty(name);
314
- if (changes.theme !== undefined)
315
- for (const [name, value] of Object.entries(changes.theme))
316
- root.style.setProperty(name, value);
324
+ if (changes.theme !== undefined || "cloth" in changes)
325
+ wear("cloth" in changes ? changes.cloth : current.cloth, changes.theme ?? current.theme ?? {});
317
326
  current = { ...current, ...changes };
318
327
  if (changes.locale !== undefined)
319
328
  session.locale = changes.locale;
@@ -9,4 +9,4 @@
9
9
  * console.log(KUMIMOJI_STYLE.includes(".km-root")); // true
10
10
  * ```
11
11
  */
12
- export declare const KUMIMOJI_STYLE = "\n.km-root {\n --km-square: 44px; --km-felt: #2f5d4a; --km-grid-line: rgba(255,255,255,.12); --km-tile: #f3e6c8; --km-tile-ink: #2a2118;\n --km-wild: #f6d27a; --km-wrong: #d9534f; --km-held: #ffcf3f; --km-ink: #1f2320; --km-panel: #f7f3ea;\n --km-line: rgba(20,20,20,.35); --km-accent: #2f5d4a; --km-accent-ink: #fff;\n --km-radius: 12px; --km-font: system-ui, -apple-system, \"Segoe UI\", sans-serif;\n display: grid; gap: 10px; min-width: 0; color: var(--km-ink); font-family: var(--km-font); font-size: 15px;\n}\n@media (prefers-color-scheme: dark) {\n .km-root { --km-felt: #1e3a2f; --km-ink: #ece8dc; --km-panel: #1d201e; --km-line: rgba(255,255,255,.25); --km-accent: #6fb08f; --km-accent-ink: #10150f; }\n}\n.km-root > * { min-width: 0; }\n.km-status { margin: 0; min-height: 1.4em; font-variant-numeric: tabular-nums; }\n.km-board { background: var(--km-felt); border-radius: var(--km-radius); padding: 10px; overflow: auto; max-height: 70vh; }\n.km-grid { display: grid; gap: 2px; width: max-content; margin: 0 auto; }\n.km-square { width: var(--km-square); height: var(--km-square); border: 0; border-radius: 5px; background: var(--km-grid-line); padding: 0; cursor: pointer; touch-action: manipulation; }\n.km-tile { display: grid; place-items: center; width: var(--km-square); height: var(--km-square); border-radius: 6px; border: 0; padding: 0;\n background: var(--km-tile); color: var(--km-tile-ink); font: 700 calc(var(--km-square) * .55)/1 var(--km-font); text-transform: uppercase;\n box-shadow: 0 2px 0 rgba(0,0,0,.35); cursor: pointer; touch-action: manipulation; }\n.km-japanese .km-tile { font-size: calc(var(--km-square) * .52); text-transform: none; }\n.km-wild { background: var(--km-wild); }\n.km-misspelt { box-shadow: 0 0 0 3px var(--km-wrong) inset, 0 2px 0 rgba(0,0,0,.35); }\n.km-apart { opacity: .6; }\n.km-held { outline: 3px solid var(--km-held); outline-offset: 1px; }\n.km-square:focus-visible, .km-in-hand:focus-visible, .km-button:focus-visible, .km-keep-title:focus-visible { outline: 3px solid var(--km-held); outline-offset: 2px; }\n.km-notes { margin: 0; min-height: 1.4em; font-size: 14px; opacity: .8; overflow-wrap: anywhere; }\n.km-hand { display: flex; flex-wrap: wrap; gap: 6px; min-height: calc(var(--km-square) + 16px); align-items: center; padding: 8px; border-radius: 10px; background: var(--km-panel); }\n.km-empty { opacity: .6; font-size: 14px; }\n.km-picker { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }\n.km-picker:empty { display: none; }\n.km-picker-title { width: 100%; font-size: 14px; }\n.km-picker .km-button { padding: 0 8px; text-transform: uppercase; }\n.km-japanese .km-picker .km-button { text-transform: none; }\n.km-controls[hidden] { display: none; }\n.km-controls { display: flex; flex-wrap: wrap; gap: 8px; }\n.km-button { font: inherit; min-height: 44px; min-width: 44px; padding: 0 14px; border-radius: 8px; border: 1px solid var(--km-line); background: var(--km-panel); color: var(--km-ink); cursor: pointer; touch-action: manipulation; }\n.km-button:disabled { opacity: .45; cursor: default; }\n.km-primary { background: var(--km-accent); color: var(--km-accent-ink); border-color: transparent; }\n.km-keep { border-radius: 10px; background: var(--km-panel); padding: 0 10px; }\n.km-keep[open] { padding-bottom: 10px; }\n.km-keep-title { min-height: 44px; display: flex; align-items: center; font-weight: 600; cursor: pointer; }\n.km-note { margin: 8px 0 0; min-height: 1.4em; font-size: 13px; }\n";
12
+ export declare const KUMIMOJI_STYLE = "\n.km-root {\n --km-square: 44px; --km-felt: #2f5d4a; --km-grid-line: rgba(255,255,255,.12); --km-tile: #f3e6c8; --km-tile-ink: #2a2118;\n --km-wild: #f6d27a; --km-wrong: #d9534f; --km-held: #ffcf3f; --km-ink: #1f2320; --km-panel: #f7f3ea;\n --km-line: rgba(20,20,20,.35); --km-accent: #2f5d4a; --km-accent-ink: #fff;\n --km-radius: 12px; --km-font: system-ui, -apple-system, \"Segoe UI\", sans-serif;\n display: grid; gap: 10px; min-width: 0; color: var(--km-ink); font-family: var(--km-font); font-size: 15px;\n}\n@media (prefers-color-scheme: dark) {\n .km-root { --km-felt: #1e3a2f; --km-ink: #ece8dc; --km-panel: #1d201e; --km-line: rgba(255,255,255,.25); --km-accent: #6fb08f; --km-accent-ink: #10150f; }\n}\n.km-root > * { min-width: 0; }\n.km-status { margin: 0; min-height: 1.4em; font-variant-numeric: tabular-nums; }\n.km-root, .km-root * { line-height: 1.35; }\n.km-board { background: var(--km-felt); border-radius: var(--km-radius); padding: 10px; overflow: auto; max-height: 70vh; }\n.km-grid { display: grid; gap: 2px; width: max-content; margin: 0 auto; }\n.km-square { width: var(--km-square); height: var(--km-square); border: 0; border-radius: 5px; background: var(--km-grid-line); padding: 0; cursor: pointer; touch-action: manipulation; }\n.km-tile { display: grid; place-items: center; width: var(--km-square); height: var(--km-square); border-radius: 6px; border: 0; padding: 0;\n background: var(--km-tile); color: var(--km-tile-ink); font: 700 calc(var(--km-square) * .55)/1 var(--km-font); text-transform: uppercase;\n box-shadow: 0 2px 0 rgba(0,0,0,.35); cursor: pointer; touch-action: manipulation; }\n.km-japanese .km-tile { font-size: calc(var(--km-square) * .52); text-transform: none; }\n.km-wild { background: var(--km-wild); }\n.km-misspelt { box-shadow: 0 0 0 3px var(--km-wrong) inset, 0 2px 0 rgba(0,0,0,.35); }\n.km-apart { opacity: .6; }\n.km-held { outline: 3px solid var(--km-held); outline-offset: 1px; }\n.km-square:focus-visible, .km-in-hand:focus-visible, .km-button:focus-visible, .km-keep-title:focus-visible { outline: 3px solid var(--km-held); outline-offset: 2px; }\n.km-notes { margin: 0; min-height: 1.4em; font-size: 14px; opacity: .8; overflow-wrap: anywhere; }\n.km-hand { display: flex; flex-wrap: wrap; gap: 6px; min-height: calc(var(--km-square) + 16px); align-items: center; padding: 8px; border-radius: 10px; background: var(--km-panel); }\n.km-empty { opacity: .6; font-size: 14px; }\n.km-picker { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }\n.km-picker:empty { display: none; }\n.km-picker-title { width: 100%; font-size: 14px; }\n.km-picker .km-button { padding: 0 8px; text-transform: uppercase; }\n.km-japanese .km-picker .km-button { text-transform: none; }\n.km-controls[hidden] { display: none; }\n.km-controls { display: flex; flex-wrap: wrap; gap: 8px; }\n.km-button { font: inherit; min-height: 44px; min-width: 44px; padding: 0 14px; border-radius: 8px; border: 1px solid var(--km-line); background: var(--km-panel); color: var(--km-ink); cursor: pointer; touch-action: manipulation; }\n.km-button:disabled { opacity: .45; cursor: default; }\n.km-primary { background: var(--km-accent); color: var(--km-accent-ink); border-color: transparent; }\n.km-keep { border-radius: 10px; background: var(--km-panel); padding: 0 10px; }\n.km-keep[open] { padding-bottom: 10px; }\n.km-keep-title { min-height: 44px; display: flex; align-items: center; font-weight: 600; cursor: pointer; }\n.km-note { margin: 8px 0 0; min-height: 1.4em; font-size: 13px; }\n";
package/dist/ui/style.js CHANGED
@@ -22,6 +22,7 @@ export const KUMIMOJI_STYLE = `
22
22
  }
23
23
  .km-root > * { min-width: 0; }
24
24
  .km-status { margin: 0; min-height: 1.4em; font-variant-numeric: tabular-nums; }
25
+ .km-root, .km-root * { line-height: 1.35; }
25
26
  .km-board { background: var(--km-felt); border-radius: var(--km-radius); padding: 10px; overflow: auto; max-height: 70vh; }
26
27
  .km-grid { display: grid; gap: 2px; width: max-content; margin: 0 auto; }
27
28
  .km-square { width: var(--km-square); height: var(--km-square); border: 0; border-radius: 5px; background: var(--km-grid-line); padding: 0; cursor: pointer; touch-action: manipulation; }
package/dist/ui.d.ts CHANGED
@@ -5,5 +5,6 @@
5
5
  */
6
6
  export { KUMIMOJI_STRINGS, type KumimojiLocale, type KumimojiStrings, kumimojiSay, kumimojiStrings, } from "./strings.ts";
7
7
  export { BOARD_LEAST, BOARD_MARGIN, type BoardModel, type BoardSquare, boardModel, type TileMark } from "./ui/board.ts";
8
+ export { type Cloth, clothVars, isCloth, KUMIMOJI_CLOTHS } from "./ui/cloth.ts";
8
9
  export { type KumimojiTableHandle, type KumimojiTableOptions, mountKumimoji } from "./ui/mount.ts";
9
10
  export { KUMIMOJI_STYLE } from "./ui/style.ts";
package/dist/ui.js CHANGED
@@ -5,5 +5,6 @@
5
5
  */
6
6
  export { KUMIMOJI_STRINGS, kumimojiSay, kumimojiStrings, } from "./strings.js";
7
7
  export { BOARD_LEAST, BOARD_MARGIN, boardModel } from "./ui/board.js";
8
+ export { clothVars, isCloth, KUMIMOJI_CLOTHS } from "./ui/cloth.js";
8
9
  export { mountKumimoji } from "./ui/mount.js";
9
10
  export { KUMIMOJI_STYLE } from "./ui/style.js";
package/dist/version.d.ts CHANGED
@@ -6,4 +6,4 @@
6
6
  * console.log(KUMIMOJI_VERSION);
7
7
  * ```
8
8
  */
9
- export declare const KUMIMOJI_VERSION = "1.3.1";
9
+ export declare const KUMIMOJI_VERSION = "1.5.0";
package/dist/version.js CHANGED
@@ -6,4 +6,4 @@
6
6
  * console.log(KUMIMOJI_VERSION);
7
7
  * ```
8
8
  */
9
- export const KUMIMOJI_VERSION = "1.3.1";
9
+ export const KUMIMOJI_VERSION = "1.5.0";
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * JAPANESE KUMIMOJI WORDS AND TILE MIX, written by scripts/word-lists-ja.mjs.
3
- * Derived from JMdict release 2026-09-28, and under CC BY-SA 4.0 with EDRDG's
3
+ * Derived from JMdict release 2026-11-04, and under CC BY-SA 4.0 with EDRDG's
4
4
  * conditions: https://www.edrdg.org/edrdg/licence.html. Never edited by hand.
5
- * 163461 hiragana readings of 2–15 kana, each spelt in the 45
5
+ * Less the readings no ordinary meaning is left for (kotoba's scripts/children-bar.mjs, the bar of its kana lists).
6
+ * 162243 hiragana readings of 2–15 kana, each spelt in the 45
6
7
  * tiles (が as か, ゃ as や, を as お), one character a kana through `codes`,
7
8
  * front-coded by length. The tiles are `JAPANESE_TILE_MIX` (tiles.constants.ts).
8
9
  */