@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,759 +0,0 @@
1
- 'use client'
2
-
3
- import {
4
- type AnyNode,
5
- COLUMN_PRESETS,
6
- type ColumnNode,
7
- type ColumnPresetId,
8
- useScene,
9
- } from '@pascal-app/core'
10
- import { useViewer } from '@pascal-app/viewer'
11
- import { Move, Trash2 } from 'lucide-react'
12
- import { useCallback } from 'react'
13
- import { sfxEmitter } from '../../../lib/sfx-bus'
14
- import useEditor from '../../../store/use-editor'
15
- import { ActionButton, ActionGroup } from '../controls/action-button'
16
- import { PanelSection } from '../controls/panel-section'
17
- import { SliderControl } from '../controls/slider-control'
18
- import { PanelWrapper } from './panel-wrapper'
19
-
20
- const SELECT_CLASS =
21
- 'h-10 w-full rounded-lg border border-border/50 bg-[#2C2C2E] px-3 text-sm text-foreground outline-none transition-colors hover:bg-[#3e3e3e] focus:ring-1 focus:ring-border'
22
-
23
- const COLUMN_PRESET_OPTIONS = Object.entries(COLUMN_PRESETS).map(([value, preset]) => ({
24
- value: value as ColumnPresetId,
25
- label: preset.label,
26
- }))
27
-
28
- const COLUMN_PROPORTION_PRESETS = {
29
- slender: {
30
- label: 'Slender',
31
- height: 3.6,
32
- width: 0.34,
33
- baseHeight: 0.18,
34
- capitalHeight: 0.16,
35
- baseWidthScale: 1.18,
36
- capitalWidthScale: 1.16,
37
- edgeSoftness: 0.02,
38
- },
39
- standard: {
40
- label: 'Standard',
41
- height: 2.9,
42
- width: 0.44,
43
- baseHeight: 0.22,
44
- capitalHeight: 0.2,
45
- baseWidthScale: 1.24,
46
- capitalWidthScale: 1.22,
47
- edgeSoftness: 0.025,
48
- },
49
- heavy: {
50
- label: 'Heavy',
51
- height: 3,
52
- width: 0.58,
53
- baseHeight: 0.28,
54
- capitalHeight: 0.26,
55
- baseWidthScale: 1.34,
56
- capitalWidthScale: 1.3,
57
- edgeSoftness: 0.035,
58
- },
59
- stout: {
60
- label: 'Short / Stout',
61
- height: 2.2,
62
- width: 0.62,
63
- baseHeight: 0.3,
64
- capitalHeight: 0.28,
65
- baseWidthScale: 1.38,
66
- capitalWidthScale: 1.34,
67
- edgeSoftness: 0.04,
68
- },
69
- } as const
70
-
71
- type ColumnProportionPresetId = keyof typeof COLUMN_PROPORTION_PRESETS
72
-
73
- const COLUMN_PROPORTION_OPTIONS = Object.entries(COLUMN_PROPORTION_PRESETS).map(
74
- ([value, preset]) => ({
75
- value: value as ColumnProportionPresetId,
76
- label: preset.label,
77
- }),
78
- )
79
-
80
- function clamp(value: number, min: number, max: number) {
81
- return Math.min(max, Math.max(min, value))
82
- }
83
-
84
- function presetUpdates(presetId: ColumnPresetId): Partial<ColumnNode> {
85
- const { label, ...preset } = COLUMN_PRESETS[presetId]
86
- return {
87
- name: label,
88
- ...preset,
89
- }
90
- }
91
-
92
- function proportionUpdates(
93
- node: ColumnNode,
94
- presetId: ColumnProportionPresetId,
95
- ): Partial<ColumnNode> {
96
- const preset = COLUMN_PROPORTION_PRESETS[presetId]
97
- const depth =
98
- node.crossSection === 'rectangular'
99
- ? clamp(preset.width * (node.depth / Math.max(node.width, 0.01)), 0.12, 1.6)
100
- : preset.width
101
- const shaftCornerRadius = Math.min(node.shaftCornerRadius ?? 0.035, preset.width * 0.18)
102
-
103
- return {
104
- height: preset.height,
105
- width: preset.width,
106
- depth,
107
- radius: preset.width / 2,
108
- baseHeight: preset.baseHeight,
109
- capitalHeight: preset.capitalHeight,
110
- baseWidthScale: preset.baseWidthScale,
111
- baseDepthScale: preset.baseWidthScale,
112
- capitalWidthScale: preset.capitalWidthScale,
113
- capitalDepthScale: preset.capitalWidthScale,
114
- edgeSoftness: preset.edgeSoftness,
115
- shaftCornerRadius,
116
- }
117
- }
118
-
119
- function shaftProfileUpdates(shaftProfile: ColumnNode['shaftProfile']): Partial<ColumnNode> {
120
- if (shaftProfile === 'tapered') {
121
- return {
122
- shaftProfile,
123
- shaftTaper: 0.14,
124
- shaftBulge: 0,
125
- shaftStartScale: 0.82,
126
- shaftEndScale: 0.72,
127
- shaftSegmentCount: 32,
128
- }
129
- }
130
-
131
- if (shaftProfile === 'bulged') {
132
- return {
133
- shaftProfile,
134
- shaftTaper: 0,
135
- shaftBulge: 0.12,
136
- shaftStartScale: 0.68,
137
- shaftEndScale: 0.68,
138
- shaftSegmentCount: 32,
139
- }
140
- }
141
-
142
- if (shaftProfile === 'hourglass') {
143
- return {
144
- shaftProfile,
145
- shaftTaper: 0,
146
- shaftBulge: 0.12,
147
- shaftStartScale: 0.84,
148
- shaftEndScale: 0.84,
149
- shaftSegmentCount: 32,
150
- }
151
- }
152
-
153
- return {
154
- shaftProfile,
155
- shaftTaper: 0,
156
- shaftBulge: 0,
157
- shaftStartScale: 0.72,
158
- shaftEndScale: 0.72,
159
- shaftSegmentCount: 1,
160
- shaftTwistStep: 0,
161
- }
162
- }
163
-
164
- export function ColumnPanel() {
165
- const selectedId = useViewer((s) => s.selection.selectedIds[0])
166
- const selectedCount = useViewer((s) => s.selection.selectedIds.length)
167
- const setSelection = useViewer((s) => s.setSelection)
168
- const updateNode = useScene((s) => s.updateNode)
169
- const deleteNode = useScene((s) => s.deleteNode)
170
- const setMovingNode = useEditor((s) => s.setMovingNode)
171
-
172
- const node = useScene((s) =>
173
- selectedId ? (s.nodes[selectedId as AnyNode['id']] as ColumnNode | undefined) : undefined,
174
- )
175
-
176
- const handleUpdate = useCallback(
177
- (updates: Partial<ColumnNode>) => {
178
- if (!selectedId) return
179
- updateNode(selectedId as AnyNode['id'], updates)
180
- },
181
- [selectedId, updateNode],
182
- )
183
-
184
- const handleClose = useCallback(() => {
185
- setSelection({ selectedIds: [] })
186
- }, [setSelection])
187
-
188
- const handleDelete = useCallback(() => {
189
- if (!selectedId) return
190
- sfxEmitter.emit('sfx:structure-delete')
191
- deleteNode(selectedId as AnyNode['id'])
192
- setSelection({ selectedIds: [] })
193
- }, [deleteNode, selectedId, setSelection])
194
-
195
- const handleMove = useCallback(() => {
196
- if (!node) return
197
- sfxEmitter.emit('sfx:item-pick')
198
- setMovingNode(node)
199
- setSelection({ selectedIds: [] })
200
- }, [node, setMovingNode, setSelection])
201
-
202
- if (!(node && node.type === 'column' && selectedId && selectedCount === 1)) return null
203
- const shaftProfile = node.shaftProfile ?? 'straight'
204
-
205
- return (
206
- <PanelWrapper
207
- icon="/icons/column.png"
208
- onClose={handleClose}
209
- title={node.name || 'Column'}
210
- width={300}
211
- >
212
- <PanelSection title="Preset">
213
- <select
214
- className={SELECT_CLASS}
215
- onChange={(event) => {
216
- if (!event.target.value) return
217
- handleUpdate(presetUpdates(event.target.value as ColumnPresetId))
218
- }}
219
- value=""
220
- >
221
- <option value="">Apply preset...</option>
222
- {COLUMN_PRESET_OPTIONS.map((option) => (
223
- <option key={option.value} value={option.value}>
224
- {option.label}
225
- </option>
226
- ))}
227
- </select>
228
- </PanelSection>
229
-
230
- <PanelSection title="Shape">
231
- <select
232
- className={SELECT_CLASS}
233
- onChange={(event) =>
234
- handleUpdate({ crossSection: event.target.value as ColumnNode['crossSection'] })
235
- }
236
- value={node.crossSection}
237
- >
238
- <option value="round">Round</option>
239
- <option value="square">Square</option>
240
- <option value="rectangular">Rectangular</option>
241
- </select>
242
- <SliderControl
243
- label="Edge Softness"
244
- max={0.12}
245
- min={0}
246
- onChange={(value) => handleUpdate({ edgeSoftness: value })}
247
- precision={3}
248
- step={0.005}
249
- unit="m"
250
- value={node.edgeSoftness ?? 0.025}
251
- />
252
- {(node.crossSection === 'square' || node.crossSection === 'rectangular') && (
253
- <SliderControl
254
- label="Shaft Corner Radius"
255
- max={0.3}
256
- min={0}
257
- onChange={(value) => handleUpdate({ shaftCornerRadius: value })}
258
- precision={3}
259
- step={0.005}
260
- unit="m"
261
- value={node.shaftCornerRadius ?? 0.035}
262
- />
263
- )}
264
- </PanelSection>
265
-
266
- <PanelSection title="Dimensions">
267
- <select
268
- className={SELECT_CLASS}
269
- onChange={(event) => {
270
- if (!event.target.value) return
271
- handleUpdate(proportionUpdates(node, event.target.value as ColumnProportionPresetId))
272
- }}
273
- value=""
274
- >
275
- <option value="">Apply proportion...</option>
276
- {COLUMN_PROPORTION_OPTIONS.map((option) => (
277
- <option key={option.value} value={option.value}>
278
- {option.label}
279
- </option>
280
- ))}
281
- </select>
282
- <SliderControl
283
- label="Height"
284
- max={6}
285
- min={0.8}
286
- onChange={(value) => handleUpdate({ height: value })}
287
- precision={2}
288
- step={0.05}
289
- unit="m"
290
- value={node.height}
291
- />
292
- <SliderControl
293
- label="Width"
294
- max={1.6}
295
- min={0.12}
296
- onChange={(value) =>
297
- handleUpdate({
298
- width: value,
299
- radius: value / 2,
300
- ...(node.crossSection === 'rectangular' ? {} : { depth: value }),
301
- })
302
- }
303
- precision={2}
304
- step={0.02}
305
- unit="m"
306
- value={node.width}
307
- />
308
- {node.crossSection === 'rectangular' && (
309
- <SliderControl
310
- label="Depth"
311
- max={1.6}
312
- min={0.12}
313
- onChange={(value) => handleUpdate({ depth: value })}
314
- precision={2}
315
- step={0.02}
316
- unit="m"
317
- value={node.depth}
318
- />
319
- )}
320
- </PanelSection>
321
-
322
- <PanelSection title="Shaft">
323
- <select
324
- className={SELECT_CLASS}
325
- onChange={(event) =>
326
- handleUpdate(shaftProfileUpdates(event.target.value as ColumnNode['shaftProfile']))
327
- }
328
- value={shaftProfile}
329
- >
330
- <option value="straight">Straight</option>
331
- <option value="tapered">Tapered</option>
332
- <option value="bulged">Bulged</option>
333
- <option value="hourglass">Hourglass</option>
334
- </select>
335
- {shaftProfile === 'straight' && (
336
- <SliderControl
337
- label="Shaft Width"
338
- max={1.2}
339
- min={0.3}
340
- onChange={(value) => handleUpdate({ shaftStartScale: value, shaftEndScale: value })}
341
- precision={2}
342
- step={0.02}
343
- value={node.shaftStartScale ?? 0.72}
344
- />
345
- )}
346
- {shaftProfile === 'tapered' && (
347
- <>
348
- <SliderControl
349
- label="Bottom Width"
350
- max={1.2}
351
- min={0.3}
352
- onChange={(value) => handleUpdate({ shaftStartScale: value })}
353
- precision={2}
354
- step={0.02}
355
- value={node.shaftStartScale ?? 0.82}
356
- />
357
- <SliderControl
358
- label="Top Width"
359
- max={1.2}
360
- min={0.3}
361
- onChange={(value) => handleUpdate({ shaftEndScale: value })}
362
- precision={2}
363
- step={0.02}
364
- value={node.shaftEndScale ?? 0.72}
365
- />
366
- <SliderControl
367
- label="Taper"
368
- max={0.45}
369
- min={0}
370
- onChange={(value) => handleUpdate({ shaftTaper: value })}
371
- precision={2}
372
- step={0.01}
373
- value={node.shaftTaper ?? 0.14}
374
- />
375
- </>
376
- )}
377
- {shaftProfile === 'bulged' && (
378
- <>
379
- <SliderControl
380
- label="End Width"
381
- max={1.2}
382
- min={0.3}
383
- onChange={(value) => handleUpdate({ shaftStartScale: value, shaftEndScale: value })}
384
- precision={2}
385
- step={0.02}
386
- value={node.shaftStartScale ?? 0.68}
387
- />
388
- <SliderControl
389
- label="Bulge"
390
- max={0.35}
391
- min={0}
392
- onChange={(value) => handleUpdate({ shaftBulge: value })}
393
- precision={2}
394
- step={0.01}
395
- value={node.shaftBulge ?? 0.12}
396
- />
397
- </>
398
- )}
399
- {shaftProfile === 'hourglass' && (
400
- <>
401
- <SliderControl
402
- label="End Width"
403
- max={1.2}
404
- min={0.3}
405
- onChange={(value) => handleUpdate({ shaftStartScale: value, shaftEndScale: value })}
406
- precision={2}
407
- step={0.02}
408
- value={node.shaftStartScale ?? 0.84}
409
- />
410
- <SliderControl
411
- label="Waist"
412
- max={0.35}
413
- min={0}
414
- onChange={(value) => handleUpdate({ shaftBulge: value })}
415
- precision={2}
416
- step={0.01}
417
- value={node.shaftBulge ?? 0.12}
418
- />
419
- </>
420
- )}
421
- <SliderControl
422
- label="Segment Twist"
423
- max={90}
424
- min={-90}
425
- onChange={(value) =>
426
- handleUpdate({
427
- shaftTwistStep: value,
428
- ...(Math.abs(value) > 0.001 && (node.shaftSegmentCount ?? 1) < 8
429
- ? { shaftSegmentCount: 12 }
430
- : {}),
431
- })
432
- }
433
- precision={0}
434
- step={5}
435
- unit="°"
436
- value={node.shaftTwistStep ?? 0}
437
- />
438
- {Math.abs(node.shaftTwistStep ?? 0) > 0.001 && (
439
- <SliderControl
440
- label="Twist Segments"
441
- max={48}
442
- min={4}
443
- onChange={(value) => handleUpdate({ shaftSegmentCount: Math.round(value) })}
444
- precision={0}
445
- step={1}
446
- value={node.shaftSegmentCount ?? 12}
447
- />
448
- )}
449
- <SliderControl
450
- label="Ring Pairs"
451
- max={4}
452
- min={0}
453
- onChange={(value) =>
454
- handleUpdate({
455
- ringCount: Math.round(value) * 2,
456
- ringPlacement: 'ends',
457
- ringSpread: node.ringSpread ?? 0.16,
458
- ringThickness: node.ringThickness ?? 0.055,
459
- })
460
- }
461
- precision={0}
462
- step={1}
463
- value={Math.ceil((node.ringCount ?? 0) / 2)}
464
- />
465
- {(node.ringCount ?? 0) > 0 && (
466
- <SliderControl
467
- label="Ring Thickness"
468
- max={0.14}
469
- min={0.01}
470
- onChange={(value) => handleUpdate({ ringThickness: value })}
471
- precision={3}
472
- step={0.005}
473
- unit="m"
474
- value={node.ringThickness ?? 0.055}
475
- />
476
- )}
477
- {(node.ringCount ?? 0) > 0 && (
478
- <SliderControl
479
- label="Ring Spread"
480
- max={0.45}
481
- min={0.04}
482
- onChange={(value) => handleUpdate({ ringSpread: value, ringPlacement: 'ends' })}
483
- precision={2}
484
- step={0.01}
485
- value={node.ringSpread ?? 0.16}
486
- />
487
- )}
488
- </PanelSection>
489
-
490
- <PanelSection title="Ends">
491
- <select
492
- className={SELECT_CLASS}
493
- onChange={(event) => {
494
- const capitalStyle = event.target.value as ColumnNode['capitalStyle']
495
- handleUpdate({
496
- capitalStyle,
497
- ...(capitalStyle === 'none'
498
- ? {}
499
- : {
500
- capitalHeight: Math.max(node.capitalHeight, 0.12),
501
- capitalTierCount:
502
- capitalStyle === 'stepped'
503
- ? Math.max(node.capitalTierCount ?? 3, 3)
504
- : node.capitalTierCount,
505
- capitalWidthScale: Math.max(
506
- node.capitalWidthScale ?? 1.3,
507
- capitalStyle === 'stepped' ? 1.42 : 1.28,
508
- ),
509
- capitalDepthScale: Math.max(
510
- node.capitalDepthScale ?? 1.3,
511
- capitalStyle === 'stepped' ? 1.42 : 1.28,
512
- ),
513
- capitalStepSpread:
514
- capitalStyle === 'stepped'
515
- ? Math.max(node.capitalStepSpread ?? 0.34, 0.34)
516
- : node.capitalStepSpread,
517
- }),
518
- })
519
- }}
520
- value={node.capitalStyle === 'simple-slab' ? 'simple' : (node.capitalStyle ?? 'simple')}
521
- >
522
- <option value="none">No Top</option>
523
- <option value="simple">Simple Top</option>
524
- <option value="stepped">Stepped Top</option>
525
- <option value="rounded">Rounded Top</option>
526
- </select>
527
- {node.capitalStyle !== 'none' && (
528
- <SliderControl
529
- label="Top Height"
530
- max={0.8}
531
- min={0.06}
532
- onChange={(value) => handleUpdate({ capitalHeight: value })}
533
- precision={2}
534
- step={0.02}
535
- unit="m"
536
- value={node.capitalHeight}
537
- />
538
- )}
539
- {node.capitalStyle !== 'none' && (
540
- <SliderControl
541
- label="Top Width"
542
- max={2.4}
543
- min={0.6}
544
- onChange={(value) =>
545
- handleUpdate({
546
- capitalWidthScale: value,
547
- ...(node.crossSection === 'rectangular' ? {} : { capitalDepthScale: value }),
548
- })
549
- }
550
- precision={2}
551
- step={0.02}
552
- value={node.capitalWidthScale ?? 1.28}
553
- />
554
- )}
555
- {node.capitalStyle !== 'none' && node.crossSection === 'rectangular' && (
556
- <SliderControl
557
- label="Top Depth"
558
- max={2.4}
559
- min={0.6}
560
- onChange={(value) => handleUpdate({ capitalDepthScale: value })}
561
- precision={2}
562
- step={0.02}
563
- value={node.capitalDepthScale ?? node.capitalWidthScale ?? 1.28}
564
- />
565
- )}
566
- {node.capitalStyle === 'stepped' && (
567
- <SliderControl
568
- label="Top Tiers"
569
- max={8}
570
- min={3}
571
- onChange={(value) => handleUpdate({ capitalTierCount: Math.round(value) })}
572
- precision={0}
573
- step={1}
574
- value={node.capitalTierCount ?? 3}
575
- />
576
- )}
577
- {node.capitalStyle === 'stepped' && (
578
- <SliderControl
579
- label="Top Step Spread"
580
- max={0.9}
581
- min={0.05}
582
- onChange={(value) => handleUpdate({ capitalStepSpread: value })}
583
- precision={2}
584
- step={0.01}
585
- value={node.capitalStepSpread ?? 0.34}
586
- />
587
- )}
588
- <select
589
- className={`${SELECT_CLASS} mt-2`}
590
- onChange={(event) => {
591
- const baseStyle = event.target.value as ColumnNode['baseStyle']
592
- handleUpdate({
593
- baseStyle,
594
- ...(baseStyle === 'none'
595
- ? {}
596
- : {
597
- baseHeight: Math.max(node.baseHeight, 0.12),
598
- baseTierCount:
599
- baseStyle === 'stepped-square'
600
- ? Math.max(node.baseTierCount ?? 3, 3)
601
- : node.baseTierCount,
602
- baseWidthScale: Math.max(
603
- node.baseWidthScale ?? 1.24,
604
- baseStyle === 'stepped-square' ? 1.42 : 1.24,
605
- ),
606
- baseDepthScale: Math.max(
607
- node.baseDepthScale ?? 1.24,
608
- baseStyle === 'stepped-square' ? 1.42 : 1.24,
609
- ),
610
- baseStepSpread:
611
- baseStyle === 'stepped-square'
612
- ? Math.max(node.baseStepSpread ?? 0.34, 0.34)
613
- : node.baseStepSpread,
614
- basePlinthHeightRatio:
615
- baseStyle === 'round-rings'
616
- ? (node.basePlinthHeightRatio ?? 0.44)
617
- : node.basePlinthHeightRatio,
618
- baseRoundBandScale:
619
- baseStyle === 'round-rings'
620
- ? (node.baseRoundBandScale ?? 0.92)
621
- : node.baseRoundBandScale,
622
- baseNeckScale:
623
- baseStyle === 'round-rings'
624
- ? (node.baseNeckScale ?? 0.72)
625
- : node.baseNeckScale,
626
- }),
627
- })
628
- }}
629
- value={node.baseStyle ?? 'square-plinth'}
630
- >
631
- <option value="none">No Bottom</option>
632
- <option value="simple-square">Simple Block Bottom</option>
633
- <option value="square-plinth">Square Plinth Bottom</option>
634
- <option value="stepped-square">Stepped Bottom</option>
635
- <option value="round-rings">Rounded Bottom</option>
636
- </select>
637
- {node.baseStyle !== 'none' && (
638
- <SliderControl
639
- label="Bottom Height"
640
- max={0.8}
641
- min={0.06}
642
- onChange={(value) => handleUpdate({ baseHeight: value })}
643
- precision={2}
644
- step={0.02}
645
- unit="m"
646
- value={node.baseHeight}
647
- />
648
- )}
649
- {node.baseStyle !== 'none' && (
650
- <SliderControl
651
- label="Bottom Width"
652
- max={2.4}
653
- min={0.6}
654
- onChange={(value) =>
655
- handleUpdate({
656
- baseWidthScale: value,
657
- ...(node.crossSection === 'rectangular' ? {} : { baseDepthScale: value }),
658
- })
659
- }
660
- precision={2}
661
- step={0.02}
662
- value={node.baseWidthScale ?? 1.24}
663
- />
664
- )}
665
- {node.baseStyle !== 'none' && node.crossSection === 'rectangular' && (
666
- <SliderControl
667
- label="Bottom Depth"
668
- max={2.4}
669
- min={0.6}
670
- onChange={(value) => handleUpdate({ baseDepthScale: value })}
671
- precision={2}
672
- step={0.02}
673
- value={node.baseDepthScale ?? node.baseWidthScale ?? 1.24}
674
- />
675
- )}
676
- {node.baseStyle === 'round-rings' && (
677
- <SliderControl
678
- label="Plinth Thickness"
679
- max={0.7}
680
- min={0.2}
681
- onChange={(value) => handleUpdate({ basePlinthHeightRatio: value })}
682
- precision={2}
683
- step={0.01}
684
- value={node.basePlinthHeightRatio ?? 0.44}
685
- />
686
- )}
687
- {node.baseStyle === 'round-rings' && (
688
- <SliderControl
689
- label="Round Band Width"
690
- max={1.2}
691
- min={0.5}
692
- onChange={(value) => handleUpdate({ baseRoundBandScale: value })}
693
- precision={2}
694
- step={0.01}
695
- value={node.baseRoundBandScale ?? 0.92}
696
- />
697
- )}
698
- {node.baseStyle === 'round-rings' && (
699
- <SliderControl
700
- label="Neck Width"
701
- max={1}
702
- min={0.35}
703
- onChange={(value) => handleUpdate({ baseNeckScale: value })}
704
- precision={2}
705
- step={0.01}
706
- value={node.baseNeckScale ?? 0.72}
707
- />
708
- )}
709
- {node.baseStyle === 'stepped-square' && (
710
- <SliderControl
711
- label="Bottom Tiers"
712
- max={8}
713
- min={3}
714
- onChange={(value) => handleUpdate({ baseTierCount: Math.round(value) })}
715
- precision={0}
716
- step={1}
717
- value={node.baseTierCount ?? 3}
718
- />
719
- )}
720
- {node.baseStyle === 'stepped-square' && (
721
- <SliderControl
722
- label="Bottom Step Spread"
723
- max={0.9}
724
- min={0.05}
725
- onChange={(value) => handleUpdate({ baseStepSpread: value })}
726
- precision={2}
727
- step={0.01}
728
- value={node.baseStepSpread ?? 0.34}
729
- />
730
- )}
731
- </PanelSection>
732
-
733
- <PanelSection title="Transform">
734
- <SliderControl
735
- label="Yaw"
736
- max={180}
737
- min={-180}
738
- onChange={(value) => handleUpdate({ rotation: (value * Math.PI) / 180 })}
739
- precision={0}
740
- step={1}
741
- unit="°"
742
- value={Math.round((node.rotation * 180) / Math.PI)}
743
- />
744
- </PanelSection>
745
-
746
- <PanelSection title="Actions">
747
- <ActionGroup>
748
- <ActionButton icon={<Move className="h-4 w-4" />} label="Move" onClick={handleMove} />
749
- <ActionButton
750
- className="border-red-500/40 text-red-200 hover:bg-red-500/15"
751
- icon={<Trash2 className="h-4 w-4" />}
752
- label="Delete"
753
- onClick={handleDelete}
754
- />
755
- </ActionGroup>
756
- </PanelSection>
757
- </PanelWrapper>
758
- )
759
- }