@dreamboard-games/sdk 0.4.0-alpha.1 → 0.4.0-alpha.2

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 (78) hide show
  1. package/REFERENCE.md +1215 -125
  2. package/dist/{ResourceCounter-BTXklCEZ.d.ts → ResourceCounter-BuiNstXs.d.ts} +5 -1
  3. package/dist/{ThemeProvider-DvMl17Ny.d.ts → ThemeProvider-B1sIoBlX.d.ts} +1 -1
  4. package/dist/{attributes-BrZtP9az.d.ts → attributes-DbvyMbXw.d.ts} +17 -2
  5. package/dist/authoring/index.d.ts +66 -0
  6. package/dist/authoring/index.js +560 -0
  7. package/dist/authoring/index.js.map +1 -0
  8. package/dist/browser-interaction.d.ts +205 -5
  9. package/dist/browser-interaction.js +14 -2
  10. package/dist/{chunk-7W6TQO3X.js → chunk-45FI6XII.js} +613 -366
  11. package/dist/chunk-45FI6XII.js.map +1 -0
  12. package/dist/{chunk-3K362ZKR.js → chunk-6JQFGSLD.js} +202 -38
  13. package/dist/chunk-6JQFGSLD.js.map +1 -0
  14. package/dist/{chunk-XXBSIPJX.js → chunk-CP35NPPY.js} +131 -59
  15. package/dist/chunk-CP35NPPY.js.map +1 -0
  16. package/dist/{chunk-XJYUSD5F.js → chunk-FWVV2X74.js} +2 -2
  17. package/dist/{chunk-IUGKW2GS.js → chunk-HWJS3VOJ.js} +6 -2
  18. package/dist/chunk-HWJS3VOJ.js.map +1 -0
  19. package/dist/chunk-K5ZRJTZZ.js +328 -0
  20. package/dist/chunk-K5ZRJTZZ.js.map +1 -0
  21. package/dist/{chunk-7LZZEI25.js → chunk-N6ZDIMRB.js} +1 -1
  22. package/dist/chunk-N6ZDIMRB.js.map +1 -0
  23. package/dist/chunk-O6M6PN4E.js +1225 -0
  24. package/dist/chunk-O6M6PN4E.js.map +1 -0
  25. package/dist/{chunk-AEUXCFGM.js → chunk-PF7L4BMG.js} +1 -8
  26. package/dist/{chunk-AEUXCFGM.js.map → chunk-PF7L4BMG.js.map} +1 -1
  27. package/dist/{chunk-CZMFAS3E.js → chunk-WOWFONLZ.js} +517 -1274
  28. package/dist/chunk-WOWFONLZ.js.map +1 -0
  29. package/dist/{chunk-MALRKLRR.js → chunk-WUIVY2KE.js} +301 -12
  30. package/dist/chunk-WUIVY2KE.js.map +1 -0
  31. package/dist/chunk-ZAS2SENW.js +12 -0
  32. package/dist/chunk-ZAS2SENW.js.map +1 -0
  33. package/dist/codegen.d.ts +11 -3
  34. package/dist/codegen.js +1 -1
  35. package/dist/{components-Dqr_LuZJ.d.ts → components-BbWPnZCF.d.ts} +37 -6
  36. package/dist/{hex-board-view-UCUjMALd.d.ts → hex-board-view-Kz9Q_zsv.d.ts} +1 -1
  37. package/dist/index.js +2 -1
  38. package/dist/package-set.d.ts +2 -2
  39. package/dist/package-set.js +2 -1
  40. package/dist/player-state-Cqpyeql0.d.ts +371 -0
  41. package/dist/protocol-BP193lva.d.ts +267 -0
  42. package/dist/reducer/advanced.js +1 -1
  43. package/dist/reducer-contract.js +1 -1
  44. package/dist/reducer.js +2 -2
  45. package/dist/reducer.js.map +1 -1
  46. package/dist/runtime/primitives.d.ts +10 -10
  47. package/dist/runtime/primitives.js +9 -7
  48. package/dist/runtime/runtime-api.d.ts +2 -2
  49. package/dist/runtime/workspace-contract.d.ts +23 -12
  50. package/dist/runtime/workspace-contract.js +7 -6
  51. package/dist/runtime-api-BJharVaj.d.ts +198 -0
  52. package/dist/runtime-api-C6aWo9c0.d.ts +95 -0
  53. package/dist/runtime.d.ts +35 -31
  54. package/dist/runtime.js +87 -797
  55. package/dist/runtime.js.map +1 -1
  56. package/dist/testing.d.ts +254 -140
  57. package/dist/testing.js +964 -149
  58. package/dist/testing.js.map +1 -1
  59. package/dist/types-CYeN0wLK.d.ts +29 -0
  60. package/dist/{types-BqzHkNMA.d.ts → types-DJj5MJkl.d.ts} +32 -5
  61. package/dist/types.d.ts +13 -8
  62. package/dist/ui/components.d.ts +4 -5
  63. package/dist/ui/components.js +19 -1
  64. package/dist/ui/player-state.d.ts +2 -365
  65. package/dist/{ui-contract-W2-kxObe.d.ts → ui-contract-CY7cxsCC.d.ts} +16 -26
  66. package/dist/ui.d.ts +7 -8
  67. package/dist/ui.js +20 -2
  68. package/package.json +25 -19
  69. package/dist/chunk-3K362ZKR.js.map +0 -1
  70. package/dist/chunk-7LZZEI25.js.map +0 -1
  71. package/dist/chunk-7W6TQO3X.js.map +0 -1
  72. package/dist/chunk-CZMFAS3E.js.map +0 -1
  73. package/dist/chunk-IUGKW2GS.js.map +0 -1
  74. package/dist/chunk-MALRKLRR.js.map +0 -1
  75. package/dist/chunk-XXBSIPJX.js.map +0 -1
  76. package/dist/hex-color-MhOyuY-o.d.ts +0 -8
  77. package/dist/runtime-api-BXd70c2e.d.ts +0 -490
  78. /package/dist/{chunk-XJYUSD5F.js.map → chunk-FWVV2X74.js.map} +0 -0
@@ -13,7 +13,7 @@ function createStringPropertySchema(description) {
13
13
  description
14
14
  };
15
15
  }
16
- function createStandard52CardDeck() {
16
+ function createStandard52CardDeck(defaultHome = { type: "detached" }) {
17
17
  const suits = ["SPADES", "HEARTS", "CLUBS", "DIAMONDS"];
18
18
  const ranks = [
19
19
  "3",
@@ -51,6 +51,7 @@ function createStandard52CardDeck() {
51
51
  id: STANDARD_DECK_ID,
52
52
  name: "Standard 52-Card Deck",
53
53
  type: "manual",
54
+ defaultHome,
54
55
  cards,
55
56
  cardSchema: {
56
57
  properties: {
@@ -68,7 +69,7 @@ function addStandardDecksIfNeeded(manifest) {
68
69
  ...manifest,
69
70
  cardSets: manifest.cardSets.map(
70
71
  (cardSet) => cardSet.type === "preset" && cardSet.presetId === STANDARD_DECK_ID ? {
71
- ...createStandard52CardDeck(),
72
+ ...createStandard52CardDeck(cardSet.defaultHome),
72
73
  id: cardSet.id,
73
74
  name: cardSet.name
74
75
  } : cardSet
@@ -79,7 +80,7 @@ function materializePresetCardSet(presetCardSet) {
79
80
  if (presetCardSet.presetId !== STANDARD_DECK_ID) {
80
81
  throw new Error(`Unsupported preset deck: ${presetCardSet.presetId}`);
81
82
  }
82
- const standardDeck = createStandard52CardDeck();
83
+ const standardDeck = createStandard52CardDeck(presetCardSet.defaultHome);
83
84
  return {
84
85
  ...standardDeck,
85
86
  id: presetCardSet.id,
@@ -434,6 +435,10 @@ function validateSlotHostsAndHomes(manifest) {
434
435
  if (cardSet.type !== "manual") {
435
436
  continue;
436
437
  }
438
+ validateHome(
439
+ cardSet.defaultHome,
440
+ `manifest.cardSets[${cardSetIndex}].defaultHome`
441
+ );
437
442
  for (const [cardIndex, card] of cardSet.cards.entries()) {
438
443
  validateHome(
439
444
  card.home,
@@ -526,21 +531,32 @@ function validateCardHomes(manifest) {
526
531
  (manifest.zones ?? []).map((zone) => [zone.id, zone.scope])
527
532
  );
528
533
  for (const [cardSetIndex, cardSet] of manifest.cardSets.entries()) {
529
- if (cardSet.type !== "manual") {
534
+ if (!cardSet.defaultHome) {
535
+ issues.push(
536
+ `manifest.cardSets[${cardSetIndex}].defaultHome: ${cardSet.type === "manual" ? "Manual" : "Preset"} card sets must declare defaultHome.`
537
+ );
530
538
  continue;
531
539
  }
532
- for (const [cardIndex, card] of cardSet.cards.entries()) {
533
- const path = `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`;
534
- if (card.home?.type === "zone" && zoneScopeById.get(card.home.zoneId) === "perPlayer") {
540
+ const validateCardHome = (home, path, label) => {
541
+ if (home?.type === "zone" && zoneScopeById.get(home.zoneId) === "perPlayer") {
535
542
  issues.push(
536
- `${path}.zoneId: Card '${card.type}' cannot target per-player zone '${card.home.zoneId}' because card inventory has no ownerId. Place it during reducer setup instead.`
543
+ `${path}.zoneId: ${label} cannot target per-player zone '${home.zoneId}' because card inventory has no ownerId. Place it during reducer setup instead.`
537
544
  );
538
545
  }
539
- if (homeTargetsBoard(card.home) && boardScopeById.get(card.home.boardId) === "perPlayer") {
546
+ if (homeTargetsBoard(home) && boardScopeById.get(home.boardId) === "perPlayer") {
540
547
  issues.push(
541
- `${path}.boardId: Card '${card.type}' cannot target per-player board '${card.home.boardId}' because card inventory has no ownerId. Place it during reducer setup instead.`
548
+ `${path}.boardId: ${label} cannot target per-player board '${home.boardId}' because card inventory has no ownerId. Place it during reducer setup instead.`
542
549
  );
543
550
  }
551
+ };
552
+ validateCardHome(
553
+ cardSet.defaultHome,
554
+ `manifest.cardSets[${cardSetIndex}].defaultHome`,
555
+ `Card set '${cardSet.id}' defaultHome`
556
+ );
557
+ for (const [cardIndex, card] of (cardSet.type === "manual" ? cardSet.cards : []).entries()) {
558
+ const path = `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`;
559
+ validateCardHome(card.home, path, `Card '${card.type}'`);
544
560
  }
545
561
  }
546
562
  return issues;
@@ -3042,6 +3058,11 @@ ${validation.errors.join("\n")}`
3042
3058
  ]);
3043
3059
  const resolveRuntimeBoardId = (boardBaseId, ownerId, context) => {
3044
3060
  const analyzedBoard = boardAnalysisByBaseId.get(boardBaseId);
3061
+ if (!analyzedBoard) {
3062
+ throw new Error(
3063
+ `${context ?? "Component home"}.boardId: Unknown board '${boardBaseId}'.`
3064
+ );
3065
+ }
3045
3066
  if (analyzedBoard?.board.scope === "perPlayer") {
3046
3067
  if (!ownerId) {
3047
3068
  throw new Error(
@@ -3052,18 +3073,30 @@ ${validation.errors.join("\n")}`
3052
3073
  }
3053
3074
  return boardBaseId;
3054
3075
  };
3055
- const resolveBoardEdgeId = (boardBaseId, spaceIds) => edgeIdByBoardBaseIdAndSpaces.get(boardBaseId)?.get(boardSpaceRefKey(spaceIds)) ?? boardSpaceRefKey(spaceIds);
3056
- const resolveBoardVertexId = (boardBaseId, spaceIds) => vertexIdByBoardBaseIdAndSpaces.get(boardBaseId)?.get(boardSpaceRefKey(spaceIds)) ?? boardSpaceRefKey(spaceIds);
3057
- const assignCardHome = (cardId, home, context) => {
3058
- if (!home || home.type === "detached") {
3059
- componentLocations[cardId] = {
3060
- type: "Detached",
3061
- position: nextLocationPosition({ type: "Detached" })
3062
- };
3063
- return;
3076
+ const resolveBoardEdgeId = (boardBaseId, spaceIds) => {
3077
+ const edgeId = edgeIdByBoardBaseIdAndSpaces.get(boardBaseId)?.get(boardSpaceRefKey(spaceIds));
3078
+ if (!edgeId) {
3079
+ throw new Error(
3080
+ `Unknown edge on board '${boardBaseId}' for spaces [${spaceIds.join(", ")}].`
3081
+ );
3082
+ }
3083
+ return edgeId;
3084
+ };
3085
+ const resolveBoardVertexId = (boardBaseId, spaceIds) => {
3086
+ const vertexId = vertexIdByBoardBaseIdAndSpaces.get(boardBaseId)?.get(boardSpaceRefKey(spaceIds));
3087
+ if (!vertexId) {
3088
+ throw new Error(
3089
+ `Unknown vertex on board '${boardBaseId}' for spaces [${spaceIds.join(", ")}].`
3090
+ );
3091
+ }
3092
+ return vertexId;
3093
+ };
3094
+ const materializeComponentLocation = (home, context) => {
3095
+ if (home.type === "detached") {
3096
+ return { type: "Detached" };
3064
3097
  }
3065
3098
  if (home.type === "slot") {
3066
- componentLocations[cardId] = {
3099
+ return {
3067
3100
  type: "InSlot",
3068
3101
  host: home.host,
3069
3102
  slotId: home.slotId,
@@ -3073,15 +3106,20 @@ ${validation.errors.join("\n")}`
3073
3106
  slotId: home.slotId
3074
3107
  })
3075
3108
  };
3076
- return;
3077
3109
  }
3078
3110
  if (home.type === "zone") {
3079
- if (zoneScopeById.get(home.zoneId) === "perPlayer") {
3111
+ const zoneScope = zoneScopeById.get(home.zoneId);
3112
+ if (!zoneScope) {
3113
+ throw new Error(
3114
+ `${context.path}.zoneId: ${context.label} targets unknown zone '${home.zoneId}'.`
3115
+ );
3116
+ }
3117
+ if (zoneScope === "perPlayer") {
3080
3118
  throw new Error(
3081
3119
  `${context.path}.zoneId: ${context.label} cannot target per-player zone '${home.zoneId}' because card inventory has no ownerId. Place it during reducer setup instead.`
3082
3120
  );
3083
3121
  }
3084
- componentLocations[cardId] = {
3122
+ return {
3085
3123
  type: "InDeck",
3086
3124
  deckId: home.zoneId,
3087
3125
  playedBy: null,
@@ -3091,7 +3129,6 @@ ${validation.errors.join("\n")}`
3091
3129
  playedBy: null
3092
3130
  })
3093
3131
  };
3094
- return;
3095
3132
  }
3096
3133
  if (home.type === "space") {
3097
3134
  const boardId = resolveRuntimeBoardId(
@@ -3099,7 +3136,12 @@ ${validation.errors.join("\n")}`
3099
3136
  null,
3100
3137
  `${context.path}.boardId: ${context.label}`
3101
3138
  );
3102
- componentLocations[cardId] = {
3139
+ if (!analysis.spaceIdsByBoardId.get(home.boardId)?.includes(home.spaceId)) {
3140
+ throw new Error(
3141
+ `${context.path}.spaceId: ${context.label} targets unknown space '${home.spaceId}' on board '${home.boardId}'.`
3142
+ );
3143
+ }
3144
+ return {
3103
3145
  type: "OnSpace",
3104
3146
  boardId,
3105
3147
  spaceId: home.spaceId,
@@ -3109,7 +3151,6 @@ ${validation.errors.join("\n")}`
3109
3151
  spaceId: home.spaceId
3110
3152
  })
3111
3153
  };
3112
- return;
3113
3154
  }
3114
3155
  if (home.type === "container") {
3115
3156
  const boardId = resolveRuntimeBoardId(
@@ -3117,7 +3158,12 @@ ${validation.errors.join("\n")}`
3117
3158
  null,
3118
3159
  `${context.path}.boardId: ${context.label}`
3119
3160
  );
3120
- componentLocations[cardId] = {
3161
+ if (!analysis.containerIdsByBoardId.get(home.boardId)?.includes(home.containerId)) {
3162
+ throw new Error(
3163
+ `${context.path}.containerId: ${context.label} targets unknown container '${home.containerId}' on board '${home.boardId}'.`
3164
+ );
3165
+ }
3166
+ return {
3121
3167
  type: "InContainer",
3122
3168
  boardId,
3123
3169
  containerId: home.containerId,
@@ -3127,7 +3173,6 @@ ${validation.errors.join("\n")}`
3127
3173
  containerId: home.containerId
3128
3174
  })
3129
3175
  };
3130
- return;
3131
3176
  }
3132
3177
  if (home.type === "edge") {
3133
3178
  const boardId = resolveRuntimeBoardId(
@@ -3136,7 +3181,7 @@ ${validation.errors.join("\n")}`
3136
3181
  `${context.path}.boardId: ${context.label}`
3137
3182
  );
3138
3183
  const edgeId = resolveBoardEdgeId(home.boardId, home.ref.spaces);
3139
- componentLocations[cardId] = {
3184
+ return {
3140
3185
  type: "OnEdge",
3141
3186
  boardId,
3142
3187
  edgeId,
@@ -3146,7 +3191,6 @@ ${validation.errors.join("\n")}`
3146
3191
  edgeId
3147
3192
  })
3148
3193
  };
3149
- return;
3150
3194
  }
3151
3195
  if (home.type === "vertex") {
3152
3196
  const boardId = resolveRuntimeBoardId(
@@ -3155,7 +3199,7 @@ ${validation.errors.join("\n")}`
3155
3199
  `${context.path}.boardId: ${context.label}`
3156
3200
  );
3157
3201
  const vertexId = resolveBoardVertexId(home.boardId, home.ref.spaces);
3158
- componentLocations[cardId] = {
3202
+ return {
3159
3203
  type: "OnVertex",
3160
3204
  boardId,
3161
3205
  vertexId,
@@ -3166,6 +3210,10 @@ ${validation.errors.join("\n")}`
3166
3210
  })
3167
3211
  };
3168
3212
  }
3213
+ const exhaustive = home;
3214
+ throw new Error(
3215
+ `Unsupported component home: ${JSON.stringify(exhaustive)}`
3216
+ );
3169
3217
  };
3170
3218
  for (const [cardSetIndex, cardSet] of manifest.cardSets.entries()) {
3171
3219
  const materializedCardSet = materializeCardSet(cardSet);
@@ -3187,10 +3235,15 @@ ${validation.errors.join("\n")}`
3187
3235
  ...card.properties ?? {}
3188
3236
  }
3189
3237
  };
3190
- assignCardHome(cardId, card.home, {
3191
- path: `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`,
3192
- label: `Card '${card.type}' instance ${instanceIndex + 1}`
3193
- });
3238
+ const resolvedHome = card.home ?? materializedCardSet.defaultHome;
3239
+ const path = card.home === void 0 ? `manifest.cardSets[${cardSetIndex}].defaultHome` : `manifest.cardSets[${cardSetIndex}].cards[${cardIndex}].home`;
3240
+ componentLocations[cardId] = materializeComponentLocation(
3241
+ resolvedHome,
3242
+ {
3243
+ path,
3244
+ label: `Card '${card.type}' instance ${instanceIndex + 1}`
3245
+ }
3246
+ );
3194
3247
  }
3195
3248
  }
3196
3249
  }
@@ -7029,6 +7082,7 @@ import {
7029
7082
  type BoardHexViewProps as BoardHexViewPropsGeneric,
7030
7083
  type BoardSpaceTargetProps as BoardSpaceTargetPropsGeneric,
7031
7084
  type ClientParamSchemaMap,
7085
+ type DefineGameUIConfig,
7032
7086
  type DreamboardUI,
7033
7087
  type GameMeState,
7034
7088
  type GamePlayersState,
@@ -7048,6 +7102,7 @@ import {
7048
7102
  type WorkspaceHandSurfaceDescriptor,
7049
7103
  type WorkspaceHandSurface,
7050
7104
  type WorkspaceInteractionFormDescriptor,
7105
+ type WorkspaceInteractionFormDialogProps,
7051
7106
  type WorkspaceInteractionSlotComponent,
7052
7107
  type WorkspaceInteractionFormsDescriptor,
7053
7108
  type WorkspacePileSurface,
@@ -7606,7 +7661,7 @@ type InteractionInputSlotByCollectorKind<
7606
7661
  export type InteractionFormSurface<Key extends InteractionKey> = {
7607
7662
  readonly Root: InteractionSlotComponent;
7608
7663
  readonly Form: DreamboardUI<typeof uiContract>["Interaction"]["Form"];
7609
- readonly Dialog: DreamboardUI<typeof uiContract>["Interaction"]["Dialog"];
7664
+ readonly Dialog: (props: WorkspaceInteractionFormDialogProps) => ReactElement | null;
7610
7665
  readonly State: DreamboardUI<typeof uiContract>["Interaction"]["State"];
7611
7666
  readonly Arm: DreamboardUI<typeof uiContract>["Interaction"]["Trigger"];
7612
7667
  readonly Submit: DreamboardUI<typeof uiContract>["Interaction"]["Submit"];
@@ -7750,8 +7805,9 @@ export type HandAuthoredTarget<
7750
7805
 
7751
7806
  /**
7752
7807
  * Summary projected from the live draft for a single hand. Authors render
7753
- * counts and validity hints via \`renderSummary\` or mirror them to outside
7754
- * state via \`onSelectionSummary\`.
7808
+ * counts and validity hints through the compound \`hand.Summary\` and
7809
+ * \`hand.Actions\` slots, or mirror them to outside state via
7810
+ * \`onSelectionSummary\`.
7755
7811
  */
7756
7812
  export type HandSelectionSummary<Card extends string> = {
7757
7813
  readonly selectedCount: number;
@@ -7797,23 +7853,6 @@ export type HandSurfaceProps<
7797
7853
  onCardIntent?: (
7798
7854
  intent: HandCardIntent<Card, HandAuthoredTarget<Card, Space, Edge, Vertex>>,
7799
7855
  ) => void;
7800
- /**
7801
- * Render-safe slot for selection summary content. Fires during render
7802
- * with the latest projected summary so authors can compose count chrome
7803
- * (e.g. "3 of 5 selected") without owning effect timing. Use
7804
- * \`onSelectionSummary\` for analytics or external state mirrors that
7805
- * need an effect.
7806
- */
7807
- renderSummary?: (summary: HandSelectionSummary<Card>) => ReactNode;
7808
- /**
7809
- * Render-safe slot for the hand's commit/action chrome (e.g. a submit
7810
- * button for a many-select interaction). Receives the same live summary as
7811
- * \`renderSummary\`. Rendered below the hand inline on desktop and pinned as a
7812
- * sticky footer inside the mobile dock, so the action stays reachable while
7813
- * the hand is docked \u2014 prefer this over anchoring the action elsewhere on
7814
- * the board, where the dock would otherwise cover it.
7815
- */
7816
- renderActions?: (summary: HandSelectionSummary<Card>) => ReactNode;
7817
7856
  /**
7818
7857
  * Optional selection summary observer. Invoked from a layout effect so
7819
7858
  * consumers may safely call \`setState\` in response.
@@ -7821,12 +7860,27 @@ export type HandSurfaceProps<
7821
7860
  onSelectionSummary?: (summary: HandSelectionSummary<Card>) => void;
7822
7861
  };
7823
7862
 
7824
- type HandCardsComponent<
7863
+ type HandComponent<
7825
7864
  Card extends string,
7826
7865
  Space extends string,
7827
7866
  Edge extends string,
7828
7867
  Vertex extends string,
7829
- > = InteractionSlotComponent<HandSurfaceProps<Card, Space, Edge, Vertex>>;
7868
+ > = InteractionSlotComponent<
7869
+ Omit<HandSurfaceProps<Card, Space, Edge, Vertex>, "children"> & {
7870
+ children: ReactNode;
7871
+ }
7872
+ >;
7873
+
7874
+ type HandCardsComponent = InteractionSlotComponent<{
7875
+ children: (
7876
+ card: WorkspaceZoneCard,
7877
+ state: InteractionVisualState,
7878
+ ) => ReactNode;
7879
+ }>;
7880
+
7881
+ type HandSummaryComponent<Card extends string> = InteractionSlotComponent<{
7882
+ children?: ReactNode | ((summary: HandSelectionSummary<Card>) => ReactNode);
7883
+ }>;
7830
7884
 
7831
7885
  /**
7832
7886
  * Always-visible staging surface for a many-select card collection (e.g. the
@@ -7849,12 +7903,15 @@ export type HandStagingProps = {
7849
7903
  type HandStagingComponent = InteractionSlotComponent<HandStagingProps>;
7850
7904
 
7851
7905
  export type HandSurface<Zones extends readonly WorkspaceZoneId[] = readonly WorkspaceZoneId[]> = {
7852
- readonly Hand: HandCardsComponent<
7906
+ readonly Hand: HandComponent<
7853
7907
  CardId & string,
7854
7908
  SpaceId & string,
7855
7909
  EdgeId & string,
7856
7910
  VertexId & string
7857
7911
  >;
7912
+ readonly Cards: HandCardsComponent;
7913
+ readonly Summary: HandSummaryComponent<CardId & string>;
7914
+ readonly Actions: HandSummaryComponent<CardId & string>;
7858
7915
  readonly Card: ZoneCardComponent;
7859
7916
  readonly Staging: HandStagingComponent;
7860
7917
  readonly slot: {
@@ -8137,6 +8194,21 @@ type WorkspaceUI = Omit<
8137
8194
  defineSurfaces<const Spec extends WorkspaceSurfaceSpec>(
8138
8195
  spec: Spec,
8139
8196
  ): () => WorkspaceSurfaceValue<Spec>;
8197
+ defineGameUI<Surfaces>(
8198
+ config: DefineGameUIConfig<GameRootState, Surfaces> & {
8199
+ useSurfaces: () => Surfaces;
8200
+ interactionRoutes?: (context: {
8201
+ game: GameRootState;
8202
+ surfaces: Surfaces;
8203
+ }) => InteractionRoutes;
8204
+ phases: {
8205
+ readonly [Phase in PhaseName]: (context: {
8206
+ game: GameRootState;
8207
+ surfaces: Surfaces;
8208
+ }) => ReactNode;
8209
+ };
8210
+ },
8211
+ ): (props: Omit<UIRootProps, "children">) => ReactElement;
8140
8212
  readonly Game: TypedGame<typeof uiContract, GameView, PlayerId, PhaseName>;
8141
8213
  readonly Interaction: Pick<DreamboardUI<typeof uiContract>["Interaction"], "State" | "Dialog"> & {
8142
8214
  useForm<Key extends InteractionKey>(interaction: Key): InteractionFormSurface<Key>;
@@ -8715,4 +8787,4 @@ export {
8715
8787
  generateAuthoritativeFiles,
8716
8788
  generateDynamicGeneratedFiles
8717
8789
  };
8718
- //# sourceMappingURL=chunk-XXBSIPJX.js.map
8790
+ //# sourceMappingURL=chunk-CP35NPPY.js.map