@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
@@ -2,9 +2,14 @@
2
2
 
3
3
  import { emitter } from '@pascal-app/core'
4
4
  import Image from 'next/image'
5
+ import useEditor from '../../../store/use-editor'
5
6
  import { ActionButton } from './action-button'
6
7
 
7
8
  export function CameraActions({ hideOrbit = false }: { hideOrbit?: boolean }) {
9
+ // Orbit stays useful in 2D-only (it spins the synced floorplan view), but
10
+ // top view only tilts the hidden 3D camera — pointless without the canvas.
11
+ const is2dOnly = useEditor((s) => s.viewMode === '2d')
12
+
8
13
  const goToTopView = () => {
9
14
  emitter.emit('camera-controls:top-view')
10
15
  }
@@ -33,7 +38,7 @@ export function CameraActions({ hideOrbit = false }: { hideOrbit?: boolean }) {
33
38
  alt="Orbit Left"
34
39
  className="h-[28px] w-[28px] -scale-x-100 object-contain opacity-70 transition-opacity group-hover:opacity-100"
35
40
  height={28}
36
- src="/icons/rotate.png"
41
+ src="/icons/rotate.webp"
37
42
  width={28}
38
43
  />
39
44
  </ActionButton>
@@ -50,7 +55,7 @@ export function CameraActions({ hideOrbit = false }: { hideOrbit?: boolean }) {
50
55
  alt="Orbit Right"
51
56
  className="h-[28px] w-[28px] object-contain opacity-70 transition-opacity group-hover:opacity-100"
52
57
  height={28}
53
- src="/icons/rotate.png"
58
+ src="/icons/rotate.webp"
54
59
  width={28}
55
60
  />
56
61
  </ActionButton>
@@ -58,21 +63,23 @@ export function CameraActions({ hideOrbit = false }: { hideOrbit?: boolean }) {
58
63
  )}
59
64
 
60
65
  {/* Top View */}
61
- <ActionButton
62
- className="group hover:bg-white/5"
63
- label="Top View"
64
- onClick={goToTopView}
65
- size="icon"
66
- variant="ghost"
67
- >
68
- <Image
69
- alt="Top View"
70
- className="h-[28px] w-[28px] object-contain opacity-70 transition-opacity group-hover:opacity-100"
71
- height={28}
72
- src="/icons/topview.png"
73
- width={28}
74
- />
75
- </ActionButton>
66
+ {!is2dOnly && (
67
+ <ActionButton
68
+ className="group hover:bg-white/5"
69
+ label="Top View"
70
+ onClick={goToTopView}
71
+ size="icon"
72
+ variant="ghost"
73
+ >
74
+ <Image
75
+ alt="Top View"
76
+ className="h-[28px] w-[28px] object-contain opacity-70 transition-opacity group-hover:opacity-100"
77
+ height={28}
78
+ src="/icons/topview.webp"
79
+ width={28}
80
+ />
81
+ </ActionButton>
82
+ )}
76
83
  </div>
77
84
  )
78
85
  }
@@ -1,23 +1,15 @@
1
1
  'use client'
2
2
 
3
3
  import { Icon } from '@iconify/react'
4
- import { type LevelNode, useScene } from '@pascal-app/core'
5
- import { useViewer } from '@pascal-app/viewer'
6
4
  import { type LucideIcon, Trash2 } from 'lucide-react'
7
5
  import Image from 'next/image'
6
+ import { Fragment } from 'react'
8
7
  import { cn } from './../../../lib/utils'
9
8
  import useEditor from './../../../store/use-editor'
10
9
  import { ActionButton } from './action-button'
10
+ import { MeasurementControl } from './measurement-control'
11
11
 
12
- type ControlId =
13
- | 'select'
14
- | 'box-select'
15
- | 'site-edit'
16
- | 'build'
17
- | 'material-paint'
18
- | 'furnish'
19
- | 'zone'
20
- | 'delete'
12
+ type ControlId = 'select' | 'box-select' | 'zone' | 'delete'
21
13
 
22
14
  type ControlConfig = {
23
15
  id: ControlId
@@ -34,53 +26,15 @@ type ControlConfig = {
34
26
  const controls: ControlConfig[] = [
35
27
  {
36
28
  id: 'select',
37
- imageSrc: '/icons/select.png',
29
+ imageSrc: '/icons/select.webp',
38
30
  label: 'Select',
39
31
  shortcut: 'V',
40
32
  color: 'hover:bg-blue-500/20 hover:text-blue-400',
41
33
  activeColor: 'bg-blue-500/20 text-blue-400',
42
34
  },
43
- {
44
- id: 'box-select',
45
- iconifyIcon: 'mdi:select-drag',
46
- label: 'Box select',
47
- color: 'hover:bg-white/5',
48
- activeColor: 'bg-white/10 hover:bg-white/10',
49
- },
50
- {
51
- id: 'site-edit',
52
- imageSrc: '/icons/site.png',
53
- label: 'Edit site',
54
- color: 'hover:bg-white/5',
55
- activeColor: 'bg-white/10 hover:bg-white/10',
56
- },
57
- {
58
- id: 'build',
59
- imageSrc: '/icons/build.png',
60
- label: 'Build',
61
- shortcut: 'B',
62
- color: 'hover:bg-green-500/20 hover:text-green-400',
63
- activeColor: 'bg-green-500/20 text-green-400',
64
- },
65
- {
66
- id: 'material-paint',
67
- imageSrc: '/icons/paint.png',
68
- label: 'Material Paint',
69
- shortcut: 'P',
70
- color: 'hover:bg-amber-500/20 hover:text-amber-400',
71
- activeColor: 'bg-amber-500/20 text-amber-400',
72
- },
73
- {
74
- id: 'furnish',
75
- imageSrc: '/icons/couch.png',
76
- label: 'Furnish',
77
- shortcut: 'F',
78
- color: 'hover:bg-green-500/20 hover:text-green-400',
79
- activeColor: 'bg-green-500/20 text-green-400',
80
- },
81
35
  {
82
36
  id: 'zone',
83
- imageSrc: '/icons/zone.png',
37
+ imageSrc: '/icons/zone.webp',
84
38
  label: 'Zone',
85
39
  shortcut: 'Z',
86
40
  color: 'hover:bg-green-500/20 hover:text-green-400',
@@ -90,7 +44,7 @@ const controls: ControlConfig[] = [
90
44
  id: 'delete',
91
45
  icon: Trash2,
92
46
  label: 'Delete',
93
- shortcut: 'D',
47
+ shortcut: 'X',
94
48
  color: 'hover:bg-red-500/20 hover:text-red-400',
95
49
  activeColor: 'bg-red-500/20 text-red-400',
96
50
  },
@@ -104,58 +58,20 @@ export function ControlModes() {
104
58
  const setPhase = useEditor((state) => state.setPhase)
105
59
  const setStructureLayer = useEditor((state) => state.setStructureLayer)
106
60
  const setSelectionTool = useEditor((state) => state.setFloorplanSelectionTool)
107
- const primeMaterialPaintFromSelection = useEditor(
108
- (state) => state.primeMaterialPaintFromSelection,
109
- )
110
- const levelId = useViewer((s) => s.selection.levelId)
111
-
112
- // Only subscribe to the primitive `level` number — when walls are added to
113
- // this level the object ref changes but this number doesn't, so Object.is
114
- // dedupes and we avoid a re-render.
115
- const levelIndex = useScene((state) => {
116
- if (!levelId) return null
117
- const node = state.nodes[levelId]
118
- return node?.type === 'level' ? (node as LevelNode).level : null
119
- })
120
61
 
121
62
  const isSiteEditing = phase === 'site'
122
- const isGroundFloor = levelIndex === 0
123
- const canEnterSiteEdit = isGroundFloor || isSiteEditing
124
63
 
125
64
  const structureLayer = useEditor((state) => state.structureLayer)
126
65
 
127
66
  const getIsActive = (id: ControlId): boolean => {
128
- if (isSiteEditing) return id === 'site-edit'
129
67
  if (id === 'select') return mode === 'select' && selectionTool === 'click'
130
68
  if (id === 'box-select') return mode === 'select' && selectionTool === 'marquee'
131
- if (id === 'site-edit') return false
132
- if (id === 'build')
133
- return mode === 'build' && phase === 'structure' && structureLayer === 'elements'
134
- if (id === 'material-paint') return mode === 'material-paint'
135
- if (id === 'furnish') return mode === 'build' && phase === 'furnish'
136
69
  if (id === 'zone')
137
70
  return mode === 'build' && phase === 'structure' && structureLayer === 'zones'
138
71
  return mode === id
139
72
  }
140
73
 
141
74
  const handleClick = (id: ControlId) => {
142
- if (id === 'site-edit') {
143
- if (isSiteEditing) {
144
- // Toggle off → back to structure/select
145
- setPhase('structure')
146
- setMode('select')
147
- setStructureLayer('elements')
148
- } else if (isGroundFloor) {
149
- // Enter site editing — set state directly to preserve level selection.
150
- // setPhase('site') calls viewer.resetSelection() which clears levelId,
151
- // breaking the 2D floorplan (it needs a level to render the SVG).
152
- useEditor.setState({ phase: 'site', mode: 'select', tool: null, catalogCategory: null })
153
- // Clear object selection so the polygon editor handles receive pointer events
154
- useViewer.getState().setSelection({ selectedIds: [] })
155
- }
156
- return
157
- }
158
-
159
75
  // Exit site editing first if needed
160
76
  if (isSiteEditing) {
161
77
  setPhase('structure')
@@ -168,33 +84,6 @@ export function ControlModes() {
168
84
  } else if (id === 'box-select') {
169
85
  setMode('select')
170
86
  setSelectionTool('marquee')
171
- } else if (id === 'build') {
172
- // Toggle: if already in structure build, go back to select
173
- if (getIsActive('build')) {
174
- setMode('select')
175
- } else {
176
- setPhase('structure')
177
- setStructureLayer('elements')
178
- setMode('build')
179
- }
180
- } else if (id === 'material-paint') {
181
- if (getIsActive('material-paint')) {
182
- setMode('select')
183
- } else {
184
- primeMaterialPaintFromSelection()
185
- setPhase('structure')
186
- setStructureLayer('elements')
187
- setMode('material-paint')
188
- }
189
- } else if (id === 'furnish') {
190
- if (getIsActive('furnish')) {
191
- setMode('select')
192
- } else {
193
- setPhase('furnish')
194
- setMode('build')
195
- // Auto-switch sidebar to the items panel so the user can pick furniture
196
- useEditor.getState().setActiveSidebarPanel('items')
197
- }
198
87
  } else if (id === 'zone') {
199
88
  if (getIsActive('zone')) {
200
89
  setMode('select')
@@ -213,64 +102,45 @@ export function ControlModes() {
213
102
  {controls.map((c) => {
214
103
  const ModeIcon = c.icon
215
104
  const isImageMode = Boolean(c.imageSrc)
216
- const isSiteButton = c.id === 'site-edit'
217
105
  const isActive = getIsActive(c.id)
218
- const isDisabled = isSiteButton && !canEnterSiteEdit
219
106
 
220
107
  return (
221
- <ActionButton
222
- className={cn(
223
- 'group text-muted-foreground',
224
- isSiteButton
225
- ? isActive
226
- ? c.activeColor
227
- : canEnterSiteEdit
228
- ? 'opacity-60 grayscale hover:bg-white/5 hover:opacity-100 hover:grayscale-0'
229
- : 'cursor-not-allowed opacity-35 grayscale'
230
- : !(isImageMode || isActive) && c.color,
231
- !(isSiteButton || isImageMode) && isActive && c.activeColor,
232
- !isSiteButton && isImageMode && isActive && 'bg-white/10 hover:bg-white/10',
233
- !isSiteButton && isImageMode && !isActive && 'hover:bg-white/5',
234
- )}
235
- disabled={isDisabled}
236
- key={c.id}
237
- label={
238
- isSiteButton
239
- ? isActive
240
- ? 'Exit site editing'
241
- : canEnterSiteEdit
242
- ? 'Edit site'
243
- : 'Site editing (ground level only)'
244
- : c.label
245
- }
246
- onClick={() => handleClick(c.id)}
247
- shortcut={c.shortcut}
248
- size="icon"
249
- variant="ghost"
250
- >
251
- {c.imageSrc ? (
252
- <Image
253
- alt={c.label}
254
- className={cn(
255
- 'h-[28px] w-[28px] object-contain transition-[opacity,filter] duration-200',
256
- isSiteButton
257
- ? isActive
258
- ? 'opacity-100 grayscale-0'
259
- : ''
260
- : isActive
108
+ <Fragment key={c.id}>
109
+ {c.id === 'delete' ? <MeasurementControl /> : null}
110
+ <ActionButton
111
+ className={cn(
112
+ 'group text-muted-foreground',
113
+ !(isImageMode || isActive) && c.color,
114
+ !isImageMode && isActive && c.activeColor,
115
+ isImageMode && isActive && 'bg-white/10 hover:bg-white/10',
116
+ isImageMode && !isActive && 'hover:bg-white/5',
117
+ )}
118
+ label={c.label}
119
+ onClick={() => handleClick(c.id)}
120
+ shortcut={c.shortcut}
121
+ size="icon"
122
+ variant="ghost"
123
+ >
124
+ {c.imageSrc ? (
125
+ <Image
126
+ alt={c.label}
127
+ className={cn(
128
+ 'h-[28px] w-[28px] object-contain transition-[opacity,filter] duration-200',
129
+ isActive
261
130
  ? 'opacity-100 grayscale-0'
262
131
  : 'opacity-60 grayscale group-hover:opacity-100 group-hover:grayscale-0',
263
- )}
264
- height={28}
265
- src={c.imageSrc}
266
- width={28}
267
- />
268
- ) : c.iconifyIcon ? (
269
- <Icon color="currentColor" height={18} icon={c.iconifyIcon} width={18} />
270
- ) : (
271
- ModeIcon && <ModeIcon className="h-5 w-5" />
272
- )}
273
- </ActionButton>
132
+ )}
133
+ height={28}
134
+ src={c.imageSrc}
135
+ width={28}
136
+ />
137
+ ) : c.iconifyIcon ? (
138
+ <Icon color="currentColor" height={18} icon={c.iconifyIcon} width={18} />
139
+ ) : (
140
+ ModeIcon && <ModeIcon className="h-5 w-5" />
141
+ )}
142
+ </ActionButton>
143
+ </Fragment>
274
144
  )
275
145
  })}
276
146
  </div>
@@ -8,9 +8,9 @@ export type FurnishToolConfig = {
8
8
  }
9
9
 
10
10
  export const furnishTools: FurnishToolConfig[] = [
11
- { id: 'item', iconSrc: '/icons/couch.png', label: 'Furniture', catalogCategory: 'furniture' },
12
- { id: 'item', iconSrc: '/icons/appliance.png', label: 'Appliance', catalogCategory: 'appliance' },
13
- { id: 'item', iconSrc: '/icons/kitchen.png', label: 'Kitchen', catalogCategory: 'kitchen' },
14
- { id: 'item', iconSrc: '/icons/bathroom.png', label: 'Bathroom', catalogCategory: 'bathroom' },
15
- { id: 'item', iconSrc: '/icons/tree.png', label: 'Outdoor', catalogCategory: 'outdoor' },
11
+ { id: 'item', iconSrc: '/icons/couch.webp', label: 'Furniture', catalogCategory: 'furniture' },
12
+ { id: 'item', iconSrc: '/icons/appliance.webp', label: 'Appliance', catalogCategory: 'appliance' },
13
+ { id: 'item', iconSrc: '/icons/kitchen.webp', label: 'Kitchen', catalogCategory: 'kitchen' },
14
+ { id: 'item', iconSrc: '/icons/bathroom.webp', label: 'Bathroom', catalogCategory: 'bathroom' },
15
+ { id: 'item', iconSrc: '/icons/tree.webp', label: 'Outdoor', catalogCategory: 'outdoor' },
16
16
  ]
@@ -1,67 +1,22 @@
1
1
  'use client'
2
2
 
3
- import { useScene } from '@pascal-app/core'
4
3
  import { useViewer } from '@pascal-app/viewer'
5
- import { AnimatePresence, motion } from 'motion/react'
6
- import { useEffect, useMemo } from 'react'
7
- import { MaterialPicker } from './../../../components/ui/controls/material-picker'
4
+ import { motion } from 'motion/react'
8
5
  import { TooltipProvider } from './../../../components/ui/primitives/tooltip'
9
6
  import { useIsMobile } from './../../../hooks/use-mobile'
10
7
  import { useReducedMotion } from './../../../hooks/use-reduced-motion'
11
- import { resolvePaintTargetFromSelection } from './../../../lib/material-paint'
12
8
  import { cn } from './../../../lib/utils'
13
9
  import useEditor from './../../../store/use-editor'
14
10
  import { CameraActions } from './camera-actions'
15
11
  import { ControlModes } from './control-modes'
16
- import { StructureTools } from './structure-tools'
17
- import { GridSnapControl, SecondaryToggles } from './view-toggles'
12
+ import { SecondaryToggles } from './view-toggles'
18
13
 
19
14
  // Mobile bottom offset matches the viewer's overlap behind the sheet's
20
15
  // rounded corners (SHEET_OVERLAP_PX in editor-layout-mobile) so the menu sits
21
16
  // just above that strip instead of inside it.
22
17
  const MOBILE_BOTTOM_OFFSET = 24
23
18
 
24
- function PaintMaterialTray() {
25
- const activePaintMaterial = useEditor((state) => state.activePaintMaterial)
26
- const activePaintTarget = useEditor((state) => state.activePaintTarget)
27
- const setActivePaintMaterial = useEditor((state) => state.setActivePaintMaterial)
28
- const setActivePaintTarget = useEditor((state) => state.setActivePaintTarget)
29
- const selectedIds = useViewer((state) => state.selection.selectedIds)
30
- const nodes = useScene((state) => state.nodes)
31
- const selectedId = selectedIds.length === 1 ? (selectedIds[0] ?? null) : null
32
-
33
- useEffect(() => {
34
- const selectedPaintTarget = resolvePaintTargetFromSelection({
35
- nodes,
36
- selectedId,
37
- })
38
-
39
- if (selectedPaintTarget) {
40
- setActivePaintTarget(selectedPaintTarget)
41
- }
42
- }, [nodes, selectedId, setActivePaintTarget])
43
-
44
- return (
45
- <div className="w-[42rem] max-w-[calc(100vw-2rem)]">
46
- <MaterialPicker
47
- onChange={(material) => {
48
- setActivePaintMaterial({ material, sourceTarget: activePaintTarget })
49
- }}
50
- onSelectMaterialPreset={(materialPreset) => {
51
- setActivePaintMaterial({ materialPreset, sourceTarget: activePaintTarget })
52
- }}
53
- selectedMaterialPreset={activePaintMaterial?.materialPreset}
54
- value={activePaintMaterial?.material}
55
- />
56
- </div>
57
- )
58
- }
59
-
60
19
  export function ActionMenu({ className }: { className?: string }) {
61
- const phase = useEditor((state) => state.phase)
62
- const mode = useEditor((state) => state.mode)
63
- const tool = useEditor((state) => state.tool)
64
- const catalogCategory = useEditor((state) => state.catalogCategory)
65
20
  const isMobile = useIsMobile()
66
21
  const hasSelectionOnMobile = useViewer((s) => isMobile && s.selection.selectedIds.length > 0)
67
22
  const hasReferenceOnMobile = useEditor((s) => isMobile && Boolean(s.selectedReferenceId))
@@ -70,7 +25,6 @@ export function ActionMenu({ className }: { className?: string }) {
70
25
  (s) => isMobile && CONTEXTUAL_TABS.has(s.activeSidebarPanel),
71
26
  )
72
27
  const reducedMotion = useReducedMotion()
73
- const showPaintTray = useMemo(() => mode === 'material-paint', [mode])
74
28
 
75
29
  // On mobile, defer the bottom rail to the selection bar when something
76
30
  // is selected — the contextual actions take priority over mode controls.
@@ -97,81 +51,14 @@ export function ActionMenu({ className }: { className?: string }) {
97
51
  style={isMobile ? { bottom: MOBILE_BOTTOM_OFFSET } : undefined}
98
52
  transition={transition}
99
53
  >
100
- {/* Structure Tools Row - Animated */}
101
- <AnimatePresence>
102
- {phase === 'structure' && mode === 'build' && (
103
- <motion.div
104
- animate={{
105
- opacity: 1,
106
- maxHeight: 80,
107
- paddingTop: 8,
108
- paddingBottom: 8,
109
- borderBottomWidth: 1,
110
- }}
111
- className={cn('max-h-20 overflow-hidden border-border border-b px-2 py-2')}
112
- exit={{
113
- opacity: 0,
114
- maxHeight: 0,
115
- paddingTop: 0,
116
- paddingBottom: 0,
117
- borderBottomWidth: 0,
118
- }}
119
- initial={{
120
- opacity: 0,
121
- maxHeight: 0,
122
- paddingTop: 0,
123
- paddingBottom: 0,
124
- borderBottomWidth: 0,
125
- }}
126
- transition={transition}
127
- >
128
- <div className="w-max">
129
- <StructureTools />
130
- </div>
131
- </motion.div>
132
- )}
133
- </AnimatePresence>
134
-
135
- <AnimatePresence>
136
- {showPaintTray && (
137
- <motion.div
138
- animate={{
139
- opacity: 1,
140
- maxHeight: 96,
141
- paddingTop: 8,
142
- paddingBottom: 8,
143
- borderBottomWidth: 1,
144
- }}
145
- className={cn('overflow-hidden border-border border-b px-3')}
146
- exit={{
147
- opacity: 0,
148
- maxHeight: 0,
149
- paddingTop: 0,
150
- paddingBottom: 0,
151
- borderBottomWidth: 0,
152
- }}
153
- initial={{
154
- opacity: 0,
155
- maxHeight: 0,
156
- paddingTop: 0,
157
- paddingBottom: 0,
158
- borderBottomWidth: 0,
159
- }}
160
- transition={transition}
161
- >
162
- <PaintMaterialTray />
163
- </motion.div>
164
- )}
165
- </AnimatePresence>
166
54
  {isMobile ? (
167
55
  <div className="flex flex-col items-stretch gap-0.5 px-2 py-1.5">
168
56
  {/* Row 1: control modes only */}
169
57
  <div className="flex items-center justify-center gap-1">
170
58
  <ControlModes />
171
59
  </div>
172
- {/* Row 2: grid snap + secondary toggles (orbit + top view hidden) */}
60
+ {/* Row 2: secondary toggles (orbit + top view hidden) */}
173
61
  <div className="flex items-center justify-center gap-1 border-border/50 border-t pt-1">
174
- <GridSnapControl />
175
62
  <SecondaryToggles />
176
63
  </div>
177
64
  </div>
@@ -179,7 +66,6 @@ export function ActionMenu({ className }: { className?: string }) {
179
66
  <div className="flex items-center justify-center gap-1 px-2 py-1.5">
180
67
  <ControlModes />
181
68
  <div className="mx-1 h-5 w-px bg-border" />
182
- <GridSnapControl />
183
69
  <SecondaryToggles />
184
70
  <div className="mx-1 h-5 w-px bg-border" />
185
71
  <CameraActions />