@pascal-app/core 0.8.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (471) hide show
  1. package/dist/events/bus.d.ts +67 -13
  2. package/dist/events/bus.d.ts.map +1 -1
  3. package/dist/hooks/scene-registry/item-clip-registry.d.ts +18 -0
  4. package/dist/hooks/scene-registry/item-clip-registry.d.ts.map +1 -0
  5. package/dist/hooks/scene-registry/item-clip-registry.js +10 -0
  6. package/dist/hooks/scene-registry/scene-registry.d.ts +14 -23
  7. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  8. package/dist/hooks/scene-registry/scene-registry.js +59 -26
  9. package/dist/hooks/scene-registry/scene-registry.test.d.ts +2 -0
  10. package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +1 -0
  11. package/dist/hooks/scene-registry/scene-registry.test.js +26 -0
  12. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
  13. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
  14. package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
  15. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
  16. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
  17. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +366 -0
  18. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +79 -11
  19. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  20. package/dist/hooks/spatial-grid/spatial-grid-manager.js +508 -141
  21. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
  22. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  23. package/dist/hooks/spatial-grid/spatial-grid-sync.js +109 -18
  24. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +2 -0
  25. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +1 -0
  26. package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +363 -0
  27. package/dist/index.d.ts +36 -8
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +33 -6
  30. package/dist/lib/level-name.d.ts +4 -0
  31. package/dist/lib/level-name.d.ts.map +1 -0
  32. package/dist/lib/level-name.js +10 -0
  33. package/dist/lib/measurement-geometry.d.ts +19 -0
  34. package/dist/lib/measurement-geometry.d.ts.map +1 -0
  35. package/dist/lib/measurement-geometry.js +232 -0
  36. package/dist/lib/measurement-geometry.test.d.ts +2 -0
  37. package/dist/lib/measurement-geometry.test.d.ts.map +1 -0
  38. package/dist/lib/measurement-geometry.test.js +102 -0
  39. package/dist/lib/polygon-geometry.d.ts +0 -1
  40. package/dist/lib/polygon-geometry.d.ts.map +1 -1
  41. package/dist/lib/polygon-geometry.js +0 -18
  42. package/dist/lib/polygon-relations.d.ts +10 -0
  43. package/dist/lib/polygon-relations.d.ts.map +1 -0
  44. package/dist/lib/polygon-relations.js +73 -0
  45. package/dist/lib/selection-proxy.d.ts +4 -0
  46. package/dist/lib/selection-proxy.d.ts.map +1 -0
  47. package/dist/lib/selection-proxy.js +12 -0
  48. package/dist/lib/slab-polygon.d.ts +31 -2
  49. package/dist/lib/slab-polygon.d.ts.map +1 -1
  50. package/dist/lib/slab-polygon.js +525 -31
  51. package/dist/lib/slab-polygon.test.d.ts +2 -0
  52. package/dist/lib/slab-polygon.test.d.ts.map +1 -0
  53. package/dist/lib/slab-polygon.test.js +635 -0
  54. package/dist/lib/slots.d.ts +14 -0
  55. package/dist/lib/slots.d.ts.map +1 -0
  56. package/dist/lib/slots.js +26 -0
  57. package/dist/lib/space-detection-pause.test.d.ts +2 -0
  58. package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
  59. package/dist/lib/space-detection-pause.test.js +42 -0
  60. package/dist/lib/space-detection.d.ts +54 -2
  61. package/dist/lib/space-detection.d.ts.map +1 -1
  62. package/dist/lib/space-detection.js +599 -124
  63. package/dist/lib/space-detection.test.d.ts +2 -0
  64. package/dist/lib/space-detection.test.d.ts.map +1 -0
  65. package/dist/lib/space-detection.test.js +376 -0
  66. package/dist/lib/wall-distance.d.ts +60 -0
  67. package/dist/lib/wall-distance.d.ts.map +1 -0
  68. package/dist/lib/wall-distance.js +85 -0
  69. package/dist/lib/zone-quantities.d.ts +21 -0
  70. package/dist/lib/zone-quantities.d.ts.map +1 -0
  71. package/dist/lib/zone-quantities.js +183 -0
  72. package/dist/lib/zone-quantities.test.d.ts +2 -0
  73. package/dist/lib/zone-quantities.test.d.ts.map +1 -0
  74. package/dist/lib/zone-quantities.test.js +82 -0
  75. package/dist/material-library.d.ts +20 -1
  76. package/dist/material-library.d.ts.map +1 -1
  77. package/dist/material-library.js +3737 -200
  78. package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
  79. package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
  80. package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
  81. package/dist/registry/handles.d.ts +394 -0
  82. package/dist/registry/handles.d.ts.map +1 -0
  83. package/dist/registry/handles.js +24 -0
  84. package/dist/registry/index.d.ts +7 -0
  85. package/dist/registry/index.d.ts.map +1 -0
  86. package/dist/registry/index.js +4 -0
  87. package/dist/registry/registry.d.ts +136 -0
  88. package/dist/registry/registry.d.ts.map +1 -0
  89. package/dist/registry/registry.js +282 -0
  90. package/dist/registry/registry.test.d.ts +2 -0
  91. package/dist/registry/registry.test.d.ts.map +1 -0
  92. package/dist/registry/registry.test.js +196 -0
  93. package/dist/registry/relations-resolver.d.ts +42 -0
  94. package/dist/registry/relations-resolver.d.ts.map +1 -0
  95. package/dist/registry/relations-resolver.js +77 -0
  96. package/dist/registry/relations-resolver.test.d.ts +2 -0
  97. package/dist/registry/relations-resolver.test.d.ts.map +1 -0
  98. package/dist/registry/relations-resolver.test.js +183 -0
  99. package/dist/registry/scene-api.d.ts +44 -0
  100. package/dist/registry/scene-api.d.ts.map +1 -0
  101. package/dist/registry/scene-api.js +111 -0
  102. package/dist/registry/scene-api.test.d.ts +2 -0
  103. package/dist/registry/scene-api.test.d.ts.map +1 -0
  104. package/dist/registry/scene-api.test.js +183 -0
  105. package/dist/registry/subtree.d.ts +67 -0
  106. package/dist/registry/subtree.d.ts.map +1 -0
  107. package/dist/registry/subtree.js +123 -0
  108. package/dist/registry/subtree.test.d.ts +2 -0
  109. package/dist/registry/subtree.test.d.ts.map +1 -0
  110. package/dist/registry/subtree.test.js +163 -0
  111. package/dist/registry/types.d.ts +2099 -0
  112. package/dist/registry/types.d.ts.map +1 -0
  113. package/dist/registry/types.js +1 -0
  114. package/dist/schema/index.d.ts +35 -4
  115. package/dist/schema/index.d.ts.map +1 -1
  116. package/dist/schema/index.js +32 -4
  117. package/dist/schema/material.d.ts +13 -2
  118. package/dist/schema/material.d.ts.map +1 -1
  119. package/dist/schema/material.js +17 -5
  120. package/dist/schema/material.test.d.ts +2 -0
  121. package/dist/schema/material.test.d.ts.map +1 -0
  122. package/dist/schema/material.test.js +43 -0
  123. package/dist/schema/nodes/box-vent.d.ts +74 -0
  124. package/dist/schema/nodes/box-vent.d.ts.map +1 -0
  125. package/dist/schema/nodes/box-vent.js +63 -0
  126. package/dist/schema/nodes/building.d.ts +1 -1
  127. package/dist/schema/nodes/building.d.ts.map +1 -1
  128. package/dist/schema/nodes/building.js +3 -2
  129. package/dist/schema/nodes/cabinet.d.ts +534 -0
  130. package/dist/schema/nodes/cabinet.d.ts.map +1 -0
  131. package/dist/schema/nodes/cabinet.js +137 -0
  132. package/dist/schema/nodes/ceiling.d.ts +5 -2
  133. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  134. package/dist/schema/nodes/ceiling.js +5 -1
  135. package/dist/schema/nodes/chimney.d.ts +158 -0
  136. package/dist/schema/nodes/chimney.d.ts.map +1 -0
  137. package/dist/schema/nodes/chimney.js +67 -0
  138. package/dist/schema/nodes/column.d.ts +600 -7
  139. package/dist/schema/nodes/column.d.ts.map +1 -1
  140. package/dist/schema/nodes/column.js +589 -6
  141. package/dist/schema/nodes/cupola.d.ts +66 -0
  142. package/dist/schema/nodes/cupola.d.ts.map +1 -0
  143. package/dist/schema/nodes/cupola.js +28 -0
  144. package/dist/schema/nodes/door.d.ts +13 -5
  145. package/dist/schema/nodes/door.d.ts.map +1 -1
  146. package/dist/schema/nodes/door.js +12 -0
  147. package/dist/schema/nodes/dormer.d.ts +228 -0
  148. package/dist/schema/nodes/dormer.d.ts.map +1 -0
  149. package/dist/schema/nodes/dormer.js +119 -0
  150. package/dist/schema/nodes/downspout.d.ts +75 -0
  151. package/dist/schema/nodes/downspout.d.ts.map +1 -0
  152. package/dist/schema/nodes/downspout.js +63 -0
  153. package/dist/schema/nodes/duct-fitting.d.ts +91 -0
  154. package/dist/schema/nodes/duct-fitting.d.ts.map +1 -0
  155. package/dist/schema/nodes/duct-fitting.js +92 -0
  156. package/dist/schema/nodes/duct-segment.d.ts +61 -0
  157. package/dist/schema/nodes/duct-segment.d.ts.map +1 -0
  158. package/dist/schema/nodes/duct-segment.js +73 -0
  159. package/dist/schema/nodes/duct-terminal.d.ts +57 -0
  160. package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
  161. package/dist/schema/nodes/duct-terminal.js +51 -0
  162. package/dist/schema/nodes/elevator.d.ts +106 -0
  163. package/dist/schema/nodes/elevator.d.ts.map +1 -0
  164. package/dist/schema/nodes/elevator.js +57 -0
  165. package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
  166. package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
  167. package/dist/schema/nodes/eyebrow-vent.js +40 -0
  168. package/dist/schema/nodes/fence.d.ts +19 -2
  169. package/dist/schema/nodes/fence.d.ts.map +1 -1
  170. package/dist/schema/nodes/fence.js +27 -2
  171. package/dist/schema/nodes/gutter.d.ts +84 -0
  172. package/dist/schema/nodes/gutter.d.ts.map +1 -0
  173. package/dist/schema/nodes/gutter.js +76 -0
  174. package/dist/schema/nodes/hvac-equipment.d.ts +63 -0
  175. package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
  176. package/dist/schema/nodes/hvac-equipment.js +55 -0
  177. package/dist/schema/nodes/item.d.ts +12 -0
  178. package/dist/schema/nodes/item.d.ts.map +1 -1
  179. package/dist/schema/nodes/item.js +20 -0
  180. package/dist/schema/nodes/level.d.ts +1 -1
  181. package/dist/schema/nodes/level.d.ts.map +1 -1
  182. package/dist/schema/nodes/level.js +4 -0
  183. package/dist/schema/nodes/lineset.d.ts +42 -0
  184. package/dist/schema/nodes/lineset.d.ts.map +1 -0
  185. package/dist/schema/nodes/lineset.js +38 -0
  186. package/dist/schema/nodes/liquid-line.d.ts +34 -0
  187. package/dist/schema/nodes/liquid-line.d.ts.map +1 -0
  188. package/dist/schema/nodes/liquid-line.js +24 -0
  189. package/dist/schema/nodes/measurement.d.ts +306 -0
  190. package/dist/schema/nodes/measurement.d.ts.map +1 -0
  191. package/dist/schema/nodes/measurement.js +87 -0
  192. package/dist/schema/nodes/measurement.test.d.ts +2 -0
  193. package/dist/schema/nodes/measurement.test.d.ts.map +1 -0
  194. package/dist/schema/nodes/measurement.test.js +139 -0
  195. package/dist/schema/nodes/pipe-fitting.d.ts +56 -0
  196. package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -0
  197. package/dist/schema/nodes/pipe-fitting.js +45 -0
  198. package/dist/schema/nodes/pipe-segment.d.ts +48 -0
  199. package/dist/schema/nodes/pipe-segment.d.ts.map +1 -0
  200. package/dist/schema/nodes/pipe-segment.js +36 -0
  201. package/dist/schema/nodes/pipe-trap.d.ts +43 -0
  202. package/dist/schema/nodes/pipe-trap.d.ts.map +1 -0
  203. package/dist/schema/nodes/pipe-trap.js +36 -0
  204. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +2 -0
  205. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +1 -0
  206. package/dist/schema/nodes/ridge-vent-defaults.test.js +205 -0
  207. package/dist/schema/nodes/ridge-vent.d.ts +82 -0
  208. package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
  209. package/dist/schema/nodes/ridge-vent.js +248 -0
  210. package/dist/schema/nodes/roof-segment-shape.d.ts +90 -0
  211. package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -0
  212. package/dist/schema/nodes/roof-segment-shape.js +318 -0
  213. package/dist/schema/nodes/roof-segment-shape.test.d.ts +2 -0
  214. package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +1 -0
  215. package/dist/schema/nodes/roof-segment-shape.test.js +175 -0
  216. package/dist/schema/nodes/roof-segment-surface.test.d.ts +2 -0
  217. package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +1 -0
  218. package/dist/schema/nodes/roof-segment-surface.test.js +48 -0
  219. package/dist/schema/nodes/roof-segment-trim.test.d.ts +2 -0
  220. package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +1 -0
  221. package/dist/schema/nodes/roof-segment-trim.test.js +107 -0
  222. package/dist/schema/nodes/roof-segment-walls.d.ts +101 -0
  223. package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -0
  224. package/dist/schema/nodes/roof-segment-walls.js +330 -0
  225. package/dist/schema/nodes/roof-segment-walls.test.d.ts +2 -0
  226. package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +1 -0
  227. package/dist/schema/nodes/roof-segment-walls.test.js +92 -0
  228. package/dist/schema/nodes/roof-segment.d.ts +273 -3
  229. package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
  230. package/dist/schema/nodes/roof-segment.js +539 -3
  231. package/dist/schema/nodes/roof.d.ts +8 -8
  232. package/dist/schema/nodes/roof.d.ts.map +1 -1
  233. package/dist/schema/nodes/roof.js +4 -16
  234. package/dist/schema/nodes/shelf.d.ts +106 -0
  235. package/dist/schema/nodes/shelf.d.ts.map +1 -0
  236. package/dist/schema/nodes/shelf.js +90 -0
  237. package/dist/schema/nodes/site.d.ts +1 -122
  238. package/dist/schema/nodes/site.d.ts.map +1 -1
  239. package/dist/schema/nodes/site.js +2 -6
  240. package/dist/schema/nodes/skylight.d.ts +279 -0
  241. package/dist/schema/nodes/skylight.d.ts.map +1 -0
  242. package/dist/schema/nodes/skylight.js +152 -0
  243. package/dist/schema/nodes/slab.d.ts +5 -2
  244. package/dist/schema/nodes/slab.d.ts.map +1 -1
  245. package/dist/schema/nodes/slab.js +5 -1
  246. package/dist/schema/nodes/solar-panel.d.ts +117 -0
  247. package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
  248. package/dist/schema/nodes/solar-panel.js +47 -0
  249. package/dist/schema/nodes/stair-segment.d.ts +4 -4
  250. package/dist/schema/nodes/stair.d.ts +13 -12
  251. package/dist/schema/nodes/stair.d.ts.map +1 -1
  252. package/dist/schema/nodes/stair.js +3 -0
  253. package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
  254. package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
  255. package/dist/schema/nodes/surface-hole-metadata.js +3 -1
  256. package/dist/schema/nodes/turbine-vent.d.ts +68 -0
  257. package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
  258. package/dist/schema/nodes/turbine-vent.js +41 -0
  259. package/dist/schema/nodes/wall.d.ts +245 -6
  260. package/dist/schema/nodes/wall.d.ts.map +1 -1
  261. package/dist/schema/nodes/wall.js +257 -0
  262. package/dist/schema/nodes/wall.test.d.ts +2 -0
  263. package/dist/schema/nodes/wall.test.d.ts.map +1 -0
  264. package/dist/schema/nodes/wall.test.js +208 -0
  265. package/dist/schema/nodes/window.d.ts +11 -3
  266. package/dist/schema/nodes/window.d.ts.map +1 -1
  267. package/dist/schema/nodes/window.js +12 -0
  268. package/dist/schema/nodes/zone.d.ts +2 -0
  269. package/dist/schema/nodes/zone.d.ts.map +1 -1
  270. package/dist/schema/nodes/zone.js +6 -0
  271. package/dist/schema/scene-material.d.ts +41 -0
  272. package/dist/schema/scene-material.d.ts.map +1 -0
  273. package/dist/schema/scene-material.js +9 -0
  274. package/dist/schema/types.d.ts +2850 -655
  275. package/dist/schema/types.d.ts.map +1 -1
  276. package/dist/schema/types.js +49 -0
  277. package/dist/services/alignment-anchors.d.ts +100 -0
  278. package/dist/services/alignment-anchors.d.ts.map +1 -0
  279. package/dist/services/alignment-anchors.js +312 -0
  280. package/dist/services/alignment-anchors.test.d.ts +2 -0
  281. package/dist/services/alignment-anchors.test.d.ts.map +1 -0
  282. package/dist/services/alignment-anchors.test.js +383 -0
  283. package/dist/services/alignment.d.ts +131 -0
  284. package/dist/services/alignment.d.ts.map +1 -0
  285. package/dist/services/alignment.js +171 -0
  286. package/dist/services/alignment.test.d.ts +2 -0
  287. package/dist/services/alignment.test.d.ts.map +1 -0
  288. package/dist/services/alignment.test.js +92 -0
  289. package/dist/services/drag-session.d.ts +47 -0
  290. package/dist/services/drag-session.d.ts.map +1 -0
  291. package/dist/services/drag-session.js +100 -0
  292. package/dist/services/drag-session.test.d.ts +2 -0
  293. package/dist/services/drag-session.test.d.ts.map +1 -0
  294. package/dist/services/drag-session.test.js +219 -0
  295. package/dist/services/hosting.d.ts +88 -0
  296. package/dist/services/hosting.d.ts.map +1 -0
  297. package/dist/services/hosting.js +131 -0
  298. package/dist/services/hosting.test.d.ts +2 -0
  299. package/dist/services/hosting.test.d.ts.map +1 -0
  300. package/dist/services/hosting.test.js +230 -0
  301. package/dist/services/index.d.ts +13 -0
  302. package/dist/services/index.d.ts.map +1 -0
  303. package/dist/services/index.js +12 -0
  304. package/dist/services/level-height.d.ts +28 -0
  305. package/dist/services/level-height.d.ts.map +1 -0
  306. package/dist/services/level-height.js +62 -0
  307. package/dist/services/movement.d.ts +48 -0
  308. package/dist/services/movement.d.ts.map +1 -0
  309. package/dist/services/movement.js +65 -0
  310. package/dist/services/movement.test.d.ts +2 -0
  311. package/dist/services/movement.test.d.ts.map +1 -0
  312. package/dist/services/movement.test.js +133 -0
  313. package/dist/services/opening-guides.d.ts +141 -0
  314. package/dist/services/opening-guides.d.ts.map +1 -0
  315. package/dist/services/opening-guides.js +267 -0
  316. package/dist/services/opening-guides.test.d.ts +2 -0
  317. package/dist/services/opening-guides.test.d.ts.map +1 -0
  318. package/dist/services/opening-guides.test.js +208 -0
  319. package/dist/services/port-connectivity.d.ts +115 -0
  320. package/dist/services/port-connectivity.d.ts.map +1 -0
  321. package/dist/services/port-connectivity.js +300 -0
  322. package/dist/services/port-connectivity.test.d.ts +2 -0
  323. package/dist/services/port-connectivity.test.d.ts.map +1 -0
  324. package/dist/services/port-connectivity.test.js +297 -0
  325. package/dist/services/riser-diagram.d.ts +40 -0
  326. package/dist/services/riser-diagram.d.ts.map +1 -0
  327. package/dist/services/riser-diagram.js +110 -0
  328. package/dist/services/riser-diagram.test.d.ts +2 -0
  329. package/dist/services/riser-diagram.test.d.ts.map +1 -0
  330. package/dist/services/riser-diagram.test.js +66 -0
  331. package/dist/services/single-undo-dance.test.d.ts +2 -0
  332. package/dist/services/single-undo-dance.test.d.ts.map +1 -0
  333. package/dist/services/single-undo-dance.test.js +210 -0
  334. package/dist/services/snap.d.ts +80 -0
  335. package/dist/services/snap.d.ts.map +1 -0
  336. package/dist/services/snap.js +140 -0
  337. package/dist/services/snap.test.d.ts +2 -0
  338. package/dist/services/snap.test.d.ts.map +1 -0
  339. package/dist/services/snap.test.js +137 -0
  340. package/dist/services/system-graph.d.ts +30 -0
  341. package/dist/services/system-graph.d.ts.map +1 -0
  342. package/dist/services/system-graph.js +164 -0
  343. package/dist/services/system-graph.test.d.ts +2 -0
  344. package/dist/services/system-graph.test.d.ts.map +1 -0
  345. package/dist/services/system-graph.test.js +138 -0
  346. package/dist/services/validate-dwv.d.ts +32 -0
  347. package/dist/services/validate-dwv.d.ts.map +1 -0
  348. package/dist/services/validate-dwv.js +118 -0
  349. package/dist/services/validate-dwv.test.d.ts +2 -0
  350. package/dist/services/validate-dwv.test.d.ts.map +1 -0
  351. package/dist/services/validate-dwv.test.js +109 -0
  352. package/dist/solar-panel-presets.d.ts +17 -0
  353. package/dist/solar-panel-presets.d.ts.map +1 -0
  354. package/dist/solar-panel-presets.js +43 -0
  355. package/dist/store/actions/node-actions.d.ts +14 -2
  356. package/dist/store/actions/node-actions.d.ts.map +1 -1
  357. package/dist/store/actions/node-actions.js +585 -15
  358. package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
  359. package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
  360. package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
  361. package/dist/store/actions/reparent.test.d.ts +2 -0
  362. package/dist/store/actions/reparent.test.d.ts.map +1 -0
  363. package/dist/store/actions/reparent.test.js +212 -0
  364. package/dist/store/actions/ridge-vent-update.test.d.ts +2 -0
  365. package/dist/store/actions/ridge-vent-update.test.d.ts.map +1 -0
  366. package/dist/store/actions/ridge-vent-update.test.js +264 -0
  367. package/dist/store/history-control.d.ts +11 -0
  368. package/dist/store/history-control.d.ts.map +1 -1
  369. package/dist/store/history-control.js +33 -0
  370. package/dist/store/use-interactive.d.ts +39 -0
  371. package/dist/store/use-interactive.d.ts.map +1 -1
  372. package/dist/store/use-interactive.js +75 -0
  373. package/dist/store/use-live-node-overrides.d.ts +22 -0
  374. package/dist/store/use-live-node-overrides.d.ts.map +1 -0
  375. package/dist/store/use-live-node-overrides.js +59 -0
  376. package/dist/store/use-scene-dirty-tracking.test.d.ts +2 -0
  377. package/dist/store/use-scene-dirty-tracking.test.d.ts.map +1 -0
  378. package/dist/store/use-scene-dirty-tracking.test.js +68 -0
  379. package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
  380. package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
  381. package/dist/store/use-scene-elevator-migration.test.js +109 -0
  382. package/dist/store/use-scene-measurement-roundtrip.test.d.ts +2 -0
  383. package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +1 -0
  384. package/dist/store/use-scene-measurement-roundtrip.test.js +97 -0
  385. package/dist/store/use-scene-plugins.test.d.ts +2 -0
  386. package/dist/store/use-scene-plugins.test.d.ts.map +1 -0
  387. package/dist/store/use-scene-plugins.test.js +51 -0
  388. package/dist/store/use-scene-wall-slot-migration.test.d.ts +2 -0
  389. package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +1 -0
  390. package/dist/store/use-scene-wall-slot-migration.test.js +210 -0
  391. package/dist/store/use-scene-window-migration.test.d.ts +2 -0
  392. package/dist/store/use-scene-window-migration.test.d.ts.map +1 -0
  393. package/dist/store/use-scene-window-migration.test.js +85 -0
  394. package/dist/store/use-scene.d.ts +28 -2
  395. package/dist/store/use-scene.d.ts.map +1 -1
  396. package/dist/store/use-scene.js +546 -49
  397. package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
  398. package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
  399. package/dist/systems/elevator/elevator-dispatch.js +57 -0
  400. package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
  401. package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
  402. package/dist/systems/elevator/elevator-geometry.js +50 -0
  403. package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
  404. package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
  405. package/dist/systems/elevator/elevator-opening-sync.js +218 -0
  406. package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
  407. package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
  408. package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
  409. package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
  410. package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
  411. package/dist/systems/elevator/elevator-opening-system.js +48 -0
  412. package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
  413. package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
  414. package/dist/systems/elevator/elevator-runtime-system.js +8 -0
  415. package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
  416. package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
  417. package/dist/systems/elevator/elevator-runtime.js +225 -0
  418. package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
  419. package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
  420. package/dist/systems/elevator/elevator-runtime.test.js +64 -0
  421. package/dist/systems/elevator/elevator-service.d.ts +19 -0
  422. package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
  423. package/dist/systems/elevator/elevator-service.js +111 -0
  424. package/dist/systems/fence/fence-centerline.d.ts +12 -0
  425. package/dist/systems/fence/fence-centerline.d.ts.map +1 -0
  426. package/dist/systems/fence/fence-centerline.js +37 -0
  427. package/dist/systems/fence/fence-spline.d.ts +39 -0
  428. package/dist/systems/fence/fence-spline.d.ts.map +1 -0
  429. package/dist/systems/fence/fence-spline.js +196 -0
  430. package/dist/systems/fence/fence-spline.test.d.ts +2 -0
  431. package/dist/systems/fence/fence-spline.test.d.ts.map +1 -0
  432. package/dist/systems/fence/fence-spline.test.js +136 -0
  433. package/dist/systems/stair/stair-footprint.d.ts +44 -0
  434. package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
  435. package/dist/systems/stair/stair-footprint.js +178 -0
  436. package/dist/systems/stair/stair-opening-preview.d.ts +2447 -0
  437. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
  438. package/dist/systems/stair/stair-opening-preview.js +93 -0
  439. package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
  440. package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
  441. package/dist/systems/stair/stair-opening-preview.test.js +80 -0
  442. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  443. package/dist/systems/stair/stair-opening-sync.js +155 -147
  444. package/dist/systems/stair/stair-opening-sync.test.js +424 -2
  445. package/dist/systems/stair/stair-opening-system.d.ts +2 -0
  446. package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
  447. package/dist/systems/stair/stair-opening-system.js +104 -0
  448. package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
  449. package/dist/systems/wall/wall-mitering.js +38 -2
  450. package/dist/systems/wall/wall-mitering.test.d.ts +2 -0
  451. package/dist/systems/wall/wall-mitering.test.d.ts.map +1 -0
  452. package/dist/systems/wall/wall-mitering.test.js +63 -0
  453. package/dist/systems/wall/wall-move.d.ts +48 -0
  454. package/dist/systems/wall/wall-move.d.ts.map +1 -0
  455. package/dist/systems/wall/wall-move.js +193 -0
  456. package/dist/utils/clone-scene-graph.d.ts +7 -4
  457. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  458. package/dist/utils/clone-scene-graph.js +29 -7
  459. package/dist/utils/clone-scene-graph.test.d.ts +2 -0
  460. package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
  461. package/dist/utils/clone-scene-graph.test.js +62 -0
  462. package/dist/utils/heal-scene-graph.d.ts +18 -0
  463. package/dist/utils/heal-scene-graph.d.ts.map +1 -0
  464. package/dist/utils/heal-scene-graph.js +90 -0
  465. package/dist/utils/heal-scene-graph.test.d.ts +2 -0
  466. package/dist/utils/heal-scene-graph.test.d.ts.map +1 -0
  467. package/dist/utils/heal-scene-graph.test.js +88 -0
  468. package/dist/validation/validate-build-json.d.ts +44 -0
  469. package/dist/validation/validate-build-json.d.ts.map +1 -0
  470. package/dist/validation/validate-build-json.js +256 -0
  471. package/package.json +11 -4
@@ -1,4 +1,389 @@
1
- import { getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, } from '../../schema';
1
+ import { nodeRegistry } from '../../registry/registry';
2
+ import { AnyNode as AnyNodeSchema, createDefaultRidgeVentsForSegment, getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, isAutoRidgeVentEnabled, isDefaultRidgeVentNode, } from '../../schema';
3
+ const DEFAULT_RIDGE_VENT_REFRESH_FIELDS = new Set([
4
+ 'roofType',
5
+ 'width',
6
+ 'depth',
7
+ 'pitch',
8
+ 'overhang',
9
+ 'wallThickness',
10
+ 'shingleThickness',
11
+ 'gambrelLowerWidthRatio',
12
+ 'mansardSteepWidthRatio',
13
+ 'dutchHipWidthRatio',
14
+ 'dutchWaistLengthRatio',
15
+ 'dutchGabletRake',
16
+ ]);
17
+ const NUMBER_FORMAT_BOUNDS = {
18
+ safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
19
+ int32: [-2147483648, 2147483647],
20
+ uint32: [0, 4294967295],
21
+ float32: [-3.4028234663852886e38, 3.4028234663852886e38],
22
+ };
23
+ const INTEGER_NUMBER_FORMATS = new Set(['safeint', 'int32', 'uint32']);
24
+ function getSchemaDef(schema) {
25
+ return schema?._zod?.def ?? schema?.def;
26
+ }
27
+ function getSchemaDefault(schema) {
28
+ const def = getSchemaDef(schema);
29
+ if (!(def?.type === 'default' || def?.type === 'prefault'))
30
+ return undefined;
31
+ return def.defaultValue;
32
+ }
33
+ function unwrapSchema(schema) {
34
+ let current = schema ?? null;
35
+ while (current) {
36
+ const def = getSchemaDef(current);
37
+ if (!(def?.type === 'default' ||
38
+ def?.type === 'prefault' ||
39
+ def?.type === 'optional' ||
40
+ def?.type === 'nullable' ||
41
+ def?.type === 'catch' ||
42
+ def?.type === 'readonly' ||
43
+ def?.type === 'nonoptional')) {
44
+ return current;
45
+ }
46
+ current = def.innerType ?? null;
47
+ }
48
+ return null;
49
+ }
50
+ function getObjectShape(schema) {
51
+ const unwrapped = unwrapSchema(schema);
52
+ const def = getSchemaDef(unwrapped);
53
+ if (def?.type !== 'object')
54
+ return null;
55
+ return unwrapped?.shape ?? def.shape ?? null;
56
+ }
57
+ function schemaAllowsValue(schema, value) {
58
+ const unwrapped = unwrapSchema(schema);
59
+ if (!unwrapped)
60
+ return false;
61
+ const def = getSchemaDef(unwrapped);
62
+ if (unwrapped._zod?.values?.has(value))
63
+ return true;
64
+ if (Array.isArray(def?.values) && def.values.includes(value))
65
+ return true;
66
+ if (def?.entries && Object.values(def.entries).includes(value))
67
+ return true;
68
+ return false;
69
+ }
70
+ function getNodeSchemaForType(type) {
71
+ const schema = AnyNodeSchema;
72
+ const options = getSchemaDef(schema)?.options;
73
+ if (!options)
74
+ return null;
75
+ for (const option of options) {
76
+ const shape = getObjectShape(option);
77
+ if (shape?.type && schemaAllowsValue(shape.type, type)) {
78
+ return option;
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+ function applyLowerLimit(current, candidate) {
84
+ if (!current)
85
+ return candidate;
86
+ if (candidate.value > current.value)
87
+ return candidate;
88
+ if (candidate.value === current.value && !candidate.inclusive)
89
+ return candidate;
90
+ return current;
91
+ }
92
+ function applyUpperLimit(current, candidate) {
93
+ if (!current)
94
+ return candidate;
95
+ if (candidate.value < current.value)
96
+ return candidate;
97
+ if (candidate.value === current.value && !candidate.inclusive)
98
+ return candidate;
99
+ return current;
100
+ }
101
+ function getNumberConstraints(schema) {
102
+ const unwrapped = unwrapSchema(schema) ?? schema;
103
+ const def = getSchemaDef(unwrapped);
104
+ const constraints = { integer: false };
105
+ const minValue = unwrapped.minValue;
106
+ if (typeof minValue === 'number') {
107
+ constraints.min = applyLowerLimit(constraints.min, { value: minValue, inclusive: true });
108
+ }
109
+ const maxValue = unwrapped.maxValue;
110
+ if (typeof maxValue === 'number') {
111
+ constraints.max = applyUpperLimit(constraints.max, { value: maxValue, inclusive: true });
112
+ }
113
+ for (const check of def?.checks ?? []) {
114
+ const checkDef = check._zod?.def;
115
+ if (!checkDef)
116
+ continue;
117
+ if (checkDef.check === 'greater_than' && typeof checkDef.value === 'number') {
118
+ constraints.min = applyLowerLimit(constraints.min, {
119
+ value: checkDef.value,
120
+ inclusive: checkDef.inclusive !== false,
121
+ });
122
+ }
123
+ else if (checkDef.check === 'less_than' && typeof checkDef.value === 'number') {
124
+ constraints.max = applyUpperLimit(constraints.max, {
125
+ value: checkDef.value,
126
+ inclusive: checkDef.inclusive !== false,
127
+ });
128
+ }
129
+ else if (checkDef.check === 'number_format' && checkDef.format) {
130
+ constraints.integer ||= INTEGER_NUMBER_FORMATS.has(checkDef.format);
131
+ const bounds = NUMBER_FORMAT_BOUNDS[checkDef.format];
132
+ if (bounds) {
133
+ constraints.min = applyLowerLimit(constraints.min, {
134
+ value: bounds[0],
135
+ inclusive: true,
136
+ });
137
+ constraints.max = applyUpperLimit(constraints.max, {
138
+ value: bounds[1],
139
+ inclusive: true,
140
+ });
141
+ }
142
+ }
143
+ }
144
+ const bag = unwrapped._zod?.bag;
145
+ if (typeof bag?.minimum === 'number') {
146
+ constraints.min = applyLowerLimit(constraints.min, { value: bag.minimum, inclusive: true });
147
+ }
148
+ if (typeof bag?.exclusiveMinimum === 'number') {
149
+ constraints.min = applyLowerLimit(constraints.min, {
150
+ value: bag.exclusiveMinimum,
151
+ inclusive: false,
152
+ });
153
+ }
154
+ if (typeof bag?.maximum === 'number') {
155
+ constraints.max = applyUpperLimit(constraints.max, { value: bag.maximum, inclusive: true });
156
+ }
157
+ if (typeof bag?.exclusiveMaximum === 'number') {
158
+ constraints.max = applyUpperLimit(constraints.max, {
159
+ value: bag.exclusiveMaximum,
160
+ inclusive: false,
161
+ });
162
+ }
163
+ return constraints;
164
+ }
165
+ function nextAbove(value) {
166
+ return value === 0 ? Number.EPSILON : value + Math.abs(value) * Number.EPSILON;
167
+ }
168
+ function nextBelow(value) {
169
+ return value === 0 ? -Number.EPSILON : value - Math.abs(value) * Number.EPSILON;
170
+ }
171
+ function clampNumber(value, constraints) {
172
+ let next = value;
173
+ let rounded = false;
174
+ let clamped = false;
175
+ if (constraints.integer && !Number.isInteger(next)) {
176
+ next = Math.round(next);
177
+ rounded = true;
178
+ }
179
+ if (constraints.min) {
180
+ const min = constraints.min.inclusive ? constraints.min.value : nextAbove(constraints.min.value);
181
+ if (next < min) {
182
+ next = min;
183
+ clamped = true;
184
+ }
185
+ }
186
+ if (constraints.max) {
187
+ const max = constraints.max.inclusive ? constraints.max.value : nextBelow(constraints.max.value);
188
+ if (next > max) {
189
+ next = max;
190
+ clamped = true;
191
+ }
192
+ }
193
+ return {
194
+ value: next,
195
+ action: clamped ? 'clamped' : rounded ? 'rounded' : undefined,
196
+ };
197
+ }
198
+ function getFiniteFallbackNumber(fallback, constraints) {
199
+ if (typeof fallback !== 'number' || !Number.isFinite(fallback))
200
+ return undefined;
201
+ return clampNumber(fallback, constraints).value;
202
+ }
203
+ function sanitizeNumber(schema, value, fallback, path) {
204
+ const constraints = schema ? getNumberConstraints(schema) : { integer: false };
205
+ if (!Number.isFinite(value)) {
206
+ const replacement = getFiniteFallbackNumber(fallback, constraints);
207
+ if (replacement === undefined) {
208
+ return {
209
+ value,
210
+ omit: true,
211
+ issues: [{ path, from: value, action: 'dropped' }],
212
+ };
213
+ }
214
+ return {
215
+ value: replacement,
216
+ issues: [{ path, from: value, to: replacement, action: 'dropped' }],
217
+ };
218
+ }
219
+ const clamped = clampNumber(value, constraints);
220
+ if (!Object.is(clamped.value, value)) {
221
+ return {
222
+ value: clamped.value,
223
+ issues: [
224
+ {
225
+ path,
226
+ from: value,
227
+ to: clamped.value,
228
+ action: clamped.action ?? 'clamped',
229
+ },
230
+ ],
231
+ };
232
+ }
233
+ return { value, issues: [] };
234
+ }
235
+ function sanitizeNumericValue(schema, value, fallback, path) {
236
+ const defaultFallback = schema ? getSchemaDefault(schema) : undefined;
237
+ const effectiveFallback = fallback === undefined ? defaultFallback : fallback;
238
+ const unwrapped = unwrapSchema(schema);
239
+ const def = getSchemaDef(unwrapped);
240
+ if (def?.type === 'number') {
241
+ if (typeof value !== 'number')
242
+ return { value, issues: [] };
243
+ return sanitizeNumber(unwrapped, value, effectiveFallback, path);
244
+ }
245
+ if (typeof value === 'number') {
246
+ return sanitizeNumber(null, value, effectiveFallback, path);
247
+ }
248
+ if (def?.type === 'tuple' && Array.isArray(value)) {
249
+ const fallbackItems = Array.isArray(effectiveFallback) ? effectiveFallback : [];
250
+ const next = [...value];
251
+ const issues = [];
252
+ for (let index = 0; index < next.length; index += 1) {
253
+ const itemSchema = def.items?.[index] ?? def.rest ?? null;
254
+ const child = sanitizeNumericValue(itemSchema, next[index], fallbackItems[index], [
255
+ ...path,
256
+ index,
257
+ ]);
258
+ issues.push(...child.issues);
259
+ if (child.omit) {
260
+ return { value, omit: true, issues };
261
+ }
262
+ next[index] = child.value;
263
+ }
264
+ return { value: issues.length > 0 ? next : value, issues };
265
+ }
266
+ if (def?.type === 'array' && Array.isArray(value)) {
267
+ const fallbackItems = Array.isArray(effectiveFallback) ? effectiveFallback : [];
268
+ const next = [];
269
+ const issues = [];
270
+ let omitted = false;
271
+ for (let index = 0; index < value.length; index += 1) {
272
+ const child = sanitizeNumericValue(def.element ?? null, value[index], fallbackItems[index], [
273
+ ...path,
274
+ index,
275
+ ]);
276
+ issues.push(...child.issues);
277
+ if (child.omit) {
278
+ omitted = true;
279
+ continue;
280
+ }
281
+ next.push(child.value);
282
+ }
283
+ return { value: issues.length > 0 || omitted ? next : value, issues };
284
+ }
285
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
286
+ const shape = def?.type === 'object' ? (unwrapped?.shape ?? def.shape ?? {}) : {};
287
+ const fallbackObject = effectiveFallback &&
288
+ typeof effectiveFallback === 'object' &&
289
+ !Array.isArray(effectiveFallback)
290
+ ? effectiveFallback
291
+ : {};
292
+ const input = value;
293
+ const next = { ...input };
294
+ const issues = [];
295
+ for (const key of Object.keys(input)) {
296
+ const child = sanitizeNumericValue(shape[key] ?? null, input[key], fallbackObject[key], [
297
+ ...path,
298
+ key,
299
+ ]);
300
+ issues.push(...child.issues);
301
+ if (child.omit) {
302
+ delete next[key];
303
+ }
304
+ else {
305
+ next[key] = child.value;
306
+ }
307
+ }
308
+ return { value: issues.length > 0 ? next : value, issues };
309
+ }
310
+ return { value, issues: [] };
311
+ }
312
+ function formatNumericValue(value) {
313
+ if (Number.isNaN(value))
314
+ return 'NaN';
315
+ if (value === Infinity)
316
+ return 'Infinity';
317
+ if (value === -Infinity)
318
+ return '-Infinity';
319
+ return String(value);
320
+ }
321
+ function numericSanitizeIssuesToMessage(issues) {
322
+ return issues
323
+ .map((issue) => {
324
+ const path = issue.path.map(String).join('.') || '<root>';
325
+ const to = issue.to === undefined ? '' : ` -> ${formatNumericValue(issue.to)}`;
326
+ return `${path}: ${formatNumericValue(issue.from)} ${issue.action}${to}`;
327
+ })
328
+ .join('; ');
329
+ }
330
+ function warnSanitizedNodeMutation(mutation, nodeId, issues) {
331
+ console.warn(`[Scene] Sanitized invalid numeric node ${mutation}`, nodeId, numericSanitizeIssuesToMessage(issues));
332
+ }
333
+ function parseCreatedNode(node, parentId) {
334
+ const candidate = { ...node, parentId };
335
+ const parsed = AnyNodeSchema.safeParse(candidate);
336
+ if (parsed.success)
337
+ return parsed.data;
338
+ const schema = getNodeSchemaForType(candidate.type);
339
+ const sanitized = sanitizeNumericValue(schema, candidate, undefined, []);
340
+ if (sanitized.issues.length === 0) {
341
+ return candidate;
342
+ }
343
+ warnSanitizedNodeMutation('create', node.id, sanitized.issues);
344
+ return sanitized.value;
345
+ }
346
+ function parseUpdatedNode(currentNode, data) {
347
+ const candidate = { ...currentNode, ...data };
348
+ const parsed = AnyNodeSchema.safeParse(candidate);
349
+ if (parsed.success)
350
+ return parsed.data;
351
+ const schema = getNodeSchemaForType(candidate.type);
352
+ const sanitized = sanitizeNumericValue(schema, data, currentNode, []);
353
+ if (sanitized.issues.length === 0) {
354
+ return candidate;
355
+ }
356
+ warnSanitizedNodeMutation('update', currentNode.id, sanitized.issues);
357
+ return { ...currentNode, ...sanitized.value };
358
+ }
359
+ function shouldRefreshDefaultRidgeVents(data) {
360
+ return Object.keys(data).some((key) => DEFAULT_RIDGE_VENT_REFRESH_FIELDS.has(key));
361
+ }
362
+ function refreshDefaultRidgeVentsForSegment(nextNodes, segment) {
363
+ const childIds = Array.isArray(segment.children) ? segment.children : [];
364
+ if (!isAutoRidgeVentEnabled(segment, nextNodes))
365
+ return [];
366
+ const defaultIds = childIds.filter((childId) => isDefaultRidgeVentNode(nextNodes[childId], segment.id));
367
+ const defaultIdSet = new Set(defaultIds);
368
+ for (const id of defaultIds) {
369
+ delete nextNodes[id];
370
+ }
371
+ const nextVents = createDefaultRidgeVentsForSegment(segment);
372
+ for (const vent of nextVents) {
373
+ nextNodes[vent.id] = {
374
+ ...vent,
375
+ parentId: segment.id,
376
+ };
377
+ }
378
+ nextNodes[segment.id] = {
379
+ ...segment,
380
+ children: [
381
+ ...childIds.filter((childId) => !defaultIdSet.has(childId)),
382
+ ...nextVents.map((vent) => vent.id),
383
+ ],
384
+ };
385
+ return nextVents.map((vent) => vent.id);
386
+ }
2
387
  // Track pending RAF for updateNodesAction to prevent multiple queued callbacks
3
388
  let pendingRafId = null;
4
389
  let pendingUpdates = new Set();
@@ -154,21 +539,25 @@ export const createNodesAction = (set, get, ops) => {
154
539
  const nextRootIds = [...state.rootNodeIds];
155
540
  for (const { node, parentId } of ops) {
156
541
  const effectiveParentId = parentId ?? node.parentId ?? null;
157
- // 1. Assign parentId to the child (Safe because BaseNode has parentId)
158
- const newNode = {
159
- ...node,
160
- parentId: effectiveParentId,
161
- };
542
+ const newNode = parseCreatedNode(node, effectiveParentId);
162
543
  nextNodes[newNode.id] = newNode;
163
- // 2. Update the Parent's children list
544
+ // 2. Update the Parent's children list. We append to ANY container
545
+ // parent (kind has `children` in its schema) — if the field is
546
+ // present but undefined (e.g. an old saved scene from before the
547
+ // kind gained children), we initialise to `[]` first so the
548
+ // reparenting goes through. Without this, hosting items on an
549
+ // old shelf (v1, before `children` was added) silently no-ops:
550
+ // the item is reparented to the shelf but the shelf's children
551
+ // array is never updated, so `ParametricNodeRenderer` doesn't
552
+ // mount it and the item "disappears".
164
553
  if (effectiveParentId && nextNodes[effectiveParentId]) {
165
554
  const parent = nextNodes[effectiveParentId];
166
- // Type Guard: Check if the parent node is a container that supports children
167
- if ('children' in parent && Array.isArray(parent.children)) {
555
+ if ('children' in parent) {
556
+ const existing = parent.children;
557
+ const children = Array.isArray(existing) ? existing : [];
168
558
  nextNodes[effectiveParentId] = {
169
559
  ...parent,
170
- // Use Set to prevent duplicate IDs if createNode is called twice
171
- children: Array.from(new Set([...parent.children, newNode.id])), // We don't verify child types here
560
+ children: Array.from(new Set([...children, newNode.id])),
172
561
  };
173
562
  }
174
563
  }
@@ -190,41 +579,183 @@ export const createNodesAction = (set, get, ops) => {
190
579
  get().markDirty(node.parentId);
191
580
  });
192
581
  };
582
+ export const applyNodeChangesAction = (set, get, changes) => {
583
+ if (get().readOnly)
584
+ return;
585
+ const createOps = changes.create ?? [];
586
+ const updateOps = changes.update ?? [];
587
+ const deleteOps = changes.delete ?? [];
588
+ const nodesToMarkDirty = new Set();
589
+ const parentsToMarkDirty = new Set();
590
+ set((state) => {
591
+ const nextNodes = { ...state.nodes };
592
+ const nextCollections = { ...state.collections };
593
+ const nextRootIds = [...state.rootNodeIds];
594
+ let resolvedRootIds = nextRootIds;
595
+ for (const { id, data } of updateOps) {
596
+ const currentNode = nextNodes[id];
597
+ if (!currentNode)
598
+ continue;
599
+ const updatedNode = parseUpdatedNode(currentNode, data);
600
+ if (data.parentId !== undefined && data.parentId !== currentNode.parentId) {
601
+ const oldParentId = currentNode.parentId;
602
+ if (oldParentId && nextNodes[oldParentId]) {
603
+ const oldParent = nextNodes[oldParentId];
604
+ nextNodes[oldParent.id] = {
605
+ ...oldParent,
606
+ children: oldParent.children.filter((childId) => childId !== id),
607
+ };
608
+ parentsToMarkDirty.add(oldParent.id);
609
+ }
610
+ const newParentId = data.parentId;
611
+ if (newParentId && nextNodes[newParentId]) {
612
+ const newParent = nextNodes[newParentId];
613
+ nextNodes[newParent.id] = {
614
+ ...newParent,
615
+ children: Array.from(new Set([...newParent.children, id])),
616
+ };
617
+ parentsToMarkDirty.add(newParent.id);
618
+ }
619
+ }
620
+ nextNodes[id] = updatedNode;
621
+ if (updatedNode.type === 'roof-segment' && shouldRefreshDefaultRidgeVents(data)) {
622
+ for (const ventId of refreshDefaultRidgeVentsForSegment(nextNodes, updatedNode)) {
623
+ nodesToMarkDirty.add(ventId);
624
+ }
625
+ }
626
+ nodesToMarkDirty.add(id);
627
+ }
628
+ for (const { node, parentId } of createOps) {
629
+ const effectiveParentId = parentId ?? node.parentId ?? null;
630
+ const newNode = parseCreatedNode(node, effectiveParentId);
631
+ nextNodes[newNode.id] = newNode;
632
+ nodesToMarkDirty.add(newNode.id);
633
+ if (effectiveParentId && nextNodes[effectiveParentId]) {
634
+ const parent = nextNodes[effectiveParentId];
635
+ if ('children' in parent && Array.isArray(parent.children)) {
636
+ nextNodes[effectiveParentId] = {
637
+ ...parent,
638
+ children: Array.from(new Set([...parent.children, newNode.id])),
639
+ };
640
+ parentsToMarkDirty.add(effectiveParentId);
641
+ }
642
+ }
643
+ else if (!effectiveParentId && !nextRootIds.includes(newNode.id)) {
644
+ nextRootIds.push(newNode.id);
645
+ }
646
+ }
647
+ const allIdsToDelete = new Set();
648
+ const collectDelete = (id) => {
649
+ if (allIdsToDelete.has(id))
650
+ return;
651
+ allIdsToDelete.add(id);
652
+ const node = nextNodes[id];
653
+ if (node && 'children' in node && Array.isArray(node.children)) {
654
+ for (const childId of node.children) {
655
+ collectDelete(childId);
656
+ }
657
+ }
658
+ };
659
+ for (const id of deleteOps) {
660
+ collectDelete(id);
661
+ }
662
+ for (const id of allIdsToDelete) {
663
+ const node = nextNodes[id];
664
+ if (!node)
665
+ continue;
666
+ const parentId = node.parentId;
667
+ if (parentId && nextNodes[parentId] && !allIdsToDelete.has(parentId)) {
668
+ const parent = nextNodes[parentId];
669
+ if (parent.children) {
670
+ nextNodes[parent.id] = {
671
+ ...parent,
672
+ children: parent.children.filter((childId) => childId !== id),
673
+ };
674
+ parentsToMarkDirty.add(parent.id);
675
+ }
676
+ }
677
+ resolvedRootIds = resolvedRootIds.filter((rootId) => rootId !== id);
678
+ if ('collectionIds' in node && node.collectionIds) {
679
+ for (const collectionId of node.collectionIds) {
680
+ const collection = nextCollections[collectionId];
681
+ if (collection) {
682
+ nextCollections[collectionId] = {
683
+ ...collection,
684
+ nodeIds: collection.nodeIds.filter((nodeId) => nodeId !== id),
685
+ };
686
+ }
687
+ }
688
+ }
689
+ delete nextNodes[id];
690
+ }
691
+ return { nodes: nextNodes, rootNodeIds: resolvedRootIds, collections: nextCollections };
692
+ });
693
+ for (const id of nodesToMarkDirty) {
694
+ get().markDirty(id);
695
+ }
696
+ for (const id of parentsToMarkDirty) {
697
+ get().markDirty(id);
698
+ const parent = get().nodes[id];
699
+ if (parent && 'children' in parent && Array.isArray(parent.children)) {
700
+ for (const childId of parent.children) {
701
+ get().markDirty(childId);
702
+ }
703
+ }
704
+ }
705
+ };
193
706
  export const updateNodesAction = (set, get, updates) => {
194
707
  if (get().readOnly)
195
708
  return;
196
709
  const parentsToUpdate = new Set();
710
+ const extraNodesToUpdate = new Set();
197
711
  set((state) => {
198
712
  const nextNodes = { ...state.nodes };
199
713
  for (const { id, data } of updates) {
200
714
  const currentNode = nextNodes[id];
201
715
  if (!currentNode)
202
716
  continue;
717
+ const updatedNode = parseUpdatedNode(currentNode, data);
203
718
  // Handle Reparenting Logic
204
719
  if (data.parentId !== undefined && data.parentId !== currentNode.parentId) {
205
720
  // 1. Remove from old parent
206
721
  const oldParentId = currentNode.parentId;
207
722
  if (oldParentId && nextNodes[oldParentId]) {
208
723
  const oldParent = nextNodes[oldParentId];
724
+ const oldChildren = Array.isArray(oldParent.children)
725
+ ? oldParent.children
726
+ : [];
209
727
  nextNodes[oldParent.id] = {
210
728
  ...oldParent,
211
- children: oldParent.children.filter((childId) => childId !== id),
729
+ children: oldChildren.filter((childId) => childId !== id),
212
730
  };
213
731
  parentsToUpdate.add(oldParent.id);
214
732
  }
215
- // 2. Add to new parent
733
+ // 2. Add to new parent. Defensive against parents that don't yet
734
+ // carry a `children` array — older saved scenes can predate the
735
+ // schema field on a particular kind (shelf v1 → v2 added one),
736
+ // and a spread of `undefined` here throws and aborts the entire
737
+ // `set` callback. Initialising to `[]` matches what the schema's
738
+ // default would have produced.
216
739
  const newParentId = data.parentId;
217
740
  if (newParentId && nextNodes[newParentId]) {
218
741
  const newParent = nextNodes[newParentId];
742
+ const newChildren = Array.isArray(newParent.children)
743
+ ? newParent.children
744
+ : [];
219
745
  nextNodes[newParent.id] = {
220
746
  ...newParent,
221
- children: Array.from(new Set([...newParent.children, id])),
747
+ children: Array.from(new Set([...newChildren, id])),
222
748
  };
223
749
  parentsToUpdate.add(newParent.id);
224
750
  }
225
751
  }
226
752
  // Apply the update
227
- nextNodes[id] = { ...nextNodes[id], ...data };
753
+ nextNodes[id] = updatedNode;
754
+ if (updatedNode.type === 'roof-segment' && shouldRefreshDefaultRidgeVents(data)) {
755
+ for (const ventId of refreshDefaultRidgeVentsForSegment(nextNodes, updatedNode)) {
756
+ extraNodesToUpdate.add(ventId);
757
+ }
758
+ }
228
759
  }
229
760
  return { nodes: nextNodes };
230
761
  });
@@ -235,6 +766,9 @@ export const updateNodesAction = (set, get, updates) => {
235
766
  for (const pId of parentsToUpdate) {
236
767
  pendingUpdates.add(pId);
237
768
  }
769
+ for (const id of extraNodesToUpdate) {
770
+ pendingUpdates.add(id);
771
+ }
238
772
  if (pendingRafId !== null) {
239
773
  cancelAnimationFrame(pendingRafId);
240
774
  }
@@ -251,6 +785,7 @@ export const deleteNodesAction = (set, get, ids) => {
251
785
  return;
252
786
  const parentsToMarkDirty = new Set();
253
787
  const nodesToMarkDirty = new Set();
788
+ const deletedIds = new Set();
254
789
  const mergePlans = buildWallMergePlans(get().nodes, ids);
255
790
  set((state) => {
256
791
  const nextNodes = { ...state.nodes };
@@ -264,6 +799,13 @@ export const deleteNodesAction = (set, get, ids) => {
264
799
  return;
265
800
  allIds.add(id);
266
801
  const node = nextNodes[id];
802
+ const cascadeDeletes = node
803
+ ? nodeRegistry.get(node.type)?.parametrics?.onDeleteCascade?.(node, nextNodes, allIds)
804
+ : null;
805
+ if (cascadeDeletes) {
806
+ for (const companionId of cascadeDeletes)
807
+ collect(companionId);
808
+ }
267
809
  if (node && 'children' in node) {
268
810
  for (const cid of node.children)
269
811
  collect(cid);
@@ -274,6 +816,29 @@ export const deleteNodesAction = (set, get, ids) => {
274
816
  for (const plan of mergePlans) {
275
817
  allIds.add(plan.secondaryWallId);
276
818
  }
819
+ for (const id of allIds)
820
+ deletedIds.add(id);
821
+ // Let each deleted kind undo what it imposed on its neighbours (e.g. an
822
+ // auto-inserted elbow re-extends the duct runs it trimmed back onto the
823
+ // corner it replaced). Read against pre-deletion `nextNodes`; skip
824
+ // patches that target a node also being deleted.
825
+ for (const id of allIds) {
826
+ const node = nextNodes[id];
827
+ if (!node)
828
+ continue;
829
+ const onDelete = nodeRegistry.get(node.type)?.parametrics?.onDelete;
830
+ if (!onDelete)
831
+ continue;
832
+ for (const { id: targetId, data } of onDelete(node, nextNodes)) {
833
+ if (allIds.has(targetId))
834
+ continue;
835
+ const target = nextNodes[targetId];
836
+ if (!target)
837
+ continue;
838
+ nextNodes[targetId] = { ...target, ...data };
839
+ nodesToMarkDirty.add(targetId);
840
+ }
841
+ }
277
842
  for (const plan of mergePlans) {
278
843
  const primaryWall = nextNodes[plan.primaryWallId];
279
844
  if (!(primaryWall && primaryWall.type === 'wall') || allIds.has(plan.primaryWallId)) {
@@ -328,6 +893,11 @@ export const deleteNodesAction = (set, get, ids) => {
328
893
  }
329
894
  return { nodes: nextNodes, rootNodeIds: nextRootIds, collections: nextCollections };
330
895
  });
896
+ // Deleted ids must leave the dirty set: every consumer skips missing
897
+ // nodes without clearing them, so a mark on a deleted node would sit in
898
+ // the set (and defeat the consumers' empty-set early exit) forever.
899
+ for (const id of deletedIds)
900
+ get().clearDirty(id);
331
901
  // Mark affected nodes dirty: parents of deleted nodes and their remaining children
332
902
  // (e.g. deleting a slab affects sibling walls via level elevation changes)
333
903
  parentsToMarkDirty.forEach((parentId) => {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=node-mutation-sanitize.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-mutation-sanitize.test.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-mutation-sanitize.test.ts"],"names":[],"mappings":""}