@johnmorrisdotca/kumimoji 1.2.2 → 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 (76) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/README.md +80 -39
  3. package/dist/check.d.ts +26 -1
  4. package/dist/check.js +26 -1
  5. package/dist/computer.types.d.ts +24 -2
  6. package/dist/computerPlay.d.ts +45 -2
  7. package/dist/computerPlay.js +45 -2
  8. package/dist/computerTurn.d.ts +42 -3
  9. package/dist/computerTurn.js +42 -3
  10. package/dist/daily.d.ts +22 -2
  11. package/dist/daily.js +22 -2
  12. package/dist/element-define.d.ts +1 -1
  13. package/dist/element-define.js +3 -13
  14. package/dist/element.d.ts +85 -4
  15. package/dist/element.js +176 -45
  16. package/dist/embed-day.d.ts +4 -0
  17. package/dist/embed-day.js +23 -0
  18. package/dist/export.d.ts +123 -6
  19. package/dist/export.js +91 -4
  20. package/dist/generate.d.ts +14 -0
  21. package/dist/generate.js +14 -0
  22. package/dist/grid.d.ts +181 -10
  23. package/dist/grid.js +122 -7
  24. package/dist/help.d.ts +36 -3
  25. package/dist/help.js +36 -3
  26. package/dist/judge.d.ts +14 -0
  27. package/dist/judge.js +14 -0
  28. package/dist/kana.d.ts +23 -1
  29. package/dist/kana.js +23 -1
  30. package/dist/kumimoji.types.d.ts +70 -5
  31. package/dist/party.d.ts +268 -14
  32. package/dist/party.js +268 -14
  33. package/dist/party.types.d.ts +50 -2
  34. package/dist/partyKept.d.ts +74 -4
  35. package/dist/partyKept.js +74 -4
  36. package/dist/partySeats.d.ts +92 -6
  37. package/dist/partySeats.js +72 -4
  38. package/dist/partyTurns.d.ts +171 -5
  39. package/dist/partyTurns.js +171 -5
  40. package/dist/placement.d.ts +50 -2
  41. package/dist/placement.js +30 -0
  42. package/dist/play.d.ts +247 -15
  43. package/dist/play.js +237 -15
  44. package/dist/random.d.ts +34 -3
  45. package/dist/random.js +23 -2
  46. package/dist/react.d.ts +42 -4
  47. package/dist/react.js +22 -5
  48. package/dist/showcase.d.ts +83 -7
  49. package/dist/showcase.js +49 -4
  50. package/dist/strings.d.ts +54 -5
  51. package/dist/strings.js +34 -3
  52. package/dist/tableView.d.ts +151 -8
  53. package/dist/tableView.js +129 -6
  54. package/dist/tileFace.d.ts +59 -5
  55. package/dist/tileFace.js +48 -4
  56. package/dist/tileFamily.d.ts +9 -0
  57. package/dist/tileFamily.js +9 -0
  58. package/dist/tileWords.d.ts +93 -6
  59. package/dist/tileWords.js +83 -6
  60. package/dist/tileWordsModule.d.ts +11 -1
  61. package/dist/tileWordsModule.js +11 -1
  62. package/dist/tiles.constants.d.ts +99 -5
  63. package/dist/tiles.constants.js +99 -5
  64. package/dist/turn.d.ts +82 -4
  65. package/dist/turn.js +82 -4
  66. package/dist/ui/board.d.ts +60 -5
  67. package/dist/ui/board.js +28 -2
  68. package/dist/ui/mount.d.ts +41 -2
  69. package/dist/ui/mount.js +38 -1
  70. package/dist/ui/style.d.ts +6 -1
  71. package/dist/ui/style.js +6 -0
  72. package/dist/version.d.ts +9 -2
  73. package/dist/version.js +9 -2
  74. package/dist/wilds.d.ts +22 -1
  75. package/dist/wilds.js +22 -1
  76. package/package.json +64 -56
package/dist/turn.d.ts CHANGED
@@ -17,24 +17,69 @@ import type { Area, View } from "./tableView.ts";
17
17
  * Pure, like the rest of the view (`tableView.ts`), which works on the turned
18
18
  * squares unchanged: fitting, zooming and panning are all in screen terms.
19
19
  */
20
- /** The next turn a press gives: a quarter clockwise, and four presses back to the start. */
20
+ /** The next turn a press gives: a quarter clockwise, and four presses back to the start.
21
+ *
22
+ * @param turn - The number of quarter turns of the view.
23
+ * @returns The next turn a press gives: a quarter clockwise, and four presses back to the start.
24
+ * @example
25
+ * ```ts
26
+ * import { nextTurn } from "@johnmorrisdotca/kumimoji";
27
+ *
28
+ * console.log(nextTurn(3));
29
+ * ```
30
+ */
21
31
  export declare function nextTurn(turn: Turn): Turn;
22
32
  /**
23
33
  * Where square (row, col) of the grid is drawn with the table turned: a
24
34
  * quarter clockwise sends a word running right to one running down, and one
25
35
  * running down to one running left. It is linear, so it turns a step (a
26
36
  * direction) as well as a square.
37
+ *
38
+ * @param row - The integer row coordinate, which may be negative.
39
+ * @param col - The integer column coordinate, which may be negative.
40
+ * @param turn - The number of quarter turns of the view.
41
+ * @returns Where square (row, col) of the grid is drawn with the table turned: a quarter clockwise sends a word running right to one running down, and one running down to one running left. It is linear, so it turns a step (a direction) as well as a square.
42
+ * @example
43
+ * ```ts
44
+ * import { turnPlace } from "@johnmorrisdotca/kumimoji";
45
+ *
46
+ * console.log(turnPlace(1,2,1));
47
+ * ```
27
48
  */
28
49
  export declare function turnPlace(row: number, col: number, turn: Turn): {
29
50
  row: number;
30
51
  col: number;
31
52
  };
32
- /** The grid's square drawn at (row, col) of the turned table: `turnPlace` undone. */
53
+ /** The grid's square drawn at (row, col) of the turned table: `turnPlace` undone.
54
+ *
55
+ * @param row - The integer row coordinate, which may be negative.
56
+ * @param col - The integer column coordinate, which may be negative.
57
+ * @param turn - The number of quarter turns of the view.
58
+ * @returns The grid's square drawn at (row, col) of the turned table: `turnPlace` undone.
59
+ * @example
60
+ * ```ts
61
+ * import { unturnPlace } from "@johnmorrisdotca/kumimoji";
62
+ *
63
+ * console.log(unturnPlace(1,2,1));
64
+ * ```
65
+ */
33
66
  export declare function unturnPlace(row: number, col: number, turn: Turn): {
34
67
  row: number;
35
68
  col: number;
36
69
  };
37
- /** The squares a turned table shows: the grid's area, turned. */
70
+ /** The squares a turned table shows: the grid's area, turned.
71
+ *
72
+ * @param area - The occupied table bounds.
73
+ * @param turn - The number of quarter turns of the view.
74
+ * @returns The squares a turned table shows: the grid's area, turned.
75
+ * @example
76
+ * ```ts
77
+ * import { turnArea } from "@johnmorrisdotca/kumimoji";
78
+ *
79
+ * const area = {top:0,left:0,rows:1,cols:3};
80
+ * console.log(turnArea(area,1));
81
+ * ```
82
+ */
38
83
  export declare function turnArea(area: Area, turn: Turn): Area;
39
84
  /**
40
85
  * A view the player zoomed or panned, turned a quarter clockwise with the
@@ -44,18 +89,51 @@ export declare function turnArea(area: Area, turn: Turn): Area;
44
89
  * On the screen a square's corner is `x + col * tile`; the turn takes a point
45
90
  * (row v, col u) of the table to (row u, col 1 - v), which is a quarter turn
46
91
  * about the middle of square (0, 0) — the same turn as `turnPlace`.
92
+ *
93
+ * @param view - The current tile size and pixel translation.
94
+ * @param width - The available viewport width in pixels.
95
+ * @param height - The available viewport height in pixels.
96
+ * @returns A view the player zoomed or panned, turned a quarter clockwise with the table about the middle of its box: the same zoom, and the spot of the table that was in the middle stays there. (A fitted view simply fits again.)
97
+ * @example
98
+ * ```ts
99
+ * import { turnView } from "@johnmorrisdotca/kumimoji";
100
+ *
101
+ * const view = {tile:40,x:100,y:100};
102
+ * console.log(turnView(view,640,480));
103
+ * ```
47
104
  */
48
105
  export declare function turnView(view: View, width: number, height: number): View;
49
106
  /**
50
107
  * Which way typed letters run ON THE SCREEN. Typing lays a word across or
51
108
  * down the grid, so that it reads as a word; with the table turned, that is
52
109
  * another way on the screen, and the cursor's arrow points it.
110
+ *
111
+ * @param across - Whether to lay or type horizontally rather than vertically.
112
+ * @param turn - The number of quarter turns of the view.
113
+ * @returns Which way typed letters run ON THE SCREEN. Typing lays a word across or down the grid, so that it reads as a word; with the table turned, that is another way on the screen, and the cursor's arrow points it.
114
+ * @example
115
+ * ```ts
116
+ * import { typingWay } from "@johnmorrisdotca/kumimoji";
117
+ *
118
+ * console.log(typingWay(true,1));
119
+ * ```
53
120
  */
54
121
  export declare function typingWay(across: boolean, turn: Turn): {
55
122
  arrow: string;
56
123
  name: string;
57
124
  };
58
- /** The step in the grid an arrow key means: the square that way on the screen, with the table turned. Null for any other key. */
125
+ /** The step in the grid an arrow key means: the square that way on the screen, with the table turned. Null for any other key.
126
+ *
127
+ * @param key - The keyboard key identifier.
128
+ * @param turn - The number of quarter turns of the view.
129
+ * @returns The step in the grid an arrow key means: the square that way on the screen, with the table turned. null for any other key.
130
+ * @example
131
+ * ```ts
132
+ * import { arrowStep } from "@johnmorrisdotca/kumimoji";
133
+ *
134
+ * console.log(arrowStep("ArrowRight",1));
135
+ * ```
136
+ */
59
137
  export declare function arrowStep(key: string, turn: Turn): {
60
138
  row: number;
61
139
  col: number;
package/dist/turn.js CHANGED
@@ -15,7 +15,17 @@
15
15
  * Pure, like the rest of the view (`tableView.ts`), which works on the turned
16
16
  * squares unchanged: fitting, zooming and panning are all in screen terms.
17
17
  */
18
- /** The next turn a press gives: a quarter clockwise, and four presses back to the start. */
18
+ /** The next turn a press gives: a quarter clockwise, and four presses back to the start.
19
+ *
20
+ * @param turn - The number of quarter turns of the view.
21
+ * @returns The next turn a press gives: a quarter clockwise, and four presses back to the start.
22
+ * @example
23
+ * ```ts
24
+ * import { nextTurn } from "@johnmorrisdotca/kumimoji";
25
+ *
26
+ * console.log(nextTurn(3));
27
+ * ```
28
+ */
19
29
  export function nextTurn(turn) {
20
30
  return ((turn + 1) % 4);
21
31
  }
@@ -26,6 +36,17 @@ const negate = (n) => 0 - n;
26
36
  * quarter clockwise sends a word running right to one running down, and one
27
37
  * running down to one running left. It is linear, so it turns a step (a
28
38
  * direction) as well as a square.
39
+ *
40
+ * @param row - The integer row coordinate, which may be negative.
41
+ * @param col - The integer column coordinate, which may be negative.
42
+ * @param turn - The number of quarter turns of the view.
43
+ * @returns Where square (row, col) of the grid is drawn with the table turned: a quarter clockwise sends a word running right to one running down, and one running down to one running left. It is linear, so it turns a step (a direction) as well as a square.
44
+ * @example
45
+ * ```ts
46
+ * import { turnPlace } from "@johnmorrisdotca/kumimoji";
47
+ *
48
+ * console.log(turnPlace(1,2,1));
49
+ * ```
29
50
  */
30
51
  export function turnPlace(row, col, turn) {
31
52
  switch (turn) {
@@ -39,11 +60,35 @@ export function turnPlace(row, col, turn) {
39
60
  return { row: negate(col), col: row };
40
61
  }
41
62
  }
42
- /** The grid's square drawn at (row, col) of the turned table: `turnPlace` undone. */
63
+ /** The grid's square drawn at (row, col) of the turned table: `turnPlace` undone.
64
+ *
65
+ * @param row - The integer row coordinate, which may be negative.
66
+ * @param col - The integer column coordinate, which may be negative.
67
+ * @param turn - The number of quarter turns of the view.
68
+ * @returns The grid's square drawn at (row, col) of the turned table: `turnPlace` undone.
69
+ * @example
70
+ * ```ts
71
+ * import { unturnPlace } from "@johnmorrisdotca/kumimoji";
72
+ *
73
+ * console.log(unturnPlace(1,2,1));
74
+ * ```
75
+ */
43
76
  export function unturnPlace(row, col, turn) {
44
77
  return turnPlace(row, col, ((4 - turn) % 4));
45
78
  }
46
- /** The squares a turned table shows: the grid's area, turned. */
79
+ /** The squares a turned table shows: the grid's area, turned.
80
+ *
81
+ * @param area - The occupied table bounds.
82
+ * @param turn - The number of quarter turns of the view.
83
+ * @returns The squares a turned table shows: the grid's area, turned.
84
+ * @example
85
+ * ```ts
86
+ * import { turnArea } from "@johnmorrisdotca/kumimoji";
87
+ *
88
+ * const area = {top:0,left:0,rows:1,cols:3};
89
+ * console.log(turnArea(area,1));
90
+ * ```
91
+ */
47
92
  export function turnArea(area, turn) {
48
93
  const a = turnPlace(area.top, area.left, turn);
49
94
  const b = turnPlace(area.top + area.rows - 1, area.left + area.cols - 1, turn);
@@ -57,6 +102,18 @@ export function turnArea(area, turn) {
57
102
  * On the screen a square's corner is `x + col * tile`; the turn takes a point
58
103
  * (row v, col u) of the table to (row u, col 1 - v), which is a quarter turn
59
104
  * about the middle of square (0, 0) — the same turn as `turnPlace`.
105
+ *
106
+ * @param view - The current tile size and pixel translation.
107
+ * @param width - The available viewport width in pixels.
108
+ * @param height - The available viewport height in pixels.
109
+ * @returns A view the player zoomed or panned, turned a quarter clockwise with the table about the middle of its box: the same zoom, and the spot of the table that was in the middle stays there. (A fitted view simply fits again.)
110
+ * @example
111
+ * ```ts
112
+ * import { turnView } from "@johnmorrisdotca/kumimoji";
113
+ *
114
+ * const view = {tile:40,x:100,y:100};
115
+ * console.log(turnView(view,640,480));
116
+ * ```
60
117
  */
61
118
  export function turnView(view, width, height) {
62
119
  const px = width / 2;
@@ -76,6 +133,16 @@ const WAYS = [
76
133
  * Which way typed letters run ON THE SCREEN. Typing lays a word across or
77
134
  * down the grid, so that it reads as a word; with the table turned, that is
78
135
  * another way on the screen, and the cursor's arrow points it.
136
+ *
137
+ * @param across - Whether to lay or type horizontally rather than vertically.
138
+ * @param turn - The number of quarter turns of the view.
139
+ * @returns Which way typed letters run ON THE SCREEN. Typing lays a word across or down the grid, so that it reads as a word; with the table turned, that is another way on the screen, and the cursor's arrow points it.
140
+ * @example
141
+ * ```ts
142
+ * import { typingWay } from "@johnmorrisdotca/kumimoji";
143
+ *
144
+ * console.log(typingWay(true,1));
145
+ * ```
79
146
  */
80
147
  export function typingWay(across, turn) {
81
148
  const step = turnPlace(across ? 0 : 1, across ? 1 : 0, turn);
@@ -89,7 +156,18 @@ const ARROW_KEYS = {
89
156
  ArrowLeft: { row: 0, col: -1 },
90
157
  ArrowUp: { row: -1, col: 0 },
91
158
  };
92
- /** The step in the grid an arrow key means: the square that way on the screen, with the table turned. Null for any other key. */
159
+ /** The step in the grid an arrow key means: the square that way on the screen, with the table turned. Null for any other key.
160
+ *
161
+ * @param key - The keyboard key identifier.
162
+ * @param turn - The number of quarter turns of the view.
163
+ * @returns The step in the grid an arrow key means: the square that way on the screen, with the table turned. null for any other key.
164
+ * @example
165
+ * ```ts
166
+ * import { arrowStep } from "@johnmorrisdotca/kumimoji";
167
+ *
168
+ * console.log(arrowStep("ArrowRight",1));
169
+ * ```
170
+ */
93
171
  export function arrowStep(key, turn) {
94
172
  const step = ARROW_KEYS[key];
95
173
  return step === undefined ? null : unturnPlace(step.row, step.col, turn);
@@ -1,7 +1,25 @@
1
1
  import { type GridVerdict, type Tiles } from "../grid.ts";
2
- /** How a tile on the table stands: part of a sound crossword, in a run that is not a word, or apart from the rest. */
2
+ /** How a tile on the table stands: part of a sound crossword, in a run that is not a word, or apart from the rest.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import type { TileMark } from "@johnmorrisdotca/kumimoji/ui";
7
+ *
8
+ * const value: TileMark = "sound";
9
+ * console.log(value);
10
+ * ```
11
+ */
3
12
  export type TileMark = "sound" | "misspelt" | "apart";
4
- /** One square of the board drawn. */
13
+ /** One square of the board drawn.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import type { BoardSquare } from "@johnmorrisdotca/kumimoji/ui";
18
+ *
19
+ * const value: BoardSquare = {square:"0,0",row:0,col:0,tile:"c",glyph:"c",wild:false,mark:null};
20
+ * console.log(value);
21
+ * ```
22
+ */
5
23
  export type BoardSquare = {
6
24
  /** Its key, `squareAt(row, col)`. */
7
25
  square: string;
@@ -16,20 +34,57 @@ export type BoardSquare = {
16
34
  /** How the tile stands; null for an empty square, or with no verdict. */
17
35
  mark: TileMark | null;
18
36
  };
19
- /** The part of the table to draw: how many rows and columns, and every square, row by row. */
37
+ /** The part of the table to draw: how many rows and columns, and every square, row by row.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * import type { BoardModel } from "@johnmorrisdotca/kumimoji/ui";
42
+ * import { boardModel } from "@johnmorrisdotca/kumimoji/ui";
43
+ *
44
+ * const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
45
+ * const value: BoardModel = boardModel(tiles,null);
46
+ * console.log(value);
47
+ * ```
48
+ */
20
49
  export type BoardModel = {
21
50
  rows: number;
22
51
  cols: number;
23
52
  squares: BoardSquare[];
24
53
  };
25
- /** How many empty squares are kept round the tiles on every side, so there is always room to build out. */
54
+ /** How many empty squares are kept round the tiles on every side, so there is always room to build out.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * import { BOARD_MARGIN } from "@johnmorrisdotca/kumimoji/ui";
59
+ * console.log(BOARD_MARGIN);
60
+ * ```
61
+ */
26
62
  export declare const BOARD_MARGIN = 2;
27
- /** The fewest squares a side, so an empty or small table is still a table. */
63
+ /** The fewest squares a side, so an empty or small table is still a table.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * import { BOARD_LEAST } from "@johnmorrisdotca/kumimoji/ui";
68
+ * console.log(BOARD_LEAST);
69
+ * ```
70
+ */
28
71
  export declare const BOARD_LEAST = 7;
29
72
  /**
30
73
  * THE PART OF THE TABLE TO DRAW, for a grid and its verdict: the tiles and a
31
74
  * margin round them, at least `BOARD_LEAST` a side, each square with its tile,
32
75
  * what is printed on it, and how it stands. Row by row, left to right. The
33
76
  * grid itself has no edges; this is only the window a board shows it through.
77
+ *
78
+ * @param tiles - The map from row,column square names to internal tile codes.
79
+ * @param verdict - The native word and connectivity verdict for this grid.
80
+ * @param glyphOf - The function that renders an internal tile code as a visible glyph.
81
+ * @returns THE PART OF THE TABLE TO DRAW, for a grid and its verdict: the tiles and a margin round them, at least `BOARD_LEAST` a side, each square with its tile, what is printed on it, and how it stands. Row by row, left to right. The grid itself has no edges; this is only the window a board shows it through.
82
+ * @example
83
+ * ```ts
84
+ * import { boardModel } from "@johnmorrisdotca/kumimoji/ui";
85
+ *
86
+ * const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
87
+ * console.log(boardModel(tiles,null).squares.length);
88
+ * ```
34
89
  */
35
90
  export declare function boardModel(tiles: Tiles, verdict: GridVerdict | null, glyphOf?: (tile: string) => string): BoardModel;
package/dist/ui/board.js CHANGED
@@ -1,14 +1,40 @@
1
1
  import { boundsOf, squareAt } from "../grid.js";
2
2
  import { tileFace } from "../tileFace.js";
3
- /** How many empty squares are kept round the tiles on every side, so there is always room to build out. */
3
+ /** How many empty squares are kept round the tiles on every side, so there is always room to build out.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { BOARD_MARGIN } from "@johnmorrisdotca/kumimoji/ui";
8
+ * console.log(BOARD_MARGIN);
9
+ * ```
10
+ */
4
11
  export const BOARD_MARGIN = 2;
5
- /** The fewest squares a side, so an empty or small table is still a table. */
12
+ /** The fewest squares a side, so an empty or small table is still a table.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { BOARD_LEAST } from "@johnmorrisdotca/kumimoji/ui";
17
+ * console.log(BOARD_LEAST);
18
+ * ```
19
+ */
6
20
  export const BOARD_LEAST = 7;
7
21
  /**
8
22
  * THE PART OF THE TABLE TO DRAW, for a grid and its verdict: the tiles and a
9
23
  * margin round them, at least `BOARD_LEAST` a side, each square with its tile,
10
24
  * what is printed on it, and how it stands. Row by row, left to right. The
11
25
  * grid itself has no edges; this is only the window a board shows it through.
26
+ *
27
+ * @param tiles - The map from row,column square names to internal tile codes.
28
+ * @param verdict - The native word and connectivity verdict for this grid.
29
+ * @param glyphOf - The function that renders an internal tile code as a visible glyph.
30
+ * @returns THE PART OF THE TABLE TO DRAW, for a grid and its verdict: the tiles and a margin round them, at least `BOARD_LEAST` a side, each square with its tile, what is printed on it, and how it stands. Row by row, left to right. The grid itself has no edges; this is only the window a board shows it through.
31
+ * @example
32
+ * ```ts
33
+ * import { boardModel } from "@johnmorrisdotca/kumimoji/ui";
34
+ *
35
+ * const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
36
+ * console.log(boardModel(tiles,null).squares.length);
37
+ * ```
12
38
  */
13
39
  export function boardModel(tiles, verdict, glyphOf = (tile) => tileFace(tile).glyph) {
14
40
  const bounds = boundsOf(tiles) ?? { top: 0, left: 0, bottom: 0, right: 0 };
@@ -2,7 +2,16 @@ 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
- /** What `mountKumimoji` may be given. Everything is optional: with nothing, it is a short English game from a hand of seven, with some wild tiles. */
5
+ /**
6
+ * Optional initial game, presentation, readiness and callback choices.
7
+ * With nothing, it is a short English game from a hand of seven, with some wild tiles.
8
+ * @example
9
+ * ```ts
10
+ * import type { KumimojiTableOptions } from "@johnmorrisdotca/kumimoji/ui";
11
+ * const options: KumimojiTableOptions = { hand: 7, keep: false, controls: true };
12
+ * console.log(options.hand);
13
+ * ```
14
+ */
6
15
  export type KumimojiTableOptions = {
7
16
  /** The language of the tiles: `"english"` or `"japanese"` kana. English by default. */
8
17
  language?: KumimojiLanguage;
@@ -32,11 +41,29 @@ export type KumimojiTableOptions = {
32
41
  theme?: Readonly<Record<string, string>>;
33
42
  /** Whether the table offers saving and loading. True by default. */
34
43
  keep?: boolean;
44
+ /** Show native play-action buttons; a host hiding them supplies its own actions. */
45
+ controls?: boolean;
46
+ /** A validated saved game to restore initially instead of dealing a new one. */
47
+ saved?: KumimojiSaved;
48
+ /** Called after a word-list load or initial restoration fails. */
49
+ onError?: (error: Error) => void;
35
50
  };
36
51
  /** The options that make a game, which a new game may change. */
37
52
  type GameOptions = Pick<KumimojiTableOptions, "language" | "hand" | "level" | "gameLength" | "diagonals" | "seed">;
38
- /** What `mountKumimoji` hands back: the game being played, and the ways to change it from outside. */
53
+ /**
54
+ * Readiness, structured play and presentation/game controls for a mounted table.
55
+ * @example
56
+ * ```ts
57
+ * import type { KumimojiTableHandle } from "@johnmorrisdotca/kumimoji/ui";
58
+ * const setJapanese = (table: KumimojiTableHandle): void => table.set({ locale: "ja" });
59
+ * console.log(typeof setJapanese);
60
+ * ```
61
+ */
39
62
  export type KumimojiTableHandle = {
63
+ /** Settles once the initial deal or restored game is ready. */
64
+ readonly ready: Promise<void>;
65
+ /** Change presentation and controls without redealing. */
66
+ set: (options: Pick<KumimojiTableOptions, "locale" | "strings" | "theme" | "keep" | "controls">) => void;
40
67
  /** The game as it stands, or null while it is being dealt. */
41
68
  play: () => TilePlay | null;
42
69
  /** The game as it would be saved (`kumimojiToJSON`), or null while it is being dealt. */
@@ -61,6 +88,18 @@ export type KumimojiTableHandle = {
61
88
  * game loads back.
62
89
  *
63
90
  * The word list is fetched the first time a game in its language is dealt.
91
+ * @param target - The host element for this table.
92
+ * @param options - Initial deal or saved state, presentation and callbacks.
93
+ * @returns The native handle, including ready and presentation updates without redealing.
94
+ * @example
95
+ * ```ts
96
+ * import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
97
+ * if (typeof document !== "undefined") {
98
+ * const table = mountKumimoji(document.createElement("div"), { keep: false });
99
+ * void table.ready.then(() => table.destroy());
100
+ * }
101
+ * console.log(typeof mountKumimoji);
102
+ * ```
64
103
  */
65
104
  export declare function mountKumimoji(target: HTMLElement, options?: KumimojiTableOptions): KumimojiTableHandle;
66
105
  export {};
package/dist/ui/mount.js CHANGED
@@ -46,6 +46,18 @@ function save(name, type, text) {
46
46
  * game loads back.
47
47
  *
48
48
  * The word list is fetched the first time a game in its language is dealt.
49
+ * @param target - The host element for this table.
50
+ * @param options - Initial deal or saved state, presentation and callbacks.
51
+ * @returns The native handle, including ready and presentation updates without redealing.
52
+ * @example
53
+ * ```ts
54
+ * import { mountKumimoji } from "@johnmorrisdotca/kumimoji/ui";
55
+ * if (typeof document !== "undefined") {
56
+ * const table = mountKumimoji(document.createElement("div"), { keep: false });
57
+ * void table.ready.then(() => table.destroy());
58
+ * }
59
+ * console.log(typeof mountKumimoji);
60
+ * ```
49
61
  */
50
62
  export function mountKumimoji(target, options = {}) {
51
63
  let settings = { language: options.language, hand: options.hand, level: options.level, gameLength: options.gameLength, diagonals: options.diagonals, seed: options.seed };
@@ -87,6 +99,7 @@ export function mountKumimoji(target, options = {}) {
87
99
  const picker = node("div", "km-picker");
88
100
  picker.dataset.testid = "km-picker";
89
101
  const controls = node("div", "km-controls");
102
+ controls.hidden = options.controls === false;
90
103
  controls.dataset.testid = "km-controls";
91
104
  const keep = node("details", "km-keep");
92
105
  keep.dataset.testid = "km-keep";
@@ -422,7 +435,29 @@ export function mountKumimoji(target, options = {}) {
422
435
  renderControls(verdict);
423
436
  keyboardBack(where);
424
437
  }
438
+ let initialReady = Promise.resolve();
425
439
  const handle = {
440
+ get ready() { return initialReady; },
441
+ set: (changes) => {
442
+ for (const name of Object.keys(options.theme ?? {}))
443
+ if (changes.theme !== undefined && !(name in changes.theme))
444
+ root.style.removeProperty(name);
445
+ if (changes.theme !== undefined)
446
+ for (const [name, value] of Object.entries(changes.theme))
447
+ root.style.setProperty(name, value);
448
+ options = { ...options, ...changes };
449
+ if (changes.locale !== undefined)
450
+ locale = changes.locale;
451
+ if (changes.strings !== undefined)
452
+ own = changes.strings;
453
+ say = kumimojiStrings(locale, own);
454
+ if (options.keep === false)
455
+ keep.remove();
456
+ else if (keep.parentNode !== root)
457
+ root.append(keep);
458
+ controls.hidden = options.controls === false;
459
+ render();
460
+ },
426
461
  play: () => play,
427
462
  saved: savedNow,
428
463
  setGame: async (saved) => {
@@ -482,6 +517,8 @@ export function mountKumimoji(target, options = {}) {
482
517
  root.remove();
483
518
  },
484
519
  };
485
- void handle.newGame(settings);
520
+ initialReady = options.saved === undefined ? handle.newGame(settings) : handle.setGame(options.saved);
521
+ void initialReady.catch((error) => { if (destroyed)
522
+ return; const problem = error instanceof Error ? error : new Error(String(error)); status.textContent = say.loadError; options.onError?.(problem); });
486
523
  return handle;
487
524
  }
@@ -3,5 +3,10 @@
3
3
  * variable so a page can wear it in its own colours (set them on the element
4
4
  * or any ancestor, or pass them as `theme`). Light and dark follow the
5
5
  * reader's system. Everything to be tapped is at least 44px.
6
+ * @example
7
+ * ```ts
8
+ * import { KUMIMOJI_STYLE } from "@johnmorrisdotca/kumimoji/ui";
9
+ * console.log(KUMIMOJI_STYLE.includes(".km-root")); // true
10
+ * ```
6
11
  */
7
- 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 { 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-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
@@ -3,6 +3,11 @@
3
3
  * variable so a page can wear it in its own colours (set them on the element
4
4
  * or any ancestor, or pass them as `theme`). Light and dark follow the
5
5
  * reader's system. Everything to be tapped is at least 44px.
6
+ * @example
7
+ * ```ts
8
+ * import { KUMIMOJI_STYLE } from "@johnmorrisdotca/kumimoji/ui";
9
+ * console.log(KUMIMOJI_STYLE.includes(".km-root")); // true
10
+ * ```
6
11
  */
7
12
  export const KUMIMOJI_STYLE = `
8
13
  .km-root {
@@ -37,6 +42,7 @@ export const KUMIMOJI_STYLE = `
37
42
  .km-picker-title { width: 100%; font-size: 14px; }
38
43
  .km-picker .km-button { padding: 0 8px; text-transform: uppercase; }
39
44
  .km-japanese .km-picker .km-button { text-transform: none; }
45
+ .km-controls[hidden] { display: none; }
40
46
  .km-controls { display: flex; flex-wrap: wrap; gap: 8px; }
41
47
  .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; }
42
48
  .km-button:disabled { opacity: .45; cursor: default; }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,9 @@
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.2";
1
+ /** The version of this package, as `package.json` has it. A test holds the two together.
2
+ *
3
+ * @example
4
+ * ```ts
5
+ * import { KUMIMOJI_VERSION } from "@johnmorrisdotca/kumimoji";
6
+ * console.log(KUMIMOJI_VERSION);
7
+ * ```
8
+ */
9
+ export declare const KUMIMOJI_VERSION = "1.3.0";
package/dist/version.js CHANGED
@@ -1,2 +1,9 @@
1
- /** The version of this package, as `package.json` has it. A test holds the two together. */
2
- export const KUMIMOJI_VERSION = "1.2.2";
1
+ /** The version of this package, as `package.json` has it. A test holds the two together.
2
+ *
3
+ * @example
4
+ * ```ts
5
+ * import { KUMIMOJI_VERSION } from "@johnmorrisdotca/kumimoji";
6
+ * console.log(KUMIMOJI_VERSION);
7
+ * ```
8
+ */
9
+ export const KUMIMOJI_VERSION = "1.3.0";
package/dist/wilds.d.ts CHANGED
@@ -21,7 +21,28 @@ import type { TileWords } from "./tileWords.ts";
21
21
  * table names the run as the player spelled it. It gives up, answering null,
22
22
  * past `WILD_SEARCH_MOST` steps: a grid it could not settle is not called
23
23
  * sound.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { WILD_SEARCH_MOST } from "@johnmorrisdotca/kumimoji";
28
+ * console.log(WILD_SEARCH_MOST);
29
+ * ```
24
30
  */
25
31
  export declare const WILD_SEARCH_MOST = 20000;
26
- /** The grid with every blank wild read as a letter that makes all its runs words, or null for none; the grid itself when it holds no blank. */
32
+ /** The grid with every blank wild read as a letter that makes all its runs words, or null for none; the grid itself when it holds no blank.
33
+ *
34
+ * @param tiles - The map from row,column square names to internal tile codes.
35
+ * @param words - The loaded native language word list and tile mappings.
36
+ * @param rules - Whether diagonal runs are also judged.
37
+ * @returns The grid with every blank wild read as a letter that makes all its runs words, or null for none; the grid itself when it holds no blank.
38
+ * @example
39
+ * ```ts
40
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
41
+ * import { readWilds } from "@johnmorrisdotca/kumimoji";
42
+ *
43
+ * const words = await loadTileWordsFromModule("english");
44
+ * const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
45
+ * console.log(readWilds(tiles,words));
46
+ * ```
47
+ */
27
48
  export declare function readWilds(tiles: Tiles, words: TileWords, rules?: GridRules): Tiles | null;
package/dist/wilds.js CHANGED
@@ -20,9 +20,30 @@ import { runsOf } from "./grid.js";
20
20
  * table names the run as the player spelled it. It gives up, answering null,
21
21
  * past `WILD_SEARCH_MOST` steps: a grid it could not settle is not called
22
22
  * sound.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { WILD_SEARCH_MOST } from "@johnmorrisdotca/kumimoji";
27
+ * console.log(WILD_SEARCH_MOST);
28
+ * ```
23
29
  */
24
30
  export const WILD_SEARCH_MOST = 20000;
25
- /** The grid with every blank wild read as a letter that makes all its runs words, or null for none; the grid itself when it holds no blank. */
31
+ /** The grid with every blank wild read as a letter that makes all its runs words, or null for none; the grid itself when it holds no blank.
32
+ *
33
+ * @param tiles - The map from row,column square names to internal tile codes.
34
+ * @param words - The loaded native language word list and tile mappings.
35
+ * @param rules - Whether diagonal runs are also judged.
36
+ * @returns The grid with every blank wild read as a letter that makes all its runs words, or null for none; the grid itself when it holds no blank.
37
+ * @example
38
+ * ```ts
39
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
40
+ * import { readWilds } from "@johnmorrisdotca/kumimoji";
41
+ *
42
+ * const words = await loadTileWordsFromModule("english");
43
+ * const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
44
+ * console.log(readWilds(tiles,words));
45
+ * ```
46
+ */
26
47
  export function readWilds(tiles, words, rules = {}) {
27
48
  const blanks = [...tiles].filter(([, tile]) => tile === "*").map(([square]) => square);
28
49
  if (blanks.length === 0)