@dreamboard-games/workspace-codegen 0.1.1 → 0.1.3

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.
package/dist/seeds.js CHANGED
@@ -10,9 +10,9 @@ import {
10
10
  createClientParamSchemasByPhase,
11
11
  } from "@dreamboard/app-sdk/reducer";
12
12
  import type {
13
- ClientParamsOfInteractionOfDefinition,
14
- DefaultedClientParamKeysOfInteractionOfDefinition,
15
- InteractionIdOfDefinition,
13
+ ClientParamsOfInteractionOfDefinition,
14
+ DefaultedClientParamKeysOfInteractionOfDefinition,
15
+ InteractionIdOfDefinition,
16
16
  InteractionIdOfDefinitionPhase,
17
17
  PhaseNamesOfDefinition,
18
18
  StageNamesOfDefinitionPhase,
@@ -20,35 +20,38 @@ import type {
20
20
  ViewOfDefinition,
21
21
  } from "@dreamboard/app-sdk/reducer";
22
22
  import {
23
- createDreamboardUI,
24
- createHexBoardView,
25
- InteractionField as InteractionFieldGeneric,
26
- InteractionForm as InteractionFormGeneric,
27
- useActivePlayers as useActivePlayersGeneric,
28
- useBoardInteractions as useBoardInteractionsGeneric,
29
- useGameView as useGameViewGeneric,
30
- useInteractionByKey as useInteractionByKeyGeneric,
31
- usePlayerTurnOrder as usePlayerTurnOrderGeneric,
32
- type BoardInteractionsContext,
33
- type BoardInteractionsOptions,
34
- type BoardSpaceIdOf,
35
- type ClientParamSchemaMap,
36
- type HexBoardView,
37
- type InteractionDescriptor,
38
- type InteractionFieldProps as InteractionFieldPropsGeneric,
39
- type InteractionFieldRenderMap,
40
- type InteractionFormProps as InteractionFormPropsGeneric,
41
- type InteractionHandle,
42
- type DreamboardUI,
43
- type UIContract,
44
- } from "@dreamboard/ui-sdk";
45
- import { createElement, useMemo, type ReactElement } from "react";
46
- import {
47
- literals,
48
- staticBoards,
49
- type CardId,
50
- type EdgeId,
23
+ createDreamboardUI,
24
+ createResourceCounter,
25
+ type BoardHexGridProps as BoardHexGridPropsGeneric,
26
+ type BoardHexViewProps as BoardHexViewPropsGeneric,
27
+ type BoardSpaceIdOf,
28
+ type ClientParamSchemaMap,
29
+ type InteractionDescriptor,
30
+ type InteractionHandle,
31
+ type InteractionRouteMap as InteractionRouteMapGeneric,
32
+ type InteractionSwitchProps as InteractionSwitchPropsGeneric,
33
+ type InteractionSwitchRenderState,
34
+ type DreamboardUI,
35
+ type ResourceCounterComponents,
36
+ type ResourceDisplayConfig,
37
+ type TypedGame,
38
+ type UIContract,
39
+ type UIRootProps,
40
+ type ZoneCardAtProps,
41
+ type ZoneCardRenderItem,
42
+ type ZoneListProps,
43
+ type ZonePileCardsProps,
44
+ } from "@dreamboard/ui-sdk";
45
+ import { createElement, type ReactElement, type ReactNode } from "react";
46
+ import {
47
+ literals,
48
+ staticBoards,
49
+ type CardId,
50
+ type CardProperties,
51
+ type CardType,
52
+ type EdgeId,
51
53
  type PlayerId,
54
+ type ResourceId,
52
55
  type SpaceId,
53
56
  type VertexId,
54
57
  type ZoneId as ManifestZoneId,
@@ -147,8 +150,7 @@ export type InteractionDescriptorFor<Key extends InteractionKey> =
147
150
 
148
151
  /**
149
152
  * Params shape for a phase-qualified interaction key. Drives strong typing
150
- * for \`useInteractionByKey\`'s draft,
151
- * \`submit\`, and \`setInput\`.
153
+ * for component-first interaction state, forms, and submits.
152
154
  */
153
155
  export type InteractionParamsOf<Key extends InteractionKey> =
154
156
  InteractionParams<PhaseOfInteractionKey<Key>, IdOfInteractionKey<Key>>;
@@ -171,6 +173,50 @@ type InteractionHandleDefaultedKeys<Key extends InteractionKey> = Extract<
171
173
  keyof InteractionParamsShape<Key> & string
172
174
  >;
173
175
 
176
+ type RequiredInteractionInputKeysOf<Key extends InteractionKey> = Exclude<
177
+ InteractionInputKeysOf<Key>,
178
+ InteractionHandleDefaultedKeys<Key>
179
+ >;
180
+
181
+ export type RequiredInteractionInputKey<Key extends InteractionKey> =
182
+ RequiredInteractionInputKeysOf<Key>;
183
+
184
+ export type ZeroInputInteractionKey = {
185
+ [K in InteractionKey]: InteractionInputKeysOf<K> extends never
186
+ ? K
187
+ : never;
188
+ }[InteractionKey];
189
+
190
+ export type InputInteractionKey = Exclude<
191
+ InteractionKey,
192
+ ZeroInputInteractionKey
193
+ >;
194
+
195
+ export type InteractionRouteRenderState<Key extends InteractionKey> =
196
+ Omit<InteractionSwitchRenderState<Key>, "descriptor"> & {
197
+ descriptor: InteractionDescriptorFor<Key>;
198
+ };
199
+
200
+ export type InteractionRouteMap = {
201
+ [K in InteractionKey]: (state: InteractionRouteRenderState<K>) => ReactNode;
202
+ };
203
+
204
+ type ExactInteractionRoutes<Routes extends InteractionRouteMap> = Routes &
205
+ Record<Exclude<keyof Routes, keyof InteractionRouteMap>, never>;
206
+
207
+ export function defineInteractionRoutes<const Routes extends InteractionRouteMap>(
208
+ routes: ExactInteractionRoutes<Routes>,
209
+ ): InteractionRouteMap {
210
+ return routes;
211
+ }
212
+
213
+ type InteractionSwitchProps = Omit<
214
+ InteractionSwitchPropsGeneric<InteractionKey>,
215
+ "routes"
216
+ > & {
217
+ routes: InteractionRouteMap;
218
+ };
219
+
174
220
  type InteractionInputKeyOf<Key extends InteractionKey> =
175
221
  Key extends InteractionKey ? InteractionInputKeysOf<Key> : never;
176
222
 
@@ -202,6 +248,10 @@ type UIPromptOptionRegistry = {
202
248
  [K in PromptOptionValue]: { value: K };
203
249
  };
204
250
 
251
+ type UIPlayerRegistry = {
252
+ [K in PlayerId & string]: { player: K };
253
+ };
254
+
205
255
  type UIZoneRegistry = {
206
256
  [K in ZoneId & string]: { zone: K };
207
257
  };
@@ -223,6 +273,7 @@ export const uiContract = {
223
273
  inputs: {} as UIInputRegistry,
224
274
  prompts: {} as UIPromptRegistry,
225
275
  promptOptions: {} as UIPromptOptionRegistry,
276
+ players: {} as UIPlayerRegistry,
226
277
  zones: {} as UIZoneRegistry,
227
278
  cards: {} as UICardRegistry,
228
279
  phases: {} as UIPhaseRegistry,
@@ -236,142 +287,6 @@ declare module "@dreamboard/ui-sdk" {
236
287
  }
237
288
  }
238
289
 
239
- type WorkspaceUI = DreamboardUI<typeof uiContract>;
240
-
241
- export const UI: WorkspaceUI = createDreamboardUI(uiContract);
242
- export const Interaction = UI.Interaction;
243
- export const Prompt = UI.Prompt;
244
- export const PromptInbox = UI.PromptInbox;
245
- export const PromptDialogHost = UI.PromptDialogHost;
246
- export const PlayerRoster: WorkspaceUI["PlayerRoster"] = UI.PlayerRoster;
247
- export const Phase = UI.Phase;
248
- export const Zone = UI.Zone;
249
- export const Board = UI.Board;
250
-
251
- export type InteractionFieldRenderers<Key extends InteractionKey> =
252
- InteractionFieldRenderMap<InteractionParamsShape<Key>>;
253
-
254
- export type InteractionFormProps<Key extends InteractionKey> = Omit<
255
- InteractionFormPropsGeneric<
256
- InteractionParamsShape<Key>,
257
- InteractionHandleDefaultedKeys<Key>
258
- >,
259
- "descriptor" | "handle"
260
- > & {
261
- descriptor: InteractionDescriptorFor<Key>;
262
- handle: InteractionHandle<
263
- InteractionParamsShape<Key>,
264
- InteractionHandleDefaultedKeys<Key>
265
- >;
266
- renderFields?: InteractionFieldRenderers<Key>;
267
- };
268
-
269
- export type InteractionFieldProps<
270
- Key extends InteractionKey,
271
- InputKey extends keyof InteractionParamsShape<Key> & string,
272
- > = Omit<
273
- InteractionFieldPropsGeneric<InteractionParamsShape<Key>, InputKey>,
274
- "descriptor" | "handle"
275
- > & {
276
- descriptor: InteractionDescriptorFor<Key>;
277
- handle: InteractionHandle<
278
- InteractionParamsShape<Key>,
279
- InteractionHandleDefaultedKeys<Key>
280
- >;
281
- };
282
-
283
- export const clientParamSchemasByPhase = createClientParamSchemasByPhase(
284
- game,
285
- ) as ClientParamSchemaMap;
286
-
287
- /**
288
- * Workspace-typed wrapper over \`@dreamboard/ui-sdk\`'s generic
289
- * \`useInteractionByKey\`. The \`key\` argument is constrained to the
290
- * generated {@link InteractionKey} union (typos are compile errors), and
291
- * the returned {@link InteractionHandle} is parameterised on
292
- * {@link InteractionParamsOf} so \`handle.draft\`, \`handle.submit\`, and
293
- * \`handle.setInput\` are statically typed against the interaction's
294
- * declared inputs.
295
- *
296
- * \`\`\`ts
297
- * const handle = useInteractionByKey("play.placeThingCard");
298
- * if (!handle) return null;
299
- * handle.setInput("cardId", card.id); // inferred as ThingsDeckCardId
300
- * await handle.submit(); // params typed from the reducer contract
301
- * \`\`\`
302
- */
303
- export function useInteractionByKey<Key extends InteractionKey>(
304
- key: Key | null | undefined,
305
- ): InteractionHandle<
306
- InteractionParamsShape<Key>,
307
- InteractionHandleDefaultedKeys<Key>
308
- > | null {
309
- return useInteractionByKeyGeneric<
310
- Key,
311
- InteractionParamsShape<Key>,
312
- InteractionHandleDefaultedKeys<Key>
313
- >(key);
314
- }
315
-
316
- export function InteractionForm<Key extends InteractionKey>(
317
- props: InteractionFormProps<Key>,
318
- ): ReactElement {
319
- return createElement(
320
- InteractionFormGeneric<
321
- InteractionParamsShape<Key>,
322
- InteractionHandleDefaultedKeys<Key>
323
- >,
324
- props,
325
- );
326
- }
327
-
328
- export function InteractionField<
329
- Key extends InteractionKey,
330
- InputKey extends keyof InteractionParamsShape<Key> & string,
331
- >(props: InteractionFieldProps<Key, InputKey>): ReactElement | null {
332
- return createElement(
333
- InteractionFieldGeneric<InteractionParamsShape<Key>, InputKey>,
334
- props,
335
- );
336
- }
337
-
338
- /**
339
- * Workspace-typed wrapper over \`@dreamboard/ui-sdk\`'s
340
- * \`useBoardInteractions\`. Returns a {@link BoardInteractionsContext}
341
- * narrowed to this workspace's board-interaction union for exhaustive
342
- * downstream logic.
343
- *
344
- * \`\`\`tsx
345
- * const board = useBoardInteractions();
346
- * <HexGrid interactiveVertices={board.targetLayers.vertex()} />
347
- * \`\`\`
348
- *
349
- * Reach for this whenever a screen keeps more than one board target
350
- * interaction live simultaneously and dispatch is target-driven instead of
351
- * armed-then-clicked. Ambiguous unarmed overlaps should be resolved in the UI
352
- * by arming an explicit interaction/input route.
353
- */
354
- export function useBoardInteractions(
355
- options?: BoardInteractionsOptions,
356
- ): BoardInteractionsContext<BoardInteractions> {
357
- return useBoardInteractionsGeneric<BoardInteractions>(options);
358
- }
359
-
360
- /** Workspace-typed active-player hook. */
361
- export function useActivePlayers(): readonly PlayerId[] {
362
- return useActivePlayersGeneric() as readonly PlayerId[];
363
- }
364
-
365
- /** Workspace-typed reducer-native player view hook. */
366
- export function useGameView(): GameView {
367
- return useGameViewGeneric() as GameView;
368
- }
369
-
370
- /** Workspace-typed player turn order hook. */
371
- export function usePlayerTurnOrder(): readonly PlayerId[] {
372
- return usePlayerTurnOrderGeneric() as readonly PlayerId[];
373
- }
374
-
375
290
  // -------------------------------------------------------------------------
376
291
  // Typed hex-board view adapter
377
292
  // -------------------------------------------------------------------------
@@ -390,49 +305,176 @@ export type HexBoardSpaceId<Id extends HexBoardId> = BoardSpaceIdOf<
390
305
  HexBoardTopology<Id>
391
306
  >;
392
307
 
393
- /**
394
- * Workspace-typed wrapper over \`@dreamboard/ui-sdk\`'s
395
- * \`createHexBoardView\`. Joins the static topology of the named hex
396
- * board with a per-space view overlay and returns a value ready for
397
- * \`<HexGrid board={...} />\`. Each rendered tile carries a \`view\`
398
- * field typed as the matched overlay row.
399
- *
400
- * Strict by construction: every static space must have exactly one
401
- * overlay; missing, duplicate, or unknown overlay ids throw.
402
- *
403
- * \`\`\`tsx
404
- * const island = useHexBoardView("island", { spaces: view.spaces });
405
- *
406
- * <HexGrid
407
- * board={island}
408
- * renderTile={(tile) => {
409
- * const terrain = tile.view.terrain;
410
- * // ...
411
- * }}
412
- * />
413
- * \`\`\`
414
- */
415
- export function useHexBoardView<
416
- const Id extends HexBoardId,
417
- const TSpaceView extends { id: HexBoardSpaceId<Id> },
418
- >(
419
- boardId: Id,
420
- options: { spaces: ReadonlyArray<TSpaceView> },
421
- ): HexBoardView<HexBoardTopology<Id>, TSpaceView> {
422
- const board = hexStaticBoards[boardId];
423
- const { spaces } = options;
424
- return useMemo(
425
- () => createHexBoardView<HexBoardTopology<Id>, TSpaceView>(board, { spaces }),
426
- [board, spaces],
427
- );
428
- }
308
+ export type HexBoardViewProps<
309
+ Id extends HexBoardId,
310
+ TSpaceView extends { id: HexBoardSpaceId<Id> },
311
+ > = Omit<BoardHexViewPropsGeneric<HexBoardTopology<Id>, TSpaceView>, "board"> & {
312
+ board: Id;
313
+ };
314
+
315
+ export type HexBoardGridProps<
316
+ Id extends HexBoardId,
317
+ TSpaceView extends { id: HexBoardSpaceId<Id> },
318
+ > = Omit<
319
+ BoardHexGridPropsGeneric<HexBoardTopology<Id>, TSpaceView>,
320
+ "board" | "interactions"
321
+ > & {
322
+ board: Id;
323
+ interactions?:
324
+ | "auto"
325
+ | false
326
+ | {
327
+ edge?: readonly InteractionKey[];
328
+ vertex?: readonly InteractionKey[];
329
+ space?: readonly InteractionKey[];
330
+ };
331
+ };
332
+
333
+ type WorkspaceBoard = Omit<DreamboardUI<typeof uiContract>["Board"], "HexView" | "HexGrid"> & {
334
+ HexView<
335
+ const Id extends HexBoardId,
336
+ const TSpaceView extends { id: HexBoardSpaceId<Id> },
337
+ >(props: HexBoardViewProps<Id, TSpaceView>): ReactElement;
338
+ HexGrid<
339
+ const Id extends HexBoardId,
340
+ const TSpaceView extends { id: HexBoardSpaceId<Id> },
341
+ >(props: HexBoardGridProps<Id, TSpaceView>): ReactElement;
342
+ };
343
+
344
+ type WorkspaceCardProperties = CardProperties extends Record<string, unknown>
345
+ ? CardProperties
346
+ : Record<string, unknown>;
347
+ type WorkspaceZoneId = [ZoneId] extends [never] ? string : ZoneId;
348
+ type WorkspaceCardId = [CardId] extends [never] ? string : CardId;
349
+ type WorkspaceCardType = [CardType] extends [never] ? string : CardType;
350
+
351
+ // ZoneCardRenderItem is a discriminated union (hidden: true | false).
352
+ // A bare Omit<..., "zone"> would collapse the union to its common keys and
353
+ // strip cardType / properties from the hydrated branch, defeating the
354
+ // discriminated-union narrowing the SDK contract promises. Distribute Omit
355
+ // across the union so each branch keeps its own shape.
356
+ type WorkspaceZoneCard = ZoneCardRenderItem<
357
+ WorkspaceCardId & string,
358
+ WorkspaceCardType & string,
359
+ WorkspaceCardProperties
360
+ > extends infer Item
361
+ ? Item extends { zone: string }
362
+ ? Omit<Item, "zone"> & { zone: WorkspaceZoneId }
363
+ : never
364
+ : never;
365
+
366
+ type WorkspaceZone = Omit<
367
+ DreamboardUI<typeof uiContract>["Zone"],
368
+ "CardAt" | "TopCard" | "List" | "PileCards"
369
+ > & {
370
+ CardAt(
371
+ props: Omit<ZoneCardAtProps<WorkspaceZoneId>, "zone" | "children"> & {
372
+ zone?: WorkspaceZoneId;
373
+ children?: ReactNode | ((card: WorkspaceZoneCard) => ReactNode);
374
+ },
375
+ ): ReactElement | null;
376
+ TopCard(
377
+ props: Omit<
378
+ ZoneCardAtProps<WorkspaceZoneId>,
379
+ "zone" | "index" | "children"
380
+ > & {
381
+ zone?: WorkspaceZoneId;
382
+ children?: ReactNode | ((card: WorkspaceZoneCard) => ReactNode);
383
+ },
384
+ ): ReactElement | null;
385
+ List(
386
+ props: Omit<ZoneListProps, "children"> & {
387
+ children?: ReactNode | ((card: WorkspaceZoneCard) => ReactNode);
388
+ },
389
+ ): ReactElement;
390
+ PileCards(
391
+ props: Omit<ZonePileCardsProps, "renderCard"> & {
392
+ renderCard: (card: WorkspaceZoneCard) => ReactNode;
393
+ },
394
+ ): ReactElement | null;
395
+ };
396
+
397
+ type WorkspaceUI = Omit<
398
+ DreamboardUI<typeof uiContract>,
399
+ "Root" | "Game" | "Interaction" | "Board" | "Zone"
400
+ > & {
401
+ Root(props: UIRootProps): ReactElement;
402
+ readonly Game: TypedGame<typeof uiContract, GameView, PlayerId, PhaseName>;
403
+ readonly Interaction: Omit<
404
+ DreamboardUI<typeof uiContract>["Interaction"],
405
+ "Switch"
406
+ > & {
407
+ Switch(props: InteractionSwitchProps): ReactElement;
408
+ };
409
+ readonly Board: WorkspaceBoard;
410
+ readonly Zone: WorkspaceZone;
411
+ readonly ResourceCounter: ResourceCounterComponents<ResourceId>;
412
+ };
413
+
414
+ const baseUI = createDreamboardUI(uiContract);
415
+
416
+ const resourcePresentationById = literals.resourcePresentationById as Partial<
417
+ Record<string, { label?: string; icon?: string }>
418
+ >;
419
+
420
+ const resourceDisplayConfig = literals.resourceIds.map((resource) => {
421
+ const resourceId = resource as ResourceId;
422
+ const presentation = resourcePresentationById[resource as string];
423
+ return {
424
+ type: resourceId,
425
+ label: presentation?.label ?? resource,
426
+ icon: presentation?.icon ?? resource,
427
+ };
428
+ }) satisfies readonly ResourceDisplayConfig<ResourceId>[];
429
+
430
+ const resourceCounter = createResourceCounter<ResourceId>(resourceDisplayConfig);
431
+
432
+ export const Board: WorkspaceUI["Board"] = {
433
+ ...baseUI.Board,
434
+ HexView<
435
+ const Id extends HexBoardId,
436
+ const TSpaceView extends { id: HexBoardSpaceId<Id> },
437
+ >({ board: boardId, ...props }: HexBoardViewProps<Id, TSpaceView>) {
438
+ return createElement(baseUI.Board.HexView<HexBoardTopology<Id>, TSpaceView>, {
439
+ ...props,
440
+ board: hexStaticBoards[boardId],
441
+ });
442
+ },
443
+ HexGrid<
444
+ const Id extends HexBoardId,
445
+ const TSpaceView extends { id: HexBoardSpaceId<Id> },
446
+ >({ board: boardId, ...props }: HexBoardGridProps<Id, TSpaceView>) {
447
+ return createElement(baseUI.Board.HexGrid<HexBoardTopology<Id>, TSpaceView>, {
448
+ ...props,
449
+ board: hexStaticBoards[boardId],
450
+ });
451
+ },
452
+ } as WorkspaceUI["Board"];
453
+
454
+ export const UI: WorkspaceUI = {
455
+ ...baseUI,
456
+ Board,
457
+ ResourceCounter: resourceCounter,
458
+ };
459
+ export const Game: WorkspaceUI["Game"] = UI.Game;
460
+ export const Interaction = UI.Interaction;
461
+ export const Prompt = UI.Prompt;
462
+ export const PromptInbox = UI.PromptInbox;
463
+ export const PlayerRoster: WorkspaceUI["PlayerRoster"] = UI.PlayerRoster;
464
+ export const Dice: WorkspaceUI["Dice"] = UI.Dice;
465
+ export const Phase = UI.Phase;
466
+ export const Zone = UI.Zone;
467
+ export const ResourceCounter: WorkspaceUI["ResourceCounter"] = UI.ResourceCounter;
468
+
469
+ export const clientParamSchemasByPhase = createClientParamSchemasByPhase(
470
+ game,
471
+ ) as ClientParamSchemaMap;
429
472
  `;
430
473
  }
431
474
  function generateReducerSupportSeed() {
432
475
  return `import {
433
- createReducerOps,
476
+ createReducerEdit,
434
477
  createStateQueries,
435
- pipe,
436
478
  } from "@dreamboard/app-sdk/reducer";
437
479
  import type { GameState } from "./game-contract";
438
480
 
@@ -451,16 +493,12 @@ import type { GameState } from "./game-contract";
451
493
  *
452
494
  * Recommended authoring pattern inside a phase reducer:
453
495
  *
454
- * const next = pipe(
455
- * state,
456
- * ops.setActivePlayers([q.players.order()[0]]),
457
- * );
458
- * return accept(next);
496
+ * const tx = edit(state);
497
+ * tx.setActivePlayers([q.players.order()[0]]);
498
+ * return accept(tx.state);
459
499
  */
460
500
 
461
- export const ops = createReducerOps<GameState>();
462
-
463
- export { pipe };
501
+ export const edit = createReducerEdit<GameState>();
464
502
 
465
503
  export function stateQueries(state: GameState) {
466
504
  return createStateQueries(state);
@@ -541,7 +579,7 @@ function generateReducerGameContractSeed() {
541
579
  import { ids, manifestContract } from "../shared/manifest-contract";
542
580
  import { defineGameContract, type GameStateOf } from "@dreamboard/app-sdk/reducer";
543
581
 
544
- // Contract files should stay focused on schemas, phase names, and exported
582
+ // Contract files should stay focused on schemas, phases, and exported
545
583
  // state types. Put reducers in app/phases/* and pure computations in
546
584
  // app/derived.ts or app/rules/*.
547
585
  const publicStateSchema = z.object({
@@ -553,15 +591,14 @@ const hiddenStateSchema = z.object({});
553
591
 
554
592
  export const gameContract = defineGameContract({
555
593
  manifest: manifestContract,
556
- // Keep this list in sync with the keys of the \`phases\` record in ./phases.
557
- // Declaring phase names here narrows \`fx.transition(...)\`, \`phase.dispatch\`,
558
- // and the flow state's \`currentPhase\` to a literal union.
559
- phaseNames: ["setup"] as const,
560
594
  state: {
561
595
  public: publicStateSchema,
562
596
  private: privateStateSchema,
563
597
  hidden: hiddenStateSchema,
564
598
  },
599
+ phases: {
600
+ setup: z.object({}),
601
+ },
565
602
  });
566
603
 
567
604
  export type GameContract = typeof gameContract;
@@ -663,8 +700,10 @@ function generateAppFrameworkTsConfig() {
663
700
  moduleResolution: "bundler",
664
701
  strict: true,
665
702
  esModuleInterop: true,
666
- skipLibCheck: true,
703
+ skipLibCheck: false,
704
+ types: [],
667
705
  declaration: false,
706
+ rootDir: "..",
668
707
  outDir: "./dist",
669
708
  paths: {
670
709
  "@dreamboard/manifest-contract": ["../shared/manifest-contract.ts"],
@@ -682,48 +721,52 @@ function generateAppFrameworkTsConfig() {
682
721
  }, null, 2)}\n`;
683
722
  }
684
723
  function generateUiFrameworkTsConfig() {
685
- return `{
686
- "compilerOptions": {
687
- "target": "ES2020",
688
- "module": "ESNext",
689
- "moduleResolution": "bundler",
690
- "jsx": "react-jsx",
691
- "strict": true,
692
- "esModuleInterop": true,
693
- "skipLibCheck": true,
694
- "paths": {
695
- "@dreamboard/manifest-contract": [
696
- "../shared/manifest-contract.ts"
697
- ],
698
- "@dreamboard/ui-contract": [
699
- "../shared/generated/ui-contract.ts"
700
- ],
701
- "@shared/*": [
702
- "../shared/*"
703
- ]
704
- }
705
- },
706
- "include": [
707
- "./**/*.ts",
708
- "./**/*.tsx",
709
- "../shared/**/*.ts"
710
- ],
711
- "exclude": [
712
- "node_modules",
713
- "dist"
714
- ]
715
- }
716
- `;
724
+ return `${JSON.stringify({
725
+ compilerOptions: {
726
+ target: "ES2020",
727
+ module: "ESNext",
728
+ moduleResolution: "bundler",
729
+ jsx: "react-jsx",
730
+ strict: true,
731
+ esModuleInterop: true,
732
+ skipLibCheck: false,
733
+ types: [],
734
+ paths: {
735
+ "@dreamboard/manifest-contract": ["../shared/manifest-contract.ts"],
736
+ "@dreamboard/ui-contract": ["../shared/generated/ui-contract.ts"],
737
+ "@shared/*": ["../shared/*"],
738
+ },
739
+ },
740
+ include: ["./**/*.ts", "./**/*.tsx", "../shared/**/*.ts"],
741
+ exclude: ["node_modules", "dist"],
742
+ }, null, 2)}\n`;
717
743
  }
718
744
  function generateReducerUiAppContent() {
719
- return `import { Phase, PromptInbox } from "@dreamboard/ui-contract";
745
+ return `import { Game, Phase, Prompt, PromptInbox } from "@dreamboard/ui-contract";
720
746
 
721
747
  function SetupPhase() {
722
748
  return (
723
749
  <main>
724
750
  <PromptInbox.Root>
725
751
  <PromptInbox.Empty>No available prompts.</PromptInbox.Empty>
726
- <PromptInbox.Items />
752
+ <PromptInbox.Items>
753
+ {(prompt) => (
754
+ <Prompt.Root
755
+ key={prompt.interactionKey}
756
+ interaction={prompt.interactionKey}
757
+ >
758
+ <Prompt.Title />
759
+ <Prompt.Message />
760
+ <Prompt.Options>
761
+ {(option) => (
762
+ <Prompt.Option value={option.id}>
763
+ {option.label ?? option.id}
764
+ </Prompt.Option>
765
+ )}
766
+ </Prompt.Options>
767
+ </Prompt.Root>
768
+ )}
769
+ </PromptInbox.Items>
727
770
  </PromptInbox.Root>
728
771
  </main>
729
772
  );
@@ -731,11 +774,15 @@ function SetupPhase() {
731
774
 
732
775
  export default function App() {
733
776
  return (
734
- <Phase.Switch
735
- routes={{
736
- setup: () => <SetupPhase />,
737
- }}
738
- />
777
+ <Game.Root>
778
+ {() => (
779
+ <Phase.Switch
780
+ routes={{
781
+ setup: () => <SetupPhase />,
782
+ }}
783
+ />
784
+ )}
785
+ </Game.Root>
739
786
  );
740
787
  }
741
788
  `;