@pascal-app/nodes 1.0.3 → 1.0.4

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 (708) hide show
  1. package/dist/block/definition.d.ts.map +1 -1
  2. package/dist/block/definition.js +18 -3
  3. package/dist/block/floorplan.d.ts +11 -1
  4. package/dist/block/floorplan.d.ts.map +1 -1
  5. package/dist/block/floorplan.js +81 -13
  6. package/dist/block/geometry.d.ts +2 -2
  7. package/dist/block/geometry.d.ts.map +1 -1
  8. package/dist/block/geometry.js +39 -24
  9. package/dist/block/hosted-edit.d.ts.map +1 -1
  10. package/dist/block/hosted-edit.js +25 -0
  11. package/dist/block/material-slots.d.ts +1 -3
  12. package/dist/block/material-slots.d.ts.map +1 -1
  13. package/dist/block/material-slots.js +1 -11
  14. package/dist/block/paint.d.ts.map +1 -1
  15. package/dist/block/paint.js +4 -10
  16. package/dist/block/panel.d.ts.map +1 -1
  17. package/dist/block/panel.js +8 -3
  18. package/dist/block/selection.d.ts.map +1 -1
  19. package/dist/block/selection.js +2 -2
  20. package/dist/box-vent/definition.d.ts.map +1 -1
  21. package/dist/box-vent/definition.js +2 -5
  22. package/dist/box-vent/panel.d.ts.map +1 -1
  23. package/dist/box-vent/panel.js +5 -28
  24. package/dist/cabinet/countertop-layout.d.ts +13 -2
  25. package/dist/cabinet/countertop-layout.d.ts.map +1 -1
  26. package/dist/cabinet/definition.d.ts.map +1 -1
  27. package/dist/cabinet/definition.js +1 -2
  28. package/dist/cabinet/floorplan.d.ts.map +1 -1
  29. package/dist/cabinet/floorplan.js +125 -28
  30. package/dist/cabinet/geometry/shared.d.ts +1 -2
  31. package/dist/cabinet/geometry/shared.d.ts.map +1 -1
  32. package/dist/cabinet/geometry/shared.js +1 -2
  33. package/dist/cabinet/run-layout.d.ts.map +1 -1
  34. package/dist/cabinet/run-layout.js +8 -5
  35. package/dist/cabinet/run-ops.d.ts +13 -2
  36. package/dist/cabinet/run-ops.d.ts.map +1 -1
  37. package/dist/cabinet/tool.d.ts.map +1 -1
  38. package/dist/cabinet/tool.js +22 -24
  39. package/dist/cabinet/wall-snap.d.ts.map +1 -1
  40. package/dist/cabinet/wall-snap.js +8 -8
  41. package/dist/ceiling/boundary-editor.d.ts.map +1 -1
  42. package/dist/ceiling/boundary-editor.js +5 -3
  43. package/dist/ceiling/definition.d.ts +4 -4
  44. package/dist/ceiling/definition.d.ts.map +1 -1
  45. package/dist/ceiling/definition.js +15 -15
  46. package/dist/ceiling/floorplan-affordances.d.ts +84 -4
  47. package/dist/ceiling/floorplan-affordances.d.ts.map +1 -1
  48. package/dist/ceiling/floorplan-affordances.js +0 -1
  49. package/dist/ceiling/floorplan-move.d.ts.map +1 -1
  50. package/dist/ceiling/floorplan-move.js +0 -1
  51. package/dist/ceiling/floorplan.d.ts +1 -1
  52. package/dist/ceiling/floorplan.d.ts.map +1 -1
  53. package/dist/ceiling/floorplan.js +2 -1
  54. package/dist/ceiling/height-bounds.d.ts +19 -0
  55. package/dist/ceiling/height-bounds.d.ts.map +1 -0
  56. package/dist/ceiling/height-bounds.js +24 -0
  57. package/dist/ceiling/hole-editor.d.ts.map +1 -1
  58. package/dist/ceiling/hole-editor.js +3 -4
  59. package/dist/ceiling/materials.d.ts +1 -1
  60. package/dist/ceiling/materials.d.ts.map +1 -1
  61. package/dist/ceiling/materials.js +10 -3
  62. package/dist/ceiling/paint.d.ts +2 -8
  63. package/dist/ceiling/paint.d.ts.map +1 -1
  64. package/dist/ceiling/paint.js +121 -24
  65. package/dist/ceiling/panel.d.ts.map +1 -1
  66. package/dist/ceiling/panel.js +40 -23
  67. package/dist/ceiling/renderer.d.ts.map +1 -1
  68. package/dist/ceiling/renderer.js +41 -5
  69. package/dist/chimney/move-tool.d.ts.map +1 -1
  70. package/dist/chimney/move-tool.js +8 -2
  71. package/dist/chimney/paint.d.ts.map +1 -1
  72. package/dist/chimney/paint.js +3 -10
  73. package/dist/column/definition.d.ts.map +1 -1
  74. package/dist/column/definition.js +24 -2
  75. package/dist/column/floorplan-affordances.d.ts.map +1 -1
  76. package/dist/column/floorplan-affordances.js +5 -0
  77. package/dist/column/floorplan.d.ts +1 -1
  78. package/dist/column/floorplan.d.ts.map +1 -1
  79. package/dist/column/floorplan.js +47 -2
  80. package/dist/column/hosted-resize.d.ts +101 -2
  81. package/dist/column/hosted-resize.d.ts.map +1 -1
  82. package/dist/column/panel.d.ts.map +1 -1
  83. package/dist/column/panel.js +147 -146
  84. package/dist/column/renderer.d.ts.map +1 -1
  85. package/dist/column/renderer.js +6 -4
  86. package/dist/column/surface.d.ts.map +1 -1
  87. package/dist/column/surface.js +7 -0
  88. package/dist/construction-dimension/floorplan.js +7 -3
  89. package/dist/cupola/definition.d.ts.map +1 -1
  90. package/dist/cupola/definition.js +2 -8
  91. package/dist/cupola/panel.d.ts.map +1 -1
  92. package/dist/cupola/panel.js +5 -23
  93. package/dist/door/definition.d.ts.map +1 -1
  94. package/dist/door/definition.js +12 -3
  95. package/dist/door/door-math.d.ts +5 -10
  96. package/dist/door/door-math.d.ts.map +1 -1
  97. package/dist/door/door-math.js +14 -15
  98. package/dist/door/floorplan-move.d.ts.map +1 -1
  99. package/dist/door/floorplan-move.js +3 -1
  100. package/dist/door/floorplan.d.ts.map +1 -1
  101. package/dist/door/floorplan.js +55 -21
  102. package/dist/door/mechanism.d.ts +4 -0
  103. package/dist/door/mechanism.d.ts.map +1 -0
  104. package/dist/door/mechanism.js +24 -0
  105. package/dist/door/move-tool.d.ts.map +1 -1
  106. package/dist/door/move-tool.js +72 -48
  107. package/dist/door/paint.d.ts.map +1 -1
  108. package/dist/door/paint.js +3 -0
  109. package/dist/door/panel.d.ts.map +1 -1
  110. package/dist/door/panel.js +34 -259
  111. package/dist/door/placement.d.ts +45 -0
  112. package/dist/door/placement.d.ts.map +1 -0
  113. package/dist/door/placement.js +51 -0
  114. package/dist/door/preview.d.ts.map +1 -1
  115. package/dist/door/preview.js +2 -0
  116. package/dist/door/renderer.d.ts.map +1 -1
  117. package/dist/door/renderer.js +3 -2
  118. package/dist/door/tool.d.ts.map +1 -1
  119. package/dist/door/tool.js +92 -21
  120. package/dist/dormer/csg-geometry.d.ts.map +1 -1
  121. package/dist/dormer/csg-geometry.js +2 -0
  122. package/dist/dormer/paint.d.ts.map +1 -1
  123. package/dist/dormer/paint.js +2 -5
  124. package/dist/dormer/panel.d.ts.map +1 -1
  125. package/dist/dormer/panel.js +4 -14
  126. package/dist/downspout/definition.d.ts.map +1 -1
  127. package/dist/downspout/definition.js +2 -2
  128. package/dist/duct-fitting/definition.d.ts.map +1 -1
  129. package/dist/duct-fitting/definition.js +2 -1
  130. package/dist/duct-fitting/geometry.d.ts.map +1 -1
  131. package/dist/duct-fitting/geometry.js +1 -1
  132. package/dist/duct-fitting/move-tool.d.ts.map +1 -1
  133. package/dist/duct-fitting/move-tool.js +8 -2
  134. package/dist/duct-segment/definition.d.ts.map +1 -1
  135. package/dist/duct-segment/definition.js +2 -2
  136. package/dist/duct-segment/geometry.d.ts.map +1 -1
  137. package/dist/duct-segment/geometry.js +1 -1
  138. package/dist/duct-segment/move-tool.d.ts.map +1 -1
  139. package/dist/duct-segment/move-tool.js +8 -2
  140. package/dist/elevator/definition.d.ts.map +1 -1
  141. package/dist/elevator/definition.js +1 -2
  142. package/dist/elevator/panel.d.ts.map +1 -1
  143. package/dist/elevator/panel.js +5 -27
  144. package/dist/elevator/parametrics.js +1 -1
  145. package/dist/elevator/renderer.d.ts.map +1 -1
  146. package/dist/elevator/renderer.js +3 -4
  147. package/dist/eyebrow-vent/definition.d.ts.map +1 -1
  148. package/dist/eyebrow-vent/definition.js +2 -5
  149. package/dist/eyebrow-vent/paint.d.ts.map +1 -1
  150. package/dist/eyebrow-vent/paint.js +3 -6
  151. package/dist/eyebrow-vent/panel.d.ts.map +1 -1
  152. package/dist/eyebrow-vent/panel.js +5 -23
  153. package/dist/fence/actions/move-endpoint.d.ts +1 -1
  154. package/dist/fence/actions/move-endpoint.d.ts.map +1 -1
  155. package/dist/fence/actions/move-endpoint.js +2 -1
  156. package/dist/fence/definition.d.ts +2 -1
  157. package/dist/fence/definition.d.ts.map +1 -1
  158. package/dist/fence/definition.js +83 -28
  159. package/dist/fence/drafting.d.ts +36 -0
  160. package/dist/fence/drafting.d.ts.map +1 -0
  161. package/dist/fence/drafting.js +210 -0
  162. package/dist/fence/feature-tool.d.ts +4 -0
  163. package/dist/fence/feature-tool.d.ts.map +1 -0
  164. package/dist/fence/feature-tool.js +140 -0
  165. package/dist/fence/features.d.ts +2 -0
  166. package/dist/fence/features.d.ts.map +1 -0
  167. package/dist/fence/features.js +12 -0
  168. package/dist/fence/floorplan-affordances.d.ts.map +1 -1
  169. package/dist/fence/floorplan-affordances.js +2 -1
  170. package/dist/fence/floorplan.d.ts +2 -3
  171. package/dist/fence/floorplan.d.ts.map +1 -1
  172. package/dist/fence/floorplan.js +131 -71
  173. package/dist/fence/geometry-parts.d.ts +50 -0
  174. package/dist/fence/geometry-parts.d.ts.map +1 -0
  175. package/dist/fence/geometry-parts.js +1144 -0
  176. package/dist/fence/geometry.d.ts +2 -3
  177. package/dist/fence/geometry.d.ts.map +1 -1
  178. package/dist/fence/geometry.js +84 -7
  179. package/dist/fence/index.d.ts +1 -0
  180. package/dist/fence/index.d.ts.map +1 -1
  181. package/dist/fence/index.js +1 -0
  182. package/dist/fence/inspector-editors.d.ts +15 -0
  183. package/dist/fence/inspector-editors.d.ts.map +1 -1
  184. package/dist/fence/inspector-editors.js +128 -3
  185. package/dist/fence/lift.d.ts.map +1 -1
  186. package/dist/fence/lift.js +5 -2
  187. package/dist/fence/move-tool.js +2 -1
  188. package/dist/fence/panel-model.d.ts +4 -0
  189. package/dist/fence/panel-model.d.ts.map +1 -0
  190. package/dist/fence/panel-model.js +12 -0
  191. package/dist/fence/parametrics.d.ts +0 -12
  192. package/dist/fence/parametrics.d.ts.map +1 -1
  193. package/dist/fence/parametrics.js +185 -18
  194. package/dist/fence/system.d.ts.map +1 -1
  195. package/dist/fence/system.js +141 -31
  196. package/dist/fence/tool.d.ts.map +1 -1
  197. package/dist/fence/tool.js +256 -47
  198. package/dist/fence-feature/actions.d.ts +7 -0
  199. package/dist/fence-feature/actions.d.ts.map +1 -0
  200. package/dist/fence-feature/actions.js +26 -0
  201. package/dist/fence-feature/definition.d.ts +4 -0
  202. package/dist/fence-feature/definition.d.ts.map +1 -0
  203. package/dist/fence-feature/definition.js +112 -0
  204. package/dist/fence-feature/floorplan-affordances.d.ts +3 -0
  205. package/dist/fence-feature/floorplan-affordances.d.ts.map +1 -0
  206. package/dist/fence-feature/floorplan-affordances.js +37 -0
  207. package/dist/fence-feature/floorplan.d.ts +4 -0
  208. package/dist/fence-feature/floorplan.d.ts.map +1 -0
  209. package/dist/fence-feature/floorplan.js +120 -0
  210. package/dist/fence-feature/geometry.d.ts +5 -0
  211. package/dist/fence-feature/geometry.d.ts.map +1 -0
  212. package/dist/fence-feature/geometry.js +43 -0
  213. package/dist/fence-feature/handles.d.ts +5 -0
  214. package/dist/fence-feature/handles.d.ts.map +1 -0
  215. package/dist/fence-feature/handles.js +84 -0
  216. package/dist/fence-feature/inspector.d.ts +6 -0
  217. package/dist/fence-feature/inspector.d.ts.map +1 -0
  218. package/dist/fence-feature/inspector.js +214 -0
  219. package/dist/fence-feature/interaction.d.ts +3 -0
  220. package/dist/fence-feature/interaction.d.ts.map +1 -0
  221. package/dist/fence-feature/interaction.js +41 -0
  222. package/dist/fence-feature/move-session.d.ts +9 -0
  223. package/dist/fence-feature/move-session.d.ts.map +1 -0
  224. package/dist/fence-feature/move-session.js +48 -0
  225. package/dist/fence-feature/move-tool.d.ts +5 -0
  226. package/dist/fence-feature/move-tool.d.ts.map +1 -0
  227. package/dist/fence-feature/move-tool.js +58 -0
  228. package/dist/fence-feature/pick-target.d.ts +7 -0
  229. package/dist/fence-feature/pick-target.d.ts.map +1 -0
  230. package/dist/fence-feature/pick-target.js +52 -0
  231. package/dist/floor-opening/definition.d.ts +3 -0
  232. package/dist/floor-opening/definition.d.ts.map +1 -0
  233. package/dist/floor-opening/definition.js +41 -0
  234. package/dist/floor-opening/floorplan.d.ts +156 -0
  235. package/dist/floor-opening/floorplan.d.ts.map +1 -0
  236. package/dist/floor-opening/floorplan.js +63 -0
  237. package/dist/gutter/definition.d.ts.map +1 -1
  238. package/dist/gutter/definition.js +2 -2
  239. package/dist/imported-mesh/definition.d.ts.map +1 -1
  240. package/dist/imported-mesh/definition.js +2 -0
  241. package/dist/imported-mesh/geometry.d.ts +1 -1
  242. package/dist/imported-mesh/geometry.d.ts.map +1 -1
  243. package/dist/imported-mesh/geometry.js +40 -12
  244. package/dist/index.d.ts +13 -2
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js +20 -2
  247. package/dist/item/authored-face-host.d.ts +10 -0
  248. package/dist/item/authored-face-host.d.ts.map +1 -0
  249. package/dist/item/authored-face-host.js +87 -0
  250. package/dist/item/authored-params.d.ts +15 -0
  251. package/dist/item/authored-params.d.ts.map +1 -0
  252. package/dist/item/authored-params.js +72 -0
  253. package/dist/item/definition.d.ts +2 -2
  254. package/dist/item/definition.d.ts.map +1 -1
  255. package/dist/item/definition.js +33 -4
  256. package/dist/item/floorplan.d.ts +1 -1
  257. package/dist/item/floorplan.d.ts.map +1 -1
  258. package/dist/item/floorplan.js +35 -5
  259. package/dist/item/index.d.ts +1 -0
  260. package/dist/item/index.d.ts.map +1 -1
  261. package/dist/item/index.js +1 -0
  262. package/dist/item/model-loader.d.ts +2 -0
  263. package/dist/item/model-loader.d.ts.map +1 -1
  264. package/dist/item/model-loader.js +55 -23
  265. package/dist/item/move-tool.js +4 -4
  266. package/dist/item/paint.d.ts.map +1 -1
  267. package/dist/item/paint.js +5 -24
  268. package/dist/item/panel.d.ts +1 -3
  269. package/dist/item/panel.d.ts.map +1 -1
  270. package/dist/item/panel.js +20 -24
  271. package/dist/item/placement-notice.d.ts +14 -0
  272. package/dist/item/placement-notice.d.ts.map +1 -0
  273. package/dist/item/placement-notice.js +38 -0
  274. package/dist/item/plan-symbols.d.ts +67 -0
  275. package/dist/item/plan-symbols.d.ts.map +1 -0
  276. package/dist/item/plan-symbols.js +407 -0
  277. package/dist/item/renderer.d.ts +10 -0
  278. package/dist/item/renderer.d.ts.map +1 -1
  279. package/dist/item/renderer.js +64 -38
  280. package/dist/item/script-param-defaults.d.ts +12 -0
  281. package/dist/item/script-param-defaults.d.ts.map +1 -0
  282. package/dist/item/script-param-defaults.js +34 -0
  283. package/dist/lean-to-extension/definition.d.ts.map +1 -1
  284. package/dist/lean-to-extension/definition.js +1 -2
  285. package/dist/lean-to-extension/geometry.d.ts.map +1 -1
  286. package/dist/lean-to-extension/geometry.js +1 -1
  287. package/dist/lean-to-extension/layout.d.ts.map +1 -1
  288. package/dist/lean-to-extension/layout.js +2 -3
  289. package/dist/lean-to-extension/placement-validation.d.ts.map +1 -1
  290. package/dist/lean-to-extension/placement-validation.js +5 -5
  291. package/dist/lean-to-extension/tool.d.ts.map +1 -1
  292. package/dist/lean-to-extension/tool.js +5 -5
  293. package/dist/lineset/move-tool.d.ts.map +1 -1
  294. package/dist/lineset/move-tool.js +8 -2
  295. package/dist/liquid-line/move-tool.d.ts.map +1 -1
  296. package/dist/liquid-line/move-tool.js +8 -2
  297. package/dist/measurement/definition.js +1 -1
  298. package/dist/pipe-fitting/move-tool.d.ts.map +1 -1
  299. package/dist/pipe-fitting/move-tool.js +8 -2
  300. package/dist/pipe-segment/move-tool.d.ts.map +1 -1
  301. package/dist/pipe-segment/move-tool.js +8 -2
  302. package/dist/procedural-item/animation.d.ts +8 -0
  303. package/dist/procedural-item/animation.d.ts.map +1 -0
  304. package/dist/procedural-item/animation.js +116 -0
  305. package/dist/procedural-item/definition.d.ts.map +1 -1
  306. package/dist/procedural-item/definition.js +74 -8
  307. package/dist/procedural-item/geometry.d.ts +5 -1
  308. package/dist/procedural-item/geometry.d.ts.map +1 -1
  309. package/dist/procedural-item/geometry.js +239 -15
  310. package/dist/procedural-item/renderer.d.ts.map +1 -1
  311. package/dist/procedural-item/renderer.js +272 -23
  312. package/dist/ridge-vent/panel.d.ts.map +1 -1
  313. package/dist/ridge-vent/panel.js +5 -23
  314. package/dist/ridge-vent/renderer.d.ts.map +1 -1
  315. package/dist/ridge-vent/renderer.js +9 -8
  316. package/dist/roof/definition.d.ts.map +1 -1
  317. package/dist/roof/definition.js +4 -1
  318. package/dist/roof/floorplan.d.ts +8 -0
  319. package/dist/roof/floorplan.d.ts.map +1 -1
  320. package/dist/roof/floorplan.js +194 -18
  321. package/dist/roof/panel-model.d.ts.map +1 -1
  322. package/dist/roof/panel-model.js +10 -6
  323. package/dist/roof/panel.d.ts.map +1 -1
  324. package/dist/roof/panel.js +3 -10
  325. package/dist/roof/renderer.d.ts.map +1 -1
  326. package/dist/roof/renderer.js +5 -2
  327. package/dist/roof-segment/panel.d.ts.map +1 -1
  328. package/dist/roof-segment/panel.js +5 -24
  329. package/dist/roof-segment/renderer.d.ts.map +1 -1
  330. package/dist/roof-segment/renderer.js +17 -8
  331. package/dist/separator/definition.d.ts +3 -0
  332. package/dist/separator/definition.d.ts.map +1 -0
  333. package/dist/separator/definition.js +39 -0
  334. package/dist/separator/renderer.d.ts +5 -0
  335. package/dist/separator/renderer.d.ts.map +1 -0
  336. package/dist/separator/renderer.js +36 -0
  337. package/dist/shared/duct-body-paint.d.ts +0 -4
  338. package/dist/shared/duct-body-paint.d.ts.map +1 -1
  339. package/dist/shared/duct-body-paint.js +1 -5
  340. package/dist/shared/item-interaction-actions.d.ts +6 -0
  341. package/dist/shared/item-interaction-actions.d.ts.map +1 -0
  342. package/dist/shared/item-interaction-actions.js +27 -0
  343. package/dist/shared/item-interactions.d.ts +9 -0
  344. package/dist/shared/item-interactions.d.ts.map +1 -0
  345. package/dist/shared/item-interactions.js +74 -0
  346. package/dist/shared/item-light-placement.d.ts +2 -0
  347. package/dist/shared/item-light-placement.d.ts.map +1 -0
  348. package/dist/shared/item-light-placement.js +3 -0
  349. package/dist/shared/move-roof-tool.js +2 -1
  350. package/dist/shared/node-batch/batchable.d.ts +12 -0
  351. package/dist/shared/node-batch/batchable.d.ts.map +1 -0
  352. package/dist/shared/node-batch/batchable.js +41 -0
  353. package/dist/shared/node-batch/candidates.d.ts +7 -5
  354. package/dist/shared/node-batch/candidates.d.ts.map +1 -1
  355. package/dist/shared/node-batch/candidates.js +43 -59
  356. package/dist/shared/node-batch/release.d.ts +8 -0
  357. package/dist/shared/node-batch/release.d.ts.map +1 -0
  358. package/dist/shared/node-batch/release.js +16 -0
  359. package/dist/shared/node-batch/system.d.ts.map +1 -1
  360. package/dist/shared/node-batch/system.js +17 -12
  361. package/dist/shared/node-batch/types.d.ts +4 -1
  362. package/dist/shared/node-batch/types.d.ts.map +1 -1
  363. package/dist/shared/opening-documentation.d.ts +13 -1
  364. package/dist/shared/opening-documentation.d.ts.map +1 -1
  365. package/dist/shared/opening-documentation.js +245 -59
  366. package/dist/shared/opening-guides-runtime.d.ts.map +1 -1
  367. package/dist/shared/opening-guides-runtime.js +17 -14
  368. package/dist/shared/opening-placement-dimensions.d.ts.map +1 -1
  369. package/dist/shared/opening-placement-dimensions.js +3 -3
  370. package/dist/shared/opening-plane-offset.d.ts +23 -0
  371. package/dist/shared/opening-plane-offset.d.ts.map +1 -0
  372. package/dist/shared/opening-plane-offset.js +24 -0
  373. package/dist/shared/polygon-centroid-move.d.ts.map +1 -1
  374. package/dist/shared/polygon-centroid-move.js +4 -1
  375. package/dist/shared/polygon-vertex-affordance.d.ts.map +1 -1
  376. package/dist/shared/polygon-vertex-affordance.js +19 -15
  377. package/dist/shared/run-hangers.d.ts.map +1 -1
  378. package/dist/shared/run-hangers.js +5 -4
  379. package/dist/shared/scripted-opening-handles.d.ts +18 -0
  380. package/dist/shared/scripted-opening-handles.d.ts.map +1 -0
  381. package/dist/shared/scripted-opening-handles.js +53 -0
  382. package/dist/shared/scripted-opening.d.ts +25 -0
  383. package/dist/shared/scripted-opening.d.ts.map +1 -0
  384. package/dist/shared/scripted-opening.js +72 -0
  385. package/dist/shared/slot-paint.d.ts +14 -2
  386. package/dist/shared/slot-paint.d.ts.map +1 -1
  387. package/dist/shared/slot-paint.js +29 -29
  388. package/dist/shared/surface-paint.d.ts.map +1 -1
  389. package/dist/shared/surface-paint.js +2 -5
  390. package/dist/shared/swap-preview-material.d.ts +11 -0
  391. package/dist/shared/swap-preview-material.d.ts.map +1 -0
  392. package/dist/shared/swap-preview-material.js +16 -0
  393. package/dist/shared/system-checks.d.ts.map +1 -1
  394. package/dist/shared/system-checks.js +5 -3
  395. package/dist/shared/wall-attach-target.d.ts +3 -10
  396. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  397. package/dist/shared/wall-attach-target.js +3 -63
  398. package/dist/shared/wall-opening-ceiling.d.ts +5 -15
  399. package/dist/shared/wall-opening-ceiling.d.ts.map +1 -1
  400. package/dist/shared/wall-opening-ceiling.js +10 -18
  401. package/dist/shelf/definition.d.ts.map +1 -1
  402. package/dist/shelf/definition.js +1 -1
  403. package/dist/shelf/geometry.d.ts.map +1 -1
  404. package/dist/shelf/geometry.js +1 -2
  405. package/dist/shelf/paint.d.ts.map +1 -1
  406. package/dist/shelf/paint.js +4 -10
  407. package/dist/site/line-ribbon.d.ts +31 -0
  408. package/dist/site/line-ribbon.d.ts.map +1 -0
  409. package/dist/site/line-ribbon.js +117 -0
  410. package/dist/site/lot-mask.d.ts +18 -0
  411. package/dist/site/lot-mask.d.ts.map +1 -0
  412. package/dist/site/lot-mask.js +35 -0
  413. package/dist/site/panel.d.ts +10 -0
  414. package/dist/site/panel.d.ts.map +1 -0
  415. package/dist/site/panel.js +155 -0
  416. package/dist/site/parametrics.d.ts +7 -0
  417. package/dist/site/parametrics.d.ts.map +1 -1
  418. package/dist/site/parametrics.js +8 -0
  419. package/dist/site/recessed-slab-ground-holes.d.ts.map +1 -1
  420. package/dist/site/recessed-slab-ground-holes.js +5 -2
  421. package/dist/site/renderer.d.ts.map +1 -1
  422. package/dist/site/renderer.js +150 -19
  423. package/dist/site/setbacks.d.ts +33 -0
  424. package/dist/site/setbacks.d.ts.map +1 -0
  425. package/dist/site/setbacks.js +204 -0
  426. package/dist/site/terrain-geometry.d.ts +28 -1
  427. package/dist/site/terrain-geometry.d.ts.map +1 -1
  428. package/dist/site/terrain-geometry.js +78 -4
  429. package/dist/site/terrain-holes.d.ts +15 -0
  430. package/dist/site/terrain-holes.d.ts.map +1 -0
  431. package/dist/site/terrain-holes.js +74 -0
  432. package/dist/site/terrain-mesh.d.ts +9 -2
  433. package/dist/site/terrain-mesh.d.ts.map +1 -1
  434. package/dist/site/terrain-mesh.js +36 -9
  435. package/dist/site/terrain-renderer.d.ts +9 -2
  436. package/dist/site/terrain-renderer.d.ts.map +1 -1
  437. package/dist/site/terrain-renderer.js +14 -4
  438. package/dist/slab/boundary-editor.d.ts +1 -1
  439. package/dist/slab/boundary-editor.d.ts.map +1 -1
  440. package/dist/slab/boundary-editor.js +13 -13
  441. package/dist/slab/definition.d.ts.map +1 -1
  442. package/dist/slab/definition.js +19 -3
  443. package/dist/slab/dependency-tracker.d.ts.map +1 -1
  444. package/dist/slab/dependency-tracker.js +67 -7
  445. package/dist/slab/elevation-limit.d.ts.map +1 -1
  446. package/dist/slab/elevation-limit.js +2 -2
  447. package/dist/slab/floorplan-affordances.d.ts +176 -4
  448. package/dist/slab/floorplan-affordances.d.ts.map +1 -1
  449. package/dist/slab/floorplan-affordances.js +0 -1
  450. package/dist/slab/floorplan-move.d.ts.map +1 -1
  451. package/dist/slab/floorplan-move.js +3 -4
  452. package/dist/slab/floorplan.d.ts +7 -0
  453. package/dist/slab/floorplan.d.ts.map +1 -1
  454. package/dist/slab/floorplan.js +22 -5
  455. package/dist/slab/geometry.d.ts.map +1 -1
  456. package/dist/slab/geometry.js +186 -76
  457. package/dist/slab/move-tool.js +7 -6
  458. package/dist/slab/paint.d.ts +18 -5
  459. package/dist/slab/paint.d.ts.map +1 -1
  460. package/dist/slab/paint.js +458 -9
  461. package/dist/slab/panel.d.ts.map +1 -1
  462. package/dist/slab/panel.js +28 -9
  463. package/dist/slab/placement-ownership.d.ts +2 -1
  464. package/dist/slab/placement-ownership.d.ts.map +1 -1
  465. package/dist/slab/placement-ownership.js +2 -2
  466. package/dist/slab/surface-split.d.ts +24 -0
  467. package/dist/slab/surface-split.d.ts.map +1 -0
  468. package/dist/slab/surface-split.js +389 -0
  469. package/dist/slab/tool.d.ts.map +1 -1
  470. package/dist/slab/tool.js +3 -3
  471. package/dist/stair/baluster-geometry.d.ts +21 -0
  472. package/dist/stair/baluster-geometry.d.ts.map +1 -0
  473. package/dist/stair/baluster-geometry.js +74 -0
  474. package/dist/stair/baluster-guard.d.ts +50 -0
  475. package/dist/stair/baluster-guard.d.ts.map +1 -0
  476. package/dist/stair/baluster-guard.js +71 -0
  477. package/dist/stair/boards-guard.d.ts +37 -0
  478. package/dist/stair/boards-guard.d.ts.map +1 -0
  479. package/dist/stair/boards-guard.js +61 -0
  480. package/dist/stair/cable-guard.d.ts +39 -0
  481. package/dist/stair/cable-guard.d.ts.map +1 -0
  482. package/dist/stair/cable-guard.js +105 -0
  483. package/dist/stair/construction-controls.d.ts +15 -0
  484. package/dist/stair/construction-controls.d.ts.map +1 -0
  485. package/dist/stair/construction-controls.js +29 -0
  486. package/dist/stair/continuous-railings.d.ts +10 -0
  487. package/dist/stair/continuous-railings.d.ts.map +1 -0
  488. package/dist/stair/continuous-railings.js +159 -0
  489. package/dist/stair/definition.d.ts.map +1 -1
  490. package/dist/stair/definition.js +16 -37
  491. package/dist/stair/documentation.d.ts +1 -1
  492. package/dist/stair/documentation.d.ts.map +1 -1
  493. package/dist/stair/documentation.js +10 -17
  494. package/dist/stair/floorplan-affordances.d.ts.map +1 -1
  495. package/dist/stair/floorplan-affordances.js +9 -25
  496. package/dist/stair/floorplan.d.ts +1 -23
  497. package/dist/stair/floorplan.d.ts.map +1 -1
  498. package/dist/stair/floorplan.js +102 -86
  499. package/dist/stair/glass-guard.d.ts +72 -0
  500. package/dist/stair/glass-guard.d.ts.map +1 -0
  501. package/dist/stair/glass-guard.js +167 -0
  502. package/dist/stair/guard-path.d.ts +119 -0
  503. package/dist/stair/guard-path.d.ts.map +1 -0
  504. package/dist/stair/guard-path.js +208 -0
  505. package/dist/stair/materials.d.ts +11 -0
  506. package/dist/stair/materials.d.ts.map +1 -0
  507. package/dist/stair/materials.js +51 -0
  508. package/dist/stair/metal-guard.d.ts +38 -0
  509. package/dist/stair/metal-guard.d.ts.map +1 -0
  510. package/dist/stair/metal-guard.js +79 -0
  511. package/dist/stair/paint.d.ts +32 -0
  512. package/dist/stair/paint.d.ts.map +1 -1
  513. package/dist/stair/paint.js +75 -36
  514. package/dist/stair/panel.d.ts.map +1 -1
  515. package/dist/stair/panel.js +154 -17
  516. package/dist/stair/parametrics.d.ts +1 -1
  517. package/dist/stair/parametrics.d.ts.map +1 -1
  518. package/dist/stair/parametrics.js +32 -15
  519. package/dist/stair/plan-entry.d.ts +31 -0
  520. package/dist/stair/plan-entry.d.ts.map +1 -0
  521. package/dist/stair/plan-entry.js +325 -0
  522. package/dist/stair/post-and-rail-guard.d.ts +31 -0
  523. package/dist/stair/post-and-rail-guard.d.ts.map +1 -0
  524. package/dist/stair/post-and-rail-guard.js +63 -0
  525. package/dist/stair/railing-controls.d.ts +9 -0
  526. package/dist/stair/railing-controls.d.ts.map +1 -0
  527. package/dist/stair/railing-controls.js +49 -0
  528. package/dist/stair/renderer.d.ts.map +1 -1
  529. package/dist/stair/renderer.js +259 -259
  530. package/dist/stair/use-stair-render-data.d.ts +317 -0
  531. package/dist/stair/use-stair-render-data.d.ts.map +1 -0
  532. package/dist/stair/use-stair-render-data.js +73 -0
  533. package/dist/stair-segment/definition.d.ts.map +1 -1
  534. package/dist/stair-segment/definition.js +51 -10
  535. package/dist/stair-segment/panel.d.ts.map +1 -1
  536. package/dist/stair-segment/panel.js +36 -42
  537. package/dist/stair-segment/parametrics.d.ts +1 -1
  538. package/dist/stair-segment/parametrics.d.ts.map +1 -1
  539. package/dist/stair-segment/parametrics.js +21 -8
  540. package/dist/stair-segment/renderer.d.ts.map +1 -1
  541. package/dist/stair-segment/renderer.js +16 -28
  542. package/dist/turbine-vent/definition.d.ts.map +1 -1
  543. package/dist/turbine-vent/definition.js +2 -5
  544. package/dist/turbine-vent/paint.d.ts.map +1 -1
  545. package/dist/turbine-vent/paint.js +2 -5
  546. package/dist/turbine-vent/panel.d.ts.map +1 -1
  547. package/dist/turbine-vent/panel.js +5 -23
  548. package/dist/unit/unit-panel.d.ts.map +1 -1
  549. package/dist/unit/unit-panel.js +6 -1
  550. package/dist/wall/actions.d.ts.map +1 -1
  551. package/dist/wall/actions.js +7 -2
  552. package/dist/wall/construction-dimensions.js +8 -5
  553. package/dist/wall/contextual-dimensions.js +4 -4
  554. package/dist/wall/curtain-wall-materials.d.ts +1 -1
  555. package/dist/wall/curtain-wall-materials.d.ts.map +1 -1
  556. package/dist/wall/curtain-wall-materials.js +2 -2
  557. package/dist/wall/curve-tool.d.ts.map +1 -1
  558. package/dist/wall/curve-tool.js +4 -0
  559. package/dist/wall/definition.d.ts.map +1 -1
  560. package/dist/wall/definition.js +41 -17
  561. package/dist/wall/floorplan-move.d.ts +1 -1
  562. package/dist/wall/floorplan-move.d.ts.map +1 -1
  563. package/dist/wall/floorplan-move.js +20 -83
  564. package/dist/wall/floorplan-tool.d.ts.map +1 -1
  565. package/dist/wall/floorplan-tool.js +10 -11
  566. package/dist/wall/floorplan.d.ts +3 -1
  567. package/dist/wall/floorplan.d.ts.map +1 -1
  568. package/dist/wall/floorplan.js +184 -21
  569. package/dist/wall/measurement.d.ts.map +1 -1
  570. package/dist/wall/measurement.js +5 -5
  571. package/dist/wall/move-endpoint-tool.d.ts.map +1 -1
  572. package/dist/wall/move-endpoint-tool.js +20 -7
  573. package/dist/wall/move-shared.d.ts.map +1 -1
  574. package/dist/wall/move-shared.js +11 -7
  575. package/dist/wall/move-tool.d.ts.map +1 -1
  576. package/dist/wall/move-tool.js +118 -148
  577. package/dist/wall/paint.d.ts +26 -15
  578. package/dist/wall/paint.d.ts.map +1 -1
  579. package/dist/wall/paint.js +306 -117
  580. package/dist/wall/panel-model.d.ts +18 -0
  581. package/dist/wall/panel-model.d.ts.map +1 -1
  582. package/dist/wall/panel-model.js +77 -19
  583. package/dist/wall/panel.d.ts.map +1 -1
  584. package/dist/wall/panel.js +217 -41
  585. package/dist/wall/parametrics.d.ts.map +1 -1
  586. package/dist/wall/parametrics.js +3 -0
  587. package/dist/wall/rectangle-command.d.ts +111 -12
  588. package/dist/wall/rectangle-command.d.ts.map +1 -1
  589. package/dist/wall/rectangle-tool.d.ts.map +1 -1
  590. package/dist/wall/rectangle-tool.js +26 -8
  591. package/dist/wall/renderer.d.ts.map +1 -1
  592. package/dist/wall/renderer.js +4 -2
  593. package/dist/wall/split-floorplan-layer.d.ts.map +1 -1
  594. package/dist/wall/split-floorplan-layer.js +7 -7
  595. package/dist/wall/split-session.d.ts.map +1 -1
  596. package/dist/wall/split-session.js +7 -1
  597. package/dist/wall/system.d.ts.map +1 -1
  598. package/dist/wall/system.js +36 -4
  599. package/dist/wall/tool.d.ts.map +1 -1
  600. package/dist/wall/tool.js +105 -49
  601. package/dist/wall/treatment-level-data.d.ts +3 -2
  602. package/dist/wall/treatment-level-data.d.ts.map +1 -1
  603. package/dist/wall/treatment-level-data.js +17 -13
  604. package/dist/wall/treatments.d.ts +10 -10
  605. package/dist/wall/treatments.d.ts.map +1 -1
  606. package/dist/wall/treatments.js +67 -54
  607. package/dist/wall/wall-batch-system.d.ts +7 -0
  608. package/dist/wall/wall-batch-system.d.ts.map +1 -1
  609. package/dist/wall/wall-batch-system.js +35 -3
  610. package/dist/wall/wall-batch.d.ts +6 -0
  611. package/dist/wall/wall-batch.d.ts.map +1 -1
  612. package/dist/wall/wall-batch.js +7 -1
  613. package/dist/window/definition.d.ts.map +1 -1
  614. package/dist/window/definition.js +12 -4
  615. package/dist/window/floorplan-move.d.ts.map +1 -1
  616. package/dist/window/floorplan-move.js +3 -1
  617. package/dist/window/floorplan.d.ts.map +1 -1
  618. package/dist/window/floorplan.js +145 -60
  619. package/dist/window/mechanism.d.ts +4 -0
  620. package/dist/window/mechanism.d.ts.map +1 -0
  621. package/dist/window/mechanism.js +20 -0
  622. package/dist/window/move-tool.d.ts.map +1 -1
  623. package/dist/window/move-tool.js +79 -48
  624. package/dist/window/paint.d.ts.map +1 -1
  625. package/dist/window/paint.js +3 -0
  626. package/dist/window/panel.d.ts.map +1 -1
  627. package/dist/window/panel.js +32 -88
  628. package/dist/window/placement.d.ts +23 -0
  629. package/dist/window/placement.d.ts.map +1 -0
  630. package/dist/window/placement.js +66 -0
  631. package/dist/window/preview.d.ts.map +1 -1
  632. package/dist/window/preview.js +2 -0
  633. package/dist/window/renderer.d.ts.map +1 -1
  634. package/dist/window/renderer.js +3 -2
  635. package/dist/window/tool.d.ts.map +1 -1
  636. package/dist/window/tool.js +96 -24
  637. package/dist/window/window-math.d.ts +6 -14
  638. package/dist/window/window-math.d.ts.map +1 -1
  639. package/dist/window/window-math.js +14 -20
  640. package/dist/zone/contextual-dimensions.d.ts.map +1 -1
  641. package/dist/zone/contextual-dimensions.js +6 -28
  642. package/dist/zone/definition.d.ts.map +1 -1
  643. package/dist/zone/definition.js +3 -3
  644. package/dist/zone/floorplan-affordances.d.ts +200 -0
  645. package/dist/zone/floorplan-affordances.d.ts.map +1 -1
  646. package/dist/zone/floorplan.d.ts +1 -1
  647. package/dist/zone/floorplan.d.ts.map +1 -1
  648. package/dist/zone/floorplan.js +93 -53
  649. package/dist/zone/quantities-panel.d.ts.map +1 -1
  650. package/dist/zone/quantities-panel.js +3 -14
  651. package/dist/zone/quick-measurement.d.ts +2 -2
  652. package/dist/zone/quick-measurement.d.ts.map +1 -1
  653. package/dist/zone/quick-measurement.js +6 -5
  654. package/dist/zone/renderer.d.ts +1 -1
  655. package/dist/zone/renderer.d.ts.map +1 -1
  656. package/dist/zone/renderer.js +58 -146
  657. package/dist/zone/room-clear-dimensions.js +3 -3
  658. package/dist/zone/room-documentation.d.ts.map +1 -1
  659. package/dist/zone/room-documentation.js +2 -6
  660. package/package.json +11 -11
  661. package/dist/block/slots.d.ts +0 -4
  662. package/dist/block/slots.d.ts.map +0 -1
  663. package/dist/block/slots.js +0 -17
  664. package/dist/cabinet/slots.d.ts +0 -4
  665. package/dist/cabinet/slots.d.ts.map +0 -1
  666. package/dist/cabinet/slots.js +0 -24
  667. package/dist/ceiling/slots.d.ts +0 -6
  668. package/dist/ceiling/slots.d.ts.map +0 -1
  669. package/dist/ceiling/slots.js +0 -7
  670. package/dist/column/slots.d.ts +0 -9
  671. package/dist/column/slots.d.ts.map +0 -1
  672. package/dist/column/slots.js +0 -19
  673. package/dist/door/slots.d.ts +0 -9
  674. package/dist/door/slots.d.ts.map +0 -1
  675. package/dist/door/slots.js +0 -20
  676. package/dist/elevator/slots.d.ts +0 -8
  677. package/dist/elevator/slots.d.ts.map +0 -1
  678. package/dist/elevator/slots.js +0 -17
  679. package/dist/fence/slots.d.ts +0 -10
  680. package/dist/fence/slots.d.ts.map +0 -1
  681. package/dist/fence/slots.js +0 -23
  682. package/dist/item/__fixtures__/main-floorplan.d.ts +0 -26
  683. package/dist/item/__fixtures__/main-floorplan.d.ts.map +0 -1
  684. package/dist/item/__fixtures__/main-floorplan.js +0 -297
  685. package/dist/lean-to-extension/slots.d.ts +0 -5
  686. package/dist/lean-to-extension/slots.d.ts.map +0 -1
  687. package/dist/lean-to-extension/slots.js +0 -15
  688. package/dist/shelf/slots.d.ts +0 -9
  689. package/dist/shelf/slots.d.ts.map +0 -1
  690. package/dist/shelf/slots.js +0 -30
  691. package/dist/slab/slots.d.ts +0 -10
  692. package/dist/slab/slots.d.ts.map +0 -1
  693. package/dist/slab/slots.js +0 -20
  694. package/dist/stair/floor-stack.d.ts +0 -10
  695. package/dist/stair/floor-stack.d.ts.map +0 -1
  696. package/dist/stair/floor-stack.js +0 -115
  697. package/dist/stair/slots.d.ts +0 -7
  698. package/dist/stair/slots.d.ts.map +0 -1
  699. package/dist/stair/slots.js +0 -13
  700. package/dist/wall/drawing-mode.d.ts +0 -12
  701. package/dist/wall/drawing-mode.d.ts.map +0 -1
  702. package/dist/wall/drawing-mode.js +0 -38
  703. package/dist/wall/slots.d.ts +0 -10
  704. package/dist/wall/slots.d.ts.map +0 -1
  705. package/dist/wall/slots.js +0 -92
  706. package/dist/window/slots.d.ts +0 -5
  707. package/dist/window/slots.d.ts.map +0 -1
  708. package/dist/window/slots.js +0 -11
@@ -1,6 +1,8 @@
1
+ import { getWallBodyCenterOffset } from '@pascal-app/core';
1
2
  import { floorplanGeometryMetadata, readFloorplanContext } from '@pascal-app/editor';
2
3
  import { buildOpeningMarkAnnotation, } from '../shared/opening-documentation';
3
4
  import { buildOpeningPlacementDimensions } from '../shared/opening-placement-dimensions';
5
+ import { resolveOpeningPlanPlane } from '../shared/opening-plane-offset';
4
6
  /**
5
7
  * Stage C floor-plan builder for window. Mirrors the legacy
6
8
  * floorplan-panel window rendering:
@@ -31,11 +33,42 @@ export function buildWindowFloorplan(node, ctx) {
31
33
  const perpZ = dirX;
32
34
  const distance = node.position[0];
33
35
  const width = node.width;
34
- const depth = wall.thickness ?? 0.1;
35
- const cx = x1 + dirX * distance;
36
- const cz = z1 + dirZ * distance;
36
+ const wallDepth = wall.thickness ?? 0.1;
37
+ const plane = resolveOpeningPlanPlane(node, wallDepth);
38
+ const depth = plane.depth;
39
+ // The plane offset is measured from the body centre, which a justified wall
40
+ // sets off its reference line.
41
+ const bodyOffset = getWallBodyCenterOffset(wall);
42
+ const across = bodyOffset + plane.offset;
43
+ const cx = x1 + dirX * distance + perpX * across;
44
+ const cz = z1 + dirZ * distance + perpZ * across;
37
45
  const halfWidth = width / 2;
38
46
  const halfDepth = depth / 2;
47
+ // An offset frame stands clear of the wall centre, but the wall is still
48
+ // cut through its whole thickness: that hole is drawn too.
49
+ const wallX = x1 + dirX * distance + perpX * bodyOffset;
50
+ const wallZ = z1 + dirZ * distance + perpZ * bodyOffset;
51
+ const halfWallDepth = wallDepth / 2;
52
+ const cutoutPoints = plane.offset === 0
53
+ ? null
54
+ : [
55
+ [
56
+ wallX - dirX * halfWidth + perpX * halfWallDepth,
57
+ wallZ - dirZ * halfWidth + perpZ * halfWallDepth,
58
+ ],
59
+ [
60
+ wallX + dirX * halfWidth + perpX * halfWallDepth,
61
+ wallZ + dirZ * halfWidth + perpZ * halfWallDepth,
62
+ ],
63
+ [
64
+ wallX + dirX * halfWidth - perpX * halfWallDepth,
65
+ wallZ + dirZ * halfWidth - perpZ * halfWallDepth,
66
+ ],
67
+ [
68
+ wallX - dirX * halfWidth - perpX * halfWallDepth,
69
+ wallZ - dirZ * halfWidth - perpZ * halfWallDepth,
70
+ ],
71
+ ];
39
72
  const points = [
40
73
  [cx - dirX * halfWidth + perpX * halfDepth, cz - dirZ * halfWidth + perpZ * halfDepth],
41
74
  [cx + dirX * halfWidth + perpX * halfDepth, cz + dirZ * halfWidth + perpZ * halfDepth],
@@ -75,44 +108,93 @@ export function buildWindowFloorplan(node, ctx) {
75
108
  // midpoint of the right edge of the cutout.
76
109
  const mullionStart = [cx - dirX * halfWidth, cz - dirZ * halfWidth];
77
110
  const mullionEnd = [cx + dirX * halfWidth, cz + dirZ * halfWidth];
78
- const children = [
79
- // Outer footprint — white fill so the wall hatch underneath
80
- // doesn't bleed through.
81
- {
82
- kind: 'polygon',
83
- points,
84
- fill: fillColor,
85
- stroke: accentColor,
86
- strokeWidth: showSelectedChrome ? 1.9 : 1.25,
87
- vectorEffect: 'non-scaling-stroke',
88
- strokeLinejoin: 'round',
89
- metadata: floorplanGeometryMetadata({ annotationObstacle: 'bounds' }),
90
- },
91
- // Inset glass-pane outline.
92
- {
93
- kind: 'polygon',
94
- points: [innerStartA, innerEndA, innerEndB, innerStartB],
95
- fill: 'none',
96
- stroke: accentColor,
97
- strokeOpacity: 0.6,
98
- strokeWidth: showSelectedChrome ? 1.3 : 0.9,
99
- vectorEffect: 'non-scaling-stroke',
100
- strokeLinejoin: 'round',
101
- },
102
- // Center mullion.
103
- {
104
- kind: 'line',
105
- x1: mullionStart[0],
106
- y1: mullionStart[1],
107
- x2: mullionEnd[0],
108
- y2: mullionEnd[1],
109
- stroke: accentColor,
110
- strokeWidth: showSelectedChrome ? 1.6 : 1.1,
111
- strokeOpacity: 0.85,
112
- strokeLinecap: 'round',
113
- vectorEffect: 'non-scaling-stroke',
114
- },
115
- ];
111
+ // A sheet draws the standard window symbol in plan-metre ink, so it prints
112
+ // the same in every back end: the two wall faces carried across the opening
113
+ // (the outline, which closes on the jambs) and the glass line between them.
114
+ const drafting = readFloorplanContext(ctx).drafting;
115
+ const cutout = cutoutPoints
116
+ ? [
117
+ drafting
118
+ ? {
119
+ kind: 'polygon',
120
+ points: cutoutPoints,
121
+ fill: '#ffffff',
122
+ stroke: '#1f2937',
123
+ strokeWidth: 0.01,
124
+ strokeLinejoin: 'miter',
125
+ }
126
+ : {
127
+ kind: 'polygon',
128
+ points: cutoutPoints,
129
+ fill: fillColor,
130
+ stroke: accentColor,
131
+ strokeWidth: showSelectedChrome ? 1.9 : 1.25,
132
+ vectorEffect: 'non-scaling-stroke',
133
+ strokeLinejoin: 'round',
134
+ metadata: floorplanGeometryMetadata({ annotationObstacle: 'bounds' }),
135
+ },
136
+ ]
137
+ : [];
138
+ const children = drafting
139
+ ? [
140
+ ...cutout,
141
+ {
142
+ kind: 'polygon',
143
+ points,
144
+ fill: '#ffffff',
145
+ stroke: '#1f2937',
146
+ strokeWidth: 0.01,
147
+ strokeLinejoin: 'miter',
148
+ },
149
+ {
150
+ kind: 'line',
151
+ x1: mullionStart[0],
152
+ y1: mullionStart[1],
153
+ x2: mullionEnd[0],
154
+ y2: mullionEnd[1],
155
+ stroke: '#1f2937',
156
+ strokeWidth: 0.008,
157
+ },
158
+ ]
159
+ : [
160
+ ...cutout,
161
+ // Outer footprint — white fill so the wall hatch underneath
162
+ // doesn't bleed through.
163
+ {
164
+ kind: 'polygon',
165
+ points,
166
+ fill: fillColor,
167
+ stroke: accentColor,
168
+ strokeWidth: showSelectedChrome ? 1.9 : 1.25,
169
+ vectorEffect: 'non-scaling-stroke',
170
+ strokeLinejoin: 'round',
171
+ metadata: floorplanGeometryMetadata({ annotationObstacle: 'bounds' }),
172
+ },
173
+ // Inset glass-pane outline.
174
+ {
175
+ kind: 'polygon',
176
+ points: [innerStartA, innerEndA, innerEndB, innerStartB],
177
+ fill: 'none',
178
+ stroke: accentColor,
179
+ strokeOpacity: 0.6,
180
+ strokeWidth: showSelectedChrome ? 1.3 : 0.9,
181
+ vectorEffect: 'non-scaling-stroke',
182
+ strokeLinejoin: 'round',
183
+ },
184
+ // Center mullion.
185
+ {
186
+ kind: 'line',
187
+ x1: mullionStart[0],
188
+ y1: mullionStart[1],
189
+ x2: mullionEnd[0],
190
+ y2: mullionEnd[1],
191
+ stroke: accentColor,
192
+ strokeWidth: showSelectedChrome ? 1.6 : 1.1,
193
+ strokeOpacity: 0.85,
194
+ strokeLinecap: 'round',
195
+ vectorEffect: 'non-scaling-stroke',
196
+ },
197
+ ];
116
198
  // Move handle — orange dot at the window center. Only when selected.
117
199
  if (isSelected) {
118
200
  children.push({
@@ -124,24 +206,27 @@ export function buildWindowFloorplan(node, ctx) {
124
206
  // `resize-width` affordance — anchored at the opposite edge, clamped
125
207
  // to wall bounds. Mirrors the 3D `WindowSideArrow` width drag and the
126
208
  // door's 2D pattern.
127
- const startEdgeX = cx - dirX * halfWidth;
128
- const startEdgeZ = cz - dirZ * halfWidth;
129
- const endEdgeX = cx + dirX * halfWidth;
130
- const endEdgeZ = cz + dirZ * halfWidth;
131
- children.push({
132
- kind: 'move-arrow',
133
- point: [startEdgeX, startEdgeZ],
134
- angle: Math.atan2(-dirZ, -dirX),
135
- affordance: 'resize-width',
136
- payload: { side: 'start' },
137
- });
138
- children.push({
139
- kind: 'move-arrow',
140
- point: [endEdgeX, endEdgeZ],
141
- angle: Math.atan2(dirZ, dirX),
142
- affordance: 'resize-width',
143
- payload: { side: 'end' },
144
- });
209
+ // A scripted opening's width is its script's: its size arrows live in 3D, on its params.
210
+ if (!node.source) {
211
+ const startEdgeX = cx - dirX * halfWidth;
212
+ const startEdgeZ = cz - dirZ * halfWidth;
213
+ const endEdgeX = cx + dirX * halfWidth;
214
+ const endEdgeZ = cz + dirZ * halfWidth;
215
+ children.push({
216
+ kind: 'move-arrow',
217
+ point: [startEdgeX, startEdgeZ],
218
+ angle: Math.atan2(-dirZ, -dirX),
219
+ affordance: 'resize-width',
220
+ payload: { side: 'start' },
221
+ });
222
+ children.push({
223
+ kind: 'move-arrow',
224
+ point: [endEdgeX, endEdgeZ],
225
+ angle: Math.atan2(dirZ, dirX),
226
+ affordance: 'resize-width',
227
+ payload: { side: 'end' },
228
+ });
229
+ }
145
230
  }
146
231
  // Placement-measurement dimensions when actively moving — same
147
232
  // contract as door (see `nodes/src/door/floorplan.ts`).
@@ -150,7 +235,7 @@ export function buildWindowFloorplan(node, ctx) {
150
235
  children.push(dim);
151
236
  }
152
237
  }
153
- const markAnnotation = buildOpeningMarkAnnotation(node, wall, ctx.levelData, { stroke: showSelectedChrome ? '#f97316' : '#334155' });
238
+ const markAnnotation = buildOpeningMarkAnnotation(node, wall, ctx.levelData, { stroke: showSelectedChrome ? '#f97316' : '#334155', drafting });
154
239
  if (markAnnotation)
155
240
  children.push(markAnnotation);
156
241
  return { kind: 'group', children };
@@ -0,0 +1,4 @@
1
+ import type { MechanismCapability } from '@pascal-app/core';
2
+ /** A window's sash: Open and Close preview it without touching the saved open state. */
3
+ export declare const windowMechanism: MechanismCapability;
4
+ //# sourceMappingURL=mechanism.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mechanism.d.ts","sourceRoot":"","sources":["../../src/window/mechanism.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAS3D,wFAAwF;AACxF,eAAO,MAAM,eAAe,EAAE,mBAiB7B,CAAA"}
@@ -0,0 +1,20 @@
1
+ import { closeWindowOpenState, getDisplayedWindowValue, isOperableWindowType, openWindowOpenState, } from '@pascal-app/editor';
2
+ import { scriptedOpening } from '../shared/scripted-opening';
3
+ /** A window's sash: Open and Close preview it without touching the saved open state. */
4
+ export const windowMechanism = {
5
+ verb: 'open',
6
+ icon: 'window',
7
+ has: (node) => node.type === 'window' &&
8
+ (node.source
9
+ ? scriptedOpening.has(node)
10
+ : node.openingKind !== 'opening' && isOperableWindowType(node.windowType)),
11
+ isOn: (node) => node.type === 'window' &&
12
+ (node.source
13
+ ? scriptedOpening.isOn(node)
14
+ : getDisplayedWindowValue(node.id, node.operationState) > 0),
15
+ set: (node, on) => {
16
+ if (node.type === 'window' && node.source)
17
+ return scriptedOpening.set(node, on);
18
+ return (on ? openWindowOpenState : closeWindowOpenState)(node.id, { persist: false });
19
+ },
20
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"move-tool.d.ts","sourceRoot":"","sources":["../../src/window/move-tool.tsx"],"names":[],"mappings":"AAAA,OAAO,EAiBL,UAAU,EACX,MAAM,kBAAkB,CAAA;AAkEzB;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAiyClD,CAAA;AAED,eAAe,cAAc,CAAA"}
1
+ {"version":3,"file":"move-tool.d.ts","sourceRoot":"","sources":["../../src/window/move-tool.tsx"],"names":[],"mappings":"AAAA,OAAO,EAqBL,UAAU,EACX,MAAM,kBAAkB,CAAA;AAmEzB;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAy0ClD,CAAA;AAED,eAAe,cAAc,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { dormerWallFacePointToDormer, emitter, holdHiddenWallPointerEvents, isCurvedWall, sceneRegistry, spatialGridManager, useLiveNodeOverrides, useLiveTransforms, useScene, WindowNode, } from '@pascal-app/core';
2
+ import { beginSceneHistoryDraft, collectionIdsOf, dormerWallFacePointToDormer, emitter, getOpeningWallPlacement, holdHiddenWallPointerEvents, isCurvedWall, runSceneHistoryDraftWrite, sceneRegistry, spatialGridManager, useLiveNodeOverrides, useLiveTransforms, useScene, WindowNode, } from '@pascal-app/core';
3
3
  import { calculateItemRotation, clearPlacementSurface, consumePlacementDragRelease, EDITOR_LAYER, getSideFromNormal, isGridSnapActive, isMagneticSnapActive, isValidWallSideFace, publishPlacementSurface, snapToHalf, stripPlacementMetadataFlags, triggerSFX, useAlignmentGuides, useEditor, useFacingPose, useRegistryToolContext, } from '@pascal-app/editor';
4
4
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
5
  import { BoxGeometry, EdgesGeometry, Vector3 } from 'three';
@@ -9,6 +9,7 @@ import { dormerEventFromHostedWindow, getDormerWindowWorldNormal, getDormerWindo
9
9
  import { clearOpeningGuides3D, publishOpeningGuidesForWallEvent, resolveSillSnap, } from '../shared/opening-guides-runtime';
10
10
  import { beginOpeningMoveHistorySession } from '../shared/opening-move-history';
11
11
  import { isWallMeshHidden, shouldIgnoreWallEventForOpeningMove, } from '../shared/opening-move-wall-gate';
12
+ import { openingPlaneOffsetOnWall } from '../shared/opening-plane-offset';
12
13
  import { getRoofWallOpeningCursorPose, resolveRoofWallOpeningTarget, } from '../shared/roof-wall-opening-placement';
13
14
  import { resolveOpeningPlacement } from '../shared/wall-attach-target';
14
15
  import { collectWallOpeningAlignmentCandidates, resolveWallSlideAlignment, } from '../shared/wall-opening-alignment';
@@ -77,6 +78,8 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
77
78
  ? movingWindowNode.metadata
78
79
  : {};
79
80
  const isNew = !!meta.isNew;
81
+ const endDraft = isNew ? null : beginSceneHistoryDraft(movingWindowNode.id, movingWindowNode);
82
+ const updateDraft = (data) => runSceneHistoryDraftWrite(() => useScene.getState().updateNode(movingWindowNode.id, data));
80
83
  // Save original state (only used in move mode)
81
84
  const original = {
82
85
  position: [...movingWindowNode.position],
@@ -102,10 +105,12 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
102
105
  // preview on the wall and can't be placed consecutively without leaving/re-entering. This
103
106
  // mirrors MoveDoorTool.
104
107
  if (!isNew) {
105
- useScene.getState().updateNode(movingWindowNode.id, {
108
+ updateDraft({
106
109
  metadata: { ...meta, isTransient: true },
107
110
  });
108
111
  }
112
+ // Same-wall moves keep the wall-local plane offset; another host resets it.
113
+ const planeOffsetOn = (wallId) => openingPlaneOffsetOnWall(movingWindowNode, wallId);
109
114
  let currentHostId = movingWindowNode.parentId;
110
115
  let committed = false;
111
116
  // Off-wall free-follow: over empty floor the window is parented to the
@@ -196,7 +201,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
196
201
  const y = movingWindowNode.position[1];
197
202
  return y > 0.1 ? y : DEFAULT_WINDOW_SILL_M + movingWindowNode.height / 2;
198
203
  };
199
- const getSlabElevation = (wallEvent) => spatialGridManager.getSlabElevationForWall(wallEvent.node.parentId ?? '', wallEvent.node.start, wallEvent.node.end, wallEvent.node.curveOffset ?? 0, wallEvent.node.thickness, wallEvent.node.supportSlabId);
204
+ const getSlabElevation = (wallEvent) => spatialGridManager.getSlabElevationForWall(wallEvent.node.parentId ?? '', wallEvent.node.start, wallEvent.node.end, wallEvent.node.curveOffset ?? 0, wallEvent.node.thickness, wallEvent.node.supportSlabId, wallEvent.node.justification);
200
205
  const hideCursor = () => {
201
206
  if (cursorGroupRef.current)
202
207
  cursorGroupRef.current.visible = false;
@@ -245,7 +250,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
245
250
  return;
246
251
  const faceSide = getSideFromNormal(event.normal);
247
252
  const side = sideOverride ?? faceSide;
248
- const rotationOffset = side !== faceSide ? Math.PI : 0;
253
+ const rotationOffset = side === faceSide ? 0 : Math.PI;
249
254
  const itemRotation = calculateItemRotation(event.normal) + rotationOffset;
250
255
  const rawLocalX = event.localPosition[0];
251
256
  const rawLocalY = event.localPosition[1];
@@ -321,9 +326,24 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
321
326
  // its stale parent (no on-wall preview at all). A stale override from a
322
327
  // free-follow / dormer hop would shadow those scene fields, so drop it.
323
328
  useLiveNodeOverrides.getState().clear(movingWindowNode.id);
324
- if (currentHostId !== target.wallId) {
325
- useScene.getState().updateNode(movingWindowNode.id, {
326
- position: [target.clampedX, target.clampedY, 0],
329
+ if (currentHostId === target.wallId) {
330
+ const windowMesh = sceneRegistry.nodes.get(movingWindowNode.id);
331
+ if (windowMesh) {
332
+ // Where the opening system will put it: the body centre plane of a
333
+ // justified wall plus the opening's own plane offset, on the arc.
334
+ const placement = getOpeningWallPlacement(target.wallNode, {
335
+ ...movingWindowNode,
336
+ position: [target.clampedX, target.clampedY, planeOffsetOn(target.wallId)],
337
+ rotation: [0, target.itemRotation, 0],
338
+ }, useScene.getState().nodes);
339
+ windowMesh.position.set(...placement.position);
340
+ windowMesh.rotation.set(...placement.rotation);
341
+ windowMesh.updateMatrixWorld(true);
342
+ }
343
+ }
344
+ else {
345
+ updateDraft({
346
+ position: [target.clampedX, target.clampedY, planeOffsetOn(target.wallId)],
327
347
  rotation: [0, target.itemRotation, 0],
328
348
  side: target.side,
329
349
  parentId: target.wallId,
@@ -337,16 +357,8 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
337
357
  markHostDirty(currentHostId);
338
358
  currentHostId = target.wallId;
339
359
  }
340
- else {
341
- const windowMesh = sceneRegistry.nodes.get(movingWindowNode.id);
342
- if (windowMesh) {
343
- windowMesh.position.set(target.clampedX, target.clampedY, 0);
344
- windowMesh.rotation.set(0, target.itemRotation, 0);
345
- windowMesh.updateMatrixWorld(true);
346
- }
347
- }
348
360
  useLiveTransforms.getState().set(movingWindowNode.id, {
349
- position: [target.clampedX, target.clampedY, 0],
361
+ position: [target.clampedX, target.clampedY, planeOffsetOn(target.wallId)],
350
362
  rotation: target.itemRotation,
351
363
  });
352
364
  markHostDirtyThrottled(target.wallId);
@@ -357,7 +369,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
357
369
  // (-wallAngle + itemRotation); `cursorRotation` is π off here. See
358
370
  // `MoveDoorTool.applyPreview`.
359
371
  const wallAngle = Math.atan2(target.wallNode.end[1] - target.wallNode.start[1], target.wallNode.end[0] - target.wallNode.start[0]);
360
- const ghostWorldPos = wallLocalToWorld(target.wallNode, target.clampedX, target.clampedY, getLevelYOffset(), getSlabElevation(target.event));
372
+ const ghostWorldPos = wallLocalToWorld(target.wallNode, target.clampedX, target.clampedY, getLevelYOffset(), getSlabElevation(target.event), planeOffsetOn(target.wallId));
361
373
  const ghostYaw = target.itemRotation - wallAngle;
362
374
  setGhostPose({
363
375
  position: ghostWorldPos,
@@ -455,13 +467,15 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
455
467
  // Duplicate mode: delete the transient draft while history is still
456
468
  // paused, then create the real node as the gesture's ONE tracked
457
469
  // write — undo removes the new window entirely.
470
+ // Read before the delete: the copy joins the collections its draft is in.
471
+ const collectionIds = collectionIdsOf(useScene.getState().collections, movingWindowNode.id);
458
472
  useScene.getState().deleteNode(movingWindowNode.id);
459
473
  const cloned = structuredClone(movingWindowNode);
460
474
  delete cloned.id;
461
475
  cloned.metadata = stripPlacementMetadataFlags(cloned.metadata);
462
476
  const node = WindowNode.parse({
463
477
  ...cloned,
464
- position: [target.clampedX, target.clampedY, 0],
478
+ position: [target.clampedX, target.clampedY, planeOffsetOn(target.wallId)],
465
479
  rotation: [0, target.itemRotation, 0],
466
480
  side: target.side,
467
481
  wallId: target.wallId,
@@ -472,7 +486,9 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
472
486
  visible: true,
473
487
  });
474
488
  history.commitStep(() => {
475
- useScene.getState().createNode(node, target.wallId);
489
+ useScene
490
+ .getState()
491
+ .createNodes([{ node, parentId: target.wallId, collectionIds }]);
476
492
  });
477
493
  placedId = node.id;
478
494
  }
@@ -480,7 +496,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
480
496
  // Move mode: restore the exact pre-drag state while history is still
481
497
  // paused (the clean undo baseline), then apply the drop as the
482
498
  // gesture's ONE tracked write — undo reverts to the original state.
483
- useScene.getState().updateNode(movingWindowNode.id, {
499
+ updateDraft({
484
500
  position: original.position,
485
501
  rotation: original.rotation,
486
502
  side: original.side,
@@ -495,7 +511,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
495
511
  });
496
512
  history.commitStep(() => {
497
513
  commitOpeningMove(movingWindowNode.id, {
498
- position: [target.clampedX, target.clampedY, 0],
514
+ position: [target.clampedX, target.clampedY, planeOffsetOn(target.wallId)],
499
515
  rotation: [0, target.itemRotation, 0],
500
516
  side: target.side,
501
517
  parentId: target.wallId,
@@ -514,6 +530,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
514
530
  triggerSFX('sfx:structure-build');
515
531
  hideCursor();
516
532
  selectNode(placedId);
533
+ endDraft?.();
517
534
  exitMoveMode();
518
535
  };
519
536
  const onWallClick = (event) => {
@@ -586,10 +603,18 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
586
603
  // Scene writes, not overrides: leaving the wall must actually remove the
587
604
  // window from the wall's `children` or the CSG cut trails the ghost
588
605
  // around the old wall (see the wall-branch note in `applyPreview`).
589
- if (currentHostId !== levelId) {
606
+ if (currentHostId === levelId) {
607
+ updateDraft({
608
+ position: [localX, sillCenterY, localZ],
609
+ rotation: [0, yaw, 0],
610
+ side: sideOverride,
611
+ visible: false,
612
+ });
613
+ }
614
+ else {
590
615
  if (currentHostId && currentHostId !== levelId)
591
616
  markHostDirty(currentHostId);
592
- useScene.getState().updateNode(movingWindowNode.id, {
617
+ updateDraft({
593
618
  position: [localX, sillCenterY, localZ],
594
619
  rotation: [0, yaw, 0],
595
620
  side: sideOverride,
@@ -603,14 +628,6 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
603
628
  });
604
629
  currentHostId = levelId;
605
630
  }
606
- else {
607
- useScene.getState().updateNode(movingWindowNode.id, {
608
- position: [localX, sillCenterY, localZ],
609
- rotation: [0, yaw, 0],
610
- side: sideOverride,
611
- visible: false,
612
- });
613
- }
614
631
  // Float the red (invalid — no wall) ghost at the cursor, level-Y lifted to
615
632
  // the sill center (sideOverride carries the R-flip so the ghost matches).
616
633
  setGhostPose({
@@ -699,6 +716,8 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
699
716
  const rotation = [0, side === 'back' ? Math.PI : 0, 0];
700
717
  let placedId;
701
718
  if (isNew) {
719
+ // Read before the delete: the copy joins the collections its draft is in.
720
+ const collectionIds = collectionIdsOf(useScene.getState().collections, movingWindowNode.id);
702
721
  useScene.getState().deleteNode(movingWindowNode.id);
703
722
  const cloned = structuredClone(movingWindowNode);
704
723
  delete cloned.id;
@@ -717,12 +736,16 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
717
736
  visible: true,
718
737
  });
719
738
  history.commitStep(() => {
720
- useScene.getState().createNode(committedNode, target.dormer.id);
739
+ useScene
740
+ .getState()
741
+ .createNodes([
742
+ { node: committedNode, parentId: target.dormer.id, collectionIds },
743
+ ]);
721
744
  });
722
745
  placedId = committedNode.id;
723
746
  }
724
747
  else {
725
- useScene.getState().updateNode(movingWindowNode.id, {
748
+ updateDraft({
726
749
  position: original.position,
727
750
  rotation: original.rotation,
728
751
  side: original.side,
@@ -759,6 +782,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
759
782
  triggerSFX('sfx:structure-build');
760
783
  hideCursor();
761
784
  selectNode(placedId);
785
+ endDraft?.();
762
786
  exitMoveMode();
763
787
  event.stopPropagation();
764
788
  };
@@ -860,25 +884,25 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
860
884
  clearOpeningGuides3D();
861
885
  // On a roof face the real mesh is the preview — drop the ghost + reveal.
862
886
  revealRealNode();
863
- if (currentHostId !== target.segment.id) {
864
- markHostDirty(currentHostId);
865
- currentHostId = target.segment.id;
887
+ if (currentHostId === target.segment.id) {
866
888
  useLiveNodeOverrides.getState().set(movingWindowNode.id, {
867
889
  position: target.position,
868
890
  rotation: [0, 0, 0],
869
- side: 'front',
870
- parentId: target.segment.id,
871
- wallId: undefined,
872
- roofSegmentId: target.segment.id,
873
891
  roofFace: target.face.id,
874
- visible: true,
875
892
  });
876
893
  }
877
894
  else {
895
+ markHostDirty(currentHostId);
896
+ currentHostId = target.segment.id;
878
897
  useLiveNodeOverrides.getState().set(movingWindowNode.id, {
879
898
  position: target.position,
880
899
  rotation: [0, 0, 0],
900
+ side: 'front',
901
+ parentId: target.segment.id,
902
+ wallId: undefined,
903
+ roofSegmentId: target.segment.id,
881
904
  roofFace: target.face.id,
905
+ visible: true,
882
906
  });
883
907
  }
884
908
  updateRoofCursor(target, event.node);
@@ -899,6 +923,8 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
899
923
  if (isNew) {
900
924
  // See commitToWall — delete the draft paused, create as the ONE
901
925
  // tracked write.
926
+ // Read before the delete: the copy joins the collections its draft is in.
927
+ const collectionIds = collectionIdsOf(useScene.getState().collections, movingWindowNode.id);
902
928
  useScene.getState().deleteNode(movingWindowNode.id);
903
929
  const cloned = structuredClone(movingWindowNode);
904
930
  delete cloned.id;
@@ -915,14 +941,16 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
915
941
  visible: true,
916
942
  });
917
943
  history.commitStep(() => {
918
- useScene.getState().createNode(node, segmentId);
944
+ useScene
945
+ .getState()
946
+ .createNodes([{ node, parentId: segmentId, collectionIds }]);
919
947
  });
920
948
  placedId = node.id;
921
949
  }
922
950
  else {
923
951
  // See commitToWall — restore the pre-drag baseline paused, drop as
924
952
  // the ONE tracked write.
925
- useScene.getState().updateNode(movingWindowNode.id, {
953
+ updateDraft({
926
954
  position: original.position,
927
955
  rotation: original.rotation,
928
956
  side: original.side,
@@ -957,6 +985,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
957
985
  triggerSFX('sfx:structure-build');
958
986
  hideCursor();
959
987
  selectNode(placedId);
988
+ endDraft?.();
960
989
  exitMoveMode();
961
990
  event.stopPropagation();
962
991
  };
@@ -979,7 +1008,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
979
1008
  markHostDirty(currentHostId);
980
1009
  }
981
1010
  else {
982
- useScene.getState().updateNode(movingWindowNode.id, {
1011
+ updateDraft({
983
1012
  position: original.position,
984
1013
  rotation: original.rotation,
985
1014
  side: original.side,
@@ -1000,6 +1029,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
1000
1029
  // entirely. `end` is idempotent — the effect cleanup's end() is a no-op.
1001
1030
  history.end();
1002
1031
  hideCursor();
1032
+ endDraft?.();
1003
1033
  exitMoveMode();
1004
1034
  };
1005
1035
  const onPlacementDragPointerUp = (event) => {
@@ -1060,7 +1090,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
1060
1090
  else {
1061
1091
  // No preview yet (R before the first pointermove): flip the hidden node
1062
1092
  // so the first preview/commit already reflects the chosen side.
1063
- useScene.getState().updateNode(movingWindowNode.id, {
1093
+ updateDraft({
1064
1094
  side: sideOverride,
1065
1095
  rotation: [0, sideOverride === 'back' ? Math.PI : 0, 0],
1066
1096
  });
@@ -1110,7 +1140,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
1110
1140
  if (hostWall?.type === 'wall') {
1111
1141
  const wallAngle = Math.atan2(hostWall.end[1] - hostWall.start[1], hostWall.end[0] - hostWall.start[0]);
1112
1142
  const ghostYaw = movingWindowNode.rotation[1] - wallAngle;
1113
- const seedPos = wallLocalToWorld(hostWall, movingWindowNode.position[0], movingWindowNode.position[1], getLevelYOffset(), spatialGridManager.getSlabElevationForWall(hostWall.parentId ?? '', hostWall.start, hostWall.end, hostWall.curveOffset ?? 0, hostWall.thickness, hostWall.supportSlabId));
1143
+ const seedPos = wallLocalToWorld(hostWall, movingWindowNode.position[0], movingWindowNode.position[1], getLevelYOffset(), spatialGridManager.getSlabElevationForWall(hostWall.parentId ?? '', hostWall.start, hostWall.end, hostWall.curveOffset ?? 0, hostWall.thickness, hostWall.supportSlabId, hostWall.justification), planeOffsetOn(hostWall.id));
1114
1144
  publishPlacementSurface(new Vector3(...seedPos), new Vector3(Math.sin(ghostYaw), 0, Math.cos(ghostYaw)));
1115
1145
  // Claim the pointer for the wall so the floor free-follow stands down for
1116
1146
  // the first frames after grab. Otherwise the first `grid:move` (the window
@@ -1131,7 +1161,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
1131
1161
  markHostDirty(currentHostId);
1132
1162
  }
1133
1163
  else {
1134
- useScene.getState().updateNode(movingWindowNode.id, {
1164
+ updateDraft({
1135
1165
  position: original.position,
1136
1166
  rotation: original.rotation,
1137
1167
  side: original.side,
@@ -1152,7 +1182,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
1152
1182
  // Safety net: a fresh (isNew) clone isn't marked `isTransient`; if we
1153
1183
  // unmount mid-free-follow it would be left hidden. Reveal it so it never
1154
1184
  // becomes an invisible orphan (place-preset deletes a true cancel).
1155
- useScene.getState().updateNode(movingWindowNode.id, { visible: true });
1185
+ updateDraft({ visible: true });
1156
1186
  }
1157
1187
  useLiveNodeOverrides.getState().clear(movingWindowNode.id);
1158
1188
  useLiveTransforms.getState().clear(movingWindowNode.id);
@@ -1162,6 +1192,7 @@ const MoveWindowTool = ({ node: movingWindowNode }) => {
1162
1192
  clearPlacementSurface();
1163
1193
  releaseHiddenWallHold();
1164
1194
  history.end();
1195
+ endDraft?.();
1165
1196
  emitter.off('wall:enter', onWallEnter);
1166
1197
  emitter.off('wall:move', onWallMove);
1167
1198
  emitter.off('wall:click', onWallClick);
@@ -1 +1 @@
1
- {"version":3,"file":"paint.d.ts","sourceRoot":"","sources":["../../src/window/paint.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,4CAGtB,CAAA"}
1
+ {"version":3,"file":"paint.d.ts","sourceRoot":"","sources":["../../src/window/paint.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,4CAOtB,CAAA"}
@@ -8,4 +8,7 @@ import { createSlotPaintCapability, previewSlotByUserData, resolveSlotByReRaycas
8
8
  export const windowPaint = createSlotPaintCapability({
9
9
  resolveRole: resolveSlotByReRaycast,
10
10
  applyPreview: previewSlotByUserData,
11
+ legacyEffective: (node, role) => node.type === 'window' && !node.source && node.material && role === 'frame'
12
+ ? { material: node.material, materialPreset: undefined }
13
+ : null,
11
14
  });