@dreamboard-games/sdk 0.2.1-alpha.0 → 0.3.0-alpha.1

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 (363) hide show
  1. package/dist/{HandView-ncJIVLhN.d.ts → HandView-PYDmehVD.d.ts} +1 -1
  2. package/dist/{ResourceCounter-CTREyF73.d.ts → ResourceCounter-BTXklCEZ.d.ts} +1 -1
  3. package/dist/{ThemeProvider-fy0_QzgO.d.ts → ThemeProvider-DvMl17Ny.d.ts} +1 -1
  4. package/dist/attributes-BeRyboMS.d.ts +279 -0
  5. package/dist/browser-interaction.d.ts +708 -0
  6. package/dist/browser-interaction.js +106 -0
  7. package/dist/{chunk-KAELH4KC.js → chunk-3RQEICD3.js} +2 -2
  8. package/dist/{chunk-TDSWKVZ4.js → chunk-ADYH6PVT.js} +2457 -1265
  9. package/dist/chunk-ADYH6PVT.js.map +1 -0
  10. package/dist/{chunk-YFY3RL52.js → chunk-P7VMTJ5D.js} +2 -2
  11. package/dist/{chunk-YFY3RL52.js.map → chunk-P7VMTJ5D.js.map} +1 -1
  12. package/dist/chunk-QLG6VEMW.js +1691 -0
  13. package/dist/chunk-QLG6VEMW.js.map +1 -0
  14. package/dist/{chunk-WYPQ3GG5.js → chunk-QZ6X4B5P.js} +1177 -1106
  15. package/dist/chunk-QZ6X4B5P.js.map +1 -0
  16. package/dist/{chunk-GKKBPPSW.js → chunk-TXXLS3OI.js} +405 -309
  17. package/dist/chunk-TXXLS3OI.js.map +1 -0
  18. package/dist/{chunk-FDNZTDD6.js → chunk-W6SGFWXH.js} +48 -44
  19. package/dist/chunk-W6SGFWXH.js.map +1 -0
  20. package/dist/{chunk-MKXPVOUT.js → chunk-Y2AFYBMB.js} +48 -4
  21. package/dist/chunk-Y2AFYBMB.js.map +1 -0
  22. package/dist/{index-BwqPQtBu.d.ts → codegen.d.ts} +57 -565
  23. package/dist/{infrastructure/workspace-codegen.js → codegen.js} +3 -3
  24. package/dist/{components-D5ZRE2Hl.d.ts → components-DaIqi9QM.d.ts} +48 -37
  25. package/dist/{hex-board-view-D_07hO6O.d.ts → hex-board-view-UCUjMALd.d.ts} +3 -2
  26. package/dist/{infrastructure/reducer-bundle-abi.d.ts → index.d-BGqVifr_.d.ts} +289 -10
  27. package/dist/index.js +1 -1
  28. package/dist/package-set.d.ts +2 -2
  29. package/dist/package-set.js +1 -1
  30. package/dist/primitive-props-BNHDkgd7.d.ts +16 -0
  31. package/dist/reducer-contract.d.ts +3 -0
  32. package/dist/{infrastructure/reducer-bundle-abi.js → reducer-contract.js} +4 -4
  33. package/dist/reducer.d.ts +185 -179
  34. package/dist/reducer.js +1637 -1353
  35. package/dist/reducer.js.map +1 -1
  36. package/dist/runtime/primitives.d.ts +10 -9
  37. package/dist/runtime/primitives.js +8 -7
  38. package/dist/runtime/runtime-api.d.ts +2 -0
  39. package/dist/runtime/runtime-api.js +1 -0
  40. package/dist/runtime/workspace-contract.d.ts +63 -17
  41. package/dist/runtime/workspace-contract.js +5 -5
  42. package/dist/{runtime-api-DWxvTr-O.d.ts → runtime-api-tGL3ArsB.d.ts} +109 -4
  43. package/dist/runtime.d.ts +12 -57
  44. package/dist/runtime.js +487 -7
  45. package/dist/runtime.js.map +1 -1
  46. package/dist/testing.d.ts +4 -4
  47. package/dist/testing.js.map +1 -1
  48. package/dist/types.d.ts +1531 -85
  49. package/dist/types.js +1 -3
  50. package/dist/types.js.map +1 -1
  51. package/dist/ui/components.d.ts +7 -8
  52. package/dist/ui/components.js +1 -1
  53. package/dist/ui/defaults.d.ts +1 -1
  54. package/dist/ui/{types/player-state.d.ts → player-state.d.ts} +2 -2
  55. package/dist/{ui-contract-iQfTtUSL.d.ts → ui-contract-SctM4ibF.d.ts} +157 -150
  56. package/dist/ui.d.ts +10 -11
  57. package/dist/ui.js +2 -2
  58. package/package.json +35 -74
  59. package/dist/bundle-CDd5FKeD.d.ts +0 -283
  60. package/dist/cards-Sl3b40Mv.d.ts +0 -13
  61. package/dist/chunk-7YAHLYBR.js +0 -481
  62. package/dist/chunk-7YAHLYBR.js.map +0 -1
  63. package/dist/chunk-FDNZTDD6.js.map +0 -1
  64. package/dist/chunk-GKKBPPSW.js.map +0 -1
  65. package/dist/chunk-I46YJSOD.js +0 -1
  66. package/dist/chunk-MKXPVOUT.js.map +0 -1
  67. package/dist/chunk-T52J5RMF.js +0 -1
  68. package/dist/chunk-TDSWKVZ4.js.map +0 -1
  69. package/dist/chunk-U5C6BONG.js +0 -34
  70. package/dist/chunk-U5C6BONG.js.map +0 -1
  71. package/dist/chunk-WYPQ3GG5.js.map +0 -1
  72. package/dist/generated/runtime/primitives.d.ts +0 -12
  73. package/dist/generated/runtime/primitives.js +0 -180
  74. package/dist/generated/runtime/primitives.js.map +0 -1
  75. package/dist/generated/runtime-api.d.ts +0 -3
  76. package/dist/generated/runtime-api.js +0 -2
  77. package/dist/generated/runtime.d.ts +0 -14
  78. package/dist/generated/runtime.js +0 -18
  79. package/dist/generated/runtime.js.map +0 -1
  80. package/dist/generated/workspace-contract.d.ts +0 -14
  81. package/dist/generated/workspace-contract.js +0 -14
  82. package/dist/generated/workspace-contract.js.map +0 -1
  83. package/dist/infrastructure/reducer-bundle-abi.js.map +0 -1
  84. package/dist/infrastructure/workspace-codegen.d.ts +0 -53
  85. package/dist/infrastructure/workspace-codegen.js.map +0 -1
  86. package/dist/manifest-contract-BNHVGFtU.d.ts +0 -9
  87. package/dist/primitive-props-DpKs-GCr.d.ts +0 -11
  88. package/dist/runtime/types/runtime-api.d.ts +0 -1
  89. package/dist/runtime/types/runtime-api.js +0 -2
  90. package/dist/runtime-api-3dshj6kK.d.ts +0 -101
  91. package/dist/slots-1GPGihk8.d.ts +0 -8
  92. package/src/generated/reducer-contract/builders.ts +0 -90
  93. package/src/generated/reducer-contract/version.ts +0 -9
  94. package/src/generated/reducer-contract/wire.ts +0 -100
  95. package/src/generated/reducer-contract/zod.ts +0 -103
  96. package/src/generated/runtime/primitives.ts +0 -2
  97. package/src/generated/runtime-api.ts +0 -5
  98. package/src/generated/runtime.ts +0 -35
  99. package/src/generated/workspace-contract.ts +0 -2
  100. package/src/index.ts +0 -7
  101. package/src/infrastructure/reducer-bundle-abi.ts +0 -8
  102. package/src/infrastructure/reducer-contract/bundle.ts +0 -37
  103. package/src/infrastructure/workspace-codegen/hex-geometry.ts +0 -69
  104. package/src/infrastructure/workspace-codegen/index.ts +0 -64
  105. package/src/infrastructure/workspace-codegen/manifest-contract.ts +0 -6632
  106. package/src/infrastructure/workspace-codegen/manifest-validation.ts +0 -795
  107. package/src/infrastructure/workspace-codegen/ownership.ts +0 -131
  108. package/src/infrastructure/workspace-codegen/preset-card-sets.ts +0 -169
  109. package/src/infrastructure/workspace-codegen/seeds.ts +0 -1705
  110. package/src/infrastructure/workspace-codegen.ts +0 -1
  111. package/src/package-set.ts +0 -19
  112. package/src/reducer/authoring/contract.ts +0 -157
  113. package/src/reducer/authoring/effect.ts +0 -224
  114. package/src/reducer/authoring/game.ts +0 -23
  115. package/src/reducer/authoring/interaction.ts +0 -98
  116. package/src/reducer/authoring/phase.ts +0 -300
  117. package/src/reducer/authoring/types.ts +0 -70
  118. package/src/reducer/authoring/validation.ts +0 -382
  119. package/src/reducer/authoring/view-stage.ts +0 -68
  120. package/src/reducer/authoring.ts +0 -29
  121. package/src/reducer/bundle/ingress-bundle.ts +0 -491
  122. package/src/reducer/bundle/trusted/engine-instruction-resolver.ts +0 -254
  123. package/src/reducer/bundle/trusted/flow-instruction-resolver.ts +0 -73
  124. package/src/reducer/bundle/trusted/instruction-runner.ts +0 -414
  125. package/src/reducer/bundle/trusted/interaction-authorization.ts +0 -137
  126. package/src/reducer/bundle/trusted/interaction-collectors.ts +0 -859
  127. package/src/reducer/bundle/trusted/interaction-decision.ts +0 -747
  128. package/src/reducer/bundle/trusted/interaction-resolver.ts +0 -95
  129. package/src/reducer/bundle/trusted/interaction-types.ts +0 -171
  130. package/src/reducer/bundle/trusted/lifecycle-runner.ts +0 -427
  131. package/src/reducer/bundle/trusted/projection-builder.ts +0 -356
  132. package/src/reducer/bundle/trusted/projection-context.ts +0 -39
  133. package/src/reducer/bundle/trusted/rng-sampler.ts +0 -150
  134. package/src/reducer/bundle/trusted/runtime-registry.ts +0 -120
  135. package/src/reducer/bundle/trusted/runtime-scope.ts +0 -336
  136. package/src/reducer/bundle/trusted/simultaneous-player.ts +0 -97
  137. package/src/reducer/bundle/trusted/stage-resolver.ts +0 -87
  138. package/src/reducer/bundle/trusted/static-projection.ts +0 -116
  139. package/src/reducer/bundle/trusted/trusted-runtime-args.ts +0 -97
  140. package/src/reducer/bundle/trusted/trusted-runtime-result.ts +0 -39
  141. package/src/reducer/bundle/trusted/trusted-setup-profiles.ts +0 -43
  142. package/src/reducer/bundle/trusted/trusted-state-codec.ts +0 -48
  143. package/src/reducer/bundle/trusted-bundle.ts +0 -97
  144. package/src/reducer/bundle/types.ts +0 -171
  145. package/src/reducer/bundle.ts +0 -2
  146. package/src/reducer/client-param-schemas.ts +0 -57
  147. package/src/reducer/compose.ts +0 -34
  148. package/src/reducer/core/runtime-input.ts +0 -30
  149. package/src/reducer/core/runtime-instruction.ts +0 -59
  150. package/src/reducer/core/types.ts +0 -62
  151. package/src/reducer/definition-index.ts +0 -277
  152. package/src/reducer/derived.ts +0 -106
  153. package/src/reducer/effects.ts +0 -92
  154. package/src/reducer/engine/runtime-instruction-engine.ts +0 -155
  155. package/src/reducer/ingress/decode-runtime-input.ts +0 -7
  156. package/src/reducer/ingress/decode-session-state.ts +0 -9
  157. package/src/reducer/ingress/encode-session-state.ts +0 -6
  158. package/src/reducer/ingress/input-codec.ts +0 -18
  159. package/src/reducer/ingress/phase-schemas.ts +0 -62
  160. package/src/reducer/ingress/raw-types.ts +0 -107
  161. package/src/reducer/ingress/runtime-codec.ts +0 -14
  162. package/src/reducer/ingress/runtime-payload.ts +0 -13
  163. package/src/reducer/ingress/session-codec.ts +0 -392
  164. package/src/reducer/ingress/types.ts +0 -6
  165. package/src/reducer/inputs/boardInput.ts +0 -217
  166. package/src/reducer/inputs/boardTarget.ts +0 -190
  167. package/src/reducer/inputs/cardInput.ts +0 -86
  168. package/src/reducer/inputs/cardTarget.ts +0 -101
  169. package/src/reducer/inputs/choiceTarget.ts +0 -104
  170. package/src/reducer/inputs/defineInputs.ts +0 -71
  171. package/src/reducer/inputs/formInput.ts +0 -809
  172. package/src/reducer/inputs/many.ts +0 -120
  173. package/src/reducer/inputs/promptInput.ts +0 -87
  174. package/src/reducer/inputs/rngInput.ts +0 -58
  175. package/src/reducer/inputs/targetRule.ts +0 -123
  176. package/src/reducer/inputs.ts +0 -41
  177. package/src/reducer/model/definition.ts +0 -1072
  178. package/src/reducer/model/extract.ts +0 -745
  179. package/src/reducer/model/manifest.ts +0 -570
  180. package/src/reducer/model/queries.ts +0 -641
  181. package/src/reducer/model/runtime.ts +0 -264
  182. package/src/reducer/model/spec.ts +0 -1386
  183. package/src/reducer/model/table.ts +0 -260
  184. package/src/reducer/model.ts +0 -7
  185. package/src/reducer/ops.ts +0 -1034
  186. package/src/reducer/parse-utils.ts +0 -28
  187. package/src/reducer/per-player.ts +0 -422
  188. package/src/reducer/rng.ts +0 -69
  189. package/src/reducer/schema-helpers.ts +0 -185
  190. package/src/reducer/setup-bootstrap-helpers.ts +0 -171
  191. package/src/reducer/setup-bootstrap.ts +0 -481
  192. package/src/reducer/table-ops.ts +0 -2671
  193. package/src/reducer/table-queries.ts +0 -372
  194. package/src/reducer/transaction.ts +0 -120
  195. package/src/reducer.ts +0 -314
  196. package/src/runtime/primitives.ts +0 -1
  197. package/src/runtime/types/runtime-api.ts +0 -1
  198. package/src/runtime/workspace-contract.ts +0 -32
  199. package/src/runtime-internal/components/InteractionForm.tsx +0 -1309
  200. package/src/runtime-internal/components/PluginRuntime.tsx +0 -103
  201. package/src/runtime-internal/components/board/target-layer.ts +0 -70
  202. package/src/runtime-internal/context/ClientParamSchemaContext.tsx +0 -44
  203. package/src/runtime-internal/context/InteractionDraftContext.tsx +0 -279
  204. package/src/runtime-internal/context/PluginSessionContext.tsx +0 -47
  205. package/src/runtime-internal/context/PluginStateContext.tsx +0 -262
  206. package/src/runtime-internal/context/RuntimeContext.tsx +0 -96
  207. package/src/runtime-internal/defaults/components.tsx +0 -409
  208. package/src/runtime-internal/defaults/index.ts +0 -11
  209. package/src/runtime-internal/errors/ValidationError.ts +0 -29
  210. package/src/runtime-internal/hooks/useActivePlayers.ts +0 -33
  211. package/src/runtime-internal/hooks/useBoardInteractions.ts +0 -665
  212. package/src/runtime-internal/hooks/useGameSelector.ts +0 -105
  213. package/src/runtime-internal/hooks/useGameView.ts +0 -9
  214. package/src/runtime-internal/hooks/useInteractionByKey.ts +0 -354
  215. package/src/runtime-internal/hooks/useInteractionHandle.ts +0 -438
  216. package/src/runtime-internal/hooks/useIsMyTurn.ts +0 -20
  217. package/src/runtime-internal/hooks/useLobby.ts +0 -76
  218. package/src/runtime-internal/hooks/useMe.ts +0 -48
  219. package/src/runtime-internal/hooks/usePlayerInfo.ts +0 -28
  220. package/src/runtime-internal/hooks/usePlayerTurnOrder.ts +0 -23
  221. package/src/runtime-internal/hooks/usePluginRuntime.ts +0 -147
  222. package/src/runtime-internal/hooks/useSeatInbox.ts +0 -61
  223. package/src/runtime-internal/hooks/useSimultaneousPhase.ts +0 -10
  224. package/src/runtime-internal/index.ts +0 -42
  225. package/src/runtime-internal/internal.ts +0 -43
  226. package/src/runtime-internal/plugin-styles.css +0 -250
  227. package/src/runtime-internal/primitives/board.tsx +0 -459
  228. package/src/runtime-internal/primitives/dialog-lifecycle.ts +0 -58
  229. package/src/runtime-internal/primitives/dice.tsx +0 -79
  230. package/src/runtime-internal/primitives/game-ui-provider.tsx +0 -35
  231. package/src/runtime-internal/primitives/game.tsx +0 -387
  232. package/src/runtime-internal/primitives/hand-intent-adapter.ts +0 -147
  233. package/src/runtime-internal/primitives/hand-surface.tsx +0 -594
  234. package/src/runtime-internal/primitives/index.ts +0 -196
  235. package/src/runtime-internal/primitives/interaction-form-binding.tsx +0 -56
  236. package/src/runtime-internal/primitives/interaction-submit.ts +0 -90
  237. package/src/runtime-internal/primitives/interaction.tsx +0 -987
  238. package/src/runtime-internal/primitives/phase.tsx +0 -43
  239. package/src/runtime-internal/primitives/player-roster.tsx +0 -302
  240. package/src/runtime-internal/primitives/primitive-props.tsx +0 -101
  241. package/src/runtime-internal/primitives/prompt.tsx +0 -255
  242. package/src/runtime-internal/primitives/ui.tsx +0 -60
  243. package/src/runtime-internal/primitives/zone.tsx +0 -791
  244. package/src/runtime-internal/reducer.ts +0 -30
  245. package/src/runtime-internal/runtime/createPluginRuntimeAPI.ts +0 -605
  246. package/src/runtime-internal/types/plugin-state.ts +0 -508
  247. package/src/runtime-internal/types/reducer-state.ts +0 -24
  248. package/src/runtime-internal/types/runtime-api.ts +0 -114
  249. package/src/runtime-internal/ui-contract.ts +0 -519
  250. package/src/runtime-internal/utils/card-intent-adapter.ts +0 -546
  251. package/src/runtime-internal/utils/interaction-inputs.ts +0 -492
  252. package/src/runtime-internal/utils/interaction-labels.ts +0 -23
  253. package/src/runtime-internal/utils/interaction-router.ts +0 -273
  254. package/src/runtime-internal/utils/interaction-status.ts +0 -74
  255. package/src/runtime-internal/workspace-contract.ts +0 -1170
  256. package/src/runtime.ts +0 -34
  257. package/src/testing/create-expect-api.ts +0 -352
  258. package/src/testing/create-test-runtime.ts +0 -381
  259. package/src/testing/definitions.ts +0 -127
  260. package/src/testing/index.ts +0 -3
  261. package/src/testing.ts +0 -1
  262. package/src/type-stubs/manifest-contract.d.ts +0 -42
  263. package/src/type-stubs/manifest-contract.js +0 -72
  264. package/src/type-stubs/ui-contract.d.ts +0 -5
  265. package/src/type-stubs/ui-contract.js +0 -1
  266. package/src/types/authoring-card-properties.type-test.ts +0 -266
  267. package/src/types/authoring.ts +0 -1282
  268. package/src/types/cards.ts +0 -19
  269. package/src/types/contracts.ts +0 -1550
  270. package/src/types/generated-helpers.ts +0 -35
  271. package/src/types/index.ts +0 -147
  272. package/src/types/slots.ts +0 -11
  273. package/src/types.ts +0 -1
  274. package/src/ui/components/ActionButton.tsx +0 -97
  275. package/src/ui/components/ActionPanel.tsx +0 -315
  276. package/src/ui/components/Card.tsx +0 -378
  277. package/src/ui/components/CardDragSurface.tsx +0 -1076
  278. package/src/ui/components/ChromeSuppressionContext.tsx +0 -70
  279. package/src/ui/components/CostDisplay.tsx +0 -145
  280. package/src/ui/components/DiceRoller.tsx +0 -581
  281. package/src/ui/components/Drawer.tsx +0 -180
  282. package/src/ui/components/ErrorBoundary.tsx +0 -275
  283. package/src/ui/components/GameEndDisplay.tsx +0 -398
  284. package/src/ui/components/GameSkeleton.tsx +0 -260
  285. package/src/ui/components/Hand.tsx +0 -468
  286. package/src/ui/components/HandDock.tsx +0 -299
  287. package/src/ui/components/HandView.tsx +0 -441
  288. package/src/ui/components/MobileHandTray.tsx +0 -381
  289. package/src/ui/components/MoreActions.tsx +0 -143
  290. package/src/ui/components/PhaseIndicator.tsx +0 -341
  291. package/src/ui/components/PlayArea.tsx +0 -146
  292. package/src/ui/components/PrimaryActionButton.tsx +0 -336
  293. package/src/ui/components/PrimaryButton.tsx +0 -45
  294. package/src/ui/components/ResourceCounter.tsx +0 -270
  295. package/src/ui/components/StagingZone.tsx +0 -134
  296. package/src/ui/components/ThemedButton.tsx +0 -113
  297. package/src/ui/components/Toast.tsx +0 -264
  298. package/src/ui/components/board/HexGrid.tsx +0 -1294
  299. package/src/ui/components/board/NetworkGraph.tsx +0 -476
  300. package/src/ui/components/board/SlotSystem.tsx +0 -388
  301. package/src/ui/components/board/SquareGrid.tsx +0 -1165
  302. package/src/ui/components/board/TrackBoard.tsx +0 -496
  303. package/src/ui/components/board/ZoneMap.tsx +0 -448
  304. package/src/ui/components/board/hex-board-view.ts +0 -123
  305. package/src/ui/components/board/index.ts +0 -142
  306. package/src/ui/components/board/interaction-accessibility.ts +0 -21
  307. package/src/ui/components/board/target-layer.ts +0 -66
  308. package/src/ui/components/card-render-content.type-test.ts +0 -27
  309. package/src/ui/components/hand-layout-math.ts +0 -163
  310. package/src/ui/components/hand-pointer-engine.ts +0 -413
  311. package/src/ui/components/index.ts +0 -245
  312. package/src/ui/components.ts +0 -1
  313. package/src/ui/defaults/components.tsx +0 -106
  314. package/src/ui/defaults/index.ts +0 -8
  315. package/src/ui/defaults.ts +0 -1
  316. package/src/ui/errors/ValidationError.ts +0 -29
  317. package/src/ui/helpers/cards.ts +0 -19
  318. package/src/ui/helpers/track-board.ts +0 -211
  319. package/src/ui/hooks/useBoardTopology.ts +0 -316
  320. package/src/ui/hooks/useCards.ts +0 -10
  321. package/src/ui/hooks/useHandCardPointer.ts +0 -381
  322. package/src/ui/hooks/useHandLayout.ts +0 -378
  323. package/src/ui/hooks/useHandPresentation.ts +0 -121
  324. package/src/ui/hooks/useHexBoard.ts +0 -74
  325. package/src/ui/hooks/useHexGrid.ts +0 -185
  326. package/src/ui/hooks/useIsMobile.ts +0 -35
  327. package/src/ui/hooks/usePanZoom.ts +0 -278
  328. package/src/ui/hooks/useSquareBoard.ts +0 -124
  329. package/src/ui/hooks/useSquareGrid.ts +0 -328
  330. package/src/ui/index.ts +0 -98
  331. package/src/ui/internal/ui/alert.tsx +0 -51
  332. package/src/ui/internal/ui/button.tsx +0 -58
  333. package/src/ui/internal/ui/dialog.tsx +0 -134
  334. package/src/ui/internal/ui/input.tsx +0 -21
  335. package/src/ui/internal/ui/label.tsx +0 -21
  336. package/src/ui/internal/ui/select.tsx +0 -129
  337. package/src/ui/internal/ui/tooltip.tsx +0 -54
  338. package/src/ui/internal/ui/utils.ts +0 -5
  339. package/src/ui/plugin-styles.css +0 -250
  340. package/src/ui/primitives/dialog-lifecycle.ts +0 -58
  341. package/src/ui/primitives/dice.tsx +0 -79
  342. package/src/ui/primitives/primitive-props.tsx +0 -101
  343. package/src/ui/theme/ThemeProvider.tsx +0 -252
  344. package/src/ui/theme/board.ts +0 -61
  345. package/src/ui/theme/css-vars.ts +0 -105
  346. package/src/ui/theme/derive.ts +0 -240
  347. package/src/ui/theme/index.ts +0 -61
  348. package/src/ui/theme/presets/arcade.ts +0 -261
  349. package/src/ui/theme/presets/studio.ts +0 -261
  350. package/src/ui/theme/presets/tabletop.ts +0 -266
  351. package/src/ui/theme/tokens.ts +0 -392
  352. package/src/ui/types/hex-color.ts +0 -20
  353. package/src/ui/types/player-state.ts +0 -463
  354. package/src/ui/types/tiled-board.ts +0 -785
  355. package/src/ui/types/visual-state.ts +0 -137
  356. package/src/ui.ts +0 -1
  357. /package/dist/{chunk-I46YJSOD.js.map → browser-interaction.js.map} +0 -0
  358. /package/dist/{chunk-KAELH4KC.js.map → chunk-3RQEICD3.js.map} +0 -0
  359. /package/dist/{chunk-T52J5RMF.js.map → codegen.js.map} +0 -0
  360. /package/dist/{generated/runtime-api.js.map → reducer-contract.js.map} +0 -0
  361. /package/dist/runtime/{types/runtime-api.js.map → runtime-api.js.map} +0 -0
  362. /package/dist/ui/{types/player-state.js → player-state.js} +0 -0
  363. /package/dist/ui/{types/player-state.js.map → player-state.js.map} +0 -0
@@ -1,1165 +0,0 @@
1
- /**
2
- * SVG-based square grid for grid-based games (Chess, Checkers, Go, Scrabble, Battleship).
3
- * All rendering controlled by parent via required render functions.
4
- */
5
-
6
- import { useMemo, useState, type ReactNode } from "react";
7
- import { clsx } from "clsx";
8
- import { usePanZoom } from "../../hooks/usePanZoom.js";
9
- import { handleKeyboardActivation } from "./interaction-accessibility.js";
10
- import {
11
- interactiveTargetRenderState,
12
- isInteractiveTargetSelectable,
13
- type InteractiveTargetLayer,
14
- type InteractiveTargetRenderState,
15
- } from "./target-layer.js";
16
- import type { SquarePieceState } from "../../types/player-state.js";
17
- import {
18
- type AuthoredSquareBoardInput,
19
- type AnySquareBoardInput,
20
- type GeneratedSquareBoardInput,
21
- type NormalizedSquareBoard,
22
- type NormalizedSquareCellOf,
23
- type NormalizedSquareEdgeOf,
24
- type NormalizedSquarePieceOf,
25
- type NormalizedSquareVertexOf,
26
- normalizeSquareBoardInput,
27
- } from "../../types/tiled-board.js";
28
-
29
- export type {
30
- InteractiveTargetLayer,
31
- InteractiveTargetRenderState,
32
- } from "./target-layer.js";
33
-
34
- // ============================================================================
35
- // Types
36
- // ============================================================================
37
-
38
- interface SquareCellWithId {
39
- id: string;
40
- row: number;
41
- col: number;
42
- }
43
-
44
- export interface SquareEdgePosition {
45
- x1: number;
46
- y1: number;
47
- x2: number;
48
- y2: number;
49
- midX: number;
50
- midY: number;
51
- angle: number;
52
- }
53
-
54
- export interface SquareVertexPosition {
55
- x: number;
56
- y: number;
57
- }
58
-
59
- export interface InteractiveSquareEdge<
60
- TBoard extends AnySquareBoardInput = AnySquareBoardInput,
61
- > extends NormalizedSquareEdgeOf<TBoard> {
62
- position: SquareEdgePosition;
63
- }
64
-
65
- export interface InteractiveSquareVertex<
66
- TBoard extends AnySquareBoardInput = AnySquareBoardInput,
67
- > extends NormalizedSquareVertexOf<TBoard> {
68
- position: SquareVertexPosition;
69
- }
70
-
71
- export type InteractiveSquareSpace<
72
- TBoard extends AnySquareBoardInput = AnySquareBoardInput,
73
- > = NormalizedSquareCellOf<TBoard>;
74
-
75
- interface SquareGeneratedGridInputProps {
76
- id?: string;
77
- layout?: "square";
78
- spaces: Extract<AnySquareBoardInput, { spaces: unknown }>["spaces"];
79
- pieces?: AnySquareBoardInput["pieces"];
80
- edges?: AnySquareBoardInput["edges"];
81
- vertices?: AnySquareBoardInput["vertices"];
82
- }
83
-
84
- interface SquareAuthoredGridInputProps {
85
- id?: string;
86
- layout?: "square";
87
- rows?: number;
88
- cols?: number;
89
- cells: Extract<AnySquareBoardInput, { cells: unknown }>["cells"];
90
- pieces?: readonly SquarePieceState[];
91
- edges?: AnySquareBoardInput["edges"];
92
- vertices?: AnySquareBoardInput["vertices"];
93
- }
94
-
95
- type SquareGridInputProps =
96
- | SquareGeneratedGridInputProps
97
- | SquareAuthoredGridInputProps;
98
-
99
- type ResolvedSquareArrayProp<Value> =
100
- Exclude<Value, undefined> extends readonly unknown[]
101
- ? Exclude<Value, undefined>
102
- : readonly [];
103
-
104
- type SquareBoardLikeOfProps<TProps extends SquareGridInputProps> =
105
- TProps extends {
106
- id?: infer Id;
107
- layout?: infer Layout;
108
- spaces: infer Spaces;
109
- pieces?: infer Pieces;
110
- edges?: infer Edges;
111
- vertices?: infer Vertices;
112
- }
113
- ? {
114
- id: Extract<Id, string> extends never ? string : Extract<Id, string>;
115
- layout?: Extract<Layout, "square">;
116
- spaces: Spaces;
117
- pieces: ResolvedSquareArrayProp<Pieces>;
118
- edges: ResolvedSquareArrayProp<Edges>;
119
- vertices: ResolvedSquareArrayProp<Vertices>;
120
- } & GeneratedSquareBoardInput
121
- : TProps extends {
122
- id?: infer Id;
123
- layout?: infer Layout;
124
- rows?: infer Rows;
125
- cols?: infer Cols;
126
- cells: infer Cells;
127
- pieces?: infer Pieces;
128
- edges?: infer Edges;
129
- vertices?: infer Vertices;
130
- }
131
- ? {
132
- id: Extract<Id, string> extends never ? string : Extract<Id, string>;
133
- layout?: Extract<Layout, "square">;
134
- rows: Extract<Rows, number> extends never
135
- ? number
136
- : Extract<Rows, number>;
137
- cols: Extract<Cols, number> extends never
138
- ? number
139
- : Extract<Cols, number>;
140
- cells: Cells;
141
- pieces: ResolvedSquareArrayProp<Pieces>;
142
- edges: ResolvedSquareArrayProp<Edges>;
143
- vertices: ResolvedSquareArrayProp<Vertices>;
144
- } & AuthoredSquareBoardInput
145
- : never;
146
-
147
- export type SquareGridProps<
148
- TProps extends SquareGridInputProps = SquareGridInputProps,
149
- > = TProps & {
150
- cellSize?: number;
151
- /** Receives row/col with transform centered at cell position */
152
- renderCell: (row: number, col: number) => ReactNode;
153
- /** Receives piece with transform centered at cell center */
154
- renderPiece: (
155
- piece: NormalizedSquarePieceOf<NoInfer<SquareBoardLikeOfProps<TProps>>>,
156
- ) => ReactNode;
157
- renderEdge?: (
158
- edge: NormalizedSquareEdgeOf<NoInfer<SquareBoardLikeOfProps<TProps>>>,
159
- position: SquareEdgePosition,
160
- ) => ReactNode;
161
- renderVertex?: (
162
- vertex: NormalizedSquareVertexOf<NoInfer<SquareBoardLikeOfProps<TProps>>>,
163
- position: SquareVertexPosition,
164
- ) => ReactNode;
165
- showCoordinates?: boolean;
166
- coordinateStyle?: "algebraic" | "numeric" | "none";
167
- width?: number | string;
168
- height?: number | string;
169
- enablePanZoom?: boolean;
170
- initialZoom?: number;
171
- minZoom?: number;
172
- maxZoom?: number;
173
- className?: string;
174
- interactiveSpaces?: InteractiveTargetLayer;
175
- interactiveEdges?: InteractiveTargetLayer;
176
- interactiveVertices?: InteractiveTargetLayer;
177
- renderInteractiveSpace?: (
178
- space: InteractiveSquareSpace<NoInfer<SquareBoardLikeOfProps<TProps>>>,
179
- state: InteractiveTargetRenderState,
180
- ) => ReactNode;
181
- renderInteractiveEdge?: (
182
- edge: InteractiveSquareEdge<NoInfer<SquareBoardLikeOfProps<TProps>>>,
183
- position: SquareEdgePosition,
184
- state: InteractiveTargetRenderState,
185
- ) => ReactNode;
186
- renderInteractiveVertex?: (
187
- vertex: InteractiveSquareVertex<NoInfer<SquareBoardLikeOfProps<TProps>>>,
188
- position: SquareVertexPosition,
189
- state: InteractiveTargetRenderState,
190
- ) => ReactNode;
191
- };
192
-
193
- export interface SquareGridBoardProps<
194
- TBoard extends AnySquareBoardInput = AnySquareBoardInput,
195
- > {
196
- board: TBoard;
197
- cellSize?: number;
198
- renderCell: (row: number, col: number) => ReactNode;
199
- renderPiece: (piece: NormalizedSquarePieceOf<NoInfer<TBoard>>) => ReactNode;
200
- renderEdge?: (
201
- edge: NormalizedSquareEdgeOf<NoInfer<TBoard>>,
202
- position: SquareEdgePosition,
203
- ) => ReactNode;
204
- renderVertex?: (
205
- vertex: NormalizedSquareVertexOf<NoInfer<TBoard>>,
206
- position: SquareVertexPosition,
207
- ) => ReactNode;
208
- showCoordinates?: boolean;
209
- coordinateStyle?: "algebraic" | "numeric" | "none";
210
- width?: number | string;
211
- height?: number | string;
212
- enablePanZoom?: boolean;
213
- initialZoom?: number;
214
- minZoom?: number;
215
- maxZoom?: number;
216
- className?: string;
217
- interactiveSpaces?: InteractiveTargetLayer;
218
- interactiveEdges?: InteractiveTargetLayer;
219
- interactiveVertices?: InteractiveTargetLayer;
220
- renderInteractiveSpace?: (
221
- space: InteractiveSquareSpace<NoInfer<TBoard>>,
222
- state: InteractiveTargetRenderState,
223
- ) => ReactNode;
224
- renderInteractiveEdge?: (
225
- edge: InteractiveSquareEdge<NoInfer<TBoard>>,
226
- position: SquareEdgePosition,
227
- state: InteractiveTargetRenderState,
228
- ) => ReactNode;
229
- renderInteractiveVertex?: (
230
- vertex: InteractiveSquareVertex<NoInfer<TBoard>>,
231
- position: SquareVertexPosition,
232
- state: InteractiveTargetRenderState,
233
- ) => ReactNode;
234
- }
235
-
236
- // ============================================================================
237
- // Pre-built Helper Components
238
- // ============================================================================
239
-
240
- export interface DefaultGridCellProps {
241
- size: number;
242
- isLight?: boolean;
243
- lightColor?: string;
244
- darkColor?: string;
245
- isHighlighted?: boolean;
246
- highlightColor?: string;
247
- isSelected?: boolean;
248
- selectedColor?: string;
249
- isValidMove?: boolean;
250
- isCapture?: boolean;
251
- onClick?: () => void;
252
- onPointerEnter?: () => void;
253
- onPointerLeave?: () => void;
254
- className?: string;
255
- }
256
-
257
- /** Pre-built grid cell component for use in `renderCell`. */
258
- export function DefaultGridCell({
259
- size,
260
- isLight = true,
261
- lightColor = "#f0d9b5",
262
- darkColor = "#b58863",
263
- isHighlighted = false,
264
- highlightColor = "rgba(250, 204, 21, 0.4)",
265
- isSelected = false,
266
- selectedColor = "rgba(59, 130, 246, 0.5)",
267
- isValidMove = false,
268
- isCapture = false,
269
- onClick,
270
- onPointerEnter,
271
- onPointerLeave,
272
- className,
273
- }: DefaultGridCellProps) {
274
- const baseColor = isLight ? lightColor : darkColor;
275
-
276
- return (
277
- <g
278
- onClick={onClick}
279
- onPointerEnter={onPointerEnter}
280
- onPointerLeave={onPointerLeave}
281
- onKeyDown={(event) => handleKeyboardActivation(event, onClick)}
282
- className={clsx(
283
- "transition-colors duration-100",
284
- onClick && "cursor-pointer",
285
- className,
286
- )}
287
- role={onClick ? "button" : undefined}
288
- tabIndex={onClick ? 0 : undefined}
289
- aria-label={onClick ? "Grid cell" : undefined}
290
- >
291
- {/* Base cell */}
292
- <rect width={size} height={size} fill={baseColor} />
293
-
294
- {/* Selected overlay */}
295
- {isSelected && (
296
- <rect
297
- width={size}
298
- height={size}
299
- fill={selectedColor}
300
- pointerEvents="none"
301
- />
302
- )}
303
-
304
- {/* Highlight overlay */}
305
- {isHighlighted && !isSelected && (
306
- <rect
307
- width={size}
308
- height={size}
309
- fill={highlightColor}
310
- pointerEvents="none"
311
- />
312
- )}
313
-
314
- {/* Valid move indicator (dot) */}
315
- {isValidMove && !isCapture && (
316
- <circle
317
- cx={size / 2}
318
- cy={size / 2}
319
- r={size * 0.15}
320
- fill="rgba(34, 197, 94, 0.6)"
321
- pointerEvents="none"
322
- />
323
- )}
324
-
325
- {/* Capture indicator (ring) */}
326
- {isCapture && (
327
- <circle
328
- cx={size / 2}
329
- cy={size / 2}
330
- r={size * 0.42}
331
- fill="none"
332
- stroke="rgba(239, 68, 68, 0.8)"
333
- strokeWidth={size * 0.08}
334
- pointerEvents="none"
335
- />
336
- )}
337
- </g>
338
- );
339
- }
340
-
341
- export interface DefaultGridPieceProps {
342
- size: number;
343
- color?: string;
344
- strokeColor?: string;
345
- label?: string;
346
- isDragging?: boolean;
347
- onClick?: () => void;
348
- onPointerDown?: (e: React.PointerEvent) => void;
349
- className?: string;
350
- }
351
-
352
- /** Pre-built grid piece component for use in `renderPiece`. */
353
- export function DefaultGridPiece({
354
- size,
355
- color = "#94a3b8",
356
- strokeColor,
357
- label,
358
- isDragging = false,
359
- onClick,
360
- onPointerDown,
361
- className,
362
- }: DefaultGridPieceProps) {
363
- const radius = size * 0.38;
364
- const effectiveStroke =
365
- strokeColor ??
366
- (color === "#f8fafc" || color === "#ffffff" ? "#1e293b" : "#f8fafc");
367
-
368
- return (
369
- <g
370
- onClick={onClick}
371
- onPointerDown={onPointerDown}
372
- onKeyDown={(event) => handleKeyboardActivation(event, onClick)}
373
- className={clsx(
374
- "transition-transform duration-150",
375
- (onClick || onPointerDown) && "cursor-pointer hover:scale-105",
376
- className,
377
- )}
378
- opacity={isDragging ? 0.8 : 1}
379
- role={onClick ? "button" : undefined}
380
- tabIndex={onClick ? 0 : undefined}
381
- aria-label={onClick ? (label ?? "Grid piece") : undefined}
382
- >
383
- <circle
384
- r={isDragging ? radius * 1.1 : radius}
385
- fill={color}
386
- stroke={effectiveStroke}
387
- strokeWidth={2}
388
- style={{ filter: "drop-shadow(1px 2px 2px rgba(0,0,0,0.4))" }}
389
- />
390
- {label && (
391
- <text
392
- y={4}
393
- textAnchor="middle"
394
- fill={effectiveStroke}
395
- fontSize={size * 0.35}
396
- fontWeight="bold"
397
- pointerEvents="none"
398
- >
399
- {label}
400
- </text>
401
- )}
402
- </g>
403
- );
404
- }
405
-
406
- export interface DefaultChessPieceProps {
407
- size: number;
408
- type: string;
409
- owner: "white" | "black";
410
- onClick?: () => void;
411
- onPointerDown?: (e: React.PointerEvent) => void;
412
- className?: string;
413
- }
414
-
415
- const CHESS_SYMBOLS: Record<string, Record<string, string>> = {
416
- white: {
417
- king: "♔",
418
- queen: "♕",
419
- rook: "♖",
420
- bishop: "♗",
421
- knight: "♘",
422
- pawn: "♙",
423
- },
424
- black: {
425
- king: "♚",
426
- queen: "♛",
427
- rook: "♜",
428
- bishop: "♝",
429
- knight: "♞",
430
- pawn: "♟",
431
- },
432
- };
433
-
434
- /** Pre-built chess piece component using Unicode symbols. */
435
- export function DefaultChessPiece({
436
- size,
437
- type,
438
- owner,
439
- onClick,
440
- onPointerDown,
441
- className,
442
- }: DefaultChessPieceProps) {
443
- const symbol = CHESS_SYMBOLS[owner]?.[type] ?? "?";
444
- const textColor = owner === "white" ? "#f8fafc" : "#1e293b";
445
- const shadowFilter =
446
- owner === "white"
447
- ? "drop-shadow(1px 1px 1px rgba(0,0,0,0.5))"
448
- : "drop-shadow(1px 1px 1px rgba(255,255,255,0.3))";
449
-
450
- return (
451
- <g
452
- onClick={onClick}
453
- onPointerDown={onPointerDown}
454
- onKeyDown={(event) => handleKeyboardActivation(event, onClick)}
455
- className={clsx(
456
- (onClick || onPointerDown) && "cursor-pointer",
457
- className,
458
- )}
459
- role={onClick ? "button" : undefined}
460
- tabIndex={onClick ? 0 : undefined}
461
- aria-label={onClick ? `${owner} ${type}` : undefined}
462
- >
463
- <text
464
- textAnchor="middle"
465
- dominantBaseline="middle"
466
- fontSize={size * 0.7}
467
- fill={textColor}
468
- style={{ filter: shadowFilter }}
469
- >
470
- {symbol}
471
- </text>
472
- </g>
473
- );
474
- }
475
-
476
- // ============================================================================
477
- // Utilities
478
- // ============================================================================
479
-
480
- /**
481
- * Convert row/col to algebraic notation (a1, b2, etc.)
482
- */
483
- export function toAlgebraic(
484
- row: number,
485
- col: number,
486
- totalRows: number,
487
- ): string {
488
- const file = String.fromCharCode(97 + col); // a, b, c, ...
489
- const rank = totalRows - row; // 8, 7, 6, ... (bottom to top)
490
- return `${file}${rank}`;
491
- }
492
-
493
- /**
494
- * Convert row/col to numeric notation (1,1, 2,3, etc.)
495
- */
496
- export function toNumeric(row: number, col: number): string {
497
- return `${row + 1},${col + 1}`;
498
- }
499
-
500
- function getCellId(cell: { id: string }): string {
501
- return cell.id;
502
- }
503
-
504
- function edgePositionForCells(
505
- firstCell: { row: number; col: number },
506
- secondCell: { row: number; col: number },
507
- cellSize: number,
508
- labelMargin: number,
509
- ): SquareEdgePosition | null {
510
- if (
511
- Math.abs(firstCell.row - secondCell.row) +
512
- Math.abs(firstCell.col - secondCell.col) !==
513
- 1
514
- ) {
515
- return null;
516
- }
517
-
518
- const minRow = Math.min(firstCell.row, secondCell.row);
519
- const minCol = Math.min(firstCell.col, secondCell.col);
520
-
521
- if (firstCell.row === secondCell.row) {
522
- const x = labelMargin + (minCol + 1) * cellSize;
523
- const y1 = minRow * cellSize;
524
- const y2 = y1 + cellSize;
525
- return {
526
- x1: x,
527
- y1,
528
- x2: x,
529
- y2,
530
- midX: x,
531
- midY: (y1 + y2) / 2,
532
- angle: 90,
533
- };
534
- }
535
-
536
- const y = (minRow + 1) * cellSize;
537
- const x1 = labelMargin + minCol * cellSize;
538
- const x2 = x1 + cellSize;
539
- return {
540
- x1,
541
- y1: y,
542
- x2,
543
- y2: y,
544
- midX: (x1 + x2) / 2,
545
- midY: y,
546
- angle: 0,
547
- };
548
- }
549
-
550
- function cornerKeysForCell(cell: {
551
- row: number;
552
- col: number;
553
- }): Record<string, SquareVertexPosition> {
554
- return {
555
- [`${cell.col},${cell.row}`]: { x: cell.col, y: cell.row },
556
- [`${cell.col + 1},${cell.row}`]: { x: cell.col + 1, y: cell.row },
557
- [`${cell.col + 1},${cell.row + 1}`]: {
558
- x: cell.col + 1,
559
- y: cell.row + 1,
560
- },
561
- [`${cell.col},${cell.row + 1}`]: { x: cell.col, y: cell.row + 1 },
562
- };
563
- }
564
-
565
- function vertexPositionForCells(
566
- cells: readonly SquareCellWithId[],
567
- cellSize: number,
568
- labelMargin: number,
569
- ): SquareVertexPosition | null {
570
- if (cells.length === 0) {
571
- return null;
572
- }
573
-
574
- const candidateKeys = cells.map(
575
- (cell) => new Set(Object.keys(cornerKeysForCell(cell))),
576
- );
577
- const firstKeySet = candidateKeys[0];
578
- if (firstKeySet === undefined) {
579
- return null;
580
- }
581
- const sharedKeys = [...firstKeySet].filter((key) =>
582
- candidateKeys.every((keySet) => keySet.has(key)),
583
- );
584
- if (sharedKeys.length !== 1) {
585
- return null;
586
- }
587
-
588
- const sharedKey = sharedKeys[0];
589
- if (!sharedKey) {
590
- return null;
591
- }
592
- const [colToken, rowToken] = sharedKey.split(",");
593
- if (colToken === undefined || rowToken === undefined) {
594
- return null;
595
- }
596
- const col = Number(colToken);
597
- const row = Number(rowToken);
598
- if (!Number.isFinite(col) || !Number.isFinite(row)) {
599
- return null;
600
- }
601
-
602
- return {
603
- x: labelMargin + col * cellSize,
604
- y: row * cellSize,
605
- };
606
- }
607
-
608
- // ============================================================================
609
- // Component
610
- // ============================================================================
611
-
612
- export interface SquareGridComponent {
613
- <const TBoard extends AnySquareBoardInput>(
614
- props: SquareGridBoardProps<TBoard>,
615
- ): ReactNode;
616
- <const TProps extends SquareGeneratedGridInputProps>(
617
- props: SquareGridProps<TProps>,
618
- ): ReactNode;
619
- <const TProps extends SquareAuthoredGridInputProps>(
620
- props: SquareGridProps<TProps>,
621
- ): ReactNode;
622
- }
623
-
624
- function SquareGridImpl(
625
- props:
626
- | SquareGridBoardProps<AnySquareBoardInput>
627
- | SquareGridProps<SquareGridInputProps>,
628
- ) {
629
- const {
630
- cellSize = 60,
631
- renderCell,
632
- renderPiece,
633
- renderEdge,
634
- renderVertex,
635
- showCoordinates = true,
636
- coordinateStyle = "algebraic",
637
- width,
638
- height,
639
- enablePanZoom = false,
640
- initialZoom = 1,
641
- minZoom = 0.5,
642
- maxZoom = 3,
643
- className,
644
- interactiveSpaces,
645
- interactiveEdges,
646
- interactiveVertices,
647
- renderInteractiveSpace,
648
- renderInteractiveEdge,
649
- renderInteractiveVertex,
650
- } = props;
651
- const board =
652
- "board" in props
653
- ? props.board
654
- : (("spaces" in props
655
- ? {
656
- id: "__square-grid__",
657
- spaces: props.spaces,
658
- pieces: props.pieces ?? [],
659
- edges: props.edges ?? [],
660
- vertices: props.vertices ?? [],
661
- }
662
- : {
663
- id: "__square-grid__",
664
- rows: props.rows ?? 0,
665
- cols: props.cols ?? 0,
666
- cells: props.cells,
667
- pieces: props.pieces ?? [],
668
- edges: props.edges ?? [],
669
- vertices: props.vertices ?? [],
670
- }) satisfies AnySquareBoardInput);
671
- const [hoveredInteractiveSpaceId, setHoveredInteractiveSpaceId] = useState<
672
- string | null
673
- >(null);
674
- const [hoveredInteractiveEdgeId, setHoveredInteractiveEdgeId] = useState<
675
- string | null
676
- >(null);
677
- const [hoveredInteractiveVertexId, setHoveredInteractiveVertexId] = useState<
678
- string | null
679
- >(null);
680
-
681
- // Use the unified pan/zoom hook
682
- const {
683
- transform,
684
- bind,
685
- isDragging: isPanning,
686
- } = usePanZoom({
687
- enabled: enablePanZoom,
688
- initialZoom,
689
- minZoom,
690
- maxZoom,
691
- mode: "viewbox",
692
- });
693
-
694
- // Coordinate label margin
695
- const labelMargin = showCoordinates && coordinateStyle !== "none" ? 24 : 0;
696
-
697
- const normalizedBoard = useMemo<NormalizedSquareBoard<AnySquareBoardInput>>(
698
- () => normalizeSquareBoardInput(board),
699
- [board],
700
- );
701
-
702
- const rows = normalizedBoard.rows;
703
- const cols = normalizedBoard.cols;
704
- const resolvedEdges = normalizedBoard.edges as Array<
705
- NormalizedSquareEdgeOf<AnySquareBoardInput>
706
- >;
707
- const resolvedVertices = normalizedBoard.vertices as Array<
708
- NormalizedSquareVertexOf<AnySquareBoardInput>
709
- >;
710
- const resolvedPieces = normalizedBoard.pieces as Array<
711
- NormalizedSquarePieceOf<AnySquareBoardInput>
712
- >;
713
-
714
- // Calculate grid dimensions
715
- const gridWidth = cols * cellSize;
716
- const gridHeight = rows * cellSize;
717
- const totalWidth = gridWidth + labelMargin;
718
- const totalHeight = gridHeight + labelMargin;
719
-
720
- const renderableCells = useMemo(() => {
721
- const result: Array<{ row: number; col: number }> = [];
722
- for (let row = 0; row < rows; row++) {
723
- for (let col = 0; col < cols; col++) {
724
- result.push({ row, col });
725
- }
726
- }
727
- return result;
728
- }, [rows, cols]);
729
-
730
- const resolvedCells = useMemo<SquareCellWithId[]>(() => {
731
- if (normalizedBoard.cells.length > 0) {
732
- return normalizedBoard.cells.map((cell) => ({
733
- ...cell,
734
- id: getCellId(cell),
735
- }));
736
- }
737
-
738
- return renderableCells.map(({ row, col }) => ({
739
- id: `${row},${col}`,
740
- row,
741
- col,
742
- }));
743
- }, [normalizedBoard.cells, renderableCells]);
744
-
745
- const cellsById = useMemo(
746
- () => new Map(resolvedCells.map((cell) => [cell.id, cell] as const)),
747
- [resolvedCells],
748
- );
749
-
750
- const resolvedEdgePositions = useMemo(
751
- () =>
752
- resolvedEdges.flatMap((edge) => {
753
- if (edge.spaceIds.length < 2) {
754
- return [];
755
- }
756
- const firstCell = cellsById.get(edge.spaceIds[0] ?? "");
757
- const secondCell = cellsById.get(edge.spaceIds[1] ?? "");
758
- if (!firstCell || !secondCell) {
759
- return [];
760
- }
761
- const position = edgePositionForCells(
762
- firstCell,
763
- secondCell,
764
- cellSize,
765
- labelMargin,
766
- );
767
- return position
768
- ? [
769
- {
770
- edge,
771
- interactiveEdge: {
772
- ...edge,
773
- position,
774
- } as InteractiveSquareEdge<AnySquareBoardInput>,
775
- },
776
- ]
777
- : [];
778
- }),
779
- [cellSize, cellsById, resolvedEdges, labelMargin],
780
- );
781
-
782
- const resolvedVertexPositions = useMemo(
783
- () =>
784
- resolvedVertices.flatMap((vertex) => {
785
- const vertexCells = vertex.spaceIds.flatMap((spaceId) => {
786
- const cell = cellsById.get(spaceId);
787
- return cell ? [cell] : [];
788
- });
789
- const position = vertexPositionForCells(
790
- vertexCells,
791
- cellSize,
792
- labelMargin,
793
- );
794
- return position
795
- ? [
796
- {
797
- vertex,
798
- interactiveVertex: {
799
- ...vertex,
800
- position,
801
- } as InteractiveSquareVertex<AnySquareBoardInput>,
802
- },
803
- ]
804
- : [];
805
- }),
806
- [cellSize, cellsById, labelMargin, resolvedVertices],
807
- );
808
-
809
- // Calculate viewBox for pan/zoom
810
- const viewBoxWidth = totalWidth / transform.zoom;
811
- const viewBoxHeight = totalHeight / transform.zoom;
812
- const viewBoxX = (totalWidth - viewBoxWidth) / 2 - transform.pan.x;
813
- const viewBoxY = (totalHeight - viewBoxHeight) / 2 - transform.pan.y;
814
-
815
- // Determine SVG dimensions
816
- const svgWidth = width ?? totalWidth;
817
- const svgHeight = height ?? totalHeight;
818
-
819
- return (
820
- <svg
821
- width={svgWidth}
822
- height={svgHeight}
823
- viewBox={
824
- enablePanZoom
825
- ? `${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}`
826
- : `0 0 ${totalWidth} ${totalHeight}`
827
- }
828
- className={clsx(
829
- "square-grid",
830
- enablePanZoom && "touch-none",
831
- isPanning && "cursor-grabbing",
832
- enablePanZoom && !isPanning && "cursor-grab",
833
- className,
834
- )}
835
- {...bind()}
836
- role="img"
837
- aria-label={`${rows}x${cols} game grid`}
838
- >
839
- <defs>
840
- {/* Drop shadow for pieces */}
841
- <filter id="pieceShadow" x="-20%" y="-20%" width="140%" height="140%">
842
- <feDropShadow dx="1" dy="2" stdDeviation="2" floodOpacity="0.4" />
843
- </filter>
844
- </defs>
845
-
846
- {/* Cells layer */}
847
- <g className="cells" role="list" aria-label="Grid cells">
848
- {renderableCells.map(({ row, col }) => {
849
- const x = labelMargin + col * cellSize;
850
- const y = row * cellSize;
851
-
852
- return (
853
- <g
854
- key={`${row}-${col}`}
855
- transform={`translate(${x}, ${y})`}
856
- role="listitem"
857
- aria-label={
858
- coordinateStyle === "algebraic"
859
- ? toAlgebraic(row, col, rows)
860
- : toNumeric(row, col)
861
- }
862
- >
863
- {renderCell(row, col)}
864
- </g>
865
- );
866
- })}
867
- </g>
868
-
869
- {interactiveSpaces && (
870
- <g className="interactive-spaces" aria-label="Interactive spaces">
871
- {resolvedCells.map((space) => {
872
- const state = interactiveTargetRenderState(
873
- interactiveSpaces,
874
- space.id,
875
- hoveredInteractiveSpaceId === space.id,
876
- );
877
- const isSelectable = isInteractiveTargetSelectable(
878
- interactiveSpaces,
879
- state,
880
- );
881
- const x = labelMargin + space.col * cellSize;
882
- const y = space.row * cellSize;
883
- return (
884
- <g
885
- key={space.id}
886
- transform={`translate(${x}, ${y})`}
887
- onClick={
888
- isSelectable
889
- ? () => {
890
- void interactiveSpaces.selectTargetId?.(space.id);
891
- }
892
- : undefined
893
- }
894
- onKeyDown={(event) =>
895
- handleKeyboardActivation(
896
- event,
897
- isSelectable
898
- ? () => {
899
- void interactiveSpaces.selectTargetId?.(space.id);
900
- }
901
- : undefined,
902
- )
903
- }
904
- onPointerEnter={() => setHoveredInteractiveSpaceId(space.id)}
905
- onPointerLeave={() =>
906
- setHoveredInteractiveSpaceId((currentId) =>
907
- currentId === space.id ? null : currentId,
908
- )
909
- }
910
- className={clsx(isSelectable && "cursor-pointer")}
911
- role={isSelectable ? "button" : undefined}
912
- tabIndex={isSelectable ? 0 : undefined}
913
- aria-label={
914
- isSelectable ? `Select space ${space.id}` : undefined
915
- }
916
- >
917
- {isSelectable && (
918
- <rect
919
- x={0}
920
- y={0}
921
- width={cellSize}
922
- height={cellSize}
923
- fill="rgba(255,255,255,0.001)"
924
- pointerEvents="all"
925
- />
926
- )}
927
- {renderInteractiveSpace
928
- ? renderInteractiveSpace(space, state)
929
- : null}
930
- </g>
931
- );
932
- })}
933
- </g>
934
- )}
935
-
936
- {renderEdge && resolvedEdgePositions.length > 0 && (
937
- <g className="edges" aria-label="Board edges">
938
- {resolvedEdgePositions.map(({ edge, interactiveEdge }) => (
939
- <g key={edge.id}>{renderEdge(edge, interactiveEdge.position)}</g>
940
- ))}
941
- </g>
942
- )}
943
-
944
- {interactiveEdges && (
945
- <g className="interactive-edges" aria-label="Interactive edges">
946
- {resolvedEdgePositions.map(({ interactiveEdge: edge }) => {
947
- const state = interactiveTargetRenderState(
948
- interactiveEdges,
949
- edge.id,
950
- hoveredInteractiveEdgeId === edge.id,
951
- );
952
- const isSelectable = isInteractiveTargetSelectable(
953
- interactiveEdges,
954
- state,
955
- );
956
- return (
957
- <g
958
- key={edge.id}
959
- onClick={
960
- isSelectable
961
- ? () => {
962
- void interactiveEdges.selectTargetId?.(edge.id);
963
- }
964
- : undefined
965
- }
966
- onKeyDown={(event) =>
967
- handleKeyboardActivation(
968
- event,
969
- isSelectable
970
- ? () => {
971
- void interactiveEdges.selectTargetId?.(edge.id);
972
- }
973
- : undefined,
974
- )
975
- }
976
- onPointerEnter={() => setHoveredInteractiveEdgeId(edge.id)}
977
- onPointerLeave={() =>
978
- setHoveredInteractiveEdgeId((currentId) =>
979
- currentId === edge.id ? null : currentId,
980
- )
981
- }
982
- className={clsx(isSelectable && "cursor-pointer")}
983
- role={isSelectable ? "button" : undefined}
984
- tabIndex={isSelectable ? 0 : undefined}
985
- aria-label={isSelectable ? `Select edge ${edge.id}` : undefined}
986
- >
987
- {renderInteractiveEdge ? (
988
- renderInteractiveEdge(edge, edge.position, state)
989
- ) : state.isEnabled && state.isEligible ? (
990
- <line
991
- x1={edge.position.x1}
992
- y1={edge.position.y1}
993
- x2={edge.position.x2}
994
- y2={edge.position.y2}
995
- stroke="rgba(255,255,255,0.001)"
996
- strokeWidth={Math.max(12, cellSize * 0.18)}
997
- pointerEvents="stroke"
998
- />
999
- ) : null}
1000
- </g>
1001
- );
1002
- })}
1003
- </g>
1004
- )}
1005
-
1006
- {renderVertex && resolvedVertexPositions.length > 0 && (
1007
- <g className="vertices" aria-label="Board vertices">
1008
- {resolvedVertexPositions.map(({ vertex, interactiveVertex }) => (
1009
- <g key={vertex.id}>
1010
- {renderVertex(vertex, interactiveVertex.position)}
1011
- </g>
1012
- ))}
1013
- </g>
1014
- )}
1015
-
1016
- {interactiveVertices && (
1017
- <g className="interactive-vertices" aria-label="Interactive vertices">
1018
- {resolvedVertexPositions.map(({ interactiveVertex: vertex }) => {
1019
- const state = interactiveTargetRenderState(
1020
- interactiveVertices,
1021
- vertex.id,
1022
- hoveredInteractiveVertexId === vertex.id,
1023
- );
1024
- const isSelectable = isInteractiveTargetSelectable(
1025
- interactiveVertices,
1026
- state,
1027
- );
1028
- return (
1029
- <g
1030
- key={vertex.id}
1031
- onClick={
1032
- isSelectable
1033
- ? () => {
1034
- void interactiveVertices.selectTargetId?.(vertex.id);
1035
- }
1036
- : undefined
1037
- }
1038
- onKeyDown={(event) =>
1039
- handleKeyboardActivation(
1040
- event,
1041
- isSelectable
1042
- ? () => {
1043
- void interactiveVertices.selectTargetId?.(vertex.id);
1044
- }
1045
- : undefined,
1046
- )
1047
- }
1048
- onPointerEnter={() => setHoveredInteractiveVertexId(vertex.id)}
1049
- onPointerLeave={() =>
1050
- setHoveredInteractiveVertexId((currentId) =>
1051
- currentId === vertex.id ? null : currentId,
1052
- )
1053
- }
1054
- className={clsx(isSelectable && "cursor-pointer")}
1055
- role={isSelectable ? "button" : undefined}
1056
- tabIndex={isSelectable ? 0 : undefined}
1057
- aria-label={
1058
- isSelectable ? `Select vertex ${vertex.id}` : undefined
1059
- }
1060
- >
1061
- {renderInteractiveVertex ? (
1062
- renderInteractiveVertex(vertex, vertex.position, state)
1063
- ) : state.isEnabled && state.isEligible ? (
1064
- <circle
1065
- cx={vertex.position.x}
1066
- cy={vertex.position.y}
1067
- r={Math.max(8, cellSize * 0.12)}
1068
- fill="rgba(255,255,255,0.001)"
1069
- pointerEvents="all"
1070
- />
1071
- ) : null}
1072
- </g>
1073
- );
1074
- })}
1075
- </g>
1076
- )}
1077
-
1078
- {/* Coordinate labels */}
1079
- {showCoordinates && coordinateStyle !== "none" && (
1080
- <g className="coordinates" aria-hidden="true">
1081
- {/* File labels (a-h) - bottom */}
1082
- {Array.from({ length: cols }).map((_, col) => {
1083
- const label =
1084
- coordinateStyle === "algebraic"
1085
- ? String.fromCharCode(97 + col)
1086
- : String(col + 1);
1087
- return (
1088
- <text
1089
- key={`file-${col}`}
1090
- x={labelMargin + col * cellSize + cellSize / 2}
1091
- y={gridHeight + 16}
1092
- textAnchor="middle"
1093
- fill="#64748b"
1094
- fontSize={12}
1095
- fontWeight="500"
1096
- >
1097
- {label}
1098
- </text>
1099
- );
1100
- })}
1101
- {/* Rank labels (1-8) - left */}
1102
- {Array.from({ length: rows }).map((_, row) => {
1103
- const label =
1104
- coordinateStyle === "algebraic"
1105
- ? String(rows - row)
1106
- : String(row + 1);
1107
- return (
1108
- <text
1109
- key={`rank-${row}`}
1110
- x={10}
1111
- y={row * cellSize + cellSize / 2 + 4}
1112
- textAnchor="middle"
1113
- fill="#64748b"
1114
- fontSize={12}
1115
- fontWeight="500"
1116
- >
1117
- {label}
1118
- </text>
1119
- );
1120
- })}
1121
- </g>
1122
- )}
1123
-
1124
- {/* Pieces layer */}
1125
- <g className="pieces" role="list" aria-label="Game pieces">
1126
- {resolvedPieces.map((piece) => {
1127
- const x = labelMargin + piece.col * cellSize + cellSize / 2;
1128
- const y = piece.row * cellSize + cellSize / 2;
1129
-
1130
- return (
1131
- <g
1132
- key={piece.id}
1133
- transform={`translate(${x}, ${y})`}
1134
- role="listitem"
1135
- aria-label={`${piece.owner ?? ""} ${piece.typeId}`}
1136
- >
1137
- {renderPiece(piece)}
1138
- </g>
1139
- );
1140
- })}
1141
- </g>
1142
-
1143
- {/* Zoom indicator */}
1144
- {enablePanZoom && transform.zoom !== 1 && (
1145
- <g
1146
- transform={`translate(${viewBoxX + 10}, ${viewBoxY + viewBoxHeight - 30})`}
1147
- >
1148
- <rect
1149
- x={0}
1150
- y={0}
1151
- width={60}
1152
- height={20}
1153
- rx={4}
1154
- fill="rgba(0,0,0,0.6)"
1155
- />
1156
- <text x={30} y={14} textAnchor="middle" fill="white" fontSize={12}>
1157
- {Math.round(transform.zoom * 100)}%
1158
- </text>
1159
- </g>
1160
- )}
1161
- </svg>
1162
- );
1163
- }
1164
-
1165
- export const SquareGrid = SquareGridImpl as SquareGridComponent;