@pascal-app/core 0.9.2 → 1.0.0-beta.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 (339) hide show
  1. package/README.md +17 -10
  2. package/dist/events/bus.d.ts +21 -2
  3. package/dist/events/bus.d.ts.map +1 -1
  4. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +17 -1
  5. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
  6. package/dist/hooks/spatial-grid/floor-placed-elevation.js +61 -8
  7. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +116 -66
  8. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  9. package/dist/hooks/spatial-grid/spatial-grid-manager.js +316 -441
  10. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +46 -1
  11. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  12. package/dist/hooks/spatial-grid/spatial-grid-sync.js +193 -12
  13. package/dist/hooks/spatial-grid/support-host-id.d.ts +3 -0
  14. package/dist/hooks/spatial-grid/support-host-id.d.ts.map +1 -0
  15. package/dist/hooks/spatial-grid/support-host-id.js +2 -0
  16. package/dist/hooks/spatial-grid/support-host-patch.d.ts +51 -0
  17. package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -0
  18. package/dist/hooks/spatial-grid/support-host-patch.js +164 -0
  19. package/dist/index.d.ts +23 -11
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +22 -10
  22. package/dist/lib/measurement-geometry.d.ts +4 -0
  23. package/dist/lib/measurement-geometry.d.ts.map +1 -1
  24. package/dist/lib/measurement-geometry.js +27 -10
  25. package/dist/lib/slab-polygon.d.ts.map +1 -1
  26. package/dist/lib/slab-polygon.js +33 -1
  27. package/dist/lib/space-detection.d.ts +15 -4
  28. package/dist/lib/space-detection.d.ts.map +1 -1
  29. package/dist/lib/space-detection.js +174 -88
  30. package/dist/lib/terrain-brush.d.ts +163 -0
  31. package/dist/lib/terrain-brush.d.ts.map +1 -0
  32. package/dist/lib/terrain-brush.js +359 -0
  33. package/dist/lib/terrain-codec.d.ts +51 -0
  34. package/dist/lib/terrain-codec.d.ts.map +1 -0
  35. package/dist/lib/terrain-codec.js +167 -0
  36. package/dist/lib/terrain-field.d.ts +150 -0
  37. package/dist/lib/terrain-field.d.ts.map +1 -0
  38. package/dist/lib/terrain-field.js +248 -0
  39. package/dist/lib/terrain-raycast.d.ts +44 -0
  40. package/dist/lib/terrain-raycast.d.ts.map +1 -0
  41. package/dist/lib/terrain-raycast.js +188 -0
  42. package/dist/lib/terrain-source.d.ts +60 -0
  43. package/dist/lib/terrain-source.d.ts.map +1 -0
  44. package/dist/lib/terrain-source.js +90 -0
  45. package/dist/lib/terrain-support.d.ts +60 -0
  46. package/dist/lib/terrain-support.d.ts.map +1 -0
  47. package/dist/lib/terrain-support.js +150 -0
  48. package/dist/lib/zone-quantities.d.ts +1 -1
  49. package/dist/lib/zone-quantities.d.ts.map +1 -1
  50. package/dist/lib/zone-quantities.js +311 -66
  51. package/dist/material-library.d.ts +9 -1
  52. package/dist/material-library.d.ts.map +1 -1
  53. package/dist/material-library.js +56 -4
  54. package/dist/registry/handles.d.ts +16 -1
  55. package/dist/registry/handles.d.ts.map +1 -1
  56. package/dist/registry/index.d.ts +1 -1
  57. package/dist/registry/index.d.ts.map +1 -1
  58. package/dist/registry/subtree.d.ts.map +1 -1
  59. package/dist/registry/subtree.js +5 -2
  60. package/dist/registry/types.d.ts +64 -1
  61. package/dist/registry/types.d.ts.map +1 -1
  62. package/dist/schema/index.d.ts +6 -3
  63. package/dist/schema/index.d.ts.map +1 -1
  64. package/dist/schema/index.js +6 -3
  65. package/dist/schema/nodes/cabinet.d.ts +2 -0
  66. package/dist/schema/nodes/cabinet.d.ts.map +1 -1
  67. package/dist/schema/nodes/cabinet.js +4 -2
  68. package/dist/schema/nodes/ceiling.d.ts +1 -1
  69. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  70. package/dist/schema/nodes/ceiling.js +7 -1
  71. package/dist/schema/nodes/column.d.ts +1 -0
  72. package/dist/schema/nodes/column.d.ts.map +1 -1
  73. package/dist/schema/nodes/column.js +2 -0
  74. package/dist/schema/nodes/construction-dimension.d.ts +195 -0
  75. package/dist/schema/nodes/construction-dimension.d.ts.map +1 -0
  76. package/dist/schema/nodes/construction-dimension.js +151 -0
  77. package/dist/schema/nodes/door.d.ts +29 -0
  78. package/dist/schema/nodes/door.d.ts.map +1 -1
  79. package/dist/schema/nodes/door.js +20 -0
  80. package/dist/schema/nodes/duct-terminal.d.ts +1 -0
  81. package/dist/schema/nodes/duct-terminal.d.ts.map +1 -1
  82. package/dist/schema/nodes/duct-terminal.js +2 -0
  83. package/dist/schema/nodes/fence.d.ts +2 -0
  84. package/dist/schema/nodes/fence.d.ts.map +1 -1
  85. package/dist/schema/nodes/fence.js +8 -0
  86. package/dist/schema/nodes/hvac-equipment.d.ts +1 -0
  87. package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -1
  88. package/dist/schema/nodes/hvac-equipment.js +2 -0
  89. package/dist/schema/nodes/item.d.ts +1 -0
  90. package/dist/schema/nodes/item.d.ts.map +1 -1
  91. package/dist/schema/nodes/item.js +13 -0
  92. package/dist/schema/nodes/level.d.ts +2 -1
  93. package/dist/schema/nodes/level.d.ts.map +1 -1
  94. package/dist/schema/nodes/level.js +12 -33
  95. package/dist/schema/nodes/shelf.d.ts +1 -0
  96. package/dist/schema/nodes/shelf.d.ts.map +1 -1
  97. package/dist/schema/nodes/shelf.js +2 -0
  98. package/dist/schema/nodes/site.d.ts +9 -0
  99. package/dist/schema/nodes/site.d.ts.map +1 -1
  100. package/dist/schema/nodes/site.js +8 -6
  101. package/dist/schema/nodes/slab.d.ts +5 -0
  102. package/dist/schema/nodes/slab.d.ts.map +1 -1
  103. package/dist/schema/nodes/slab.js +14 -2
  104. package/dist/schema/nodes/spawn.d.ts +1 -0
  105. package/dist/schema/nodes/spawn.d.ts.map +1 -1
  106. package/dist/schema/nodes/spawn.js +2 -0
  107. package/dist/schema/nodes/stair.d.ts +3 -1
  108. package/dist/schema/nodes/stair.d.ts.map +1 -1
  109. package/dist/schema/nodes/stair.js +8 -1
  110. package/dist/schema/nodes/structural-grid.d.ts +27 -0
  111. package/dist/schema/nodes/structural-grid.d.ts.map +1 -0
  112. package/dist/schema/nodes/structural-grid.js +17 -0
  113. package/dist/schema/nodes/wall.d.ts +5 -2
  114. package/dist/schema/nodes/wall.d.ts.map +1 -1
  115. package/dist/schema/nodes/wall.js +13 -4
  116. package/dist/schema/nodes/window.d.ts +29 -0
  117. package/dist/schema/nodes/window.d.ts.map +1 -1
  118. package/dist/schema/nodes/window.js +18 -0
  119. package/dist/schema/nodes/zone.d.ts +20 -0
  120. package/dist/schema/nodes/zone.d.ts.map +1 -1
  121. package/dist/schema/nodes/zone.js +15 -0
  122. package/dist/schema/terrain.d.ts +28 -0
  123. package/dist/schema/terrain.d.ts.map +1 -0
  124. package/dist/schema/terrain.js +30 -0
  125. package/dist/schema/types.d.ts +211 -3
  126. package/dist/schema/types.d.ts.map +1 -1
  127. package/dist/schema/types.js +4 -0
  128. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts +80 -0
  129. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.d.ts.map +1 -0
  130. package/dist/services/__fixtures__/wall-plane-top-boundary-repro.js +89 -0
  131. package/dist/services/index.d.ts +2 -1
  132. package/dist/services/index.d.ts.map +1 -1
  133. package/dist/services/index.js +2 -1
  134. package/dist/services/level-height.d.ts +8 -9
  135. package/dist/services/level-height.d.ts.map +1 -1
  136. package/dist/services/level-height.js +48 -16
  137. package/dist/services/storey.d.ts +103 -0
  138. package/dist/services/storey.d.ts.map +1 -0
  139. package/dist/services/storey.js +296 -0
  140. package/dist/store/actions/node-actions.d.ts.map +1 -1
  141. package/dist/store/actions/node-actions.js +47 -3
  142. package/dist/store/history-control.d.ts +20 -0
  143. package/dist/store/history-control.d.ts.map +1 -1
  144. package/dist/store/history-control.js +110 -13
  145. package/dist/store/use-live-terrain.d.ts +65 -0
  146. package/dist/store/use-live-terrain.d.ts.map +1 -0
  147. package/dist/store/use-live-terrain.js +31 -0
  148. package/dist/store/use-live-transforms.d.ts +8 -0
  149. package/dist/store/use-live-transforms.d.ts.map +1 -1
  150. package/dist/store/use-scene.d.ts +31 -2
  151. package/dist/store/use-scene.d.ts.map +1 -1
  152. package/dist/store/use-scene.js +638 -8
  153. package/dist/systems/elevator/elevator-opening-system.d.ts +1 -0
  154. package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -1
  155. package/dist/systems/elevator/elevator-opening-system.js +27 -21
  156. package/dist/systems/elevator/elevator-service.d.ts +0 -2
  157. package/dist/systems/elevator/elevator-service.d.ts.map +1 -1
  158. package/dist/systems/elevator/elevator-service.js +2 -24
  159. package/dist/systems/slab/slab-placement.d.ts +11 -0
  160. package/dist/systems/slab/slab-placement.d.ts.map +1 -0
  161. package/dist/systems/slab/slab-placement.js +14 -0
  162. package/dist/systems/slab/slab-support.d.ts +137 -0
  163. package/dist/systems/slab/slab-support.d.ts.map +1 -0
  164. package/dist/systems/slab/slab-support.js +557 -0
  165. package/dist/systems/stair/stair-opening-preview.d.ts +1060 -935
  166. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
  167. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  168. package/dist/systems/stair/stair-opening-sync.js +18 -15
  169. package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
  170. package/dist/systems/stair/stair-opening-system.js +32 -4
  171. package/dist/systems/stair/stair-rise.d.ts +22 -0
  172. package/dist/systems/stair/stair-rise.d.ts.map +1 -0
  173. package/dist/systems/stair/stair-rise.js +79 -0
  174. package/dist/systems/wall/wall-curve.d.ts +10 -0
  175. package/dist/systems/wall/wall-curve.d.ts.map +1 -1
  176. package/dist/systems/wall/wall-curve.js +1 -1
  177. package/dist/systems/wall/wall-mitering.js +6 -6
  178. package/dist/systems/wall/wall-top.d.ts +40 -0
  179. package/dist/systems/wall/wall-top.d.ts.map +1 -0
  180. package/dist/systems/wall/wall-top.js +46 -0
  181. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  182. package/dist/utils/clone-scene-graph.js +34 -3
  183. package/dist/utils/heal-scene-graph.d.ts +1 -1
  184. package/dist/utils/heal-scene-graph.d.ts.map +1 -1
  185. package/dist/utils/heal-scene-graph.js +25 -9
  186. package/dist/validation/validate-build-json.d.ts +2 -0
  187. package/dist/validation/validate-build-json.d.ts.map +1 -1
  188. package/dist/validation/validate-build-json.js +59 -3
  189. package/package.json +2 -2
  190. package/dist/hooks/scene-registry/scene-registry.test.d.ts +0 -2
  191. package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +0 -1
  192. package/dist/hooks/scene-registry/scene-registry.test.js +0 -26
  193. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +0 -2
  194. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +0 -1
  195. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +0 -366
  196. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +0 -2
  197. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +0 -1
  198. package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +0 -363
  199. package/dist/lib/measurement-geometry.test.d.ts +0 -2
  200. package/dist/lib/measurement-geometry.test.d.ts.map +0 -1
  201. package/dist/lib/measurement-geometry.test.js +0 -102
  202. package/dist/lib/slab-polygon.test.d.ts +0 -2
  203. package/dist/lib/slab-polygon.test.d.ts.map +0 -1
  204. package/dist/lib/slab-polygon.test.js +0 -635
  205. package/dist/lib/space-detection-pause.test.d.ts +0 -2
  206. package/dist/lib/space-detection-pause.test.d.ts.map +0 -1
  207. package/dist/lib/space-detection-pause.test.js +0 -42
  208. package/dist/lib/space-detection.test.d.ts +0 -2
  209. package/dist/lib/space-detection.test.d.ts.map +0 -1
  210. package/dist/lib/space-detection.test.js +0 -376
  211. package/dist/lib/zone-quantities.test.d.ts +0 -2
  212. package/dist/lib/zone-quantities.test.d.ts.map +0 -1
  213. package/dist/lib/zone-quantities.test.js +0 -82
  214. package/dist/registry/registry.test.d.ts +0 -2
  215. package/dist/registry/registry.test.d.ts.map +0 -1
  216. package/dist/registry/registry.test.js +0 -196
  217. package/dist/registry/relations-resolver.test.d.ts +0 -2
  218. package/dist/registry/relations-resolver.test.d.ts.map +0 -1
  219. package/dist/registry/relations-resolver.test.js +0 -183
  220. package/dist/registry/scene-api.test.d.ts +0 -2
  221. package/dist/registry/scene-api.test.d.ts.map +0 -1
  222. package/dist/registry/scene-api.test.js +0 -183
  223. package/dist/registry/subtree.test.d.ts +0 -2
  224. package/dist/registry/subtree.test.d.ts.map +0 -1
  225. package/dist/registry/subtree.test.js +0 -163
  226. package/dist/schema/asset-url.test.d.ts +0 -2
  227. package/dist/schema/asset-url.test.d.ts.map +0 -1
  228. package/dist/schema/asset-url.test.js +0 -134
  229. package/dist/schema/material.test.d.ts +0 -2
  230. package/dist/schema/material.test.d.ts.map +0 -1
  231. package/dist/schema/material.test.js +0 -43
  232. package/dist/schema/nodes/measurement.test.d.ts +0 -2
  233. package/dist/schema/nodes/measurement.test.d.ts.map +0 -1
  234. package/dist/schema/nodes/measurement.test.js +0 -139
  235. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +0 -2
  236. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +0 -1
  237. package/dist/schema/nodes/ridge-vent-defaults.test.js +0 -205
  238. package/dist/schema/nodes/roof-segment-shape.test.d.ts +0 -2
  239. package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +0 -1
  240. package/dist/schema/nodes/roof-segment-shape.test.js +0 -175
  241. package/dist/schema/nodes/roof-segment-surface.test.d.ts +0 -2
  242. package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +0 -1
  243. package/dist/schema/nodes/roof-segment-surface.test.js +0 -48
  244. package/dist/schema/nodes/roof-segment-trim.test.d.ts +0 -2
  245. package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +0 -1
  246. package/dist/schema/nodes/roof-segment-trim.test.js +0 -107
  247. package/dist/schema/nodes/roof-segment-walls.test.d.ts +0 -2
  248. package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +0 -1
  249. package/dist/schema/nodes/roof-segment-walls.test.js +0 -92
  250. package/dist/schema/nodes/wall.test.d.ts +0 -2
  251. package/dist/schema/nodes/wall.test.d.ts.map +0 -1
  252. package/dist/schema/nodes/wall.test.js +0 -208
  253. package/dist/services/alignment-anchors.test.d.ts +0 -2
  254. package/dist/services/alignment-anchors.test.d.ts.map +0 -1
  255. package/dist/services/alignment-anchors.test.js +0 -383
  256. package/dist/services/alignment.test.d.ts +0 -2
  257. package/dist/services/alignment.test.d.ts.map +0 -1
  258. package/dist/services/alignment.test.js +0 -92
  259. package/dist/services/drag-session.test.d.ts +0 -2
  260. package/dist/services/drag-session.test.d.ts.map +0 -1
  261. package/dist/services/drag-session.test.js +0 -219
  262. package/dist/services/hosting.test.d.ts +0 -2
  263. package/dist/services/hosting.test.d.ts.map +0 -1
  264. package/dist/services/hosting.test.js +0 -230
  265. package/dist/services/movement.test.d.ts +0 -2
  266. package/dist/services/movement.test.d.ts.map +0 -1
  267. package/dist/services/movement.test.js +0 -133
  268. package/dist/services/opening-guides.test.d.ts +0 -2
  269. package/dist/services/opening-guides.test.d.ts.map +0 -1
  270. package/dist/services/opening-guides.test.js +0 -208
  271. package/dist/services/port-connectivity.test.d.ts +0 -2
  272. package/dist/services/port-connectivity.test.d.ts.map +0 -1
  273. package/dist/services/port-connectivity.test.js +0 -297
  274. package/dist/services/riser-diagram.test.d.ts +0 -2
  275. package/dist/services/riser-diagram.test.d.ts.map +0 -1
  276. package/dist/services/riser-diagram.test.js +0 -66
  277. package/dist/services/single-undo-dance.test.d.ts +0 -2
  278. package/dist/services/single-undo-dance.test.d.ts.map +0 -1
  279. package/dist/services/single-undo-dance.test.js +0 -210
  280. package/dist/services/snap.test.d.ts +0 -2
  281. package/dist/services/snap.test.d.ts.map +0 -1
  282. package/dist/services/snap.test.js +0 -137
  283. package/dist/services/system-graph.test.d.ts +0 -2
  284. package/dist/services/system-graph.test.d.ts.map +0 -1
  285. package/dist/services/system-graph.test.js +0 -138
  286. package/dist/services/validate-dwv.test.d.ts +0 -2
  287. package/dist/services/validate-dwv.test.d.ts.map +0 -1
  288. package/dist/services/validate-dwv.test.js +0 -109
  289. package/dist/store/actions/node-mutation-sanitize.test.d.ts +0 -2
  290. package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +0 -1
  291. package/dist/store/actions/node-mutation-sanitize.test.js +0 -147
  292. package/dist/store/actions/reparent.test.d.ts +0 -2
  293. package/dist/store/actions/reparent.test.d.ts.map +0 -1
  294. package/dist/store/actions/reparent.test.js +0 -212
  295. package/dist/store/actions/ridge-vent-update.test.d.ts +0 -2
  296. package/dist/store/actions/ridge-vent-update.test.d.ts.map +0 -1
  297. package/dist/store/actions/ridge-vent-update.test.js +0 -264
  298. package/dist/store/use-scene-dirty-tracking.test.d.ts +0 -2
  299. package/dist/store/use-scene-dirty-tracking.test.d.ts.map +0 -1
  300. package/dist/store/use-scene-dirty-tracking.test.js +0 -68
  301. package/dist/store/use-scene-elevator-migration.test.d.ts +0 -2
  302. package/dist/store/use-scene-elevator-migration.test.d.ts.map +0 -1
  303. package/dist/store/use-scene-elevator-migration.test.js +0 -109
  304. package/dist/store/use-scene-measurement-roundtrip.test.d.ts +0 -2
  305. package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +0 -1
  306. package/dist/store/use-scene-measurement-roundtrip.test.js +0 -97
  307. package/dist/store/use-scene-plugins.test.d.ts +0 -2
  308. package/dist/store/use-scene-plugins.test.d.ts.map +0 -1
  309. package/dist/store/use-scene-plugins.test.js +0 -51
  310. package/dist/store/use-scene-wall-slot-migration.test.d.ts +0 -2
  311. package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +0 -1
  312. package/dist/store/use-scene-wall-slot-migration.test.js +0 -210
  313. package/dist/store/use-scene-window-migration.test.d.ts +0 -2
  314. package/dist/store/use-scene-window-migration.test.d.ts.map +0 -1
  315. package/dist/store/use-scene-window-migration.test.js +0 -85
  316. package/dist/systems/elevator/elevator-opening-sync.test.d.ts +0 -2
  317. package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +0 -1
  318. package/dist/systems/elevator/elevator-opening-sync.test.js +0 -146
  319. package/dist/systems/elevator/elevator-runtime.test.d.ts +0 -2
  320. package/dist/systems/elevator/elevator-runtime.test.d.ts.map +0 -1
  321. package/dist/systems/elevator/elevator-runtime.test.js +0 -64
  322. package/dist/systems/fence/fence-spline.test.d.ts +0 -2
  323. package/dist/systems/fence/fence-spline.test.d.ts.map +0 -1
  324. package/dist/systems/fence/fence-spline.test.js +0 -136
  325. package/dist/systems/stair/stair-opening-preview.test.d.ts +0 -2
  326. package/dist/systems/stair/stair-opening-preview.test.d.ts.map +0 -1
  327. package/dist/systems/stair/stair-opening-preview.test.js +0 -80
  328. package/dist/systems/stair/stair-opening-sync.test.d.ts +0 -2
  329. package/dist/systems/stair/stair-opening-sync.test.d.ts.map +0 -1
  330. package/dist/systems/stair/stair-opening-sync.test.js +0 -485
  331. package/dist/systems/wall/wall-mitering.test.d.ts +0 -2
  332. package/dist/systems/wall/wall-mitering.test.d.ts.map +0 -1
  333. package/dist/systems/wall/wall-mitering.test.js +0 -63
  334. package/dist/utils/clone-scene-graph.test.d.ts +0 -2
  335. package/dist/utils/clone-scene-graph.test.d.ts.map +0 -1
  336. package/dist/utils/clone-scene-graph.test.js +0 -62
  337. package/dist/utils/heal-scene-graph.test.d.ts +0 -2
  338. package/dist/utils/heal-scene-graph.test.d.ts.map +0 -1
  339. package/dist/utils/heal-scene-graph.test.js +0 -88
@@ -1,219 +0,0 @@
1
- import { beforeEach, describe, expect, mock, test } from 'bun:test';
2
- import { z } from 'zod';
3
- import { nodeRegistry, registerNode } from '../registry/registry';
4
- import { createDragSession } from './drag-session';
5
- const id = (s) => s;
6
- function makeSpyScene(initial = {}) {
7
- const calls = {
8
- pauseHistory: 0,
9
- resumeHistory: 0,
10
- restoreAll: 0,
11
- markedDirty: [],
12
- updated: [],
13
- };
14
- const nodes = { ...initial };
15
- return {
16
- get: ((nid) => nodes[nid]),
17
- nodes: () => nodes,
18
- update: (nid, patch) => {
19
- calls.updated.push([nid, patch]);
20
- const existing = nodes[nid];
21
- if (existing)
22
- nodes[nid] = { ...existing, ...patch };
23
- },
24
- upsert: (n) => {
25
- nodes[n.id] = n;
26
- return n.id;
27
- },
28
- delete: (nid) => {
29
- delete nodes[nid];
30
- },
31
- restore: () => { },
32
- restoreAll: () => {
33
- calls.restoreAll += 1;
34
- },
35
- markDirty: (nid) => {
36
- calls.markedDirty.push(nid);
37
- },
38
- pauseHistory: () => {
39
- calls.pauseHistory += 1;
40
- },
41
- resumeHistory: () => {
42
- calls.resumeHistory += 1;
43
- },
44
- getSubtree: () => null,
45
- cloneNodesInto: () => null,
46
- _calls: calls,
47
- };
48
- }
49
- function makeDef(kind, relations) {
50
- return {
51
- kind,
52
- schemaVersion: 1,
53
- schema: z.object({ type: z.literal(kind) }),
54
- category: 'utility',
55
- defaults: () => ({}),
56
- capabilities: {},
57
- relations,
58
- renderer: { kind: 'parametric', module: async () => ({ default: () => null }) },
59
- };
60
- }
61
- function makeAction() {
62
- return {
63
- begin: ({ node }) => ({ id: node?.id ?? id('default') }),
64
- preview: (_ctx, point) => ({ x: point[0] }),
65
- apply: (_draft, ctx) => [ctx.id],
66
- cancel: () => { },
67
- };
68
- }
69
- describe('createDragSession', () => {
70
- beforeEach(() => {
71
- nodeRegistry._reset();
72
- });
73
- test('start pauses history; commit resumes', () => {
74
- const scene = makeSpyScene();
75
- const session = createDragSession(makeAction(), scene);
76
- session.start({ point: [0, 0] });
77
- expect(scene._calls.pauseHistory).toBe(1);
78
- expect(scene._calls.resumeHistory).toBe(0);
79
- session.commit();
80
- expect(scene._calls.resumeHistory).toBe(1);
81
- });
82
- test('cancel resumes history and calls restoreAll', () => {
83
- const scene = makeSpyScene();
84
- const session = createDragSession(makeAction(), scene);
85
- session.start({ point: [0, 0] });
86
- session.cancel();
87
- expect(scene._calls.resumeHistory).toBe(1);
88
- expect(scene._calls.restoreAll).toBe(1);
89
- });
90
- test('move runs preview + apply and marks the returned id dirty', () => {
91
- const scene = makeSpyScene({ a: { id: id('a'), type: 'thing' } });
92
- const session = createDragSession(makeAction(), scene);
93
- session.start({ point: [0, 0], node: { id: id('a') } });
94
- session.move([1, 0], { shift: false, alt: false, ctrl: false, meta: false });
95
- expect(session.getDraft()).toEqual({ x: 1 });
96
- expect(scene._calls.markedDirty).toContain(id('a'));
97
- });
98
- test('snap callback is invoked when defined', () => {
99
- const action = {
100
- ...makeAction(),
101
- snap: (draft) => ({ x: Math.round(draft.x) }),
102
- };
103
- const scene = makeSpyScene({ a: { id: id('a'), type: 'thing' } });
104
- const session = createDragSession(action, scene);
105
- session.start({ point: [0, 0], node: { id: id('a') } });
106
- session.move([0.7, 0], { shift: false, alt: false, ctrl: false, meta: false });
107
- expect(session.getDraft()).toEqual({ x: 1 });
108
- });
109
- test('commit returns false when action.commit returns false; calls action.cancel and restoreAll', () => {
110
- const cancelSpy = mock(() => { });
111
- const action = {
112
- ...makeAction(),
113
- cancel: cancelSpy,
114
- commit: () => false,
115
- };
116
- const scene = makeSpyScene({ a: { id: id('a'), type: 'thing' } });
117
- const session = createDragSession(action, scene);
118
- session.start({ point: [0, 0], node: { id: id('a') } });
119
- session.move([1, 0], { shift: false, alt: false, ctrl: false, meta: false });
120
- const result = session.commit();
121
- expect(result).toBe(false);
122
- expect(cancelSpy).toHaveBeenCalledTimes(1);
123
- expect(scene._calls.restoreAll).toBe(1);
124
- });
125
- test('move is a no-op when session is not active', () => {
126
- const scene = makeSpyScene();
127
- const session = createDragSession(makeAction(), scene);
128
- session.move([1, 0], { shift: false, alt: false, ctrl: false, meta: false });
129
- expect(scene._calls.markedDirty.length).toBe(0);
130
- });
131
- test('repeated start is a no-op (re-entry guard)', () => {
132
- const scene = makeSpyScene();
133
- const session = createDragSession(makeAction(), scene);
134
- session.start({ point: [0, 0] });
135
- session.start({ point: [99, 99] });
136
- expect(scene._calls.pauseHistory).toBe(1); // only one pause
137
- });
138
- test('dispose mid-drag cancels and cleans up', () => {
139
- const scene = makeSpyScene();
140
- const session = createDragSession(makeAction(), scene);
141
- session.start({ point: [0, 0] });
142
- expect(session.isActive()).toBe(true);
143
- session.dispose();
144
- expect(session.isActive()).toBe(false);
145
- expect(scene._calls.resumeHistory).toBe(1);
146
- expect(scene._calls.restoreAll).toBe(1);
147
- });
148
- test('dispose when inactive is a no-op', () => {
149
- const scene = makeSpyScene();
150
- const session = createDragSession(makeAction(), scene);
151
- session.dispose();
152
- expect(scene._calls.pauseHistory).toBe(0);
153
- expect(scene._calls.resumeHistory).toBe(0);
154
- });
155
- test('onCommit callback fires on successful commit', () => {
156
- const onCommit = mock(() => { });
157
- const onCancel = mock(() => { });
158
- const scene = makeSpyScene();
159
- const session = createDragSession(makeAction(), scene, { onCommit, onCancel });
160
- session.start({ point: [0, 0] });
161
- session.commit();
162
- expect(onCommit).toHaveBeenCalledTimes(1);
163
- expect(onCancel).toHaveBeenCalledTimes(0);
164
- });
165
- test('onCancel callback fires on explicit cancel', () => {
166
- const onCommit = mock(() => { });
167
- const onCancel = mock(() => { });
168
- const scene = makeSpyScene();
169
- const session = createDragSession(makeAction(), scene, { onCommit, onCancel });
170
- session.start({ point: [0, 0] });
171
- session.cancel();
172
- expect(onCommit).toHaveBeenCalledTimes(0);
173
- expect(onCancel).toHaveBeenCalledTimes(1);
174
- });
175
- test('dispose does NOT fire onCancel (silent cleanup)', () => {
176
- const onCommit = mock(() => { });
177
- const onCancel = mock(() => { });
178
- const scene = makeSpyScene();
179
- const session = createDragSession(makeAction(), scene, { onCommit, onCancel });
180
- session.start({ point: [0, 0] });
181
- session.dispose();
182
- expect(onCommit).toHaveBeenCalledTimes(0);
183
- expect(onCancel).toHaveBeenCalledTimes(0);
184
- expect(scene._calls.resumeHistory).toBe(1);
185
- expect(scene._calls.restoreAll).toBe(1);
186
- });
187
- test('dirty cascade fires once per id even across multiple move ticks', () => {
188
- // Register a kind with no relations — cascade returns just {startId}.
189
- registerNode(makeDef('thing'));
190
- const scene = makeSpyScene({ a: { id: id('a'), type: 'thing' } });
191
- const session = createDragSession(makeAction(), scene);
192
- session.start({ point: [0, 0], node: { id: id('a') } });
193
- session.move([1, 0], { shift: false, alt: false, ctrl: false, meta: false });
194
- session.move([2, 0], { shift: false, alt: false, ctrl: false, meta: false });
195
- session.move([3, 0], { shift: false, alt: false, ctrl: false, meta: false });
196
- // a is marked once, not three times
197
- expect(scene._calls.markedDirty.filter((mid) => mid === id('a')).length).toBe(1);
198
- });
199
- test('dirty cascade follows hosts relations from the registry', () => {
200
- registerNode(makeDef('wall', { hosts: ['door'] }));
201
- registerNode(makeDef('door'));
202
- const scene = makeSpyScene({
203
- w: { id: id('w'), type: 'wall', children: [id('d')] },
204
- d: { id: id('d'), type: 'door', parentId: id('w') },
205
- });
206
- const action = {
207
- begin: () => ({ id: id('w') }),
208
- preview: (_ctx, point) => ({ x: point[0] }),
209
- apply: (_draft, ctx) => [ctx.id],
210
- cancel: () => { },
211
- };
212
- const session = createDragSession(action, scene);
213
- session.start({ point: [0, 0], node: { id: id('w') } });
214
- session.move([1, 0], { shift: false, alt: false, ctrl: false, meta: false });
215
- // both wall and door marked dirty
216
- expect(scene._calls.markedDirty).toContain(id('w'));
217
- expect(scene._calls.markedDirty).toContain(id('d'));
218
- });
219
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=hosting.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hosting.test.d.ts","sourceRoot":"","sources":["../../src/services/hosting.test.ts"],"names":[],"mappings":""}
@@ -1,230 +0,0 @@
1
- import { beforeEach, describe, expect, test } from 'bun:test';
2
- import { z } from 'zod';
3
- import { nodeRegistry, registerNode } from '../registry/registry';
4
- import { canAttach, canHostOnTop, clampYToHostTop, getSurface, getTopSurfaceHeight, MAX_HOST_DEPTH, pickHost, } from './hosting';
5
- const id = (s) => s;
6
- function makeItem(idStr, attachTo) {
7
- return {
8
- id: id(idStr),
9
- type: 'item',
10
- parentId: null,
11
- visible: true,
12
- asset: attachTo ? { attachTo } : {},
13
- };
14
- }
15
- function makeDef(kind, capabilities = {}, overrides = {}) {
16
- return {
17
- kind,
18
- schemaVersion: 1,
19
- schema: z.object({ type: z.literal(kind) }),
20
- category: 'utility',
21
- defaults: () => ({}),
22
- capabilities,
23
- renderer: { kind: 'parametric', module: async () => ({ default: () => null }) },
24
- ...overrides,
25
- };
26
- }
27
- function makeNode(kind, idStr, parentId = null) {
28
- return {
29
- id: id(idStr),
30
- type: kind,
31
- parentId: parentId ? id(parentId) : null,
32
- visible: true,
33
- };
34
- }
35
- function makeFakeScene(nodes) {
36
- return {
37
- get: ((nid) => nodes[nid]),
38
- nodes: () => nodes,
39
- update: () => { },
40
- upsert: () => id(''),
41
- delete: () => { },
42
- restore: () => { },
43
- restoreAll: () => { },
44
- markDirty: () => { },
45
- pauseHistory: () => { },
46
- resumeHistory: () => { },
47
- getSubtree: () => null,
48
- cloneNodesInto: () => null,
49
- };
50
- }
51
- describe('canAttach', () => {
52
- beforeEach(() => {
53
- nodeRegistry._reset();
54
- });
55
- test('rejects self-host', () => {
56
- const scene = makeFakeScene({ a: makeNode('thing', 'a') });
57
- const result = canAttach(id('a'), id('a'), scene);
58
- expect(result.ok).toBe(false);
59
- if (!result.ok)
60
- expect(result.error.kind).toBe('self-host');
61
- });
62
- test('rejects missing host', () => {
63
- const scene = makeFakeScene({ a: makeNode('thing', 'a') });
64
- const result = canAttach(id('a'), id('missing'), scene);
65
- expect(result.ok).toBe(false);
66
- if (!result.ok)
67
- expect(result.error.kind).toBe('host-missing');
68
- });
69
- test('detects cycle when host is a descendant of child', () => {
70
- // child=a, host=b, b's parent chain leads back to a → cycle.
71
- const scene = makeFakeScene({
72
- a: makeNode('thing', 'a'),
73
- b: makeNode('thing', 'b', 'c'),
74
- c: makeNode('thing', 'c', 'a'), // c.parent = a, b.parent = c → cycle if a → b
75
- });
76
- const result = canAttach(id('a'), id('b'), scene);
77
- expect(result.ok).toBe(false);
78
- if (!result.ok)
79
- expect(result.error.kind).toBe('cycle');
80
- });
81
- test('rejects when chain would exceed MAX_HOST_DEPTH', () => {
82
- const nodes = {};
83
- for (let i = 0; i <= MAX_HOST_DEPTH; i++) {
84
- nodes[`n${i}`] = makeNode('thing', `n${i}`, i === 0 ? null : `n${i - 1}`);
85
- }
86
- // n6 is already MAX_HOST_DEPTH deep — attaching n_new beneath it would
87
- // push the child to MAX_HOST_DEPTH + 1.
88
- nodes.candidate = makeNode('thing', 'candidate');
89
- const scene = makeFakeScene(nodes);
90
- const result = canAttach(id('candidate'), id(`n${MAX_HOST_DEPTH}`), scene);
91
- expect(result.ok).toBe(false);
92
- if (!result.ok)
93
- expect(result.error.kind).toBe('depth-exceeded');
94
- });
95
- test('accepts attach when chain stays within MAX_HOST_DEPTH', () => {
96
- const scene = makeFakeScene({
97
- root: makeNode('thing', 'root'),
98
- candidate: makeNode('thing', 'candidate'),
99
- });
100
- expect(canAttach(id('candidate'), id('root'), scene).ok).toBe(true);
101
- });
102
- test('rejects host kind not in child def.hostable.parents', () => {
103
- registerNode(makeDef('shelf', { hostable: { parents: ['wall', 'slab'] } }));
104
- const scene = makeFakeScene({
105
- s: makeNode('shelf', 's'),
106
- ceiling: makeNode('ceiling', 'ceiling'),
107
- });
108
- const result = canAttach(id('s'), id('ceiling'), scene);
109
- expect(result.ok).toBe(false);
110
- if (!result.ok)
111
- expect(result.error.kind).toBe('kind-not-allowed');
112
- });
113
- test('accepts when host kind is in parents', () => {
114
- registerNode(makeDef('shelf', { hostable: { parents: ['wall', 'slab'] } }));
115
- const scene = makeFakeScene({
116
- s: makeNode('shelf', 's'),
117
- w: makeNode('wall', 'w'),
118
- });
119
- expect(canAttach(id('s'), id('w'), scene).ok).toBe(true);
120
- });
121
- test('no def or no hostable.parents = no kind restriction', () => {
122
- // Some kinds (e.g. items via catalog) defer to runtime checks instead of
123
- // declaring parents up front. canAttach should not block them.
124
- const scene = makeFakeScene({
125
- i: makeNode('item', 'i'),
126
- w: makeNode('wall', 'w'),
127
- });
128
- expect(canAttach(id('i'), id('w'), scene).ok).toBe(true);
129
- });
130
- test('allows missing child (placement preview before commit)', () => {
131
- const scene = makeFakeScene({ w: makeNode('wall', 'w') });
132
- expect(canAttach(id('future-child'), id('w'), scene).ok).toBe(true);
133
- });
134
- });
135
- describe('getSurface / getTopSurfaceHeight', () => {
136
- beforeEach(() => {
137
- nodeRegistry._reset();
138
- });
139
- test('returns null when host has no registered def', () => {
140
- const node = makeNode('mystery', 'm');
141
- expect(getSurface(node)).toBeNull();
142
- expect(getTopSurfaceHeight(node)).toBeNull();
143
- });
144
- test('returns surface config when declared', () => {
145
- registerNode(makeDef('table', {
146
- surfaces: { top: { height: 0.74 } },
147
- }));
148
- const t = makeNode('table', 't');
149
- expect(getSurface(t)?.top?.height).toBe(0.74);
150
- expect(getTopSurfaceHeight(t)).toBe(0.74);
151
- });
152
- test('evaluates function-valued height with the node', () => {
153
- registerNode(makeDef('shelf', {
154
- surfaces: {
155
- top: {
156
- height: (n) => (n.id === id('high') ? 1.8 : 0.3),
157
- },
158
- },
159
- }));
160
- expect(getTopSurfaceHeight(makeNode('shelf', 'high'))).toBe(1.8);
161
- expect(getTopSurfaceHeight(makeNode('shelf', 'low'))).toBe(0.3);
162
- });
163
- });
164
- describe('clampYToHostTop', () => {
165
- beforeEach(() => {
166
- nodeRegistry._reset();
167
- });
168
- test('clamps to top when host has one', () => {
169
- registerNode(makeDef('table', { surfaces: { top: { height: 0.74 } } }));
170
- expect(clampYToHostTop(makeNode('table', 't'), 5)).toBe(0.74);
171
- });
172
- test('passes through when host has no top surface', () => {
173
- registerNode(makeDef('plain'));
174
- expect(clampYToHostTop(makeNode('plain', 'p'), 5)).toBe(5);
175
- });
176
- });
177
- describe('pickHost', () => {
178
- beforeEach(() => {
179
- nodeRegistry._reset();
180
- });
181
- test('returns first candidate that has hostable capability', () => {
182
- registerNode(makeDef('slab', { hostable: { parents: ['*'] } }));
183
- registerNode(makeDef('item')); // no hostable
184
- const candidates = [makeNode('item', 'i'), makeNode('slab', 's')];
185
- const picked = pickHost({
186
- point: [0, 0, 0],
187
- candidates,
188
- placedKind: 'chair',
189
- });
190
- expect(picked?.id).toBe(id('s'));
191
- });
192
- test('returns null when nothing in candidates is hostable', () => {
193
- registerNode(makeDef('item'));
194
- const candidates = [makeNode('item', 'i')];
195
- expect(pickHost({ point: [0, 0, 0], candidates, placedKind: 'chair' })).toBeNull();
196
- });
197
- test('hitTest can reject hostable candidates', () => {
198
- registerNode(makeDef('slab', { hostable: { parents: ['*'] } }));
199
- const candidates = [makeNode('slab', 's1'), makeNode('slab', 's2')];
200
- const picked = pickHost({
201
- point: [0, 0, 0],
202
- candidates,
203
- placedKind: 'chair',
204
- hitTest: (host) => host.id === id('s2'),
205
- });
206
- expect(picked?.id).toBe(id('s2'));
207
- });
208
- test('excludes ceiling-mounted hosts (ceiling fan cannot be a top surface)', () => {
209
- registerNode(makeDef('item', { hostable: { parents: ['*'] } }));
210
- const candidates = [makeItem('fan', 'ceiling'), makeItem('table')];
211
- const picked = pickHost({ point: [0, 0, 0], candidates, placedKind: 'item' });
212
- expect(picked?.id).toBe(id('table'));
213
- });
214
- test('keeps wall-mounted hosts (wall shelf still hosts)', () => {
215
- registerNode(makeDef('item', { hostable: { parents: ['*'] } }));
216
- const candidates = [makeItem('shelf', 'wall')];
217
- const picked = pickHost({ point: [0, 0, 0], candidates, placedKind: 'item' });
218
- expect(picked?.id).toBe(id('shelf'));
219
- });
220
- });
221
- describe('canHostOnTop', () => {
222
- test('rejects ceiling-attachTo hosts', () => {
223
- expect(canHostOnTop(makeItem('fan', 'ceiling'))).toBe(false);
224
- });
225
- test('accepts wall / wall-side / floor (undefined) hosts', () => {
226
- expect(canHostOnTop(makeItem('shelf', 'wall'))).toBe(true);
227
- expect(canHostOnTop(makeItem('sconce', 'wall-side'))).toBe(true);
228
- expect(canHostOnTop(makeItem('table'))).toBe(true);
229
- });
230
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=movement.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"movement.test.d.ts","sourceRoot":"","sources":["../../src/services/movement.test.ts"],"names":[],"mappings":""}
@@ -1,133 +0,0 @@
1
- import { beforeEach, describe, expect, test } from 'bun:test';
2
- import { z } from 'zod';
3
- import { nodeRegistry, registerNode } from '../registry/registry';
4
- import { applyAxisLock, isMovable, movePlanToward, moveToward, resolveMovable } from './movement';
5
- const id = (s) => s;
6
- function makeDef(kind, capabilities = {}) {
7
- return {
8
- kind,
9
- schemaVersion: 1,
10
- schema: z.object({ type: z.literal(kind) }),
11
- category: 'utility',
12
- defaults: () => ({}),
13
- capabilities,
14
- renderer: { kind: 'parametric', module: async () => ({ default: () => null }) },
15
- };
16
- }
17
- function makeNode(kind, idStr) {
18
- return {
19
- id: id(idStr),
20
- type: kind,
21
- parentId: null,
22
- visible: true,
23
- };
24
- }
25
- describe('resolveMovable', () => {
26
- beforeEach(() => {
27
- nodeRegistry._reset();
28
- });
29
- test('returns null when no def is registered', () => {
30
- expect(resolveMovable(makeNode('mystery', 'm'))).toBeNull();
31
- });
32
- test('returns null when def declares no movable capability', () => {
33
- registerNode(makeDef('static'));
34
- expect(resolveMovable(makeNode('static', 's'))).toBeNull();
35
- });
36
- test('returns the declared config', () => {
37
- registerNode(makeDef('column', { movable: { axes: ['x', 'z'], gridSnap: true } }));
38
- const config = resolveMovable(makeNode('column', 'c'));
39
- expect(config?.axes).toEqual(['x', 'z']);
40
- expect(config?.gridSnap).toBe(true);
41
- });
42
- test('runs override callback when present', () => {
43
- let overrideRan = false;
44
- const config = {
45
- axes: ['x'],
46
- override: () => {
47
- overrideRan = true;
48
- return { axes: ['y'] };
49
- },
50
- };
51
- registerNode(makeDef('weird', { movable: config }));
52
- const resolved = resolveMovable(makeNode('weird', 'w'));
53
- expect(overrideRan).toBe(true);
54
- expect(resolved?.axes).toEqual(['y']);
55
- });
56
- test('override returning null falls back to base config', () => {
57
- registerNode(makeDef('column', {
58
- movable: { axes: ['x', 'z'], override: () => null },
59
- }));
60
- const resolved = resolveMovable(makeNode('column', 'c'));
61
- expect(resolved?.axes).toEqual(['x', 'z']);
62
- });
63
- });
64
- describe('applyAxisLock', () => {
65
- test('passes through unlocked axes only', () => {
66
- expect(applyAxisLock([1, 2, 3], [10, 20, 30], ['x'])).toEqual([10, 2, 3]);
67
- expect(applyAxisLock([1, 2, 3], [10, 20, 30], ['x', 'z'])).toEqual([10, 2, 30]);
68
- expect(applyAxisLock([1, 2, 3], [10, 20, 30], ['x', 'y', 'z'])).toEqual([10, 20, 30]);
69
- });
70
- test('empty lock returns current unchanged', () => {
71
- expect(applyAxisLock([1, 2, 3], [10, 20, 30], [])).toEqual([1, 2, 3]);
72
- });
73
- });
74
- describe('moveToward', () => {
75
- beforeEach(() => {
76
- nodeRegistry._reset();
77
- });
78
- test('returns null when node is not movable', () => {
79
- registerNode(makeDef('static'));
80
- expect(moveToward(makeNode('static', 's'), [0, 0, 0], [1, 1, 1])).toBeNull();
81
- });
82
- test('axis-locks and returns the constrained target', () => {
83
- registerNode(makeDef('column', { movable: { axes: ['x', 'z'] } }));
84
- expect(moveToward(makeNode('column', 'c'), [0, 0.5, 0], [1, 99, 2])).toEqual([1, 0.5, 2]);
85
- });
86
- test('applies grid snap when capability declares gridSnap: true', () => {
87
- registerNode(makeDef('column', { movable: { axes: ['x', 'z'], gridSnap: true } }));
88
- const result = moveToward(makeNode('column', 'c'), [0, 0, 0], [0.3, 0, 0.6], {
89
- gridStep: 0.25,
90
- });
91
- expect(result).toEqual([0.25, 0, 0.5]);
92
- });
93
- test('grid snap can be overridden at call site', () => {
94
- registerNode(makeDef('column', { movable: { axes: ['x', 'z'], gridSnap: true } }));
95
- // Caller explicitly disables grid snap for this call
96
- const result = moveToward(makeNode('column', 'c'), [0, 0, 0], [0.3, 0, 0.6], {
97
- gridSnap: false,
98
- });
99
- expect(result).toEqual([0.3, 0, 0.6]);
100
- });
101
- });
102
- describe('movePlanToward', () => {
103
- beforeEach(() => {
104
- nodeRegistry._reset();
105
- });
106
- test('returns 2D point with X/Z constrained, Y dropped', () => {
107
- registerNode(makeDef('column', { movable: { axes: ['x', 'z'], gridSnap: true } }));
108
- const result = movePlanToward(makeNode('column', 'c'), 0.5, // currentY
109
- [0, 0], [0.3, 0.6], { gridStep: 0.25 });
110
- expect(result).toEqual([0.25, 0.5]);
111
- });
112
- test('returns null when node is not movable', () => {
113
- registerNode(makeDef('static'));
114
- expect(movePlanToward(makeNode('static', 's'), 0, [0, 0], [1, 1])).toBeNull();
115
- });
116
- });
117
- describe('isMovable', () => {
118
- beforeEach(() => {
119
- nodeRegistry._reset();
120
- });
121
- test('true when movable.axes has entries', () => {
122
- registerNode(makeDef('column', { movable: { axes: ['x'] } }));
123
- expect(isMovable(makeNode('column', 'c'))).toBe(true);
124
- });
125
- test('false when no movable capability', () => {
126
- registerNode(makeDef('static'));
127
- expect(isMovable(makeNode('static', 's'))).toBe(false);
128
- });
129
- test('false when movable.axes is empty', () => {
130
- registerNode(makeDef('locked', { movable: { axes: [] } }));
131
- expect(isMovable(makeNode('locked', 'l'))).toBe(false);
132
- });
133
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=opening-guides.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"opening-guides.test.d.ts","sourceRoot":"","sources":["../../src/services/opening-guides.test.ts"],"names":[],"mappings":""}