@dreamboard-games/cli 0.1.30-alpha.1 → 0.1.30-alpha.10

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 (156) hide show
  1. package/README.md +179 -22
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +31 -30
  3. package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -0
  4. package/dist/agent-verifier/{chunk-4WD3YU2E.mjs → chunk-3IJBOLGT.mjs} +4 -12
  5. package/dist/agent-verifier/chunk-3IJBOLGT.mjs.map +1 -0
  6. package/dist/agent-verifier/{chunk-6A5HRJMQ.mjs → chunk-4GU3PCHV.mjs} +62 -99
  7. package/dist/agent-verifier/chunk-4GU3PCHV.mjs.map +1 -0
  8. package/dist/agent-verifier/{chunk-SYPLYRGB.mjs → chunk-6XRC5PWB.mjs} +119 -310
  9. package/dist/agent-verifier/chunk-6XRC5PWB.mjs.map +1 -0
  10. package/dist/agent-verifier/{chunk-BVVNBJM4.mjs → chunk-COB56ESI.mjs} +2 -1
  11. package/dist/agent-verifier/chunk-COB56ESI.mjs.map +1 -0
  12. package/dist/agent-verifier/{chunk-2GBBP27W.mjs → chunk-F2DIOJJZ.mjs} +1 -0
  13. package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +1 -0
  14. package/dist/agent-verifier/{chunk-CFU5EWIC.mjs → chunk-G42BGGG2.mjs} +7 -6
  15. package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +1 -0
  16. package/dist/agent-verifier/{chunk-XYDL7GY6.mjs → chunk-H6XDQJ3N.mjs} +1 -0
  17. package/dist/agent-verifier/{chunk-LM3OZLZG.mjs → chunk-IAYRNVUC.mjs} +1 -0
  18. package/dist/agent-verifier/chunk-IAYRNVUC.mjs.map +1 -0
  19. package/dist/agent-verifier/{chunk-2QMNAVV4.mjs → chunk-JZTH3EMV.mjs} +2 -1
  20. package/dist/agent-verifier/chunk-JZTH3EMV.mjs.map +1 -0
  21. package/dist/agent-verifier/chunk-KK47X7RV.mjs +14 -0
  22. package/dist/agent-verifier/chunk-KK47X7RV.mjs.map +1 -0
  23. package/dist/agent-verifier/{chunk-SHUMAVAP.mjs → chunk-M7UVBANQ.mjs} +8 -9
  24. package/dist/agent-verifier/chunk-M7UVBANQ.mjs.map +1 -0
  25. package/dist/agent-verifier/{chunk-JH22JNYD.mjs → chunk-MGXX4WFR.mjs} +87 -22
  26. package/dist/agent-verifier/chunk-MGXX4WFR.mjs.map +1 -0
  27. package/dist/agent-verifier/{chunk-2E5P5NWG.mjs → chunk-NAK77WXW.mjs} +58 -126
  28. package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +1 -0
  29. package/dist/agent-verifier/{chunk-CEQ2VJWN.mjs → chunk-POBFNXD4.mjs} +2 -1
  30. package/dist/agent-verifier/chunk-POBFNXD4.mjs.map +1 -0
  31. package/dist/agent-verifier/{chunk-6UUJEYDV.mjs → chunk-QBAF7EYR.mjs} +1 -0
  32. package/dist/agent-verifier/chunk-QBAF7EYR.mjs.map +1 -0
  33. package/dist/agent-verifier/{chunk-7653FPGJ.mjs → chunk-RHI6S4SU.mjs} +3 -2
  34. package/dist/agent-verifier/chunk-RHI6S4SU.mjs.map +1 -0
  35. package/dist/agent-verifier/{chunk-MINCYHXN.mjs → chunk-TAEQKBJB.mjs} +1 -0
  36. package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +1 -0
  37. package/dist/agent-verifier/{chunk-7E65UQLY.mjs → chunk-TLYGTHXU.mjs} +3 -2
  38. package/dist/agent-verifier/chunk-TLYGTHXU.mjs.map +1 -0
  39. package/dist/agent-verifier/{chunk-EIQWDQWJ.mjs → chunk-UWJIZML3.mjs} +13 -14
  40. package/dist/agent-verifier/chunk-UWJIZML3.mjs.map +1 -0
  41. package/dist/agent-verifier/{chunk-CJEEA6NJ.mjs → chunk-VLOIZDR6.mjs} +15 -31
  42. package/dist/agent-verifier/chunk-VLOIZDR6.mjs.map +1 -0
  43. package/dist/agent-verifier/{chunk-HJFQDSTU.mjs → chunk-W2MDP5ZN.mjs} +6 -5
  44. package/dist/agent-verifier/chunk-W2MDP5ZN.mjs.map +1 -0
  45. package/dist/agent-verifier/{chunk-CEDUHGNH.mjs → chunk-XKCJBIRY.mjs} +2 -1
  46. package/dist/agent-verifier/chunk-XKCJBIRY.mjs.map +1 -0
  47. package/dist/agent-verifier/{chunk-VYJTHSYR.mjs → chunk-YDIOW2BO.mjs} +2 -1
  48. package/dist/agent-verifier/chunk-YDIOW2BO.mjs.map +1 -0
  49. package/dist/agent-verifier/{chunk-MRCUP5SW.mjs → chunk-YE7UAO3T.mjs} +1 -0
  50. package/dist/agent-verifier/chunk-YE7UAO3T.mjs.map +1 -0
  51. package/dist/agent-verifier/{chunk-EOQIV6PS.mjs → chunk-YR664DJX.mjs} +111 -116
  52. package/dist/agent-verifier/chunk-YR664DJX.mjs.map +1 -0
  53. package/dist/agent-verifier/{chunk-2SZHMP6F.mjs → chunk-Z6OZWUIZ.mjs} +6 -9
  54. package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs.map +1 -0
  55. package/dist/agent-verifier/{chunk-RBDDIIPM.mjs → chunk-ZEELHSY3.mjs} +1 -0
  56. package/dist/agent-verifier/chunk-ZEELHSY3.mjs.map +1 -0
  57. package/dist/agent-verifier/{compile-5QSPIOUT.mjs → compile-C2VIP6VC.mjs} +27 -27
  58. package/dist/agent-verifier/compile-C2VIP6VC.mjs.map +1 -0
  59. package/dist/agent-verifier/{global-config-WX3ZZIVU.mjs → global-config-XHL7BCKN.mjs} +6 -5
  60. package/dist/agent-verifier/global-config-XHL7BCKN.mjs.map +1 -0
  61. package/dist/agent-verifier/{keychain-backend-TNOPQV3Z.mjs → keychain-backend-A3MRWLPF.mjs} +2 -1
  62. package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs.map +1 -0
  63. package/dist/agent-verifier/{local-files-MTPLP62S.mjs → local-files-ZW52HSVT.mjs} +10 -11
  64. package/dist/agent-verifier/local-files-ZW52HSVT.mjs.map +1 -0
  65. package/dist/agent-verifier/local-typecheck-3JXL2NMG.mjs +10 -0
  66. package/dist/agent-verifier/local-typecheck-3JXL2NMG.mjs.map +1 -0
  67. package/dist/agent-verifier/{materialize-workspace-FKALAE2T.mjs → materialize-workspace-BKZLLFI4.mjs} +20 -20
  68. package/dist/agent-verifier/materialize-workspace-BKZLLFI4.mjs.map +1 -0
  69. package/dist/agent-verifier/{project-state-7GR6BQTQ.mjs → project-state-XKUSCFSV.mjs} +3 -2
  70. package/dist/agent-verifier/project-state-XKUSCFSV.mjs.map +1 -0
  71. package/dist/agent-verifier/{prompt-3BAINGAQ.mjs → prompt-VKHMCQT6.mjs} +2 -1
  72. package/dist/agent-verifier/prompt-VKHMCQT6.mjs.map +1 -0
  73. package/dist/agent-verifier/{reducer-bundle-preflight-C73LEXI2.mjs → reducer-bundle-preflight-7NYZF5ZT.mjs} +6 -9
  74. package/dist/agent-verifier/reducer-bundle-preflight-7NYZF5ZT.mjs.map +1 -0
  75. package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs +11 -0
  76. package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs.map +1 -0
  77. package/dist/agent-verifier/{reducer-native-test-harness-GMWBUISX.mjs → reducer-native-test-harness-D4VWPIAC.mjs} +14 -17
  78. package/dist/agent-verifier/reducer-native-test-harness-D4VWPIAC.mjs.map +1 -0
  79. package/dist/agent-verifier/static-scaffold-JCRBDKEH.mjs +26 -0
  80. package/dist/agent-verifier/static-scaffold-JCRBDKEH.mjs.map +1 -0
  81. package/dist/agent-verifier/{sync-3DUQH32H.mjs → sync-UTL2IIZV.mjs} +35 -39
  82. package/dist/agent-verifier/sync-UTL2IIZV.mjs.map +1 -0
  83. package/dist/agent-verifier/{test-P4U5INTD.mjs → test-H26XCBFA.mjs} +29 -31
  84. package/dist/agent-verifier/test-H26XCBFA.mjs.map +1 -0
  85. package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs +10 -0
  86. package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs.map +1 -0
  87. package/dist/agent-verifier/{workspace-dependencies-HZ6VVS4G.mjs → workspace-dependencies-ULZZZPNX.mjs} +5 -4
  88. package/dist/agent-verifier/workspace-dependencies-ULZZZPNX.mjs.map +1 -0
  89. package/dist/{chunk-C6UAT6EH.js → chunk-GXM7RRZJ.js} +9 -11
  90. package/dist/chunk-GXM7RRZJ.js.map +1 -0
  91. package/dist/{chunk-RS7UXJZV.js → chunk-P5TITCD3.js} +790 -17875
  92. package/dist/chunk-P5TITCD3.js.map +1 -0
  93. package/dist/{global-config-AGFBDFYD.js → global-config-WPJRXVDO.js} +2 -2
  94. package/dist/global-config-WPJRXVDO.js.map +1 -0
  95. package/dist/index.js +437 -52
  96. package/dist/index.js.map +1 -1
  97. package/dist/internal.js +2 -3
  98. package/package.json +8 -7
  99. package/skills/dreamboard/references/building-your-first-game.md +510 -0
  100. package/skills/dreamboard/references/cli.md +104 -0
  101. package/skills/dreamboard/references/game-interface.md +548 -0
  102. package/skills/dreamboard/references/manifest-authoring.md +597 -0
  103. package/skills/dreamboard/references/quickstart.md +66 -0
  104. package/skills/dreamboard/references/reducer.md +864 -0
  105. package/skills/dreamboard/references/rule-authoring.md +147 -0
  106. package/skills/dreamboard/references/testing.md +249 -0
  107. package/skills/dreamboard/scripts/events-extract.mjs +218 -0
  108. package/dist/agent-verifier/chunk-54TAYXUD.mjs +0 -12
  109. package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
  110. package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
  111. package/dist/agent-verifier/chunk-U6OJN7XS.mjs +0 -8092
  112. package/dist/agent-verifier/local-typecheck-QFYYAZOK.mjs +0 -9
  113. package/dist/agent-verifier/reducer-contract-preflight-22X7DSZW.mjs +0 -10
  114. package/dist/agent-verifier/static-scaffold-AJMZZQWS.mjs +0 -28
  115. package/dist/agent-verifier/testing-5K2BJYF2.mjs +0 -674
  116. package/dist/agent-verifier/workspace-codegen-JDZJRSDV.mjs +0 -11
  117. package/dist/chunk-7FOO4AJI.js +0 -50
  118. package/dist/chunk-7FOO4AJI.js.map +0 -1
  119. package/dist/chunk-C6UAT6EH.js.map +0 -1
  120. package/dist/chunk-RS7UXJZV.js.map +0 -1
  121. package/dist/internal.d.ts +0 -311
  122. package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +0 -71
  123. package/dist/runtime-packages/ui-host-runtime/src/browser-interaction.ts +0 -139
  124. package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +0 -374
  125. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +0 -266
  126. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +0 -212
  127. package/dist/runtime-packages/ui-host-runtime/src/components/host-primitives.tsx +0 -271
  128. package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +0 -135
  129. package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +0 -5
  130. package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +0 -194
  131. package/dist/runtime-packages/ui-host-runtime/src/gameplay-authority-transport.ts +0 -626
  132. package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +0 -1
  133. package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +0 -1
  134. package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +0 -294
  135. package/dist/runtime-packages/ui-host-runtime/src/index.ts +0 -3
  136. package/dist/runtime-packages/ui-host-runtime/src/logger.ts +0 -11
  137. package/dist/runtime-packages/ui-host-runtime/src/perf.ts +0 -324
  138. package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +0 -195
  139. package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +0 -138
  140. package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +0 -159
  141. package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +0 -551
  142. package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +0 -13
  143. package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +0 -122
  144. package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +0 -26
  145. package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +0 -583
  146. package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +0 -219
  147. package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +0 -117
  148. package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +0 -431
  149. package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +0 -211
  150. package/dist/runtime-packages/ui-host-runtime/src/session-recovery.ts +0 -80
  151. package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +0 -1034
  152. package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +0 -416
  153. package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +0 -184
  154. package/dist/testing-KLSV6CPJ.js +0 -674
  155. package/dist/testing-KLSV6CPJ.js.map +0 -1
  156. /package/dist/{global-config-AGFBDFYD.js.map → agent-verifier/chunk-H6XDQJ3N.mjs.map} +0 -0
@@ -0,0 +1,548 @@
1
+ <!-- Generated by apps/dreamboard-cli/scripts/sync-skill-docs.ts. -->
2
+ <!-- Source: docs/reference/game-interface.mdx -->
3
+
4
+ # Game interface
5
+
6
+ Reference for authoring Dreamboard game interfaces.
7
+
8
+ `@dreamboard/ui-sdk` is Dreamboard's game interface package. Use
9
+ it to create UI for the game using projected game view, handle player actions and render prompts
10
+ windows.
11
+
12
+ ## Package boundary
13
+
14
+ Use `@dreamboard/ui-sdk` for:
15
+
16
+ - runtime bootstrap and loading/error shell
17
+ - reducer-view reads for the controlling seat
18
+ - typed action, prompt, and window submission
19
+ - lobby and session metadata
20
+ - presentational UI and board primitives
21
+
22
+ ## Import surface
23
+
24
+ Game UIs should import from `@dreamboard/ui-sdk` only.
25
+
26
+ ```tsx
27
+ import {
28
+ ErrorBoundary,
29
+ GameSkeleton,
30
+ PluginRuntime,
31
+ ToastProvider,
32
+ useActions,
33
+ useGameSelector,
34
+ useGameView,
35
+ useGameplayPrompts,
36
+ useGameplayWindows,
37
+ useLobby,
38
+ useMe,
39
+ usePlayerInfo,
40
+ usePluginSession,
41
+ } from "@dreamboard/ui-sdk";
42
+ ```
43
+
44
+ ## Runtime shell
45
+
46
+ ### `PluginRuntime`
47
+
48
+ `PluginRuntime` waits for the first reducer-native state-sync snapshot and then
49
+ provides runtime, session, and plugin-state context to authored hooks.
50
+
51
+ | Prop | Required | Notes |
52
+ | --- | --- | --- |
53
+ | `children` | Yes | Rendered after runtime initialization succeeds |
54
+ | `timeout` | No | Milliseconds to wait for first state-sync snapshot; defaults to `10000` |
55
+ | `loadingComponent` | No | Replaces the default loading shell |
56
+ | `errorComponent` | No | Receives the runtime boot error string |
57
+
58
+ ```tsx
59
+ import { PluginRuntime } from "@dreamboard/ui-sdk";
60
+ import { createRoot } from "react-dom/client";
61
+
62
+ createRoot(document.getElementById("root")!).render(
63
+ <PluginRuntime timeout={15000}>
64
+ <App />
65
+ </PluginRuntime>,
66
+ );
67
+ ```
68
+
69
+ ### `ErrorBoundary`
70
+
71
+ Wrap the root UI so authored render failures stay inside the plugin instead of
72
+ blanking the whole frame.
73
+
74
+ ```tsx
75
+ import { ErrorBoundary, PluginRuntime } from "@dreamboard/ui-sdk";
76
+
77
+ <ErrorBoundary>
78
+ <PluginRuntime>
79
+ <App />
80
+ </PluginRuntime>
81
+ </ErrorBoundary>;
82
+ ```
83
+
84
+ ### `ToastProvider` and `useToast()`
85
+
86
+ Use `ToastProvider` once near the root. Use `useToast()` from children when the
87
+ UI needs transient feedback that is not reducer state.
88
+
89
+ ```tsx
90
+ import { ToastProvider, useToast } from "@dreamboard/ui-sdk";
91
+
92
+ function EndTurnButton() {
93
+ const { success } = useToast();
94
+
95
+ return (
96
+ <button onClick={() => success("Turn submitted")}>
97
+ End turn
98
+ </button>
99
+ );
100
+ }
101
+ ```
102
+
103
+ `useToast()` returns:
104
+
105
+ - `toasts`
106
+ - `show(message, type?, duration?)`
107
+ - `dismiss(id)`
108
+ - `success(message, duration?)`
109
+ - `error(message, duration?)`
110
+ - `info(message, duration?)`
111
+ - `warning(message, duration?)`
112
+
113
+ ### `GameSkeleton`
114
+
115
+ Use `GameSkeleton` while the plugin session is still loading or when the UI
116
+ needs a fallback shell.
117
+
118
+ | Prop | Required | Notes |
119
+ | --- | --- | --- |
120
+ | `variant` | No | One of `default`, `cards`, `players`, or `minimal` |
121
+ | `message` | No | Loading or placeholder text |
122
+ | `className` | No | Additional container classes |
123
+
124
+ ```tsx
125
+ import { GameSkeleton, usePluginSession } from "@dreamboard/ui-sdk";
126
+
127
+ function AppLoader() {
128
+ const { status } = usePluginSession();
129
+
130
+ if (status === "loading") {
131
+ return <GameSkeleton variant="default" message="Loading game..." />;
132
+ }
133
+
134
+ return <App />;
135
+ }
136
+ ```
137
+
138
+ ## View and session hooks
139
+
140
+ ### `useGameView()`
141
+
142
+ Reads the full reducer-projected view for the controlling seat.
143
+
144
+ ```ts
145
+ const view = useGameView();
146
+ ```
147
+
148
+ Use this when the component genuinely needs the whole view object. Otherwise,
149
+ prefer `useGameSelector(...)`.
150
+
151
+ ### `useGameSelector(selector, equalityFn?)`
152
+
153
+ Reads one derived slice from the projected reducer view and only re-renders
154
+ when that slice changes according to `equalityFn`.
155
+
156
+ ```ts
157
+ const legalCardIds = useGameSelector(
158
+ (view) => new Set(view.legalCardIds),
159
+ (left, right) =>
160
+ left.size === right.size && [...left].every((cardId) => right.has(cardId)),
161
+ );
162
+ ```
163
+
164
+ Use selector hooks for:
165
+
166
+ - compact status text
167
+ - legal-target hints
168
+ - small card or resource subsets
169
+ - board-specific render inputs
170
+
171
+ ### `usePluginSession()`
172
+
173
+ Returns runtime session metadata for the current plugin.
174
+
175
+ | Field | Type | Notes |
176
+ | --- | --- | --- |
177
+ | `status` | `"loading" \| "ready"` | Session initialization status |
178
+ | `sessionId` | `string \| null` | Current session ID |
179
+ | `controllablePlayerIds` | `string[]` | Seats the user can control |
180
+ | `controllingPlayerId` | `string \| null` | Currently selected seat |
181
+ | `userId` | `string \| null` | Current user ID |
182
+
183
+ ```ts
184
+ const { controllingPlayerId, controllablePlayerIds, status } =
185
+ usePluginSession();
186
+ ```
187
+
188
+ ### `useLobby()`
189
+
190
+ Returns the current lobby snapshot from state-sync.
191
+
192
+ ```ts
193
+ const lobby = useLobby();
194
+ const seatCount = lobby.seats.length;
195
+ ```
196
+
197
+ Use this for seat order, display names, colours, and host markers.
198
+
199
+ ### `useMe()`
200
+
201
+ Returns the player currently being controlled by this user.
202
+
203
+ ```ts
204
+ const me = useMe();
205
+ // me.playerId
206
+ // me.name
207
+ // me.color
208
+ ```
209
+
210
+ This throws if there is no controlling player, so it is for playable seats, not
211
+ spectator-only UI.
212
+
213
+ ### `usePlayerInfo()`
214
+
215
+ Builds a `Map<PlayerId, Player>` from the current lobby snapshot.
216
+
217
+ ```ts
218
+ const players = usePlayerInfo();
219
+ const judge = players.get("player-1");
220
+ ```
221
+
222
+ Use it when the reducer view contains player IDs and the UI needs labels,
223
+ colours, or host markers.
224
+
225
+ ## Actions, prompts, and windows
226
+
227
+ ### `useActions()`
228
+
229
+ `useActions()` is the typed reducer action surface for the current phase.
230
+
231
+ | Field | Notes |
232
+ | --- | --- |
233
+ | `phase` | Current runtime phase |
234
+ | `commands` | Typed reducer action factories for the current phase |
235
+ | `availableActions` | `ReadonlySet` of currently available action names |
236
+ | `can(name)` | Whether an action is currently available |
237
+ | `dispatch(command)` | Submit one action |
238
+ | `validate(command)` | Validate one action without submitting it |
239
+ | `respondToPrompt(prompt, response)` | Submit a prompt response |
240
+ | `submitWindowAction(window, command)` | Submit a gameplay-window action |
241
+
242
+ ```ts
243
+ const phase = useActions();
244
+
245
+ if (phase.phase === "placeThing") {
246
+ await phase.dispatch(
247
+ phase.commands.placeThing({
248
+ cardId: "a-dog",
249
+ ringId: "ring-1",
250
+ }),
251
+ );
252
+ }
253
+ ```
254
+
255
+ Use `phase.commands.<action>(...)` instead of hand-writing `{ type, params }`
256
+ objects. The command and phase types come from the generated UI contract.
257
+
258
+ `dispatch(...)`, `validate(...)`, `respondToPrompt(...)`, and
259
+ `submitWindowAction(...)` reject when runtime validation or submission fails.
260
+
261
+ ### `useGameplayPrompts(promptId?)`
262
+
263
+ Returns active prompts for the controlling seat. Pass a prompt ID to narrow the
264
+ result to one prompt family.
265
+
266
+ ```ts
267
+ const judgePrompts = useGameplayPrompts("judge-placement");
268
+
269
+ if (judgePrompts[0]) {
270
+ await phase.respondToPrompt(judgePrompts[0], "ring-2");
271
+ }
272
+ ```
273
+
274
+ Use prompts for reducer-owned deferred input, not ad hoc local modal state.
275
+
276
+ Typed prompt response example:
277
+
278
+ ```tsx
279
+ import type { PromptResponse } from "@dreamboard/ui-contract";
280
+ import { useActions, useGameplayPrompts } from "@dreamboard/ui-sdk";
281
+
282
+ export function JudgePromptActions() {
283
+ const phase = useActions();
284
+ const prompts = useGameplayPrompts("judge-placement");
285
+ const activePrompt = prompts[0];
286
+
287
+ if (!activePrompt) {
288
+ return null;
289
+ }
290
+
291
+ const chooseRing = async (ringId: "ring-1" | "ring-2" | "ring-3") => {
292
+ const response: PromptResponse<"judge-placement"> = ringId;
293
+ await phase.respondToPrompt(activePrompt, response);
294
+ };
295
+
296
+ return (
297
+ <>
298
+ <button onClick={() => void chooseRing("ring-1")}>Ring 1</button>
299
+ <button onClick={() => void chooseRing("ring-2")}>Ring 2</button>
300
+ <button onClick={() => void chooseRing("ring-3")}>Ring 3</button>
301
+ </>
302
+ );
303
+ }
304
+ ```
305
+
306
+ ### `useGameplayWindows(windowId?)`
307
+
308
+ Returns active gameplay windows for the controlling seat. Pass a window ID to
309
+ filter the result.
310
+
311
+ ```ts
312
+ const tradeWindows = useGameplayWindows("trade-offer");
313
+
314
+ if (tradeWindows[0]) {
315
+ await phase.submitWindowAction(tradeWindows[0], {
316
+ type: "acceptTrade",
317
+ });
318
+ }
319
+ ```
320
+
321
+ Window actions are still reducer-defined and typed from the generated contract.
322
+
323
+ Typed window action example:
324
+
325
+ ```tsx
326
+ import { windowCommands } from "../shared/generated/ui-contract";
327
+ import { useActions, useGameplayWindows } from "@dreamboard/ui-sdk";
328
+
329
+ export function TradeWindowActions() {
330
+ const phase = useActions();
331
+ const windows = useGameplayWindows("trade-offer");
332
+ const activeWindow = windows[0];
333
+
334
+ if (!activeWindow) {
335
+ return null;
336
+ }
337
+
338
+ const acceptTrade = async () => {
339
+ await phase.submitWindowAction(
340
+ activeWindow,
341
+ windowCommands["trade-offer"].acceptTrade({
342
+ offerId: "offer-1",
343
+ }),
344
+ );
345
+ };
346
+
347
+ const declineTrade = async () => {
348
+ await phase.submitWindowAction(
349
+ activeWindow,
350
+ windowCommands["trade-offer"].declineTrade(),
351
+ );
352
+ };
353
+
354
+ return (
355
+ <>
356
+ <button onClick={() => void acceptTrade()}>Accept</button>
357
+ <button onClick={() => void declineTrade()}>Decline</button>
358
+ </>
359
+ );
360
+ }
361
+ ```
362
+
363
+ Import `windowCommands` from your workspace's generated
364
+ `shared/generated/ui-contract.ts` file.
365
+
366
+ ## Presentational primitives
367
+
368
+ These components are presentational. Feed them from reducer view data and
369
+ explicit props.
370
+
371
+ | Export | Use it for |
372
+ | --- | --- |
373
+ | `Card` | One authored card face |
374
+ | `Hand` | Responsive hand layout with render props |
375
+ | `PlayArea` | Shared played-card or centre-table region |
376
+ | `PlayerInfo` | Player badges, scores, and active-seat markers |
377
+ | `ActionButton` | One action with availability, loading, and cost state |
378
+ | `ActionPanel` / `ActionGroup` | Grouped action regions |
379
+ | `ResourceCounter` | Resource totals |
380
+ | `CostDisplay` | Cost breakdowns |
381
+ | `PhaseIndicator` | Current phase or step status |
382
+ | `GameEndDisplay` | Final rankings and scores |
383
+ | `DiceRoller` | Reducer-driven dice results |
384
+ | `Drawer` and related exports | Mobile overflow or detail panels |
385
+
386
+ `DiceRoller` is display-only. In reducer-native games, dice values are
387
+ typically driven by authored die state updated through reducer runtime effects
388
+ such as `effects.rollDie(...)`.
389
+
390
+ ### `Card`
391
+
392
+ `Card` renders one `CardItem`. Use `renderContent` when the default face is not
393
+ enough.
394
+
395
+ ```tsx
396
+ <Card
397
+ card={card}
398
+ selected={selectedIds.includes(card.id)}
399
+ onCardClick={(cardId) => setSelectedIds([cardId])}
400
+ renderContent={(item) => <ThingCardFace card={item} />}
401
+ />
402
+ ```
403
+
404
+ ### `Hand`
405
+
406
+ `Hand` is a render-prop primitive. You supply `renderCard`, `renderDrawer`, and
407
+ `renderEmpty`.
408
+
409
+ ```tsx
410
+ <Hand
411
+ cards={view.handCards}
412
+ selectedIds={selectedIds}
413
+ renderCard={({ card, x, y, zIndex, isSelected }) => (
414
+ <div
415
+ key={card.id}
416
+ style={{
417
+ position: "absolute",
418
+ left: x,
419
+ transform: `translateY(${y}px)`,
420
+ zIndex,
421
+ }}
422
+ >
423
+ <Card
424
+ card={card}
425
+ selected={isSelected}
426
+ onCardClick={(cardId) => setSelectedIds([cardId])}
427
+ />
428
+ </div>
429
+ )}
430
+ renderDrawer={({ cards }) => <button>View {cards.length} cards</button>}
431
+ renderEmpty={() => <p>No cards in hand</p>}
432
+ />
433
+ ```
434
+
435
+ Use `Hand` when you want SDK layout behaviour but game-specific card faces.
436
+
437
+ ### `PlayArea`
438
+
439
+ `PlayArea` renders a shared card region from an array of `CardItem` values.
440
+
441
+ ```tsx
442
+ <PlayArea
443
+ cards={view.trickCards}
444
+ layout="row"
445
+ renderCard={(card) => <ThingCardFace card={card} />}
446
+ />
447
+ ```
448
+
449
+ ### `ActionButton`
450
+
451
+ Use `ActionButton` for one reducer action. It can reflect availability,
452
+ resource affordability, and loading state.
453
+
454
+ ```tsx
455
+ <ActionButton
456
+ label="Build road"
457
+ cost={{ brick: 1, lumber: 1 }}
458
+ currentResources={view.resources}
459
+ resourceDefs={resourceDefs}
460
+ available={phase.can("buildRoad")}
461
+ onClick={() => phase.dispatch(phase.commands.buildRoad())}
462
+ />
463
+ ```
464
+
465
+ ### `ActionPanel` and `ActionGroup`
466
+
467
+ Use these to keep the action surface grouped by phase or intent.
468
+
469
+ ```tsx
470
+ <ActionPanel title="Your turn" state={phase.phase}>
471
+ <ActionGroup title="Build">
472
+ <ActionButton
473
+ label="Build road"
474
+ available={phase.can("buildRoad")}
475
+ onClick={() => phase.dispatch(phase.commands.buildRoad())}
476
+ />
477
+ </ActionGroup>
478
+ </ActionPanel>
479
+ ```
480
+
481
+ ### `PlayerInfo`
482
+
483
+ `PlayerInfo` is a display component. Pass display-ready values, not raw lobby
484
+ records.
485
+
486
+ ```tsx
487
+ <PlayerInfo
488
+ playerId={me.playerId}
489
+ name={me.name}
490
+ color={me.color}
491
+ isCurrentPlayer
492
+ isActive={view.activePlayerId === me.playerId}
493
+ score={view.scores[me.playerId] ?? 0}
494
+ />
495
+ ```
496
+
497
+ ## Board primitives
498
+
499
+ Use board primitives when the reducer view already contains render-ready board
500
+ data.
501
+
502
+ | Export | Use it for |
503
+ | --- | --- |
504
+ | `TrackBoard` | Linear, circular, or branching tracks |
505
+ | `SquareGrid` | Chess-like or tile-grid boards |
506
+ | `HexGrid` | Hex maps with tiles, edges, and vertices |
507
+ | `NetworkGraph` | Nodes, connections, and route graphs |
508
+ | `ZoneMap` | Region or area control boards |
509
+ | `SlotSystem` | Worker-placement and slot occupancy layouts |
510
+
511
+ ```tsx
512
+ <TrackBoard
513
+ spaces={view.track.spaces}
514
+ pieces={view.track.pieces}
515
+ type="circular"
516
+ renderSpace={(space, pieces) => (
517
+ <g aria-label={space.name ?? space.id}>
518
+ <circle cx={space.position.x} cy={space.position.y} r={24} />
519
+ <text x={space.position.x} y={space.position.y}>
520
+ {pieces.length}
521
+ </text>
522
+ </g>
523
+ )}
524
+ />
525
+ ```
526
+
527
+ Board renderers are still UI code. The reducer should provide:
528
+
529
+ - legal targets
530
+ - ownership markers
531
+ - highlight states
532
+ - labels and annotations
533
+ - grouped piece and card data in the shape the component expects
534
+
535
+ ## Exported types
536
+
537
+ Use these root exports when the UI needs to type reducer-owned runtime data.
538
+
539
+ | Type | Meaning |
540
+ | --- | --- |
541
+ | `PhaseActions<Phase>` | Current phase action API returned by `useActions()` |
542
+ | `ActionDefinition` | One available action from gameplay state |
543
+ | `GameplayPromptInstance` | One typed active prompt instance |
544
+ | `GameplayWindowInstance` | One typed active window instance |
545
+ | `GameplaySnapshot` | Transport state that sits alongside the projected view |
546
+ | `PluginStateSnapshot` | Complete state-sync payload delivered to the plugin |
547
+ | `LobbyState` | Current lobby seats and host metadata |
548
+ | `Player` | Simplified player metadata from the lobby |