@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,222 +0,0 @@
1
- 'use client'
2
-
3
- import {
4
- type AnyNode,
5
- type AnyNodeId,
6
- type FenceNode,
7
- getClampedWallCurveOffset,
8
- getMaxWallCurveOffset,
9
- getWallCurveLength,
10
- type MaterialSchema,
11
- normalizeWallCurveOffset,
12
- useScene,
13
- } from '@pascal-app/core'
14
-
15
- import { useViewer } from '@pascal-app/viewer'
16
- import { Move, Spline } from 'lucide-react'
17
- import { useCallback } from 'react'
18
-
19
- import { sfxEmitter } from '../../../lib/sfx-bus'
20
- import useEditor from '../../../store/use-editor'
21
- import { ActionButton, ActionGroup } from '../controls/action-button'
22
- import { MaterialPicker } from '../controls/material-picker'
23
- import { PanelSection } from '../controls/panel-section'
24
- import { SegmentedControl } from '../controls/segmented-control'
25
- import { SliderControl } from '../controls/slider-control'
26
- import { PanelWrapper } from './panel-wrapper'
27
-
28
- type FenceStyleValue = 'slat' | 'rail' | 'privacy'
29
- type FenceBaseStyleValue = 'grounded' | 'floating'
30
-
31
- const FENCE_STYLE_OPTIONS: { label: string; value: FenceStyleValue }[] = [
32
- { label: 'Slat', value: 'slat' },
33
- { label: 'Rail', value: 'rail' },
34
- { label: 'Privacy', value: 'privacy' },
35
- ]
36
-
37
- const FENCE_BASE_STYLE_OPTIONS: { label: string; value: FenceBaseStyleValue }[] = [
38
- { label: 'Grounded', value: 'grounded' },
39
- { label: 'Floating', value: 'floating' },
40
- ]
41
-
42
- export function FencePanel() {
43
- const selectedId = useViewer((s) => s.selection.selectedIds[0])
44
- const selectedCount = useViewer((s) => s.selection.selectedIds.length)
45
- const setSelection = useViewer((s) => s.setSelection)
46
- const updateNode = useScene((s) => s.updateNode)
47
- const setMovingNode = useEditor((s) => s.setMovingNode)
48
- const setCurvingFence = useEditor((s) => s.setCurvingFence)
49
-
50
- const node = useScene((s) =>
51
- selectedId ? (s.nodes[selectedId as AnyNode['id']] as FenceNode | undefined) : undefined,
52
- )
53
-
54
- const handleUpdate = useCallback(
55
- (updates: Partial<FenceNode>) => {
56
- if (!selectedId) return
57
- updateNode(selectedId as AnyNode['id'], updates)
58
- useScene.getState().dirtyNodes.add(selectedId as AnyNodeId)
59
- },
60
- [selectedId, updateNode],
61
- )
62
-
63
- const handleUpdateLength = useCallback(
64
- (newLength: number) => {
65
- if (!node || newLength <= 0) return
66
-
67
- const dx = node.end[0] - node.start[0]
68
- const dz = node.end[1] - node.start[1]
69
- const currentLength = Math.sqrt(dx * dx + dz * dz)
70
- if (currentLength === 0) return
71
-
72
- const dirX = dx / currentLength
73
- const dirZ = dz / currentLength
74
- const newEnd: [number, number] = [
75
- node.start[0] + dirX * newLength,
76
- node.start[1] + dirZ * newLength,
77
- ]
78
-
79
- handleUpdate({ end: newEnd })
80
- },
81
- [node, handleUpdate],
82
- )
83
-
84
- const handleClose = useCallback(() => {
85
- setSelection({ selectedIds: [] })
86
- }, [setSelection])
87
-
88
- if (!(node && node.type === 'fence' && selectedId && selectedCount === 1)) return null
89
-
90
- const length = getWallCurveLength(node)
91
- const curveOffset = getClampedWallCurveOffset(node)
92
- const maxCurveOffset = getMaxWallCurveOffset(node)
93
-
94
- return (
95
- <PanelWrapper
96
- icon="/icons/build.png"
97
- onClose={handleClose}
98
- title={node.name || 'Fence'}
99
- width={300}
100
- >
101
- <PanelSection title="Style">
102
- <SegmentedControl
103
- onChange={(value) => handleUpdate({ style: value })}
104
- options={FENCE_STYLE_OPTIONS}
105
- value={node.style}
106
- />
107
- <SegmentedControl
108
- className="mt-2"
109
- onChange={(value) => handleUpdate({ baseStyle: value })}
110
- options={FENCE_BASE_STYLE_OPTIONS}
111
- value={node.baseStyle}
112
- />
113
- </PanelSection>
114
-
115
- <PanelSection title="Dimensions">
116
- <SliderControl
117
- label="Length"
118
- max={50}
119
- min={0.1}
120
- onChange={handleUpdateLength}
121
- precision={2}
122
- step={0.01}
123
- unit="m"
124
- value={length}
125
- />
126
- <SliderControl
127
- label="Curve"
128
- max={Math.max(0.01, maxCurveOffset)}
129
- min={-Math.max(0.01, maxCurveOffset)}
130
- onChange={(value) => handleUpdate({ curveOffset: normalizeWallCurveOffset(node, value) })}
131
- precision={2}
132
- step={0.1}
133
- unit="m"
134
- value={Math.round(curveOffset * 100) / 100}
135
- />
136
- <SliderControl
137
- label="Height"
138
- max={4}
139
- min={0.4}
140
- onChange={(value) => handleUpdate({ height: Math.max(0.4, value) })}
141
- precision={2}
142
- step={0.05}
143
- unit="m"
144
- value={node.height}
145
- />
146
- <SliderControl
147
- label="Thickness"
148
- max={0.5}
149
- min={0.03}
150
- onChange={(value) => handleUpdate({ thickness: Math.max(0.03, value) })}
151
- precision={3}
152
- step={0.005}
153
- unit="m"
154
- value={node.thickness}
155
- />
156
- </PanelSection>
157
-
158
- <PanelSection title="Structure">
159
- <SliderControl
160
- label="Base Height"
161
- max={1}
162
- min={0.04}
163
- onChange={(value) => handleUpdate({ baseHeight: Math.max(0.04, value) })}
164
- precision={3}
165
- step={0.01}
166
- unit="m"
167
- value={node.baseHeight}
168
- />
169
- <SliderControl
170
- label="Top Rail"
171
- max={0.25}
172
- min={0.01}
173
- onChange={(value) => handleUpdate({ topRailHeight: Math.max(0.01, value) })}
174
- precision={3}
175
- step={0.005}
176
- unit="m"
177
- value={node.topRailHeight}
178
- />
179
- <SliderControl
180
- label="Post Spacing"
181
- max={5}
182
- min={0.2}
183
- onChange={(value) => handleUpdate({ postSpacing: Math.max(0.2, value) })}
184
- precision={2}
185
- step={0.05}
186
- unit="m"
187
- value={node.postSpacing}
188
- />
189
- <SliderControl
190
- label="Post Size"
191
- max={0.4}
192
- min={0.01}
193
- onChange={(value) => handleUpdate({ postSize: Math.max(0.01, value) })}
194
- precision={3}
195
- step={0.005}
196
- unit="m"
197
- value={node.postSize}
198
- />
199
- <SliderControl
200
- label="Ground Clear"
201
- max={0.6}
202
- min={0}
203
- onChange={(value) => handleUpdate({ groundClearance: Math.max(0, value) })}
204
- precision={3}
205
- step={0.005}
206
- unit="m"
207
- value={node.groundClearance}
208
- />
209
- <SliderControl
210
- label="Edge Inset"
211
- max={0.25}
212
- min={0.005}
213
- onChange={(value) => handleUpdate({ edgeInset: Math.max(0.005, value) })}
214
- precision={3}
215
- step={0.005}
216
- unit="m"
217
- value={node.edgeInset}
218
- />
219
- </PanelSection>
220
- </PanelWrapper>
221
- )
222
- }
@@ -1,306 +0,0 @@
1
- 'use client'
2
-
3
- import { type AnyNode, getScaledDimensions, ItemNode, useScene } from '@pascal-app/core'
4
- import { useViewer } from '@pascal-app/viewer'
5
- import { Copy, Link, Link2Off, Move, Trash2 } from 'lucide-react'
6
- import { useCallback, useState } from 'react'
7
- import { sfxEmitter } from '../../../lib/sfx-bus'
8
- import { cn } from '../../../lib/utils'
9
- import useEditor from '../../../store/use-editor'
10
- import { ActionButton, ActionGroup } from '../controls/action-button'
11
- import { PanelSection } from '../controls/panel-section'
12
- import { SliderControl } from '../controls/slider-control'
13
- import { CollectionsPopover } from './collections/collections-popover'
14
- import { PanelWrapper } from './panel-wrapper'
15
-
16
- export function ItemPanel() {
17
- const selectedId = useViewer((s) => s.selection.selectedIds[0])
18
- const setSelection = useViewer((s) => s.setSelection)
19
- const updateNode = useScene((s) => s.updateNode)
20
- const deleteNode = useScene((s) => s.deleteNode)
21
- const setMovingNode = useEditor((s) => s.setMovingNode)
22
-
23
- const node = useScene((s) =>
24
- selectedId ? (s.nodes[selectedId as AnyNode['id']] as ItemNode | undefined) : undefined,
25
- )
26
-
27
- const [uniformScale, setUniformScale] = useState(true)
28
-
29
- const handleUpdate = useCallback(
30
- (updates: Partial<ItemNode>) => {
31
- if (!(selectedId && node)) return
32
- updateNode(selectedId as AnyNode['id'], updates)
33
-
34
- if (node.asset.attachTo === 'wall' && node.parentId) {
35
- requestAnimationFrame(() => {
36
- useScene.getState().dirtyNodes.add(node.parentId as AnyNode['id'])
37
- })
38
- }
39
- },
40
- [selectedId, node, updateNode],
41
- )
42
-
43
- const handleClose = useCallback(() => {
44
- setSelection({ selectedIds: [] })
45
- }, [setSelection])
46
-
47
- const handleMove = useCallback(() => {
48
- if (node) {
49
- sfxEmitter.emit('sfx:item-pick')
50
- setMovingNode(node)
51
- setSelection({ selectedIds: [] })
52
- }
53
- }, [node, setMovingNode, setSelection])
54
-
55
- const handleDuplicate = useCallback(() => {
56
- if (!node) return
57
- sfxEmitter.emit('sfx:item-pick')
58
- const proto = ItemNode.parse({
59
- position: [...node.position] as [number, number, number],
60
- rotation: [...node.rotation] as [number, number, number],
61
- name: node.name,
62
- asset: node.asset,
63
- parentId: node.parentId,
64
- side: node.side,
65
- metadata: { isNew: true },
66
- })
67
- setMovingNode(proto)
68
- setSelection({ selectedIds: [] })
69
- }, [node, setMovingNode, setSelection])
70
-
71
- const handleDelete = useCallback(() => {
72
- if (!selectedId) return
73
- sfxEmitter.emit('sfx:item-delete')
74
- deleteNode(selectedId as AnyNode['id'])
75
- setSelection({ selectedIds: [] })
76
- }, [selectedId, deleteNode, setSelection])
77
-
78
- if (!(node && node.type === 'item' && selectedId)) return null
79
-
80
- return (
81
- <PanelWrapper
82
- icon={node.asset.thumbnail || '/icons/furniture.png'}
83
- onClose={handleClose}
84
- title={node.name || node.asset.name}
85
- width={300}
86
- >
87
- <PanelSection title="Position">
88
- <SliderControl
89
- label={
90
- <>
91
- X<sub className="ml-[1px] text-[11px] opacity-70">pos</sub>
92
- </>
93
- }
94
- max={node.position[0] + 2}
95
- min={node.position[0] - 2}
96
- onChange={(value) =>
97
- handleUpdate({ position: [value, node.position[1], node.position[2]] })
98
- }
99
- precision={2}
100
- step={0.01}
101
- unit="m"
102
- value={Math.round(node.position[0] * 100) / 100}
103
- />
104
- <SliderControl
105
- label={
106
- <>
107
- Y<sub className="ml-[1px] text-[11px] opacity-70">pos</sub>
108
- </>
109
- }
110
- max={node.position[1] + 2}
111
- min={node.position[1] - 2}
112
- onChange={(value) =>
113
- handleUpdate({ position: [node.position[0], value, node.position[2]] })
114
- }
115
- precision={2}
116
- step={0.01}
117
- unit="m"
118
- value={Math.round(node.position[1] * 100) / 100}
119
- />
120
- <SliderControl
121
- label={
122
- <>
123
- Z<sub className="ml-[1px] text-[11px] opacity-70">pos</sub>
124
- </>
125
- }
126
- max={node.position[2] + 2}
127
- min={node.position[2] - 2}
128
- onChange={(value) =>
129
- handleUpdate({ position: [node.position[0], node.position[1], value] })
130
- }
131
- precision={2}
132
- step={0.01}
133
- unit="m"
134
- value={Math.round(node.position[2] * 100) / 100}
135
- />
136
- </PanelSection>
137
-
138
- <PanelSection title="Rotation">
139
- <SliderControl
140
- label={
141
- <>
142
- Y<sub className="ml-[1px] text-[11px] opacity-70">rot</sub>
143
- </>
144
- }
145
- max={Math.round((node.rotation[1] * 180) / Math.PI) + 45}
146
- min={Math.round((node.rotation[1] * 180) / Math.PI) - 45}
147
- onChange={(degrees) => {
148
- const radians = (degrees * Math.PI) / 180
149
- handleUpdate({ rotation: [node.rotation[0], radians, node.rotation[2]] })
150
- }}
151
- precision={0}
152
- step={1}
153
- unit="°"
154
- value={Math.round((node.rotation[1] * 180) / Math.PI)}
155
- />
156
- <div className="flex gap-1.5 px-1 pt-2 pb-1">
157
- <ActionButton
158
- label="-45°"
159
- onClick={() => {
160
- sfxEmitter.emit('sfx:item-rotate')
161
- const currentDegrees = (node.rotation[1] * 180) / Math.PI
162
- const radians = ((currentDegrees - 45) * Math.PI) / 180
163
- handleUpdate({ rotation: [node.rotation[0], radians, node.rotation[2]] })
164
- }}
165
- />
166
- <ActionButton
167
- label="+45°"
168
- onClick={() => {
169
- sfxEmitter.emit('sfx:item-rotate')
170
- const currentDegrees = (node.rotation[1] * 180) / Math.PI
171
- const radians = ((currentDegrees + 45) * Math.PI) / 180
172
- handleUpdate({ rotation: [node.rotation[0], radians, node.rotation[2]] })
173
- }}
174
- />
175
- </div>
176
- </PanelSection>
177
-
178
- <PanelSection title="Scale">
179
- <div className="flex items-center justify-between px-2 pb-2">
180
- <span className="font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider">
181
- Uniform Scale
182
- </span>
183
- <button
184
- className={cn(
185
- 'flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground',
186
- uniformScale ? 'bg-[#3e3e3e]' : 'bg-[#2C2C2E] hover:bg-[#3e3e3e]',
187
- )}
188
- onClick={() => setUniformScale((v) => !v)}
189
- type="button"
190
- >
191
- {uniformScale ? <Link className="h-3.5 w-3.5" /> : <Link2Off className="h-3.5 w-3.5" />}
192
- </button>
193
- </div>
194
-
195
- {uniformScale ? (
196
- <SliderControl
197
- label={
198
- <>
199
- XYZ<sub className="ml-[1px] text-[11px] opacity-70">scale</sub>
200
- </>
201
- }
202
- max={10}
203
- min={0.01}
204
- onChange={(value) => {
205
- const v = Math.max(0.01, value)
206
- handleUpdate({ scale: [v, v, v] })
207
- }}
208
- precision={2}
209
- step={0.1}
210
- value={Math.round(node.scale[0] * 100) / 100}
211
- />
212
- ) : (
213
- <>
214
- <SliderControl
215
- label={
216
- <>
217
- X<sub className="ml-[1px] text-[11px] opacity-70">scale</sub>
218
- </>
219
- }
220
- max={10}
221
- min={0.01}
222
- onChange={(value) =>
223
- handleUpdate({ scale: [Math.max(0.01, value), node.scale[1], node.scale[2]] })
224
- }
225
- precision={2}
226
- step={0.1}
227
- value={Math.round(node.scale[0] * 100) / 100}
228
- />
229
- <SliderControl
230
- label={
231
- <>
232
- Y<sub className="ml-[1px] text-[11px] opacity-70">scale</sub>
233
- </>
234
- }
235
- max={10}
236
- min={0.01}
237
- onChange={(value) =>
238
- handleUpdate({ scale: [node.scale[0], Math.max(0.01, value), node.scale[2]] })
239
- }
240
- precision={2}
241
- step={0.1}
242
- value={Math.round(node.scale[1] * 100) / 100}
243
- />
244
- <SliderControl
245
- label={
246
- <>
247
- Z<sub className="ml-[1px] text-[11px] opacity-70">scale</sub>
248
- </>
249
- }
250
- max={10}
251
- min={0.01}
252
- onChange={(value) =>
253
- handleUpdate({ scale: [node.scale[0], node.scale[1], Math.max(0.01, value)] })
254
- }
255
- precision={2}
256
- step={0.1}
257
- value={Math.round(node.scale[2] * 100) / 100}
258
- />
259
- </>
260
- )}
261
- </PanelSection>
262
-
263
- <PanelSection title="Info">
264
- <div className="flex items-center justify-between px-2 py-1 text-muted-foreground text-sm">
265
- <span>Dimensions</span>
266
- {(() => {
267
- const [w, h, d] = getScaledDimensions(node)
268
- return (
269
- <span className="font-mono text-white">
270
- {Math.round(w * 100) / 100}×{Math.round(h * 100) / 100}×{Math.round(d * 100) / 100}
271
- </span>
272
- )
273
- })()}
274
- </div>
275
- </PanelSection>
276
-
277
- <PanelSection title="Collections">
278
- <ActionGroup>
279
- <CollectionsPopover
280
- collectionIds={node.collectionIds}
281
- nodeId={selectedId as AnyNode['id']}
282
- >
283
- <ActionButton label="Manage collections…" />
284
- </CollectionsPopover>
285
- </ActionGroup>
286
- </PanelSection>
287
-
288
- <PanelSection title="Actions">
289
- <ActionGroup>
290
- <ActionButton icon={<Move className="h-3.5 w-3.5" />} label="Move" onClick={handleMove} />
291
- <ActionButton
292
- icon={<Copy className="h-3.5 w-3.5" />}
293
- label="Duplicate"
294
- onClick={handleDuplicate}
295
- />
296
- <ActionButton
297
- className="hover:bg-red-500/20"
298
- icon={<Trash2 className="h-3.5 w-3.5 text-red-400" />}
299
- label="Delete"
300
- onClick={handleDelete}
301
- />
302
- </ActionGroup>
303
- </PanelSection>
304
- </PanelWrapper>
305
- )
306
- }
@@ -1,163 +0,0 @@
1
- 'use client'
2
-
3
- import useEditor from '../../../store/use-editor'
4
- import { PanelSection } from '../controls/panel-section'
5
- import { Input } from '../primitives/input'
6
- import { PanelWrapper } from './panel-wrapper'
7
-
8
- function buildDefaultCustomMaterial() {
9
- return {
10
- preset: 'custom' as const,
11
- properties: {
12
- color: '#ffffff',
13
- roughness: 0.5,
14
- metalness: 0,
15
- opacity: 1,
16
- transparent: false,
17
- side: 'front' as const,
18
- },
19
- }
20
- }
21
-
22
- export function PaintPanel() {
23
- const activePaintMaterial = useEditor((state) => state.activePaintMaterial)
24
- const activePaintTarget = useEditor((state) => state.activePaintTarget)
25
- const setActivePaintMaterial = useEditor((state) => state.setActivePaintMaterial)
26
- const setPaintPanelOpen = useEditor((state) => state.setPaintPanelOpen)
27
-
28
- const customMaterial =
29
- activePaintMaterial?.material?.properties && !activePaintMaterial.materialPreset
30
- ? activePaintMaterial.material
31
- : null
32
-
33
- if (!customMaterial) return null
34
-
35
- const currentProps = customMaterial.properties ?? buildDefaultCustomMaterial().properties
36
-
37
- const updateCustomMaterial = (
38
- updates: Partial<typeof currentProps>,
39
- nextTransparent = currentProps.transparent,
40
- ) => {
41
- setActivePaintMaterial({
42
- material: {
43
- preset: 'custom',
44
- properties: {
45
- ...currentProps,
46
- ...updates,
47
- transparent: nextTransparent,
48
- },
49
- },
50
- sourceTarget: activePaintMaterial?.sourceTarget ?? activePaintTarget,
51
- })
52
- }
53
-
54
- return (
55
- <PanelWrapper onClose={() => setPaintPanelOpen(false)} title="Material" width={320}>
56
- <PanelSection title="Custom Material">
57
- <div className="space-y-3">
58
- <div className="space-y-2">
59
- <label className="block font-medium text-muted-foreground text-xs uppercase tracking-[0.12em]">
60
- Color
61
- </label>
62
- <div className="flex items-center gap-2">
63
- <input
64
- className="h-10 w-14 cursor-pointer rounded-md border border-input bg-transparent"
65
- onChange={(e) => updateCustomMaterial({ color: e.target.value })}
66
- type="color"
67
- value={currentProps.color}
68
- />
69
- <Input
70
- onChange={(e) => updateCustomMaterial({ color: e.target.value })}
71
- value={currentProps.color}
72
- />
73
- </div>
74
- </div>
75
-
76
- <div className="space-y-2">
77
- <div className="flex items-center justify-between">
78
- <label className="font-medium text-muted-foreground text-xs uppercase tracking-[0.12em]">
79
- Roughness
80
- </label>
81
- <span className="font-mono text-muted-foreground text-xs">
82
- {currentProps.roughness.toFixed(2)}
83
- </span>
84
- </div>
85
- <input
86
- className="h-2 w-full cursor-pointer appearance-none rounded-full bg-accent"
87
- max={1}
88
- min={0}
89
- onChange={(e) =>
90
- updateCustomMaterial({ roughness: Number.parseFloat(e.target.value) })
91
- }
92
- step={0.01}
93
- type="range"
94
- value={currentProps.roughness}
95
- />
96
- </div>
97
-
98
- <div className="space-y-2">
99
- <div className="flex items-center justify-between">
100
- <label className="font-medium text-muted-foreground text-xs uppercase tracking-[0.12em]">
101
- Metalness
102
- </label>
103
- <span className="font-mono text-muted-foreground text-xs">
104
- {currentProps.metalness.toFixed(2)}
105
- </span>
106
- </div>
107
- <input
108
- className="h-2 w-full cursor-pointer appearance-none rounded-full bg-accent"
109
- max={1}
110
- min={0}
111
- onChange={(e) =>
112
- updateCustomMaterial({ metalness: Number.parseFloat(e.target.value) })
113
- }
114
- step={0.01}
115
- type="range"
116
- value={currentProps.metalness}
117
- />
118
- </div>
119
-
120
- <div className="space-y-2">
121
- <div className="flex items-center justify-between">
122
- <label className="font-medium text-muted-foreground text-xs uppercase tracking-[0.12em]">
123
- Opacity
124
- </label>
125
- <span className="font-mono text-muted-foreground text-xs">
126
- {currentProps.opacity.toFixed(2)}
127
- </span>
128
- </div>
129
- <input
130
- className="h-2 w-full cursor-pointer appearance-none rounded-full bg-accent"
131
- max={1}
132
- min={0}
133
- onChange={(e) => {
134
- const opacity = Number.parseFloat(e.target.value)
135
- updateCustomMaterial({ opacity }, opacity < 1 || currentProps.transparent)
136
- }}
137
- step={0.01}
138
- type="range"
139
- value={currentProps.opacity}
140
- />
141
- </div>
142
-
143
- <div className="space-y-2">
144
- <label className="block font-medium text-muted-foreground text-xs uppercase tracking-[0.12em]">
145
- Side
146
- </label>
147
- <select
148
- className="h-9 w-full rounded-md border border-input bg-transparent px-3 text-sm shadow-xs outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:bg-input/30"
149
- onChange={(e) =>
150
- updateCustomMaterial({ side: e.target.value as 'front' | 'back' | 'double' })
151
- }
152
- value={currentProps.side}
153
- >
154
- <option value="front">Front</option>
155
- <option value="back">Back</option>
156
- <option value="double">Double</option>
157
- </select>
158
- </div>
159
- </div>
160
- </PanelSection>
161
- </PanelWrapper>
162
- )
163
- }