@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
@@ -1,14 +1,28 @@
1
1
  'use client'
2
2
 
3
- import { emitter, type GridEvent, sceneRegistry } from '@pascal-app/core'
4
- import { useViewer } from '@pascal-app/viewer'
3
+ import { type AnyNodeId, emitter, type GridEvent, sceneRegistry } from '@pascal-app/core'
4
+ import { GRID_LAYER, getSceneTheme, useViewer } from '@pascal-app/viewer'
5
5
  import { useFrame } from '@react-three/fiber'
6
6
  import { useEffect, useMemo, useRef, useState } from 'react'
7
- import { MathUtils, type Mesh, Vector2 } from 'three'
7
+ import { DoubleSide, type Mesh, PlaneGeometry, Quaternion, Vector2, Vector3 } from 'three'
8
8
  import { color, float, fract, fwidth, mix, positionLocal, uniform } from 'three/tsl'
9
9
  import { MeshBasicNodeMaterial } from 'three/webgpu'
10
+ import { useCeilingEvents } from '../../hooks/use-ceiling-events'
10
11
  import { useGridEvents } from '../../hooks/use-grid-events'
11
- import { EDITOR_LAYER } from '../../lib/constants'
12
+ import { getPlacementSurface } from '../../lib/active-placement-surface'
13
+ import useEditor, { isGridSnapActive } from '../../store/use-editor'
14
+ import { getMovingNode } from '../../store/use-interaction-scope'
15
+
16
+ // Reveal radius (m) of the cursor-local grid patch shown while placing/moving in
17
+ // grid-snap mode — much tighter than the idle reveal so only the area you're
18
+ // about to snap into lights up.
19
+ const PLACEMENT_REVEAL_RADIUS = 12
20
+
21
+ const UP = new Vector3(0, 1, 0)
22
+ // PlaneGeometry faces +Z; this is the orientation that lays it flat (its normal
23
+ // → world +Y), equivalent to the old `rotation-x={-π/2}`.
24
+ const PLANE_LOCAL_NORMAL = new Vector3(0, 0, 1)
25
+ const HORIZONTAL_QUATERNION = new Quaternion().setFromUnitVectors(PLANE_LOCAL_NORMAL, UP)
12
26
 
13
27
  export const Grid = ({
14
28
  cellSize = 0.5,
@@ -31,13 +45,37 @@ export const Grid = ({
31
45
  fadeStrength?: number
32
46
  revealRadius?: number
33
47
  }) => {
34
- const theme = useViewer((state) => state.theme)
48
+ const isDark = useViewer((state) => getSceneTheme(state.sceneTheme).appearance === 'dark')
35
49
 
36
- // Use slightly lighter colors for dark mode grid to make it apparent
37
- const effectiveCellColor = theme === 'dark' ? '#555566' : cellColor
38
- const effectiveSectionColor = theme === 'dark' ? '#666677' : sectionColor
50
+ // Use slightly lighter colors for dark themes' grid to make it apparent
51
+ const effectiveCellColor = isDark ? '#555566' : cellColor
52
+ const effectiveSectionColor = isDark ? '#666677' : sectionColor
39
53
 
40
54
  const cursorPositionRef = useRef(new Vector2(0, 0))
55
+ // Scratch for reading a moving node's world Y (surface elevation) each frame.
56
+ const worldPosRef = useRef(new Vector3())
57
+ // Scratch for the wall-anchored branch: invert the plane orientation to map the
58
+ // ghost into plane-local XY (the cursor reveal) without re-centring the mesh.
59
+ const invQuatRef = useRef(new Quaternion())
60
+ const wallCursorRef = useRef(new Vector3())
61
+ // Scratch for deriving a moving wall-item's host normal from its mesh.
62
+ const wallNormalRef = useRef(new Vector3())
63
+ // Last Y pushed to `gridY` state, so the per-frame surface follow only triggers
64
+ // a React re-render when the height actually changes (not every frame).
65
+ const lastGridYRef = useRef<number | null>(null)
66
+
67
+ // Reveal radius + baseline alpha are uniforms so a placement/move can shrink
68
+ // the grid to a tight cursor patch (and drop the always-on baseline) without
69
+ // rebuilding the shader. Driven each frame in `useFrame`.
70
+ const revealRadiusUniform = useMemo(() => uniform(revealRadius), [revealRadius])
71
+ const baseAlphaUniform = useMemo(() => uniform(0.4), [])
72
+ // Created once and driven by `.value` each frame (see `useFrame`). Keying this
73
+ // on `cellSize` rebuilt the uniform AND the material `useMemo` below on every
74
+ // `gridSnapStep` change — a full shader recompile that stalled hard whenever
75
+ // the grid resolution changed. The live cell size is a uniform write only.
76
+ // biome-ignore lint/correctness/useExhaustiveDependencies: created once on purpose; `.value` is driven each frame.
77
+ const cellSizeUniform = useMemo(() => uniform(cellSize), [])
78
+ const patchAlphaUniform = useMemo(() => uniform(1), [])
41
79
 
42
80
  const material = useMemo(() => {
43
81
  // Use xy since plane geometry is in XY space (before rotation)
@@ -48,7 +86,7 @@ export const Grid = ({
48
86
 
49
87
  // Grid line function using fwidth for anti-aliasing
50
88
  // Returns 1 on grid lines, 0 elsewhere
51
- const getGrid = (size: number, thickness: number) => {
89
+ const getGrid = (size: number | typeof cellSizeUniform, thickness: number) => {
52
90
  const r = pos.div(size)
53
91
  const fw = fwidth(r)
54
92
  // Distance to nearest grid line for each axis
@@ -70,7 +108,7 @@ export const Grid = ({
70
108
  return lineX.max(lineY)
71
109
  }
72
110
 
73
- const g1 = getGrid(cellSize, cellThickness)
111
+ const g1 = getGrid(cellSizeUniform, cellThickness)
74
112
  const g2 = getGrid(sectionSize, sectionThickness)
75
113
 
76
114
  // Distance fade from center
@@ -79,7 +117,9 @@ export const Grid = ({
79
117
 
80
118
  // Cursor reveal effect - distance from cursor
81
119
  const cursorDist = pos.sub(cursorPos).length()
82
- const cursorFade = float(1).sub(cursorDist.div(revealRadius).clamp(0, 1)).smoothstep(0, 1)
120
+ const cursorFade = float(1)
121
+ .sub(cursorDist.div(revealRadiusUniform).clamp(0, 1))
122
+ .smoothstep(0, 1)
83
123
 
84
124
  // Mix colors based on section grid
85
125
  const gridColor = mix(
@@ -88,21 +128,26 @@ export const Grid = ({
88
128
  float(sectionThickness).mul(g2).min(1),
89
129
  )
90
130
 
91
- // Baseline alpha: small amount of opacity everywhere the grid exists
92
- const baseAlpha = float(0.4) // Subtle global visibility
93
-
94
131
  // Combined alpha with cursor fade and baseline minimum
95
- const alpha = g1.add(g2).mul(fade).mul(cursorFade.max(baseAlpha))
96
- const finalAlpha = mix(alpha.mul(0.75), alpha, g2)
132
+ const alpha = g1.add(g2).mul(fade).mul(cursorFade.max(baseAlphaUniform))
133
+ const boostedAlpha = alpha.mul(patchAlphaUniform).min(1)
134
+ const finalAlpha = mix(boostedAlpha.mul(0.75), boostedAlpha, g2)
97
135
 
98
136
  return new MeshBasicNodeMaterial({
99
137
  transparent: true,
100
138
  colorNode: gridColor,
101
139
  opacityNode: finalAlpha,
102
140
  depthWrite: false,
141
+ // `depthTest` is toggled per-frame in `useFrame`: ON for the floor lattice
142
+ // (so the ground occludes a sub-floor grid) and OFF on a wall (so the
143
+ // lattice shows through the wall when the opening is handled from the far
144
+ // side). Default ON for the floor case.
145
+ depthTest: true,
146
+ // Wall-plane placements are handled from either side of the wall, so the
147
+ // lattice must render from both faces.
148
+ side: DoubleSide,
103
149
  })
104
150
  }, [
105
- cellSize,
106
151
  cellThickness,
107
152
  effectiveCellColor,
108
153
  sectionSize,
@@ -110,7 +155,10 @@ export const Grid = ({
110
155
  effectiveSectionColor,
111
156
  fadeDistance,
112
157
  fadeStrength,
113
- revealRadius,
158
+ revealRadiusUniform,
159
+ baseAlphaUniform,
160
+ cellSizeUniform,
161
+ patchAlphaUniform,
114
162
  ])
115
163
 
116
164
  const gridRef = useRef<Mesh>(null!)
@@ -118,11 +166,20 @@ export const Grid = ({
118
166
 
119
167
  // Use custom raycasting for grid events (independent of mesh events)
120
168
  useGridEvents(gridY)
169
+ // Same technique for ceiling-item placement: a math-plane raycast per ceiling,
170
+ // so commits don't depend on hitting the thin, single-sided `ceiling-grid`
171
+ // overlay mesh (which dropped clicks even with the green box showing).
172
+ useCeilingEvents()
121
173
 
122
- // Update cursor position from grid:move events
174
+ // Track the last world-space cursor hit. The reveal-fade shader reads
175
+ // `positionLocal.xy` (vertex position on the un-transformed plane), and the
176
+ // laid-flat orientation maps `positionLocal.y` to world `-Z` relative to the
177
+ // mesh origin. The cursor is recomputed every frame from the stored world hit
178
+ // so the reveal stays put regardless of where the mesh origin sits.
179
+ const lastWorldCursorRef = useRef<{ x: number; z: number } | null>(null)
123
180
  useEffect(() => {
124
181
  const onGridMove = (event: GridEvent) => {
125
- cursorPositionRef.current.set(event.position[0], -event.position[2])
182
+ lastWorldCursorRef.current = { x: event.position[0], z: event.position[2] }
126
183
  }
127
184
 
128
185
  emitter.on('grid:move', onGridMove)
@@ -131,31 +188,135 @@ export const Grid = ({
131
188
  }
132
189
  }, [])
133
190
 
134
- useFrame((_, delta) => {
135
- const currentLevelId = useViewer.getState().selection.levelId
136
- let targetY = 0
137
- if (currentLevelId) {
138
- const levelMesh = sceneRegistry.nodes.get(currentLevelId)
191
+ useFrame(() => {
192
+ const { levelId } = useViewer.getState().selection
193
+ let levelY = 0
194
+ if (levelId) {
195
+ const levelMesh = sceneRegistry.nodes.get(levelId)
139
196
  if (levelMesh) {
140
- targetY = levelMesh.position.y
197
+ levelY = levelMesh.position.y
198
+ }
199
+ }
200
+
201
+ // Resolve the surface the active ghost is snapped to (contact point +
202
+ // normal). A fresh GLB item / drawn kind publishes via the surface module; a
203
+ // moving node is read straight off its mesh (treated as horizontal). Null
204
+ // when nothing is being placed.
205
+ const published = getPlacementSurface()
206
+ const movingForGrid = getMovingNode()
207
+ let surfacePoint: Vector3 | null = null
208
+ let surfaceNormal = UP
209
+ if (published) {
210
+ surfacePoint = published.point
211
+ surfaceNormal = published.normal
212
+ } else if (movingForGrid) {
213
+ const ghostMesh = sceneRegistry.nodes.get(movingForGrid.id as AnyNodeId)
214
+ if (ghostMesh) {
215
+ surfacePoint = ghostMesh.getWorldPosition(worldPosRef.current)
216
+ // A wall-hosted item read off its mesh gets its host's orientation
217
+ // immediately: its local +Z faces out of the wall (same convention the
218
+ // placement coordinator publishes). Without this the grid flashes
219
+ // horizontal on move-start until the first pointer move publishes a
220
+ // wall surface.
221
+ const attachTo = movingForGrid.type === 'item' ? movingForGrid.asset?.attachTo : undefined
222
+ if (attachTo === 'wall' || attachTo === 'wall-side') {
223
+ ghostMesh.getWorldQuaternion(invQuatRef.current)
224
+ const fwd = wallNormalRef.current.set(0, 0, 1).applyQuaternion(invQuatRef.current)
225
+ fwd.y = 0
226
+ if (fwd.lengthSq() > 1e-6) surfaceNormal = fwd.normalize()
227
+ }
228
+ }
229
+ }
230
+
231
+ const gridMesh = gridRef.current
232
+ const onWall = surfacePoint != null && Math.abs(surfaceNormal.y) < 0.5
233
+ if (onWall && surfacePoint) {
234
+ // Wall-anchored lattice: orient the plane into the wall and pin the mesh to
235
+ // the plane's FOOT (the point on the wall plane closest to the world origin)
236
+ // — never the moving ghost. Sliding the opening along the wall then only
237
+ // moves the reveal patch (the cursor uniform); the snap lattice stays put.
238
+ // (Copying `surfacePoint` here made the grid follow the item — useless.)
239
+ gridMesh.quaternion.setFromUnitVectors(PLANE_LOCAL_NORMAL, surfaceNormal)
240
+ const planeOffset = surfacePoint.dot(surfaceNormal)
241
+ gridMesh.position.copy(surfaceNormal).multiplyScalar(planeOffset)
242
+ // Cursor → plane-local XY: rotate (ghost − anchor) by the inverse plane
243
+ // orientation. Both lie in the plane, so the resulting local Z is ~0.
244
+ invQuatRef.current.copy(gridMesh.quaternion).invert()
245
+ wallCursorRef.current
246
+ .copy(surfacePoint)
247
+ .sub(gridMesh.position)
248
+ .applyQuaternion(invQuatRef.current)
249
+ cursorPositionRef.current.set(wallCursorRef.current.x, wallCursorRef.current.y)
250
+ if (lastGridYRef.current !== surfacePoint.y) {
251
+ lastGridYRef.current = surfacePoint.y
252
+ setGridY(surfacePoint.y)
253
+ }
254
+ } else {
255
+ // Horizontal: keep the lattice anchored to world XZ (0,0); only the Y
256
+ // origin follows the surface height (floor / shelf top). Snap directly —
257
+ // the old lerp made the grid visibly drift up to a new floor height.
258
+ // Cursor uniform tracks the world cursor (mirrored on Z for the flat plane).
259
+ const targetY = surfacePoint ? surfacePoint.y : levelY
260
+ // Visual mesh rides 1mm above the surface: at exactly the surface Y the
261
+ // lattice is coplanar with the slab top and z-fights it. The event plane
262
+ // (`gridY` → useGridEvents) stays at the true surface height.
263
+ gridMesh.position.set(0, targetY + 0.001, 0)
264
+ gridMesh.quaternion.copy(HORIZONTAL_QUATERNION)
265
+ const world = lastWorldCursorRef.current
266
+ if (world) {
267
+ cursorPositionRef.current.set(world.x, -world.z)
268
+ }
269
+ if (lastGridYRef.current !== targetY) {
270
+ lastGridYRef.current = targetY
271
+ setGridY(targetY)
141
272
  }
142
273
  }
143
- const newY = MathUtils.lerp(gridRef.current.position.y, targetY, 12 * delta)
144
- gridRef.current.position.y = newY
145
- setGridY(newY)
274
+
275
+ // Floor grid depth-tests against the scene (ground occludes a sub-floor
276
+ // lattice); the wall grid ignores depth so it stays visible through the wall
277
+ // when the opening is being handled from the opposite side.
278
+ if (material.depthTest === onWall) {
279
+ material.depthTest = !onWall
280
+ material.needsUpdate = true
281
+ }
282
+
283
+ // The grid is a placement aid: a tight cursor patch (no always-on baseline)
284
+ // shown whenever the active context is in grid-snap mode — ANY armed
285
+ // draft/build tool (wall / slab / fence / ceiling / zone / column / MEP / …),
286
+ // a node move, or a reshape — and hidden in select/idle, paint, and non-grid
287
+ // (lines/off) modes. `isGridSnapActive()` already derives the snap context
288
+ // from the interaction scope OR the armed build tool and is true only when
289
+ // that context resolves to grid, so it IS the gate. (Previously this also
290
+ // required a ghost in flight, so a merely-armed draft tool showed nothing.)
291
+ const snapPatchVisible = isGridSnapActive()
292
+ revealRadiusUniform.value = PLACEMENT_REVEAL_RADIUS
293
+ baseAlphaUniform.value = 0
294
+ cellSizeUniform.value = useEditor.getState().gridSnapStep
295
+ patchAlphaUniform.value = 1.5
296
+ gridRef.current.visible = snapPatchVisible
146
297
  })
147
298
 
148
- const showGrid = useViewer((state) => state.showGrid)
299
+ // Pass the geometry as a prop instead of a JSX child so the mesh
300
+ // is never reconciled with R3F's empty placeholder `BufferGeometry`.
301
+ // Combined with the grid's `MeshBasicNodeMaterial`, the child-attach
302
+ // path can submit a `Draw(0, 1, 0, 0)` on the first frame before
303
+ // `<planeGeometry>` attaches — which WebGPU flags as "Vertex buffer
304
+ // slot 0 ... was not set" (see `wall-move-side-handles.tsx`).
305
+ const geometry = useMemo(
306
+ () => new PlaneGeometry(fadeDistance * 2, fadeDistance * 2),
307
+ [fadeDistance],
308
+ )
309
+ useEffect(() => () => geometry.dispose(), [geometry])
149
310
 
150
311
  return (
312
+ // Orientation is driven imperatively in `useFrame` (horizontal by default,
313
+ // tilted into the wall plane while placing on a wall), so no static rotation.
151
314
  <mesh
152
- layers={EDITOR_LAYER}
315
+ geometry={geometry}
316
+ layers={GRID_LAYER}
153
317
  material={material}
154
318
  ref={gridRef}
155
- rotation-x={-Math.PI / 2}
156
- visible={showGrid}
157
- >
158
- <planeGeometry args={[fadeDistance * 2, fadeDistance * 2]} />
159
- </mesh>
319
+ renderOrder={1}
320
+ />
160
321
  )
161
322
  }