dreamboard 0.1.7 → 0.1.8

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 (92) hide show
  1. package/LICENSE +89 -0
  2. package/NOTICE +1 -0
  3. package/README.md +22 -178
  4. package/dist/{chunk-MP7IBNWW.js → chunk-BMYC6772.js} +577 -38
  5. package/dist/chunk-BMYC6772.js.map +1 -0
  6. package/dist/chunk-D4HDZEJT.js +37 -0
  7. package/dist/chunk-EM7NOFFC.js +19072 -0
  8. package/dist/chunk-EM7NOFFC.js.map +1 -0
  9. package/dist/chunk-SET56RVA.js +435 -0
  10. package/dist/chunk-SET56RVA.js.map +1 -0
  11. package/dist/{dist-B3R64F6G.js → dist-WJRJNFLI.js} +3 -3
  12. package/dist/global-config-VQJZ3JOA.js +15 -0
  13. package/dist/global-config-VQJZ3JOA.js.map +1 -0
  14. package/dist/index.js +23993 -24387
  15. package/dist/index.js.map +1 -1
  16. package/dist/keychain-backend-2DL3C6BS.js +125 -0
  17. package/dist/keychain-backend-2DL3C6BS.js.map +1 -0
  18. package/dist/{prompt-GMZABCJC.js → prompt-IV67HSPU.js} +2 -2
  19. package/dist/scaffold/assets/static/app/tsconfig.framework.json +24 -0
  20. package/dist/scaffold/assets/static/app/tsconfig.json +3 -0
  21. package/dist/scaffold/assets/static/ui/App.tsx +22 -0
  22. package/dist/scaffold/assets/static/ui/index.tsx +12 -0
  23. package/dist/scaffold/assets/static/ui/style.css +4 -0
  24. package/dist/scaffold/assets/static/ui/tsconfig.framework.json +31 -0
  25. package/dist/scaffold/assets/static/ui/tsconfig.json +5 -0
  26. package/dist/src-K2NK3A4P.js +626 -0
  27. package/dist/src-K2NK3A4P.js.map +1 -0
  28. package/package.json +41 -4
  29. package/skills/dreamboard/SKILL.md +126 -59
  30. package/skills/dreamboard/references/authoring-lifecycle.md +102 -0
  31. package/skills/dreamboard/references/board-surfaces.md +36 -0
  32. package/skills/dreamboard/references/board-topology.md +443 -0
  33. package/skills/dreamboard/references/boards-and-topology.md +100 -0
  34. package/skills/dreamboard/references/card-actions.md +107 -0
  35. package/skills/dreamboard/references/cli.md +120 -0
  36. package/skills/dreamboard/references/core-concepts.md +514 -0
  37. package/skills/dreamboard/references/custom-renderers.md +26 -0
  38. package/skills/dreamboard/references/derived-values.md +55 -0
  39. package/skills/dreamboard/references/effects.md +111 -0
  40. package/skills/dreamboard/references/game-contract.md +89 -0
  41. package/skills/dreamboard/references/game-definition.md +89 -0
  42. package/skills/dreamboard/references/game-shell.md +80 -0
  43. package/skills/dreamboard/references/hand-surfaces.md +33 -0
  44. package/skills/dreamboard/references/index.md +112 -0
  45. package/skills/dreamboard/references/inputs-and-targets.md +160 -0
  46. package/skills/dreamboard/references/interactions.md +158 -0
  47. package/skills/dreamboard/references/manifest-fields.md +137 -0
  48. package/skills/dreamboard/references/manifest.md +81 -0
  49. package/skills/dreamboard/references/package-surfaces.md +69 -0
  50. package/skills/dreamboard/references/phases.md +145 -0
  51. package/skills/dreamboard/references/prompts-and-choices.md +55 -0
  52. package/skills/dreamboard/references/quickstart.md +71 -0
  53. package/skills/dreamboard/references/rule-authoring.md +81 -77
  54. package/skills/dreamboard/references/setup-bootstrap.md +78 -0
  55. package/skills/dreamboard/references/stages-and-zones.md +59 -0
  56. package/skills/dreamboard/references/static-views.md +67 -0
  57. package/skills/dreamboard/references/table-queries-and-ops.md +103 -0
  58. package/skills/dreamboard/references/testing-bases.md +147 -0
  59. package/skills/dreamboard/references/testing-generated-contracts.md +142 -0
  60. package/skills/dreamboard/references/testing-runtime-assertions.md +124 -0
  61. package/skills/dreamboard/references/testing-scenarios.md +148 -0
  62. package/skills/dreamboard/references/testing-ui-tests.md +174 -0
  63. package/skills/dreamboard/references/testing.md +161 -0
  64. package/skills/dreamboard/references/ui-architecture.md +137 -0
  65. package/skills/dreamboard/references/ui-components.md +34 -0
  66. package/skills/dreamboard/references/views.md +72 -0
  67. package/skills/dreamboard/references/workspace-layout.md +136 -0
  68. package/dist/chunk-2H7UOFLK.js +0 -11
  69. package/dist/chunk-MP7IBNWW.js.map +0 -1
  70. package/dist/chunk-WM7TY7DV.js +0 -2328
  71. package/dist/chunk-WM7TY7DV.js.map +0 -1
  72. package/dist/embedded-harness-IHP7A2ZX.js +0 -344
  73. package/dist/embedded-harness-IHP7A2ZX.js.map +0 -1
  74. package/skills/dreamboard/references/adversarial-testing.md +0 -113
  75. package/skills/dreamboard/references/all-players-tracking.md +0 -75
  76. package/skills/dreamboard/references/api-reference.md +0 -197
  77. package/skills/dreamboard/references/app-best-practices.md +0 -98
  78. package/skills/dreamboard/references/building-your-first-game.md +0 -49
  79. package/skills/dreamboard/references/hands-vs-decks.md +0 -124
  80. package/skills/dreamboard/references/manifest-authoring.md +0 -328
  81. package/skills/dreamboard/references/phase-handlers.md +0 -134
  82. package/skills/dreamboard/references/scenario-format.md +0 -99
  83. package/skills/dreamboard/references/test-harness.md +0 -225
  84. package/skills/dreamboard/references/ui-best-practices.md +0 -156
  85. package/skills/dreamboard/references/ui-genre-resource-management.md +0 -187
  86. package/skills/dreamboard/references/ui-genre-trick-taking.md +0 -110
  87. package/skills/dreamboard/references/ui-genre-worker-placement.md +0 -143
  88. package/skills/dreamboard/references/ui-style-guide.md +0 -54
  89. package/skills/dreamboard/scripts/events-extract.mjs +0 -218
  90. /package/dist/{chunk-2H7UOFLK.js.map → chunk-D4HDZEJT.js.map} +0 -0
  91. /package/dist/{dist-B3R64F6G.js.map → dist-WJRJNFLI.js.map} +0 -0
  92. /package/dist/{prompt-GMZABCJC.js.map → prompt-IV67HSPU.js.map} +0 -0
@@ -0,0 +1,174 @@
1
+ # UI tests
2
+
3
+ Test workspace UI with reducer-native runtime providers.
4
+
5
+ Use `createTestRuntime` for reducer-backed, backend-free React tests that need real
6
+ Dreamboard runtime context. It creates an in-memory runtime from a generated
7
+ base state, drives the same reducer bundle as scenarios, and feeds snapshots
8
+ through the UI host runtime store.
9
+
10
+ This is for UI logic tests, not browser end-to-end tests.
11
+
12
+ ## Generate first
13
+
14
+ UI tests depend on generated base states:
15
+
16
+ ```bash
17
+ dreamboard test generate
18
+ ```
19
+
20
+ If the base state is missing or stale, regenerate before debugging React.
21
+
22
+ ## Basic harness
23
+
24
+ ```tsx
25
+ import assert from "node:assert/strict";
26
+ import { test } from "node:test";
27
+ import React from "react";
28
+ import { renderToStaticMarkup } from "react-dom/server";
29
+ import {
30
+ PluginStateProvider,
31
+ RuntimeProvider,
32
+ useGameView,
33
+ useIsMyTurn,
34
+ } from "@dreamboard/ui-sdk";
35
+ import { useInteractionByKey } from "../shared/generated/ui-contract";
36
+ import { createTestRuntime } from "../test/testing-types";
37
+
38
+ function Probe() {
39
+ const view = useGameView();
40
+ const placeThing = useInteractionByKey("placeThing.placeThingCard");
41
+ const isMyTurn = useIsMyTurn();
42
+
43
+ return (
44
+ <div
45
+ data-is-my-turn={isMyTurn ? "true" : "false"}
46
+ data-surface={placeThing?.descriptor.surface ?? "missing"}
47
+ data-hand-size={view.handCards.length}
48
+ />
49
+ );
50
+ }
51
+
52
+ function renderTree(runtime: ReturnType<typeof createTestRuntime>) {
53
+ return renderToStaticMarkup(
54
+ <RuntimeProvider runtime={runtime.runtime}>
55
+ <PluginStateProvider>
56
+ <Probe />
57
+ </PluginStateProvider>
58
+ </RuntimeProvider>,
59
+ );
60
+ }
61
+
62
+ test("renders the initial playable hand", () => {
63
+ const runtime = createTestRuntime({
64
+ baseId: "initial-turn",
65
+ phase: "placeThing",
66
+ controllingPlayerId: "player-2",
67
+ });
68
+
69
+ const markup = renderTree(runtime);
70
+
71
+ assert.match(markup, /data-is-my-turn="true"/);
72
+ assert.match(markup, /data-surface="hand"/);
73
+ assert.match(markup, /data-hand-size="5"/);
74
+ });
75
+ ```
76
+
77
+ The important providers are:
78
+
79
+ | Provider | Purpose |
80
+ | --- | --- |
81
+ | `RuntimeProvider` | Supplies the in-memory `RuntimeAPI`. |
82
+ | `PluginStateProvider` | Supplies reducer-projected plugin state to UI hooks. |
83
+
84
+ ## Runtime options
85
+
86
+ ```ts
87
+ const runtime = createTestRuntime({
88
+ baseId: "initial-turn",
89
+ phase: "placeThing",
90
+ controllingPlayerId: "player-2",
91
+ userId: "test-user",
92
+ });
93
+ ```
94
+
95
+ Options:
96
+
97
+ | Option | Meaning |
98
+ | --- | --- |
99
+ | `baseId` | Generated base state id. Required. |
100
+ | `phase` | Optional expected phase for the initial snapshot. |
101
+ | `controllingPlayerId` | Seat controlled by the rendered UI. |
102
+ | `userId` | Test user id; defaults to `"test-user"`. |
103
+
104
+ `baseId` is typed to generated `BASE_STATES`, so misspelled bases fail at
105
+ compile time.
106
+
107
+ ## Submitting interactions
108
+
109
+ `createTestRuntime` returns helpers for driving state:
110
+
111
+ ```tsx
112
+ const runtime = createTestRuntime({
113
+ baseId: "initial-turn",
114
+ phase: "placeThing",
115
+ controllingPlayerId: "player-2",
116
+ });
117
+
118
+ await runtime.submit(runtime.seat(1), "placeThingCard", {
119
+ cardId: "a-diamond",
120
+ ringId: "ring-1",
121
+ });
122
+
123
+ const after = renderTree(runtime);
124
+ assert.match(after, /data-hand-size="4"/);
125
+ ```
126
+
127
+ Submission validates input, dispatches through the reducer bundle, applies a
128
+ fresh gameplay snapshot, and notifies UI subscribers.
129
+
130
+ ## Switching seats
131
+
132
+ Use `setControllingPlayer` to test player-specific views:
133
+
134
+ ```tsx
135
+ runtime.setControllingPlayer("player-3");
136
+
137
+ const markup = renderTree(runtime);
138
+ assert.match(markup, /data-is-my-turn="false"/);
139
+ ```
140
+
141
+ This exercises the same session-store path used by host player switching.
142
+
143
+ ## What to test here
144
+
145
+ Good UI runtime tests:
146
+
147
+ - hooks see the expected phase, view, and available interactions
148
+ - `useIsMyTurn` and controlling-player changes behave correctly
149
+ - custom primitive renderers receive descriptors
150
+ - card hands update after reducer submissions
151
+ - prompt or blocker UI appears for the addressed seat only
152
+
153
+ Avoid testing browser-only layout here. Use the local dev host or browser runner
154
+ for screenshots, pointer events, and CSS layout issues.
155
+
156
+ ## Projection fixture tests
157
+
158
+ For lighter UI probes, read generated base projection JSON and provide a small
159
+ mock `RuntimeAPI`. This is useful when the test only needs hooks and render
160
+ behavior, not reducer dispatch.
161
+
162
+ Star Settlers uses this pattern in `test/ui/app.test.tsx`: it loads
163
+ `test/generated/bases/<base>/player-*.projection.json`, wraps probes in
164
+ `RuntimeProvider` and `PluginStateProvider`, then asserts that typed hooks such
165
+ as `useGameView`, `useInteractionByKey`, and `useBoardInteractions` see the
166
+ expected projected state.
167
+
168
+ ## TSX runtime config
169
+
170
+ Reducer-native UI tests that import React components from workspace or SDK
171
+ sources may need a TSX-specific test tsconfig that enables the React JSX
172
+ transform for those sources. Keep that config separate from the normal
173
+ `test/tsconfig.json`; broadening the normal typecheck include can pull SDK
174
+ fixtures into `tsc --noEmit`.
@@ -0,0 +1,161 @@
1
+ # Testing overview
2
+
3
+ Generate and run Dreamboard reducer-native tests.
4
+
5
+ Dreamboard tests run authored scenarios against the
6
+ same reducer contract that powers the game, using generated workspace types for
7
+ player ids, interaction ids, params, phases, views, and assertion helpers.
8
+
9
+ Use tests for repeatable gameplay coverage:
10
+
11
+ - opening-state invariants
12
+ - setup checkpoints reused by many scenarios
13
+ - happy paths across a turn, round, or full game
14
+ - rejection paths for illegal actors or params
15
+ - prompt recipient and availability checks
16
+ - UI component probes against reducer-projected plugin state
17
+
18
+ ## Workspace layout
19
+
20
+ | Path | Ownership | Purpose |
21
+ | --- | --- | --- |
22
+ | `test/bases/*.base.ts` | Authored | Reusable seeded starting states via `defineBase`. |
23
+ | `test/scenarios/*.scenario.ts` | Authored | Reducer-native scenario files via `defineScenario`. |
24
+ | `test/ui/*.test.tsx` | Authored | Optional UI tests using `createTestRuntime`. |
25
+ | `test/testing-types.ts` | Generated | Workspace-narrowed test import surface. |
26
+ | `test/generated/*` | Generated | Base snapshots, testing contract, scenario manifest, projections, snapshots, and metadata. |
27
+
28
+ Import test helpers from `../testing-types`. Do not import directly from
29
+ `test/generated/*` and do not edit generated files by hand.
30
+
31
+ ## Command flow
32
+
33
+ Generate or refresh reducer-native artifacts:
34
+
35
+ ```bash
36
+ dreamboard test generate
37
+ ```
38
+
39
+ Run all reducer scenarios:
40
+
41
+ ```bash
42
+ dreamboard test run
43
+ ```
44
+
45
+ Run one scenario file:
46
+
47
+ ```bash
48
+ dreamboard test run --scenario test/scenarios/trade-full-lifecycle.scenario.ts
49
+ ```
50
+
51
+ Refresh scenario snapshots while running:
52
+
53
+ ```bash
54
+ dreamboard test run --update-snapshots
55
+ ```
56
+
57
+ Public workspaces should normally omit `--runner`. The default runner is the
58
+ reducer runner. Internal source-checkout lanes may also use `embedded` or
59
+ `browser`, but those are parity lanes rather than the public authoring loop.
60
+
61
+ Example workspaces may also expose package-local checks. Star Settlers, for
62
+ example, uses `pnpm typecheck:test` for test TypeScript and `pnpm test:ui` for
63
+ React UI probes under `test/ui`.
64
+
65
+ ## What generate does
66
+
67
+ `dreamboard test generate`:
68
+
69
+ 1. Ensures `test/testing-types.ts` and `test/generated/testing-contract.ts`
70
+ exist and match the current workspace contract.
71
+ 2. Loads `test/bases/*.base.ts` and `test/scenarios/*.scenario.ts`.
72
+ 3. Validates that every scenario references an existing base.
73
+ 4. Executes base setup against the reducer to produce deterministic base
74
+ snapshots.
75
+ 5. Writes generated artifacts under `test/generated/`.
76
+
77
+ Regenerate after changes that affect runtime or type shape:
78
+
79
+ - `manifest.ts`
80
+ - `app/game.ts`
81
+ - phase files, interactions, card actions, targets, or views
82
+ - setup profiles
83
+ - generated `shared/generated/ui-contract.ts`
84
+ - stale base fingerprints reported by `dreamboard test run`
85
+
86
+ ## What run does
87
+
88
+ `dreamboard test run`:
89
+
90
+ 1. Ensures generated testing files exist.
91
+ 2. Loads bases, scenarios, generated base states, manifest, and game reducer.
92
+ 3. Checks generated base fingerprints against the current workspace.
93
+ 4. Starts each scenario from its base.
94
+ 5. Runs `when`.
95
+ 6. Checks optional `phase` and `stage`.
96
+ 7. Runs `then`.
97
+ 8. Prints one pass or fail line per scenario plus a summary.
98
+
99
+ If the base artifact is stale, the runner fails before pretending the scenario
100
+ is meaningful. Regenerate instead of debugging against an old snapshot.
101
+
102
+ ## Minimal base and scenario
103
+
104
+ ```ts
105
+ // test/bases/initial-turn.base.ts
106
+ import { defineBase } from "../testing-types";
107
+
108
+ export default defineBase({
109
+ id: "initial-turn",
110
+ seed: 1337,
111
+ players: 4,
112
+ setup: async () => undefined,
113
+ });
114
+ ```
115
+
116
+ ```ts
117
+ // test/scenarios/smoke-initial-turn.scenario.ts
118
+ import { defineScenario } from "../testing-types";
119
+
120
+ export default defineScenario({
121
+ id: "smoke-initial-turn",
122
+ from: "initial-turn",
123
+ phase: "setup",
124
+ when: async () => undefined,
125
+ then: ({ state, interactions, expect, seat }) => {
126
+ expect(state()).toBe("setup");
127
+ expect(interactions(seat(0))).toHaveInteraction("placeSetupMarker");
128
+ },
129
+ });
130
+ ```
131
+
132
+ Use `seat(0)`, `seat(1)`, and `players()` instead of hard-coded `"player-1"`
133
+ strings. This keeps tests portable across player counts and generated player id
134
+ unions.
135
+
136
+ ## Recommended coverage
137
+
138
+ Every game should have:
139
+
140
+ - one opening-state scenario
141
+ - one normal turn or round scenario
142
+ - one scenario that crosses a phase boundary
143
+ - one rejection scenario for out-of-turn or invalid params
144
+ - one prompt recipient scenario if the game uses prompts
145
+ - one setup-profile or setup-completion scenario when setup is non-trivial
146
+
147
+ For larger games, create bases for expensive checkpoints such as post-setup,
148
+ mid-game economy, pending trade, pending combat, or near-endgame. Do not make
149
+ one scenario replay a full game just to test a late rule.
150
+
151
+ ## Split pages
152
+
153
+ - [Bases](./testing-bases.md) explains seeded checkpoints and inheritance.
154
+ - [Scenarios](./testing-scenarios.md) explains `defineScenario` and scenario
155
+ flow.
156
+ - [Runtime assertions](./testing-runtime-assertions.md) lists context helpers
157
+ and matchers.
158
+ - [UI tests](./testing-ui-tests.md) shows `createTestRuntime` with React
159
+ providers.
160
+ - [Generated testing contracts](./testing-generated-contracts.md) explains
161
+ generated files and stale-artifact failures.
@@ -0,0 +1,137 @@
1
+ # UI architecture
2
+
3
+ Understand the generated UI contract, UI SDK, and host runtime boundaries.
4
+
5
+ Dreamboard UI is reducer-driven and component-first. The reducer projects the
6
+ current player's view and available interactions; React composes generated
7
+ primitives, package defaults, and local visuals around those authoritative
8
+ descriptors.
9
+
10
+ Do not reimplement legality in React. UI code may choose layout, copy,
11
+ highlights, and controls, but availability, eligible targets, prompt options,
12
+ and submitted param shapes should come from authored reducer contracts and
13
+ generated types.
14
+
15
+ ## Layers
16
+
17
+ | Layer | Import path | Owns |
18
+ | --- | --- | --- |
19
+ | Workspace UI contract | `@dreamboard/ui-contract` | Typed `Interaction`, `Prompt`, `PromptInbox`, `Zone`, `Board`, generated hooks, interaction keys, input keys, zone ids, card ids, phase names, and board targets. |
20
+ | UI SDK | `@dreamboard/ui-sdk` | Runtime providers, generic hooks, visual primitives such as `Card`, `HexGrid`, `ResourceCounter`, and advanced escape-hatch hooks. |
21
+ | UI defaults | `@dreamboard/ui-sdk/defaults` | Styled wrappers over primitives such as `DefaultPromptInbox`, `DefaultInteractionList`, `DefaultZone`, and `GameLayout`. |
22
+ | Host runtime | Provided by the dev/play host | Session state, state-sync snapshots, interaction submission, validation RPCs, player switching, history restore, and host-only feedback. |
23
+
24
+ Most authored UI imports the generated contract first:
25
+
26
+ ```tsx
27
+ import {
28
+ Interaction,
29
+ PromptInbox,
30
+ Zone,
31
+ useBoardInteractions,
32
+ useGameView,
33
+ useInteractionByKey,
34
+ } from "@dreamboard/ui-contract";
35
+ ```
36
+
37
+ Use `@dreamboard/ui-sdk` for framework primitives that are not
38
+ workspace-specific:
39
+
40
+ ```tsx
41
+ import { Card, HexGrid, ResourceCounter, ToastProvider } from "@dreamboard/ui-sdk";
42
+ ```
43
+
44
+ `@dreamboard/ui-contract` is a generated workspace alias backed by
45
+ `shared/generated/ui-contract.ts`, not a standalone npm package. The generated
46
+ file augments `@dreamboard/ui-sdk`'s `DreamboardUIRegister`, calls
47
+ `createDreamboardUI(uiContract)`, and exports typed primitives such as
48
+ `Interaction`, `Prompt`, `PromptInbox`, `PromptDialogHost`, `PlayerRoster`,
49
+ `Phase`, `Zone`, and `Board`.
50
+
51
+ ## Data flow
52
+
53
+ 1. Authoring files declare manifest objects, phases, interactions, inputs,
54
+ targets, card actions, and views.
55
+ 2. `dreamboard sync` generates workspace contracts, including
56
+ `@dreamboard/ui-contract`.
57
+ 3. The trusted bundle evaluates the current seat and projects descriptors into
58
+ plugin state.
59
+ 4. UI primitives read those descriptors and expose state through props,
60
+ contexts, `data-*` attributes, and typed handles.
61
+ 5. UI submits through `Interaction.Submit`, `Prompt.Option`,
62
+ `InteractionHandle`, or generated board target helpers.
63
+
64
+ The descriptor is a behavior contract, not a presentation DSL. It carries ids,
65
+ availability, unavailable reasons, input domains, target eligibility, prompt
66
+ options, zone metadata, and phase metadata. Visual policy belongs in React/CSS
67
+ or default wrappers.
68
+
69
+ ## Primitive scaffold
70
+
71
+ ```tsx
72
+ import { PromptInbox } from "@dreamboard/ui-contract";
73
+
74
+ export default function App() {
75
+ return (
76
+ <main>
77
+ <PromptInbox.Root>
78
+ <PromptInbox.Empty>No available prompts.</PromptInbox.Empty>
79
+ <PromptInbox.Items />
80
+ </PromptInbox.Root>
81
+ </main>
82
+ );
83
+ }
84
+ ```
85
+
86
+ Use the same pattern for actions and zones:
87
+
88
+ ```tsx
89
+ <Interaction.Root interaction="playerTurn.buyCard">
90
+ <Interaction.Label />
91
+ <Interaction.Submit>Buy</Interaction.Submit>
92
+ </Interaction.Root>
93
+
94
+ <Zone.Root zone="hand">
95
+ <Zone.List>
96
+ {view.hand.map((card) => (
97
+ <Zone.Item key={card.id} card={card.id}>
98
+ <Interaction.CardInput input="cardId" card={card.id}>
99
+ <Card card={card} />
100
+ </Interaction.CardInput>
101
+ </Zone.Item>
102
+ ))}
103
+ </Zone.List>
104
+ </Zone.Root>
105
+ ```
106
+
107
+ ## Custom board escape hatch
108
+
109
+ SVG or canvas-heavy boards can use generated `useBoardInteractions()` while the
110
+ reducer still owns eligibility:
111
+
112
+ ```tsx
113
+ const board = useBoardInteractions();
114
+
115
+ <HexGrid
116
+ board={view.board}
117
+ interactiveVertices={board.targetLayers.vertex()}
118
+ interactiveEdges={board.targetLayers.edge()}
119
+ renderTile={(tile, geometry) => <polygon points={geometry.points()} />}
120
+ renderEdge={(edge) => <line />}
121
+ renderVertex={(vertex) => <circle /> /* sketch only */}
122
+ />;
123
+ ```
124
+
125
+ ## Boundaries
126
+
127
+ | Put it here | Examples |
128
+ | --- | --- |
129
+ | Reducer authoring | Turn order, actor rules, prompt recipients, target eligibility, costs, validation, state transitions. |
130
+ | Generated UI contract | Interaction keys, input names, prompt options, phase-qualified param types, zones, card ids, board target ids. |
131
+ | Authored React UI | Layout, styling, board geometry, card faces, component composition, selected draft inputs. |
132
+ | Host runtime | Player switching, session chrome, state-sync, submission transport, host-only notifications. |
133
+
134
+ Do not mirror host-only concepts into authored reducer state just to make UI
135
+ chrome easier. Do not copy target predicates into React just to enable or
136
+ disable a board element. If React needs a highlight, expose it through a target
137
+ collector or derived view.
@@ -0,0 +1,34 @@
1
+ # UI components
2
+
3
+ Generic presentational components available from @dreamboard/ui-sdk.
4
+
5
+ Dreamboard ships generic presentational components in `@dreamboard/ui-sdk`.
6
+ They are not game-specific; pair them with generated primitives from
7
+ `@dreamboard/ui-contract` when behavior needs typed interactions.
8
+
9
+ | Component | Use |
10
+ | --- | --- |
11
+ | `Card` | Render a card face from projected view data. |
12
+ | `Hand` | Presentational card row/fan helper. |
13
+ | `HexGrid` / `SquareGrid` | Board geometry renderers. |
14
+ | `ResourceCounter` / `CostDisplay` | Resource and cost display. |
15
+ | `DiceRoller` | Dice display plus optional roll action handle. |
16
+ | `PlayerRoster` | Generated player roster primitive for player chrome. |
17
+ | `GameEndDisplay` | End-of-game summary. |
18
+ | `ToastProvider` | Local toast context for UI feedback. |
19
+
20
+ Behavior comes from generated primitives:
21
+
22
+ ```tsx
23
+ import { Interaction, Zone } from "@dreamboard/ui-contract";
24
+ import { Card } from "@dreamboard/ui-sdk";
25
+
26
+ <Zone.Item card={card.id}>
27
+ <Interaction.CardInput input="cardId" card={card.id}>
28
+ <Card card={card} />
29
+ </Interaction.CardInput>
30
+ </Zone.Item>
31
+ ```
32
+
33
+ Presentational components should render projected view data or interaction
34
+ handles. They should not inspect reducer internals or recompute legality.
@@ -0,0 +1,72 @@
1
+ # Views
2
+
3
+ Use defineView to project dynamic player-facing state for UI and clients.
4
+
5
+ Views are the dynamic player-facing projection boundary. The UI reads a view; it should not inspect raw reducer state.
6
+
7
+ ```ts
8
+ export const playerView = defineView<GameContract>()({
9
+ project({ state, playerId, q }) {
10
+ const scores = state.publicState.scores;
11
+ const handCards = q.zone.playerCards(playerId, "things-hand").map((cardId) => {
12
+ const card = q.card.get(cardId);
13
+ return {
14
+ id: cardId,
15
+ label: card.name ?? cardId,
16
+ };
17
+ });
18
+
19
+ return {
20
+ phase: state.flow.currentPhase,
21
+ myScore: scores[playerId] ?? 0,
22
+ scores,
23
+ handCards,
24
+ winnerPlayerId: state.publicState.winnerPlayerId,
25
+ };
26
+ },
27
+ });
28
+ ```
29
+
30
+ Register views in `defineGame`:
31
+
32
+ ```ts
33
+ export default defineGame({
34
+ contract: gameContract,
35
+ phases,
36
+ views: {
37
+ player: playerView,
38
+ },
39
+ });
40
+ ```
41
+
42
+ The generated UI contract exposes the projected type as `GameView` and `InferView<"player">`.
43
+
44
+ ## What a view can read
45
+
46
+ `project` receives:
47
+
48
+ - `state`, including public/private/hidden/phase/table data as appropriate for the runtime projection
49
+ - `playerId`
50
+ - `q`, the typed table query namespace
51
+ - `derived`, for memoized derived values
52
+ - runtime context such as current phase, player order, active players, and setup
53
+
54
+ Use `q` for table facts such as cards in a hand, board occupants, resource balances, and component locations.
55
+
56
+ ## What belongs in a view
57
+
58
+ Views should shape data for UI consumption:
59
+
60
+ - labels for cards or pieces
61
+ - counts and summaries
62
+ - player-specific hand information
63
+ - current phase and step display data
64
+ - safe public slices of score and winner state
65
+
66
+ Views should not run rule validation or decide legal moves. The runtime projects interactions and eligible targets from reducer definitions.
67
+
68
+ ## Static vs dynamic
69
+
70
+ Use `defineView` for data that can change during a session. Use [Static views](./static-views.md) for manifest-derived payloads that do not change after session initialization.
71
+
72
+ Avoid putting large static topology in every dynamic view update.