@pascal-app/editor 0.8.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/package.json +15 -13
  2. package/src/components/editor/alignment-3d-guide-layer.tsx +168 -0
  3. package/src/components/editor/bake-exporter.tsx +47 -0
  4. package/src/components/editor/custom-camera-controls.tsx +795 -28
  5. package/src/components/editor/editor-layout-mobile.tsx +8 -3
  6. package/src/components/editor/editor-layout-v2.tsx +76 -51
  7. package/src/components/editor/export-manager.tsx +69 -32
  8. package/src/components/editor/fence-tangent-lines-3d.tsx +87 -0
  9. package/src/components/editor/first-person/build-collider-world.test.ts +182 -0
  10. package/src/components/editor/first-person/build-collider-world.ts +228 -83
  11. package/src/components/editor/first-person-controls.tsx +943 -27
  12. package/src/components/editor/floating-action-menu.tsx +676 -208
  13. package/src/components/editor/floating-building-action-menu.tsx +2 -2
  14. package/src/components/editor/floorplan-background-selection.test.ts +58 -0
  15. package/src/components/editor/floorplan-background-selection.ts +3 -2
  16. package/src/components/editor/floorplan-panel.tsx +6916 -12137
  17. package/src/components/editor/grid.tsx +197 -36
  18. package/src/components/editor/group-actions.ts +457 -0
  19. package/src/components/editor/group-floating-action-menu.tsx +130 -0
  20. package/src/components/editor/group-move-3d.ts +397 -0
  21. package/src/components/editor/group-rotate-handle.tsx +420 -0
  22. package/src/components/editor/group-selection-box-3d.tsx +173 -0
  23. package/src/components/editor/group-transform-shared.test.ts +396 -0
  24. package/src/components/editor/group-transform-shared.ts +489 -0
  25. package/src/components/editor/handles/handle-arrow.tsx +581 -0
  26. package/src/components/editor/handles/use-handle-drag.ts +246 -0
  27. package/src/components/editor/index.tsx +412 -162
  28. package/src/components/editor/measurement-pill.tsx +115 -0
  29. package/src/components/editor/node-action-menu.tsx +14 -1
  30. package/src/components/editor/node-arrow-handles.tsx +1646 -0
  31. package/src/components/editor/opening-guides-3d-layer.tsx +144 -0
  32. package/src/components/editor/quick-measurement-card.tsx +70 -0
  33. package/src/components/editor/quick-measurement-hud.tsx +28 -0
  34. package/src/components/editor/riser-diagram-panel.tsx +137 -0
  35. package/src/components/editor/selection-manager.tsx +1106 -483
  36. package/src/components/editor/site-edge-labels.tsx +45 -21
  37. package/src/components/editor/slab-hole-highlights.tsx +474 -0
  38. package/src/components/editor/snapshot-capture-overlay.tsx +369 -155
  39. package/src/components/editor/three-context-bridge.ts +22 -0
  40. package/src/components/editor/thumbnail-generator.tsx +140 -41
  41. package/src/components/editor/use-floorplan-background-placement.ts +190 -35
  42. package/src/components/editor/use-floorplan-hit-testing.ts +24 -0
  43. package/src/components/editor/use-floorplan-scene-data.ts +19 -7
  44. package/src/components/editor/use-mesh-settle-epoch.ts +26 -0
  45. package/src/components/editor/wall-measurement-label.tsx +14 -19
  46. package/src/components/editor/wall-move-side-handles.tsx +870 -0
  47. package/src/components/editor/wall-opening-highlights.tsx +181 -0
  48. package/src/components/editor/wall-snap-beacon-layer.tsx +304 -0
  49. package/src/components/editor-2d/floorplan-action-menu-layer.tsx +15 -6
  50. package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +142 -0
  51. package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +5 -3
  52. package/src/components/editor-2d/floorplan-group-action-menu.tsx +87 -0
  53. package/src/components/editor-2d/floorplan-group-move.tsx +701 -0
  54. package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +113 -0
  55. package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +1656 -0
  56. package/src/components/editor-2d/floorplan-quick-measure-layer.tsx +212 -0
  57. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +392 -0
  58. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +857 -0
  59. package/src/components/editor-2d/floorplan-render-context.tsx +62 -0
  60. package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +77 -0
  61. package/src/components/editor-2d/floorplan-wall-move-ghost-layer.tsx +47 -0
  62. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +266 -0
  63. package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +15 -0
  64. package/src/components/editor-2d/renderers/floorplan-label-angle.ts +14 -0
  65. package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +108 -66
  66. package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +62 -0
  67. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +308 -0
  68. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +3325 -0
  69. package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +24 -9
  70. package/src/components/editor-2d/renderers/floorplan-voronoi-layer.tsx +128 -0
  71. package/src/components/editor-2d/svg-paths.ts +10 -2
  72. package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +439 -57
  73. package/src/components/systems/ceiling/ceiling-system.tsx +109 -12
  74. package/src/components/systems/roof/roof-edit-system.tsx +1851 -22
  75. package/src/components/systems/selection-affordance-manager.tsx +38 -0
  76. package/src/components/systems/stair/stair-edit-system.tsx +1 -1
  77. package/src/components/systems/zone/zone-label-editor-system.tsx +14 -1
  78. package/src/components/systems/zone/zone-system.tsx +42 -13
  79. package/src/components/tools/elevator/elevator-defaults.ts +8 -0
  80. package/src/components/tools/elevator/elevator-tool.tsx +375 -0
  81. package/src/components/tools/elevator/move-elevator-tool.tsx +263 -0
  82. package/src/components/tools/fence/fence-drafting.ts +141 -27
  83. package/src/components/tools/item/move-tool.tsx +45 -105
  84. package/src/components/tools/item/placement-math.test.ts +22 -0
  85. package/src/components/tools/item/placement-math.ts +59 -7
  86. package/src/components/tools/item/placement-strategies.ts +432 -16
  87. package/src/components/tools/item/placement-types.ts +22 -1
  88. package/src/components/tools/item/use-draft-node.ts +51 -2
  89. package/src/components/tools/item/use-placement-coordinator.tsx +1399 -256
  90. package/src/components/tools/registry/move-registry-node-tool.tsx +1064 -0
  91. package/src/components/tools/roof/roof-tool.tsx +350 -28
  92. package/src/components/tools/select/box-select-state.ts +88 -0
  93. package/src/components/tools/select/box-select-tool.tsx +442 -520
  94. package/src/components/tools/select/marquee-geometry.test.ts +161 -0
  95. package/src/components/tools/select/marquee-geometry.ts +155 -0
  96. package/src/components/tools/select/plane-box-select-tool.tsx +570 -0
  97. package/src/components/tools/select/screen-rectangle-selection.ts +84 -0
  98. package/src/components/tools/select/select-candidates.test.ts +109 -0
  99. package/src/components/tools/select/select-candidates.ts +85 -0
  100. package/src/components/tools/shared/affordance-dispatch.ts +30 -0
  101. package/src/components/tools/shared/cursor-sphere.tsx +62 -26
  102. package/src/components/tools/shared/drag-bounding-box.tsx +195 -0
  103. package/src/components/tools/shared/facing-indicator.tsx +87 -0
  104. package/src/components/tools/shared/facing-pose-indicator.tsx +54 -0
  105. package/src/components/tools/shared/floor-stack-preview.ts +25 -0
  106. package/src/components/tools/shared/fresh-placement-visibility.ts +61 -0
  107. package/src/components/tools/shared/placement-box-geometry.ts +123 -0
  108. package/src/components/tools/shared/placement-box.tsx +305 -0
  109. package/src/components/tools/shared/polygon-editor.tsx +866 -175
  110. package/src/components/tools/shared/segment-angle.ts +56 -0
  111. package/src/components/tools/site/site-boundary-editor.tsx +520 -16
  112. package/src/components/tools/site/site-flag-model.ts +18 -0
  113. package/src/components/tools/stair/stair-defaults.ts +2 -1
  114. package/src/components/tools/stair/stair-tool.tsx +342 -37
  115. package/src/components/tools/tool-manager.tsx +239 -62
  116. package/src/components/tools/wall/wall-drafting.test.ts +330 -0
  117. package/src/components/tools/wall/wall-drafting.ts +248 -171
  118. package/src/components/tools/wall/wall-snap-geometry.test.ts +123 -0
  119. package/src/components/tools/wall/wall-snap-geometry.ts +331 -0
  120. package/src/components/tools/zone/zone-boundary-editor.tsx +5 -1
  121. package/src/components/tools/zone/zone-tool.tsx +86 -82
  122. package/src/components/ui/action-menu/action-button.tsx +21 -1
  123. package/src/components/ui/action-menu/camera-actions.tsx +24 -17
  124. package/src/components/ui/action-menu/control-modes.tsx +40 -170
  125. package/src/components/ui/action-menu/furnish-tools.tsx +5 -5
  126. package/src/components/ui/action-menu/index.tsx +3 -117
  127. package/src/components/ui/action-menu/measurement-control.tsx +188 -0
  128. package/src/components/ui/action-menu/structure-tools.tsx +27 -93
  129. package/src/components/ui/action-menu/view-toggles.tsx +279 -90
  130. package/src/components/ui/command-palette/editor-commands.tsx +20 -15
  131. package/src/components/ui/command-palette/index.tsx +5 -3
  132. package/src/components/ui/controls/material-paint-panel.tsx +152 -0
  133. package/src/components/ui/controls/material-picker.tsx +87 -177
  134. package/src/components/ui/controls/material-properties-editor.tsx +108 -0
  135. package/src/components/ui/controls/metric-control.tsx +142 -46
  136. package/src/components/ui/controls/scene-material-list.tsx +247 -0
  137. package/src/components/ui/controls/slider-control.tsx +88 -38
  138. package/src/components/ui/floating-level-selector.tsx +47 -10
  139. package/src/components/ui/helpers/building-helper.tsx +10 -23
  140. package/src/components/ui/helpers/contextual-helper-panel.tsx +428 -0
  141. package/src/components/ui/helpers/helper-manager.tsx +235 -23
  142. package/src/components/ui/helpers/item-helper.tsx +28 -32
  143. package/src/components/ui/helpers/registered-tool-helper.tsx +59 -0
  144. package/src/components/ui/helpers/roof-helper.tsx +10 -12
  145. package/src/components/ui/icon-ref.tsx +47 -0
  146. package/src/components/ui/item-catalog/catalog-items.tsx +40 -1
  147. package/src/components/ui/item-catalog/item-catalog.tsx +16 -36
  148. package/src/components/ui/level-duplicate-dialog.tsx +2 -1
  149. package/src/components/ui/panels/node-display.ts +17 -16
  150. package/src/components/ui/panels/panel-manager.tsx +55 -58
  151. package/src/components/ui/panels/panel-wrapper.tsx +238 -12
  152. package/src/components/ui/panels/parametric-inspector.tsx +482 -0
  153. package/src/components/ui/panels/reference-panel.tsx +54 -19
  154. package/src/components/ui/primitives/dropdown-menu.tsx +12 -8
  155. package/src/components/ui/primitives/shortcut-token.tsx +39 -3
  156. package/src/components/ui/primitives/sidebar.tsx +1 -0
  157. package/src/components/ui/scene-loader.tsx +1 -3
  158. package/src/components/ui/sidebar/app-sidebar.tsx +5 -1
  159. package/src/components/ui/sidebar/icon-rail.tsx +50 -31
  160. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +263 -0
  161. package/src/components/ui/sidebar/panels/items-panel/index.tsx +82 -8
  162. package/src/components/ui/sidebar/panels/plugins-panel.tsx +218 -0
  163. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +122 -41
  164. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +66 -7
  165. package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +267 -0
  166. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -1
  167. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +9 -5
  168. package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +86 -0
  169. package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -1
  170. package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +7 -2
  171. package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +92 -0
  172. package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +75 -0
  173. package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +3 -3
  174. package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +86 -0
  175. package/src/components/ui/sidebar/panels/site-panel/index.tsx +42 -21
  176. package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +31 -17
  177. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -1
  178. package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +140 -0
  179. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +48 -8
  180. package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +132 -0
  181. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +9 -5
  182. package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +86 -0
  183. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +7 -1
  184. package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +2 -2
  185. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +112 -39
  186. package/src/components/ui/sidebar/panels/site-panel/tree-structure.ts +36 -0
  187. package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -1
  188. package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +7 -2
  189. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +3 -3
  190. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +51 -2
  191. package/src/components/ui/sidebar/tab-bar.tsx +75 -1
  192. package/src/components/ui/sidebar/use-plugin-panels.tsx +132 -0
  193. package/src/components/ui/snap-target-badge.test.tsx +40 -0
  194. package/src/components/ui/snap-target-badge.tsx +88 -0
  195. package/src/components/viewer-overlay.tsx +253 -61
  196. package/src/components/viewer-zone-system.tsx +7 -2
  197. package/src/hooks/use-auto-save.test.ts +14 -0
  198. package/src/hooks/use-auto-save.ts +64 -10
  199. package/src/hooks/use-ceiling-events.ts +176 -0
  200. package/src/hooks/use-drag-action.ts +127 -0
  201. package/src/hooks/use-keyboard.ts +493 -95
  202. package/src/hooks/use-selection.ts +64 -0
  203. package/src/index.tsx +451 -6
  204. package/src/lib/active-placement-surface.ts +35 -0
  205. package/src/lib/ceiling-plan-snap.ts +24 -0
  206. package/src/lib/constants.ts +6 -2
  207. package/src/lib/contextual-help.test.ts +112 -0
  208. package/src/lib/contextual-help.ts +144 -0
  209. package/src/lib/continuation.ts +64 -0
  210. package/src/lib/direct-manipulation.test.ts +192 -0
  211. package/src/lib/direct-manipulation.ts +109 -0
  212. package/src/lib/editor-api.ts +50 -0
  213. package/src/lib/elevator-support.ts +96 -0
  214. package/src/lib/floorplan/apply-alignment.test.ts +25 -0
  215. package/src/lib/floorplan/apply-alignment.ts +117 -0
  216. package/src/lib/floorplan/floorplan-export.tsx +364 -0
  217. package/src/lib/floorplan/geometry.ts +53 -0
  218. package/src/lib/floorplan/index.ts +10 -0
  219. package/src/lib/floorplan/items.ts +5 -2
  220. package/src/lib/floorplan/plan-coords.ts +21 -0
  221. package/src/lib/floorplan/selection-tool.ts +40 -0
  222. package/src/lib/floorplan/stairs.ts +12 -1
  223. package/src/lib/fresh-planar-placement.test.ts +339 -0
  224. package/src/lib/fresh-planar-placement.ts +130 -0
  225. package/src/lib/glb-export.test.ts +441 -0
  226. package/src/lib/glb-export.ts +874 -0
  227. package/src/lib/history.ts +11 -1
  228. package/src/lib/interaction/hot-set.test.ts +133 -0
  229. package/src/lib/interaction/hot-set.ts +67 -0
  230. package/src/lib/interaction/overlay-policy.test.ts +51 -0
  231. package/src/lib/interaction/overlay-policy.ts +59 -0
  232. package/src/lib/interaction/scope.ts +211 -0
  233. package/src/lib/level-duplication.ts +54 -42
  234. package/src/lib/level-selection.ts +2 -2
  235. package/src/lib/material-paint.ts +227 -48
  236. package/src/lib/measurement-kind.test.ts +30 -0
  237. package/src/lib/measurement-kind.ts +19 -0
  238. package/src/lib/measurement-label.test.ts +47 -0
  239. package/src/lib/measurement-label.ts +68 -0
  240. package/src/lib/measurement-parser.ts +116 -0
  241. package/src/lib/measurements.test.ts +183 -0
  242. package/src/lib/measurements.ts +199 -0
  243. package/src/lib/paint-scope.test.ts +454 -0
  244. package/src/lib/paint-scope.ts +461 -0
  245. package/src/lib/placement-drag-release.ts +23 -0
  246. package/src/lib/placement-metadata.ts +29 -0
  247. package/src/lib/planar-cursor-placement.test.ts +100 -0
  248. package/src/lib/planar-cursor-placement.ts +42 -0
  249. package/src/lib/plugin-panels.test.ts +19 -0
  250. package/src/lib/plugin-panels.ts +91 -0
  251. package/src/lib/quick-measurement.test.ts +77 -0
  252. package/src/lib/quick-measurement.ts +109 -0
  253. package/src/lib/roof-duplication.ts +7 -7
  254. package/src/lib/roof-hover-outline-proxy.ts +22 -0
  255. package/src/lib/roof-wall-hit.ts +164 -0
  256. package/src/lib/scene-clipboard.test.ts +196 -0
  257. package/src/lib/scene-clipboard.ts +332 -0
  258. package/src/lib/scene.ts +40 -8
  259. package/src/lib/selection-routing.test.ts +298 -0
  260. package/src/lib/selection-routing.ts +175 -0
  261. package/src/lib/sfx/index.ts +1 -0
  262. package/src/lib/sfx/movement-tick.test.ts +65 -0
  263. package/src/lib/sfx/movement-tick.ts +18 -0
  264. package/src/lib/sfx-bus.ts +69 -10
  265. package/src/lib/sfx-player.test.ts +124 -0
  266. package/src/lib/sfx-player.ts +148 -55
  267. package/src/lib/site-boundary.ts +1 -0
  268. package/src/lib/slab-plan-snap.test.ts +93 -0
  269. package/src/lib/slab-plan-snap.ts +108 -0
  270. package/src/lib/snapping-mode.test.ts +138 -0
  271. package/src/lib/snapping-mode.ts +179 -0
  272. package/src/lib/stair-duplication.ts +4 -4
  273. package/src/lib/stair-levels.test.ts +146 -0
  274. package/src/lib/stair-levels.ts +177 -0
  275. package/src/lib/surface-plan-snap.test.ts +71 -0
  276. package/src/lib/surface-plan-snap.ts +256 -0
  277. package/src/lib/use-linear-display.ts +40 -0
  278. package/src/lib/world-grid-snap.ts +231 -0
  279. package/src/lib/zone-content.ts +130 -0
  280. package/src/store/use-alignment-guides.ts +21 -0
  281. package/src/store/use-direct-manipulation-feedback.ts +20 -0
  282. package/src/store/use-editor.tsx +767 -114
  283. package/src/store/use-facing-pose.ts +44 -0
  284. package/src/store/use-fence-curve-draft.ts +21 -0
  285. package/src/store/use-floorplan-draft-preview.ts +101 -0
  286. package/src/store/use-floorplan-marquee.ts +50 -0
  287. package/src/store/use-interaction-scope.test.ts +186 -0
  288. package/src/store/use-interaction-scope.ts +132 -0
  289. package/src/store/use-measurement-draft.test.ts +530 -0
  290. package/src/store/use-measurement-draft.ts +543 -0
  291. package/src/store/use-opening-guides.ts +41 -0
  292. package/src/store/use-placement-preview.ts +39 -0
  293. package/src/store/use-quick-measurement-hud.test.ts +53 -0
  294. package/src/store/use-quick-measurement-hud.ts +77 -0
  295. package/src/store/use-segment-draft-chain.ts +28 -0
  296. package/src/store/use-stair-build-preview.ts +43 -0
  297. package/src/store/use-wall-move-ghosts.ts +36 -0
  298. package/src/store/use-wall-snap-indicator.ts +34 -0
  299. package/src/components/editor/first-person/bvh-ecctrl.tsx +0 -860
  300. package/src/components/editor/preset-thumbnail-generator.tsx +0 -125
  301. package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +0 -113
  302. package/src/components/tools/building/move-building-tool.tsx +0 -157
  303. package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +0 -43
  304. package/src/components/tools/ceiling/ceiling-hole-editor.tsx +0 -49
  305. package/src/components/tools/ceiling/ceiling-tool.tsx +0 -465
  306. package/src/components/tools/ceiling/move-ceiling-tool.tsx +0 -264
  307. package/src/components/tools/column/column-tool.tsx +0 -97
  308. package/src/components/tools/column/move-column-tool.tsx +0 -105
  309. package/src/components/tools/door/door-math.ts +0 -110
  310. package/src/components/tools/door/door-tool.tsx +0 -324
  311. package/src/components/tools/door/move-door-tool.tsx +0 -412
  312. package/src/components/tools/fence/curve-fence-tool.tsx +0 -178
  313. package/src/components/tools/fence/fence-tool.tsx +0 -346
  314. package/src/components/tools/fence/move-fence-endpoint-tool.tsx +0 -441
  315. package/src/components/tools/fence/move-fence-tool.tsx +0 -302
  316. package/src/components/tools/item/item-tool.tsx +0 -26
  317. package/src/components/tools/roof/move-roof-tool.tsx +0 -364
  318. package/src/components/tools/slab/move-slab-tool.tsx +0 -182
  319. package/src/components/tools/slab/slab-boundary-editor.tsx +0 -43
  320. package/src/components/tools/slab/slab-hole-editor.tsx +0 -49
  321. package/src/components/tools/slab/slab-tool.tsx +0 -322
  322. package/src/components/tools/spawn/move-spawn-tool.tsx +0 -101
  323. package/src/components/tools/spawn/spawn-tool.tsx +0 -130
  324. package/src/components/tools/wall/curve-wall-tool.tsx +0 -178
  325. package/src/components/tools/wall/move-wall-endpoint-tool.tsx +0 -426
  326. package/src/components/tools/wall/move-wall-tool.tsx +0 -358
  327. package/src/components/tools/wall/wall-tool.tsx +0 -332
  328. package/src/components/tools/window/move-window-tool.tsx +0 -447
  329. package/src/components/tools/window/window-math.ts +0 -117
  330. package/src/components/tools/window/window-tool.tsx +0 -332
  331. package/src/components/ui/helpers/ceiling-helper.tsx +0 -20
  332. package/src/components/ui/helpers/slab-helper.tsx +0 -20
  333. package/src/components/ui/helpers/wall-helper.tsx +0 -20
  334. package/src/components/ui/panels/ceiling-panel.tsx +0 -249
  335. package/src/components/ui/panels/column-panel.tsx +0 -759
  336. package/src/components/ui/panels/door-panel.tsx +0 -1299
  337. package/src/components/ui/panels/fence-panel.tsx +0 -222
  338. package/src/components/ui/panels/item-panel.tsx +0 -306
  339. package/src/components/ui/panels/paint-panel.tsx +0 -163
  340. package/src/components/ui/panels/presets/presets-popover.tsx +0 -511
  341. package/src/components/ui/panels/roof-panel.tsx +0 -280
  342. package/src/components/ui/panels/roof-segment-panel.tsx +0 -311
  343. package/src/components/ui/panels/slab-panel.tsx +0 -250
  344. package/src/components/ui/panels/spawn-panel.tsx +0 -161
  345. package/src/components/ui/panels/stair-panel.tsx +0 -577
  346. package/src/components/ui/panels/stair-segment-panel.tsx +0 -325
  347. package/src/components/ui/panels/wall-panel.tsx +0 -273
  348. package/src/components/ui/panels/window-panel.tsx +0 -968
  349. package/src/contexts/presets-context.tsx +0 -121
  350. package/src/hooks/use-contextual-tools.ts +0 -61
@@ -0,0 +1,25 @@
1
+ import { type AnyNode, type AnyNodeId, getFloorStackedPosition, useScene } from '@pascal-app/core'
2
+
3
+ type FloorStackPreviewArgs = {
4
+ node: AnyNode
5
+ position: [number, number, number]
6
+ rotation?: unknown
7
+ levelId?: string | null
8
+ nodes?: Record<AnyNodeId, AnyNode>
9
+ }
10
+
11
+ export function getFloorStackPreviewPosition({
12
+ node,
13
+ position,
14
+ rotation,
15
+ levelId,
16
+ nodes,
17
+ }: FloorStackPreviewArgs): [number, number, number] {
18
+ return getFloorStackedPosition({
19
+ node,
20
+ nodes: nodes ?? useScene.getState().nodes,
21
+ position,
22
+ rotation,
23
+ levelId,
24
+ })
25
+ }
@@ -0,0 +1,61 @@
1
+ 'use client'
2
+
3
+ import { type AnyNode, type AnyNodeId, sceneRegistry, useScene } from '@pascal-app/core'
4
+ import { useCallback, useRef, useState } from 'react'
5
+ import { isFreshPlacementMetadata } from '../../../lib/placement-metadata'
6
+ import useEditor from '../../../store/use-editor'
7
+
8
+ type FreshPlacementNode = Pick<AnyNode, 'id' | 'metadata'>
9
+
10
+ type FreshPlacementVisibilityArgs = {
11
+ node: FreshPlacementNode
12
+ enabled?: boolean
13
+ }
14
+
15
+ export function useFreshPlacementVisibility({
16
+ node,
17
+ enabled = true,
18
+ }: FreshPlacementVisibilityArgs) {
19
+ const isFreshPlacement = enabled && isFreshPlacementMetadata(node.metadata)
20
+ const useAbsoluteCursorPlacement = isFreshPlacement && !useEditor.getState().placementDragMode
21
+ const shouldStartHidden = useAbsoluteCursorPlacement
22
+
23
+ const [visibility, setVisibility] = useState(() => ({
24
+ nodeId: node.id,
25
+ visible: !shouldStartHidden,
26
+ }))
27
+ const visibilityRef = useRef(visibility)
28
+ const previewVisible = visibility.nodeId === node.id ? visibility.visible : !shouldStartHidden
29
+
30
+ const setPreviewVisibleForNode = useCallback(
31
+ (visible: boolean) => {
32
+ const current = visibilityRef.current
33
+ if (current.nodeId === node.id && current.visible === visible) return
34
+ const next = { nodeId: node.id, visible }
35
+ visibilityRef.current = next
36
+ setVisibility(next)
37
+ },
38
+ [node.id],
39
+ )
40
+
41
+ const revealFreshPlacement = useCallback(() => {
42
+ if (!isFreshPlacement) return
43
+ setPreviewVisibleForNode(true)
44
+
45
+ sceneRegistry.nodes.get(node.id)?.traverse((child) => {
46
+ child.visible = true
47
+ })
48
+
49
+ const liveNode = useScene.getState().nodes[node.id as AnyNodeId]
50
+ if (liveNode?.visible === false) {
51
+ useScene.getState().updateNode(node.id as AnyNodeId, { visible: true } as Partial<AnyNode>)
52
+ }
53
+ }, [isFreshPlacement, node.id, setPreviewVisibleForNode])
54
+
55
+ return {
56
+ isFreshPlacement,
57
+ previewVisible,
58
+ revealFreshPlacement,
59
+ useAbsoluteCursorPlacement,
60
+ }
61
+ }
@@ -0,0 +1,123 @@
1
+ import { BufferGeometry, Float32BufferAttribute, type LineSegments } from 'three'
2
+
3
+ /**
4
+ * Axis-aligned box description shared by every placement-cursor wireframe.
5
+ * `dimensions` is the box extent on each axis; `center` is its centre in the
6
+ * cursor group's local space (so an off-centre mesh bbox stays off-centre).
7
+ * `min`/`max` are kept for callers that need the explicit corners.
8
+ */
9
+ export type PreviewBounds = {
10
+ min: [number, number, number]
11
+ max: [number, number, number]
12
+ dimensions: [number, number, number]
13
+ center: [number, number, number]
14
+ }
15
+
16
+ export function createLineGeometry(points: number[] = [0, 0, 0, 0, 0, 0]): BufferGeometry {
17
+ const geometry = new BufferGeometry()
18
+ geometry.setAttribute('position', new Float32BufferAttribute(points, 3))
19
+ return geometry
20
+ }
21
+
22
+ /** Flatten a box's 12 edges into a `LineSegments` position array. */
23
+ export function getBoxEdgePoints(bounds: PreviewBounds): number[] {
24
+ const [width, height, depth] = bounds.dimensions
25
+ const [centerX, centerY, centerZ] = bounds.center
26
+ const minX = centerX - width / 2
27
+ const maxX = centerX + width / 2
28
+ const minY = centerY - height / 2
29
+ const maxY = centerY + height / 2
30
+ const minZ = centerZ - depth / 2
31
+ const maxZ = centerZ + depth / 2
32
+
33
+ return [
34
+ minX,
35
+ minY,
36
+ minZ,
37
+ maxX,
38
+ minY,
39
+ minZ,
40
+ maxX,
41
+ minY,
42
+ minZ,
43
+ maxX,
44
+ minY,
45
+ maxZ,
46
+ maxX,
47
+ minY,
48
+ maxZ,
49
+ minX,
50
+ minY,
51
+ maxZ,
52
+ minX,
53
+ minY,
54
+ maxZ,
55
+ minX,
56
+ minY,
57
+ minZ,
58
+
59
+ minX,
60
+ maxY,
61
+ minZ,
62
+ maxX,
63
+ maxY,
64
+ minZ,
65
+ maxX,
66
+ maxY,
67
+ minZ,
68
+ maxX,
69
+ maxY,
70
+ maxZ,
71
+ maxX,
72
+ maxY,
73
+ maxZ,
74
+ minX,
75
+ maxY,
76
+ maxZ,
77
+ minX,
78
+ maxY,
79
+ maxZ,
80
+ minX,
81
+ maxY,
82
+ minZ,
83
+
84
+ minX,
85
+ minY,
86
+ minZ,
87
+ minX,
88
+ maxY,
89
+ minZ,
90
+ maxX,
91
+ minY,
92
+ minZ,
93
+ maxX,
94
+ maxY,
95
+ minZ,
96
+ maxX,
97
+ minY,
98
+ maxZ,
99
+ maxX,
100
+ maxY,
101
+ maxZ,
102
+ minX,
103
+ minY,
104
+ maxZ,
105
+ minX,
106
+ maxY,
107
+ maxZ,
108
+ ]
109
+ }
110
+
111
+ export function updateLineGeometry(ref: React.RefObject<LineSegments>, points: number[]) {
112
+ const geometry = ref.current?.geometry
113
+ if (!geometry) return
114
+
115
+ const attribute = geometry.getAttribute('position') as Float32BufferAttribute | undefined
116
+ if (!attribute || attribute.array.length !== points.length) {
117
+ geometry.setAttribute('position', new Float32BufferAttribute(points, 3))
118
+ } else {
119
+ attribute.set(points)
120
+ attribute.needsUpdate = true
121
+ }
122
+ geometry.computeBoundingSphere()
123
+ }
@@ -0,0 +1,305 @@
1
+ 'use client'
2
+
3
+ import '../../../three-types'
4
+
5
+ import { Html } from '@react-three/drei'
6
+ import { useEffect, useMemo } from 'react'
7
+ import { PlaneGeometry } from 'three'
8
+ import { distance, smoothstep, uv, vec2 } from 'three/tsl'
9
+ import { LineBasicNodeMaterial, MeshBasicNodeMaterial } from 'three/webgpu'
10
+ import { EDITOR_LAYER } from '../../../lib/constants'
11
+ import { formatLinearMeasurement, type LinearUnit } from '../../../lib/measurements'
12
+ import { createLineGeometry, getBoxEdgePoints } from './placement-box-geometry'
13
+
14
+ const VALID_COLOR = 0x22_c5_5e // green-500
15
+ const INVALID_COLOR = 0xef_44_44 // red-500
16
+ const MEASUREMENT_COLOR = 0x0f_17_2a
17
+
18
+ type PlacementBoxMeasurements = {
19
+ unit: LinearUnit
20
+ }
21
+
22
+ function getMeasurementGuidePoints(width: number, height: number, depth: number) {
23
+ const minX = -width / 2
24
+ const maxX = width / 2
25
+ const minZ = -depth / 2
26
+ const maxZ = depth / 2
27
+ const guideOffset = 0.18
28
+ const tick = 0.08
29
+ const y = 0.02
30
+
31
+ return {
32
+ depth: [
33
+ maxX + guideOffset,
34
+ y,
35
+ minZ,
36
+ maxX + guideOffset,
37
+ y,
38
+ maxZ,
39
+
40
+ maxX + guideOffset - tick,
41
+ y,
42
+ minZ,
43
+ maxX + guideOffset + tick,
44
+ y,
45
+ minZ,
46
+
47
+ maxX + guideOffset - tick,
48
+ y,
49
+ maxZ,
50
+ maxX + guideOffset + tick,
51
+ y,
52
+ maxZ,
53
+ ],
54
+ height: [
55
+ minX - guideOffset,
56
+ 0,
57
+ minZ,
58
+ minX - guideOffset,
59
+ height,
60
+ minZ,
61
+
62
+ minX - guideOffset - tick,
63
+ 0,
64
+ minZ,
65
+ minX - guideOffset + tick,
66
+ 0,
67
+ minZ,
68
+
69
+ minX - guideOffset - tick,
70
+ height,
71
+ minZ,
72
+ minX - guideOffset + tick,
73
+ height,
74
+ minZ,
75
+ ],
76
+ width: [
77
+ minX,
78
+ y,
79
+ maxZ + guideOffset,
80
+ maxX,
81
+ y,
82
+ maxZ + guideOffset,
83
+
84
+ minX,
85
+ y,
86
+ maxZ + guideOffset - tick,
87
+ minX,
88
+ y,
89
+ maxZ + guideOffset + tick,
90
+
91
+ maxX,
92
+ y,
93
+ maxZ + guideOffset - tick,
94
+ maxX,
95
+ y,
96
+ maxZ + guideOffset + tick,
97
+ ],
98
+ }
99
+ }
100
+
101
+ function MeasurementPill({
102
+ label,
103
+ position,
104
+ }: {
105
+ label: string
106
+ position: [number, number, number]
107
+ }) {
108
+ return (
109
+ <Html center position={position} style={{ pointerEvents: 'none' }}>
110
+ <div
111
+ style={{
112
+ background: 'rgba(15, 23, 42, 0.86)',
113
+ border: '1px solid rgba(15, 23, 42, 0.65)',
114
+ borderRadius: '999px',
115
+ color: '#f8fafc',
116
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
117
+ fontSize: '11px',
118
+ fontWeight: 600,
119
+ lineHeight: 1,
120
+ padding: '4px 8px',
121
+ pointerEvents: 'none',
122
+ whiteSpace: 'nowrap',
123
+ }}
124
+ >
125
+ {label}
126
+ </div>
127
+ </Html>
128
+ )
129
+ }
130
+
131
+ /**
132
+ * Green/red placement footprint shown while a node follows the cursor — the
133
+ * same wireframe-box + radial base plane the GLB item tool draws (geometry
134
+ * helpers shared via `placement-box-geometry`). Unlike the item coordinator's
135
+ * imperative cursor (it mutates module-singleton materials in a `useFrame`
136
+ * loop), this is a declarative, React-driven box: the caller passes the live
137
+ * `position` / `rotationY` / `valid` and the box re-renders. Its own materials
138
+ * are instanced per-mount so it never fights the item tool's singletons.
139
+ *
140
+ * The box is centred on its footprint in X/Z and sits on the floor (its base
141
+ * at the group origin's Y), so a node whose local origin is floor-level — like
142
+ * a shelf — lines up without an extra offset.
143
+ */
144
+ export function PlacementBox({
145
+ dimensions,
146
+ measurements,
147
+ position,
148
+ rotationY = 0,
149
+ valid,
150
+ }: {
151
+ /** Footprint extent `[width, height, depth]` (unrotated). */
152
+ dimensions: [number, number, number]
153
+ /** Optional dimension guide labels matching the GLB item placement cursor. */
154
+ measurements?: PlacementBoxMeasurements
155
+ /** World-plan position of the footprint centre (floor level). */
156
+ position: [number, number, number]
157
+ /** Y-rotation in radians, applied to the whole box. */
158
+ rotationY?: number
159
+ /** Drives the colour: green when placeable, red otherwise. */
160
+ valid: boolean
161
+ }) {
162
+ const [width, height, depth] = dimensions
163
+
164
+ const edgeGeometry = useMemo(
165
+ () =>
166
+ createLineGeometry(
167
+ getBoxEdgePoints({
168
+ min: [-width / 2, 0, -depth / 2],
169
+ max: [width / 2, height, depth / 2],
170
+ dimensions: [width, height, depth],
171
+ center: [0, height / 2, 0],
172
+ }),
173
+ ),
174
+ [width, height, depth],
175
+ )
176
+
177
+ const basePlaneGeometry = useMemo(() => {
178
+ const geometry = new PlaneGeometry(width, depth)
179
+ geometry.rotateX(-Math.PI / 2)
180
+ geometry.translate(0, 0.01, 0)
181
+ return geometry
182
+ }, [width, depth])
183
+ const measurementGuideGeometries = useMemo(() => {
184
+ const points = getMeasurementGuidePoints(width, height, depth)
185
+ return {
186
+ depth: createLineGeometry(points.depth),
187
+ height: createLineGeometry(points.height),
188
+ width: createLineGeometry(points.width),
189
+ }
190
+ }, [width, height, depth])
191
+
192
+ const edgeMaterial = useMemo(
193
+ () => new LineBasicNodeMaterial({ linewidth: 3, depthTest: false, depthWrite: false }),
194
+ [],
195
+ )
196
+ const basePlaneMaterial = useMemo(() => {
197
+ const material = new MeshBasicNodeMaterial({
198
+ transparent: true,
199
+ depthTest: false,
200
+ depthWrite: false,
201
+ })
202
+ // Radial opacity: transparent in the centre, opaque toward the edges —
203
+ // matches the item placement base plane.
204
+ material.opacityNode = smoothstep(0, 0.7, distance(uv(), vec2(0.5, 0.5))).mul(0.6)
205
+ return material
206
+ }, [])
207
+ const measurementMaterial = useMemo(
208
+ () =>
209
+ new LineBasicNodeMaterial({
210
+ color: MEASUREMENT_COLOR,
211
+ linewidth: 2,
212
+ depthTest: false,
213
+ depthWrite: false,
214
+ }),
215
+ [],
216
+ )
217
+
218
+ useEffect(() => {
219
+ const color = valid ? VALID_COLOR : INVALID_COLOR
220
+ edgeMaterial.color.setHex(color)
221
+ basePlaneMaterial.color.setHex(color)
222
+ }, [valid, edgeMaterial, basePlaneMaterial])
223
+
224
+ useEffect(
225
+ () => () => {
226
+ edgeGeometry.dispose()
227
+ },
228
+ [edgeGeometry],
229
+ )
230
+ useEffect(
231
+ () => () => {
232
+ measurementGuideGeometries.depth.dispose()
233
+ measurementGuideGeometries.height.dispose()
234
+ measurementGuideGeometries.width.dispose()
235
+ },
236
+ [measurementGuideGeometries],
237
+ )
238
+ useEffect(
239
+ () => () => {
240
+ basePlaneGeometry.dispose()
241
+ },
242
+ [basePlaneGeometry],
243
+ )
244
+ useEffect(
245
+ () => () => {
246
+ edgeMaterial.dispose()
247
+ basePlaneMaterial.dispose()
248
+ measurementMaterial.dispose()
249
+ },
250
+ [edgeMaterial, basePlaneMaterial, measurementMaterial],
251
+ )
252
+
253
+ const measurementContent = measurements ? (
254
+ <>
255
+ <lineSegments
256
+ geometry={measurementGuideGeometries.width}
257
+ layers={EDITOR_LAYER}
258
+ material={measurementMaterial}
259
+ renderOrder={998}
260
+ />
261
+ <lineSegments
262
+ geometry={measurementGuideGeometries.depth}
263
+ layers={EDITOR_LAYER}
264
+ material={measurementMaterial}
265
+ renderOrder={998}
266
+ />
267
+ <lineSegments
268
+ geometry={measurementGuideGeometries.height}
269
+ layers={EDITOR_LAYER}
270
+ material={measurementMaterial}
271
+ renderOrder={998}
272
+ />
273
+ <MeasurementPill
274
+ label={formatLinearMeasurement(width, measurements.unit)}
275
+ position={[0, 0.04, depth / 2 + 0.24]}
276
+ />
277
+ <MeasurementPill
278
+ label={formatLinearMeasurement(depth, measurements.unit)}
279
+ position={[width / 2 + 0.24, 0.04, 0]}
280
+ />
281
+ <MeasurementPill
282
+ label={formatLinearMeasurement(height, measurements.unit)}
283
+ position={[-width / 2 - 0.24, height / 2, -depth / 2]}
284
+ />
285
+ </>
286
+ ) : null
287
+
288
+ return (
289
+ <group position={position} rotation={[0, rotationY, 0]}>
290
+ <lineSegments
291
+ geometry={edgeGeometry}
292
+ layers={EDITOR_LAYER}
293
+ material={edgeMaterial}
294
+ renderOrder={999}
295
+ />
296
+ {measurementContent}
297
+ <mesh
298
+ geometry={basePlaneGeometry}
299
+ layers={EDITOR_LAYER}
300
+ material={basePlaneMaterial}
301
+ renderOrder={999}
302
+ />
303
+ </group>
304
+ )
305
+ }