@jgengine/core 0.10.0 → 0.12.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 (727) hide show
  1. package/CHANGELOG.md +39 -2
  2. package/LICENSE +203 -661
  3. package/README.md +1 -1
  4. package/dist/ai/crowd.d.ts +55 -0
  5. package/dist/ai/crowd.js +114 -0
  6. package/dist/ai/flock.d.ts +5 -2
  7. package/dist/ai/flock.js +5 -2
  8. package/dist/ai/groupAssist.d.ts +1 -0
  9. package/dist/ai/groupAssist.js +1 -0
  10. package/dist/ai/heatSystem.d.ts +76 -0
  11. package/dist/ai/heatSystem.js +97 -0
  12. package/dist/ai/interestScheduler.d.ts +107 -0
  13. package/dist/ai/interestScheduler.js +118 -0
  14. package/dist/ai/jobBoard.d.ts +1 -0
  15. package/dist/ai/jobBoard.js +1 -0
  16. package/dist/ai/laneSelect.d.ts +5 -2
  17. package/dist/ai/laneSelect.js +5 -2
  18. package/dist/ai/mobBrain.d.ts +1 -0
  19. package/dist/ai/mobBrain.js +1 -0
  20. package/dist/ai/populationDirector.d.ts +103 -0
  21. package/dist/ai/populationDirector.js +190 -0
  22. package/dist/ai/pursuit.d.ts +56 -0
  23. package/dist/ai/pursuit.js +53 -0
  24. package/dist/ai/spawnDirector.d.ts +7 -2
  25. package/dist/ai/spawnDirector.js +18 -15
  26. package/dist/ai/spawnPoint.d.ts +3 -1
  27. package/dist/ai/spawnPoint.js +3 -1
  28. package/dist/ai/targetAcquisition.d.ts +84 -0
  29. package/dist/ai/targetAcquisition.js +92 -0
  30. package/dist/anim/easing.d.ts +19 -0
  31. package/dist/anim/easing.js +19 -0
  32. package/dist/anim/oscillator.d.ts +12 -0
  33. package/dist/anim/oscillator.js +25 -0
  34. package/dist/area/areaEffectField.d.ts +127 -0
  35. package/dist/area/areaEffectField.js +175 -0
  36. package/dist/area/stackPolicy.d.ts +47 -0
  37. package/dist/area/stackPolicy.js +99 -0
  38. package/dist/authoring.d.ts +2 -1
  39. package/dist/authoring.js +2 -1
  40. package/dist/board/laneBoard.d.ts +13 -0
  41. package/dist/board/laneBoard.js +13 -0
  42. package/dist/board/timelineBoard.d.ts +10 -0
  43. package/dist/board/timelineBoard.js +10 -0
  44. package/dist/combat/abilityKit.d.ts +18 -0
  45. package/dist/combat/abilityKit.js +24 -9
  46. package/dist/combat/animationState.d.ts +5 -0
  47. package/dist/combat/animationState.js +5 -0
  48. package/dist/combat/breakMeters.d.ts +10 -0
  49. package/dist/combat/breakMeters.js +10 -0
  50. package/dist/combat/castRunner.d.ts +5 -0
  51. package/dist/combat/castRunner.js +5 -0
  52. package/dist/combat/comboPoints.d.ts +5 -0
  53. package/dist/combat/comboPoints.js +5 -0
  54. package/dist/combat/comboString.d.ts +5 -0
  55. package/dist/combat/comboString.js +5 -0
  56. package/dist/combat/damageInterceptors.d.ts +192 -0
  57. package/dist/combat/damageInterceptors.js +251 -0
  58. package/dist/combat/damageMatchup.d.ts +68 -0
  59. package/dist/combat/damageMatchup.js +42 -0
  60. package/dist/combat/damageResolution.d.ts +63 -0
  61. package/dist/combat/damageResolution.js +50 -0
  62. package/dist/combat/death.d.ts +7 -1
  63. package/dist/combat/death.js +5 -0
  64. package/dist/combat/deathReason.d.ts +14 -0
  65. package/dist/combat/deathReason.js +1 -0
  66. package/dist/combat/defensiveWindow.d.ts +5 -0
  67. package/dist/combat/defensiveWindow.js +5 -0
  68. package/dist/combat/dotField.d.ts +5 -1
  69. package/dist/combat/dotField.js +5 -1
  70. package/dist/combat/downed.d.ts +5 -0
  71. package/dist/combat/downed.js +5 -0
  72. package/dist/combat/effects.d.ts +5 -0
  73. package/dist/combat/effects.js +5 -0
  74. package/dist/combat/encounterSequence.d.ts +258 -0
  75. package/dist/combat/encounterSequence.js +426 -0
  76. package/dist/combat/hitReaction.d.ts +57 -1
  77. package/dist/combat/hitReaction.js +45 -5
  78. package/dist/combat/magazine.d.ts +54 -0
  79. package/dist/combat/magazine.js +96 -0
  80. package/dist/combat/projectiles.d.ts +5 -0
  81. package/dist/combat/projectiles.js +32 -14
  82. package/dist/combat/receivedDamage.d.ts +117 -0
  83. package/dist/combat/receivedDamage.js +92 -0
  84. package/dist/combat/regenShield.d.ts +5 -1
  85. package/dist/combat/regenShield.js +5 -1
  86. package/dist/combat/renderCues.d.ts +51 -0
  87. package/dist/combat/renderCues.js +44 -0
  88. package/dist/combat/resourcePool.d.ts +5 -0
  89. package/dist/combat/resourcePool.js +5 -0
  90. package/dist/combat/rewardAllocation.d.ts +121 -0
  91. package/dist/combat/rewardAllocation.js +251 -0
  92. package/dist/combat/shotOrigin.d.ts +18 -1
  93. package/dist/combat/shotOrigin.js +39 -0
  94. package/dist/combat/statusApplication.d.ts +96 -0
  95. package/dist/combat/statusApplication.js +81 -0
  96. package/dist/combat/telegraph.d.ts +11 -2
  97. package/dist/combat/telegraph.js +11 -2
  98. package/dist/combat/thresholdCrossings.d.ts +85 -0
  99. package/dist/combat/thresholdCrossings.js +116 -0
  100. package/dist/combat.d.ts +35 -0
  101. package/dist/combat.js +35 -0
  102. package/dist/commands/commandRegistry.d.ts +11 -0
  103. package/dist/commands/commandRegistry.js +9 -2
  104. package/dist/crafting/crop.d.ts +18 -0
  105. package/dist/crafting/crop.js +18 -0
  106. package/dist/crafting/production.d.ts +5 -0
  107. package/dist/crafting/production.js +5 -0
  108. package/dist/devtools/controls.d.ts +38 -0
  109. package/dist/devtools/controls.js +181 -0
  110. package/dist/devtools/devtools.d.ts +5 -197
  111. package/dist/devtools/devtools.js +38 -774
  112. package/dist/devtools/discover.d.ts +32 -0
  113. package/dist/devtools/discover.js +240 -0
  114. package/dist/devtools/fallbackSeams.d.ts +50 -0
  115. package/dist/devtools/fallbackSeams.js +111 -0
  116. package/dist/devtools/frame.d.ts +15 -0
  117. package/dist/devtools/frame.js +247 -0
  118. package/dist/devtools/logs.d.ts +15 -0
  119. package/dist/devtools/logs.js +86 -0
  120. package/dist/devtools/overrides.d.ts +16 -0
  121. package/dist/devtools/overrides.js +66 -0
  122. package/dist/devtools/rewriteTunables.d.ts +17 -0
  123. package/dist/devtools/rewriteTunables.js +276 -0
  124. package/dist/devtools/saveEndpoint.d.ts +46 -0
  125. package/dist/devtools/saveEndpoint.js +48 -0
  126. package/dist/devtools/transformTunables.d.ts +3 -0
  127. package/dist/devtools/transformTunables.js +3 -0
  128. package/dist/devtools/tunableSchema.d.ts +31 -0
  129. package/dist/devtools/tunableSchema.js +31 -0
  130. package/dist/devtools/types.d.ts +212 -0
  131. package/dist/devtools/types.js +1 -0
  132. package/dist/economy/auctionBook.d.ts +122 -0
  133. package/dist/economy/auctionBook.js +213 -0
  134. package/dist/economy/currency.d.ts +12 -1
  135. package/dist/economy/currency.js +12 -1
  136. package/dist/economy/listingBook.d.ts +91 -0
  137. package/dist/economy/listingBook.js +144 -0
  138. package/dist/economy/priceHistory.d.ts +44 -0
  139. package/dist/economy/priceHistory.js +89 -0
  140. package/dist/economy/resourceLedger.d.ts +222 -0
  141. package/dist/economy/resourceLedger.js +353 -0
  142. package/dist/economy/sharedWallet.d.ts +14 -0
  143. package/dist/economy/sharedWallet.js +14 -0
  144. package/dist/economy/techTree.d.ts +12 -0
  145. package/dist/economy/techTree.js +12 -0
  146. package/dist/economy/unlockPoints.d.ts +92 -0
  147. package/dist/economy/unlockPoints.js +103 -0
  148. package/dist/economy/wallet.d.ts +26 -2
  149. package/dist/economy/wallet.js +35 -6
  150. package/dist/editor/commandApply.d.ts +5 -0
  151. package/dist/editor/commandApply.js +755 -0
  152. package/dist/editor/commands.d.ts +235 -7
  153. package/dist/editor/commands.js +82 -124
  154. package/dist/editor/directives.d.ts +94 -0
  155. package/dist/editor/directives.js +206 -0
  156. package/dist/editor/document.d.ts +194 -13
  157. package/dist/editor/document.js +1166 -19
  158. package/dist/editor/entityCatalog.d.ts +42 -0
  159. package/dist/editor/entityCatalog.js +101 -0
  160. package/dist/editor/environment.d.ts +74 -0
  161. package/dist/editor/environment.js +169 -0
  162. package/dist/editor/grid.d.ts +229 -0
  163. package/dist/editor/grid.js +366 -0
  164. package/dist/editor/gridAdapters.d.ts +80 -0
  165. package/dist/editor/gridAdapters.js +134 -0
  166. package/dist/editor/index.d.ts +98 -6
  167. package/dist/editor/index.js +13 -2
  168. package/dist/editor/liveSync.d.ts +127 -0
  169. package/dist/editor/liveSync.js +249 -0
  170. package/dist/editor/minimap.d.ts +31 -0
  171. package/dist/editor/minimap.js +73 -0
  172. package/dist/editor/runtimeInspector.d.ts +88 -0
  173. package/dist/editor/runtimeInspector.js +239 -0
  174. package/dist/editor/streamer.d.ts +66 -0
  175. package/dist/editor/streamer.js +68 -0
  176. package/dist/editor/types.d.ts +250 -1
  177. package/dist/editor/world.d.ts +136 -0
  178. package/dist/editor/world.js +303 -0
  179. package/dist/faction/reputation.d.ts +5 -0
  180. package/dist/faction/reputation.js +5 -0
  181. package/dist/format/distance.d.ts +17 -0
  182. package/dist/format/distance.js +18 -0
  183. package/dist/format/duration.d.ts +27 -0
  184. package/dist/format/duration.js +45 -0
  185. package/dist/format/speed.d.ts +17 -0
  186. package/dist/format/speed.js +25 -0
  187. package/dist/game/breeding.d.ts +161 -0
  188. package/dist/game/breeding.js +181 -0
  189. package/dist/game/cameraConfig.d.ts +451 -0
  190. package/dist/game/cameraConfig.js +1 -0
  191. package/dist/game/connectedPlayers.d.ts +6 -4
  192. package/dist/game/connectedPlayers.js +2 -2
  193. package/dist/game/cosmetics.d.ts +9 -0
  194. package/dist/game/cosmetics.js +16 -0
  195. package/dist/game/defineGame.d.ts +110 -5
  196. package/dist/game/defineGame.js +49 -4
  197. package/dist/game/defineSystem.d.ts +90 -0
  198. package/dist/game/defineSystem.js +47 -0
  199. package/dist/game/dialogue.d.ts +33 -0
  200. package/dist/game/dialogue.js +21 -0
  201. package/dist/game/events.d.ts +63 -13
  202. package/dist/game/events.js +5 -0
  203. package/dist/game/feed.d.ts +33 -0
  204. package/dist/game/feed.js +35 -2
  205. package/dist/game/gamePhase.d.ts +11 -0
  206. package/dist/game/gamePhase.js +16 -0
  207. package/dist/game/leaderboard.d.ts +5 -0
  208. package/dist/game/leaderboard.js +5 -0
  209. package/dist/game/loadout.d.ts +5 -0
  210. package/dist/game/loadout.js +5 -0
  211. package/dist/game/lootFilter.d.ts +2 -0
  212. package/dist/game/lootFilter.js +2 -0
  213. package/dist/game/lootPipeline.d.ts +164 -0
  214. package/dist/game/lootPipeline.js +216 -0
  215. package/dist/game/lootTable.d.ts +19 -2
  216. package/dist/game/lootTable.js +15 -5
  217. package/dist/game/objectives.d.ts +43 -0
  218. package/dist/game/objectives.js +36 -0
  219. package/dist/game/paramLayers.d.ts +148 -0
  220. package/dist/game/paramLayers.js +185 -0
  221. package/dist/game/ping.d.ts +5 -0
  222. package/dist/game/ping.js +5 -0
  223. package/dist/game/playableGame.d.ts +116 -432
  224. package/dist/game/playableGame.js +1 -1
  225. package/dist/game/progression.d.ts +35 -0
  226. package/dist/game/progression.js +19 -0
  227. package/dist/game/quest.d.ts +8 -0
  228. package/dist/game/quest.js +30 -3
  229. package/dist/game/race.d.ts +141 -0
  230. package/dist/game/race.js +169 -0
  231. package/dist/game/recordBook.d.ts +3 -1
  232. package/dist/game/recordBook.js +4 -1
  233. package/dist/game/ruleSelection.d.ts +75 -0
  234. package/dist/game/ruleSelection.js +127 -0
  235. package/dist/game/runDraft.d.ts +5 -0
  236. package/dist/game/runDraft.js +5 -0
  237. package/dist/game/saveStore.d.ts +111 -0
  238. package/dist/game/saveStore.js +312 -0
  239. package/dist/game/snapshotHistory.d.ts +67 -0
  240. package/dist/game/snapshotHistory.js +95 -0
  241. package/dist/game/social.d.ts +38 -1
  242. package/dist/game/social.js +71 -0
  243. package/dist/game/spawnPoints.d.ts +5 -0
  244. package/dist/game/spawnPoints.js +5 -0
  245. package/dist/game/systemRuntime.d.ts +50 -0
  246. package/dist/game/systemRuntime.js +157 -0
  247. package/dist/game/systemSchedule.d.ts +72 -0
  248. package/dist/game/systemSchedule.js +242 -0
  249. package/dist/game/toasts.d.ts +43 -0
  250. package/dist/game/toasts.js +44 -0
  251. package/dist/game/trade.d.ts +10 -0
  252. package/dist/game/trade.js +10 -0
  253. package/dist/game/tribe.d.ts +184 -0
  254. package/dist/game/tribe.js +432 -0
  255. package/dist/game/unlocks.d.ts +8 -0
  256. package/dist/game/unlocks.js +16 -0
  257. package/dist/game/vfxInstance.d.ts +138 -0
  258. package/dist/game/vfxInstance.js +96 -0
  259. package/dist/game/worldItem.d.ts +17 -4
  260. package/dist/game/worldItem.js +17 -4
  261. package/dist/gameplay.d.ts +93 -0
  262. package/dist/gameplay.js +93 -0
  263. package/dist/index.d.ts +5 -14
  264. package/dist/index.js +5 -14
  265. package/dist/input/actionBindings.d.ts +23 -6
  266. package/dist/input/actionBindings.js +23 -6
  267. package/dist/input/axisInput.d.ts +7 -2
  268. package/dist/input/axisInput.js +7 -2
  269. package/dist/input/controlGroups.d.ts +67 -0
  270. package/dist/input/controlGroups.js +41 -0
  271. package/dist/input/touchScheme.d.ts +82 -6
  272. package/dist/input/touchScheme.js +123 -10
  273. package/dist/interaction/lockpick.d.ts +61 -0
  274. package/dist/interaction/lockpick.js +168 -0
  275. package/dist/interaction/proximityPrompt.d.ts +2 -0
  276. package/dist/interaction/proximityPrompt.js +2 -0
  277. package/dist/interaction/qte.d.ts +5 -0
  278. package/dist/interaction/qte.js +5 -0
  279. package/dist/interaction/skillCheck.d.ts +5 -0
  280. package/dist/interaction/skillCheck.js +5 -0
  281. package/dist/inventory/encumbrance.d.ts +69 -0
  282. package/dist/inventory/encumbrance.js +68 -0
  283. package/dist/inventory/inventoryModel.d.ts +12 -0
  284. package/dist/inventory/inventoryModel.js +12 -0
  285. package/dist/inventory/shapedGrid.d.ts +16 -0
  286. package/dist/inventory/shapedGrid.js +16 -0
  287. package/dist/inventory/slotModel.d.ts +10 -0
  288. package/dist/inventory/slotModel.js +10 -0
  289. package/dist/item/durability.d.ts +5 -0
  290. package/dist/item/durability.js +6 -3
  291. package/dist/item/itemIdentity.d.ts +171 -0
  292. package/dist/item/itemIdentity.js +138 -0
  293. package/dist/item/itemInstanceRegistry.d.ts +32 -0
  294. package/dist/item/itemInstanceRegistry.js +34 -0
  295. package/dist/item/itemgen.d.ts +163 -0
  296. package/dist/item/itemgen.js +151 -0
  297. package/dist/item/modularItem.d.ts +5 -0
  298. package/dist/item/modularItem.js +5 -0
  299. package/dist/item/use.d.ts +5 -0
  300. package/dist/item/use.js +5 -0
  301. package/dist/item/useBehavior.d.ts +142 -0
  302. package/dist/item/useBehavior.js +105 -0
  303. package/dist/item/weapon.d.ts +5 -0
  304. package/dist/item/weapon.js +5 -0
  305. package/dist/math/scalar.d.ts +25 -0
  306. package/dist/math/scalar.js +47 -0
  307. package/dist/meta/changelog.d.ts +4 -1
  308. package/dist/meta/changelog.js +39 -2
  309. package/dist/movement/avoidance.d.ts +45 -0
  310. package/dist/movement/avoidance.js +123 -0
  311. package/dist/movement/cameraRig.d.ts +8 -0
  312. package/dist/movement/cameraRig.js +8 -0
  313. package/dist/movement/dash.d.ts +11 -0
  314. package/dist/movement/dash.js +11 -0
  315. package/dist/movement/formation.d.ts +135 -0
  316. package/dist/movement/formation.js +194 -0
  317. package/dist/movement/glideModel.d.ts +5 -0
  318. package/dist/movement/glideModel.js +5 -0
  319. package/dist/movement/grappleSwing.d.ts +5 -0
  320. package/dist/movement/grappleSwing.js +5 -0
  321. package/dist/movement/leaderTrail.d.ts +5 -0
  322. package/dist/movement/leaderTrail.js +5 -0
  323. package/dist/movement/movementModel.d.ts +50 -9
  324. package/dist/movement/movementModel.js +75 -29
  325. package/dist/movement/playerMovement.js +167 -12
  326. package/dist/movement/poseState.d.ts +13 -0
  327. package/dist/movement/poseState.js +16 -0
  328. package/dist/movement/steering.d.ts +8 -0
  329. package/dist/movement/steering.js +18 -0
  330. package/dist/multiplayer/combatSnapshot.d.ts +3 -0
  331. package/dist/multiplayer/combatSnapshot.js +16 -8
  332. package/dist/multiplayer/lagCompensation.d.ts +5 -0
  333. package/dist/multiplayer/lagCompensation.js +5 -0
  334. package/dist/multiplayer/presenceModel.d.ts +11 -1
  335. package/dist/multiplayer/presenceModel.js +17 -1
  336. package/dist/multiplayer/simultaneousCommit.d.ts +3 -0
  337. package/dist/multiplayer/simultaneousCommit.js +3 -0
  338. package/dist/multiplayer.d.ts +11 -0
  339. package/dist/multiplayer.js +11 -0
  340. package/dist/nav/pathFollow.d.ts +57 -0
  341. package/dist/nav/pathFollow.js +109 -0
  342. package/dist/orders/orderKinds.d.ts +156 -0
  343. package/dist/orders/orderKinds.js +197 -0
  344. package/dist/orders/orderQueue.d.ts +208 -0
  345. package/dist/orders/orderQueue.js +211 -0
  346. package/dist/physics/drivableVehicle.d.ts +31 -0
  347. package/dist/physics/drivableVehicle.js +16 -0
  348. package/dist/physics/flightDynamics.d.ts +82 -0
  349. package/dist/physics/flightDynamics.js +165 -0
  350. package/dist/physics/gravityField.d.ts +25 -0
  351. package/dist/physics/gravityField.js +41 -0
  352. package/dist/physics/kinematicVehicle.d.ts +108 -1
  353. package/dist/physics/kinematicVehicle.js +212 -15
  354. package/dist/physics/physicsWorld.d.ts +50 -12
  355. package/dist/physics/physicsWorld.js +120 -122
  356. package/dist/physics/radialImpulse.d.ts +25 -0
  357. package/dist/physics/radialImpulse.js +27 -0
  358. package/dist/physics/vehicleObstacles.d.ts +44 -0
  359. package/dist/physics/vehicleObstacles.js +115 -0
  360. package/dist/procedural.d.ts +3 -0
  361. package/dist/procedural.js +3 -0
  362. package/dist/progression/statGraph.d.ts +116 -0
  363. package/dist/progression/statGraph.js +318 -0
  364. package/dist/puzzle/cellGrid.d.ts +9 -0
  365. package/dist/puzzle/cellGrid.js +9 -0
  366. package/dist/puzzle/fallingPiece.d.ts +9 -0
  367. package/dist/puzzle/fallingPiece.js +9 -0
  368. package/dist/puzzle/nonogram.d.ts +39 -0
  369. package/dist/puzzle/nonogram.js +136 -0
  370. package/dist/random/nameGen.d.ts +5 -0
  371. package/dist/random/nameGen.js +5 -0
  372. package/dist/random/pick.d.ts +8 -0
  373. package/dist/random/pick.js +31 -0
  374. package/dist/random/rng.d.ts +26 -1
  375. package/dist/random/rng.js +32 -9
  376. package/dist/random/seedLink.d.ts +4 -0
  377. package/dist/random/seedLink.js +4 -0
  378. package/dist/relation/keyedValues.d.ts +54 -0
  379. package/dist/relation/keyedValues.js +115 -0
  380. package/dist/relation/thresholds.d.ts +62 -0
  381. package/dist/relation/thresholds.js +62 -0
  382. package/dist/render/color.d.ts +12 -0
  383. package/dist/render/color.js +26 -0
  384. package/dist/render/lookPreset.d.ts +41 -0
  385. package/dist/render/lookPreset.js +29 -0
  386. package/dist/render/postProcessing.d.ts +20 -0
  387. package/dist/render/postProcessing.js +21 -1
  388. package/dist/rules/predicate.d.ts +53 -0
  389. package/dist/rules/predicate.js +64 -0
  390. package/dist/rules/ruleEffects.d.ts +38 -0
  391. package/dist/rules/ruleEffects.js +37 -0
  392. package/dist/rules/triggeredRules.d.ts +160 -0
  393. package/dist/rules/triggeredRules.js +241 -0
  394. package/dist/runtime/adapter.d.ts +55 -3
  395. package/dist/runtime/adapter.js +57 -1
  396. package/dist/runtime/cameraDirector.d.ts +1 -0
  397. package/dist/runtime/cameraDirector.js +1 -0
  398. package/dist/runtime/commandRunner.d.ts +1 -0
  399. package/dist/runtime/commandRunner.js +1 -0
  400. package/dist/runtime/context/combat.d.ts +51 -0
  401. package/dist/runtime/context/combat.js +130 -0
  402. package/dist/runtime/context/combatFx.d.ts +23 -0
  403. package/dist/runtime/context/combatFx.js +161 -0
  404. package/dist/runtime/context/deathLoot.d.ts +30 -0
  405. package/dist/runtime/context/deathLoot.js +39 -0
  406. package/dist/runtime/context/player.d.ts +48 -0
  407. package/dist/runtime/context/player.js +125 -0
  408. package/dist/runtime/context/registries.d.ts +13 -0
  409. package/dist/runtime/context/registries.js +84 -0
  410. package/dist/runtime/context/scene.d.ts +59 -0
  411. package/dist/runtime/context/scene.js +291 -0
  412. package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
  413. package/dist/runtime/context/snapshotCodecs.js +26 -0
  414. package/dist/runtime/context/worldItems.d.ts +21 -0
  415. package/dist/runtime/context/worldItems.js +37 -0
  416. package/dist/runtime/descriptors/baseline.d.ts +48 -0
  417. package/dist/runtime/descriptors/baseline.js +157 -0
  418. package/dist/runtime/descriptors/features.d.ts +79 -0
  419. package/dist/runtime/descriptors/features.js +299 -0
  420. package/dist/runtime/gameContext.d.ts +2 -327
  421. package/dist/runtime/gameContext.js +273 -778
  422. package/dist/runtime/gameContextTypes.d.ts +467 -0
  423. package/dist/runtime/gameContextTypes.js +1 -0
  424. package/dist/runtime/gameRuntime.d.ts +1 -0
  425. package/dist/runtime/gameRuntime.js +1 -0
  426. package/dist/runtime/headlessRunner.d.ts +62 -0
  427. package/dist/runtime/headlessRunner.js +45 -0
  428. package/dist/runtime/hostPersistence.d.ts +12 -0
  429. package/dist/runtime/hostPersistence.js +12 -0
  430. package/dist/runtime/hostPolicy.d.ts +51 -0
  431. package/dist/runtime/hostPolicy.js +68 -0
  432. package/dist/runtime/hostedGameRunner.d.ts +11 -4
  433. package/dist/runtime/hostedGameRunner.js +27 -3
  434. package/dist/runtime/hostedWorldSession.d.ts +15 -5
  435. package/dist/runtime/hostedWorldSession.js +12 -4
  436. package/dist/runtime/inputSnapshot.d.ts +8 -3
  437. package/dist/runtime/inputSnapshot.js +6 -2
  438. package/dist/runtime/motionIntents.d.ts +11 -2
  439. package/dist/runtime/motionIntents.js +16 -2
  440. package/dist/runtime/perContext.d.ts +15 -0
  441. package/dist/runtime/perContext.js +21 -0
  442. package/dist/runtime/runtimeSave.d.ts +79 -0
  443. package/dist/runtime/runtimeSave.js +105 -0
  444. package/dist/runtime/save.d.ts +4 -0
  445. package/dist/runtime/save.js +4 -0
  446. package/dist/runtime/snapshot.d.ts +7 -0
  447. package/dist/runtime/snapshot.js +7 -0
  448. package/dist/runtime/visibility.d.ts +2 -1
  449. package/dist/runtime/visibility.js +2 -1
  450. package/dist/runtime/worldChannel.d.ts +7 -3
  451. package/dist/runtime/worldChannel.js +51 -6
  452. package/dist/runtime/worldMirror.d.ts +14 -2
  453. package/dist/runtime/worldMirror.js +15 -2
  454. package/dist/runtime/worldProjection.d.ts +47 -0
  455. package/dist/runtime/worldProjection.js +52 -0
  456. package/dist/runtime/worldReplication.d.ts +30 -7
  457. package/dist/runtime/worldReplication.js +41 -9
  458. package/dist/runtime/worldSnapshot.d.ts +42 -3
  459. package/dist/runtime/worldSnapshot.js +34 -7
  460. package/dist/scene/assetCatalog.d.ts +26 -0
  461. package/dist/scene/assetCatalog.js +4 -0
  462. package/dist/scene/assetDiagnostics.d.ts +69 -0
  463. package/dist/scene/assetDiagnostics.js +110 -0
  464. package/dist/scene/assetGenerator.d.ts +65 -0
  465. package/dist/scene/assetGenerator.js +64 -0
  466. package/dist/scene/assetSpace.d.ts +137 -0
  467. package/dist/scene/assetSpace.js +257 -0
  468. package/dist/scene/authoredTriggers.d.ts +219 -0
  469. package/dist/scene/authoredTriggers.js +454 -0
  470. package/dist/scene/behaviorRuntime.d.ts +75 -0
  471. package/dist/scene/behaviorRuntime.js +217 -0
  472. package/dist/scene/behaviors.d.ts +14 -2
  473. package/dist/scene/behaviors.js +7 -2
  474. package/dist/scene/bodyBind.d.ts +60 -0
  475. package/dist/scene/bodyBind.js +51 -0
  476. package/dist/scene/builtinSceneKinds.d.ts +5 -0
  477. package/dist/scene/builtinSceneKinds.js +67 -0
  478. package/dist/scene/colliders.d.ts +71 -1
  479. package/dist/scene/colliders.js +145 -1
  480. package/dist/scene/collisionMesh.d.ts +94 -0
  481. package/dist/scene/collisionMesh.js +699 -0
  482. package/dist/scene/companion.d.ts +147 -0
  483. package/dist/scene/companion.js +193 -0
  484. package/dist/scene/entityStats.d.ts +18 -11
  485. package/dist/scene/entityStats.js +21 -16
  486. package/dist/scene/entityStore.d.ts +12 -1
  487. package/dist/scene/entityStore.js +15 -0
  488. package/dist/scene/facing.d.ts +18 -0
  489. package/dist/scene/facing.js +26 -0
  490. package/dist/scene/modelSockets.d.ts +33 -0
  491. package/dist/scene/modelSockets.js +29 -0
  492. package/dist/scene/objectQuery.d.ts +2 -0
  493. package/dist/scene/objectQuery.js +2 -0
  494. package/dist/scene/objectStore.d.ts +4 -0
  495. package/dist/scene/objectStore.js +6 -0
  496. package/dist/scene/possession.d.ts +8 -0
  497. package/dist/scene/possession.js +14 -0
  498. package/dist/scene/roster.d.ts +3 -0
  499. package/dist/scene/roster.js +12 -0
  500. package/dist/scene/sceneKinds.d.ts +256 -0
  501. package/dist/scene/sceneKinds.js +218 -0
  502. package/dist/scene/sceneOwnership.d.ts +169 -0
  503. package/dist/scene/sceneOwnership.js +179 -0
  504. package/dist/scene/sceneRaycast.js +15 -0
  505. package/dist/scene/selectionBookmarks.d.ts +93 -0
  506. package/dist/scene/selectionBookmarks.js +122 -0
  507. package/dist/scene/vehicleSeat.d.ts +68 -0
  508. package/dist/scene/vehicleSeat.js +59 -0
  509. package/dist/sensor/concealment.d.ts +3 -0
  510. package/dist/sensor/concealment.js +3 -0
  511. package/dist/sensor/freezeMonitor.d.ts +1 -0
  512. package/dist/sensor/freezeMonitor.js +1 -0
  513. package/dist/sensor/frustumSensor.d.ts +7 -3
  514. package/dist/sensor/frustumSensor.js +7 -3
  515. package/dist/sensor/hiddenStateProbe.d.ts +5 -2
  516. package/dist/sensor/hiddenStateProbe.js +5 -2
  517. package/dist/sensor/recordingBuffer.d.ts +2 -1
  518. package/dist/sensor/recordingBuffer.js +2 -1
  519. package/dist/sensor/replayLoop.d.ts +6 -2
  520. package/dist/sensor/replayLoop.js +6 -2
  521. package/dist/sensor/revealQuery.d.ts +2 -1
  522. package/dist/sensor/revealQuery.js +2 -1
  523. package/dist/sensor/visionCone.d.ts +10 -3
  524. package/dist/sensor/visionCone.js +10 -3
  525. package/dist/session/contestedChannel.d.ts +1 -0
  526. package/dist/session/contestedChannel.js +1 -0
  527. package/dist/session/extraction.d.ts +1 -0
  528. package/dist/session/extraction.js +1 -0
  529. package/dist/session/roles.d.ts +6 -0
  530. package/dist/session/roles.js +6 -0
  531. package/dist/session/roundState.d.ts +8 -0
  532. package/dist/session/roundState.js +2 -0
  533. package/dist/settings/settingsModel.d.ts +1 -0
  534. package/dist/settings/settingsModel.js +1 -0
  535. package/dist/stats/accumulatorMeter.d.ts +6 -0
  536. package/dist/stats/accumulatorMeter.js +6 -0
  537. package/dist/stats/eventMeter.d.ts +5 -0
  538. package/dist/stats/eventMeter.js +5 -0
  539. package/dist/stats/rollCheck.d.ts +5 -0
  540. package/dist/stats/rollCheck.js +5 -0
  541. package/dist/stats/spawnLevelStats.d.ts +110 -0
  542. package/dist/stats/spawnLevelStats.js +140 -0
  543. package/dist/stats/statModifiers.d.ts +5 -0
  544. package/dist/stats/statModifiers.js +5 -0
  545. package/dist/stats/statPool.d.ts +70 -0
  546. package/dist/stats/statPool.js +59 -0
  547. package/dist/store/changeSignal.d.ts +3 -0
  548. package/dist/store/changeSignal.js +2 -0
  549. package/dist/store/defineKeyedStore.d.ts +38 -0
  550. package/dist/store/defineKeyedStore.js +42 -0
  551. package/dist/store/defineStore.d.ts +33 -0
  552. package/dist/store/defineStore.js +35 -0
  553. package/dist/store/observableKeyedStore.d.ts +7 -0
  554. package/dist/store/observableKeyedStore.js +35 -2
  555. package/dist/survival/decayMeter.d.ts +55 -0
  556. package/dist/survival/decayMeter.js +130 -57
  557. package/dist/survival/moodle.d.ts +2 -0
  558. package/dist/survival/regionHealth.d.ts +6 -0
  559. package/dist/survival/regionHealth.js +6 -3
  560. package/dist/tactics/fallingGrid.d.ts +4 -2
  561. package/dist/tactics/fallingGrid.js +4 -2
  562. package/dist/tactics/predictiveQuery.d.ts +3 -0
  563. package/dist/tactics/predictiveQuery.js +3 -0
  564. package/dist/tactics/snapshot.d.ts +2 -0
  565. package/dist/tactics/snapshot.js +2 -0
  566. package/dist/tactics/surface.d.ts +1 -0
  567. package/dist/tactics/surface.js +1 -0
  568. package/dist/tactics/tacticalGrid.d.ts +1 -0
  569. package/dist/tactics/tacticalGrid.js +1 -0
  570. package/dist/time/beatClock.d.ts +14 -4
  571. package/dist/time/beatClock.js +14 -4
  572. package/dist/time/calendarClock.d.ts +3 -0
  573. package/dist/time/calendarClock.js +3 -0
  574. package/dist/time/idleProgress.d.ts +4 -0
  575. package/dist/time/idleProgress.js +4 -0
  576. package/dist/time/simClock.d.ts +2 -0
  577. package/dist/time/simClock.js +20 -5
  578. package/dist/ui/actionModel.d.ts +110 -0
  579. package/dist/ui/actionModel.js +159 -0
  580. package/dist/ui/gameLayout.d.ts +30 -11
  581. package/dist/ui/gameLayout.js +30 -11
  582. package/dist/ui/hudDocument.d.ts +114 -0
  583. package/dist/ui/hudDocument.js +220 -0
  584. package/dist/ui/hudLayout.d.ts +38 -1
  585. package/dist/ui/hudLayout.js +220 -17
  586. package/dist/ui/selectionModel.d.ts +92 -0
  587. package/dist/ui/selectionModel.js +88 -0
  588. package/dist/ui/tooltipModel.d.ts +63 -0
  589. package/dist/ui/tooltipModel.js +82 -0
  590. package/dist/ui.d.ts +15 -0
  591. package/dist/ui.js +15 -0
  592. package/dist/visibility/bounds.d.ts +8 -1
  593. package/dist/visibility/bounds.js +8 -1
  594. package/dist/visibility/frustum.d.ts +12 -3
  595. package/dist/visibility/frustum.js +12 -3
  596. package/dist/work/jobQueue.d.ts +201 -0
  597. package/dist/work/jobQueue.js +253 -0
  598. package/dist/work/unitTraining.d.ts +58 -0
  599. package/dist/work/unitTraining.js +52 -0
  600. package/dist/world/authoredEntities.d.ts +29 -0
  601. package/dist/world/authoredEntities.js +29 -0
  602. package/dist/world/authoredObjects.d.ts +72 -0
  603. package/dist/world/authoredObjects.js +81 -0
  604. package/dist/world/authoredSpawn.d.ts +39 -0
  605. package/dist/world/authoredSpawn.js +37 -0
  606. package/dist/world/buildSockets.d.ts +152 -0
  607. package/dist/world/buildSockets.js +188 -0
  608. package/dist/world/buildingGenerator.d.ts +18 -0
  609. package/dist/world/buildingGenerator.js +62 -0
  610. package/dist/world/buildings.d.ts +5 -0
  611. package/dist/world/buildings.js +5 -0
  612. package/dist/world/catenary.d.ts +22 -0
  613. package/dist/world/catenary.js +69 -0
  614. package/dist/world/cityBlocks.d.ts +152 -0
  615. package/dist/world/cityBlocks.js +670 -0
  616. package/dist/world/cityContent.d.ts +77 -0
  617. package/dist/world/cityContent.js +261 -0
  618. package/dist/world/cityGeometry.d.ts +114 -0
  619. package/dist/world/cityGeometry.js +573 -0
  620. package/dist/world/cityKind.d.ts +314 -0
  621. package/dist/world/cityKind.js +1578 -0
  622. package/dist/world/features.d.ts +133 -10
  623. package/dist/world/features.js +66 -6
  624. package/dist/world/footprintGrid.d.ts +59 -0
  625. package/dist/world/footprintGrid.js +141 -0
  626. package/dist/world/geometry.d.ts +64 -0
  627. package/dist/world/geometry.js +94 -0
  628. package/dist/world/grassKind.d.ts +51 -0
  629. package/dist/world/grassKind.js +101 -0
  630. package/dist/world/markers.d.ts +50 -4
  631. package/dist/world/markers.js +49 -0
  632. package/dist/world/massing.js +1 -1
  633. package/dist/world/minimapBake.d.ts +78 -0
  634. package/dist/world/minimapBake.js +208 -0
  635. package/dist/world/minimapTrack.d.ts +17 -0
  636. package/dist/world/minimapTrack.js +22 -0
  637. package/dist/world/pathInstances.d.ts +27 -0
  638. package/dist/world/pathInstances.js +27 -0
  639. package/dist/world/pathNetwork.d.ts +159 -0
  640. package/dist/world/pathNetwork.js +684 -0
  641. package/dist/world/pathTerrain.d.ts +80 -0
  642. package/dist/world/pathTerrain.js +142 -0
  643. package/dist/world/place.d.ts +166 -0
  644. package/dist/world/place.js +96 -0
  645. package/dist/world/placeAsset.d.ts +81 -0
  646. package/dist/world/placeAsset.js +79 -0
  647. package/dist/world/placement.d.ts +4 -0
  648. package/dist/world/placement.js +4 -0
  649. package/dist/world/placementController.d.ts +6 -0
  650. package/dist/world/placementController.js +6 -0
  651. package/dist/world/poleLineKind.d.ts +40 -0
  652. package/dist/world/poleLineKind.js +97 -0
  653. package/dist/world/polyline.d.ts +34 -0
  654. package/dist/world/polyline.js +88 -0
  655. package/dist/world/resourceNode.d.ts +109 -0
  656. package/dist/world/resourceNode.js +140 -0
  657. package/dist/world/roads.d.ts +8 -0
  658. package/dist/world/roads.js +38 -0
  659. package/dist/world/scatter.js +2 -20
  660. package/dist/world/scatterRegion.d.ts +164 -0
  661. package/dist/world/scatterRegion.js +409 -0
  662. package/dist/world/sceneShapes.d.ts +43 -0
  663. package/dist/world/sceneShapes.js +1 -0
  664. package/dist/world/segment.d.ts +27 -0
  665. package/dist/world/segment.js +43 -0
  666. package/dist/world/soilKind.d.ts +48 -0
  667. package/dist/world/soilKind.js +92 -0
  668. package/dist/world/spatialSample.d.ts +241 -0
  669. package/dist/world/spatialSample.js +465 -0
  670. package/dist/world/streets.d.ts +29 -0
  671. package/dist/world/streets.js +55 -0
  672. package/dist/world/terraform.d.ts +213 -1
  673. package/dist/world/terraform.js +627 -22
  674. package/dist/world/terrain.d.ts +164 -20
  675. package/dist/world/terrain.js +325 -41
  676. package/dist/world/terrainGuides.d.ts +182 -0
  677. package/dist/world/terrainGuides.js +343 -0
  678. package/dist/world/vec2.d.ts +81 -0
  679. package/dist/world/vec2.js +128 -0
  680. package/dist/world/vec3.d.ts +49 -0
  681. package/dist/world/vec3.js +76 -0
  682. package/dist/world/vegetation.d.ts +71 -0
  683. package/dist/world/vegetation.js +151 -0
  684. package/dist/world/visibilityField.d.ts +154 -0
  685. package/dist/world/visibilityField.js +271 -0
  686. package/dist/world/volumetricClouds.d.ts +52 -0
  687. package/dist/world/volumetricClouds.js +43 -0
  688. package/dist/world/walls.d.ts +9 -0
  689. package/dist/world/walls.js +9 -0
  690. package/dist/world/waterKind.d.ts +55 -0
  691. package/dist/world/waterKind.js +99 -0
  692. package/dist/world.d.ts +141 -0
  693. package/dist/world.js +141 -0
  694. package/package.json +32 -8
  695. package/skills/game-design/SKILL.md +28 -0
  696. package/skills/game-design/agents/openai.yaml +4 -0
  697. package/skills/game-design/references/application-playbook.md +170 -0
  698. package/skills/game-design/references/field-guide.md +288 -0
  699. package/skills/jgengine/SKILL.md +54 -0
  700. package/skills/jgengine/api.md +527 -0
  701. package/skills/jgengine/capabilities.md +131 -0
  702. package/skills/jgengine/recipes/minimal-game.md +88 -0
  703. package/skills/jgengine-combat/SKILL.md +38 -0
  704. package/skills/jgengine-combat/api.md +585 -0
  705. package/skills/jgengine-combat/capabilities.md +213 -0
  706. package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
  707. package/skills/jgengine-combat/reference.md +108 -0
  708. package/skills/jgengine-gameplay/SKILL.md +39 -0
  709. package/skills/jgengine-gameplay/api.md +1920 -0
  710. package/skills/jgengine-gameplay/capabilities.md +454 -0
  711. package/skills/jgengine-gameplay/reference-systems.md +56 -0
  712. package/skills/jgengine-world/SKILL.md +51 -0
  713. package/skills/jgengine-world/api.md +3056 -0
  714. package/skills/jgengine-world/capabilities.md +476 -0
  715. package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
  716. package/skills/jgengine-world/reference.md +261 -0
  717. package/dist/cartridge/runtime.d.ts +0 -15
  718. package/dist/cartridge/runtime.js +0 -556
  719. package/dist/cartridge/spec.d.ts +0 -188
  720. package/dist/cartridge/spec.js +0 -19
  721. package/dist/cartridge/validate.d.ts +0 -2
  722. package/dist/cartridge/validate.js +0 -78
  723. package/dist/store/engineStore.d.ts +0 -22
  724. package/dist/store/engineStore.js +0 -50
  725. package/dist/store/features.d.ts +0 -10
  726. package/dist/store/features.js +0 -23
  727. package/llms.txt +0 -5389
@@ -0,0 +1,755 @@
1
+ import { applyDeltaToSnapshot, applySurfaceDeltaToSnapshot, applyWeightDeltaToSnapshot, } from "../world/terraform.js";
2
+ import { patchUiPanel, removeUiPanel } from "../ui/hudDocument.js";
3
+ import { cloneEditorDocument, collectDescendants, createPrefabFragment, editorDocumentExtras, ensureUniqueEditorId, extractEditorFragment, findEditorCollection, findEditorMarker, findEditorPrefab, findEditorVolume, importEditorDocumentJson, isEditorObjectLocked, wouldCreateCycle, } from "./document.js";
4
+ import { fillGridRect, floodFillGrid, migrateGridLayer, paintGridCells, resizeGridLayer, } from "./grid.js";
5
+ import { parseParams } from "../scene/sceneKinds.js";
6
+ /** Pure document mutation helpers + exhaustive EditorCommand apply table. */
7
+ function removeByIds(doc, ids) {
8
+ return {
9
+ version: 1,
10
+ markers: doc.markers.filter((marker) => !ids.has(marker.id)),
11
+ volumes: doc.volumes.filter((volume) => !ids.has(volume.id)),
12
+ paths: doc.paths.filter((path) => !ids.has(path.id)),
13
+ annotations: doc.annotations.filter((note) => !ids.has(note.id)),
14
+ ...editorDocumentExtras(doc),
15
+ collections: doc.collections.map((collection) => ({
16
+ ...collection,
17
+ memberIds: collection.memberIds.filter((id) => !ids.has(id)),
18
+ })),
19
+ };
20
+ }
21
+ function shifted(point, delta) {
22
+ return { x: point.x + delta.x, y: point.y + delta.y, z: point.z + delta.z };
23
+ }
24
+ function translateByIds(doc, ids, delta) {
25
+ return {
26
+ version: 1,
27
+ markers: doc.markers.map((marker) => ids.has(marker.id) ? { ...marker, position: shifted(marker.position, delta) } : marker),
28
+ volumes: doc.volumes.map((volume) => ids.has(volume.id) ? { ...volume, center: shifted(volume.center, delta) } : volume),
29
+ paths: doc.paths.map((path) => ids.has(path.id) ? { ...path, points: path.points.map((point) => shifted(point, delta)) } : path),
30
+ annotations: doc.annotations.map((note) => ids.has(note.id) ? { ...note, position: shifted(note.position, delta) } : note),
31
+ ...editorDocumentExtras(doc),
32
+ };
33
+ }
34
+ function collectIds(doc) {
35
+ const all = new Set();
36
+ for (const marker of doc.markers)
37
+ all.add(marker.id);
38
+ for (const volume of doc.volumes)
39
+ all.add(volume.id);
40
+ for (const path of doc.paths)
41
+ all.add(path.id);
42
+ for (const note of doc.annotations)
43
+ all.add(note.id);
44
+ return all;
45
+ }
46
+ /**
47
+ * Adding to a collection replaces a same-id member in that same collection (the existing
48
+ * upsert behavior); anything else collides with the document-global id space shared by markers,
49
+ * volumes, paths, and annotations, so it gets re-idded instead of shadowing whatever already
50
+ * holds that id in a different collection.
51
+ */
52
+ function ownIdOrGloballyUnique(doc, item, ownCollection) {
53
+ if (ownCollection.some((entry) => entry.id === item.id))
54
+ return item;
55
+ const id = ensureUniqueEditorId(item.id, collectIds(doc));
56
+ return id === item.id ? item : { ...item, id };
57
+ }
58
+ function copyId(base, taken) {
59
+ let candidate = `${base}_copy`;
60
+ let n = 2;
61
+ while (taken.has(candidate)) {
62
+ candidate = `${base}_copy${n}`;
63
+ n += 1;
64
+ }
65
+ taken.add(candidate);
66
+ return candidate;
67
+ }
68
+ function insertFragment(state, fragment, offset, renameAll) {
69
+ const doc = state.document;
70
+ const taken = collectIds(doc);
71
+ const clones = cloneEditorDocument(fragment);
72
+ const newIds = [];
73
+ const withId = (item) => {
74
+ const id = renameAll || taken.has(item.id) ? copyId(item.id, taken) : item.id;
75
+ taken.add(id);
76
+ newIds.push(id);
77
+ return { ...item, id };
78
+ };
79
+ const markers = clones.markers.map((marker) => withId({ ...marker, position: shifted(marker.position, offset) }));
80
+ const volumes = clones.volumes.map((volume) => withId({ ...volume, center: shifted(volume.center, offset) }));
81
+ const paths = clones.paths.map((path) => withId({ ...path, points: path.points.map((point) => shifted(point, offset)) }));
82
+ const annotations = clones.annotations.map((note) => withId({ ...note, position: shifted(note.position, offset) }));
83
+ if (newIds.length === 0)
84
+ return state;
85
+ return {
86
+ document: {
87
+ version: 1,
88
+ markers: [...doc.markers, ...markers],
89
+ volumes: [...doc.volumes, ...volumes],
90
+ paths: [...doc.paths, ...paths],
91
+ annotations: [...doc.annotations, ...annotations],
92
+ ...editorDocumentExtras(doc),
93
+ },
94
+ selection: newIds,
95
+ };
96
+ }
97
+ function fragmentAsDocument(fragment) {
98
+ return {
99
+ version: 1,
100
+ markers: [...fragment.markers],
101
+ volumes: [...fragment.volumes],
102
+ paths: [...fragment.paths],
103
+ annotations: [...fragment.annotations],
104
+ prefabs: [],
105
+ collections: [],
106
+ catalogs: [],
107
+ };
108
+ }
109
+ function tagWithInstance(item, prefabId, instanceId) {
110
+ return { ...item, meta: { ...item.meta, prefabId, prefabInstanceId: instanceId } };
111
+ }
112
+ /**
113
+ * Replaces one grid layer in place via `transform`, returning `null` when the layer is missing and
114
+ * the same state when the transform is a no-op (so history skips it).
115
+ */
116
+ function updateGridLayer(state, id, transform) {
117
+ const grids = state.document.grids;
118
+ if (grids === undefined)
119
+ return null;
120
+ const index = grids.findIndex((layer) => layer.id === id);
121
+ if (index < 0)
122
+ return null;
123
+ const next = transform(grids[index]);
124
+ if (next === grids[index])
125
+ return state;
126
+ const updated = grids.map((layer, i) => (i === index ? next : layer));
127
+ return { ...state, document: { ...state.document, grids: updated } };
128
+ }
129
+ /**
130
+ * Maps a single `fn` over all four placeable collections (markers/volumes/paths/annotations),
131
+ * returning just those four fields for spreading back into a document. Notes lack a `label` field,
132
+ * so callers that treat notes differently pass a dedicated `fnNote`; by default notes share `fn`.
133
+ */
134
+ function mapPlaceables(doc, fn, fnNote = fn) {
135
+ return {
136
+ markers: doc.markers.map(fn),
137
+ volumes: doc.volumes.map(fn),
138
+ paths: doc.paths.map(fn),
139
+ annotations: doc.annotations.map(fnNote),
140
+ };
141
+ }
142
+ const mutationHandlers = {
143
+ select: (state, command) => ({ ...state, selection: [...command.ids] }),
144
+ clearSelection: (state) => ({ ...state, selection: [] }),
145
+ setTransform: (state, command) => {
146
+ if (isEditorObjectLocked(state.document, command.id))
147
+ return null;
148
+ const marker = findEditorMarker(state.document, command.id);
149
+ const volume = findEditorVolume(state.document, command.id);
150
+ const note = state.document.annotations.find((n) => n.id === command.id);
151
+ const previous = marker?.position ?? volume?.center ?? note?.position;
152
+ // Moving a parent to a new position carries its whole subtree by the same delta.
153
+ const childDelta = command.position === undefined || previous === undefined
154
+ ? null
155
+ : {
156
+ x: command.position.x - previous.x,
157
+ y: command.position.y - previous.y,
158
+ z: command.position.z - previous.z,
159
+ };
160
+ const descendants = childDelta === null ? new Set() : collectDescendants(state.document, [command.id]);
161
+ const shift = (point) => childDelta === null ? point : shifted(point, childDelta);
162
+ const markers = state.document.markers.map((entry) => {
163
+ if (entry.id === command.id) {
164
+ return {
165
+ ...entry,
166
+ position: command.position === undefined ? entry.position : { ...command.position },
167
+ ...(command.rotationY === undefined ? {} : { rotationY: command.rotationY }),
168
+ };
169
+ }
170
+ return descendants.has(entry.id) ? { ...entry, position: shift(entry.position) } : entry;
171
+ });
172
+ const volumes = state.document.volumes.map((entry) => {
173
+ if (entry.id === command.id)
174
+ return command.position === undefined ? entry : { ...entry, center: { ...command.position } };
175
+ return descendants.has(entry.id) ? { ...entry, center: shift(entry.center) } : entry;
176
+ });
177
+ const paths = state.document.paths.map((entry) => descendants.has(entry.id) ? { ...entry, points: entry.points.map(shift) } : entry);
178
+ const annotations = state.document.annotations.map((entry) => {
179
+ if (entry.id === command.id)
180
+ return command.position === undefined ? entry : { ...entry, position: { ...command.position } };
181
+ return descendants.has(entry.id) ? { ...entry, position: shift(entry.position) } : entry;
182
+ });
183
+ return {
184
+ ...state,
185
+ document: { ...state.document, markers, volumes, paths, annotations },
186
+ };
187
+ },
188
+ translate: (state, command) => {
189
+ const movable = command.ids.filter((id) => !isEditorObjectLocked(state.document, id));
190
+ if (movable.length === 0)
191
+ return null;
192
+ const ids = new Set(movable);
193
+ for (const descendant of collectDescendants(state.document, movable))
194
+ ids.add(descendant);
195
+ return {
196
+ ...state,
197
+ document: translateByIds(state.document, ids, command.delta),
198
+ };
199
+ },
200
+ setParent: (state, command) => {
201
+ const targets = command.ids.filter((id) => !wouldCreateCycle(state.document, id, command.parentId));
202
+ if (targets.length === 0)
203
+ return state;
204
+ const wanted = new Set(targets);
205
+ const reparent = (entry) => {
206
+ if (!wanted.has(entry.id))
207
+ return entry;
208
+ if (command.parentId === null) {
209
+ if (entry.parentId === undefined)
210
+ return entry;
211
+ const next = { ...entry };
212
+ delete next.parentId;
213
+ return next;
214
+ }
215
+ return { ...entry, parentId: command.parentId };
216
+ };
217
+ return { ...state, document: { ...state.document, ...mapPlaceables(state.document, reparent) } };
218
+ },
219
+ addMarker: (state, command) => {
220
+ const marker = ownIdOrGloballyUnique(state.document, command.marker, state.document.markers);
221
+ return {
222
+ ...state,
223
+ document: {
224
+ ...state.document,
225
+ markers: [...state.document.markers.filter((m) => m.id !== marker.id), marker],
226
+ },
227
+ selection: [marker.id],
228
+ };
229
+ },
230
+ addVolume: (state, command) => {
231
+ const volume = ownIdOrGloballyUnique(state.document, command.volume, state.document.volumes);
232
+ return {
233
+ ...state,
234
+ document: {
235
+ ...state.document,
236
+ volumes: [...state.document.volumes.filter((v) => v.id !== volume.id), volume],
237
+ },
238
+ selection: [volume.id],
239
+ };
240
+ },
241
+ addPath: (state, command) => {
242
+ const path = ownIdOrGloballyUnique(state.document, command.path, state.document.paths);
243
+ return {
244
+ ...state,
245
+ document: {
246
+ ...state.document,
247
+ paths: [...state.document.paths.filter((p) => p.id !== path.id), path],
248
+ },
249
+ selection: [path.id],
250
+ };
251
+ },
252
+ addNote: (state, command) => {
253
+ const note = ownIdOrGloballyUnique(state.document, command.note, state.document.annotations);
254
+ return {
255
+ ...state,
256
+ document: {
257
+ ...state.document,
258
+ annotations: [...state.document.annotations.filter((n) => n.id !== note.id), note],
259
+ },
260
+ selection: [note.id],
261
+ };
262
+ },
263
+ setMarker: (state, command) => {
264
+ const markers = state.document.markers.map((marker) => {
265
+ if (marker.id !== command.id)
266
+ return marker;
267
+ return {
268
+ ...marker,
269
+ ...command.patch,
270
+ position: command.patch.position === undefined ? marker.position : { ...command.patch.position },
271
+ meta: command.patch.meta === undefined ? marker.meta : { ...command.patch.meta },
272
+ };
273
+ });
274
+ return { ...state, document: { ...state.document, markers } };
275
+ },
276
+ setVolume: (state, command) => {
277
+ const volumes = state.document.volumes.map((volume) => {
278
+ if (volume.id !== command.id)
279
+ return volume;
280
+ return {
281
+ ...volume,
282
+ ...command.patch,
283
+ center: command.patch.center === undefined ? volume.center : { ...command.patch.center },
284
+ halfExtents: command.patch.halfExtents === undefined
285
+ ? volume.halfExtents
286
+ : { ...command.patch.halfExtents },
287
+ meta: command.patch.meta === undefined ? volume.meta : { ...command.patch.meta },
288
+ };
289
+ });
290
+ return { ...state, document: { ...state.document, volumes } };
291
+ },
292
+ setPath: (state, command) => {
293
+ const paths = state.document.paths.map((path) => {
294
+ if (path.id !== command.id)
295
+ return path;
296
+ return {
297
+ ...path,
298
+ ...command.patch,
299
+ points: command.patch.points === undefined
300
+ ? path.points
301
+ : command.patch.points.map((point) => ({ ...point })),
302
+ meta: command.patch.meta === undefined ? path.meta : { ...command.patch.meta },
303
+ };
304
+ });
305
+ return { ...state, document: { ...state.document, paths } };
306
+ },
307
+ setNote: (state, command) => {
308
+ const annotations = state.document.annotations.map((note) => {
309
+ if (note.id !== command.id)
310
+ return note;
311
+ return {
312
+ ...note,
313
+ ...command.patch,
314
+ position: command.patch.position === undefined ? note.position : { ...command.patch.position },
315
+ meta: command.patch.meta === undefined ? note.meta : { ...command.patch.meta },
316
+ };
317
+ });
318
+ return { ...state, document: { ...state.document, annotations } };
319
+ },
320
+ setCatalogEntry: (state, command) => {
321
+ const catalog = state.document.catalogs.find((entry) => entry.id === command.catalogId);
322
+ if (catalog === undefined)
323
+ return null;
324
+ if (!catalog.entries.some((entry) => entry.id === command.entryId))
325
+ return null;
326
+ const catalogs = state.document.catalogs.map((row) => {
327
+ if (row.id !== command.catalogId)
328
+ return row;
329
+ return {
330
+ ...row,
331
+ entries: row.entries.map((entry) => {
332
+ if (entry.id !== command.entryId)
333
+ return entry;
334
+ const next = { id: entry.id };
335
+ const label = command.patch.label ?? entry.label;
336
+ if (label !== undefined)
337
+ next.label = label;
338
+ if (command.patch.meta !== undefined)
339
+ next.meta = { ...entry.meta, ...command.patch.meta };
340
+ else if (entry.meta !== undefined)
341
+ next.meta = { ...entry.meta };
342
+ return next;
343
+ }),
344
+ };
345
+ });
346
+ return { ...state, document: { ...state.document, catalogs } };
347
+ },
348
+ addCatalogEntry: (state, command) => {
349
+ const trimmedId = command.entry.id.trim();
350
+ if (trimmedId.length === 0)
351
+ return null;
352
+ const existing = state.document.catalogs.find((catalog) => catalog.id === command.catalogId);
353
+ const taken = new Set((existing?.entries ?? []).map((entry) => entry.id));
354
+ let id = trimmedId;
355
+ let n = 2;
356
+ while (taken.has(id)) {
357
+ id = `${trimmedId}_${n}`;
358
+ n += 1;
359
+ }
360
+ const entry = {
361
+ id,
362
+ ...(command.entry.label === undefined ? {} : { label: command.entry.label }),
363
+ ...(command.entry.meta === undefined ? {} : { meta: { ...command.entry.meta } }),
364
+ };
365
+ const catalogs = existing === undefined
366
+ ? [...state.document.catalogs, { id: command.catalogId, entries: [entry] }]
367
+ : state.document.catalogs.map((catalog) => catalog.id === command.catalogId ? { ...catalog, entries: [...catalog.entries, entry] } : catalog);
368
+ return { ...state, document: { ...state.document, catalogs } };
369
+ },
370
+ removeCatalogEntry: (state, command) => {
371
+ const catalog = state.document.catalogs.find((entry) => entry.id === command.catalogId);
372
+ if (catalog === undefined || !catalog.entries.some((entry) => entry.id === command.entryId))
373
+ return null;
374
+ const catalogs = state.document.catalogs.map((row) => row.id === command.catalogId
375
+ ? { ...row, entries: row.entries.filter((entry) => entry.id !== command.entryId) }
376
+ : row);
377
+ return { ...state, document: { ...state.document, catalogs } };
378
+ },
379
+ addCatalog: (state, command) => {
380
+ const id = command.id.trim();
381
+ if (id.length === 0)
382
+ return null;
383
+ if (state.document.catalogs.some((catalog) => catalog.id === id))
384
+ return null;
385
+ const catalog = {
386
+ id,
387
+ ...(command.label === undefined ? {} : { label: command.label }),
388
+ ...(command.schema === undefined ? {} : { schema: command.schema }),
389
+ entries: [],
390
+ };
391
+ return { ...state, document: { ...state.document, catalogs: [...state.document.catalogs, catalog] } };
392
+ },
393
+ removeCatalog: (state, command) => {
394
+ const next = state.document.catalogs.filter((catalog) => catalog.id !== command.id);
395
+ if (next.length === state.document.catalogs.length)
396
+ return null;
397
+ return { ...state, document: { ...state.document, catalogs: next } };
398
+ },
399
+ setCatalogSchema: (state, command) => {
400
+ if (!state.document.catalogs.some((catalog) => catalog.id === command.id))
401
+ return null;
402
+ // Re-parse every row's meta against the NEW schema so removed keys drop, added keys default in,
403
+ // and range/number values clamp — the whole clamp contract for a schema edit in one step.
404
+ const catalogs = state.document.catalogs.map((catalog) => catalog.id === command.id
405
+ ? {
406
+ ...catalog,
407
+ schema: command.schema,
408
+ ...(command.label === undefined ? {} : { label: command.label }),
409
+ entries: catalog.entries.map((entry) => ({
410
+ ...entry,
411
+ meta: parseParams(command.schema, entry.meta),
412
+ })),
413
+ }
414
+ : catalog);
415
+ return { ...state, document: { ...state.document, catalogs } };
416
+ },
417
+ remove: (state, command) => {
418
+ if (isEditorObjectLocked(state.document, command.id))
419
+ return null;
420
+ return {
421
+ document: removeByIds(state.document, new Set([command.id])),
422
+ selection: state.selection.filter((id) => id !== command.id),
423
+ };
424
+ },
425
+ removeMany: (state, command) => {
426
+ const gone = new Set(command.ids.filter((id) => !isEditorObjectLocked(state.document, id)));
427
+ if (gone.size === 0)
428
+ return null;
429
+ return {
430
+ document: removeByIds(state.document, gone),
431
+ selection: state.selection.filter((id) => !gone.has(id)),
432
+ };
433
+ },
434
+ duplicate: (state, command) => insertFragment(state, extractEditorFragment(state.document, command.ids), command.offset ?? { x: 2, y: 0, z: 2 }, true),
435
+ addFragment: (state, command) => insertFragment(state, command.fragment, command.offset ?? { x: 0, y: 0, z: 0 }, false),
436
+ importDocument: (_state, command) => ({
437
+ document: cloneEditorDocument(command.document),
438
+ selection: [],
439
+ }),
440
+ replaceDocument: (_state, command) => ({
441
+ document: cloneEditorDocument(command.document),
442
+ selection: [],
443
+ }),
444
+ importJson: (_state, command) => ({
445
+ document: importEditorDocumentJson(command.json),
446
+ selection: [],
447
+ }),
448
+ setTerrain: (state, command) => ({ ...state, document: { ...state.document, terrain: command.terrain } }),
449
+ setMinimapBake: (state, command) => ({ ...state, document: { ...state.document, minimap: command.minimap } }),
450
+ setEnvironment: (state, command) => {
451
+ if (command.environment === undefined) {
452
+ const { environment: _removed, ...rest } = state.document;
453
+ void _removed;
454
+ return { ...state, document: rest };
455
+ }
456
+ return { ...state, document: { ...state.document, environment: command.environment } };
457
+ },
458
+ clearTerrain: (state) => {
459
+ const nextDoc = {
460
+ version: 1,
461
+ markers: state.document.markers,
462
+ volumes: state.document.volumes,
463
+ paths: state.document.paths,
464
+ annotations: state.document.annotations,
465
+ prefabs: state.document.prefabs,
466
+ collections: state.document.collections,
467
+ catalogs: state.document.catalogs,
468
+ ...(state.document.grids === undefined ? {} : { grids: state.document.grids }),
469
+ ...(state.document.ui === undefined ? {} : { ui: state.document.ui }),
470
+ ...(state.document.directives === undefined ? {} : { directives: state.document.directives }),
471
+ ...(state.document.minimap === undefined ? {} : { minimap: state.document.minimap }),
472
+ ...(state.document.environment === undefined ? {} : { environment: state.document.environment }),
473
+ };
474
+ return { ...state, document: nextDoc };
475
+ },
476
+ setUiPanel: (state, command) => {
477
+ const ui = patchUiPanel(state.document.ui, command.id, command.patch);
478
+ return { ...state, document: { ...state.document, ui } };
479
+ },
480
+ removeUiPanel: (state, command) => {
481
+ const ui = removeUiPanel(state.document.ui, command.id);
482
+ if (ui === undefined) {
483
+ const { ui: _removed, ...rest } = state.document;
484
+ void _removed;
485
+ return { ...state, document: rest };
486
+ }
487
+ return { ...state, document: { ...state.document, ui } };
488
+ },
489
+ setUi: (state, command) => {
490
+ if (command.ui === undefined) {
491
+ const { ui: _removed, ...rest } = state.document;
492
+ void _removed;
493
+ return { ...state, document: rest };
494
+ }
495
+ return { ...state, document: { ...state.document, ui: command.ui } };
496
+ },
497
+ sculptTerrain: (state, command) => {
498
+ if (state.document.terrain === undefined)
499
+ return state;
500
+ return {
501
+ ...state,
502
+ document: { ...state.document, terrain: applyDeltaToSnapshot(state.document.terrain, command.delta) },
503
+ };
504
+ },
505
+ paintTerrain: (state, command) => {
506
+ if (state.document.terrain === undefined)
507
+ return state;
508
+ return {
509
+ ...state,
510
+ document: { ...state.document, terrain: applySurfaceDeltaToSnapshot(state.document.terrain, command.delta) },
511
+ };
512
+ },
513
+ blendTerrain: (state, command) => {
514
+ if (state.document.terrain === undefined)
515
+ return state;
516
+ return {
517
+ ...state,
518
+ document: { ...state.document, terrain: applyWeightDeltaToSnapshot(state.document.terrain, command.delta) },
519
+ };
520
+ },
521
+ setTerrainLayers: (state, command) => {
522
+ if (state.document.terrain === undefined)
523
+ return state;
524
+ const terrain = state.document.terrain;
525
+ const layers = command.layers.map((layer) => ({ ...layer }));
526
+ const before = terrain.layers ?? [];
527
+ // Keep hand-painted weights only when the layer id sequence is unchanged (a params-only edit).
528
+ const sameSequence = before.length === layers.length && before.every((layer, i) => layer.id === layers[i].id);
529
+ const next = sameSequence
530
+ ? { ...terrain, layers }
531
+ : (() => {
532
+ const copy = { ...terrain, layers };
533
+ delete copy.weights;
534
+ return copy;
535
+ })();
536
+ return { ...state, document: { ...state.document, terrain: next } };
537
+ },
538
+ convertScatterToObjects: (state, command) => {
539
+ const path = state.document.paths.find((entry) => entry.id === command.pathId);
540
+ if (path === undefined)
541
+ return state;
542
+ const taken = collectIds(state.document);
543
+ const markers = command.markers.map((marker) => {
544
+ let id = marker.id;
545
+ while (taken.has(id))
546
+ id = copyId(marker.id, taken);
547
+ taken.add(id);
548
+ return { ...marker, id };
549
+ });
550
+ return {
551
+ document: {
552
+ ...state.document,
553
+ markers: [...state.document.markers, ...markers],
554
+ paths: state.document.paths.filter((entry) => entry.id !== command.pathId),
555
+ },
556
+ selection: markers.map((marker) => marker.id),
557
+ };
558
+ },
559
+ createPrefab: (state, command) => {
560
+ if (command.ids.length === 0)
561
+ return state;
562
+ const fragment = createPrefabFragment(state.document, command.ids);
563
+ const prefab = { id: command.id, name: command.name, fragment };
564
+ return {
565
+ ...state,
566
+ document: {
567
+ ...state.document,
568
+ prefabs: [...state.document.prefabs.filter((entry) => entry.id !== command.id), prefab],
569
+ },
570
+ };
571
+ },
572
+ deletePrefab: (state, command) => ({
573
+ ...state,
574
+ document: {
575
+ ...state.document,
576
+ prefabs: state.document.prefabs.filter((entry) => entry.id !== command.prefabId),
577
+ },
578
+ }),
579
+ insertPrefab: (state, command) => {
580
+ const prefab = findEditorPrefab(state.document, command.prefabId);
581
+ if (prefab === undefined)
582
+ return state;
583
+ const instanceId = command.instanceId ??
584
+ `${prefab.id}_inst_${Date.now().toString(36)}_${Math.floor(Math.random() * 1_296_000).toString(36)}`;
585
+ const tagged = fragmentAsDocument({
586
+ markers: prefab.fragment.markers.map((marker) => tagWithInstance(marker, prefab.id, instanceId)),
587
+ volumes: prefab.fragment.volumes.map((volume) => tagWithInstance(volume, prefab.id, instanceId)),
588
+ paths: prefab.fragment.paths.map((path) => tagWithInstance(path, prefab.id, instanceId)),
589
+ annotations: prefab.fragment.annotations.map((note) => tagWithInstance(note, prefab.id, instanceId)),
590
+ });
591
+ return insertFragment(state, tagged, command.at, true);
592
+ },
593
+ detachPrefabInstance: (state, command) => {
594
+ const strip = (item) => {
595
+ if (item.meta?.prefabInstanceId !== command.instanceId)
596
+ return item;
597
+ const meta = { ...item.meta };
598
+ delete meta.prefabId;
599
+ delete meta.prefabInstanceId;
600
+ return { ...item, meta };
601
+ };
602
+ return { ...state, document: { ...state.document, ...mapPlaceables(state.document, strip) } };
603
+ },
604
+ createCollection: (state, command) => {
605
+ const collection = {
606
+ id: command.id,
607
+ name: command.name,
608
+ memberIds: [...(command.memberIds ?? [])],
609
+ };
610
+ return {
611
+ ...state,
612
+ document: {
613
+ ...state.document,
614
+ collections: [...state.document.collections.filter((entry) => entry.id !== command.id), collection],
615
+ },
616
+ };
617
+ },
618
+ renameCollection: (state, command) => {
619
+ const collections = state.document.collections.map((collection) => collection.id === command.id ? { ...collection, name: command.name } : collection);
620
+ return { ...state, document: { ...state.document, collections } };
621
+ },
622
+ deleteCollection: (state, command) => ({
623
+ ...state,
624
+ document: {
625
+ ...state.document,
626
+ collections: state.document.collections.filter((entry) => entry.id !== command.id),
627
+ },
628
+ }),
629
+ setCollectionMembers: (state, command) => {
630
+ const collections = state.document.collections.map((collection) => collection.id === command.id ? { ...collection, memberIds: [...command.memberIds] } : collection);
631
+ return { ...state, document: { ...state.document, collections } };
632
+ },
633
+ addToCollection: (state, command) => {
634
+ const adding = new Set(command.ids);
635
+ const collections = state.document.collections.map((collection) => {
636
+ if (collection.id !== command.id)
637
+ return collection;
638
+ const merged = new Set(collection.memberIds);
639
+ for (const id of adding)
640
+ merged.add(id);
641
+ return { ...collection, memberIds: [...merged] };
642
+ });
643
+ return { ...state, document: { ...state.document, collections } };
644
+ },
645
+ removeFromCollection: (state, command) => {
646
+ const removing = new Set(command.ids);
647
+ const collections = state.document.collections.map((collection) => collection.id === command.id
648
+ ? { ...collection, memberIds: collection.memberIds.filter((id) => !removing.has(id)) }
649
+ : collection);
650
+ return { ...state, document: { ...state.document, collections } };
651
+ },
652
+ setCollectionFlags: (state, command) => {
653
+ const collections = state.document.collections.map((collection) => collection.id === command.id ? { ...collection, ...command.patch } : collection);
654
+ return { ...state, document: { ...state.document, collections } };
655
+ },
656
+ setObjectFlags: (state, command) => {
657
+ const ids = new Set(command.ids);
658
+ if (ids.size === 0)
659
+ return null;
660
+ const { locked, hidden } = command.patch;
661
+ if (locked === undefined && hidden === undefined)
662
+ return null;
663
+ const apply = (item) => {
664
+ if (!ids.has(item.id))
665
+ return item;
666
+ const next = { ...item };
667
+ if (locked !== undefined) {
668
+ if (locked)
669
+ next.locked = true;
670
+ else
671
+ delete next.locked;
672
+ }
673
+ if (hidden !== undefined) {
674
+ if (hidden)
675
+ next.hidden = true;
676
+ else
677
+ delete next.hidden;
678
+ }
679
+ return next;
680
+ };
681
+ return { ...state, document: { ...state.document, ...mapPlaceables(state.document, apply) } };
682
+ },
683
+ selectCollection: (state, command) => {
684
+ const collection = findEditorCollection(state.document, command.id);
685
+ if (collection === undefined)
686
+ return null;
687
+ return { ...state, selection: [...collection.memberIds] };
688
+ },
689
+ batchSetProperties: (state, command) => {
690
+ const ids = new Set(command.ids);
691
+ const patch = command.patch;
692
+ // Notes carry no `label`, so they take color/meta only via a dedicated mapper.
693
+ const apply = (item) => ids.has(item.id)
694
+ ? {
695
+ ...item,
696
+ ...(patch.color === undefined ? {} : { color: patch.color }),
697
+ ...(patch.label === undefined ? {} : { label: patch.label }),
698
+ ...(patch.meta === undefined ? {} : { meta: { ...item.meta, ...patch.meta } }),
699
+ }
700
+ : item;
701
+ const applyNote = (note) => ids.has(note.id)
702
+ ? {
703
+ ...note,
704
+ ...(patch.color === undefined ? {} : { color: patch.color }),
705
+ ...(patch.meta === undefined ? {} : { meta: { ...note.meta, ...patch.meta } }),
706
+ }
707
+ : note;
708
+ return { ...state, document: { ...state.document, ...mapPlaceables(state.document, apply, applyNote) } };
709
+ },
710
+ addGridLayer: (state, command) => {
711
+ const layer = migrateGridLayer(command.layer);
712
+ const grids = state.document.grids ?? [];
713
+ return {
714
+ ...state,
715
+ document: {
716
+ ...state.document,
717
+ grids: [...grids.filter((entry) => entry.id !== layer.id), layer],
718
+ },
719
+ };
720
+ },
721
+ removeGridLayer: (state, command) => {
722
+ const grids = state.document.grids;
723
+ if (grids === undefined)
724
+ return null;
725
+ const next = grids.filter((layer) => layer.id !== command.id);
726
+ if (next.length === grids.length)
727
+ return null;
728
+ return { ...state, document: { ...state.document, grids: next } };
729
+ },
730
+ setGridLayer: (state, command) => updateGridLayer(state, command.id, (layer) => migrateGridLayer({ ...layer, ...command.patch })),
731
+ paintGridCells: (state, command) => updateGridLayer(state, command.id, (layer) => paintGridCells(layer, command.cells)),
732
+ fillGridRect: (state, command) => updateGridLayer(state, command.id, (layer) => fillGridRect(layer, command.col0, command.row0, command.col1, command.row1, command.value)),
733
+ floodFillGrid: (state, command) => updateGridLayer(state, command.id, (layer) => floodFillGrid(layer, command.col, command.row, command.value)),
734
+ resizeGridLayer: (state, command) => updateGridLayer(state, command.id, (layer) => resizeGridLayer(layer, command.cols, command.rows)),
735
+ assignMaterial: (state, command) => {
736
+ const ids = new Set(command.ids);
737
+ const stamp = (item) => ids.has(item.id) ? { ...item, meta: { ...item.meta, materialId: command.materialId } } : item;
738
+ return { ...state, document: { ...state.document, ...mapPlaceables(state.document, stamp) } };
739
+ },
740
+ undo: () => null,
741
+ redo: () => null,
742
+ };
743
+ /** Apply a mutating editor command; returns null when the command type has no handler. @internal */
744
+ export function applyMutating(state, command) {
745
+ const handler = mutationHandlers[command.type];
746
+ return handler(state, command);
747
+ }
748
+ /** True when the command changes document structure (not select/undo/redo). @internal */
749
+ export function isStructural(command) {
750
+ return (command.type !== "select" &&
751
+ command.type !== "clearSelection" &&
752
+ command.type !== "selectCollection" &&
753
+ command.type !== "undo" &&
754
+ command.type !== "redo");
755
+ }