dreamboard 0.1.21 → 0.1.23

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 (186) hide show
  1. package/dist/{chunk-EYYWGWTO.js → chunk-2RCUHMGL.js} +343 -1911
  2. package/dist/chunk-2RCUHMGL.js.map +1 -0
  3. package/dist/{chunk-MOVHYB6E.js → chunk-GN7232BY.js} +1025 -695
  4. package/dist/chunk-GN7232BY.js.map +1 -0
  5. package/dist/{chunk-BMYC6772.js → chunk-H3O43F5P.js} +9765 -3068
  6. package/dist/chunk-H3O43F5P.js.map +1 -0
  7. package/dist/dev-host/dev-api-proxy-plugin.ts +330 -0
  8. package/dist/dev-host/dev-diagnostics.ts +62 -0
  9. package/dist/dev-host/dev-fallback-stylesheet.ts +50 -0
  10. package/dist/dev-host/dev-host-controller.ts +686 -0
  11. package/dist/dev-host/dev-host-player-query.ts +17 -0
  12. package/dist/dev-host/dev-host-session-transport.ts +52 -0
  13. package/dist/dev-host/dev-host-storage.ts +56 -0
  14. package/dist/dev-host/dev-log-relay-plugin.ts +469 -0
  15. package/dist/dev-host/dev-runtime-config.ts +14 -0
  16. package/dist/dev-host/dev-runtime-platform.ts +419 -0
  17. package/dist/dev-host/dev-virtual-modules-plugin.ts +63 -0
  18. package/dist/dev-host/host-main.css +182 -0
  19. package/dist/dev-host/host-main.tsx +754 -0
  20. package/dist/dev-host/index.html +56 -0
  21. package/dist/dev-host/plugin-main.ts +55 -0
  22. package/dist/dev-host/plugin.html +24 -0
  23. package/dist/dev-host/start-dev-server.ts +138 -0
  24. package/dist/dev-host/virtual-modules.d.ts +27 -0
  25. package/dist/dist-FEPN3BDN.js +16543 -0
  26. package/dist/dist-FEPN3BDN.js.map +1 -0
  27. package/dist/{dist-WJRJNFLI.js → dist-FRURQI7Q.js} +4 -2
  28. package/dist/index.js +3 -3
  29. package/dist/internal.js +159 -124
  30. package/dist/internal.js.map +1 -1
  31. package/dist/runtime-packages/tailwind-config/shared-styles.css +146 -0
  32. package/dist/runtime-packages/ui/src/components/.gitkeep +0 -0
  33. package/dist/runtime-packages/ui/src/components/accordion.tsx +66 -0
  34. package/dist/runtime-packages/ui/src/components/ai-elements/actions.tsx +65 -0
  35. package/dist/runtime-packages/ui/src/components/ai-elements/artifact.tsx +147 -0
  36. package/dist/runtime-packages/ui/src/components/ai-elements/branch.tsx +215 -0
  37. package/dist/runtime-packages/ui/src/components/ai-elements/canvas.tsx +22 -0
  38. package/dist/runtime-packages/ui/src/components/ai-elements/chain-of-thought.tsx +228 -0
  39. package/dist/runtime-packages/ui/src/components/ai-elements/code-block.tsx +179 -0
  40. package/dist/runtime-packages/ui/src/components/ai-elements/confirmation.tsx +158 -0
  41. package/dist/runtime-packages/ui/src/components/ai-elements/connection.tsx +28 -0
  42. package/dist/runtime-packages/ui/src/components/ai-elements/context.tsx +408 -0
  43. package/dist/runtime-packages/ui/src/components/ai-elements/controls.tsx +18 -0
  44. package/dist/runtime-packages/ui/src/components/ai-elements/conversation.tsx +97 -0
  45. package/dist/runtime-packages/ui/src/components/ai-elements/edge.tsx +140 -0
  46. package/dist/runtime-packages/ui/src/components/ai-elements/image.tsx +24 -0
  47. package/dist/runtime-packages/ui/src/components/ai-elements/inline-citation.tsx +287 -0
  48. package/dist/runtime-packages/ui/src/components/ai-elements/loader.tsx +96 -0
  49. package/dist/runtime-packages/ui/src/components/ai-elements/message.tsx +463 -0
  50. package/dist/runtime-packages/ui/src/components/ai-elements/node.tsx +71 -0
  51. package/dist/runtime-packages/ui/src/components/ai-elements/open-in-chat.tsx +365 -0
  52. package/dist/runtime-packages/ui/src/components/ai-elements/panel.tsx +15 -0
  53. package/dist/runtime-packages/ui/src/components/ai-elements/plan.tsx +142 -0
  54. package/dist/runtime-packages/ui/src/components/ai-elements/prompt-input.tsx +1380 -0
  55. package/dist/runtime-packages/ui/src/components/ai-elements/queue.tsx +274 -0
  56. package/dist/runtime-packages/ui/src/components/ai-elements/reasoning.tsx +182 -0
  57. package/dist/runtime-packages/ui/src/components/ai-elements/response.tsx +22 -0
  58. package/dist/runtime-packages/ui/src/components/ai-elements/shimmer.tsx +64 -0
  59. package/dist/runtime-packages/ui/src/components/ai-elements/sources.tsx +77 -0
  60. package/dist/runtime-packages/ui/src/components/ai-elements/suggestion.tsx +53 -0
  61. package/dist/runtime-packages/ui/src/components/ai-elements/task.tsx +87 -0
  62. package/dist/runtime-packages/ui/src/components/ai-elements/tool.tsx +165 -0
  63. package/dist/runtime-packages/ui/src/components/ai-elements/toolbar.tsx +16 -0
  64. package/dist/runtime-packages/ui/src/components/ai-elements/web-preview.tsx +263 -0
  65. package/dist/runtime-packages/ui/src/components/alert-dialog.tsx +157 -0
  66. package/dist/runtime-packages/ui/src/components/alert.tsx +66 -0
  67. package/dist/runtime-packages/ui/src/components/avatar.tsx +53 -0
  68. package/dist/runtime-packages/ui/src/components/badge.tsx +46 -0
  69. package/dist/runtime-packages/ui/src/components/button-group.tsx +83 -0
  70. package/dist/runtime-packages/ui/src/components/button.tsx +65 -0
  71. package/dist/runtime-packages/ui/src/components/card.tsx +92 -0
  72. package/dist/runtime-packages/ui/src/components/carousel.tsx +241 -0
  73. package/dist/runtime-packages/ui/src/components/collapsible.tsx +33 -0
  74. package/dist/runtime-packages/ui/src/components/command.tsx +184 -0
  75. package/dist/runtime-packages/ui/src/components/context-menu.tsx +252 -0
  76. package/dist/runtime-packages/ui/src/components/counter.tsx +11 -0
  77. package/dist/runtime-packages/ui/src/components/dialog.tsx +146 -0
  78. package/dist/runtime-packages/ui/src/components/drawer.tsx +132 -0
  79. package/dist/runtime-packages/ui/src/components/dropdown-menu.tsx +257 -0
  80. package/dist/runtime-packages/ui/src/components/form.tsx +168 -0
  81. package/dist/runtime-packages/ui/src/components/header.tsx +13 -0
  82. package/dist/runtime-packages/ui/src/components/hover-card.tsx +44 -0
  83. package/dist/runtime-packages/ui/src/components/input-group.tsx +178 -0
  84. package/dist/runtime-packages/ui/src/components/input.tsx +21 -0
  85. package/dist/runtime-packages/ui/src/components/item.tsx +193 -0
  86. package/dist/runtime-packages/ui/src/components/label.tsx +24 -0
  87. package/dist/runtime-packages/ui/src/components/navigation-menu.tsx +168 -0
  88. package/dist/runtime-packages/ui/src/components/popover.tsx +55 -0
  89. package/dist/runtime-packages/ui/src/components/progress.tsx +31 -0
  90. package/dist/runtime-packages/ui/src/components/radio-group.tsx +45 -0
  91. package/dist/runtime-packages/ui/src/components/resizable.tsx +56 -0
  92. package/dist/runtime-packages/ui/src/components/scroll-area.tsx +58 -0
  93. package/dist/runtime-packages/ui/src/components/select.tsx +187 -0
  94. package/dist/runtime-packages/ui/src/components/separator.tsx +28 -0
  95. package/dist/runtime-packages/ui/src/components/skeleton.tsx +13 -0
  96. package/dist/runtime-packages/ui/src/components/sonner.tsx +25 -0
  97. package/dist/runtime-packages/ui/src/components/spinner.tsx +16 -0
  98. package/dist/runtime-packages/ui/src/components/switch.tsx +31 -0
  99. package/dist/runtime-packages/ui/src/components/table.tsx +137 -0
  100. package/dist/runtime-packages/ui/src/components/tabs.tsx +74 -0
  101. package/dist/runtime-packages/ui/src/components/textarea.tsx +23 -0
  102. package/dist/runtime-packages/ui/src/components/theme-provider.tsx +73 -0
  103. package/dist/runtime-packages/ui/src/components/tooltip.tsx +61 -0
  104. package/dist/runtime-packages/ui/src/components/ui/file-upload.tsx +195 -0
  105. package/dist/runtime-packages/ui/src/hooks/.gitkeep +0 -0
  106. package/dist/runtime-packages/ui/src/index.ts +68 -0
  107. package/dist/runtime-packages/ui/src/lib/utils.ts +6 -0
  108. package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +68 -0
  109. package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +359 -0
  110. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +274 -0
  111. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +214 -0
  112. package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +135 -0
  113. package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +5 -0
  114. package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +194 -0
  115. package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +1 -0
  116. package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +1 -0
  117. package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +162 -0
  118. package/dist/runtime-packages/ui-host-runtime/src/index.ts +2 -0
  119. package/dist/runtime-packages/ui-host-runtime/src/logger.ts +11 -0
  120. package/dist/runtime-packages/ui-host-runtime/src/perf.ts +253 -0
  121. package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +195 -0
  122. package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +138 -0
  123. package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +159 -0
  124. package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +524 -0
  125. package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +12 -0
  126. package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +122 -0
  127. package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +26 -0
  128. package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +477 -0
  129. package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +209 -0
  130. package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +112 -0
  131. package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +318 -0
  132. package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +211 -0
  133. package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +814 -0
  134. package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +334 -0
  135. package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +180 -0
  136. package/dist/scaffold/assets/static/app/tsconfig.framework.json +3 -1
  137. package/dist/scaffold/assets/static/ui/tsconfig.framework.json +7 -19
  138. package/package.json +52 -17
  139. package/skills/dreamboard/SKILL.md +41 -75
  140. package/LICENSE +0 -89
  141. package/NOTICE +0 -1
  142. package/dist/chunk-BMYC6772.js.map +0 -1
  143. package/dist/chunk-EYYWGWTO.js.map +0 -1
  144. package/dist/chunk-MOVHYB6E.js.map +0 -1
  145. package/dist/scaffold/assets/static/ui/App.tsx +0 -22
  146. package/dist/src-CUL7EGGG.js +0 -634
  147. package/dist/src-CUL7EGGG.js.map +0 -1
  148. package/skills/dreamboard/references/authoring-lifecycle.md +0 -102
  149. package/skills/dreamboard/references/board-surfaces.md +0 -36
  150. package/skills/dreamboard/references/board-topology.md +0 -443
  151. package/skills/dreamboard/references/boards-and-topology.md +0 -100
  152. package/skills/dreamboard/references/card-actions.md +0 -107
  153. package/skills/dreamboard/references/cli.md +0 -120
  154. package/skills/dreamboard/references/core-concepts.md +0 -514
  155. package/skills/dreamboard/references/custom-renderers.md +0 -26
  156. package/skills/dreamboard/references/derived-values.md +0 -55
  157. package/skills/dreamboard/references/effects.md +0 -111
  158. package/skills/dreamboard/references/game-contract.md +0 -89
  159. package/skills/dreamboard/references/game-definition.md +0 -89
  160. package/skills/dreamboard/references/game-shell.md +0 -80
  161. package/skills/dreamboard/references/hand-surfaces.md +0 -33
  162. package/skills/dreamboard/references/index.md +0 -112
  163. package/skills/dreamboard/references/inputs-and-targets.md +0 -160
  164. package/skills/dreamboard/references/interactions.md +0 -158
  165. package/skills/dreamboard/references/manifest-fields.md +0 -137
  166. package/skills/dreamboard/references/manifest.md +0 -81
  167. package/skills/dreamboard/references/package-surfaces.md +0 -69
  168. package/skills/dreamboard/references/phases.md +0 -145
  169. package/skills/dreamboard/references/prompts-and-choices.md +0 -55
  170. package/skills/dreamboard/references/quickstart.md +0 -71
  171. package/skills/dreamboard/references/rule-authoring.md +0 -144
  172. package/skills/dreamboard/references/setup-bootstrap.md +0 -78
  173. package/skills/dreamboard/references/stages-and-zones.md +0 -59
  174. package/skills/dreamboard/references/static-views.md +0 -67
  175. package/skills/dreamboard/references/table-queries-and-ops.md +0 -103
  176. package/skills/dreamboard/references/testing-bases.md +0 -147
  177. package/skills/dreamboard/references/testing-generated-contracts.md +0 -142
  178. package/skills/dreamboard/references/testing-runtime-assertions.md +0 -124
  179. package/skills/dreamboard/references/testing-scenarios.md +0 -148
  180. package/skills/dreamboard/references/testing-ui-tests.md +0 -174
  181. package/skills/dreamboard/references/testing.md +0 -161
  182. package/skills/dreamboard/references/ui-architecture.md +0 -137
  183. package/skills/dreamboard/references/ui-components.md +0 -34
  184. package/skills/dreamboard/references/views.md +0 -72
  185. package/skills/dreamboard/references/workspace-layout.md +0 -136
  186. /package/dist/{dist-WJRJNFLI.js.map → dist-FRURQI7Q.js.map} +0 -0
@@ -1,514 +0,0 @@
1
- # Core concepts
2
-
3
- Learn Dreamboard's core concepts through real reducer, UI, and test code.
4
-
5
- Dreamboard games are authored as a small TypeScript program. The same authored
6
- program feeds the reducer runtime, generated UI contract, generated testing
7
- contract, and local CLI checks.
8
-
9
- The snippets below use generic SDK-shaped code and scaffold patterns.
10
-
11
- ## Manifest
12
-
13
- The manifest is the static table model. Author it in `manifest.ts` with
14
- `defineTopologyManifest`.
15
-
16
- ```ts
17
- import { defineTopologyManifest } from "@dreamboard/sdk-types";
18
-
19
- export default defineTopologyManifest({
20
- players: {
21
- minPlayers: 3,
22
- maxPlayers: 4,
23
- optimalPlayers: 4,
24
- },
25
-
26
- zones: [
27
- {
28
- id: "action-deck",
29
- name: "Action Deck",
30
- scope: "shared",
31
- allowedCardSetIds: ["action-cards"],
32
- visibility: "hidden",
33
- },
34
- {
35
- id: "player-hand",
36
- name: "Player Hand",
37
- scope: "perPlayer",
38
- allowedCardSetIds: ["action-cards"],
39
- visibility: "ownerOnly",
40
- },
41
- {
42
- id: "played-cards",
43
- name: "Played Cards",
44
- scope: "shared",
45
- allowedCardSetIds: ["action-cards"],
46
- visibility: "public",
47
- },
48
- ],
49
-
50
- boardTemplates: [
51
- {
52
- id: "shared-board-template",
53
- name: "Shared board template",
54
- layout: "hex",
55
- orientation: "pointy-top",
56
- spaces: [
57
- { id: "cell-01", q: 0, r: 0, typeId: "standard" },
58
- { id: "cell-02", q: 1, r: -1, typeId: "standard" },
59
- ],
60
- },
61
- ],
62
- });
63
- ```
64
-
65
- `dreamboard sync` turns the manifest into generated ID unions, ID schemas,
66
- runtime table types, board helpers, setup helpers, and typed UI/test contracts.
67
-
68
- ## Game contract
69
-
70
- The game contract binds the generated manifest contract to reducer state schemas
71
- and phase names.
72
-
73
- ```ts
74
- import { z } from "zod";
75
- import { ids, manifestContract } from "../shared/manifest-contract";
76
- import {
77
- defineGameContract,
78
- sparseCounts,
79
- type GameStateOf,
80
- } from "@dreamboard/app-sdk/reducer";
81
-
82
- const playerTurnPhaseStateSchema = z.object({
83
- step: z.enum(["roll", "resolve", "main", "cleanup"]),
84
- diceRolled: z.boolean(),
85
- diceValues: z
86
- .tuple([z.number().int().min(1).max(6), z.number().int().min(1).max(6)])
87
- .nullable(),
88
- });
89
-
90
- export const gameContract = defineGameContract({
91
- manifest: manifestContract,
92
- state: {
93
- public: z.object({
94
- progressByPlayer: sparseCounts(ids.playerId),
95
- }),
96
- private: z.object({}),
97
- hidden: z.object({}),
98
- },
99
- phaseNames: ["setup", "playerTurn"] as const,
100
- });
101
-
102
- export type GameContract = typeof gameContract;
103
- export type GameState = GameStateOf<GameContract>;
104
- export type PlayerTurnPhaseState = z.infer<typeof playerTurnPhaseStateSchema>;
105
- ```
106
-
107
- The contract is the reducer's type root. If a phase name, player id, card id,
108
- zone id, board id, or state field changes, dependent reducer/UI/test code should
109
- fail to compile.
110
-
111
- ## Game definition
112
-
113
- `defineGame` assembles the contract into a runnable reducer definition. Initial
114
- state belongs here, not in `defineGameContract`.
115
-
116
- ```ts
117
- import {
118
- defineGame,
119
- type ReducerGameDefinition,
120
- } from "@dreamboard/app-sdk/reducer";
121
- import { gameContract, type GameContract } from "./game-contract";
122
- import { phases } from "./phases";
123
- import { playerView } from "./player-view";
124
- import { boardStatic } from "./board-static";
125
- import setupProfiles from "./setup-profiles";
126
-
127
- const views = {
128
- player: playerView,
129
- };
130
-
131
- const game: ReducerGameDefinition<GameContract, typeof phases, typeof views> =
132
- defineGame({
133
- contract: gameContract,
134
- initial: {
135
- public: ({ playerIds }) => {
136
- const progressByPlayer: Record<string, number> = {};
137
- for (const pid of playerIds) {
138
- // Session-time player ids come from the manifest-backed setup.
139
- progressByPlayer[pid] = 0;
140
- }
141
- return { progressByPlayer };
142
- },
143
- private: () => ({}),
144
- hidden: () => ({}),
145
- },
146
- initialPhase: "setup",
147
- setupProfiles,
148
- phases,
149
- views,
150
- staticView: boardStatic,
151
- });
152
-
153
- export default game;
154
- ```
155
-
156
- Generated UI and testing contracts read from this definition. Registered phases,
157
- interactions, zones, and board targets become typed UI/test primitives
158
- automatically.
159
-
160
- ## Phase
161
-
162
- A phase owns a slice of game flow: phase-local state, actor selection, optional
163
- entry behavior, interactions, card actions, stages, and zones.
164
-
165
- ```ts
166
- import { definePhase, pipe } from "@dreamboard/app-sdk/reducer";
167
- import { zones } from "../../../shared/manifest-contract";
168
- import {
169
- playerTurnPhaseStateSchema,
170
- type GameContract,
171
- } from "../../game-contract";
172
- import { rollDice } from "./roll-dice";
173
- import { playActionCard } from "./card-actions";
174
- import { endTurn } from "./end-turn";
175
- import { FRESH_TURN } from "./turn-state";
176
-
177
- export const playerTurn = definePhase<GameContract>()({
178
- kind: "player",
179
- state: playerTurnPhaseStateSchema,
180
- initialState: () => ({ ...FRESH_TURN }),
181
- enter({ state, accept, ops, q }) {
182
- const activePlayer = state.flow.activePlayers[0] ?? q.player.order()[0]!;
183
-
184
- // Entering from setup seeds the first turn. Later turns are set by endTurn.
185
- return accept(pipe(state, ops.setActivePlayers([activePlayer])));
186
- },
187
- actor: ({ state }) => state.flow.activePlayers,
188
- zones: [zones.playerHand],
189
- cardActions: {
190
- playActionCard,
191
- },
192
- interactions: {
193
- rollDice,
194
- endTurn,
195
- },
196
- });
197
- ```
198
-
199
- Use phases for real game-flow states: setup, turn, discard, resolve prompt,
200
- round cleanup, and end game.
201
-
202
- ## Interaction
203
-
204
- An interaction is a player-submitted command or prompt. The trusted runtime
205
- authorizes the actor, validates input collectors, parses params, and only then
206
- calls `reduce`.
207
-
208
- ```ts
209
- import {
210
- defineInteraction,
211
- rngInput,
212
- pipe,
213
- } from "@dreamboard/app-sdk/reducer";
214
- import {
215
- playerTurnPhaseStateSchema,
216
- type GameContract,
217
- } from "../../game-contract";
218
-
219
- export const rollDice = defineInteraction<
220
- GameContract,
221
- typeof playerTurnPhaseStateSchema
222
- >()({
223
- step: "roll",
224
- visibility: "all",
225
- inputs: {
226
- // The client submits no dice values. The trusted reducer bundle samples
227
- // both dice and passes the result to reduce as input.params.dice.values.
228
- dice: rngInput.d6(2),
229
- },
230
- validate({ state }) {
231
- if (state.phase.diceRolled) {
232
- return {
233
- errorCode: "ALREADY_ROLLED",
234
- message: "Already rolled this turn.",
235
- };
236
- }
237
- return null;
238
- },
239
- reduce({ state, input, accept, ops }) {
240
- const [d1, d2] = input.params.dice.values;
241
- return accept(
242
- pipe(
243
- state,
244
- ops.patchPhaseState({
245
- step: "main",
246
- diceRolled: true,
247
- diceValues: [d1!, d2!],
248
- }),
249
- ),
250
- );
251
- },
252
- });
253
- ```
254
-
255
- Use `validate` for business rules. Do not repeat turn authorization in every
256
- interaction when phase-level `actor` already owns it.
257
-
258
- ## Card action
259
-
260
- A card action is a hand-oriented interaction. It is tied to a `cardType` and a
261
- `playFrom` zone, and the runtime adds `cardId` to reducer params.
262
-
263
- ```ts
264
- import { defineCardAction, pipe } from "@dreamboard/app-sdk/reducer";
265
- import {
266
- playerTurnPhaseStateSchema,
267
- type GameContract,
268
- } from "../../game-contract";
269
- import { cardTypes, zones } from "../../../shared/manifest-contract";
270
-
271
- export const playActionCard = defineCardAction<
272
- GameContract,
273
- typeof playerTurnPhaseStateSchema
274
- >()({
275
- cardType: cardTypes.bonus,
276
- playFrom: zones.playerHand,
277
- label: "Play action card",
278
- step: "main",
279
- reduce({ state, input, accept, ops }) {
280
- return accept(
281
- pipe(
282
- state,
283
- ops.moveCardFromPlayerZoneToSharedZone({
284
- playerId: input.playerId,
285
- fromZoneId: zones.playerHand,
286
- toZoneId: zones.playedCards,
287
- cardId: input.params.cardId,
288
- playedBy: input.playerId,
289
- }),
290
- ops.patchPhaseState({ actionCardPlayedThisTurn: true }),
291
- ),
292
- );
293
- },
294
- });
295
- ```
296
-
297
- Use `defineCardAction` for "play this card from hand" mechanics. Use
298
- `defineInteraction` for actions that are not fundamentally card-instance plays.
299
-
300
- ## Input collector
301
-
302
- An input collector describes one parameter for an interaction. Collectors are
303
- the source of reducer parameter types and client-facing metadata.
304
-
305
- ```ts
306
- import { boardInput, formInput } from "@dreamboard/app-sdk/reducer";
307
- import type { GameState } from "../../game-contract";
308
- import {
309
- literals,
310
- type ResourceId,
311
- type SpaceId,
312
- } from "../../../shared/manifest-contract";
313
- import { tokenSpaceTarget } from "../../eligibility";
314
-
315
- const moveTokenInputs = {
316
- spaceId: boardInput.space<GameState, SpaceId>({
317
- target: tokenSpaceTarget,
318
- }),
319
- receiveResource: formInput.choice<ResourceId>({
320
- choices: literals.resourceIds.map((resourceId) => ({
321
- value: resourceId,
322
- label: resourceId,
323
- })),
324
- }),
325
- };
326
- ```
327
-
328
- Engine-sampled collectors such as `rngInput.*` appear in reducer params but are
329
- omitted from client-submitted params.
330
-
331
- ## Target rule
332
-
333
- A target rule is a finite eligibility rule built with `boardTarget`,
334
- `cardTarget`, or `choiceTarget`.
335
-
336
- ```ts
337
- import { boardTarget } from "@dreamboard/app-sdk/reducer";
338
- import type { GameState } from "./game-contract";
339
- import type { EdgeId } from "../shared/manifest-contract";
340
- import { markerAt } from "./reducer-support";
341
-
342
- export const buildMarkerTarget = boardTarget
343
- .edge<GameState, EdgeId>("sector")
344
- .where({
345
- id: "empty",
346
- errorCode: "EDGE_OCCUPIED",
347
- message: "That edge already has a marker.",
348
- test: ({ state, q, targetId }) => !markerAt(state, q, targetId),
349
- })
350
- .where({
351
- id: "connected",
352
- errorCode: "NOT_CONNECTED",
353
- message: "Marker must connect to your network.",
354
- // Local domain helpers can use the same state/q/playerId inputs as targets.
355
- test: ({ state, q, targetId, playerId }) =>
356
- isConnectedMarkerTarget(state, q, targetId, playerId),
357
- })
358
- .build();
359
- ```
360
-
361
- The same rule should drive UI hints, submit validation, and tests. That keeps
362
- "which space/card/choice is legal?" out of duplicated UI code.
363
-
364
- ## View
365
-
366
- A dynamic view is the player-facing projection authored with `defineView`. UI
367
- code should read views and generated hooks, not raw reducer state.
368
-
369
- ```ts
370
- import { defineView } from "@dreamboard/app-sdk/reducer";
371
- import type { GameContract, PlayerTurnPhaseState } from "./game-contract";
372
- import { scoreLeader } from "./derived";
373
-
374
- export const playerView = defineView<GameContract>()({
375
- project({ state, playerId, q, derived }) {
376
- const turn: PlayerTurnPhaseState | null =
377
- state.flow.currentPhase === "playerTurn"
378
- ? (state.phase as PlayerTurnPhaseState)
379
- : null;
380
-
381
- return {
382
- diceRolled: turn?.diceRolled ?? false,
383
- diceValues: turn?.diceValues ?? null,
384
- myResources: q.player.resources(playerId),
385
- leader: derived(scoreLeader),
386
- };
387
- },
388
- });
389
- ```
390
-
391
- Views receive state, `playerId`, runtime helpers, table queries, and derived
392
- value access.
393
-
394
- ## Static view
395
-
396
- A static view is session-scoped immutable data derived from the manifest and
397
- static queries.
398
-
399
- ```ts
400
- import { defineStaticView } from "@dreamboard/app-sdk/reducer";
401
- import type { GameContract } from "./game-contract";
402
-
403
- export const boardStatic = defineStaticView<GameContract>()({
404
- project: ({ q }) => {
405
- // Board topology does not change during play, so the UI can cache it.
406
- return q.board.hex("sector");
407
- },
408
- });
409
- ```
410
-
411
- Use static views for board topology, labels, and precomputed decoration derived
412
- from the manifest.
413
-
414
- ## Derived value
415
-
416
- A derived value is a pure reusable computation authored with `defineDerived`.
417
- It is memoized for one state snapshot.
418
-
419
- ```ts
420
- import { defineDerived } from "@dreamboard/app-sdk/reducer";
421
- import type { GameContract } from "./game-contract";
422
- import type { PlayerId } from "../shared/manifest-contract";
423
-
424
- // Finds the current leader from stored per-player progress for this state.
425
- export const scoreLeader = defineDerived<GameContract>()({
426
- name: "scoreLeader",
427
- compute: ({ state, q }): { playerId: PlayerId | null; score: number } => {
428
- let playerId: PlayerId | null = null;
429
- let score = 0;
430
-
431
- for (const pid of q.player.order()) {
432
- const playerScore = state.publicState.progressByPlayer[pid] ?? 0;
433
- if (playerScore > score) {
434
- playerId = pid;
435
- score = playerScore;
436
- }
437
- }
438
-
439
- return { playerId, score };
440
- },
441
- });
442
- ```
443
-
444
- Keep source facts in state and compute aggregates from them.
445
-
446
- ## UI primitive
447
-
448
- Generated UI primitives are typed React wrappers for the reducer contract. They
449
- let UI code submit interactions, answer prompts, render zones, and target board
450
- spaces without stringly typed input names.
451
-
452
- ```tsx
453
- import {
454
- Interaction,
455
- PromptInbox,
456
- Zone,
457
- useInteractionByKey,
458
- } from "@dreamboard/ui-contract";
459
-
460
- export function PlayerTurnPanel() {
461
- const rollDice = useInteractionByKey("playerTurn.rollDice");
462
-
463
- return (
464
- <section>
465
- <PromptInbox.Root>
466
- <PromptInbox.Empty>No prompts.</PromptInbox.Empty>
467
- <PromptInbox.Items />
468
- </PromptInbox.Root>
469
-
470
- <Interaction.Root interaction="playerTurn.rollDice">
471
- <Interaction.Submit disabled={!rollDice?.canSubmit}>
472
- Roll dice
473
- </Interaction.Submit>
474
- </Interaction.Root>
475
-
476
- <Zone.Root zone="playerHand">
477
- <Zone.List />
478
- </Zone.Root>
479
- </section>
480
- );
481
- }
482
- ```
483
-
484
- Use `@dreamboard/ui-contract` for game-specific typing and `@dreamboard/ui-sdk`
485
- for generic components such as cards, boards, resources, dice, and player
486
- chrome.
487
-
488
- ## Scenario
489
-
490
- A scenario is a reducer-native test authored with `defineScenario`.
491
-
492
- ```ts
493
- import { defineScenario } from "../testing-types";
494
-
495
- export default defineScenario({
496
- id: "player-turn-roll",
497
- description: "Seat 0 rolls dice on their first turn after setup",
498
- from: "after-setup",
499
- when: async ({ game, seat }) => {
500
- // rollDice uses rngInput.d6(2), so the client submits no params.
501
- await game.submit(seat(0), "rollDice", {});
502
- },
503
- then: ({ expect, state, view, seat }) => {
504
- expect(state()).toBe("playerTurn");
505
-
506
- const p1View = view(seat(0));
507
- expect(p1View.diceRolled).toBe(true);
508
- expect(p1View.diceValues).not.toBeNull();
509
- },
510
- });
511
- ```
512
-
513
- Scenarios verify the same reducer bundle that `dreamboard dev` runs. They are
514
- the fastest way to prove reducer behavior without a browser.
@@ -1,26 +0,0 @@
1
- # Custom renderers
2
-
3
- Use hooks as local escape hatches while keeping reducer behavior authoritative.
4
-
5
- Most UI should be built from `Interaction`, `Prompt`, `PromptInbox`, and `Zone`
6
- components. Use hooks when component composition cannot express the UI.
7
-
8
- Good hook escape hatches:
9
-
10
- - SVG or canvas board geometry
11
- - gesture-heavy interaction layers
12
- - styling a card based on draft state
13
- - integrating a complex third-party renderer
14
-
15
- ```tsx
16
- const buildRoute = useInteractionByKey("playerTurn.buildRoute");
17
- const board = useBoardInteractions();
18
- ```
19
-
20
- Do not use hooks to recreate a generic action panel or prompt inbox. Prefer
21
- primitives and defaults for ordinary actions, prompts, card selection, and
22
- zones.
23
-
24
- When a custom renderer needs a descriptor, bind it through
25
- `useInteractionByKey` or `useInteractionHandle`; do not call runtime transport
26
- methods directly.
@@ -1,55 +0,0 @@
1
- # Derived values
2
-
3
- Use defineDerived for reusable, memoized pure computations over reducer state and table queries.
4
-
5
- Derived values are pure computations over one state snapshot. They are useful for expensive or repeated calculations: longest route, area majority, winner selection, eligible scoring sets, or aggregate card/resource summaries.
6
-
7
- ```ts
8
- export const winnerOf = defineDerived<GameContract>()({
9
- name: "winnerOf",
10
- compute: ({ state, q }) => {
11
- const players = q.player.order();
12
- const scores = players.map((playerId) => ({
13
- playerId,
14
- score: state.publicState.scores[playerId] ?? 0,
15
- }));
16
- return scores.toSorted((a, b) => b.score - a.score)[0]?.playerId ?? null;
17
- },
18
- });
19
- ```
20
-
21
- Read it from reducer callbacks or views through injected `derived`:
22
-
23
- ```ts
24
- reduce({ state, derived, accept, ops }) {
25
- const winnerPlayerId = derived(winnerOf);
26
- return accept(pipe(state, ops.patchPublicState({ winnerPlayerId })));
27
- }
28
- ```
29
-
30
- ## Cache lifetime
31
-
32
- Derived values are memoized by definition identity for one state snapshot. Each engine tick and each view projection gets a fresh resolver.
33
-
34
- That means:
35
-
36
- - repeated reads of the same derived value in one callback are cheap
37
- - derived values should be pure
38
- - cache lifetime does not cross reducer steps
39
- - derived values can depend on other derived values through `derived(...)`
40
-
41
- Cyclic derived dependencies throw a readable error.
42
-
43
- ## Do not cache mirrors in publicState
44
-
45
- Avoid this pattern:
46
-
47
- ```ts
48
- publicState: {
49
- scoreByPlayerId: ...
50
- }
51
- ```
52
-
53
- if the value can be recomputed from board occupancy, card locations, resources, or other source facts. Store the source facts and derive the aggregate.
54
-
55
- Use state for mutable facts. Use `defineDerived` for computed meaning.
@@ -1,111 +0,0 @@
1
- # Effects
2
-
3
- Use defineEffect and fx.effect for engine-side dice rolls and zone shuffles.
4
-
5
- Effects are engine-side cues. They are for runtime work the pure reducer should not perform itself, such as rolling a die or shuffling a shared or player zone.
6
-
7
- Effects are authored with `defineEffect`, registered on a phase, and dispatched with `fx.effect`.
8
-
9
- ## Shuffle a shared zone
10
-
11
- ```ts
12
- const shuffleDeck = defineEffect<GameContract>()({
13
- type: "shuffleSharedZone",
14
- id: "shuffle-deck",
15
- });
16
-
17
- export const shufflePhase = definePhase<GameContract>()({
18
- kind: "auto",
19
- state: z.object({}),
20
- initialState: () => ({}),
21
- effects: {
22
- shuffleDeck,
23
- },
24
- stages: {
25
- shuffle: definePhaseStage<GameContract, typeof phaseState>()({
26
- allow: [],
27
- onEnter({ state, accept, fx }) {
28
- return accept(state, [
29
- fx.effect(shuffleDeck, { zoneId: "draw" }),
30
- fx.transition("deal"),
31
- ]);
32
- },
33
- }),
34
- },
35
- });
36
- ```
37
-
38
- After the runtime shuffles the zone, later reducer code can deal deterministically from the top with `ops.dealCardsToPlayerZone`.
39
-
40
- Use `shufflePlayerZone` when the runtime should shuffle a single player's zone:
41
-
42
- ```ts
43
- const shuffleHand = defineEffect<GameContract>()({
44
- type: "shufflePlayerZone",
45
- id: "shuffle-hand",
46
- });
47
-
48
- fx.effect(shuffleHand, {
49
- zoneId: "hand",
50
- playerId: input.playerId,
51
- });
52
- ```
53
-
54
- ## Roll a die with a continuation
55
-
56
- Use `reduce` on an effect when the reducer needs the engine result.
57
-
58
- ```ts
59
- const rollProduction = defineEffect<GameContract>()({
60
- type: "rollDie",
61
- id: "roll-production",
62
- context: z.object({
63
- reason: z.string(),
64
- }),
65
- reduce({ state, input, accept, ops }) {
66
- return accept(
67
- pipe(
68
- state,
69
- ops.patchPublicState({
70
- lastRoll: input.response.value,
71
- lastRollReason: input.data.reason,
72
- }),
73
- ),
74
- );
75
- },
76
- });
77
- ```
78
-
79
- Dispatch it from a reducer callback:
80
-
81
- ```ts
82
- return accept(state, [
83
- fx.effect(rollProduction, {
84
- dieId: "production-die",
85
- context: { reason: "turn-start" },
86
- }),
87
- ]);
88
- ```
89
-
90
- The `context` is author data captured when the effect is queued. `input.response` is the engine result.
91
-
92
- ## Effects are not prompts
93
-
94
- Use effects for engine work:
95
-
96
- - `rollDie`
97
- - `shuffleSharedZone`
98
- - `shufflePlayerZone`
99
-
100
- Use [Interactions](./interactions.md) with `promptInput` for addressed player requests. Prompts are player submissions; effects are runtime instructions.
101
-
102
- ## Effects and rngInput
103
-
104
- `rngInput.d6(2)` is different from `defineEffect({ type: "rollDie" })`.
105
-
106
- | Primitive | Use when |
107
- | --- | --- |
108
- | `rngInput.*` | Randomness is sampled as part of one player interaction submit. |
109
- | `defineEffect` + `fx.effect` | Randomness or shuffling should be queued as a runtime instruction, often from an auto phase or with a continuation. |
110
-
111
- Prefer the simpler `rngInput.*` when the random value is only needed inside the same interaction's `reduce`.