@pascal-app/nodes 1.0.0 → 1.0.3

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 (439) hide show
  1. package/dist/block/definition.d.ts.map +1 -1
  2. package/dist/block/definition.js +4 -1
  3. package/dist/block/face-host.d.ts.map +1 -1
  4. package/dist/block/face-host.js +19 -2
  5. package/dist/block/hosted-edit.d.ts +13 -0
  6. package/dist/block/hosted-edit.d.ts.map +1 -0
  7. package/dist/block/hosted-edit.js +205 -0
  8. package/dist/block/last-operation.d.ts +1 -1
  9. package/dist/block/last-operation.d.ts.map +1 -1
  10. package/dist/block/last-operation.js +6 -4
  11. package/dist/block/modal-session.d.ts +2 -1
  12. package/dist/block/modal-session.d.ts.map +1 -1
  13. package/dist/block/modal-session.js +2 -2
  14. package/dist/block/selection.d.ts.map +1 -1
  15. package/dist/block/selection.js +82 -24
  16. package/dist/block/surface.d.ts +4 -0
  17. package/dist/block/surface.d.ts.map +1 -0
  18. package/dist/block/surface.js +54 -0
  19. package/dist/block/use-block-face-operation.d.ts +2 -2
  20. package/dist/block/use-block-face-operation.d.ts.map +1 -1
  21. package/dist/block/use-block-face-operation.js +25 -11
  22. package/dist/box-vent/definition.d.ts.map +1 -1
  23. package/dist/box-vent/definition.js +1 -0
  24. package/dist/box-vent/panel.js +4 -4
  25. package/dist/building/renderer.d.ts.map +1 -1
  26. package/dist/building/renderer.js +1 -1
  27. package/dist/cabinet/appliance-layout.d.ts +26 -0
  28. package/dist/cabinet/appliance-layout.d.ts.map +1 -0
  29. package/dist/cabinet/appliance-layout.js +52 -0
  30. package/dist/cabinet/countertop-layout.d.ts +173 -0
  31. package/dist/cabinet/countertop-layout.d.ts.map +1 -0
  32. package/dist/cabinet/countertop-layout.js +80 -0
  33. package/dist/cabinet/definition.d.ts.map +1 -1
  34. package/dist/cabinet/definition.js +17 -8
  35. package/dist/cabinet/geometry/cooktop.d.ts.map +1 -1
  36. package/dist/cabinet/geometry/cooktop.js +2 -2
  37. package/dist/cabinet/geometry/run.d.ts.map +1 -1
  38. package/dist/cabinet/geometry/run.js +10 -71
  39. package/dist/cabinet/geometry/sink.d.ts +2 -12
  40. package/dist/cabinet/geometry/sink.d.ts.map +1 -1
  41. package/dist/cabinet/geometry/sink.js +8 -39
  42. package/dist/cabinet/hosted-resize.d.ts +5 -0
  43. package/dist/cabinet/hosted-resize.d.ts.map +1 -0
  44. package/dist/cabinet/hosted-resize.js +14 -0
  45. package/dist/cabinet/placement-snap.d.ts.map +1 -1
  46. package/dist/cabinet/placement-snap.js +4 -2
  47. package/dist/cabinet/run-layout.d.ts +8 -1
  48. package/dist/cabinet/run-layout.d.ts.map +1 -1
  49. package/dist/cabinet/run-layout.js +22 -14
  50. package/dist/cabinet/run-ops.d.ts +3 -3
  51. package/dist/cabinet/run-ops.js +3 -3
  52. package/dist/cabinet/run-panel.d.ts +1 -1
  53. package/dist/cabinet/run-panel.d.ts.map +1 -1
  54. package/dist/cabinet/run-panel.js +16 -7
  55. package/dist/cabinet/surfaces.d.ts +5 -0
  56. package/dist/cabinet/surfaces.d.ts.map +1 -0
  57. package/dist/cabinet/surfaces.js +113 -0
  58. package/dist/cabinet/tool.d.ts.map +1 -1
  59. package/dist/cabinet/tool.js +8 -4
  60. package/dist/cabinet/tree-structure.d.ts.map +1 -1
  61. package/dist/cabinet/tree-structure.js +10 -9
  62. package/dist/ceiling/definition.d.ts.map +1 -1
  63. package/dist/ceiling/definition.js +6 -1
  64. package/dist/ceiling/floorplan-affordances.d.ts +4 -4
  65. package/dist/ceiling/panel.js +1 -1
  66. package/dist/ceiling/renderer.d.ts.map +1 -1
  67. package/dist/ceiling/renderer.js +3 -2
  68. package/dist/chimney/definition.d.ts.map +1 -1
  69. package/dist/chimney/definition.js +1 -0
  70. package/dist/chimney/panel.js +9 -9
  71. package/dist/column/definition.d.ts.map +1 -1
  72. package/dist/column/definition.js +10 -3
  73. package/dist/column/floorplan-affordances.d.ts.map +1 -1
  74. package/dist/column/floorplan-affordances.js +6 -1
  75. package/dist/column/floorplan.d.ts.map +1 -1
  76. package/dist/column/floorplan.js +23 -11
  77. package/dist/column/hosted-resize.d.ts +116 -0
  78. package/dist/column/hosted-resize.d.ts.map +1 -0
  79. package/dist/column/hosted-resize.js +9 -0
  80. package/dist/column/panel.d.ts.map +1 -1
  81. package/dist/column/panel.js +6 -3
  82. package/dist/column/renderer.d.ts.map +1 -1
  83. package/dist/column/renderer.js +25 -103
  84. package/dist/column/shape.d.ts +22 -0
  85. package/dist/column/shape.d.ts.map +1 -0
  86. package/dist/column/shape.js +120 -0
  87. package/dist/column/surface.d.ts +4 -0
  88. package/dist/column/surface.d.ts.map +1 -0
  89. package/dist/column/surface.js +66 -0
  90. package/dist/cupola/definition.d.ts.map +1 -1
  91. package/dist/cupola/definition.js +1 -0
  92. package/dist/cupola/panel.js +1 -1
  93. package/dist/door/definition.d.ts.map +1 -1
  94. package/dist/door/definition.js +40 -1
  95. package/dist/door/door-math.d.ts +1 -1
  96. package/dist/door/door-math.d.ts.map +1 -1
  97. package/dist/door/door-math.js +7 -10
  98. package/dist/door/floorplan-affordances.d.ts.map +1 -1
  99. package/dist/door/floorplan-affordances.js +8 -12
  100. package/dist/door/move-tool.d.ts.map +1 -1
  101. package/dist/door/move-tool.js +3 -4
  102. package/dist/door/panel.d.ts.map +1 -1
  103. package/dist/door/panel.js +28 -57
  104. package/dist/door/tool.d.ts.map +1 -1
  105. package/dist/door/tool.js +20 -2
  106. package/dist/dormer/definition.d.ts.map +1 -1
  107. package/dist/dormer/definition.js +1 -0
  108. package/dist/dormer/panel-position-section.js +2 -2
  109. package/dist/dormer/panel.js +1 -1
  110. package/dist/downspout/definition.d.ts.map +1 -1
  111. package/dist/downspout/definition.js +1 -0
  112. package/dist/duct-segment/parametrics.d.ts.map +1 -1
  113. package/dist/duct-segment/parametrics.js +5 -1
  114. package/dist/duct-segment/tool.d.ts +1 -0
  115. package/dist/duct-segment/tool.d.ts.map +1 -1
  116. package/dist/duct-segment/tool.js +21 -8
  117. package/dist/duct-terminal/tool.d.ts.map +1 -1
  118. package/dist/duct-terminal/tool.js +77 -8
  119. package/dist/elevator/definition.d.ts.map +1 -1
  120. package/dist/elevator/definition.js +2 -0
  121. package/dist/elevator/parametrics.d.ts.map +1 -1
  122. package/dist/elevator/parametrics.js +98 -1
  123. package/dist/eyebrow-vent/definition.d.ts.map +1 -1
  124. package/dist/eyebrow-vent/definition.js +1 -0
  125. package/dist/eyebrow-vent/panel.js +1 -1
  126. package/dist/fence/definition.d.ts.map +1 -1
  127. package/dist/fence/definition.js +1 -0
  128. package/dist/fence/inspector-editors.js +1 -1
  129. package/dist/fence/tool.js +2 -2
  130. package/dist/guide/definition.d.ts.map +1 -1
  131. package/dist/guide/definition.js +1 -0
  132. package/dist/gutter/definition.d.ts.map +1 -1
  133. package/dist/gutter/definition.js +1 -0
  134. package/dist/hvac-equipment/tool.d.ts.map +1 -1
  135. package/dist/hvac-equipment/tool.js +8 -2
  136. package/dist/imported-mesh/definition.d.ts +5 -0
  137. package/dist/imported-mesh/definition.d.ts.map +1 -0
  138. package/dist/imported-mesh/definition.js +38 -0
  139. package/dist/imported-mesh/floorplan.d.ts +4 -0
  140. package/dist/imported-mesh/floorplan.d.ts.map +1 -0
  141. package/dist/imported-mesh/floorplan.js +34 -0
  142. package/dist/imported-mesh/geometry.d.ts +5 -0
  143. package/dist/imported-mesh/geometry.d.ts.map +1 -0
  144. package/dist/imported-mesh/geometry.js +34 -0
  145. package/dist/imported-mesh/index.d.ts +4 -0
  146. package/dist/imported-mesh/index.d.ts.map +1 -0
  147. package/dist/imported-mesh/index.js +3 -0
  148. package/dist/imported-mesh/schema.d.ts +2 -0
  149. package/dist/imported-mesh/schema.d.ts.map +1 -0
  150. package/dist/imported-mesh/schema.js +1 -0
  151. package/dist/index.d.ts +2 -0
  152. package/dist/index.d.ts.map +1 -1
  153. package/dist/index.js +8 -0
  154. package/dist/item/__fixtures__/main-floorplan.d.ts +26 -0
  155. package/dist/item/__fixtures__/main-floorplan.d.ts.map +1 -0
  156. package/dist/item/__fixtures__/main-floorplan.js +297 -0
  157. package/dist/item/definition.d.ts.map +1 -1
  158. package/dist/item/definition.js +3 -1
  159. package/dist/item/floorplan-move.d.ts +9 -1
  160. package/dist/item/floorplan-move.d.ts.map +1 -1
  161. package/dist/item/floorplan-move.js +231 -99
  162. package/dist/item/floorplan.d.ts +22 -0
  163. package/dist/item/floorplan.d.ts.map +1 -1
  164. package/dist/item/floorplan.js +72 -2
  165. package/dist/item/move-tool.d.ts +1 -1
  166. package/dist/item/move-tool.d.ts.map +1 -1
  167. package/dist/item/move-tool.js +15 -7
  168. package/dist/item/panel.js +1 -1
  169. package/dist/item/renderer.d.ts.map +1 -1
  170. package/dist/item/renderer.js +8 -2
  171. package/dist/level/renderer.js +1 -1
  172. package/dist/lineset/tool.d.ts.map +1 -1
  173. package/dist/lineset/tool.js +1 -0
  174. package/dist/liquid-line/tool.d.ts.map +1 -1
  175. package/dist/liquid-line/tool.js +5 -1
  176. package/dist/measurement/definition.d.ts.map +1 -1
  177. package/dist/measurement/definition.js +1 -0
  178. package/dist/pipe-segment/parametrics.d.ts.map +1 -1
  179. package/dist/pipe-segment/parametrics.js +5 -1
  180. package/dist/pipe-segment/tool.d.ts.map +1 -1
  181. package/dist/pipe-segment/tool.js +14 -3
  182. package/dist/pipe-trap/tool.d.ts.map +1 -1
  183. package/dist/pipe-trap/tool.js +7 -8
  184. package/dist/procedural-item/definition.d.ts +4 -0
  185. package/dist/procedural-item/definition.d.ts.map +1 -0
  186. package/dist/procedural-item/definition.js +257 -0
  187. package/dist/procedural-item/geometry.d.ts +31 -0
  188. package/dist/procedural-item/geometry.d.ts.map +1 -0
  189. package/dist/procedural-item/geometry.js +88 -0
  190. package/dist/procedural-item/index.d.ts +3 -0
  191. package/dist/procedural-item/index.d.ts.map +1 -0
  192. package/dist/procedural-item/index.js +2 -0
  193. package/dist/procedural-item/move-session.d.ts +15 -0
  194. package/dist/procedural-item/move-session.d.ts.map +1 -0
  195. package/dist/procedural-item/move-session.js +197 -0
  196. package/dist/procedural-item/move-tool.d.ts +5 -0
  197. package/dist/procedural-item/move-tool.d.ts.map +1 -0
  198. package/dist/procedural-item/move-tool.js +193 -0
  199. package/dist/procedural-item/renderer.d.ts +5 -0
  200. package/dist/procedural-item/renderer.d.ts.map +1 -0
  201. package/dist/procedural-item/renderer.js +66 -0
  202. package/dist/ridge-vent/definition.d.ts.map +1 -1
  203. package/dist/ridge-vent/definition.js +1 -0
  204. package/dist/ridge-vent/panel.js +4 -4
  205. package/dist/roof/definition.d.ts.map +1 -1
  206. package/dist/roof/definition.js +4 -1
  207. package/dist/roof/panel-model.d.ts +4 -0
  208. package/dist/roof/panel-model.d.ts.map +1 -0
  209. package/dist/roof/panel-model.js +151 -0
  210. package/dist/roof/panel.d.ts.map +1 -1
  211. package/dist/roof/panel.js +9 -8
  212. package/dist/roof/parametrics.d.ts.map +1 -1
  213. package/dist/roof/parametrics.js +16 -1
  214. package/dist/roof-segment/definition.d.ts.map +1 -1
  215. package/dist/roof-segment/definition.js +1 -0
  216. package/dist/roof-segment/panel.d.ts.map +1 -1
  217. package/dist/roof-segment/panel.js +5 -5
  218. package/dist/roof-segment/parametrics.d.ts.map +1 -1
  219. package/dist/roof-segment/parametrics.js +176 -1
  220. package/dist/scan/definition.d.ts.map +1 -1
  221. package/dist/scan/definition.js +1 -0
  222. package/dist/shared/automatic-run-end-cap.d.ts +1 -0
  223. package/dist/shared/automatic-run-end-cap.d.ts.map +1 -1
  224. package/dist/shared/automatic-run-end-cap.js +10 -0
  225. package/dist/shared/commit-opening-move.d.ts +4 -0
  226. package/dist/shared/commit-opening-move.d.ts.map +1 -0
  227. package/dist/shared/commit-opening-move.js +9 -0
  228. package/dist/shared/curtain-opening-limits.d.ts +14 -0
  229. package/dist/shared/curtain-opening-limits.d.ts.map +1 -0
  230. package/dist/shared/curtain-opening-limits.js +53 -0
  231. package/dist/shared/distribution-run-tool.d.ts +10 -0
  232. package/dist/shared/distribution-run-tool.d.ts.map +1 -1
  233. package/dist/shared/distribution-run-tool.js +56 -11
  234. package/dist/shared/draft-axis-guides.d.ts +0 -6
  235. package/dist/shared/draft-axis-guides.d.ts.map +1 -1
  236. package/dist/shared/draft-axis-guides.js +1 -8
  237. package/dist/shared/hosted-resize.d.ts +12 -0
  238. package/dist/shared/hosted-resize.d.ts.map +1 -0
  239. package/dist/shared/hosted-resize.js +166 -0
  240. package/dist/shared/opening-grid-follow.d.ts +8 -0
  241. package/dist/shared/opening-grid-follow.d.ts.map +1 -0
  242. package/dist/shared/opening-grid-follow.js +12 -0
  243. package/dist/shared/opening-property-preview-host.d.ts +3 -0
  244. package/dist/shared/opening-property-preview-host.d.ts.map +1 -0
  245. package/dist/shared/opening-property-preview-host.js +21 -0
  246. package/dist/shared/opening-property-preview.d.ts +19 -0
  247. package/dist/shared/opening-property-preview.d.ts.map +1 -0
  248. package/dist/shared/opening-property-preview.js +99 -0
  249. package/dist/shared/panel-fields.d.ts +15 -0
  250. package/dist/shared/panel-fields.d.ts.map +1 -0
  251. package/dist/shared/panel-fields.js +31 -0
  252. package/dist/shared/resting-surface-plan.d.ts +4 -0
  253. package/dist/shared/resting-surface-plan.d.ts.map +1 -0
  254. package/dist/shared/resting-surface-plan.js +53 -0
  255. package/dist/shared/surface-counterparts.d.ts +12 -0
  256. package/dist/shared/surface-counterparts.d.ts.map +1 -0
  257. package/dist/shared/surface-counterparts.js +27 -0
  258. package/dist/shared/wall-attach-target.d.ts +1 -0
  259. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  260. package/dist/shared/wall-attach-target.js +4 -1
  261. package/dist/shared/wall-event-from-grid.d.ts +5 -0
  262. package/dist/shared/wall-event-from-grid.d.ts.map +1 -0
  263. package/dist/shared/wall-event-from-grid.js +36 -0
  264. package/dist/shelf/definition.d.ts.map +1 -1
  265. package/dist/shelf/definition.js +5 -2
  266. package/dist/shelf/floorplan-affordances.d.ts.map +1 -1
  267. package/dist/shelf/floorplan-affordances.js +5 -3
  268. package/dist/shelf/floorplan-move.d.ts.map +1 -1
  269. package/dist/shelf/floorplan-move.js +3 -2
  270. package/dist/shelf/floorplan.d.ts.map +1 -1
  271. package/dist/shelf/floorplan.js +9 -2
  272. package/dist/shelf/geometry.d.ts.map +1 -1
  273. package/dist/shelf/geometry.js +2 -10
  274. package/dist/site/renderer.d.ts.map +1 -1
  275. package/dist/site/renderer.js +9 -2
  276. package/dist/skylight/definition.d.ts.map +1 -1
  277. package/dist/skylight/definition.js +1 -0
  278. package/dist/skylight/panel.js +6 -6
  279. package/dist/slab/definition.d.ts.map +1 -1
  280. package/dist/slab/definition.js +4 -0
  281. package/dist/slab/panel.js +1 -1
  282. package/dist/solar-panel/definition.d.ts.map +1 -1
  283. package/dist/solar-panel/definition.js +1 -0
  284. package/dist/solar-panel/panel.js +2 -2
  285. package/dist/spawn/definition.d.ts.map +1 -1
  286. package/dist/spawn/definition.js +1 -0
  287. package/dist/spawn/panel.js +1 -1
  288. package/dist/stair/definition.d.ts.map +1 -1
  289. package/dist/stair/definition.js +2 -0
  290. package/dist/stair/panel.d.ts.map +1 -1
  291. package/dist/stair/panel.js +9 -7
  292. package/dist/stair/parametrics.d.ts.map +1 -1
  293. package/dist/stair/parametrics.js +164 -1
  294. package/dist/stair-segment/definition.d.ts.map +1 -1
  295. package/dist/stair-segment/definition.js +1 -0
  296. package/dist/stair-segment/panel.js +4 -4
  297. package/dist/stair-segment/parametrics.d.ts.map +1 -1
  298. package/dist/stair-segment/parametrics.js +81 -1
  299. package/dist/turbine-vent/definition.d.ts.map +1 -1
  300. package/dist/turbine-vent/definition.js +1 -0
  301. package/dist/turbine-vent/panel.js +1 -1
  302. package/dist/unit/definition.d.ts +10 -0
  303. package/dist/unit/definition.d.ts.map +1 -0
  304. package/dist/unit/definition.js +38 -0
  305. package/dist/unit/index.d.ts +2 -0
  306. package/dist/unit/index.d.ts.map +1 -0
  307. package/dist/unit/index.js +1 -0
  308. package/dist/unit/parametrics.d.ts +3 -0
  309. package/dist/unit/parametrics.d.ts.map +1 -0
  310. package/dist/unit/parametrics.js +4 -0
  311. package/dist/unit/schema.d.ts +2 -0
  312. package/dist/unit/schema.d.ts.map +1 -0
  313. package/dist/unit/schema.js +1 -0
  314. package/dist/unit/unit-panel.d.ts +5 -0
  315. package/dist/unit/unit-panel.d.ts.map +1 -0
  316. package/dist/unit/unit-panel.js +61 -0
  317. package/dist/wall/actions.d.ts +3 -0
  318. package/dist/wall/actions.d.ts.map +1 -0
  319. package/dist/wall/actions.js +56 -0
  320. package/dist/wall/curtain-opening-frame.d.ts +14 -0
  321. package/dist/wall/curtain-opening-frame.d.ts.map +1 -0
  322. package/dist/wall/curtain-opening-frame.js +111 -0
  323. package/dist/wall/curtain-panel-highlight.d.ts +3 -0
  324. package/dist/wall/curtain-panel-highlight.d.ts.map +1 -0
  325. package/dist/wall/curtain-panel-highlight.js +113 -0
  326. package/dist/wall/curtain-wall-adapter.d.ts +3 -0
  327. package/dist/wall/curtain-wall-adapter.d.ts.map +1 -0
  328. package/dist/wall/curtain-wall-adapter.js +55 -0
  329. package/dist/wall/curtain-wall-floorplan.d.ts +3 -0
  330. package/dist/wall/curtain-wall-floorplan.d.ts.map +1 -0
  331. package/dist/wall/curtain-wall-floorplan.js +91 -0
  332. package/dist/wall/curtain-wall-geometry.d.ts +4 -0
  333. package/dist/wall/curtain-wall-geometry.d.ts.map +1 -0
  334. package/dist/wall/curtain-wall-geometry.js +226 -0
  335. package/dist/wall/curtain-wall-layout.d.ts +16 -0
  336. package/dist/wall/curtain-wall-layout.d.ts.map +1 -0
  337. package/dist/wall/curtain-wall-layout.js +176 -0
  338. package/dist/wall/curtain-wall-materials.d.ts +5 -0
  339. package/dist/wall/curtain-wall-materials.d.ts.map +1 -0
  340. package/dist/wall/curtain-wall-materials.js +55 -0
  341. package/dist/wall/curtain-wall-panel.d.ts +14 -0
  342. package/dist/wall/curtain-wall-panel.d.ts.map +1 -0
  343. package/dist/wall/curtain-wall-panel.js +167 -0
  344. package/dist/wall/curtain-wall-piece-geometry.d.ts +5 -0
  345. package/dist/wall/curtain-wall-piece-geometry.d.ts.map +1 -0
  346. package/dist/wall/curtain-wall-piece-geometry.js +220 -0
  347. package/dist/wall/curtain-wall-shadow.d.ts +4 -0
  348. package/dist/wall/curtain-wall-shadow.d.ts.map +1 -0
  349. package/dist/wall/curtain-wall-shadow.js +15 -0
  350. package/dist/wall/definition.d.ts +0 -15
  351. package/dist/wall/definition.d.ts.map +1 -1
  352. package/dist/wall/definition.js +65 -4
  353. package/dist/wall/drawing-mode.d.ts +12 -0
  354. package/dist/wall/drawing-mode.d.ts.map +1 -0
  355. package/dist/wall/drawing-mode.js +38 -0
  356. package/dist/wall/floorplan-tool.d.ts +4 -0
  357. package/dist/wall/floorplan-tool.d.ts.map +1 -0
  358. package/dist/wall/floorplan-tool.js +107 -0
  359. package/dist/wall/floorplan.d.ts.map +1 -1
  360. package/dist/wall/floorplan.js +6 -2
  361. package/dist/wall/length-patch.d.ts +3 -0
  362. package/dist/wall/length-patch.d.ts.map +1 -0
  363. package/dist/wall/length-patch.js +14 -0
  364. package/dist/wall/move-tool.d.ts.map +1 -1
  365. package/dist/wall/move-tool.js +23 -1
  366. package/dist/wall/paint.d.ts.map +1 -1
  367. package/dist/wall/paint.js +39 -4
  368. package/dist/wall/panel-model.d.ts +4 -0
  369. package/dist/wall/panel-model.d.ts.map +1 -0
  370. package/dist/wall/panel-model.js +117 -0
  371. package/dist/wall/panel.d.ts.map +1 -1
  372. package/dist/wall/panel.js +20 -24
  373. package/dist/wall/property-preview.d.ts +7 -0
  374. package/dist/wall/property-preview.d.ts.map +1 -0
  375. package/dist/wall/property-preview.js +31 -0
  376. package/dist/wall/rectangle-command.d.ts +149 -0
  377. package/dist/wall/rectangle-command.d.ts.map +1 -0
  378. package/dist/wall/rectangle-command.js +22 -0
  379. package/dist/wall/rectangle-tool.d.ts +2 -0
  380. package/dist/wall/rectangle-tool.d.ts.map +1 -0
  381. package/dist/wall/rectangle-tool.js +147 -0
  382. package/dist/wall/renderer.d.ts.map +1 -1
  383. package/dist/wall/renderer.js +7 -4
  384. package/dist/wall/slots.d.ts +2 -2
  385. package/dist/wall/slots.d.ts.map +1 -1
  386. package/dist/wall/slots.js +10 -2
  387. package/dist/wall/split-floorplan-layer.d.ts +4 -0
  388. package/dist/wall/split-floorplan-layer.d.ts.map +1 -0
  389. package/dist/wall/split-floorplan-layer.js +71 -0
  390. package/dist/wall/split-pointer.d.ts +3 -0
  391. package/dist/wall/split-pointer.d.ts.map +1 -0
  392. package/dist/wall/split-pointer.js +95 -0
  393. package/dist/wall/split-preview.d.ts +55 -0
  394. package/dist/wall/split-preview.d.ts.map +1 -0
  395. package/dist/wall/split-preview.js +106 -0
  396. package/dist/wall/split-session.d.ts +16 -0
  397. package/dist/wall/split-session.d.ts.map +1 -0
  398. package/dist/wall/split-session.js +138 -0
  399. package/dist/wall/split-store.d.ts +16 -0
  400. package/dist/wall/split-store.d.ts.map +1 -0
  401. package/dist/wall/split-store.js +6 -0
  402. package/dist/wall/split-tool.d.ts +10 -0
  403. package/dist/wall/split-tool.d.ts.map +1 -0
  404. package/dist/wall/split-tool.js +95 -0
  405. package/dist/wall/system.d.ts.map +1 -1
  406. package/dist/wall/system.js +3 -1
  407. package/dist/wall/tool.d.ts.map +1 -1
  408. package/dist/wall/tool.js +14 -6
  409. package/dist/wall/wall-batch-system.d.ts.map +1 -1
  410. package/dist/wall/wall-batch-system.js +3 -0
  411. package/dist/window/definition.d.ts.map +1 -1
  412. package/dist/window/definition.js +48 -1
  413. package/dist/window/floorplan-affordances.d.ts.map +1 -1
  414. package/dist/window/floorplan-affordances.js +8 -10
  415. package/dist/window/move-tool.d.ts.map +1 -1
  416. package/dist/window/move-tool.js +4 -6
  417. package/dist/window/panel.d.ts.map +1 -1
  418. package/dist/window/panel.js +22 -60
  419. package/dist/window/tool.d.ts.map +1 -1
  420. package/dist/window/tool.js +34 -2
  421. package/dist/window/window-math.d.ts +1 -1
  422. package/dist/window/window-math.d.ts.map +1 -1
  423. package/dist/window/window-math.js +8 -11
  424. package/dist/zone/definition.d.ts.map +1 -1
  425. package/dist/zone/definition.js +8 -1
  426. package/dist/zone/floorplan.d.ts +1 -3
  427. package/dist/zone/floorplan.d.ts.map +1 -1
  428. package/dist/zone/floorplan.js +43 -28
  429. package/dist/zone/quantities-panel.d.ts.map +1 -1
  430. package/dist/zone/quantities-panel.js +32 -44
  431. package/dist/zone/renderer.d.ts.map +1 -1
  432. package/dist/zone/renderer.js +21 -7
  433. package/dist/zone/unit-membership.d.ts +12 -0
  434. package/dist/zone/unit-membership.d.ts.map +1 -0
  435. package/dist/zone/unit-membership.js +22 -0
  436. package/package.json +15 -9
  437. package/dist/shared/opening-documentation-fields.d.ts +0 -16
  438. package/dist/shared/opening-documentation-fields.d.ts.map +0 -1
  439. package/dist/shared/opening-documentation-fields.js +0 -54
@@ -5,6 +5,7 @@ import { useViewer } from '@pascal-app/viewer';
5
5
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
6
  import { BoxGeometry, EdgesGeometry, Vector3 } from 'three';
7
7
  import { LineBasicNodeMaterial } from 'three/webgpu';
8
+ import { commitOpeningMove } from '../shared/commit-opening-move';
8
9
  import { clearOpeningGuides3D, publishOpeningGuidesForWallEvent, } from '../shared/opening-guides-runtime';
9
10
  import { beginOpeningMoveHistorySession } from '../shared/opening-move-history';
10
11
  import { isWallMeshHidden, shouldIgnoreWallEventForOpeningMove, } from '../shared/opening-move-wall-gate';
@@ -451,14 +452,13 @@ const MoveDoorTool = ({ node: movingDoorNode }) => {
451
452
  visible: original.visible,
452
453
  });
453
454
  history.commitStep(() => {
454
- useScene.getState().updateNode(movingDoorNode.id, {
455
+ commitOpeningMove(movingDoorNode.id, {
455
456
  position: [target.clampedX, target.clampedY, 0],
456
457
  rotation: [0, target.itemRotation, 0],
457
458
  side: target.side,
458
459
  parentId: target.wallId,
459
460
  wallId: target.wallId,
460
461
  roofSegmentId: undefined,
461
- metadata: {},
462
462
  visible: true,
463
463
  });
464
464
  });
@@ -712,7 +712,7 @@ const MoveDoorTool = ({ node: movingDoorNode }) => {
712
712
  visible: original.visible,
713
713
  });
714
714
  history.commitStep(() => {
715
- useScene.getState().updateNode(movingDoorNode.id, {
715
+ commitOpeningMove(movingDoorNode.id, {
716
716
  position: target.position,
717
717
  rotation: [0, 0, 0],
718
718
  side: 'front',
@@ -720,7 +720,6 @@ const MoveDoorTool = ({ node: movingDoorNode }) => {
720
720
  wallId: undefined,
721
721
  roofSegmentId: segmentId,
722
722
  roofFace: target.face.id,
723
- metadata: {},
724
723
  visible: true,
725
724
  });
726
725
  });
@@ -1 +1 @@
1
- {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/door/panel.tsx"],"names":[],"mappings":"AAuIA,MAAM,CAAC,OAAO,UAAU,SAAS,uCAkpChC"}
1
+ {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/door/panel.tsx"],"names":[],"mappings":"AAyIA,MAAM,CAAC,OAAO,UAAU,SAAS,uCA+nChC"}
@@ -4,8 +4,10 @@ import { DoorNode, useInteractive, useScene } from '@pascal-app/core';
4
4
  import { ActionButton, ActionGroup, cn, PanelSection, PanelWrapper, SegmentedControl, SliderControl, ToggleControl, triggerSFX, useEditor, } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { Copy, DoorOpen, FlipHorizontal2, Move, Trash2 } from 'lucide-react';
7
- import { useCallback, useRef } from 'react';
8
- import { OpeningDocumentationFields } from '../shared/opening-documentation-fields';
7
+ import { useCallback, useEffect, useMemo } from 'react';
8
+ import { constrainCurtainOpening, curtainOpeningLimits } from '../shared/curtain-opening-limits';
9
+ import { createOpeningPropertyPreview } from '../shared/opening-property-preview';
10
+ import { openingPropertyPreviewHost } from '../shared/opening-property-preview-host';
9
11
  import { scaleHandleHeight } from './door-math';
10
12
  const doorTypeOptions = [
11
13
  { label: 'Hinged', value: 'hinged', available: true },
@@ -106,7 +108,10 @@ export default function DoorPanel() {
106
108
  const setSelection = useViewer((s) => s.setSelection);
107
109
  const deleteNode = useScene((s) => s.deleteNode);
108
110
  const setMovingNode = useEditor((s) => s.setMovingNode);
109
- const previewRef = useRef(null);
111
+ const preview = useMemo(() => selectedId
112
+ ? createOpeningPropertyPreview(selectedId, openingPropertyPreviewHost)
113
+ : null, [selectedId]);
114
+ useEffect(() => () => preview?.cancel(), [preview]);
110
115
  const node = useScene((s) => selectedId ? s.nodes[selectedId] : undefined);
111
116
  // Panel slider-drag fix recipe (plans/editor-node-registry.md). Without
112
117
  // it, the 29+ SliderControls in this panel would loop on drag.
@@ -116,6 +121,7 @@ export default function DoorPanel() {
116
121
  const liveNode = useScene.getState().nodes[selectedId];
117
122
  if (liveNode?.type !== 'door')
118
123
  return;
124
+ updates = constrainCurtainOpening(liveNode, updates, useScene.getState().nodes);
119
125
  const hasChange = Object.entries(updates).some(([key, value]) => {
120
126
  const currentValue = liveNode[key];
121
127
  return !isSameDoorValue(currentValue, value);
@@ -131,43 +137,8 @@ export default function DoorPanel() {
131
137
  if (liveNode.parentId)
132
138
  scene.dirtyNodes.add(liveNode.parentId);
133
139
  }, [selectedId]);
134
- const previewDoorUpdate = useCallback((key, value) => {
135
- if (!selectedId)
136
- return;
137
- const liveNode = useScene.getState().nodes[selectedId];
138
- if (liveNode?.type !== 'door')
139
- return;
140
- if (!(previewRef.current &&
141
- previewRef.current.id === selectedId &&
142
- previewRef.current.key === key)) {
143
- previewRef.current = {
144
- id: selectedId,
145
- key,
146
- value: liveNode[key],
147
- };
148
- }
149
- if (isSameDoorValue(liveNode[key], value))
150
- return;
151
- liveNode[key] = value;
152
- useScene.getState().dirtyNodes.add(selectedId);
153
- }, [selectedId]);
154
- const commitDoorPreview = useCallback((key, value) => {
155
- if (!selectedId)
156
- return;
157
- const scene = useScene.getState();
158
- const liveNode = scene.nodes[selectedId];
159
- const preview = previewRef.current;
160
- if (liveNode?.type === 'door' && preview?.id === selectedId && preview.key === key) {
161
- ;
162
- liveNode[key] = preview.value;
163
- scene.dirtyNodes.add(selectedId);
164
- }
165
- previewRef.current = null;
166
- useScene
167
- .getState()
168
- .updateNode(selectedId, { [key]: value });
169
- scene.dirtyNodes.add(selectedId);
170
- }, [selectedId]);
140
+ const previewDoorUpdate = (key, value) => preview?.preview({ [key]: value });
141
+ const commitDoorPreview = (key, value) => preview?.commit({ [key]: value });
171
142
  const handleClose = useCallback(() => {
172
143
  setSelection({ selectedIds: [] });
173
144
  }, [setSelection]);
@@ -254,6 +225,12 @@ export default function DoorPanel() {
254
225
  };
255
226
  if (!(node && node.type === 'door' && selectedId))
256
227
  return null;
228
+ const limits = curtainOpeningLimits(node, useScene.getState().nodes);
229
+ const heightUpdates = (height) => ({
230
+ height,
231
+ position: [node.position[0], height / 2, node.position[2]],
232
+ handleHeight: scaleHandleHeight(node.handleHeight, node.height, height),
233
+ });
257
234
  const hSum = node.segments.reduce((s, seg) => s + seg.heightRatio, 0);
258
235
  const normHeights = node.segments.map((seg) => seg.heightRatio / hSum);
259
236
  const isOpening = node.openingKind === 'opening';
@@ -478,19 +455,13 @@ export default function DoorPanel() {
478
455
  ? 'border-orange-400/60 bg-orange-400/10 text-foreground'
479
456
  : 'border-border/50 bg-[#2C2C2E] text-muted-foreground hover:bg-[#3e3e3e] hover:text-foreground', !option.available &&
480
457
  'cursor-not-allowed opacity-45 hover:bg-[#2C2C2E] hover:text-muted-foreground'), disabled: !option.available, onClick: () => handleUpdate(getDoorTypeUpdates(option.value)), type: "button", children: [_jsx(DoorOpen, { className: "h-4 w-4 shrink-0" }), _jsx("span", { className: "truncate font-medium", children: option.label })] }, option.value));
481
- }) }))] }), _jsx(PanelSection, { title: "Documentation", children: _jsx(OpeningDocumentationFields, { constructionType: node.constructionType, dimensionReference: node.dimensionReference, finishOpeningHeight: node.finishOpeningHeight, finishOpeningWidth: node.finishOpeningWidth, mark: node.mark, masonryOpeningHeight: node.masonryOpeningHeight, masonryOpeningWidth: node.masonryOpeningWidth, onChange: handleUpdate, roughOpeningHeight: node.roughOpeningHeight, roughOpeningWidth: node.roughOpeningWidth }) }), _jsxs(PanelSection, { title: "Position", children: [_jsx(SliderControl, { label: _jsxs(_Fragment, { children: ["X", _jsx("sub", { className: "ml-[1px] text-[11px] opacity-70", children: "wall" })] }), max: 10, min: -10, onChange: (v) => handleUpdate({ position: [v, node.position[1], node.position[2]] }), precision: 2, step: 0.1, unit: "m", value: Math.round(node.position[0] * 100) / 100 }), showFlipSide && (_jsx("div", { className: "px-1 pt-2 pb-1", children: _jsx(ActionButton, { className: "w-full", icon: _jsx(FlipHorizontal2, { className: "h-4 w-4" }), label: "Flip Side", onClick: handleFlip }) }))] }), showFoldSection && (_jsxs(PanelSection, { title: "Fold", children: [_jsx("div", { className: "flex flex-col gap-2 px-1 pb-1", children: _jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Panels" }), _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ leafCount: v === '2' ? 2 : 4 }), options: [
458
+ }) }))] }), _jsxs(PanelSection, { title: "Position", children: [_jsx(SliderControl, { label: _jsxs(_Fragment, { children: ["X", _jsx("sub", { className: "ml-[1px] text-[11px] opacity-70", children: "wall" })] }), max: 10, min: -10, onChange: (v) => handleUpdate({ position: [v, node.position[1], node.position[2]] }), precision: 2, step: 0.1, unit: "m", value: node.position[0] }), showFlipSide && (_jsx("div", { className: "px-1 pt-2 pb-1", children: _jsx(ActionButton, { className: "w-full", icon: _jsx(FlipHorizontal2, { className: "h-4 w-4" }), label: "Flip Side", onClick: handleFlip }) }))] }), showFoldSection && (_jsxs(PanelSection, { title: "Fold", children: [_jsx("div", { className: "flex flex-col gap-2 px-1 pb-1", children: _jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Panels" }), _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ leafCount: v === '2' ? 2 : 4 }), options: [
482
459
  { label: '2', value: '2' },
483
460
  { label: '4', value: '4' },
484
461
  ], value: node.leafCount === 2 ? '2' : '4' })] }) }), _jsx(SliderControl, { label: "Open", max: 100, min: 0, onChange: (v) => handleUpdate({ operationState: v / 100 }), precision: 0, restoreOnCommit: false, step: 5, unit: "%", value: Math.round((node.operationState ?? 0) * 100) })] })), showSlideSection && (_jsxs(PanelSection, { title: "Slide", children: [_jsx("div", { className: "flex flex-col gap-2 px-1 pb-1", children: _jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: doorType === 'pocket' ? 'Pocket' : doorType === 'barn' ? 'Rail' : 'Panel' }), _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ slideDirection: v }), options: [
485
462
  { label: 'Left', value: 'left' },
486
463
  { label: 'Right', value: 'right' },
487
- ], value: node.slideDirection ?? 'left' })] }) }), _jsx(SliderControl, { label: "Open", max: 100, min: 0, onChange: (v) => handleUpdate({ operationState: v / 100 }), precision: 0, restoreOnCommit: false, step: 5, unit: "%", value: Math.round((node.operationState ?? 0) * 100) })] })), showGarageSection && (_jsxs(PanelSection, { title: "Garage", children: [_jsx(SliderControl, { label: "Open", max: 100, min: 0, onChange: (v) => handleUpdate({ operationState: v / 100 }), precision: 0, restoreOnCommit: false, step: 5, unit: "%", value: Math.round((node.operationState ?? 0) * 100) }), isSectionalGarageDoor && (_jsx(SliderControl, { label: "Panels", max: 8, min: 3, onChange: (v) => handleUpdate({ garagePanelCount: Math.round(v) }), precision: 0, restoreOnCommit: false, step: 1, value: node.garagePanelCount ?? 4 }))] })), _jsxs(PanelSection, { title: "Dimensions", children: [_jsx(SliderControl, { label: "Width", max: maxDoorWidth, min: 0.5, onChange: (v) => handleUpdate({ width: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(node.width * 100) / 100 }), _jsx(SliderControl, { label: "Height", max: 1000, min: 1.0, onChange: (v) => handleUpdate({
488
- height: v,
489
- position: [node.position[0], v / 2, node.position[2]],
490
- // Keep the handle at the same relative height as the door resizes,
491
- // matching the height-resize arrow.
492
- handleHeight: scaleHandleHeight(node.handleHeight, node.height, v),
493
- }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(node.height * 100) / 100 })] }), showDoorShapeSection && (_jsxs(PanelSection, { title: "Top Shape", children: [_jsx("div", { className: "flex flex-col gap-2 px-1 pb-1", children: _jsx(SegmentedControl, { onChange: (v) => handleUpdate({
464
+ ], value: node.slideDirection ?? 'left' })] }) }), _jsx(SliderControl, { label: "Open", max: 100, min: 0, onChange: (v) => handleUpdate({ operationState: v / 100 }), precision: 0, restoreOnCommit: false, step: 5, unit: "%", value: Math.round((node.operationState ?? 0) * 100) })] })), showGarageSection && (_jsxs(PanelSection, { title: "Garage", children: [_jsx(SliderControl, { label: "Open", max: 100, min: 0, onChange: (v) => handleUpdate({ operationState: v / 100 }), precision: 0, restoreOnCommit: false, step: 5, unit: "%", value: Math.round((node.operationState ?? 0) * 100) }), isSectionalGarageDoor && (_jsx(SliderControl, { label: "Panels", max: 8, min: 3, onChange: (v) => handleUpdate({ garagePanelCount: Math.round(v) }), precision: 0, restoreOnCommit: false, step: 1, value: node.garagePanelCount ?? 4 }))] })), _jsxs(PanelSection, { title: "Dimensions", children: [limits && (_jsx("p", { className: "text-[11px] text-muted-foreground", children: "Size is limited to the wall, including clearance for the opening frame." })), _jsx(SliderControl, { label: "Width", max: Math.min(maxDoorWidth, limits?.width ?? Infinity), min: Math.min(0.5, limits?.width ?? 0.5), onChange: (v) => previewDoorUpdate('width', v), onCommit: (v) => commitDoorPreview('width', v), onCancel: () => preview?.cancel(), previewWhileTyping: true, precision: 2, restoreOnCommit: false, step: 0.01, unit: "m", value: node.width }), _jsx(SliderControl, { label: "Height", max: limits?.height ?? 1000, min: Math.min(1, limits?.height ?? 1), onChange: (v) => preview?.preview(heightUpdates(v)), onCommit: (v) => preview?.commit(heightUpdates(v)), onCancel: () => preview?.cancel(), previewWhileTyping: true, precision: 2, restoreOnCommit: false, step: 0.01, unit: "m", value: node.height })] }), showDoorShapeSection && (_jsxs(PanelSection, { title: "Top Shape", children: [_jsx("div", { className: "flex flex-col gap-2 px-1 pb-1", children: _jsx(SegmentedControl, { onChange: (v) => handleUpdate({
494
465
  openingShape: v,
495
466
  ...(v === 'rounded'
496
467
  ? {
@@ -508,10 +479,10 @@ export default function DoorPanel() {
508
479
  ], value: doorShape }) }), doorShape === 'rounded' && (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex flex-col gap-2 px-1 pb-1", children: _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ openingRadiusMode: v }), options: [
509
480
  { label: 'All', value: 'all' },
510
481
  { label: 'Individual', value: 'individual' },
511
- ], value: openingRadiusMode }) }), openingRadiusMode === 'all' ? (_jsx(SliderControl, { label: "Corner Radius", max: maxRoundedRadius, min: 0, onChange: (v) => previewDoorUpdate('cornerRadius', v), onCommit: (v) => commitDoorPreview('cornerRadius', v), precision: 2, step: 0.05, unit: "m", value: Math.round(cornerRadius * 100) / 100 })) : (_jsx(_Fragment, { children: [
482
+ ], value: openingRadiusMode }) }), openingRadiusMode === 'all' ? (_jsx(SliderControl, { label: "Corner Radius", max: maxRoundedRadius, min: 0, onChange: (v) => previewDoorUpdate('cornerRadius', v), onCommit: (v) => commitDoorPreview('cornerRadius', v), onCancel: () => preview?.cancel(), previewWhileTyping: true, restoreOnCommit: false, precision: 2, step: 0.05, unit: "m", value: cornerRadius })) : (_jsx(_Fragment, { children: [
512
483
  ['Top Left', 0],
513
484
  ['Top Right', 1],
514
- ].map(([label, index]) => (_jsx(SliderControl, { label: label, max: maxRoundedRadius, min: 0, onChange: (v) => setOpeningTopRadius(index, v), onCommit: (v) => setOpeningTopRadius(index, v, true), precision: 2, step: 0.05, unit: "m", value: Math.round((openingTopRadii[index] ?? 0) * 100) / 100 }, label))) })), _jsx(SliderControl, { label: "Reveal Radius", max: 0.08, min: 0, onChange: (v) => previewDoorUpdate('openingRevealRadius', v), onCommit: (v) => commitDoorPreview('openingRevealRadius', v), precision: 3, step: 0.005, unit: "m", value: Math.round(openingRevealRadius * 1000) / 1000 })] })), doorShape === 'arch' && (_jsx(SliderControl, { label: "Arch Height", max: node.height, min: 0.05, onChange: (v) => handleUpdate({ archHeight: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(archHeight * 100) / 100 }))] })), showOpeningShapeSection && (_jsxs(PanelSection, { title: "Opening Shape", children: [_jsx("div", { className: "flex flex-col gap-2 px-1 pb-1", children: _jsx(SegmentedControl, { onChange: (v) => handleUpdate({
485
+ ].map(([label, index]) => (_jsx(SliderControl, { label: label, max: maxRoundedRadius, min: 0, onChange: (v) => setOpeningTopRadius(index, v), onCommit: (v) => setOpeningTopRadius(index, v, true), onCancel: () => preview?.cancel(), previewWhileTyping: true, restoreOnCommit: false, precision: 2, step: 0.05, unit: "m", value: openingTopRadii[index] ?? 0 }, label))) })), _jsx(SliderControl, { label: "Reveal Radius", max: 0.08, min: 0, onChange: (v) => previewDoorUpdate('openingRevealRadius', v), onCommit: (v) => commitDoorPreview('openingRevealRadius', v), onCancel: () => preview?.cancel(), previewWhileTyping: true, restoreOnCommit: false, precision: 3, step: 0.005, unit: "m", value: openingRevealRadius })] })), doorShape === 'arch' && (_jsx(SliderControl, { label: "Arch Height", max: node.height, min: 0.05, onChange: (v) => previewDoorUpdate('archHeight', v), onCommit: (v) => commitDoorPreview('archHeight', v), onCancel: () => preview?.cancel(), previewWhileTyping: true, precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: archHeight }))] })), showOpeningShapeSection && (_jsxs(PanelSection, { title: "Opening Shape", children: [_jsx("div", { className: "flex flex-col gap-2 px-1 pb-1", children: _jsx(SegmentedControl, { onChange: (v) => handleUpdate({
515
486
  openingShape: v,
516
487
  ...(v === 'rounded'
517
488
  ? { openingRadiusMode, openingTopRadii, cornerRadius, openingRevealRadius }
@@ -524,19 +495,19 @@ export default function DoorPanel() {
524
495
  ], value: openingShape }) }), openingShape === 'rounded' && (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex flex-col gap-2 px-1 pb-1", children: _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ openingRadiusMode: v }), options: [
525
496
  { label: 'All', value: 'all' },
526
497
  { label: 'Individual', value: 'individual' },
527
- ], value: openingRadiusMode }) }), openingRadiusMode === 'all' ? (_jsx(SliderControl, { label: "Corner Radius", max: maxRoundedRadius, min: 0, onChange: (v) => previewDoorUpdate('cornerRadius', v), onCommit: (v) => commitDoorPreview('cornerRadius', v), precision: 2, step: 0.05, unit: "m", value: Math.round(cornerRadius * 100) / 100 })) : (_jsx(_Fragment, { children: [
498
+ ], value: openingRadiusMode }) }), openingRadiusMode === 'all' ? (_jsx(SliderControl, { label: "Corner Radius", max: maxRoundedRadius, min: 0, onChange: (v) => previewDoorUpdate('cornerRadius', v), onCommit: (v) => commitDoorPreview('cornerRadius', v), onCancel: () => preview?.cancel(), previewWhileTyping: true, restoreOnCommit: false, precision: 2, step: 0.05, unit: "m", value: cornerRadius })) : (_jsx(_Fragment, { children: [
528
499
  ['Top Left', 0],
529
500
  ['Top Right', 1],
530
- ].map(([label, index]) => (_jsx(SliderControl, { label: label, max: maxRoundedRadius, min: 0, onChange: (v) => setOpeningTopRadius(index, v), onCommit: (v) => setOpeningTopRadius(index, v, true), precision: 2, step: 0.05, unit: "m", value: Math.round((openingTopRadii[index] ?? 0) * 100) / 100 }, label))) })), _jsx(SliderControl, { label: "Reveal Radius", max: 0.08, min: 0, onChange: (v) => previewDoorUpdate('openingRevealRadius', v), onCommit: (v) => commitDoorPreview('openingRevealRadius', v), precision: 3, step: 0.005, unit: "m", value: Math.round(openingRevealRadius * 1000) / 1000 })] })), openingShape === 'arch' && (_jsx(SliderControl, { label: "Arch Height", max: node.height, min: 0.05, onChange: (v) => handleUpdate({ archHeight: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(archHeight * 100) / 100 }))] })), !isCutoutOnly && (_jsxs(_Fragment, { children: [showFrameSection && (_jsxs(PanelSection, { title: "Frame", children: [_jsx(SliderControl, { label: "Thickness", max: 0.2, min: 0.01, onChange: (v) => handleUpdate({ frameThickness: v }), precision: 3, step: 0.01, unit: "m", value: Math.round(node.frameThickness * 1000) / 1000 }), _jsx(SliderControl, { label: "Depth", max: 0.3, min: 0.01, onChange: (v) => handleUpdate({ frameDepth: v }), precision: 3, step: 0.01, unit: "m", value: Math.round(node.frameDepth * 1000) / 1000 })] })), showContentPaddingSection && (_jsxs(PanelSection, { title: "Content Padding", children: [_jsx(SliderControl, { label: "Horizontal", max: 0.2, min: 0, onChange: (v) => handleUpdate({ contentPadding: [v, node.contentPadding[1]] }), precision: 3, step: 0.005, unit: "m", value: Math.round(node.contentPadding[0] * 1000) / 1000 }), _jsx(SliderControl, { label: "Vertical", max: 0.2, min: 0, onChange: (v) => handleUpdate({ contentPadding: [node.contentPadding[0], v] }), precision: 3, step: 0.005, unit: "m", value: Math.round(node.contentPadding[1] * 1000) / 1000 })] })), showSwingSection && (_jsx(PanelSection, { title: "Swing", children: _jsxs("div", { className: "flex flex-col gap-2 px-1 pb-1", children: [supportsHingeSide && (_jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Hinges Side" }), _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ hingesSide: v }), options: [
501
+ ].map(([label, index]) => (_jsx(SliderControl, { label: label, max: maxRoundedRadius, min: 0, onChange: (v) => setOpeningTopRadius(index, v), onCommit: (v) => setOpeningTopRadius(index, v, true), onCancel: () => preview?.cancel(), previewWhileTyping: true, restoreOnCommit: false, precision: 2, step: 0.05, unit: "m", value: openingTopRadii[index] ?? 0 }, label))) })), _jsx(SliderControl, { label: "Reveal Radius", max: 0.08, min: 0, onChange: (v) => previewDoorUpdate('openingRevealRadius', v), onCommit: (v) => commitDoorPreview('openingRevealRadius', v), onCancel: () => preview?.cancel(), previewWhileTyping: true, restoreOnCommit: false, precision: 3, step: 0.005, unit: "m", value: openingRevealRadius })] })), openingShape === 'arch' && (_jsx(SliderControl, { label: "Arch Height", max: node.height, min: 0.05, onChange: (v) => previewDoorUpdate('archHeight', v), onCommit: (v) => commitDoorPreview('archHeight', v), onCancel: () => preview?.cancel(), previewWhileTyping: true, precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: archHeight }))] })), !isCutoutOnly && (_jsxs(_Fragment, { children: [showFrameSection && (_jsxs(PanelSection, { title: "Frame", children: [_jsx(SliderControl, { label: "Thickness", max: 0.2, min: 0.01, onChange: (v) => handleUpdate({ frameThickness: v }), precision: 3, step: 0.01, unit: "m", value: node.frameThickness }), _jsx(SliderControl, { label: "Depth", max: 0.3, min: 0.01, onChange: (v) => handleUpdate({ frameDepth: v }), precision: 3, step: 0.01, unit: "m", value: node.frameDepth })] })), showContentPaddingSection && (_jsxs(PanelSection, { title: "Content Padding", children: [_jsx(SliderControl, { label: "Horizontal", max: 0.2, min: 0, onChange: (v) => handleUpdate({ contentPadding: [v, node.contentPadding[1]] }), precision: 3, step: 0.005, unit: "m", value: node.contentPadding[0] }), _jsx(SliderControl, { label: "Vertical", max: 0.2, min: 0, onChange: (v) => handleUpdate({ contentPadding: [node.contentPadding[0], v] }), precision: 3, step: 0.005, unit: "m", value: node.contentPadding[1] })] })), showSwingSection && (_jsx(PanelSection, { title: "Swing", children: _jsxs("div", { className: "flex flex-col gap-2 px-1 pb-1", children: [supportsHingeSide && (_jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Hinges Side" }), _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ hingesSide: v }), options: [
531
502
  { label: 'Left', value: 'left' },
532
503
  { label: 'Right', value: 'right' },
533
504
  ], value: node.hingesSide })] })), _jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Direction" }), _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ swingDirection: v }), options: [
534
505
  { label: 'Inward', value: 'inward' },
535
506
  { label: 'Outward', value: 'outward' },
536
- ], value: node.swingDirection })] })] }) })), showThresholdSection && (_jsxs(PanelSection, { title: "Threshold", children: [_jsx(ToggleControl, { checked: node.threshold, label: "Enable Threshold", onChange: (checked) => handleUpdate({ threshold: checked }) }), node.threshold && (_jsx("div", { className: "mt-1 flex flex-col gap-1", children: _jsx(SliderControl, { label: "Height", max: 0.1, min: 0.005, onChange: (v) => handleUpdate({ thresholdHeight: v }), precision: 3, step: 0.005, unit: "m", value: Math.round(node.thresholdHeight * 1000) / 1000 }) }))] })), showHandleSection && (_jsxs(PanelSection, { title: "Handle", children: [isSwingDoor && (_jsx(ToggleControl, { checked: node.handle, label: "Enable Handle", onChange: (checked) => handleUpdate({ handle: checked }) })), (node.handle || !isSwingDoor) && (_jsxs("div", { className: "mt-1 flex flex-col gap-1", children: [_jsx(SliderControl, { label: "Height", max: node.height - 0.1, min: 0.5, onChange: (v) => handleUpdate({ handleHeight: v }), precision: 2, step: 0.05, unit: "m", value: Math.round(node.handleHeight * 100) / 100 }), supportsHandleSide && (_jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Handle Side" }), _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ handleSide: v }), options: [
507
+ ], value: node.swingDirection })] })] }) })), showThresholdSection && (_jsxs(PanelSection, { title: "Threshold", children: [_jsx(ToggleControl, { checked: node.threshold, label: "Enable Threshold", onChange: (checked) => handleUpdate({ threshold: checked }) }), node.threshold && (_jsx("div", { className: "mt-1 flex flex-col gap-1", children: _jsx(SliderControl, { label: "Height", max: 0.1, min: 0.005, onChange: (v) => handleUpdate({ thresholdHeight: v }), precision: 3, step: 0.005, unit: "m", value: node.thresholdHeight }) }))] })), showHandleSection && (_jsxs(PanelSection, { title: "Handle", children: [isSwingDoor && (_jsx(ToggleControl, { checked: node.handle, label: "Enable Handle", onChange: (checked) => handleUpdate({ handle: checked }) })), (node.handle || !isSwingDoor) && (_jsxs("div", { className: "mt-1 flex flex-col gap-1", children: [_jsx(SliderControl, { label: "Height", max: node.height - 0.1, min: 0.5, onChange: (v) => handleUpdate({ handleHeight: v }), precision: 2, step: 0.05, unit: "m", value: node.handleHeight }), supportsHandleSide && (_jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Handle Side" }), _jsx(SegmentedControl, { onChange: (v) => handleUpdate({ handleSide: v }), options: [
537
508
  { label: 'Left', value: 'left' },
538
509
  { label: 'Right', value: 'right' },
539
- ], value: node.handleSide })] }))] }))] })), showHardwareSection && (_jsxs(PanelSection, { title: "Hardware", children: [_jsx(ToggleControl, { checked: node.doorCloser, label: "Door Closer", onChange: (checked) => handleUpdate({ doorCloser: checked }) }), _jsx(ToggleControl, { checked: node.panicBar, label: "Panic Bar", onChange: (checked) => handleUpdate({ panicBar: checked }) }), node.panicBar && (_jsx("div", { className: "mt-1 flex flex-col gap-1", children: _jsx(SliderControl, { label: "Bar Height", max: node.height - 0.1, min: 0.5, onChange: (v) => handleUpdate({ panicBarHeight: v }), precision: 2, step: 0.05, unit: "m", value: Math.round(node.panicBarHeight * 100) / 100 }) }))] })), showSegmentsSection && (_jsxs(PanelSection, { title: "Segments", children: [node.segments.map((seg, i) => {
510
+ ], value: node.handleSide })] }))] }))] })), showHardwareSection && (_jsxs(PanelSection, { title: "Hardware", children: [_jsx(ToggleControl, { checked: node.doorCloser, label: "Door Closer", onChange: (checked) => handleUpdate({ doorCloser: checked }) }), _jsx(ToggleControl, { checked: node.panicBar, label: "Panic Bar", onChange: (checked) => handleUpdate({ panicBar: checked }) }), node.panicBar && (_jsx("div", { className: "mt-1 flex flex-col gap-1", children: _jsx(SliderControl, { label: "Bar Height", max: node.height - 0.1, min: 0.5, onChange: (v) => handleUpdate({ panicBarHeight: v }), precision: 2, step: 0.05, unit: "m", value: node.panicBarHeight }) }))] })), showSegmentsSection && (_jsxs(PanelSection, { title: "Segments", children: [node.segments.map((seg, i) => {
540
511
  const numCols = seg.columnRatios.length;
541
512
  const colSum = seg.columnRatios.reduce((a, b) => a + b, 0);
542
513
  const normCols = seg.columnRatios.map((r) => r / colSum);
@@ -554,13 +525,13 @@ export default function DoorPanel() {
554
525
  }, precision: 0, step: 1, value: numCols }), numCols > 1 && (_jsxs("div", { className: "mt-1 border-border/50 border-t pt-1", children: [normCols.map((ratio, ci) => (_jsx(SliderControl, { label: `C${ci + 1}`, max: 95, min: 5, onChange: (v) => setSegmentColumnRatio(i, ci, v / 100), precision: 1, step: 1, unit: "%", value: Math.round(ratio * 100 * 10) / 10 }, `c-${ci}`))), _jsx(SliderControl, { label: "Divider", max: 0.1, min: 0.005, onChange: (v) => {
555
526
  const updated = node.segments.map((s, idx) => idx === i ? { ...s, dividerThickness: v } : s);
556
527
  handleUpdate({ segments: updated });
557
- }, precision: 3, step: 0.005, unit: "m", value: Math.round(seg.dividerThickness * 1000) / 1000 })] })), seg.type === 'panel' && (_jsxs("div", { className: "mt-1 border-border/50 border-t pt-1", children: [_jsx(SliderControl, { label: "Inset", max: 0.1, min: 0, onChange: (v) => {
528
+ }, precision: 3, step: 0.005, unit: "m", value: seg.dividerThickness })] })), seg.type === 'panel' && (_jsxs("div", { className: "mt-1 border-border/50 border-t pt-1", children: [_jsx(SliderControl, { label: "Inset", max: 0.1, min: 0, onChange: (v) => {
558
529
  const updated = node.segments.map((s, idx) => idx === i ? { ...s, panelInset: v } : s);
559
530
  handleUpdate({ segments: updated });
560
- }, precision: 3, step: 0.005, unit: "m", value: Math.round(seg.panelInset * 1000) / 1000 }), _jsx(SliderControl, { label: "Depth", max: 0.1, min: 0, onChange: (v) => {
531
+ }, precision: 3, step: 0.005, unit: "m", value: seg.panelInset }), _jsx(SliderControl, { label: "Depth", max: 0.1, min: 0, onChange: (v) => {
561
532
  const updated = node.segments.map((s, idx) => idx === i ? { ...s, panelDepth: v } : s);
562
533
  handleUpdate({ segments: updated });
563
- }, precision: 3, step: 0.005, unit: "m", value: Math.round(seg.panelDepth * 1000) / 1000 })] }))] }, i));
534
+ }, precision: 3, step: 0.005, unit: "m", value: seg.panelDepth })] }))] }, i));
564
535
  }), _jsxs("div", { className: "flex gap-1.5 px-1 pt-1", children: [_jsx(ActionButton, { label: "+ Add Segment", onClick: () => {
565
536
  const updated = [
566
537
  ...node.segments,
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/door/tool.tsx"],"names":[],"mappings":"AAiEA;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,QAAQ,EAAE,KAAK,CAAC,EAqsBrB,CAAA;AAED,eAAe,QAAQ,CAAA"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/door/tool.tsx"],"names":[],"mappings":"AAqEA;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,QAAQ,EAAE,KAAK,CAAC,EAwtBrB,CAAA;AAED,eAAe,QAAQ,CAAA"}
package/dist/door/tool.js CHANGED
@@ -5,6 +5,7 @@ import { useViewer } from '@pascal-app/viewer';
5
5
  import { useEffect, useMemo, useRef, useState } from 'react';
6
6
  import { BoxGeometry, EdgesGeometry, Vector3 } from 'three';
7
7
  import { LineBasicNodeMaterial } from 'three/webgpu';
8
+ import { shouldFollowOpeningGrid, shouldHandleOpeningHostLeave, } from '../shared/opening-grid-follow';
8
9
  import { clearOpeningGuides3D, publishOpeningGuidesForWallEvent, } from '../shared/opening-guides-runtime';
9
10
  import { getRoofWallOpeningCursorPose, resolveRoofWallOpeningTarget, worldToSelectedBuildingLocal, } from '../shared/roof-wall-opening-placement';
10
11
  import { collectWallOpeningAlignmentCandidates, resolveWallSlideAlignment, } from '../shared/wall-opening-alignment';
@@ -385,7 +386,17 @@ const DoorTool = () => {
385
386
  commitDoorAtWall(event.node, clampedX, clampedY, side, itemRotation);
386
387
  event.stopPropagation();
387
388
  };
388
- const onWallLeave = () => {
389
+ // XR runtimes deliver selectend as a grid click, but do not replay the
390
+ // R3F wall pointer-up event. Commit the latest valid wall hover so a
391
+ // controller/hand release places the opening on the wall it visibly hit.
392
+ const onXRGridClick = (event) => {
393
+ if (event.nativeEvent?.pointerType !== 'xr' || !lastWallEvent)
394
+ return;
395
+ onWallClick(lastWallEvent);
396
+ };
397
+ const onWallLeave = (event) => {
398
+ if (!shouldHandleOpeningHostLeave(event.nativeEvent))
399
+ return;
389
400
  if (hostKind !== 'wall')
390
401
  return;
391
402
  lastWallEvent = null;
@@ -404,7 +415,12 @@ const DoorTool = () => {
404
415
  // timeStamp) — it owns the frame and has snapped the draft, so skip the
405
416
  // floor follow this tick.
406
417
  const ts = event.nativeEvent?.timeStamp ?? -1;
407
- if (ts === lastMeshEventTime)
418
+ if (!shouldFollowOpeningGrid({
419
+ eventTime: ts,
420
+ hasActiveHost: hostKind !== null,
421
+ lastHostEventTime: lastMeshEventTime,
422
+ pointerType: event.nativeEvent?.pointerType,
423
+ }))
408
424
  return;
409
425
  // Fresh floor-only frame: the cursor is off any wall/roof. Drop any draft
410
426
  // and free-follow the cursor with the invalid (unplaceable) ghost.
@@ -575,6 +591,7 @@ const DoorTool = () => {
575
591
  emitter.on('wall:enter', onWallHover);
576
592
  emitter.on('wall:move', onWallHover);
577
593
  emitter.on('wall:click', onWallClick);
594
+ emitter.on('grid:click', onXRGridClick);
578
595
  emitter.on('wall:leave', onWallLeave);
579
596
  emitter.on('roof:enter', onRoofHover);
580
597
  emitter.on('roof:move', onRoofHover);
@@ -599,6 +616,7 @@ const DoorTool = () => {
599
616
  emitter.off('wall:enter', onWallHover);
600
617
  emitter.off('wall:move', onWallHover);
601
618
  emitter.off('wall:click', onWallClick);
619
+ emitter.off('grid:click', onXRGridClick);
602
620
  emitter.off('wall:leave', onWallLeave);
603
621
  emitter.off('roof:enter', onRoofHover);
604
622
  emitter.off('roof:move', onRoofHover);
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/dormer/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAA;AAKzB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAqPrC;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,OAAO,UAAU,CA8E9D,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/dormer/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAA;AAKzB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAqPrC;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,OAAO,UAAU,CA+E9D,CAAA"}
@@ -293,6 +293,7 @@ export const dormerDefinition = {
293
293
  parametrics: dormerParametrics,
294
294
  handles: dormerHandles,
295
295
  floorplan: buildDormerFloorplan,
296
+ rendersChildren: false,
296
297
  renderer: {
297
298
  kind: 'parametric',
298
299
  module: () => import('./renderer'),
@@ -141,12 +141,12 @@ export function DormerPositionSection({ node, segment, roof, selectedId, preview
141
141
  return;
142
142
  const local = worldToSegLocal(newWorldX, worldZ_now, segment);
143
143
  previewProp({ position: [local.localX, py ?? 0, local.localZ] });
144
- }, onCommit: (newWorldX) => commitWorldPosition(newWorldX, worldZ_now), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(worldX_now * 100) / 100 }), _jsx(SliderControl, { label: "Z", max: Math.round(worldMaxZ * 10) / 10, min: Math.round(worldMinZ * 10) / 10, onChange: (newWorldZ) => {
144
+ }, onCommit: (newWorldX) => commitWorldPosition(newWorldX, worldZ_now), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: worldX_now }), _jsx(SliderControl, { label: "Z", max: Math.round(worldMaxZ * 10) / 10, min: Math.round(worldMinZ * 10) / 10, onChange: (newWorldZ) => {
145
145
  if (!segment)
146
146
  return;
147
147
  const local = worldToSegLocal(worldX_now, newWorldZ, segment);
148
148
  previewProp({ position: [local.localX, py ?? 0, local.localZ] });
149
- }, onCommit: (newWorldZ) => commitWorldPosition(worldX_now, newWorldZ), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(worldZ_now * 100) / 100 }), _jsx(SliderControl, { label: "Rotation", max: 180, min: -180, onChange: (degrees) => {
149
+ }, onCommit: (newWorldZ) => commitWorldPosition(worldX_now, newWorldZ), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: worldZ_now }), _jsx(SliderControl, { label: "Rotation", max: 180, min: -180, onChange: (degrees) => {
150
150
  const newWorldRot = (degrees * Math.PI) / 180;
151
151
  let ancestorWorldY = 0;
152
152
  if (segment) {
@@ -202,7 +202,7 @@ export default function DormerPanel() {
202
202
  return (_jsx("button", { className: cn('flex min-h-10 items-center justify-center rounded-lg border px-2 py-2 text-center text-xs transition-colors', isSelected
203
203
  ? 'border-orange-400/60 bg-orange-400/10 text-foreground'
204
204
  : 'border-border/50 bg-[#2C2C2E] text-muted-foreground hover:bg-[#3e3e3e] hover:text-foreground'), onClick: () => setSection(option.value), type: "button", children: _jsx("span", { className: "truncate font-medium", children: option.label }) }, option.value));
205
- }) }) }), section === 'dormer' && (_jsxs(_Fragment, { children: [_jsxs(PanelSection, { title: "Dimensions", children: [_jsx(SliderControl, { label: "Width", max: 1000, min: 0.5, onChange: (v) => previewProp({ width: v }), onCommit: (v) => commitProp({ width: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(node.width * 100) / 100 }), _jsx(SliderControl, { label: "Depth", max: 1000, min: 0.5, onChange: (v) => previewProp({ depth: v }), onCommit: (v) => commitProp({ depth: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(node.depth * 100) / 100 }), _jsx(SliderControl, { label: "Wall Height", max: 1000, min: 0, onChange: (v) => previewProp({ height: v }), onCommit: (v) => commitProp({ height: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(node.height * 100) / 100 }), _jsx(SliderControl, { label: node.roofType === 'shed' ? 'Pitch Rise' : 'Roof Height', max: 3, min: 0, onChange: (v) => previewProp({ roofHeight: v }), onCommit: (v) => commitProp({ roofHeight: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: Math.round(node.roofHeight * 100) / 100 })] }), _jsx(PanelSection, { title: "Roof Type", children: _jsx("div", { className: "grid grid-cols-3 gap-1.5 px-1 pt-1", children: ROOF_TYPE_OPTIONS.map((option) => {
205
+ }) }) }), section === 'dormer' && (_jsxs(_Fragment, { children: [_jsxs(PanelSection, { title: "Dimensions", children: [_jsx(SliderControl, { label: "Width", max: 1000, min: 0.5, onChange: (v) => previewProp({ width: v }), onCommit: (v) => commitProp({ width: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: node.width }), _jsx(SliderControl, { label: "Depth", max: 1000, min: 0.5, onChange: (v) => previewProp({ depth: v }), onCommit: (v) => commitProp({ depth: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: node.depth }), _jsx(SliderControl, { label: "Wall Height", max: 1000, min: 0, onChange: (v) => previewProp({ height: v }), onCommit: (v) => commitProp({ height: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: node.height }), _jsx(SliderControl, { label: node.roofType === 'shed' ? 'Pitch Rise' : 'Roof Height', max: 3, min: 0, onChange: (v) => previewProp({ roofHeight: v }), onCommit: (v) => commitProp({ roofHeight: v }), precision: 2, restoreOnCommit: false, step: 0.05, unit: "m", value: node.roofHeight })] }), _jsx(PanelSection, { title: "Roof Type", children: _jsx("div", { className: "grid grid-cols-3 gap-1.5 px-1 pt-1", children: ROOF_TYPE_OPTIONS.map((option) => {
206
206
  const isSelected = node.roofType === option.value;
207
207
  return (_jsx("button", { className: cn('flex min-h-10 items-center justify-center rounded-lg border px-2 py-2 text-xs transition-colors', isSelected
208
208
  ? 'border-orange-400/60 bg-orange-400/10 text-foreground'
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/downspout/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AASzB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAiIxC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,EAAE,cAAc,CAAC,OAAO,aAAa,CA4DpE,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/downspout/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AASzB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAiIxC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,EAAE,cAAc,CAAC,OAAO,aAAa,CA6DpE,CAAA"}
@@ -158,6 +158,7 @@ export const downspoutDefinition = {
158
158
  },
159
159
  parametrics: downspoutParametrics,
160
160
  handles: downspoutHandles,
161
+ rendersChildren: false,
161
162
  renderer: {
162
163
  kind: 'parametric',
163
164
  module: () => import('./renderer'),
@@ -1 +1 @@
1
- {"version":3,"file":"parametrics.d.ts","sourceRoot":"","sources":["../../src/duct-segment/parametrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,oBAAoB,EAAY,MAAM,kBAAkB,CAAA;AAK5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AA0E/C,eAAO,MAAM,sBAAsB,EAAE,oBAAoB,CAAC,eAAe,CA+IxE,CAAA"}
1
+ {"version":3,"file":"parametrics.d.ts","sourceRoot":"","sources":["../../src/duct-segment/parametrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,oBAAoB,EAAY,MAAM,kBAAkB,CAAA;AAM5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AA0E/C,eAAO,MAAM,sBAAsB,EAAE,oBAAoB,CAAC,eAAe,CAiJxE,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { useScene } from '@pascal-app/core';
2
2
  import { Vector3 } from 'three';
3
3
  import { getDuctFittingPorts } from '../duct-fitting/ports';
4
+ import { findAutomaticRunEndCapIds } from '../shared/automatic-run-end-cap';
4
5
  import { fittingDeletionPlansForRun } from '../shared/fitting-deletion-cleanup';
5
6
  import { rollToContinueAcrossElbow } from './geometry';
6
7
  /** A run endpoint sitting this close to a collar counts as mated. */
@@ -95,7 +96,10 @@ export const ductSegmentParametrics = {
95
96
  return out;
96
97
  },
97
98
  onDelete: (duct, nodes, _pendingDeleteIds, requestedDeleteIds) => fittingDeletionPlansForRun(duct, nodes, requestedDeleteIds, true).flatMap((plan) => plan.updates),
98
- onDeleteCascade: (duct, nodes, _pendingDeleteIds, requestedDeleteIds) => fittingDeletionPlansForRun(duct, nodes, requestedDeleteIds, false).flatMap((plan) => plan.deleteFitting ? [plan.fittingId, ...plan.cascadeDeleteIds] : []),
99
+ onDeleteCascade: (duct, nodes, _pendingDeleteIds, requestedDeleteIds) => [
100
+ ...findAutomaticRunEndCapIds(duct.id, nodes, 'duct-fitting'),
101
+ ...fittingDeletionPlansForRun(duct, nodes, requestedDeleteIds, false).flatMap((plan) => plan.deleteFitting ? [plan.fittingId, ...plan.cascadeDeleteIds] : []),
102
+ ],
99
103
  trailingSection: () => import('../shared/run-hanger-inspector'),
100
104
  groups: [
101
105
  {
@@ -8,6 +8,7 @@ type DraftProfile = {
8
8
  width: number;
9
9
  height: number;
10
10
  };
11
+ export declare function ductSurfaceClearanceM(profile: DraftProfile, wall?: boolean): number;
11
12
  /** The full set of nodes a drawn segment produces. The drawn `ducts`
12
13
  * (and any trunk `tails` from a tee / cross split) are previewed by the
13
14
  * duct ghost already; `fittings` are the auto-inserted elbow / tee /
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/duct-segment/tool.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAuBtF,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AAYpG,OAAO,EAKL,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAA;AA0IxB,qEAAqE;AACrE,KAAK,YAAY,GAAG;IAClB,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAgDD;;;;;6BAK6B;AAC7B,KAAK,YAAY,GAAG;IAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB,OAAO,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,EAAE,CAAA;IACxD,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAA;CACxB,CAAA;AA4CD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC/B,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC7B,SAAS,EAAE,SAAS,GAAG,IAAI,EAC3B,SAAS,EAAE,UAAU,GAAG,IAAI,EAC5B,OAAO,EAAE,SAAS,GAAG,IAAI,EACzB,OAAO,EAAE,UAAU,GAAG,IAAI,EAC1B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,EACjC,WAAW,UAAQ,EACnB,YAAY,GAAE,OAAO,CAAC,eAAe,CAAM,EAC3C,WAAW,GAAE,QAAQ,GAAG,QAAmB,GAC1C,YAAY,GAAG,IAAI,CAsLrB;AAED,QAAA,MAAM,eAAe,0CA+QpB,CAAA;AAED,eAAe,eAAe,CAAA"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/duct-segment/tool.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAuBtF,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AAYpG,OAAO,EAKL,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAA;AA2IxB,qEAAqE;AACrE,KAAK,YAAY,GAAG;IAClB,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,UAAQ,GAAG,MAAM,CAUjF;AAgDD;;;;;6BAK6B;AAC7B,KAAK,YAAY,GAAG;IAClB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB,OAAO,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,EAAE,CAAA;IACxD,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAA;CACxB,CAAA;AA4CD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC/B,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC7B,SAAS,EAAE,SAAS,GAAG,IAAI,EAC3B,SAAS,EAAE,UAAU,GAAG,IAAI,EAC5B,OAAO,EAAE,SAAS,GAAG,IAAI,EACzB,OAAO,EAAE,UAAU,GAAG,IAAI,EAC1B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,EACjC,WAAW,UAAQ,EACnB,YAAY,GAAE,OAAO,CAAC,eAAe,CAAM,EAC3C,WAAW,GAAE,QAAQ,GAAG,QAAmB,GAC1C,YAAY,GAAG,IAAI,CAsLrB;AAED,QAAA,MAAM,eAAe,0CAoRpB,CAAA;AAED,eAAe,eAAe,CAAA"}
@@ -56,6 +56,7 @@ import { rectSectionAxes, rollToContinueAcrossElbow } from './geometry';
56
56
  */
57
57
  const DUCT_DIAMETERS_IN = [4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20];
58
58
  const BODY_SNAP_RADIUS_M = 0.35;
59
+ const DUCT_WALL_STANDOFF_M = 0.01;
59
60
  /** Angle step (radians) for the XZ angle lock — 45°. */
60
61
  /**
61
62
  * Cross-section roll for a new rect run leaving `port` along `newDir`,
@@ -134,6 +135,13 @@ function getConnectionPorts(levelId, nodes) {
134
135
  levelId: levelId ?? undefined,
135
136
  }).filter((port) => !isRunEndCapPort(port, nodes));
136
137
  }
138
+ export function ductSurfaceClearanceM(profile, wall = false) {
139
+ return (runSectionHalfSizeM(profile.shape === 'round'
140
+ ? profile.diameter
141
+ : wall
142
+ ? Math.max(profile.width, profile.height)
143
+ : profile.height) + (wall && profile.shape !== 'round' ? DUCT_WALL_STANDOFF_M : 0));
144
+ }
137
145
  /**
138
146
  * Profile to inherit when the segment start snaps onto `port` — joining
139
147
  * means continuing that thing: a rect trunk end keeps its W×H, a round
@@ -382,6 +390,7 @@ export function planDuctDraw(start, end, startPort, startBody, endPort, endBody,
382
390
  }
383
391
  const DuctSegmentTool = () => {
384
392
  const { activeLevelId, sceneApi, unit } = useRegistryToolContext();
393
+ const toolDefaults = useEditor((s) => s.toolDefaults['duct-segment']);
385
394
  const cursorRef = useRef(null);
386
395
  const continuationSeedRef = useRef(currentDuctContinuationSeed());
387
396
  const continuationSeed = continuationSeedRef.current;
@@ -425,11 +434,7 @@ const DuctSegmentTool = () => {
425
434
  : null,
426
435
  getPorts: () => getConnectionPorts(activeLevelId, sceneApi.nodes()),
427
436
  findBody: (point) => findNearestRunBody3D(point, BODY_SNAP_RADIUS_M, { levelId: activeLevelId ?? undefined }),
428
- surfaceClearance: (surface) => surface
429
- ? runSectionHalfSizeM(profileRef.current.shape === 'round'
430
- ? profileRef.current.diameter
431
- : profileRef.current.height)
432
- : 0,
437
+ surfaceClearance: (surface) => surface ? ductSurfaceClearanceM(profileRef.current, surface.kind === 'wall') : 0,
433
438
  minimumSegmentLength: 0.08,
434
439
  inheritFromConnection: ({ port }) => {
435
440
  if (!port)
@@ -447,9 +452,7 @@ const DuctSegmentTool = () => {
447
452
  return null;
448
453
  const attachDuct = (node) => {
449
454
  const wallAttachment = surfaceTarget?.kind === 'wall'
450
- ? createRunWallAttachment(surfaceTarget.hostId, surfaceTarget.side, node.path[0], node.path.at(-1), surfaceTarget, profileRef.current.shape === 'round'
451
- ? runSectionHalfSizeM(profileRef.current.diameter)
452
- : runSectionHalfSizeM(profileRef.current.height))
455
+ ? createRunWallAttachment(surfaceTarget.hostId, surfaceTarget.side, node.path[0], node.path.at(-1), surfaceTarget, ductSurfaceClearanceM(profileRef.current, true))
453
456
  : undefined;
454
457
  return { ...node, wallAttachment };
455
458
  };
@@ -521,6 +524,16 @@ const DuctSegmentTool = () => {
521
524
  }
522
525
  },
523
526
  });
527
+ useEffect(() => {
528
+ if (!toolDefaults)
529
+ return;
530
+ setProfile((current) => ({
531
+ shape: toolDefaults.shape ?? current.shape,
532
+ diameter: toolDefaults.diameter ?? current.diameter,
533
+ width: toolDefaults.width ?? current.width,
534
+ height: toolDefaults.height ?? current.height,
535
+ }));
536
+ }, [toolDefaults]);
524
537
  const previewPlan = useMemo(() => {
525
538
  if (!(activeLevelId && run.start && run.cursor))
526
539
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/duct-terminal/tool.tsx"],"names":[],"mappings":"AAgJA;;;;;;;;;GASG;AACH,QAAA,MAAM,gBAAgB,0CAgUrB,CAAA;AAED,eAAe,gBAAgB,CAAA"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/duct-terminal/tool.tsx"],"names":[],"mappings":"AAmJA;;;;;;;;;GASG;AACH,QAAA,MAAM,gBAAgB,0CAyYrB,CAAA;AAED,eAAe,gBAAgB,CAAA"}