@dreamboard-games/sdk 0.2.0

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 (369) hide show
  1. package/LICENSE.md +96 -0
  2. package/README.md +12 -0
  3. package/dist/HandView-ncJIVLhN.d.ts +193 -0
  4. package/dist/ResourceCounter-CTREyF73.d.ts +102 -0
  5. package/dist/ThemeProvider-fy0_QzgO.d.ts +99 -0
  6. package/dist/bundle-TIZcw8LB.d.ts +281 -0
  7. package/dist/cards-Sl3b40Mv.d.ts +13 -0
  8. package/dist/chunk-7YAHLYBR.js +481 -0
  9. package/dist/chunk-7YAHLYBR.js.map +1 -0
  10. package/dist/chunk-FDNZTDD6.js +8085 -0
  11. package/dist/chunk-FDNZTDD6.js.map +1 -0
  12. package/dist/chunk-GKKBPPSW.js +598 -0
  13. package/dist/chunk-GKKBPPSW.js.map +1 -0
  14. package/dist/chunk-I46YJSOD.js +1 -0
  15. package/dist/chunk-I46YJSOD.js.map +1 -0
  16. package/dist/chunk-KAELH4KC.js +104 -0
  17. package/dist/chunk-KAELH4KC.js.map +1 -0
  18. package/dist/chunk-PZ5AY32C.js +10 -0
  19. package/dist/chunk-PZ5AY32C.js.map +1 -0
  20. package/dist/chunk-T3ZKNUZ7.js +1 -0
  21. package/dist/chunk-T3ZKNUZ7.js.map +1 -0
  22. package/dist/chunk-T52J5RMF.js +1 -0
  23. package/dist/chunk-T52J5RMF.js.map +1 -0
  24. package/dist/chunk-TDSWKVZ4.js +5401 -0
  25. package/dist/chunk-TDSWKVZ4.js.map +1 -0
  26. package/dist/chunk-U5C6BONG.js +34 -0
  27. package/dist/chunk-U5C6BONG.js.map +1 -0
  28. package/dist/chunk-VDXOF4FW.js +69 -0
  29. package/dist/chunk-VDXOF4FW.js.map +1 -0
  30. package/dist/chunk-VFTAA4WO.js +115 -0
  31. package/dist/chunk-VFTAA4WO.js.map +1 -0
  32. package/dist/chunk-WN74KVNY.js +17 -0
  33. package/dist/chunk-WN74KVNY.js.map +1 -0
  34. package/dist/chunk-WYPQ3GG5.js +10990 -0
  35. package/dist/chunk-WYPQ3GG5.js.map +1 -0
  36. package/dist/components-D5ZRE2Hl.d.ts +1451 -0
  37. package/dist/generated/runtime/primitives.d.ts +12 -0
  38. package/dist/generated/runtime/primitives.js +180 -0
  39. package/dist/generated/runtime/primitives.js.map +1 -0
  40. package/dist/generated/runtime-api.d.ts +3 -0
  41. package/dist/generated/runtime-api.js +2 -0
  42. package/dist/generated/runtime-api.js.map +1 -0
  43. package/dist/generated/runtime.d.ts +14 -0
  44. package/dist/generated/runtime.js +18 -0
  45. package/dist/generated/runtime.js.map +1 -0
  46. package/dist/generated/workspace-contract.d.ts +14 -0
  47. package/dist/generated/workspace-contract.js +14 -0
  48. package/dist/generated/workspace-contract.js.map +1 -0
  49. package/dist/hex-board-view-D_07hO6O.d.ts +933 -0
  50. package/dist/hex-color-MhOyuY-o.d.ts +8 -0
  51. package/dist/index-BwqPQtBu.d.ts +1433 -0
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.js +12 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/infrastructure/reducer-bundle-abi.d.ts +1083 -0
  56. package/dist/infrastructure/reducer-bundle-abi.js +14 -0
  57. package/dist/infrastructure/reducer-bundle-abi.js.map +1 -0
  58. package/dist/infrastructure/workspace-codegen.d.ts +53 -0
  59. package/dist/infrastructure/workspace-codegen.js +44 -0
  60. package/dist/infrastructure/workspace-codegen.js.map +1 -0
  61. package/dist/manifest-contract-BNHVGFtU.d.ts +9 -0
  62. package/dist/package-set.d.ts +13 -0
  63. package/dist/package-set.js +12 -0
  64. package/dist/package-set.js.map +1 -0
  65. package/dist/primitive-props-DpKs-GCr.d.ts +11 -0
  66. package/dist/reducer.d.ts +3786 -0
  67. package/dist/reducer.js +8131 -0
  68. package/dist/reducer.js.map +1 -0
  69. package/dist/runtime/primitives.d.ts +226 -0
  70. package/dist/runtime/primitives.js +180 -0
  71. package/dist/runtime/primitives.js.map +1 -0
  72. package/dist/runtime/types/runtime-api.d.ts +1 -0
  73. package/dist/runtime/types/runtime-api.js +2 -0
  74. package/dist/runtime/types/runtime-api.js.map +1 -0
  75. package/dist/runtime/workspace-contract.d.ts +172 -0
  76. package/dist/runtime/workspace-contract.js +14 -0
  77. package/dist/runtime/workspace-contract.js.map +1 -0
  78. package/dist/runtime-api-3dshj6kK.d.ts +101 -0
  79. package/dist/runtime-api-DWxvTr-O.d.ts +379 -0
  80. package/dist/runtime.d.ts +58 -0
  81. package/dist/runtime.js +13 -0
  82. package/dist/runtime.js.map +1 -0
  83. package/dist/slots-1GPGihk8.d.ts +8 -0
  84. package/dist/testing.d.ts +149 -0
  85. package/dist/testing.js +513 -0
  86. package/dist/testing.js.map +1 -0
  87. package/dist/types.d.ts +496 -0
  88. package/dist/types.js +28 -0
  89. package/dist/types.js.map +1 -0
  90. package/dist/ui/components.d.ts +16 -0
  91. package/dist/ui/components.js +192 -0
  92. package/dist/ui/components.js.map +1 -0
  93. package/dist/ui/defaults.d.ts +19 -0
  94. package/dist/ui/defaults.js +104 -0
  95. package/dist/ui/defaults.js.map +1 -0
  96. package/dist/ui/plugin-styles.css +250 -0
  97. package/dist/ui/types/player-state.d.ts +365 -0
  98. package/dist/ui/types/player-state.js +1 -0
  99. package/dist/ui/types/player-state.js.map +1 -0
  100. package/dist/ui-contract-iQfTtUSL.d.ts +1161 -0
  101. package/dist/ui.d.ts +320 -0
  102. package/dist/ui.js +253 -0
  103. package/dist/ui.js.map +1 -0
  104. package/package.json +199 -0
  105. package/src/generated/reducer-contract/builders.ts +90 -0
  106. package/src/generated/reducer-contract/version.ts +9 -0
  107. package/src/generated/reducer-contract/wire.ts +100 -0
  108. package/src/generated/reducer-contract/zod.ts +101 -0
  109. package/src/generated/runtime/primitives.ts +2 -0
  110. package/src/generated/runtime-api.ts +5 -0
  111. package/src/generated/runtime.ts +35 -0
  112. package/src/generated/workspace-contract.ts +2 -0
  113. package/src/index.ts +7 -0
  114. package/src/infrastructure/reducer-bundle-abi.ts +8 -0
  115. package/src/infrastructure/reducer-contract/bundle.ts +37 -0
  116. package/src/infrastructure/workspace-codegen/hex-geometry.ts +69 -0
  117. package/src/infrastructure/workspace-codegen/index.ts +64 -0
  118. package/src/infrastructure/workspace-codegen/manifest-contract.ts +6632 -0
  119. package/src/infrastructure/workspace-codegen/manifest-validation.ts +795 -0
  120. package/src/infrastructure/workspace-codegen/ownership.ts +131 -0
  121. package/src/infrastructure/workspace-codegen/preset-card-sets.ts +169 -0
  122. package/src/infrastructure/workspace-codegen/seeds.ts +1705 -0
  123. package/src/infrastructure/workspace-codegen.ts +1 -0
  124. package/src/package-set.ts +19 -0
  125. package/src/reducer/authoring/contract.ts +157 -0
  126. package/src/reducer/authoring/effect.ts +224 -0
  127. package/src/reducer/authoring/game.ts +23 -0
  128. package/src/reducer/authoring/interaction.ts +98 -0
  129. package/src/reducer/authoring/phase.ts +300 -0
  130. package/src/reducer/authoring/types.ts +70 -0
  131. package/src/reducer/authoring/validation.ts +382 -0
  132. package/src/reducer/authoring/view-stage.ts +68 -0
  133. package/src/reducer/authoring.ts +29 -0
  134. package/src/reducer/bundle/ingress-bundle.ts +491 -0
  135. package/src/reducer/bundle/trusted/engine-instruction-resolver.ts +254 -0
  136. package/src/reducer/bundle/trusted/flow-instruction-resolver.ts +73 -0
  137. package/src/reducer/bundle/trusted/instruction-runner.ts +414 -0
  138. package/src/reducer/bundle/trusted/interaction-authorization.ts +137 -0
  139. package/src/reducer/bundle/trusted/interaction-collectors.ts +859 -0
  140. package/src/reducer/bundle/trusted/interaction-decision.ts +747 -0
  141. package/src/reducer/bundle/trusted/interaction-resolver.ts +95 -0
  142. package/src/reducer/bundle/trusted/interaction-types.ts +171 -0
  143. package/src/reducer/bundle/trusted/lifecycle-runner.ts +427 -0
  144. package/src/reducer/bundle/trusted/projection-builder.ts +356 -0
  145. package/src/reducer/bundle/trusted/projection-context.ts +39 -0
  146. package/src/reducer/bundle/trusted/rng-sampler.ts +150 -0
  147. package/src/reducer/bundle/trusted/runtime-registry.ts +120 -0
  148. package/src/reducer/bundle/trusted/runtime-scope.ts +336 -0
  149. package/src/reducer/bundle/trusted/simultaneous-player.ts +97 -0
  150. package/src/reducer/bundle/trusted/stage-resolver.ts +87 -0
  151. package/src/reducer/bundle/trusted/static-projection.ts +116 -0
  152. package/src/reducer/bundle/trusted/trusted-runtime-args.ts +97 -0
  153. package/src/reducer/bundle/trusted/trusted-runtime-result.ts +39 -0
  154. package/src/reducer/bundle/trusted/trusted-setup-profiles.ts +43 -0
  155. package/src/reducer/bundle/trusted/trusted-state-codec.ts +48 -0
  156. package/src/reducer/bundle/trusted-bundle.ts +97 -0
  157. package/src/reducer/bundle/types.ts +171 -0
  158. package/src/reducer/bundle.ts +2 -0
  159. package/src/reducer/client-param-schemas.ts +57 -0
  160. package/src/reducer/compose.ts +34 -0
  161. package/src/reducer/core/runtime-input.ts +30 -0
  162. package/src/reducer/core/runtime-instruction.ts +59 -0
  163. package/src/reducer/core/types.ts +62 -0
  164. package/src/reducer/definition-index.ts +277 -0
  165. package/src/reducer/derived.ts +106 -0
  166. package/src/reducer/effects.ts +92 -0
  167. package/src/reducer/engine/runtime-instruction-engine.ts +155 -0
  168. package/src/reducer/ingress/decode-runtime-input.ts +7 -0
  169. package/src/reducer/ingress/decode-session-state.ts +9 -0
  170. package/src/reducer/ingress/encode-session-state.ts +6 -0
  171. package/src/reducer/ingress/input-codec.ts +18 -0
  172. package/src/reducer/ingress/phase-schemas.ts +62 -0
  173. package/src/reducer/ingress/raw-types.ts +107 -0
  174. package/src/reducer/ingress/runtime-codec.ts +14 -0
  175. package/src/reducer/ingress/runtime-payload.ts +13 -0
  176. package/src/reducer/ingress/session-codec.ts +392 -0
  177. package/src/reducer/ingress/types.ts +6 -0
  178. package/src/reducer/inputs/boardInput.ts +217 -0
  179. package/src/reducer/inputs/boardTarget.ts +190 -0
  180. package/src/reducer/inputs/cardInput.ts +86 -0
  181. package/src/reducer/inputs/cardTarget.ts +101 -0
  182. package/src/reducer/inputs/choiceTarget.ts +104 -0
  183. package/src/reducer/inputs/defineInputs.ts +71 -0
  184. package/src/reducer/inputs/formInput.ts +809 -0
  185. package/src/reducer/inputs/many.ts +120 -0
  186. package/src/reducer/inputs/promptInput.ts +87 -0
  187. package/src/reducer/inputs/rngInput.ts +58 -0
  188. package/src/reducer/inputs/targetRule.ts +123 -0
  189. package/src/reducer/inputs.ts +41 -0
  190. package/src/reducer/model/definition.ts +1072 -0
  191. package/src/reducer/model/extract.ts +745 -0
  192. package/src/reducer/model/manifest.ts +570 -0
  193. package/src/reducer/model/queries.ts +641 -0
  194. package/src/reducer/model/runtime.ts +264 -0
  195. package/src/reducer/model/spec.ts +1386 -0
  196. package/src/reducer/model/table.ts +260 -0
  197. package/src/reducer/model.ts +7 -0
  198. package/src/reducer/ops.ts +1034 -0
  199. package/src/reducer/parse-utils.ts +28 -0
  200. package/src/reducer/per-player.ts +422 -0
  201. package/src/reducer/rng.ts +69 -0
  202. package/src/reducer/schema-helpers.ts +185 -0
  203. package/src/reducer/setup-bootstrap-helpers.ts +171 -0
  204. package/src/reducer/setup-bootstrap.ts +481 -0
  205. package/src/reducer/table-ops.ts +2671 -0
  206. package/src/reducer/table-queries.ts +372 -0
  207. package/src/reducer/transaction.ts +120 -0
  208. package/src/reducer.ts +314 -0
  209. package/src/runtime/primitives.ts +1 -0
  210. package/src/runtime/types/runtime-api.ts +1 -0
  211. package/src/runtime/workspace-contract.ts +32 -0
  212. package/src/runtime-internal/components/InteractionForm.tsx +1309 -0
  213. package/src/runtime-internal/components/PluginRuntime.tsx +103 -0
  214. package/src/runtime-internal/components/board/target-layer.ts +70 -0
  215. package/src/runtime-internal/context/ClientParamSchemaContext.tsx +44 -0
  216. package/src/runtime-internal/context/InteractionDraftContext.tsx +279 -0
  217. package/src/runtime-internal/context/PluginSessionContext.tsx +47 -0
  218. package/src/runtime-internal/context/PluginStateContext.tsx +262 -0
  219. package/src/runtime-internal/context/RuntimeContext.tsx +96 -0
  220. package/src/runtime-internal/defaults/components.tsx +409 -0
  221. package/src/runtime-internal/defaults/index.ts +11 -0
  222. package/src/runtime-internal/errors/ValidationError.ts +29 -0
  223. package/src/runtime-internal/hooks/useActivePlayers.ts +33 -0
  224. package/src/runtime-internal/hooks/useBoardInteractions.ts +665 -0
  225. package/src/runtime-internal/hooks/useGameSelector.ts +105 -0
  226. package/src/runtime-internal/hooks/useGameView.ts +9 -0
  227. package/src/runtime-internal/hooks/useInteractionByKey.ts +354 -0
  228. package/src/runtime-internal/hooks/useInteractionHandle.ts +438 -0
  229. package/src/runtime-internal/hooks/useIsMyTurn.ts +20 -0
  230. package/src/runtime-internal/hooks/useLobby.ts +76 -0
  231. package/src/runtime-internal/hooks/useMe.ts +48 -0
  232. package/src/runtime-internal/hooks/usePlayerInfo.ts +28 -0
  233. package/src/runtime-internal/hooks/usePlayerTurnOrder.ts +23 -0
  234. package/src/runtime-internal/hooks/usePluginRuntime.ts +147 -0
  235. package/src/runtime-internal/hooks/useSeatInbox.ts +61 -0
  236. package/src/runtime-internal/hooks/useSimultaneousPhase.ts +10 -0
  237. package/src/runtime-internal/index.ts +42 -0
  238. package/src/runtime-internal/internal.ts +43 -0
  239. package/src/runtime-internal/plugin-styles.css +250 -0
  240. package/src/runtime-internal/primitives/board.tsx +459 -0
  241. package/src/runtime-internal/primitives/dialog-lifecycle.ts +58 -0
  242. package/src/runtime-internal/primitives/dice.tsx +79 -0
  243. package/src/runtime-internal/primitives/game-ui-provider.tsx +35 -0
  244. package/src/runtime-internal/primitives/game.tsx +387 -0
  245. package/src/runtime-internal/primitives/hand-intent-adapter.ts +147 -0
  246. package/src/runtime-internal/primitives/hand-surface.tsx +594 -0
  247. package/src/runtime-internal/primitives/index.ts +196 -0
  248. package/src/runtime-internal/primitives/interaction-form-binding.tsx +56 -0
  249. package/src/runtime-internal/primitives/interaction-submit.ts +90 -0
  250. package/src/runtime-internal/primitives/interaction.tsx +987 -0
  251. package/src/runtime-internal/primitives/phase.tsx +43 -0
  252. package/src/runtime-internal/primitives/player-roster.tsx +302 -0
  253. package/src/runtime-internal/primitives/primitive-props.tsx +101 -0
  254. package/src/runtime-internal/primitives/prompt.tsx +255 -0
  255. package/src/runtime-internal/primitives/ui.tsx +60 -0
  256. package/src/runtime-internal/primitives/zone.tsx +791 -0
  257. package/src/runtime-internal/reducer.ts +30 -0
  258. package/src/runtime-internal/runtime/createPluginRuntimeAPI.ts +605 -0
  259. package/src/runtime-internal/types/plugin-state.ts +508 -0
  260. package/src/runtime-internal/types/reducer-state.ts +24 -0
  261. package/src/runtime-internal/types/runtime-api.ts +114 -0
  262. package/src/runtime-internal/ui-contract.ts +519 -0
  263. package/src/runtime-internal/utils/card-intent-adapter.ts +546 -0
  264. package/src/runtime-internal/utils/interaction-inputs.ts +492 -0
  265. package/src/runtime-internal/utils/interaction-labels.ts +23 -0
  266. package/src/runtime-internal/utils/interaction-router.ts +273 -0
  267. package/src/runtime-internal/utils/interaction-status.ts +74 -0
  268. package/src/runtime-internal/workspace-contract.ts +1170 -0
  269. package/src/runtime.ts +34 -0
  270. package/src/testing/create-expect-api.ts +352 -0
  271. package/src/testing/create-test-runtime.ts +381 -0
  272. package/src/testing/definitions.ts +127 -0
  273. package/src/testing/index.ts +3 -0
  274. package/src/testing.ts +1 -0
  275. package/src/type-stubs/manifest-contract.d.ts +42 -0
  276. package/src/type-stubs/manifest-contract.js +72 -0
  277. package/src/type-stubs/ui-contract.d.ts +5 -0
  278. package/src/type-stubs/ui-contract.js +1 -0
  279. package/src/types/authoring-card-properties.type-test.ts +266 -0
  280. package/src/types/authoring.ts +1282 -0
  281. package/src/types/cards.ts +19 -0
  282. package/src/types/contracts.ts +1550 -0
  283. package/src/types/generated-helpers.ts +35 -0
  284. package/src/types/index.ts +147 -0
  285. package/src/types/slots.ts +11 -0
  286. package/src/types.ts +1 -0
  287. package/src/ui/components/ActionButton.tsx +97 -0
  288. package/src/ui/components/ActionPanel.tsx +315 -0
  289. package/src/ui/components/Card.tsx +378 -0
  290. package/src/ui/components/CardDragSurface.tsx +1076 -0
  291. package/src/ui/components/ChromeSuppressionContext.tsx +70 -0
  292. package/src/ui/components/CostDisplay.tsx +145 -0
  293. package/src/ui/components/DiceRoller.tsx +581 -0
  294. package/src/ui/components/Drawer.tsx +180 -0
  295. package/src/ui/components/ErrorBoundary.tsx +275 -0
  296. package/src/ui/components/GameEndDisplay.tsx +398 -0
  297. package/src/ui/components/GameSkeleton.tsx +260 -0
  298. package/src/ui/components/Hand.tsx +468 -0
  299. package/src/ui/components/HandDock.tsx +299 -0
  300. package/src/ui/components/HandView.tsx +441 -0
  301. package/src/ui/components/MobileHandTray.tsx +381 -0
  302. package/src/ui/components/MoreActions.tsx +143 -0
  303. package/src/ui/components/PhaseIndicator.tsx +341 -0
  304. package/src/ui/components/PlayArea.tsx +146 -0
  305. package/src/ui/components/PrimaryActionButton.tsx +336 -0
  306. package/src/ui/components/PrimaryButton.tsx +45 -0
  307. package/src/ui/components/ResourceCounter.tsx +270 -0
  308. package/src/ui/components/StagingZone.tsx +134 -0
  309. package/src/ui/components/ThemedButton.tsx +113 -0
  310. package/src/ui/components/Toast.tsx +264 -0
  311. package/src/ui/components/board/HexGrid.tsx +1294 -0
  312. package/src/ui/components/board/NetworkGraph.tsx +476 -0
  313. package/src/ui/components/board/SlotSystem.tsx +388 -0
  314. package/src/ui/components/board/SquareGrid.tsx +1165 -0
  315. package/src/ui/components/board/TrackBoard.tsx +496 -0
  316. package/src/ui/components/board/ZoneMap.tsx +448 -0
  317. package/src/ui/components/board/hex-board-view.ts +123 -0
  318. package/src/ui/components/board/index.ts +142 -0
  319. package/src/ui/components/board/interaction-accessibility.ts +21 -0
  320. package/src/ui/components/board/target-layer.ts +66 -0
  321. package/src/ui/components/card-render-content.type-test.ts +27 -0
  322. package/src/ui/components/hand-layout-math.ts +163 -0
  323. package/src/ui/components/hand-pointer-engine.ts +413 -0
  324. package/src/ui/components/index.ts +245 -0
  325. package/src/ui/components.ts +1 -0
  326. package/src/ui/defaults/components.tsx +106 -0
  327. package/src/ui/defaults/index.ts +8 -0
  328. package/src/ui/defaults.ts +1 -0
  329. package/src/ui/errors/ValidationError.ts +29 -0
  330. package/src/ui/helpers/cards.ts +19 -0
  331. package/src/ui/helpers/track-board.ts +211 -0
  332. package/src/ui/hooks/useBoardTopology.ts +316 -0
  333. package/src/ui/hooks/useCards.ts +10 -0
  334. package/src/ui/hooks/useHandCardPointer.ts +381 -0
  335. package/src/ui/hooks/useHandLayout.ts +378 -0
  336. package/src/ui/hooks/useHandPresentation.ts +121 -0
  337. package/src/ui/hooks/useHexBoard.ts +74 -0
  338. package/src/ui/hooks/useHexGrid.ts +185 -0
  339. package/src/ui/hooks/useIsMobile.ts +35 -0
  340. package/src/ui/hooks/usePanZoom.ts +278 -0
  341. package/src/ui/hooks/useSquareBoard.ts +124 -0
  342. package/src/ui/hooks/useSquareGrid.ts +328 -0
  343. package/src/ui/index.ts +98 -0
  344. package/src/ui/internal/ui/alert.tsx +51 -0
  345. package/src/ui/internal/ui/button.tsx +58 -0
  346. package/src/ui/internal/ui/dialog.tsx +134 -0
  347. package/src/ui/internal/ui/input.tsx +21 -0
  348. package/src/ui/internal/ui/label.tsx +21 -0
  349. package/src/ui/internal/ui/select.tsx +129 -0
  350. package/src/ui/internal/ui/tooltip.tsx +54 -0
  351. package/src/ui/internal/ui/utils.ts +5 -0
  352. package/src/ui/plugin-styles.css +250 -0
  353. package/src/ui/primitives/dialog-lifecycle.ts +58 -0
  354. package/src/ui/primitives/dice.tsx +79 -0
  355. package/src/ui/primitives/primitive-props.tsx +101 -0
  356. package/src/ui/theme/ThemeProvider.tsx +252 -0
  357. package/src/ui/theme/board.ts +61 -0
  358. package/src/ui/theme/css-vars.ts +105 -0
  359. package/src/ui/theme/derive.ts +240 -0
  360. package/src/ui/theme/index.ts +61 -0
  361. package/src/ui/theme/presets/arcade.ts +261 -0
  362. package/src/ui/theme/presets/studio.ts +261 -0
  363. package/src/ui/theme/presets/tabletop.ts +266 -0
  364. package/src/ui/theme/tokens.ts +392 -0
  365. package/src/ui/types/hex-color.ts +20 -0
  366. package/src/ui/types/player-state.ts +463 -0
  367. package/src/ui/types/tiled-board.ts +785 -0
  368. package/src/ui/types/visual-state.ts +137 -0
  369. package/src/ui.ts +1 -0
@@ -0,0 +1,1705 @@
1
+ import type { GameTopologyManifest } from "../../types/index.js";
2
+
3
+ export const SETUP_PROFILES_SEED_MARKER =
4
+ "Generated by dreamboard setup-profiles scaffold.";
5
+
6
+ function generateUiContractContent(): string {
7
+ return `/**
8
+ * Generated file.
9
+ * Do not edit directly.
10
+ */
11
+
12
+ import game from "../../app/game";
13
+ import {
14
+ createClientParamSchemasByPhase,
15
+ } from "@dreamboard-games/sdk/reducer";
16
+ import type {
17
+ CardInputZoneIdsOfDefinition,
18
+ ClientParamsOfInteractionOfDefinition,
19
+ DefaultedClientParamKeysOfInteractionOfDefinition,
20
+ InputKeysWithCollectorKindOfDefinition,
21
+ InteractionIdOfDefinition,
22
+ InteractionIdOfDefinitionPhase,
23
+ PhaseNamesOfDefinition,
24
+ StageNamesOfDefinitionPhase,
25
+ ViewNamesOfDefinition,
26
+ ViewOfDefinition,
27
+ } from "@dreamboard-games/sdk/reducer";
28
+ import {
29
+ type BoardSpaceIdOf,
30
+ type InteractionVisualState,
31
+ } from "@dreamboard-games/sdk/ui";
32
+ import { type InteractionDescriptor } from "@dreamboard-games/sdk/generated/runtime";
33
+ import {
34
+ createWorkspaceUIContract,
35
+ type BoardHexGridProps as BoardHexGridPropsGeneric,
36
+ type BoardHexViewProps as BoardHexViewPropsGeneric,
37
+ type BoardSpaceTargetProps as BoardSpaceTargetPropsGeneric,
38
+ type ClientParamSchemaMap,
39
+ type DreamboardUI,
40
+ type GameMeState,
41
+ type GamePlayersState,
42
+ type GameRenderState,
43
+ type GameTurnState,
44
+ type ResourceCounterComponents,
45
+ type TypedGame,
46
+ type UIContract,
47
+ type UIRootProps,
48
+ type WorkspaceBoardSurfaceDescriptor,
49
+ type WorkspaceBoardSurface,
50
+ type WorkspaceBoardTargetInputSlot,
51
+ type WorkspaceCardCollectionSurface,
52
+ type WorkspaceCardCollectionSurfaceDescriptor,
53
+ type WorkspaceCardInputSlot,
54
+ type WorkspaceFormInputSlot,
55
+ type WorkspaceHandSurfaceDescriptor,
56
+ type WorkspaceHandSurface,
57
+ type WorkspaceInteractionFormDescriptor,
58
+ type WorkspaceInteractionSlotComponent,
59
+ type WorkspaceInteractionFormsDescriptor,
60
+ type WorkspacePileSurface,
61
+ type WorkspacePileSurfaceDescriptor,
62
+ type WorkspacePilesSurfaceDescriptor,
63
+ type WorkspaceSurfaceSpec,
64
+ type ZoneCardRenderItem,
65
+ type ZoneListProps,
66
+ } from "@dreamboard-games/sdk/generated/workspace-contract";
67
+ import { type ButtonHTMLAttributes, type ReactElement, type ReactNode } from "react";
68
+ import {
69
+ boardHelpers,
70
+ literals,
71
+ staticBoards,
72
+ type BoardBaseId,
73
+ type CardId,
74
+ type CardProperties,
75
+ type CardType,
76
+ type EdgeId,
77
+ type PlayerId,
78
+ type ResourceId,
79
+ type SpaceId,
80
+ type TiledBoardId,
81
+ type TiledEdgeState,
82
+ type TiledVertexState,
83
+ type VertexId,
84
+ type ZoneId as ManifestZoneId,
85
+ } from "../manifest-contract";
86
+
87
+ /**
88
+ * Boundary tripwire (do not remove).
89
+ *
90
+ * The authored UI boundary depends on \`@dreamboard-games/sdk/runtime\` resolving to
91
+ * real types in every typecheck path (local \`tsc\` and the remote compiler
92
+ * service). If a future change to module resolution or type loading silently
93
+ * degrades \`@dreamboard-games/sdk/runtime\` to \`any\`, the assertions below stop
94
+ * compiling, turning invisible type erosion into a hard typecheck error in
95
+ * every generated workspace. \`InteractionDescriptor\` comes from the package
96
+ * root and \`ZoneCardRenderItem\` from the \`/workspace-contract\` subpath, so
97
+ * both entry points are covered.
98
+ */
99
+ type IsAny<T> = 0 extends 1 & T ? true : false;
100
+ type AssertNotAny<T> = IsAny<T> extends true
101
+ ? "SDK runtime types resolved to any — boundary degraded"
102
+ : true;
103
+ const _uiRuntimeBoundaryResolved: [
104
+ AssertNotAny<InteractionDescriptor>,
105
+ AssertNotAny<ZoneCardRenderItem>,
106
+ ] = [true, true];
107
+ void _uiRuntimeBoundaryResolved;
108
+
109
+ type GameDefinition = typeof game;
110
+
111
+ export type ViewName = ViewNamesOfDefinition<GameDefinition>;
112
+ export type InferView<Name extends ViewName> = ViewOfDefinition<
113
+ GameDefinition,
114
+ Name
115
+ >;
116
+ export type GameView = Extract<"player", ViewName> extends never
117
+ ? never
118
+ : InferView<Extract<"player", ViewName>>;
119
+
120
+ export type PhaseName = PhaseNamesOfDefinition<GameDefinition>;
121
+ export type GameRootState = GameRenderState<GameView, PlayerId, PhaseName>;
122
+ export type GamePlayers = GamePlayersState<PlayerId>;
123
+ export type GameMe = GameMeState<PlayerId>;
124
+ export type GameTurn = GameTurnState<PlayerId, PhaseName>;
125
+
126
+ // -------------------------------------------------------------------------
127
+ // Interaction / Stage / Zone types (authored via defineInteraction/Stage/zones)
128
+ // -------------------------------------------------------------------------
129
+
130
+ /** Union of all interaction ids across phases. */
131
+ export type InteractionId = InteractionIdOfDefinition<GameDefinition>;
132
+
133
+ /** Interactions declared in a specific phase. */
134
+ export type InteractionIdForPhase<Phase extends PhaseName> =
135
+ InteractionIdOfDefinitionPhase<GameDefinition, Phase>;
136
+
137
+ /**
138
+ * Client-facing params type for an interaction, inferred from its input
139
+ * collectors. Engine-sampled collectors (e.g. \`rngInput.*\`) are omitted
140
+ * — the trusted reducer bundle fills those fields during submit, so the
141
+ * client never supplies them.
142
+ */
143
+ export type InteractionParams<
144
+ Phase extends PhaseName,
145
+ Id extends InteractionIdForPhase<Phase>,
146
+ > = ClientParamsOfInteractionOfDefinition<GameDefinition, Phase, Id>;
147
+
148
+ /** Client-facing params with authored input defaults. */
149
+ export type InteractionDefaultedKeys<
150
+ Phase extends PhaseName,
151
+ Id extends InteractionIdForPhase<Phase>,
152
+ > = DefaultedClientParamKeysOfInteractionOfDefinition<GameDefinition, Phase, Id>;
153
+
154
+ /** Phase-qualified interaction key for a specific phase. */
155
+ export type InteractionKeyForPhase<Phase extends PhaseName> =
156
+ \`\${Phase}.\${InteractionIdForPhase<Phase>}\`;
157
+
158
+ /** Phase-qualified union of every client/UI interaction key. */
159
+ export type InteractionKey = {
160
+ [P in PhaseName]: InteractionKeyForPhase<P>;
161
+ }[PhaseName];
162
+
163
+ type PhaseOfInteractionKey<Key extends InteractionKey> =
164
+ Key extends \`\${infer P}.\${string}\` ? Extract<P, PhaseName> : never;
165
+
166
+ type IdOfInteractionKey<Key extends InteractionKey> =
167
+ Key extends \`\${infer P}.\${infer I}\`
168
+ ? P extends PhaseName
169
+ ? Extract<I, InteractionIdForPhase<P>>
170
+ : never
171
+ : never;
172
+
173
+ export type BoardInteractions = InteractionKey;
174
+
175
+ /** Stage names declared in a phase. */
176
+ export type StageName<Phase extends PhaseName> = StageNamesOfDefinitionPhase<
177
+ GameDefinition,
178
+ Phase
179
+ >;
180
+
181
+ /** Union of zone ids authored in the workspace manifest. */
182
+ export type ZoneId = ManifestZoneId;
183
+
184
+ type CamelCase<S extends string> = S extends \`\${infer Head}-\${infer Tail}\`
185
+ ? \`\${Head}\${Capitalize<CamelCase<Tail>>}\`
186
+ : S;
187
+
188
+ /** JS-friendly keys for authored zones, e.g. "dev-hand" -> "devHand". */
189
+ export type WorkspaceZoneKey = CamelCase<ZoneId>;
190
+
191
+ type PlayerCardZoneId = {
192
+ [Z in (typeof literals.playerZoneIds)[number]]: (typeof literals.cardSetIdsByPlayerZoneId)[Z] extends readonly []
193
+ ? never
194
+ : Z;
195
+ }[(typeof literals.playerZoneIds)[number]];
196
+
197
+ type SharedCardZoneId = {
198
+ [Z in (typeof literals.sharedZoneIds)[number]]: (typeof literals.cardSetIdsBySharedZoneId)[Z] extends readonly []
199
+ ? never
200
+ : Z;
201
+ }[(typeof literals.sharedZoneIds)[number]];
202
+
203
+ type CardZoneId = PlayerCardZoneId | SharedCardZoneId;
204
+
205
+ /** JS-friendly keys for per-player zones that can contain cards. */
206
+ export type WorkspacePlayerCardZoneKey = CamelCase<PlayerCardZoneId>;
207
+
208
+ /** Interaction descriptor specialised to a concrete phase-qualified key. */
209
+ export type InteractionDescriptorFor<Key extends InteractionKey> =
210
+ InteractionDescriptor<Key>;
211
+
212
+ export type InteractionItem<Key extends InteractionKey> = {
213
+ readonly interaction: Key;
214
+ readonly descriptor: InteractionDescriptorFor<Key>;
215
+ };
216
+
217
+ /**
218
+ * Params shape for a phase-qualified interaction key. Drives strong typing
219
+ * for component-first interaction state, forms, and submits.
220
+ */
221
+ export type InteractionParamsOf<Key extends InteractionKey> =
222
+ InteractionParams<PhaseOfInteractionKey<Key>, IdOfInteractionKey<Key>>;
223
+
224
+ export type InteractionDefaultedKeysOf<Key extends InteractionKey> =
225
+ InteractionDefaultedKeys<PhaseOfInteractionKey<Key>, IdOfInteractionKey<Key>>;
226
+
227
+ type InteractionParamsShape<Key extends InteractionKey> =
228
+ InteractionParamsOf<Key> extends Record<string, unknown>
229
+ ? InteractionParamsOf<Key>
230
+ : Record<string, unknown>;
231
+
232
+ type InteractionCollectorKind =
233
+ | "form"
234
+ | "board-vertex"
235
+ | "board-edge"
236
+ | "board-tile"
237
+ | "board-space"
238
+ | "card"
239
+ | "prompt";
240
+
241
+ type AuthoredInteractionInputKeysOf<Key extends InteractionKey> =
242
+ InputKeysWithCollectorKindOfDefinition<
243
+ GameDefinition,
244
+ PhaseOfInteractionKey<Key>,
245
+ IdOfInteractionKey<Key>,
246
+ InteractionCollectorKind
247
+ > extends infer Input
248
+ ? string extends Input
249
+ ? never
250
+ : Input & string
251
+ : never;
252
+
253
+ type InteractionInputKeysOf<Key extends InteractionKey> =
254
+ | (string extends keyof InteractionParamsOf<Key>
255
+ ? never
256
+ : keyof InteractionParamsOf<Key> & string)
257
+ | AuthoredInteractionInputKeysOf<Key>
258
+ | (InteractionDefaultedKeysOf<Key> & string);
259
+
260
+ type InteractionHandleDefaultedKeys<Key extends InteractionKey> = Extract<
261
+ InteractionDefaultedKeysOf<Key>,
262
+ keyof InteractionParamsShape<Key> & string
263
+ >;
264
+
265
+ type RequiredInteractionInputKeysOf<Key extends InteractionKey> =
266
+ InteractionInputKeysOf<Key>;
267
+
268
+ export type RequiredInteractionInputKey<Key extends InteractionKey> =
269
+ RequiredInteractionInputKeysOf<Key>;
270
+
271
+ type InteractionSlotComponent<Props = object> = (
272
+ props: Props extends { children: unknown }
273
+ ? Props
274
+ : Props & { children?: ReactNode },
275
+ ) => ReactElement | null;
276
+
277
+ type InteractionDefaultInputSlot = {
278
+ readonly Default: InteractionSlotComponent;
279
+ };
280
+
281
+ type InteractionValueInputSlot<Value = unknown> = {
282
+ readonly Value: InteractionSlotComponent<{
283
+ children: (value: unknown | undefined) => ReactNode;
284
+ }>;
285
+ };
286
+
287
+ type InteractionFormInputSlot<Value = unknown> = {
288
+ readonly Field: InteractionSlotComponent;
289
+ readonly Options: InteractionSlotComponent<{
290
+ children: (option: { value: Value; label: string }) => ReactNode;
291
+ }>;
292
+ };
293
+
294
+ type DreamboardSlotBrand<Meta> = {
295
+ readonly __dreamboardSlot: Meta;
296
+ };
297
+
298
+ type InteractionCardTargetInputSlot<Card extends string = string> = {
299
+ readonly Card: InteractionSlotComponent<
300
+ { value: string } & Omit<
301
+ ButtonHTMLAttributes<HTMLButtonElement>,
302
+ | "children"
303
+ | "disabled"
304
+ | "aria-disabled"
305
+ | "aria-pressed"
306
+ | "onClick"
307
+ | "type"
308
+ | "value"
309
+ >
310
+ >;
311
+ readonly Cards: InteractionSlotComponent<{
312
+ children: (card: { id: Card }) => ReactNode;
313
+ }>;
314
+ };
315
+
316
+ type InteractionBoardTargetInputSlot<Target extends string = string> = {
317
+ readonly Target: InteractionSlotComponent<
318
+ { value: string } & Omit<
319
+ ButtonHTMLAttributes<HTMLButtonElement>,
320
+ | "children"
321
+ | "disabled"
322
+ | "aria-disabled"
323
+ | "aria-pressed"
324
+ | "onClick"
325
+ | "type"
326
+ | "value"
327
+ >
328
+ >;
329
+ };
330
+
331
+ type InteractionBoardSpaceTargetInputSlot<Space extends string = string> =
332
+ InteractionBoardTargetInputSlot<Space>;
333
+
334
+ type InteractionBoardEdgeTargetInputSlot<Edge extends string = string> =
335
+ InteractionBoardTargetInputSlot<Edge>;
336
+
337
+ type InteractionBoardVertexTargetInputSlot<Vertex extends string = string> =
338
+ InteractionBoardTargetInputSlot<Vertex>;
339
+
340
+ type InteractionBoardTileTargetInputSlot<Tile extends string = string> =
341
+ InteractionBoardTargetInputSlot<Tile>;
342
+
343
+ type InteractionSubmitSlot = {
344
+ readonly Button: InteractionSlotComponent<
345
+ Omit<
346
+ ButtonHTMLAttributes<HTMLButtonElement>,
347
+ "children" | "disabled" | "type" | "value"
348
+ >
349
+ >;
350
+ };
351
+
352
+ type InteractionInputValue<
353
+ Key extends InteractionKey,
354
+ Input extends InteractionInputKeysOf<Key>,
355
+ > = InteractionParamsShape<Key>[Input & keyof InteractionParamsShape<Key>];
356
+
357
+ type InteractionSlotValue<Value> = Value extends readonly (infer Item)[]
358
+ ? InteractionSlotValue<Item>
359
+ : Value extends { spaceId: infer Space extends string }
360
+ ? Space
361
+ : Value extends { edgeId: infer Edge extends string }
362
+ ? Edge
363
+ : Value extends { vertexId: infer Vertex extends string }
364
+ ? Vertex
365
+ : Value extends { tileId: infer Tile extends string }
366
+ ? Tile
367
+ : Value extends { cardId: infer Card extends string }
368
+ ? Card
369
+ : Value;
370
+
371
+ type InteractionSlotStringValue<Value> = Extract<
372
+ InteractionSlotValue<Value>,
373
+ string
374
+ >;
375
+
376
+ type FormInteractionInputKey<Key extends InteractionKey> =
377
+ InputKeysWithCollectorKindOfDefinition<
378
+ GameDefinition,
379
+ PhaseOfInteractionKey<Key>,
380
+ IdOfInteractionKey<Key>,
381
+ "form" | "prompt"
382
+ > &
383
+ InteractionInputKeysOf<Key>;
384
+
385
+ type CardTargetInteractionInputKey<Key extends InteractionKey> =
386
+ InputKeysWithCollectorKindOfDefinition<
387
+ GameDefinition,
388
+ PhaseOfInteractionKey<Key>,
389
+ IdOfInteractionKey<Key>,
390
+ "card"
391
+ > &
392
+ InteractionInputKeysOf<Key>;
393
+
394
+ type CardTargetZoneIds<
395
+ Key extends InteractionKey,
396
+ Input extends InteractionInputKeysOf<Key>,
397
+ > =
398
+ CardInputZoneIdsOfDefinition<
399
+ GameDefinition,
400
+ PhaseOfInteractionKey<Key>,
401
+ IdOfInteractionKey<Key>,
402
+ Input & string
403
+ > extends infer Zone extends string
404
+ ? Extract<Zone, WorkspaceZoneId>
405
+ : never;
406
+
407
+ type BoardSpaceTargetInteractionInputKey<Key extends InteractionKey> =
408
+ InputKeysWithCollectorKindOfDefinition<
409
+ GameDefinition,
410
+ PhaseOfInteractionKey<Key>,
411
+ IdOfInteractionKey<Key>,
412
+ "board-space"
413
+ > &
414
+ InteractionInputKeysOf<Key>;
415
+
416
+ type BoardEdgeTargetInteractionInputKey<Key extends InteractionKey> =
417
+ InputKeysWithCollectorKindOfDefinition<
418
+ GameDefinition,
419
+ PhaseOfInteractionKey<Key>,
420
+ IdOfInteractionKey<Key>,
421
+ "board-edge"
422
+ > &
423
+ InteractionInputKeysOf<Key>;
424
+
425
+ type BoardVertexTargetInteractionInputKey<Key extends InteractionKey> =
426
+ InputKeysWithCollectorKindOfDefinition<
427
+ GameDefinition,
428
+ PhaseOfInteractionKey<Key>,
429
+ IdOfInteractionKey<Key>,
430
+ "board-vertex"
431
+ > &
432
+ InteractionInputKeysOf<Key>;
433
+
434
+ type BoardTileTargetInteractionInputKey<Key extends InteractionKey> =
435
+ InputKeysWithCollectorKindOfDefinition<
436
+ GameDefinition,
437
+ PhaseOfInteractionKey<Key>,
438
+ IdOfInteractionKey<Key>,
439
+ "board-tile"
440
+ > &
441
+ InteractionInputKeysOf<Key>;
442
+
443
+ type InteractionDefaultSlotFor<
444
+ Key extends InteractionKey,
445
+ Input extends InteractionInputKeysOf<Key>,
446
+ > = Input extends InteractionHandleDefaultedKeys<Key>
447
+ ? InteractionDefaultInputSlot
448
+ : object;
449
+
450
+ export type InteractionInputSlot<
451
+ Key extends InteractionKey,
452
+ Input extends InteractionInputKeysOf<Key>,
453
+ > = InteractionValueInputSlot<InteractionInputValue<Key, Input>> &
454
+ InteractionDefaultSlotFor<Key, Input> &
455
+ (Input extends CardTargetInteractionInputKey<Key>
456
+ ? InteractionCardTargetInputSlot<
457
+ InteractionSlotStringValue<InteractionInputValue<Key, Input>>
458
+ >
459
+ : Input extends BoardSpaceTargetInteractionInputKey<Key>
460
+ ? InteractionBoardSpaceTargetInputSlot<
461
+ InteractionSlotStringValue<InteractionInputValue<Key, Input>>
462
+ >
463
+ : Input extends BoardEdgeTargetInteractionInputKey<Key>
464
+ ? InteractionBoardEdgeTargetInputSlot<
465
+ InteractionSlotStringValue<InteractionInputValue<Key, Input>>
466
+ >
467
+ : Input extends BoardVertexTargetInteractionInputKey<Key>
468
+ ? InteractionBoardVertexTargetInputSlot<
469
+ InteractionSlotStringValue<InteractionInputValue<Key, Input>>
470
+ >
471
+ : Input extends BoardTileTargetInteractionInputKey<Key>
472
+ ? InteractionBoardTileTargetInputSlot<
473
+ InteractionSlotStringValue<InteractionInputValue<Key, Input>>
474
+ >
475
+ : Input extends FormInteractionInputKey<Key>
476
+ ? InteractionFormInputSlot<InteractionInputValue<Key, Input>>
477
+ : never);
478
+
479
+ type FormSurfaceInputSlot<
480
+ Key extends InteractionKey,
481
+ Input extends FormInteractionInputKey<Key>,
482
+ > = InteractionValueInputSlot<InteractionInputValue<Key, Input>> &
483
+ InteractionDefaultSlotFor<Key, Input> &
484
+ InteractionFormInputSlot<InteractionInputValue<Key, Input>> &
485
+ DreamboardSlotBrand<{
486
+ readonly kind: "form";
487
+ readonly interaction: Key;
488
+ readonly input: Input;
489
+ }>;
490
+
491
+ type CardSurfaceInputSlot<Zones extends string> =
492
+ InteractionValueInputSlot<unknown> &
493
+ InteractionCardTargetInputSlot<WorkspaceCardId & string> &
494
+ DreamboardSlotBrand<{
495
+ readonly kind: "card";
496
+ readonly zones: Zones;
497
+ readonly selection: "one" | "many";
498
+ }>;
499
+
500
+ type BoardSurfaceInputSlot<
501
+ Kind extends "space" | "edge" | "vertex" | "tile",
502
+ Target extends string = string,
503
+ > =
504
+ InteractionValueInputSlot<unknown> &
505
+ InteractionBoardTargetInputSlot<Target> &
506
+ DreamboardSlotBrand<{
507
+ readonly kind: "board";
508
+ readonly targetKind: Kind;
509
+ }>;
510
+
511
+ type InteractionCollectorSlot<
512
+ Key extends InteractionKey,
513
+ Input extends InteractionInputKeysOf<Key>,
514
+ > = Input extends CardTargetInteractionInputKey<Key>
515
+ ? CardSurfaceInputSlot<CardTargetZoneIds<Key, Input>>
516
+ : Input extends BoardSpaceTargetInteractionInputKey<Key>
517
+ ? BoardSurfaceInputSlot<"space", string>
518
+ : Input extends BoardEdgeTargetInteractionInputKey<Key>
519
+ ? BoardSurfaceInputSlot<"edge", string>
520
+ : Input extends BoardVertexTargetInteractionInputKey<Key>
521
+ ? BoardSurfaceInputSlot<"vertex", string>
522
+ : Input extends BoardTileTargetInteractionInputKey<Key>
523
+ ? BoardSurfaceInputSlot<"tile", string>
524
+ : Input extends FormInteractionInputKey<Key>
525
+ ? FormSurfaceInputSlot<Key, Input>
526
+ : never;
527
+
528
+ type InteractionKeysWithInput<Input extends InteractionInputKey> = {
529
+ [Key in InteractionKey]: Input extends InteractionInputKeysOf<Key>
530
+ ? Key
531
+ : never;
532
+ }[InteractionKey];
533
+
534
+ type InteractionInputSlotByName<Input extends InteractionInputKey> =
535
+ InteractionInputSlot<
536
+ InteractionKeysWithInput<Input>,
537
+ Input & InteractionInputKeysOf<InteractionKeysWithInput<Input>>
538
+ >;
539
+
540
+ type InteractionFormInputSlotByName<Input extends InteractionInputKey> =
541
+ InteractionInputSlot<
542
+ {
543
+ [Key in InteractionKey]: Input extends FormInteractionInputKey<Key>
544
+ ? Key
545
+ : never;
546
+ }[InteractionKey],
547
+ Input &
548
+ InteractionInputKeysOf<
549
+ {
550
+ [Key in InteractionKey]: Input extends FormInteractionInputKey<Key>
551
+ ? Key
552
+ : never;
553
+ }[InteractionKey]
554
+ >
555
+ >;
556
+
557
+ type InteractionInputsForCollectorKind<
558
+ Key extends InteractionKey,
559
+ CollectorKind extends InteractionCollectorKind,
560
+ > = {
561
+ [Input in InteractionInputKeysOf<Key>]: CollectorKind extends "form"
562
+ ? Input extends FormInteractionInputKey<Key>
563
+ ? Input
564
+ : never
565
+ : CollectorKind extends "card"
566
+ ? Input extends CardTargetInteractionInputKey<Key>
567
+ ? Input
568
+ : never
569
+ : CollectorKind extends "board-space"
570
+ ? Input extends BoardSpaceTargetInteractionInputKey<Key>
571
+ ? Input
572
+ : never
573
+ : CollectorKind extends "board-edge"
574
+ ? Input extends BoardEdgeTargetInteractionInputKey<Key>
575
+ ? Input
576
+ : never
577
+ : CollectorKind extends "board-vertex"
578
+ ? Input extends BoardVertexTargetInteractionInputKey<Key>
579
+ ? Input
580
+ : never
581
+ : CollectorKind extends "board-tile"
582
+ ? Input extends BoardTileTargetInteractionInputKey<Key>
583
+ ? Input
584
+ : never
585
+ : never;
586
+ }[InteractionInputKeysOf<Key>];
587
+
588
+ type InteractionKeysForCollectorKind<
589
+ CollectorKind extends InteractionCollectorKind,
590
+ > = {
591
+ [Key in InteractionKey]: InteractionInputsForCollectorKind<
592
+ Key,
593
+ CollectorKind
594
+ > extends never
595
+ ? never
596
+ : Key;
597
+ }[InteractionKey];
598
+
599
+ type InteractionInputSlotByCollectorKind<
600
+ CollectorKind extends InteractionCollectorKind,
601
+ > = InteractionInputSlot<
602
+ InteractionKeysForCollectorKind<CollectorKind>,
603
+ InteractionInputsForCollectorKind<
604
+ InteractionKeysForCollectorKind<CollectorKind>,
605
+ CollectorKind
606
+ >
607
+ >;
608
+
609
+ export type InteractionFormSurface<Key extends InteractionKey> = {
610
+ readonly Root: InteractionSlotComponent;
611
+ readonly Form: DreamboardUI<typeof uiContract>["Interaction"]["Form"];
612
+ readonly Dialog: DreamboardUI<typeof uiContract>["Interaction"]["Dialog"];
613
+ readonly State: DreamboardUI<typeof uiContract>["Interaction"]["State"];
614
+ readonly Arm: DreamboardUI<typeof uiContract>["Interaction"]["Trigger"];
615
+ readonly Submit: DreamboardUI<typeof uiContract>["Interaction"]["Submit"];
616
+ readonly Field: <Input extends FormInteractionInputKey<Key>>(
617
+ props: { input: Input; children?: ReactNode },
618
+ ) => ReactElement | null;
619
+ readonly slot: {
620
+ readonly [Input in FormInteractionInputKey<Key>]: FormSurfaceInputSlot<
621
+ Key,
622
+ Input
623
+ >;
624
+ };
625
+ };
626
+
627
+ type BoardSurfaceSpaceProps<Target extends string> = {
628
+ value: Target;
629
+ children?: ReactNode;
630
+ } & Omit<BoardSpaceTargetPropsGeneric<Target>, "value" | "children">;
631
+
632
+ export type BoardSurfaceBoardId = BoardBaseId & string;
633
+
634
+ type BoardSurfaceRuntimeBoardId<Board extends BoardSurfaceBoardId> =
635
+ ReturnType<typeof boardHelpers.boardIdsForBase<Board>>[number] & string;
636
+
637
+ type BoardSurfaceSpaceId<Board extends BoardSurfaceBoardId> =
638
+ ReturnType<
639
+ typeof boardHelpers.spaceIds<BoardSurfaceRuntimeBoardId<Board>>
640
+ >[number] &
641
+ string;
642
+
643
+ type BoardSurfaceTiledBoardId<Board extends BoardSurfaceBoardId> = Extract<
644
+ BoardSurfaceRuntimeBoardId<Board>,
645
+ TiledBoardId
646
+ >;
647
+
648
+ type BoardSurfaceEdgeId<Board extends BoardSurfaceBoardId> =
649
+ TiledEdgeState<BoardSurfaceTiledBoardId<Board>> extends { id: infer Id }
650
+ ? Id & string
651
+ : EdgeId & string;
652
+
653
+ type BoardSurfaceVertexId<Board extends BoardSurfaceBoardId> =
654
+ TiledVertexState<BoardSurfaceTiledBoardId<Board>> extends { id: infer Id }
655
+ ? Id & string
656
+ : VertexId & string;
657
+
658
+ export type BoardSurface<Board extends BoardSurfaceBoardId = BoardSurfaceBoardId> = {
659
+ readonly Root: InteractionSlotComponent;
660
+ readonly Space: <Target extends BoardSurfaceSpaceId<Board>>(
661
+ props: BoardSurfaceSpaceProps<Target>,
662
+ ) => ReactElement;
663
+ readonly slot: {
664
+ readonly space: BoardSurfaceInputSlot<
665
+ "space",
666
+ BoardSurfaceSpaceId<Board>
667
+ >;
668
+ readonly playerSpace: BoardSurfaceInputSlot<
669
+ "space",
670
+ BoardSurfaceSpaceId<Board>
671
+ >;
672
+ readonly edge: BoardSurfaceInputSlot<"edge", BoardSurfaceEdgeId<Board>>;
673
+ readonly vertex: BoardSurfaceInputSlot<
674
+ "vertex",
675
+ BoardSurfaceVertexId<Board>
676
+ >;
677
+ readonly tile: BoardSurfaceInputSlot<"tile", BoardSurfaceSpaceId<Board>>;
678
+ };
679
+ };
680
+
681
+ type ZoneCardsComponent = InteractionSlotComponent<
682
+ Omit<ZoneListProps, "children" | "empty"> & {
683
+ empty?: ReactNode;
684
+ children: (card: WorkspaceZoneCard) => ReactNode;
685
+ }
686
+ >;
687
+
688
+ type ZoneCardComponent = InteractionSlotComponent<
689
+ Omit<ButtonHTMLAttributes<HTMLButtonElement>, "type" | "value"> & {
690
+ card: WorkspaceZoneCard;
691
+ }
692
+ >;
693
+
694
+ /**
695
+ * Generic SDK card-intent shape for typed hand emission. Authors do not
696
+ * import the runtime; the typed \`targetId\` is constrained by the
697
+ * authored interaction domain on the generated hand surface.
698
+ */
699
+ export type HandCardIntent<Card extends string, Target extends string> =
700
+ | { type: "activate"; cardId: Card; source: "tap" | "keyboard" }
701
+ | { type: "previewStart"; cardId: Card }
702
+ | { type: "previewEnd"; cardId: Card }
703
+ | {
704
+ type: "drop";
705
+ cardId: Card;
706
+ targetId: Target;
707
+ source: "pointer" | "keyboard";
708
+ };
709
+
710
+ export type HandLayoutKindGen = "fan" | "compressed-fan" | "tray" | "strip" | "stack";
711
+ export type HandLayoutPolicyGen = {
712
+ desktop?: HandLayoutKindGen;
713
+ mobile?: HandLayoutKindGen;
714
+ };
715
+ export type HandMobileInteractionPolicy = "direct-activate" | "drag-to-target";
716
+
717
+ export type HandStagingTarget<Card extends string> = {
718
+ readonly kind: "card";
719
+ readonly card: Card;
720
+ };
721
+
722
+ export type HandBoardDropTargetKind = "space" | "edge" | "vertex" | "tile";
723
+
724
+ /**
725
+ * Discriminated union that ties each board target kind to its manifest id
726
+ * family. \`tile\` shares the space-id family because tiles are addressed
727
+ * by their underlying space.
728
+ */
729
+ export type HandBoardDropTarget<
730
+ Space extends string,
731
+ Edge extends string,
732
+ Vertex extends string,
733
+ > =
734
+ | { readonly kind: "space"; readonly target: Space }
735
+ | { readonly kind: "edge"; readonly target: Edge }
736
+ | { readonly kind: "vertex"; readonly target: Vertex }
737
+ | { readonly kind: "tile"; readonly target: Space };
738
+
739
+ export type HandDropTargetSpec<
740
+ Card extends string,
741
+ Space extends string,
742
+ Edge extends string,
743
+ Vertex extends string,
744
+ > = HandStagingTarget<Card> | HandBoardDropTarget<Space, Edge, Vertex>;
745
+
746
+ /** Union of all manifest target ids the typed hand can carry. */
747
+ export type HandAuthoredTarget<
748
+ Card extends string,
749
+ Space extends string,
750
+ Edge extends string,
751
+ Vertex extends string,
752
+ > = Card | Space | Edge | Vertex;
753
+
754
+ /**
755
+ * Summary projected from the live draft for a single hand. Authors render
756
+ * counts and validity hints via \`renderSummary\` or mirror them to outside
757
+ * state via \`onSelectionSummary\`.
758
+ */
759
+ export type HandSelectionSummary<Card extends string> = {
760
+ readonly selectedCount: number;
761
+ readonly selectedIds: readonly Card[];
762
+ readonly hasInvalidSelection: boolean;
763
+ };
764
+
765
+ export type HandSurfaceProps<
766
+ Card extends string,
767
+ Space extends string,
768
+ Edge extends string,
769
+ Vertex extends string,
770
+ > = Omit<ZoneListProps, "children" | "empty"> & {
771
+ empty?: ReactNode;
772
+ /**
773
+ * Per-card render callback. Receives the projected
774
+ * \`InteractionVisualState\` (\`eligible\`, \`selected\`, \`invalid\`,
775
+ * \`disabled\`) so authors can wire SDK \`CardFace\` directly without
776
+ * recomputing draft state.
777
+ */
778
+ children: (
779
+ card: WorkspaceZoneCard,
780
+ state: InteractionVisualState,
781
+ ) => ReactNode;
782
+ layout?: HandLayoutKindGen | HandLayoutPolicyGen;
783
+ mobileInteraction?: HandMobileInteractionPolicy;
784
+ cardSize?: "sm" | "md" | "lg";
785
+ ariaLabel?: string;
786
+ /**
787
+ * Typed drop targets that participate in \`drag-to-target\` interaction.
788
+ * Each target's \`kind\` constrains the allowed \`target\` id family, so
789
+ * a \`SpaceId\` cannot be authored as an edge or vertex target.
790
+ */
791
+ dropTargets?: ReadonlyArray<{
792
+ target: HandDropTargetSpec<Card, Space, Edge, Vertex>;
793
+ label: string;
794
+ render: (state: unknown) => ReactNode;
795
+ className?: string;
796
+ role?: string;
797
+ order?: number;
798
+ }>;
799
+ renderDropTargets?: (children: ReactNode) => ReactNode;
800
+ onCardIntent?: (
801
+ intent: HandCardIntent<Card, HandAuthoredTarget<Card, Space, Edge, Vertex>>,
802
+ ) => void;
803
+ /**
804
+ * Render-safe slot for selection summary content. Fires during render
805
+ * with the latest projected summary so authors can compose count chrome
806
+ * (e.g. "3 of 5 selected") without owning effect timing. Use
807
+ * \`onSelectionSummary\` for analytics or external state mirrors that
808
+ * need an effect.
809
+ */
810
+ renderSummary?: (summary: HandSelectionSummary<Card>) => ReactNode;
811
+ /**
812
+ * Render-safe slot for the hand's commit/action chrome (e.g. a submit
813
+ * button for a many-select interaction). Receives the same live summary as
814
+ * \`renderSummary\`. Rendered below the hand inline on desktop and pinned as a
815
+ * sticky footer inside the mobile dock, so the action stays reachable while
816
+ * the hand is docked — prefer this over anchoring the action elsewhere on
817
+ * the board, where the dock would otherwise cover it.
818
+ */
819
+ renderActions?: (summary: HandSelectionSummary<Card>) => ReactNode;
820
+ /**
821
+ * Optional selection summary observer. Invoked from a layout effect so
822
+ * consumers may safely call \`setState\` in response.
823
+ */
824
+ onSelectionSummary?: (summary: HandSelectionSummary<Card>) => void;
825
+ };
826
+
827
+ type HandCardsComponent<
828
+ Card extends string,
829
+ Space extends string,
830
+ Edge extends string,
831
+ Vertex extends string,
832
+ > = InteractionSlotComponent<HandSurfaceProps<Card, Space, Edge, Vertex>>;
833
+
834
+ /**
835
+ * Always-visible staging surface for a many-select card collection (e.g. the
836
+ * cards you've chosen to pass): a fixed row of slots showing the staged cards,
837
+ * with empty placeholders otherwise. Tapping a staged card removes it from the
838
+ * collection (it returns to the hand). Renders nothing when the zone has no
839
+ * active many-select collection, so it can be mounted unconditionally.
840
+ */
841
+ export type HandStagingProps = {
842
+ // Staged cards are always visible (you can only stage a card you can see), so
843
+ // the slot renderer receives the hydrated card variant — no hidden-card guard.
844
+ children: (card: Extract<WorkspaceZoneCard, { hidden: false }>) => ReactNode;
845
+ label?: ReactNode;
846
+ renderEmptySlot?: (index: number) => ReactNode;
847
+ cardSize?: "sm" | "md" | "lg";
848
+ ariaLabel?: string;
849
+ className?: string;
850
+ };
851
+
852
+ type HandStagingComponent = InteractionSlotComponent<HandStagingProps>;
853
+
854
+ export type HandSurface<Zones extends readonly WorkspaceZoneId[] = readonly WorkspaceZoneId[]> = {
855
+ readonly Hand: HandCardsComponent<
856
+ CardId & string,
857
+ SpaceId & string,
858
+ EdgeId & string,
859
+ VertexId & string
860
+ >;
861
+ readonly Card: ZoneCardComponent;
862
+ readonly Staging: HandStagingComponent;
863
+ readonly slot: {
864
+ readonly card: CardSurfaceInputSlot<Zones[number] & string>;
865
+ };
866
+ };
867
+
868
+ export type PileSurface<Zones extends readonly WorkspaceZoneId[] = readonly WorkspaceZoneId[]> = {
869
+ readonly Pile: ZoneCardsComponent;
870
+ readonly Card: ZoneCardComponent;
871
+ };
872
+
873
+ export type CardCollectionSurface<Zones extends readonly WorkspaceZoneId[] = readonly WorkspaceZoneId[]> = {
874
+ readonly Collection: ZoneCardsComponent;
875
+ readonly Card: ZoneCardComponent;
876
+ readonly slot: {
877
+ readonly card: CardSurfaceInputSlot<Zones[number] & string>;
878
+ };
879
+ };
880
+
881
+ export type InteractionFormInputs<Key extends InteractionKey> = {
882
+ [Input in InteractionInputKeysOf<Key>]: (
883
+ slot: InteractionInputSlot<Key, Input>,
884
+ ) => ReactNode;
885
+ };
886
+
887
+ export type InteractionCollectSlots<Key extends InteractionKey> = {
888
+ [Input in RequiredInteractionInputKeysOf<Key>]: InteractionCollectorSlot<
889
+ Key,
890
+ Input
891
+ >;
892
+ };
893
+
894
+ export type InteractionRoute<Key extends InteractionKey> = {
895
+ readonly collect: InteractionCollectSlots<Key>;
896
+ };
897
+
898
+ export type InteractionRoutes = {
899
+ [Key in InteractionKey]: InteractionRoute<Key>;
900
+ };
901
+
902
+ export type InteractionRoutesProps = {
903
+ routes: InteractionRoutes;
904
+ fallback?: ReactNode;
905
+ includeUnavailable?: boolean | null;
906
+ };
907
+
908
+ export type ZeroInputInteractionKey = {
909
+ [K in InteractionKey]: InteractionInputKeysOf<K> extends never
910
+ ? K
911
+ : never;
912
+ }[InteractionKey];
913
+
914
+ export type InputInteractionKey = Exclude<
915
+ InteractionKey,
916
+ ZeroInputInteractionKey
917
+ >;
918
+
919
+ type InteractionInputKeyOf<Key extends InteractionKey> =
920
+ Key extends InteractionKey ? InteractionInputKeysOf<Key> : never;
921
+
922
+ export type InteractionInputKey = InteractionInputKeyOf<InteractionKey>;
923
+
924
+ export type PromptKey = InteractionKey;
925
+
926
+ export type PromptOptionValue = string;
927
+
928
+ export type BoardTargetId = SpaceId | EdgeId | VertexId;
929
+
930
+ type UIInteractionRegistry = {
931
+ [K in InteractionKey]: {
932
+ interaction: K;
933
+ phase: PhaseOfInteractionKey<K>;
934
+ id: IdOfInteractionKey<K>;
935
+ };
936
+ };
937
+
938
+ type UIInputRegistry = {
939
+ [K in InteractionInputKey]: { input: K };
940
+ };
941
+
942
+ type UIPromptRegistry = {
943
+ [K in PromptKey]: { interaction: K };
944
+ };
945
+
946
+ type UIPromptOptionRegistry = {
947
+ [K in PromptOptionValue]: { value: K };
948
+ };
949
+
950
+ type UIPlayerRegistry = {
951
+ [K in PlayerId & string]: { player: K };
952
+ };
953
+
954
+ type UIZoneRegistry = {
955
+ [K in ZoneId & string]: { zone: K };
956
+ };
957
+
958
+ type UICardRegistry = {
959
+ [K in CardId & string]: { card: K };
960
+ };
961
+
962
+ type UIPhaseRegistry = {
963
+ [K in PhaseName & string]: { phase: K };
964
+ };
965
+
966
+ type UIBoardTargetRegistry = {
967
+ [K in BoardTargetId & string]: { target: K };
968
+ };
969
+
970
+ export const uiContract = {
971
+ interactions: {} as UIInteractionRegistry,
972
+ inputs: {} as UIInputRegistry,
973
+ prompts: {} as UIPromptRegistry,
974
+ promptOptions: {} as UIPromptOptionRegistry,
975
+ players: {} as UIPlayerRegistry,
976
+ zones: {} as UIZoneRegistry,
977
+ cards: {} as UICardRegistry,
978
+ phases: {} as UIPhaseRegistry,
979
+ boardTargets: {} as UIBoardTargetRegistry,
980
+ } satisfies UIContract;
981
+
982
+ declare module "@dreamboard-games/sdk/generated/runtime" {
983
+ interface DreamboardUIRegister {
984
+ contract: GameDefinition;
985
+ ui: typeof uiContract;
986
+ }
987
+ }
988
+
989
+ // -------------------------------------------------------------------------
990
+ // Typed hex-board view adapter
991
+ // -------------------------------------------------------------------------
992
+
993
+ /** Generated hex-board topology source, keyed by hex-board id. */
994
+ const hexStaticBoards = staticBoards.hex;
995
+
996
+ /** Union of authored hex-board ids in this workspace's manifest. */
997
+ export type HexBoardId = keyof typeof hexStaticBoards & string;
998
+
999
+ /** Topology object for the named hex board, drawn from \`staticBoards.hex\`. */
1000
+ export type HexBoardTopology<Id extends HexBoardId> = (typeof hexStaticBoards)[Id];
1001
+
1002
+ /** Space id type for the named hex board. */
1003
+ export type HexBoardSpaceId<Id extends HexBoardId> = BoardSpaceIdOf<
1004
+ HexBoardTopology<Id>
1005
+ >;
1006
+
1007
+ export type HexBoardViewProps<
1008
+ Id extends HexBoardId,
1009
+ TSpaceView extends { id: HexBoardSpaceId<Id> },
1010
+ > = Omit<BoardHexViewPropsGeneric<HexBoardTopology<Id>, TSpaceView>, "board"> & {
1011
+ board: Id;
1012
+ };
1013
+
1014
+ export type HexBoardGridProps<
1015
+ Id extends HexBoardId,
1016
+ TSpaceView extends { id: HexBoardSpaceId<Id> },
1017
+ > = Omit<
1018
+ BoardHexGridPropsGeneric<HexBoardTopology<Id>, TSpaceView>,
1019
+ "board" | "interactions"
1020
+ > & {
1021
+ board: Id;
1022
+ };
1023
+
1024
+ type WorkspaceBoard = {
1025
+ surface<const Board extends BoardSurfaceBoardId>(
1026
+ board: Board,
1027
+ ): WorkspaceBoardSurfaceDescriptor<Board>;
1028
+ useSurface(name: string): BoardSurface;
1029
+ useSurface<const Board extends BoardSurfaceBoardId>(
1030
+ name: string,
1031
+ options: { board: Board },
1032
+ ): BoardSurface<Board>;
1033
+ HexView<
1034
+ const Id extends HexBoardId,
1035
+ const TSpaceView extends { id: HexBoardSpaceId<Id> },
1036
+ >(props: HexBoardViewProps<Id, TSpaceView>): ReactElement;
1037
+ HexGrid<
1038
+ const Id extends HexBoardId,
1039
+ const TSpaceView extends { id: HexBoardSpaceId<Id> },
1040
+ >(props: HexBoardGridProps<Id, TSpaceView>): ReactElement;
1041
+ };
1042
+
1043
+ type WorkspaceCardProperties = CardProperties extends Record<string, unknown>
1044
+ ? CardProperties
1045
+ : Record<string, unknown>;
1046
+ type WorkspaceZoneId = [ZoneId] extends [never] ? string : ZoneId;
1047
+ type WorkspaceCardId = [CardId] extends [never] ? string : CardId;
1048
+ type WorkspaceCardType = [CardType] extends [never] ? string : CardType;
1049
+
1050
+ // ZoneCardRenderItem is a discriminated union (hidden: true | false).
1051
+ // A bare Omit<..., "zone"> would collapse the union to its common keys and
1052
+ // strip cardType / properties from the hydrated branch, defeating the
1053
+ // discriminated-union narrowing the SDK contract promises. Distribute Omit
1054
+ // across the union so each branch keeps its own shape.
1055
+ type WorkspaceZoneCard = ZoneCardRenderItem<
1056
+ WorkspaceCardId & string,
1057
+ WorkspaceCardType & string,
1058
+ WorkspaceCardProperties
1059
+ > extends infer Item
1060
+ ? Item extends { zone: string }
1061
+ ? Omit<Item, "zone"> & { zone: WorkspaceZoneId }
1062
+ : never
1063
+ : never;
1064
+
1065
+ type HandRole = "primary" | "auxiliary" | "task";
1066
+
1067
+ type PilesSurfaceValue<Zones extends readonly CardZoneId[]> = {
1068
+ readonly [Zone in Zones[number]]: PileSurface<readonly [Zone]>;
1069
+ };
1070
+
1071
+ type InteractionFormsSurfaceValue<
1072
+ Forms extends Readonly<Record<string, InteractionKey>>,
1073
+ > = {
1074
+ readonly [Key in keyof Forms]: Forms[Key] extends InteractionKey
1075
+ ? InteractionFormSurface<Forms[Key]>
1076
+ : never;
1077
+ };
1078
+
1079
+ type WorkspaceSurfaceValue<Spec> =
1080
+ Spec extends WorkspaceBoardSurfaceDescriptor<infer Board extends BoardSurfaceBoardId>
1081
+ ? BoardSurface<Board>
1082
+ : Spec extends WorkspaceHandSurfaceDescriptor<infer Zone extends PlayerCardZoneId>
1083
+ ? HandSurface<readonly [Zone]>
1084
+ : Spec extends WorkspacePileSurfaceDescriptor<infer Zone extends CardZoneId>
1085
+ ? PileSurface<readonly [Zone]>
1086
+ : Spec extends WorkspacePilesSurfaceDescriptor<
1087
+ infer Zones extends readonly CardZoneId[]
1088
+ >
1089
+ ? PilesSurfaceValue<Zones>
1090
+ : Spec extends WorkspaceCardCollectionSurfaceDescriptor<
1091
+ infer Zones extends readonly CardZoneId[]
1092
+ >
1093
+ ? CardCollectionSurface<Zones>
1094
+ : Spec extends WorkspaceInteractionFormDescriptor<
1095
+ infer Interaction extends InteractionKey
1096
+ >
1097
+ ? InteractionFormSurface<Interaction>
1098
+ : Spec extends WorkspaceInteractionFormsDescriptor<
1099
+ infer Forms extends Readonly<Record<string, InteractionKey>>
1100
+ >
1101
+ ? InteractionFormsSurfaceValue<Forms>
1102
+ : Spec extends WorkspaceSurfaceSpec
1103
+ ? { readonly [Key in keyof Spec]: WorkspaceSurfaceValue<Spec[Key]> }
1104
+ : never;
1105
+
1106
+ type WorkspaceZone = {
1107
+ hand<const Zone extends PlayerCardZoneId>(
1108
+ zone: Zone,
1109
+ options: { role: HandRole; label: string; order?: number },
1110
+ ): WorkspaceHandSurfaceDescriptor<Zone>;
1111
+ pile<const Zone extends CardZoneId>(
1112
+ zone: Zone,
1113
+ ): WorkspacePileSurfaceDescriptor<Zone>;
1114
+ piles<const Zones extends readonly CardZoneId[]>(
1115
+ zones: Zones,
1116
+ ): WorkspacePilesSurfaceDescriptor<Zones>;
1117
+ collection<const Zones extends readonly CardZoneId[]>(
1118
+ zones: Zones,
1119
+ options?: { mode?: "all" | "top-card" },
1120
+ ): WorkspaceCardCollectionSurfaceDescriptor<Zones>;
1121
+ useHand<const Zone extends PlayerCardZoneId>(
1122
+ name: string,
1123
+ options: { zone: Zone; role: HandRole; label: string; order?: number },
1124
+ ): HandSurface<readonly [Zone]>;
1125
+ usePile<const Zone extends CardZoneId>(
1126
+ name: string,
1127
+ options: { zone: Zone },
1128
+ ): PileSurface<readonly [Zone]>;
1129
+ useCardCollection<const Zones extends readonly CardZoneId[]>(
1130
+ name: string,
1131
+ options: { zones: Zones; mode?: "all" | "top-card" },
1132
+ ): CardCollectionSurface<Zones>;
1133
+ };
1134
+
1135
+ type WorkspaceUI = Omit<
1136
+ DreamboardUI<typeof uiContract>,
1137
+ "Root" | "Game" | "Interaction" | "Board" | "Zone" | "Prompt" | "PromptInbox"
1138
+ > & {
1139
+ Root(props: UIRootProps): ReactElement;
1140
+ defineSurfaces<const Spec extends WorkspaceSurfaceSpec>(
1141
+ spec: Spec,
1142
+ ): () => WorkspaceSurfaceValue<Spec>;
1143
+ readonly Game: TypedGame<typeof uiContract, GameView, PlayerId, PhaseName>;
1144
+ readonly Interaction: Pick<DreamboardUI<typeof uiContract>["Interaction"], "State" | "Dialog"> & {
1145
+ useForm<Key extends InteractionKey>(interaction: Key): InteractionFormSurface<Key>;
1146
+ form<const Interaction extends InteractionKey>(
1147
+ interaction: Interaction,
1148
+ ): WorkspaceInteractionFormDescriptor<Interaction>;
1149
+ forms<const Interactions extends Readonly<Record<string, InteractionKey>>>(
1150
+ interactions: Interactions,
1151
+ ): WorkspaceInteractionFormsDescriptor<Interactions>;
1152
+ Routes(props: InteractionRoutesProps): ReactElement;
1153
+ };
1154
+ readonly Board: WorkspaceBoard;
1155
+ readonly Zone: WorkspaceZone;
1156
+ readonly ResourceCounter: ResourceCounterComponents<ResourceId>;
1157
+ };
1158
+
1159
+ function formInputKeysForInteraction(interaction: string): Set<string> {
1160
+ const [phase, id] = interaction.split(".", 2);
1161
+ const phaseSpec = phase ? (game.phases as Record<string, unknown>)[phase] : undefined;
1162
+ const phaseRecord = phaseSpec as {
1163
+ interactions?: Record<string, { inputs?: Record<string, { kind?: string }> }>;
1164
+ cardActions?: Record<string, { inputs?: Record<string, { kind?: string }> }>;
1165
+ submit?: { inputs?: Record<string, { kind?: string }> };
1166
+ } | undefined;
1167
+ const spec =
1168
+ (id ? phaseRecord?.interactions?.[id] : undefined) ??
1169
+ (id ? phaseRecord?.cardActions?.[id] : undefined) ??
1170
+ (id === "submit" ? phaseRecord?.submit : undefined);
1171
+ return new Set(
1172
+ Object.entries((spec?.inputs ?? {}) as Record<string, { kind?: string }>)
1173
+ .filter(
1174
+ ([, collector]) =>
1175
+ collector.kind === "form" || collector.kind === "prompt",
1176
+ )
1177
+ .map(([input]) => input),
1178
+ );
1179
+ }
1180
+
1181
+ export const UI = createWorkspaceUIContract<
1182
+ WorkspaceUI,
1183
+ typeof uiContract,
1184
+ ResourceId,
1185
+ WorkspaceZoneCard,
1186
+ typeof hexStaticBoards
1187
+ >({
1188
+ uiContract,
1189
+ clientParamSchemasByPhase: createClientParamSchemasByPhase(game) as ClientParamSchemaMap,
1190
+ formInputKeysForInteraction,
1191
+ resourceIds: literals.resourceIds as readonly ResourceId[],
1192
+ resourcePresentationById: literals.resourcePresentationById as Partial<
1193
+ Record<string, { label?: string; icon?: string }>
1194
+ >,
1195
+ hexStaticBoards,
1196
+ cardIdFromZoneCard: (card) => card.id,
1197
+ zoneIdFromZoneCard: (card) => card.zone,
1198
+ });
1199
+ export const Board: WorkspaceUI["Board"] = UI.Board;
1200
+ export const Zone: WorkspaceUI["Zone"] = UI.Zone;
1201
+ export const Game: WorkspaceUI["Game"] = UI.Game;
1202
+ export const Interaction = UI.Interaction;
1203
+ export const PlayerRoster: WorkspaceUI["PlayerRoster"] = UI.PlayerRoster;
1204
+ export const Dice: WorkspaceUI["Dice"] = UI.Dice;
1205
+ export const Phase = UI.Phase;
1206
+ export const ResourceCounter: WorkspaceUI["ResourceCounter"] = UI.ResourceCounter;
1207
+
1208
+ export const clientParamSchemasByPhase = createClientParamSchemasByPhase(
1209
+ game,
1210
+ ) as ClientParamSchemaMap;
1211
+ `;
1212
+ }
1213
+
1214
+ function generateReducerSupportSeed(): string {
1215
+ return `import {
1216
+ createReducerEdit,
1217
+ createStateQueries,
1218
+ } from "@dreamboard-games/sdk/reducer";
1219
+ import type { GameState } from "./game-contract";
1220
+
1221
+ /**
1222
+ * Small, SDK-shaped reducer helpers belong here.
1223
+ *
1224
+ * Keep schema and contract declarations in app/game-contract.ts.
1225
+ * Keep initial state callbacks and defineGame wiring in app/game.ts.
1226
+ * Keep memoized aggregates (winner checks, VP totals, longest-road,
1227
+ * largest-army) in app/derived.ts via \`defineDerived\`.
1228
+ * Keep phase files focused on one game-flow state.
1229
+ *
1230
+ * When this file starts collecting real game rules, split them by domain
1231
+ * under app/rules/ instead, for example app/rules/board.ts,
1232
+ * app/rules/resources.ts, or app/rules/scoring.ts.
1233
+ *
1234
+ * Recommended authoring pattern inside a phase reducer:
1235
+ *
1236
+ * const tx = edit(state);
1237
+ * tx.setActivePlayers([q.players.order()[0]]);
1238
+ * return accept(tx.state);
1239
+ */
1240
+
1241
+ export const edit = createReducerEdit<GameState>();
1242
+
1243
+ export function stateQueries(state: GameState) {
1244
+ return createStateQueries(state);
1245
+ }
1246
+ `;
1247
+ }
1248
+
1249
+ function generateAppReadmeSeed(): string {
1250
+ return `# App organization
1251
+
1252
+ This directory owns the reducer: game state schemas, phases, interactions,
1253
+ derived values, player views, and reducer helpers.
1254
+
1255
+ Use this layout while the game is small:
1256
+
1257
+ \`\`\`txt
1258
+ app/
1259
+ game-contract.ts
1260
+ game.ts
1261
+ derived.ts
1262
+ reducer-support.ts
1263
+ phases/
1264
+ setup.ts
1265
+ \`\`\`
1266
+
1267
+ When a phase grows beyond a single idea, make the phase directory the table of
1268
+ contents and split the implementation by game concept:
1269
+
1270
+ \`\`\`txt
1271
+ app/phases/player-turn/
1272
+ index.ts # definePhase assembly only
1273
+ state.ts # phase-local constants and types
1274
+ inputs.ts # shared input and presentation helpers
1275
+ build.ts # build interactions
1276
+ trade.ts # trade interactions
1277
+ end-turn.ts # end-turn interaction
1278
+ \`\`\`
1279
+
1280
+ Split a phase when it has multiple action families, shared input helpers, card
1281
+ actions, or is roughly 250-300 lines. Keep \`index.ts\` as the assembly point
1282
+ that imports interactions and registers them with \`definePhase\`.
1283
+
1284
+ Do not turn \`reducer-support.ts\` into a catch-all rule module. Keep it small
1285
+ for shared reducer plumbing; put real game rules under \`app/rules/*\` once
1286
+ there is more than one domain.
1287
+ `;
1288
+ }
1289
+
1290
+ function generateReducerDerivedSeed(): string {
1291
+ return `// Memoized, pure projections of reducer state. Read them from reducer
1292
+ // callbacks and view projections via the injected \`derived\` helper:
1293
+ //
1294
+ // reduce({ state, derived, accept }) {
1295
+ // const winner = derived(winnerOf);
1296
+ // return accept({ ...state, publicState: { ...state.publicState, winnerPlayerId: winner } });
1297
+ // }
1298
+ //
1299
+ // Do NOT mirror derived values back into \`publicState\`. Keep the raw
1300
+ // inputs (component locations, zone contents, counters) in state and
1301
+ // express aggregates here.
1302
+ //
1303
+ // Uncomment the example below once you have something to derive.
1304
+
1305
+ // import { defineDerived } from "../../reducer.js";
1306
+ // import type { GameContract } from "./game-contract";
1307
+ //
1308
+ // export const winnerOf = defineDerived<GameContract>()({
1309
+ // name: "winnerOf",
1310
+ // compute: ({ state }) => {
1311
+ // // Example: return the first player at or above the VP target.
1312
+ // return state.publicState.winnerPlayerId ?? null;
1313
+ // },
1314
+ // });
1315
+
1316
+ export {};
1317
+ `;
1318
+ }
1319
+
1320
+ function generateReducerGameContractSeed(): string {
1321
+ return `import { z } from "zod";
1322
+ import { ids, manifestContract } from "../shared/manifest-contract";
1323
+ import { defineGameContract, type GameStateOf } from "@dreamboard-games/sdk/reducer";
1324
+
1325
+ // Contract files should stay focused on schemas, phases, and exported
1326
+ // state types. Put reducers in app/phases/* and pure computations in
1327
+ // app/derived.ts or app/rules/*.
1328
+ const publicStateSchema = z.object({
1329
+ currentPlayerId: ids.playerId.nullable(),
1330
+ notesByPlayerId: z.partialRecord(ids.playerId, z.string()).default({}),
1331
+ });
1332
+ const privateStateSchema = z.object({});
1333
+ const hiddenStateSchema = z.object({});
1334
+
1335
+ export const gameContract = defineGameContract({
1336
+ manifest: manifestContract,
1337
+ state: {
1338
+ public: publicStateSchema,
1339
+ private: privateStateSchema,
1340
+ hidden: hiddenStateSchema,
1341
+ },
1342
+ phases: {
1343
+ setup: z.object({}),
1344
+ },
1345
+ });
1346
+
1347
+ export type GameContract = typeof gameContract;
1348
+ export type GameState = GameStateOf<GameContract>;
1349
+ `;
1350
+ }
1351
+
1352
+ function generateReducerGameSeed(): string {
1353
+ return `import { defineGame } from "@dreamboard-games/sdk/reducer";
1354
+ import { gameContract } from "./game-contract";
1355
+ import { phases } from "./phases";
1356
+ import setupProfiles from "./setup-profiles";
1357
+
1358
+ // Keep this file as the defineGame assembly point. Put rules in phases,
1359
+ // projections in views/derived modules, and reusable domain helpers in
1360
+ // app/rules/* once reducer-support.ts is no longer small.
1361
+ export default defineGame({
1362
+ contract: gameContract,
1363
+ initial: {
1364
+ public: ({ playerIds }) => ({
1365
+ currentPlayerId: playerIds[0] ?? null,
1366
+ notesByPlayerId: {},
1367
+ }),
1368
+ private: () => ({}),
1369
+ hidden: () => ({}),
1370
+ },
1371
+ initialPhase: "setup",
1372
+ setupProfiles,
1373
+ phases,
1374
+ views: {},
1375
+ });
1376
+ `;
1377
+ }
1378
+
1379
+ function generateSetupProfilesSeed(manifest: GameTopologyManifest): string {
1380
+ const setupProfiles = manifest.setupProfiles ?? [];
1381
+ const setupProfileEntries =
1382
+ setupProfiles.length === 0
1383
+ ? ""
1384
+ : `{\n${setupProfiles
1385
+ .map((profile) => ` ${JSON.stringify(profile.id)}: {},`)
1386
+ .join("\n")}\n}`;
1387
+
1388
+ return `// ${SETUP_PROFILES_SEED_MARKER}
1389
+ import { setupProfiles } from "../shared/manifest-contract";
1390
+
1391
+ export default setupProfiles(${setupProfileEntries || "{}"});
1392
+ `;
1393
+ }
1394
+
1395
+ export function isFrameworkOwnedSetupProfilesSeed(
1396
+ content: string | null | undefined,
1397
+ ): boolean {
1398
+ if (content === null || content === undefined) {
1399
+ return false;
1400
+ }
1401
+ const trimmed = content.trim();
1402
+ if (trimmed.length === 0) {
1403
+ return true;
1404
+ }
1405
+ if (trimmed.includes(SETUP_PROFILES_SEED_MARKER)) {
1406
+ return true;
1407
+ }
1408
+ return false;
1409
+ }
1410
+
1411
+ function generateSetupPhaseSeed(): string {
1412
+ return `import { z } from "zod";
1413
+ import type { GameContract, GameState } from "../game-contract";
1414
+ import {
1415
+ defineInteraction,
1416
+ definePhase,
1417
+ formInput,
1418
+ } from "@dreamboard-games/sdk/reducer";
1419
+ import { edit } from "../reducer-support";
1420
+
1421
+ const setupMoodChoices = [
1422
+ { value: "ready", label: "Ready" },
1423
+ { value: "exploring", label: "Exploring" },
1424
+ ] as const;
1425
+ type SetupMood = (typeof setupMoodChoices)[number]["value"];
1426
+ const setupPhaseStateSchema = z.object({});
1427
+
1428
+ // A single phase file is fine while the phase is small. When a phase grows
1429
+ // multiple action families, move it to app/phases/<phase>/index.ts and split
1430
+ // interactions into neighboring concept files.
1431
+ export const setup = definePhase<GameContract>()({
1432
+ kind: "player",
1433
+ state: setupPhaseStateSchema,
1434
+ initialState: () => ({}),
1435
+ actor: ({ state }) => state.publicState.currentPlayerId,
1436
+ interactions: {
1437
+ submit: defineInteraction<GameContract, typeof setupPhaseStateSchema>()({
1438
+ inputs: {
1439
+ mood: formInput.choice<SetupMood, GameState>({
1440
+ choices: setupMoodChoices,
1441
+ defaultValue: "ready",
1442
+ }),
1443
+ },
1444
+ reduce({ state, input, accept }) {
1445
+ const tx = edit(state);
1446
+ tx.patchPublicState({
1447
+ notesByPlayerId: {
1448
+ ...state.publicState.notesByPlayerId,
1449
+ [input.playerId]: input.params.mood,
1450
+ },
1451
+ });
1452
+ return accept(tx.state);
1453
+ },
1454
+ }),
1455
+ },
1456
+ });
1457
+ `;
1458
+ }
1459
+
1460
+ function generatePhaseIndexSeed(): string {
1461
+ return `import { setup } from "./setup";
1462
+ import type { GameContract } from "../game-contract";
1463
+ import type { PhaseMapOf } from "@dreamboard-games/sdk/reducer";
1464
+
1465
+ export const phases = {
1466
+ setup,
1467
+ } satisfies PhaseMapOf<GameContract>;
1468
+ `;
1469
+ }
1470
+
1471
+ function generateReducerAppIndex(): string {
1472
+ return `import game from "./game";
1473
+ import { createReducerBundle } from "@dreamboard-games/sdk/reducer";
1474
+
1475
+ export default createReducerBundle(game);
1476
+ `;
1477
+ }
1478
+
1479
+ function generateAppFrameworkTsConfig(): string {
1480
+ return `${JSON.stringify(
1481
+ {
1482
+ compilerOptions: {
1483
+ target: "ES2020",
1484
+ module: "ESNext",
1485
+ moduleResolution: "bundler",
1486
+ strict: true,
1487
+ esModuleInterop: true,
1488
+ skipLibCheck: false,
1489
+ types: [],
1490
+ declaration: false,
1491
+ rootDir: "..",
1492
+ outDir: "./dist",
1493
+ paths: {
1494
+ "@dreamboard/manifest-contract": ["../shared/manifest-contract.ts"],
1495
+ "#dreamboard/ui-contract": ["../shared/generated/ui-contract.ts"],
1496
+ "@shared/*": ["../shared/*"],
1497
+ },
1498
+ },
1499
+ include: [
1500
+ "./**/*.ts",
1501
+ "./**/*.d.ts",
1502
+ "../shared/manifest-*.ts",
1503
+ "../shared/manifest-*.d.ts",
1504
+ ],
1505
+ exclude: ["node_modules", "dist", "../shared/generated"],
1506
+ },
1507
+ null,
1508
+ 2,
1509
+ )}\n`;
1510
+ }
1511
+
1512
+ function generateUiFrameworkTsConfig(): string {
1513
+ return `${JSON.stringify(
1514
+ {
1515
+ compilerOptions: {
1516
+ target: "ES2020",
1517
+ module: "ESNext",
1518
+ moduleResolution: "bundler",
1519
+ jsx: "react-jsx",
1520
+ strict: true,
1521
+ esModuleInterop: true,
1522
+ skipLibCheck: false,
1523
+ types: [],
1524
+ paths: {
1525
+ "@dreamboard/manifest-contract": ["../shared/manifest-contract.ts"],
1526
+ "#dreamboard/ui-contract": ["../shared/generated/ui-contract.ts"],
1527
+ "@shared/*": ["../shared/*"],
1528
+ },
1529
+ },
1530
+ include: ["./**/*.ts", "./**/*.tsx", "../shared/**/*.ts"],
1531
+ exclude: ["node_modules", "dist"],
1532
+ },
1533
+ null,
1534
+ 2,
1535
+ )}\n`;
1536
+ }
1537
+
1538
+ function generateReducerUiAppContent(): string {
1539
+ return `import { Game, Interaction, Phase, UI } from "#dreamboard/ui-contract";
1540
+ import { SetupInteractionRoutes } from "./interaction-routes";
1541
+ import { SetupScreen } from "./setup-screen";
1542
+
1543
+ function SetupPhase() {
1544
+ const setupForm = Interaction.useForm("setup.submit");
1545
+
1546
+ return (
1547
+ <>
1548
+ <SetupScreen />
1549
+ <SetupInteractionRoutes form={setupForm} />
1550
+ </>
1551
+ );
1552
+ }
1553
+
1554
+ export default function App() {
1555
+ return (
1556
+ <UI.Root>
1557
+ <Game.Root>
1558
+ {() => (
1559
+ <Phase.Switch
1560
+ routes={{
1561
+ setup: () => <SetupPhase />,
1562
+ }}
1563
+ />
1564
+ )}
1565
+ </Game.Root>
1566
+ </UI.Root>
1567
+ );
1568
+ }
1569
+ `;
1570
+ }
1571
+
1572
+ function generateReducerUiSetupScreenContent(): string {
1573
+ return `import { PANEL_CLASS, SHELL_CLASS } from "./styles";
1574
+
1575
+ export function SetupScreen() {
1576
+ return (
1577
+ <main className={SHELL_CLASS}>
1578
+ <section className={PANEL_CLASS}>
1579
+ <p className="text-xs font-bold uppercase tracking-[0.14em] text-slate-500">
1580
+ Setup
1581
+ </p>
1582
+ <h1 className="text-2xl font-bold text-slate-950">Your game starts here</h1>
1583
+ <p className="max-w-prose text-sm leading-6 text-slate-600">
1584
+ Replace this screen with your board, hands, mats, and table state.
1585
+ Keep interaction wiring in the route file so visual components stay
1586
+ focused on layout and styling.
1587
+ </p>
1588
+ </section>
1589
+ </main>
1590
+ );
1591
+ }
1592
+ `;
1593
+ }
1594
+
1595
+ function generateReducerUiInteractionRoutesContent(): string {
1596
+ return `import {
1597
+ Interaction,
1598
+ type InteractionFormSurface,
1599
+ } from "#dreamboard/ui-contract";
1600
+ import { ACTION_BUTTON_CLASS, PANEL_CLASS } from "./styles";
1601
+
1602
+ export function SetupInteractionRoutes({
1603
+ form,
1604
+ }: {
1605
+ form: InteractionFormSurface<"setup.submit">;
1606
+ }) {
1607
+ return (
1608
+ <>
1609
+ <Interaction.Routes
1610
+ routes={{
1611
+ "setup.submit": {
1612
+ collect: {
1613
+ mood: form.slot.mood,
1614
+ },
1615
+ },
1616
+ }}
1617
+ />
1618
+ <aside className={PANEL_CLASS}>
1619
+ <div className="grid gap-2">
1620
+ <p className="text-sm font-bold text-slate-950">Ready check</p>
1621
+ <form.slot.mood.Field />
1622
+ <form.Submit className={ACTION_BUTTON_CLASS}>
1623
+ Start building
1624
+ </form.Submit>
1625
+ </div>
1626
+ </aside>
1627
+ </>
1628
+ );
1629
+ }
1630
+ `;
1631
+ }
1632
+
1633
+ function generateReducerUiStylesContent(): string {
1634
+ return `export const SHELL_CLASS =
1635
+ "min-h-screen bg-[#f8fafc] p-6 text-slate-950";
1636
+
1637
+ export const PANEL_CLASS =
1638
+ "mx-auto grid max-w-3xl gap-4 rounded-lg border border-slate-200 bg-white p-5 shadow-sm";
1639
+
1640
+ export const ACTION_BUTTON_CLASS =
1641
+ "rounded-md bg-slate-950 px-4 py-2 text-sm font-bold text-white disabled:cursor-not-allowed disabled:opacity-50";
1642
+ `;
1643
+ }
1644
+
1645
+ function generateReducerUiContractTypingSmokeContent(): string {
1646
+ return `import { Interaction } from "#dreamboard/ui-contract";
1647
+
1648
+ export function UiContractTypingSmoke() {
1649
+ const form = Interaction.useForm("setup.submit");
1650
+
1651
+ return (
1652
+ <Interaction.Routes
1653
+ routes={{
1654
+ "setup.submit": {
1655
+ collect: {
1656
+ mood: form.slot.mood,
1657
+ },
1658
+ },
1659
+ }}
1660
+ />
1661
+ );
1662
+ }
1663
+
1664
+ // @ts-expect-error generated contracts hide legacy interaction roots.
1665
+ Interaction.Root({ interaction: "setup.submit", children: null });
1666
+ `;
1667
+ }
1668
+
1669
+ export function generateSeedFiles(
1670
+ manifest: GameTopologyManifest,
1671
+ ): Record<string, string> {
1672
+ return {
1673
+ "app/README.md": generateAppReadmeSeed(),
1674
+ "ui/App.tsx": generateReducerUiAppContent(),
1675
+ "ui/interaction-routes.tsx": generateReducerUiInteractionRoutesContent(),
1676
+ "ui/setup-screen.tsx": generateReducerUiSetupScreenContent(),
1677
+ "ui/styles.ts": generateReducerUiStylesContent(),
1678
+ "ui/ui-contract-typing-smoke.tsx":
1679
+ generateReducerUiContractTypingSmokeContent(),
1680
+ "app/game-contract.ts": generateReducerGameContractSeed(),
1681
+ "app/game.ts": generateReducerGameSeed(),
1682
+ "app/setup-profiles.ts": generateSetupProfilesSeed(manifest),
1683
+ "app/reducer-support.ts": generateReducerSupportSeed(),
1684
+ "app/derived.ts": generateReducerDerivedSeed(),
1685
+ "app/phases/setup.ts": generateSetupPhaseSeed(),
1686
+ "app/phases/index.ts": generatePhaseIndexSeed(),
1687
+ };
1688
+ }
1689
+
1690
+ export function generateAuthoritativeIndexFile(
1691
+ _manifest: GameTopologyManifest,
1692
+ ): string {
1693
+ return generateReducerAppIndex();
1694
+ }
1695
+
1696
+ export function generateFrameworkFiles(
1697
+ manifest: GameTopologyManifest,
1698
+ ): Record<string, string> {
1699
+ return {
1700
+ "app/tsconfig.framework.json": generateAppFrameworkTsConfig(),
1701
+ "ui/tsconfig.framework.json": generateUiFrameworkTsConfig(),
1702
+ "shared/generated/ui-contract.ts": generateUiContractContent(),
1703
+ "app/index.ts": generateAuthoritativeIndexFile(manifest),
1704
+ };
1705
+ }