@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,577 +0,0 @@
1
- 'use client'
2
-
3
- import {
4
- type AnyNode,
5
- type AnyNodeId,
6
- getEffectiveStairSurfaceMaterial,
7
- type LevelNode,
8
- type MaterialSchema,
9
- type StairNode,
10
- StairNode as StairNodeSchema,
11
- type StairRailingMode,
12
- type StairSegmentNode,
13
- StairSegmentNode as StairSegmentNodeSchema,
14
- type StairSlabOpeningMode,
15
- type StairSurfaceMaterialRole,
16
- type StairTopLandingMode,
17
- type StairType,
18
- useScene,
19
- } from '@pascal-app/core'
20
- import { useViewer } from '@pascal-app/viewer'
21
- import { Copy, Move, Plus, Trash2 } from 'lucide-react'
22
- import { useCallback } from 'react'
23
- import { useShallow } from 'zustand/react/shallow'
24
- import { buildStairSurfaceMaterialPatch } from '../../../lib/material-paint'
25
- import { sfxEmitter } from '../../../lib/sfx-bus'
26
- import { duplicateStairSubtree } from '../../../lib/stair-duplication'
27
- import useEditor from '../../../store/use-editor'
28
- import { DEFAULT_SPIRAL_STAIR_SWEEP_ANGLE } from '../../tools/stair/stair-defaults'
29
- import { ActionButton, ActionGroup } from '../controls/action-button'
30
- import { MaterialPicker } from '../controls/material-picker'
31
- import { MetricControl } from '../controls/metric-control'
32
- import { PanelSection } from '../controls/panel-section'
33
- import { SegmentedControl } from '../controls/segmented-control'
34
- import { SliderControl } from '../controls/slider-control'
35
- import { ToggleControl } from '../controls/toggle-control'
36
- import { PanelWrapper } from './panel-wrapper'
37
-
38
- const RAILING_MODE_OPTIONS: { label: string; value: StairRailingMode }[] = [
39
- { label: 'None', value: 'none' },
40
- { label: 'Left', value: 'left' },
41
- { label: 'Right', value: 'right' },
42
- { label: 'Both', value: 'both' },
43
- ]
44
-
45
- const STAIR_TYPE_OPTIONS: { label: string; value: StairType }[] = [
46
- { label: 'Straight', value: 'straight' },
47
- { label: 'Curved', value: 'curved' },
48
- { label: 'Spiral', value: 'spiral' },
49
- ]
50
-
51
- const TOP_LANDING_MODE_OPTIONS: { label: string; value: StairTopLandingMode }[] = [
52
- { label: 'None', value: 'none' },
53
- { label: 'Integrated', value: 'integrated' },
54
- ]
55
-
56
- const STAIR_SLAB_OPENING_OPTIONS: { label: string; value: StairSlabOpeningMode }[] = [
57
- { label: 'None', value: 'none' },
58
- { label: 'Destination', value: 'destination' },
59
- ]
60
-
61
- export function StairPanel() {
62
- const selectedId = useViewer((s) => s.selection.selectedIds[0])
63
- const selectedCount = useViewer((s) => s.selection.selectedIds.length)
64
- const setSelection = useViewer((s) => s.setSelection)
65
- const updateNode = useScene((s) => s.updateNode)
66
- const createNode = useScene((s) => s.createNode)
67
- const setMovingNode = useEditor((s) => s.setMovingNode)
68
- const selectedMaterialTarget = useEditor((s) => s.selectedMaterialTarget)
69
-
70
- const node = useScene((s) =>
71
- selectedId ? (s.nodes[selectedId as AnyNode['id']] as StairNode | undefined) : undefined,
72
- )
73
- const levels = useScene(
74
- useShallow((s) =>
75
- Object.values(s.nodes)
76
- .filter((entry): entry is LevelNode => entry.type === 'level')
77
- .sort((left, right) => left.level - right.level),
78
- ),
79
- )
80
- const segments = useScene(
81
- useShallow((s) => {
82
- if (!selectedId) return []
83
- const stairNode = s.nodes[selectedId as AnyNode['id']] as StairNode | undefined
84
- if (stairNode?.type !== 'stair') return []
85
- return (stairNode.children ?? [])
86
- .map((childId) => s.nodes[childId as AnyNodeId] as StairSegmentNode | undefined)
87
- .filter((entry): entry is StairSegmentNode => entry?.type === 'stair-segment')
88
- }),
89
- )
90
-
91
- const handleUpdate = useCallback(
92
- (updates: Partial<StairNode>) => {
93
- if (!selectedId) return
94
- updateNode(selectedId as AnyNode['id'], updates)
95
- },
96
- [selectedId, updateNode],
97
- )
98
-
99
- const materialTargetRole =
100
- selectedMaterialTarget &&
101
- selectedMaterialTarget.nodeId === node?.id &&
102
- (selectedMaterialTarget.role === 'railing' ||
103
- selectedMaterialTarget.role === 'tread' ||
104
- selectedMaterialTarget.role === 'side')
105
- ? selectedMaterialTarget.role
106
- : null
107
- const materialPickerValue =
108
- node && materialTargetRole ? getEffectiveStairSurfaceMaterial(node, materialTargetRole) : {}
109
-
110
- const handleTargetedMaterialChange = useCallback(
111
- (material: MaterialSchema) => {
112
- if (!(node && materialTargetRole)) return
113
- handleUpdate(buildStairSurfaceMaterialPatch(node, materialTargetRole, material, undefined))
114
- },
115
- [handleUpdate, materialTargetRole, node],
116
- )
117
-
118
- const handleTargetedMaterialPresetChange = useCallback(
119
- (materialPreset: string) => {
120
- if (!(node && materialTargetRole)) return
121
- handleUpdate(
122
- buildStairSurfaceMaterialPatch(node, materialTargetRole, undefined, materialPreset),
123
- )
124
- },
125
- [handleUpdate, materialTargetRole, node],
126
- )
127
-
128
- const handleClose = useCallback(() => {
129
- setSelection({ selectedIds: [] })
130
- }, [setSelection])
131
-
132
- const getLastSegmentFillDefaults = useCallback(() => {
133
- if (!node) return { fillToFloor: true }
134
- const children = node.children ?? []
135
- const lastChildId = children[children.length - 1]
136
- if (lastChildId) {
137
- const lastChild = useScene.getState().nodes[lastChildId as AnyNodeId] as
138
- | StairSegmentNode
139
- | undefined
140
- if (lastChild?.type === 'stair-segment') {
141
- return { fillToFloor: lastChild.fillToFloor }
142
- }
143
- }
144
- return { fillToFloor: true }
145
- }, [node])
146
-
147
- const handleAddFlight = useCallback(() => {
148
- if (!node) return
149
- const { fillToFloor } = getLastSegmentFillDefaults()
150
- const segment = StairSegmentNodeSchema.parse({
151
- segmentType: 'stair',
152
- width: 1.0,
153
- length: 3.0,
154
- height: 2.5,
155
- stepCount: 10,
156
- attachmentSide: 'front',
157
- fillToFloor,
158
- thickness: 0.25,
159
- position: [0, 0, 0],
160
- })
161
- createNode(segment, node.id as AnyNodeId)
162
- }, [node, createNode, getLastSegmentFillDefaults])
163
-
164
- const handleAddLanding = useCallback(() => {
165
- if (!node) return
166
- const { fillToFloor } = getLastSegmentFillDefaults()
167
- const segment = StairSegmentNodeSchema.parse({
168
- segmentType: 'landing',
169
- width: 1.0,
170
- length: 1.0,
171
- height: 0,
172
- stepCount: 0,
173
- attachmentSide: 'front',
174
- fillToFloor,
175
- thickness: 0.32,
176
- position: [0, 0, 0],
177
- })
178
- createNode(segment, node.id as AnyNodeId)
179
- }, [node, createNode, getLastSegmentFillDefaults])
180
-
181
- const handleSelectSegment = useCallback(
182
- (segmentId: string) => {
183
- setSelection({ selectedIds: [segmentId as AnyNode['id']] })
184
- },
185
- [setSelection],
186
- )
187
-
188
- const handleDuplicate = useCallback(() => {
189
- if (!node) return
190
- sfxEmitter.emit('sfx:item-pick')
191
-
192
- try {
193
- duplicateStairSubtree(node.id as AnyNodeId, { mode: 'move' })
194
- } catch (e) {
195
- console.error('Failed to duplicate stair', e)
196
- }
197
- }, [node])
198
-
199
- const handleMove = useCallback(() => {
200
- if (node) {
201
- sfxEmitter.emit('sfx:item-pick')
202
- setMovingNode(node)
203
- setSelection({ selectedIds: [] })
204
- }
205
- }, [node, setMovingNode, setSelection])
206
-
207
- const handleDelete = useCallback(() => {
208
- if (!(selectedId && node)) return
209
- sfxEmitter.emit('sfx:item-delete')
210
- const parentId = node.parentId
211
- useScene.getState().deleteNode(selectedId as AnyNodeId)
212
- if (parentId) {
213
- useScene.getState().dirtyNodes.add(parentId as AnyNodeId)
214
- }
215
- setSelection({ selectedIds: [] })
216
- }, [selectedId, node, setSelection])
217
-
218
- if (!(node && node.type === 'stair' && selectedId && selectedCount === 1)) return null
219
-
220
- const resolvedFromLevelId = node.fromLevelId ?? node.parentId ?? levels[0]?.id ?? null
221
- const resolvedToLevelId = node.toLevelId ?? resolvedFromLevelId
222
-
223
- return (
224
- <PanelWrapper
225
- icon="/icons/stairs.png"
226
- onClose={handleClose}
227
- title={node.name || 'Staircase'}
228
- width={300}
229
- >
230
- <PanelSection title="Type">
231
- <SegmentedControl
232
- onChange={(value) =>
233
- handleUpdate(
234
- value === 'spiral' && node.stairType !== 'spiral'
235
- ? {
236
- stairType: value,
237
- sweepAngle: DEFAULT_SPIRAL_STAIR_SWEEP_ANGLE,
238
- position: [node.position[0], 0, node.position[2]],
239
- }
240
- : { stairType: value },
241
- )
242
- }
243
- options={STAIR_TYPE_OPTIONS}
244
- value={node.stairType ?? 'straight'}
245
- />
246
- </PanelSection>
247
-
248
- <PanelSection title="Opening">
249
- <div className="space-y-3">
250
- <ToggleControl
251
- checked={(node.slabOpeningMode ?? 'none') === 'destination'}
252
- label="Auto Cutout"
253
- onChange={(checked) =>
254
- handleUpdate({
255
- slabOpeningMode: checked ? 'destination' : 'none',
256
- })
257
- }
258
- />
259
-
260
- <div className="space-y-1.5">
261
- <div className="px-1 text-[11px] text-muted-foreground uppercase tracking-[0.14em]">
262
- From Level
263
- </div>
264
- <select
265
- className="h-9 w-full rounded-lg border border-border/50 bg-[#2C2C2E] px-3 text-foreground text-sm"
266
- onChange={(event) => handleUpdate({ fromLevelId: event.target.value })}
267
- value={resolvedFromLevelId ?? ''}
268
- >
269
- {levels.map((level) => (
270
- <option key={level.id} value={level.id}>
271
- {level.name || `Level ${level.level + 1}`}
272
- </option>
273
- ))}
274
- </select>
275
- </div>
276
-
277
- <div className="space-y-1.5">
278
- <div className="px-1 text-[11px] text-muted-foreground uppercase tracking-[0.14em]">
279
- To Level
280
- </div>
281
- <select
282
- className="h-9 w-full rounded-lg border border-border/50 bg-[#2C2C2E] px-3 text-foreground text-sm"
283
- onChange={(event) => handleUpdate({ toLevelId: event.target.value })}
284
- value={resolvedToLevelId ?? ''}
285
- >
286
- {levels.map((level) => (
287
- <option key={level.id} value={level.id}>
288
- {level.name || `Level ${level.level + 1}`}
289
- </option>
290
- ))}
291
- </select>
292
- </div>
293
-
294
- <SegmentedControl
295
- onChange={(value) => handleUpdate({ slabOpeningMode: value as StairSlabOpeningMode })}
296
- options={STAIR_SLAB_OPENING_OPTIONS}
297
- value={node.slabOpeningMode ?? 'none'}
298
- />
299
-
300
- {(node.slabOpeningMode ?? 'none') === 'destination' ? (
301
- <MetricControl
302
- label="Opening Offset"
303
- max={0.5}
304
- min={0}
305
- onChange={(value) => handleUpdate({ openingOffset: value })}
306
- precision={2}
307
- step={0.01}
308
- unit="m"
309
- value={Math.round((node.openingOffset ?? 0) * 100) / 100}
310
- />
311
- ) : null}
312
- </div>
313
- </PanelSection>
314
-
315
- {node.stairType === 'straight' && (
316
- <PanelSection title="Segments">
317
- <div className="flex flex-col gap-1">
318
- {segments.map((seg, i) => (
319
- <button
320
- className="flex items-center justify-between rounded-lg border border-border/50 bg-[#2C2C2E] px-3 py-2 text-foreground text-sm transition-colors hover:bg-[#3e3e3e]"
321
- key={seg.id}
322
- onClick={() => handleSelectSegment(seg.id)}
323
- type="button"
324
- >
325
- <span className="truncate">{seg.name || `Segment ${i + 1}`}</span>
326
- <span className="text-muted-foreground text-xs capitalize">{seg.segmentType}</span>
327
- </button>
328
- ))}
329
- </div>
330
- <div className="flex gap-1.5">
331
- <ActionButton
332
- icon={<Plus className="h-3.5 w-3.5" />}
333
- label="Add flight"
334
- onClick={handleAddFlight}
335
- />
336
- <ActionButton
337
- icon={<Plus className="h-3.5 w-3.5" />}
338
- label="Add landing"
339
- onClick={handleAddLanding}
340
- />
341
- </div>
342
- </PanelSection>
343
- )}
344
-
345
- {(node.stairType === 'curved' || node.stairType === 'spiral') && (
346
- <PanelSection title="Geometry">
347
- <MetricControl
348
- label="Width"
349
- max={10}
350
- min={0.4}
351
- onChange={(value) => handleUpdate({ width: value })}
352
- precision={2}
353
- step={0.05}
354
- unit="m"
355
- value={Math.round((node.width ?? 1) * 100) / 100}
356
- />
357
- <MetricControl
358
- label="Rise"
359
- max={10}
360
- min={0.2}
361
- onChange={(value) => handleUpdate({ totalRise: value })}
362
- precision={2}
363
- step={0.05}
364
- unit="m"
365
- value={Math.round((node.totalRise ?? 2.5) * 100) / 100}
366
- />
367
- <MetricControl
368
- label="Steps"
369
- max={32}
370
- min={2}
371
- onChange={(value) => handleUpdate({ stepCount: Math.max(2, Math.round(value)) })}
372
- precision={0}
373
- step={1}
374
- unit=""
375
- value={Math.max(2, Math.round(node.stepCount ?? 10))}
376
- />
377
- {node.stairType !== 'spiral' && (
378
- <ToggleControl
379
- checked={node.fillToFloor ?? true}
380
- label="Fit To Floor"
381
- onChange={(checked) => handleUpdate({ fillToFloor: checked })}
382
- />
383
- )}
384
- {(node.stairType === 'spiral' || !(node.fillToFloor ?? true)) && (
385
- <MetricControl
386
- label="Thickness"
387
- max={1}
388
- min={0.02}
389
- onChange={(value) => handleUpdate({ thickness: value })}
390
- precision={2}
391
- step={0.01}
392
- unit="m"
393
- value={Math.round((node.thickness ?? 0.25) * 100) / 100}
394
- />
395
- )}
396
- <MetricControl
397
- label="Inner Radius"
398
- max={10}
399
- min={node.stairType === 'spiral' ? 0.05 : 0.2}
400
- onChange={(value) => handleUpdate({ innerRadius: value })}
401
- precision={2}
402
- step={0.05}
403
- unit="m"
404
- value={Math.round((node.innerRadius ?? 0.9) * 100) / 100}
405
- />
406
- <SliderControl
407
- label="Sweep"
408
- max={node.stairType === 'spiral' ? 720 : 270}
409
- min={node.stairType === 'spiral' ? -720 : -270}
410
- onChange={(degrees) => handleUpdate({ sweepAngle: (degrees * Math.PI) / 180 })}
411
- precision={0}
412
- step={1}
413
- unit="°"
414
- value={Math.round(((node.sweepAngle ?? Math.PI / 2) * 180) / Math.PI)}
415
- />
416
- {node.stairType === 'spiral' && (
417
- <>
418
- <SegmentedControl
419
- onChange={(value) => handleUpdate({ topLandingMode: value })}
420
- options={TOP_LANDING_MODE_OPTIONS}
421
- value={node.topLandingMode ?? 'none'}
422
- />
423
- {(node.topLandingMode ?? 'none') === 'integrated' && (
424
- <MetricControl
425
- label="Top Landing"
426
- max={5}
427
- min={0.3}
428
- onChange={(value) => handleUpdate({ topLandingDepth: value })}
429
- precision={2}
430
- step={0.05}
431
- unit="m"
432
- value={Math.round((node.topLandingDepth ?? 0.9) * 100) / 100}
433
- />
434
- )}
435
- <ToggleControl
436
- checked={node.showCenterColumn ?? true}
437
- label="Center Column"
438
- onChange={(checked) => handleUpdate({ showCenterColumn: checked })}
439
- />
440
- <ToggleControl
441
- checked={node.showStepSupports ?? true}
442
- label="Step Supports"
443
- onChange={(checked) => handleUpdate({ showStepSupports: checked })}
444
- />
445
- </>
446
- )}
447
- </PanelSection>
448
- )}
449
-
450
- <PanelSection title="Position">
451
- <SliderControl
452
- label="X"
453
- max={50}
454
- min={-50}
455
- onChange={(v) => {
456
- const pos = [...node.position] as [number, number, number]
457
- pos[0] = v
458
- handleUpdate({ position: pos })
459
- }}
460
- precision={2}
461
- step={0.05}
462
- unit="m"
463
- value={Math.round(node.position[0] * 100) / 100}
464
- />
465
- <SliderControl
466
- label="Y"
467
- max={50}
468
- min={-50}
469
- onChange={(v) => {
470
- const pos = [...node.position] as [number, number, number]
471
- pos[1] = v
472
- handleUpdate({ position: pos })
473
- }}
474
- precision={2}
475
- step={0.05}
476
- unit="m"
477
- value={Math.round(node.position[1] * 100) / 100}
478
- />
479
- <SliderControl
480
- label="Z"
481
- max={50}
482
- min={-50}
483
- onChange={(v) => {
484
- const pos = [...node.position] as [number, number, number]
485
- pos[2] = v
486
- handleUpdate({ position: pos })
487
- }}
488
- precision={2}
489
- step={0.05}
490
- unit="m"
491
- value={Math.round(node.position[2] * 100) / 100}
492
- />
493
- <SliderControl
494
- label="Rotation"
495
- max={180}
496
- min={-180}
497
- onChange={(degrees) => {
498
- handleUpdate({ rotation: (degrees * Math.PI) / 180 })
499
- }}
500
- precision={0}
501
- step={1}
502
- unit="°"
503
- value={Math.round((node.rotation * 180) / Math.PI)}
504
- />
505
- <div className="flex gap-1.5 px-1 pt-2 pb-1">
506
- <ActionButton
507
- label="-45°"
508
- onClick={() => {
509
- sfxEmitter.emit('sfx:item-rotate')
510
- handleUpdate({ rotation: node.rotation - Math.PI / 4 })
511
- }}
512
- />
513
- <ActionButton
514
- label="+45°"
515
- onClick={() => {
516
- sfxEmitter.emit('sfx:item-rotate')
517
- handleUpdate({ rotation: node.rotation + Math.PI / 4 })
518
- }}
519
- />
520
- </div>
521
- </PanelSection>
522
-
523
- <PanelSection title="Railing">
524
- <SegmentedControl
525
- onChange={(value) => handleUpdate({ railingMode: value })}
526
- options={RAILING_MODE_OPTIONS}
527
- value={node.railingMode ?? 'none'}
528
- />
529
- {(node.railingMode ?? 'none') !== 'none' && (
530
- <SliderControl
531
- label="Height"
532
- max={1.4}
533
- min={0.7}
534
- onChange={(value) => handleUpdate({ railingHeight: value })}
535
- precision={2}
536
- step={0.02}
537
- unit="m"
538
- value={Math.round((node.railingHeight ?? 0.92) * 100) / 100}
539
- />
540
- )}
541
- </PanelSection>
542
-
543
- <PanelSection title="Actions">
544
- <ActionGroup>
545
- <ActionButton icon={<Move className="h-3.5 w-3.5" />} label="Move" onClick={handleMove} />
546
- <ActionButton
547
- icon={<Copy className="h-3.5 w-3.5" />}
548
- label="Duplicate"
549
- onClick={handleDuplicate}
550
- />
551
- <ActionButton
552
- className="hover:bg-red-500/20"
553
- icon={<Trash2 className="h-3.5 w-3.5 text-red-400" />}
554
- label="Delete"
555
- onClick={handleDelete}
556
- />
557
- </ActionGroup>
558
- </PanelSection>
559
- <PanelSection title="Material">
560
- {materialTargetRole ? null : (
561
- <div className="mb-3 rounded-lg border border-border/50 bg-[#2C2C2E] px-3 py-2 text-[11px] text-muted-foreground">
562
- Click the stair surface you want to edit. Materials apply to one target at a time.
563
- </div>
564
- )}
565
- <MaterialPicker
566
- disabled={!materialTargetRole}
567
- hideSideControl
568
- nodeType="stair"
569
- onChange={handleTargetedMaterialChange}
570
- onSelectMaterialPreset={handleTargetedMaterialPresetChange}
571
- selectedMaterialPreset={materialPickerValue.materialPreset}
572
- value={materialPickerValue.material}
573
- />
574
- </PanelSection>
575
- </PanelWrapper>
576
- )
577
- }