@dreamboard-games/ui-sdk 0.0.42 → 0.0.45

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 (166) hide show
  1. package/dist/components/ActionButton.d.ts.map +1 -1
  2. package/dist/components/ActionButton.js +2 -1
  3. package/dist/components/Card.d.ts +1 -1
  4. package/dist/components/Card.d.ts.map +1 -1
  5. package/dist/components/DiceRoller.d.ts +3 -2
  6. package/dist/components/DiceRoller.d.ts.map +1 -1
  7. package/dist/components/DiceRoller.js +4 -13
  8. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  9. package/dist/components/ErrorBoundary.js +94 -2
  10. package/dist/components/InteractionForm.d.ts +1 -1
  11. package/dist/components/InteractionForm.d.ts.map +1 -1
  12. package/dist/components/InteractionForm.js +29 -15
  13. package/dist/components/PrimaryActionButton.d.ts.map +1 -1
  14. package/dist/components/PrimaryActionButton.js +7 -6
  15. package/dist/components/ResourceCounter.d.ts +59 -25
  16. package/dist/components/ResourceCounter.d.ts.map +1 -1
  17. package/dist/components/ResourceCounter.js +106 -115
  18. package/dist/components/Toast.d.ts +13 -6
  19. package/dist/components/Toast.d.ts.map +1 -1
  20. package/dist/components/Toast.js +10 -5
  21. package/dist/components/board/HexGrid.js +6 -6
  22. package/dist/components/board/target-layer.d.ts +18 -2
  23. package/dist/components/board/target-layer.d.ts.map +1 -1
  24. package/dist/components/board/target-layer.js +20 -3
  25. package/dist/components/index.d.ts +3 -4
  26. package/dist/components/index.d.ts.map +1 -1
  27. package/dist/components/index.js +3 -4
  28. package/dist/components/surfaces/InboxSurface.d.ts.map +1 -1
  29. package/dist/components/surfaces/InboxSurface.js +2 -6
  30. package/dist/components/surfaces/PlayerCardsSurface.js +2 -2
  31. package/dist/components/surfaces/internal/CardZoneRoutedForm.d.ts +7 -0
  32. package/dist/components/surfaces/internal/CardZoneRoutedForm.d.ts.map +1 -0
  33. package/dist/components/surfaces/internal/CardZoneRoutedForm.js +9 -0
  34. package/dist/components/surfaces/internal/DefaultInteractionButton.d.ts.map +1 -1
  35. package/dist/components/surfaces/internal/DefaultInteractionButton.js +5 -8
  36. package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts +2 -2
  37. package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts.map +1 -1
  38. package/dist/components/surfaces/internal/useCardZoneInteractions.js +19 -43
  39. package/dist/context/InteractionDraftContext.d.ts +11 -2
  40. package/dist/context/InteractionDraftContext.d.ts.map +1 -1
  41. package/dist/context/InteractionDraftContext.js +41 -4
  42. package/dist/defaults/components.d.ts +0 -5
  43. package/dist/defaults/components.d.ts.map +1 -1
  44. package/dist/defaults/components.js +7 -11
  45. package/dist/hooks/useBoardInteractions.d.ts +35 -12
  46. package/dist/hooks/useBoardInteractions.d.ts.map +1 -1
  47. package/dist/hooks/useBoardInteractions.js +186 -82
  48. package/dist/hooks/useInteractionHandle.d.ts +1 -1
  49. package/dist/hooks/useInteractionHandle.d.ts.map +1 -1
  50. package/dist/hooks/useInteractionHandle.js +12 -27
  51. package/dist/index.d.ts +11 -17
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +5 -14
  54. package/dist/primitives/board.d.ts +53 -3
  55. package/dist/primitives/board.d.ts.map +1 -1
  56. package/dist/primitives/board.js +65 -41
  57. package/dist/primitives/dialog-lifecycle.d.ts +17 -0
  58. package/dist/primitives/dialog-lifecycle.d.ts.map +1 -0
  59. package/dist/primitives/dialog-lifecycle.js +24 -0
  60. package/dist/primitives/dice.d.ts +31 -0
  61. package/dist/primitives/dice.d.ts.map +1 -0
  62. package/dist/primitives/dice.js +33 -0
  63. package/dist/primitives/game.d.ts +55 -0
  64. package/dist/primitives/game.d.ts.map +1 -0
  65. package/dist/primitives/game.js +101 -0
  66. package/dist/primitives/index.d.ts +7 -4
  67. package/dist/primitives/index.d.ts.map +1 -1
  68. package/dist/primitives/index.js +7 -4
  69. package/dist/primitives/interaction-form-binding.d.ts +12 -0
  70. package/dist/primitives/interaction-form-binding.d.ts.map +1 -0
  71. package/dist/primitives/interaction-form-binding.js +14 -0
  72. package/dist/primitives/interaction-submit.d.ts +23 -0
  73. package/dist/primitives/interaction-submit.d.ts.map +1 -0
  74. package/dist/primitives/interaction-submit.js +41 -0
  75. package/dist/primitives/interaction.d.ts +76 -6
  76. package/dist/primitives/interaction.d.ts.map +1 -1
  77. package/dist/primitives/interaction.js +210 -26
  78. package/dist/primitives/player-roster.d.ts +2 -1
  79. package/dist/primitives/player-roster.d.ts.map +1 -1
  80. package/dist/primitives/prompt.d.ts +36 -11
  81. package/dist/primitives/prompt.d.ts.map +1 -1
  82. package/dist/primitives/prompt.js +29 -17
  83. package/dist/primitives/ui.d.ts +9 -0
  84. package/dist/primitives/ui.d.ts.map +1 -0
  85. package/dist/primitives/ui.js +7 -0
  86. package/dist/primitives/zone.d.ts +111 -5
  87. package/dist/primitives/zone.d.ts.map +1 -1
  88. package/dist/primitives/zone.js +349 -9
  89. package/dist/reducer.d.ts +2 -14
  90. package/dist/reducer.d.ts.map +1 -1
  91. package/dist/reducer.js +1 -14
  92. package/dist/runtime/createPluginRuntimeAPI.js +1 -1
  93. package/dist/types/hex-color.d.ts +7 -0
  94. package/dist/types/hex-color.d.ts.map +1 -0
  95. package/dist/types/hex-color.js +13 -0
  96. package/dist/types/player-state.d.ts +28 -14
  97. package/dist/types/player-state.d.ts.map +1 -1
  98. package/dist/types/plugin-state.d.ts +9 -3
  99. package/dist/types/plugin-state.d.ts.map +1 -1
  100. package/dist/ui-contract.d.ts +119 -14
  101. package/dist/ui-contract.d.ts.map +1 -1
  102. package/dist/ui-contract.js +4 -3
  103. package/dist/ui-sdk.d.ts +1637 -1245
  104. package/dist/utils/interaction-inputs.d.ts +8 -5
  105. package/dist/utils/interaction-inputs.d.ts.map +1 -1
  106. package/dist/utils/interaction-inputs.js +82 -14
  107. package/dist/utils/interaction-router.d.ts +31 -0
  108. package/dist/utils/interaction-router.d.ts.map +1 -0
  109. package/dist/utils/interaction-router.js +114 -0
  110. package/package.json +2 -2
  111. package/src/components/ActionButton.tsx +2 -1
  112. package/src/components/Card.tsx +1 -1
  113. package/src/components/DiceRoller.tsx +13 -22
  114. package/src/components/ErrorBoundary.test.tsx +19 -0
  115. package/src/components/ErrorBoundary.tsx +113 -24
  116. package/src/components/InteractionForm.test.tsx +24 -0
  117. package/src/components/InteractionForm.tsx +48 -23
  118. package/src/components/PrimaryActionButton.tsx +19 -5
  119. package/src/components/ResourceCounter.test.tsx +13 -13
  120. package/src/components/ResourceCounter.tsx +238 -244
  121. package/src/components/Toast.tsx +23 -10
  122. package/src/components/__fixtures__/ResourceCounter.fixture.tsx +70 -169
  123. package/src/components/board/HexGrid.tsx +6 -6
  124. package/src/components/board/target-layer.ts +44 -5
  125. package/src/components/index.ts +17 -10
  126. package/src/components/surfaces/InboxSurface.tsx +7 -5
  127. package/src/components/surfaces/PlayerCardsSurface.tsx +6 -6
  128. package/src/components/surfaces/internal/CardZoneRoutedForm.tsx +35 -0
  129. package/src/components/surfaces/internal/DefaultInteractionButton.tsx +17 -7
  130. package/src/components/surfaces/internal/useCardZoneInteractions.ts +25 -67
  131. package/src/context/InteractionDraftContext.tsx +51 -5
  132. package/src/defaults/components.tsx +12 -50
  133. package/src/defaults/defaults.test.tsx +1 -50
  134. package/src/hooks/useBoardInteractions.test.tsx +240 -17
  135. package/src/hooks/useBoardInteractions.ts +330 -105
  136. package/src/hooks/useInteractionHandle.ts +23 -28
  137. package/src/index.test.ts +60 -40
  138. package/src/index.ts +30 -36
  139. package/src/primitives/board.test.tsx +73 -0
  140. package/src/primitives/board.tsx +191 -40
  141. package/src/primitives/dialog-lifecycle.ts +58 -0
  142. package/src/primitives/dice.test.tsx +47 -0
  143. package/src/primitives/dice.tsx +79 -0
  144. package/src/primitives/game.test.tsx +98 -0
  145. package/src/primitives/game.tsx +213 -0
  146. package/src/primitives/index.ts +84 -0
  147. package/src/primitives/interaction-form-binding.tsx +56 -0
  148. package/src/primitives/interaction-submit.ts +90 -0
  149. package/src/primitives/interaction.test.tsx +396 -0
  150. package/src/primitives/interaction.tsx +451 -31
  151. package/src/primitives/player-roster.tsx +2 -1
  152. package/src/primitives/prompt.test.tsx +94 -3
  153. package/src/primitives/prompt.tsx +87 -48
  154. package/src/primitives/ui.test.tsx +131 -0
  155. package/src/primitives/ui.tsx +13 -0
  156. package/src/primitives/zone.test.tsx +305 -0
  157. package/src/primitives/zone.tsx +660 -12
  158. package/src/reducer.ts +7 -20
  159. package/src/runtime/createPluginRuntimeAPI.ts +1 -1
  160. package/src/types/hex-color.ts +20 -0
  161. package/src/types/player-state.ts +36 -18
  162. package/src/types/plugin-state.ts +10 -3
  163. package/src/ui-contract.ts +253 -21
  164. package/src/utils/interaction-inputs.test.ts +400 -0
  165. package/src/utils/interaction-inputs.ts +113 -11
  166. package/src/utils/interaction-router.ts +200 -0
@@ -17,13 +17,17 @@ import type { InteractionDescriptor } from "../types/plugin-state.js";
17
17
  import {
18
18
  applyInteractionInputDefaults,
19
19
  hasInteractionFieldErrors,
20
- inputByKey,
21
- isInputValueReady,
22
20
  interactionArmScope,
23
21
  interactionInputKeys,
24
22
  mergeInteractionFieldErrors,
25
23
  validateInteractionInputDomains,
26
24
  } from "../utils/interaction-inputs.js";
25
+ import {
26
+ applyInteractionDraftMutation,
27
+ claimInteractionSubmit,
28
+ clearInteractionRoute,
29
+ getInteractionDraftReadiness,
30
+ } from "../utils/interaction-router.js";
27
31
 
28
32
  /**
29
33
  * Anything that can be used as a submit params object. Subset of TS
@@ -115,7 +119,7 @@ export interface InteractionHandle<
115
119
  isReady: boolean;
116
120
  /**
117
121
  * True when this interaction is the currently armed one on its surface.
118
- * Armed interactions are the ones that surfaces like `BoardSurface` use
122
+ * Armed interactions are the ones that board primitives use
119
123
  * to highlight eligible targets and route clicks.
120
124
  */
121
125
  isArmed: boolean;
@@ -184,16 +188,11 @@ export function useInteractionHandle<
184
188
  : "open";
185
189
 
186
190
  const isReady = useMemo(() => {
187
- const required = inputKeys;
188
- if (required.length === 0) return true;
189
- return required.every((key) => {
190
- const input = inputByKey(descriptor, key);
191
- const value = (values as Record<string, unknown>)[key];
192
- return input
193
- ? isInputValueReady(input, value)
194
- : value !== null && value !== undefined;
195
- });
196
- }, [descriptor, values, inputKeys]);
191
+ return getInteractionDraftReadiness(
192
+ descriptor,
193
+ values as Record<string, unknown>,
194
+ ).ready;
195
+ }, [descriptor, values]);
197
196
 
198
197
  const requirePlayer = useCallback(() => {
199
198
  if (!controllingPlayerId) {
@@ -212,8 +211,13 @@ export function useInteractionHandle<
212
211
  : "Interaction submission is already in progress.",
213
212
  );
214
213
  }
214
+ if (!claimInteractionSubmit(store, descriptor)) {
215
+ throw new ValidationError(
216
+ "SUBMITTING",
217
+ "Interaction submission is already in progress.",
218
+ );
219
+ }
215
220
  submittingRef.current = true;
216
- store.setSubmitting(interactionKey, true);
217
221
  const finalParams = applyInteractionInputDefaults<Params>(
218
222
  descriptor,
219
223
  params ?? values,
@@ -224,10 +228,7 @@ export function useInteractionHandle<
224
228
  interactionId,
225
229
  finalParams as Record<string, unknown>,
226
230
  );
227
- store.clearInput(interactionKey);
228
- if (store.getArmed(armScope) === interactionKey) {
229
- store.arm(armScope, null);
230
- }
231
+ clearInteractionRoute(store, descriptor);
231
232
  } catch (error) {
232
233
  throw validationErrorFromUnknown(error);
233
234
  } finally {
@@ -242,7 +243,6 @@ export function useInteractionHandle<
242
243
  requirePlayer,
243
244
  interactionId,
244
245
  interactionKey,
245
- armScope,
246
246
  store,
247
247
  status,
248
248
  ],
@@ -269,13 +269,8 @@ export function useInteractionHandle<
269
269
  const validateDraft = useCallback((): DraftValidation<Params> => {
270
270
  const rawDraft = { ...values } as Record<string, unknown>;
271
271
  const required = inputKeys;
272
- const missing = required.filter((key) => {
273
- const input = inputByKey(descriptor, key);
274
- const value = rawDraft[key];
275
- return input
276
- ? !isInputValueReady(input, value)
277
- : value === null || value === undefined;
278
- }) as Array<keyof Params & string>;
272
+ const missing = getInteractionDraftReadiness(descriptor, rawDraft)
273
+ .missingInputs as Array<keyof Params & string>;
279
274
  const domainFieldErrors = validateInteractionInputDomains(
280
275
  descriptor,
281
276
  rawDraft,
@@ -392,9 +387,9 @@ export function useInteractionHandle<
392
387
 
393
388
  const setInput = useCallback(
394
389
  <K extends keyof Params & string>(key: K, value: Params[K]) => {
395
- store.setInput(interactionKey, key, value as unknown);
390
+ applyInteractionDraftMutation(store, descriptor, [{ key, value }]);
396
391
  },
397
- [store, interactionKey],
392
+ [store, descriptor],
398
393
  );
399
394
 
400
395
  const clearInput = useCallback(
package/src/index.test.ts CHANGED
@@ -1,14 +1,11 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { expect, test } from "bun:test";
4
+ import type { ComponentProps } from "react";
4
5
  import * as components from "./components/index.js";
5
6
  import * as defaults from "./defaults/index.js";
6
7
  import * as sdk from "./index.js";
7
- import type {
8
- CardCollection,
9
- ViewCard,
10
- ViewSlotOccupant,
11
- } from "@dreamboard/sdk-types";
8
+ import type { ViewSlotOccupant } from "@dreamboard/sdk-types";
12
9
  import {
13
10
  BoardEdgeIdOf,
14
11
  BoardSpaceIdOf,
@@ -32,17 +29,6 @@ import {
32
29
  type SquareGridProps,
33
30
  type SquarePieceState,
34
31
  type SquareVertexState,
35
- useBoardTopology,
36
- useCards,
37
- useHandLayout,
38
- useHexBoard,
39
- useInteractionHandle,
40
- useIsMobile,
41
- usePanZoom,
42
- usePluginRuntime,
43
- usePluginState,
44
- useSeatInbox,
45
- useSquareBoard,
46
32
  } from "./index.js";
47
33
 
48
34
  async function walkFiles(rootDir: string): Promise<string[]> {
@@ -72,15 +58,33 @@ async function walkFiles(rootDir: string): Promise<string[]> {
72
58
  test("top-level ui-sdk exports the headless scaffold support surface", () => {
73
59
  expect(RuntimeProvider).toBeDefined();
74
60
  expect(PluginStateProvider).toBeDefined();
75
- expect(usePluginRuntime).toBeDefined();
76
- expect(usePluginState).toBeDefined();
77
- expect(useHandLayout).toBeDefined();
78
- expect(usePanZoom).toBeDefined();
79
- expect(useIsMobile).toBeDefined();
80
61
  expect(calculateViewBox).toBeDefined();
81
62
  expect(toTrackBoardData).toBeDefined();
82
- expect(useSeatInbox).toBeDefined();
83
- expect(useInteractionHandle).toBeDefined();
63
+ for (const exportName of [
64
+ "useActivePlayers",
65
+ "useBoardInteractions",
66
+ "useBoardTopology",
67
+ "useCards",
68
+ "useGameView",
69
+ "useHandLayout",
70
+ "useHexBoard",
71
+ "useInteractionByKey",
72
+ "useInteractionHandle",
73
+ "useIsMobile",
74
+ "usePanZoom",
75
+ "usePlayerInfo",
76
+ "usePlayerTurnOrder",
77
+ "usePluginActions",
78
+ "usePluginRuntime",
79
+ "usePluginSession",
80
+ "usePluginState",
81
+ "useRuntimeContext",
82
+ "useSeatInbox",
83
+ "useSquareBoard",
84
+ "useToast",
85
+ ]) {
86
+ expect(exportName in sdk).toBe(false);
87
+ }
84
88
  });
85
89
 
86
90
  test("top-level ui-sdk is the canonical root for visual components", () => {
@@ -91,7 +95,7 @@ test("top-level ui-sdk is the canonical root for visual components", () => {
91
95
  expect("ActionButton" in sdk).toBe(true);
92
96
  expect("Dialog" in sdk).toBe(true);
93
97
  expect("DialogContent" in sdk).toBe(true);
94
- expect("PromptDialogHost" in sdk).toBe(true);
98
+ expect("PromptDialogHost" in sdk).toBe(false);
95
99
  });
96
100
 
97
101
  test("top-level ui-sdk no longer exports the legacy interaction-button theme primitives", () => {
@@ -108,6 +112,19 @@ test("top-level ui-sdk no longer exports the legacy interaction-button theme pri
108
112
  }
109
113
  });
110
114
 
115
+ test("top-level ui-sdk no longer exports secondary interaction shortcuts", () => {
116
+ for (const exportName of [
117
+ "InteractionButton",
118
+ "InteractionDialogForm",
119
+ "InteractionButtonProps",
120
+ "InteractionDialogFormProps",
121
+ ]) {
122
+ expect(exportName in sdk).toBe(false);
123
+ }
124
+ expect("Button" in sdk.Interaction).toBe(false);
125
+ expect("DialogForm" in sdk.Interaction).toBe(false);
126
+ });
127
+
111
128
  test("top-level ui-sdk publishes the unified Theme contract", () => {
112
129
  expect("ThemeProvider" in sdk).toBe(true);
113
130
  expect("useTheme" in sdk).toBe(true);
@@ -128,10 +145,27 @@ test("top-level ui-sdk publishes the UI contract registration scaffold", () => {
128
145
  });
129
146
 
130
147
  expect(ui.contract.interactions?.["setup.ready"]).toEqual({});
131
- expect(ui.PromptDialogHost).toBeDefined();
148
+ expect(ui.Game).toBeDefined();
149
+ expect("PromptDialogHost" in ui).toBe(false);
150
+ expect("Button" in ui.Interaction).toBe(false);
151
+ expect("DialogForm" in ui.Interaction).toBe(false);
132
152
  expect(ui.PlayerRoster).toBeDefined();
133
153
  });
134
154
 
155
+ test("empty UI contract buckets stay string-compatible", () => {
156
+ const ui = sdk.createDreamboardUI({
157
+ inputs: {},
158
+ cards: {},
159
+ });
160
+ const cardInputProps: ComponentProps<typeof ui.Interaction.CardInput> = {
161
+ input: "cardId",
162
+ card: "tech-card-1",
163
+ };
164
+
165
+ expect(ui.Interaction.CardInput).toBeDefined();
166
+ expect(cardInputProps.input).toBe("cardId");
167
+ });
168
+
135
169
  test("top-level ui-sdk exposes PlayerRoster as the only player roster primitive", () => {
136
170
  expect("PlayerRoster" in sdk).toBe(true);
137
171
  for (const exportName of [
@@ -180,7 +214,7 @@ test("components subpath does not export shell slot internals", () => {
180
214
 
181
215
  test("defaults subpath exposes primitive-composed Radix-style defaults", () => {
182
216
  expect("GameLayout" in defaults).toBe(true);
183
- expect("DefaultPromptInbox" in defaults).toBe(true);
217
+ expect("DefaultPromptInbox" in defaults).toBe(false);
184
218
  expect("DefaultInteractionList" in defaults).toBe(true);
185
219
  expect("DefaultInteractionForm" in defaults).toBe(true);
186
220
  expect("DefaultZone" in defaults).toBe(true);
@@ -385,18 +419,6 @@ test("tiled board hooks and types accept generated board-state records", () => {
385
419
  generatedSquareBoard;
386
420
  const _squareTopologyInput: Parameters<typeof useBoardTopology>[0] =
387
421
  generatedSquareBoard;
388
- const _useCardsArgs: [Parameters<typeof useCards>[0]] = [
389
- {
390
- cardIds: ["card-1"] as const,
391
- cardsById: {
392
- "card-1": {
393
- id: "card-1",
394
- cardType: "goods",
395
- properties: {},
396
- },
397
- },
398
- } satisfies CardCollection<string, ViewCard>,
399
- ];
400
422
  const _readonlySquareEdge = {
401
423
  id: "a1$$a2",
402
424
  spaceIds: ["a1", "a2"] as const,
@@ -454,7 +476,6 @@ test("tiled board hooks and types accept generated board-state records", () => {
454
476
  _hexTopologyInput,
455
477
  _squareHookInput,
456
478
  _squareTopologyInput,
457
- _useCardsArgs,
458
479
  _readonlySquareEdge,
459
480
  _readonlySquareVertex,
460
481
  _squarePiece,
@@ -469,6 +490,5 @@ test("tiled board hooks and types accept generated board-state records", () => {
469
490
  _squareVertexId,
470
491
  ];
471
492
 
472
- expect(_useCardsArgs[0].cardsById["card-1"]?.cardType).toBe("goods");
473
493
  expect(_trackBoardData.pieces[0]?.spaceId).toBe("start");
474
494
  });
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- export * from "./reducer.js";
2
1
  export {
3
2
  createDreamboardUI,
4
3
  type BoardTargetKey,
@@ -12,6 +11,7 @@ export {
12
11
  type PromptKey,
13
12
  type RegisteredUI,
14
13
  type TypedBoard,
14
+ type TypedGame,
15
15
  type TypedInteraction,
16
16
  type TypedPhase,
17
17
  type TypedPlayerRoster,
@@ -23,34 +23,11 @@ export {
23
23
  type ZoneKey,
24
24
  } from "./ui-contract.js";
25
25
  export * from "./primitives/index.js";
26
- export {
27
- BoardInteractionConflictError,
28
- useBoardInteractions,
29
- type BoardInteractionsContext,
30
- type BoardInteractionsOptions,
31
- type BoardTargetLayerFactory,
32
- type BoardTargetLayerOptions,
33
- } from "./hooks/useBoardInteractions.js";
34
- export { useBoardTopology } from "./hooks/useBoardTopology.js";
35
- export { useCards } from "./hooks/useCards.js";
36
- export { useHexBoard } from "./hooks/useHexBoard.js";
37
- export { useSquareBoard } from "./hooks/useSquareBoard.js";
38
- export { useHandLayout } from "./hooks/useHandLayout.js";
39
- export { useInteractionByKey } from "./hooks/useInteractionByKey.js";
40
- export { useIsMobile } from "./hooks/useIsMobile.js";
41
- export { usePanZoom, calculateViewBox } from "./hooks/usePanZoom.js";
42
- export { usePluginRuntime } from "./hooks/usePluginRuntime.js";
26
+ export type { BoardSelectionResult } from "./hooks/useBoardInteractions.js";
27
+ export { calculateViewBox } from "./hooks/usePanZoom.js";
43
28
  export { toTrackBoardData } from "./helpers/track-board.js";
44
- export {
45
- PluginStateProvider,
46
- usePluginActions,
47
- usePluginState,
48
- } from "./context/PluginStateContext.js";
49
- export {
50
- RuntimeContext,
51
- RuntimeProvider,
52
- useRuntimeContext,
53
- } from "./context/RuntimeContext.js";
29
+ export { PluginStateProvider } from "./context/PluginStateContext.js";
30
+ export { RuntimeContext, RuntimeProvider } from "./context/RuntimeContext.js";
54
31
  export {
55
32
  type ClientParamSchema,
56
33
  type ClientParamSchemaMap,
@@ -102,24 +79,41 @@ export {
102
79
  normalizeSquareBoardInput,
103
80
  } from "./types/tiled-board.js";
104
81
  export {
105
- InteractionField,
106
82
  InteractionForm,
107
83
  defaultFormInputs,
108
84
  hasDefaultInteractionFormFields,
109
85
  } from "./components/InteractionForm.js";
110
- export type {
111
- InteractionFieldProps,
112
- InteractionFieldRenderMap,
113
- InteractionFieldRenderProps,
114
- InteractionFormProps,
115
- } from "./components/InteractionForm.js";
86
+ export type { InteractionFormProps } from "./components/InteractionForm.js";
116
87
  export * from "./components/index.js";
117
88
  export type * from "./hooks/useHandLayout.js";
118
89
  export type * from "./hooks/usePanZoom.js";
119
90
  export type * from "./hooks/usePluginRuntime.js";
120
- export type { PluginStateSnapshot } from "./types/plugin-state.js";
91
+ export type {
92
+ InteractionContext,
93
+ InteractionContextOption,
94
+ InteractionDescriptor,
95
+ InputDomainDependencyCase,
96
+ PluginStateSnapshot,
97
+ } from "./types/plugin-state.js";
98
+ export {
99
+ hexColor,
100
+ isHexColor,
101
+ parseHexColor,
102
+ type HexColor,
103
+ } from "./types/hex-color.js";
121
104
  export type * from "./types/player-state.js";
122
105
  export type * from "./types/runtime-api.js";
106
+ export type {
107
+ DraftValidation,
108
+ InteractionHandle,
109
+ InteractionHandleStatus,
110
+ InteractionParamsShape,
111
+ } from "./hooks/useInteractionHandle.js";
112
+ export type {
113
+ CardCollection,
114
+ ViewCard,
115
+ ViewSlotOccupant,
116
+ } from "@dreamboard/sdk-types";
123
117
  export type {
124
118
  AnyHexBoardInput,
125
119
  AnySquareBoardInput,
@@ -100,6 +100,25 @@ test("Board.Target exposes reducer-projected target eligibility", () => {
100
100
  expect(html).not.toContain('data-ambiguous="true"');
101
101
  });
102
102
 
103
+ test("Board target kind aliases use the canonical target router surface", () => {
104
+ const runtime = createRuntime(
105
+ createSnapshot([createBoardDescriptor("claimSpace")]),
106
+ );
107
+
108
+ const html = renderToString(
109
+ <GameUIProvider runtime={runtime}>
110
+ <Board.Root>
111
+ <Board.SpaceTarget value="space-1">Space one</Board.SpaceTarget>
112
+ </Board.Root>
113
+ </GameUIProvider>,
114
+ );
115
+
116
+ expect(html).toContain('data-dreamboard-board-target=""');
117
+ expect(html).toContain('data-target-kind="space"');
118
+ expect(html).toContain('data-target-id="space-1"');
119
+ expect(html).toContain('data-eligible="true"');
120
+ });
121
+
103
122
  test("Board.Target requires explicit routing when equal-priority matches are ambiguous", () => {
104
123
  const runtime = createRuntime(
105
124
  createSnapshot([
@@ -137,3 +156,57 @@ test("Board.Target requires explicit routing when equal-priority matches are amb
137
156
  expect(explicitHtml).toContain('data-input-name="spaceId"');
138
157
  expect(explicitHtml).not.toContain('data-ambiguous="true"');
139
158
  });
159
+
160
+ test("Board.State exposes the generated board interaction context", () => {
161
+ const runtime = createRuntime(
162
+ createSnapshot([createBoardDescriptor("move")]),
163
+ );
164
+ const html = renderToString(
165
+ <GameUIProvider runtime={runtime}>
166
+ <Board.Root>
167
+ <Board.State>
168
+ {(board) => (
169
+ <output
170
+ data-interactions={board.interactions.length}
171
+ data-eligible={board.isEligible("space-1", "space")}
172
+ />
173
+ )}
174
+ </Board.State>
175
+ </Board.Root>
176
+ </GameUIProvider>,
177
+ );
178
+
179
+ expect(html).toContain('data-interactions="1"');
180
+ expect(html).toContain('data-eligible="true"');
181
+ });
182
+
183
+ test("Board.HexView joins static hex topology with typed space overlays", () => {
184
+ const staticBoard = {
185
+ id: "island",
186
+ layout: "hex",
187
+ orientation: "pointy-top",
188
+ tiles: [
189
+ { id: "space-1", q: 0, r: 0 },
190
+ { id: "space-2", q: 1, r: 0 },
191
+ ],
192
+ edges: [],
193
+ vertices: [],
194
+ } as const;
195
+ const spaces = [
196
+ { id: "space-1", terrain: "forest" },
197
+ { id: "space-2", terrain: "field" },
198
+ ] as const;
199
+
200
+ const html = renderToString(
201
+ <Board.HexView board={staticBoard} spaces={spaces}>
202
+ {(view) => (
203
+ <span>
204
+ board:{view.id} first:{view.tiles[0]?.view.terrain}
205
+ </span>
206
+ )}
207
+ </Board.HexView>,
208
+ ).replace(/<!-- -->/g, "");
209
+
210
+ expect(html).toContain("board:island");
211
+ expect(html).toContain("first:forest");
212
+ });