@pascal-app/editor 0.7.0 → 0.9.1

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 (244) hide show
  1. package/package.json +12 -13
  2. package/src/components/editor/alignment-3d-guide-layer.tsx +160 -0
  3. package/src/components/editor/custom-camera-controls.tsx +755 -25
  4. package/src/components/editor/editor-layout-mobile.tsx +3 -3
  5. package/src/components/editor/editor-layout-v2.tsx +62 -54
  6. package/src/components/editor/export-manager.tsx +32 -3
  7. package/src/components/editor/first-person/build-collider-world.test.ts +149 -0
  8. package/src/components/editor/first-person/build-collider-world.ts +203 -87
  9. package/src/components/editor/first-person/bvh-ecctrl.tsx +127 -62
  10. package/src/components/editor/first-person-controls.tsx +836 -31
  11. package/src/components/editor/floating-action-menu.tsx +287 -196
  12. package/src/components/editor/floating-building-action-menu.tsx +1 -1
  13. package/src/components/editor/floorplan-panel.tsx +5837 -12006
  14. package/src/components/editor/grid.tsx +51 -17
  15. package/src/components/editor/group-move-handle.tsx +316 -0
  16. package/src/components/editor/group-rotate-handle.tsx +382 -0
  17. package/src/components/editor/group-transform-shared.test.ts +205 -0
  18. package/src/components/editor/group-transform-shared.ts +256 -0
  19. package/src/components/editor/handles/handle-arrow.tsx +523 -0
  20. package/src/components/editor/handles/use-handle-drag.ts +213 -0
  21. package/src/components/editor/index.tsx +234 -190
  22. package/src/components/editor/measurement-pill.tsx +75 -0
  23. package/src/components/editor/node-arrow-handles.tsx +1453 -0
  24. package/src/components/editor/selection-manager.tsx +435 -158
  25. package/src/components/editor/site-edge-labels.tsx +44 -20
  26. package/src/components/editor/slab-hole-highlights.tsx +466 -0
  27. package/src/components/editor/snapshot-capture-overlay.tsx +539 -0
  28. package/src/components/editor/thumbnail-generator.tsx +27 -64
  29. package/src/components/editor/use-floorplan-background-placement.ts +122 -35
  30. package/src/components/editor/use-floorplan-hit-testing.ts +24 -0
  31. package/src/components/editor/use-floorplan-scene-data.ts +14 -2
  32. package/src/components/editor/wall-measurement-label.tsx +11 -6
  33. package/src/components/editor/wall-move-side-handles.tsx +855 -0
  34. package/src/components/editor/wall-opening-highlights.tsx +181 -0
  35. package/src/components/editor/wall-snap-beacon-layer.tsx +150 -0
  36. package/src/components/editor-2d/floorplan-action-menu-layer.tsx +6 -1
  37. package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +142 -0
  38. package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +5 -3
  39. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +227 -0
  40. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +616 -0
  41. package/src/components/editor-2d/floorplan-render-context.tsx +62 -0
  42. package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +75 -0
  43. package/src/components/editor-2d/floorplan-wall-move-ghost-layer.tsx +47 -0
  44. package/src/components/editor-2d/renderers/floorplan-draft-layer.tsx +6 -1
  45. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +266 -0
  46. package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +108 -61
  47. package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +59 -0
  48. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +1963 -0
  49. package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +29 -14
  50. package/src/components/editor-2d/svg-paths.ts +10 -2
  51. package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +30 -9
  52. package/src/components/systems/ceiling/ceiling-system.tsx +12 -8
  53. package/src/components/systems/roof/roof-edit-system.tsx +86 -12
  54. package/src/components/systems/stair/stair-edit-system.tsx +2 -2
  55. package/src/components/systems/zone/zone-label-editor-system.tsx +0 -0
  56. package/src/components/systems/zone/zone-system.tsx +0 -0
  57. package/src/components/tools/elevator/elevator-defaults.ts +8 -0
  58. package/src/components/tools/elevator/elevator-tool.tsx +349 -0
  59. package/src/components/tools/elevator/move-elevator-tool.tsx +252 -0
  60. package/src/components/tools/fence/fence-drafting.ts +64 -22
  61. package/src/components/tools/item/move-tool.tsx +40 -107
  62. package/src/components/tools/item/placement-math.test.ts +10 -0
  63. package/src/components/tools/item/placement-math.ts +33 -9
  64. package/src/components/tools/item/placement-strategies.ts +201 -25
  65. package/src/components/tools/item/placement-types.ts +10 -1
  66. package/src/components/tools/item/use-draft-node.ts +20 -2
  67. package/src/components/tools/item/use-placement-coordinator.tsx +700 -299
  68. package/src/components/tools/registry/move-registry-node-tool.tsx +602 -0
  69. package/src/components/tools/roof/roof-tool.tsx +108 -12
  70. package/src/components/tools/select/box-select-state.ts +77 -0
  71. package/src/components/tools/select/box-select-tool.tsx +324 -543
  72. package/src/components/tools/select/plane-box-select-tool.tsx +577 -0
  73. package/src/components/tools/select/screen-rectangle-selection.ts +84 -0
  74. package/src/components/tools/select/select-candidates.test.ts +109 -0
  75. package/src/components/tools/select/select-candidates.ts +85 -0
  76. package/src/components/tools/shared/affordance-dispatch.ts +30 -0
  77. package/src/components/tools/shared/drag-bounding-box.tsx +171 -0
  78. package/src/components/tools/shared/floor-stack-preview.ts +25 -0
  79. package/src/components/tools/shared/fresh-placement-visibility.ts +61 -0
  80. package/src/components/tools/shared/placement-box-geometry.ts +123 -0
  81. package/src/components/tools/shared/placement-box.tsx +123 -0
  82. package/src/components/tools/shared/polygon-editor.tsx +617 -167
  83. package/src/components/tools/shared/segment-angle.ts +57 -1
  84. package/src/components/tools/site/site-boundary-editor.tsx +464 -12
  85. package/src/components/tools/site/site-flag-model.ts +18 -0
  86. package/src/components/tools/stair/stair-defaults.ts +1 -1
  87. package/src/components/tools/stair/stair-tool.tsx +269 -35
  88. package/src/components/tools/tool-manager.tsx +172 -56
  89. package/src/components/tools/wall/wall-drafting.ts +98 -98
  90. package/src/components/tools/wall/wall-snap-geometry.test.ts +79 -0
  91. package/src/components/tools/wall/wall-snap-geometry.ts +270 -0
  92. package/src/components/tools/zone/zone-tool.tsx +36 -11
  93. package/src/components/ui/action-menu/action-button.tsx +21 -1
  94. package/src/components/ui/action-menu/camera-actions.tsx +0 -0
  95. package/src/components/ui/action-menu/control-modes.tsx +5 -76
  96. package/src/components/ui/action-menu/furnish-tools.tsx +6 -92
  97. package/src/components/ui/action-menu/index.tsx +33 -196
  98. package/src/components/ui/action-menu/structure-tools.tsx +13 -88
  99. package/src/components/ui/action-menu/view-toggles.tsx +262 -37
  100. package/src/components/ui/command-palette/editor-commands.tsx +24 -17
  101. package/src/components/ui/command-palette/index.tsx +6 -256
  102. package/src/components/ui/controls/material-paint-panel.tsx +83 -0
  103. package/src/components/ui/controls/material-picker.tsx +17 -35
  104. package/src/components/ui/controls/metric-control.tsx +33 -11
  105. package/src/components/ui/floating-level-selector.tsx +48 -11
  106. package/src/components/ui/helpers/helper-manager.tsx +21 -17
  107. package/src/components/ui/helpers/registered-tool-helper.tsx +25 -0
  108. package/src/components/ui/item-catalog/catalog-items.tsx +1743 -315
  109. package/src/components/ui/item-catalog/item-catalog.tsx +91 -46
  110. package/src/components/ui/level-duplicate-dialog.tsx +5 -6
  111. package/src/components/ui/panels/node-display.ts +1 -0
  112. package/src/components/ui/panels/paint-panel.tsx +66 -41
  113. package/src/components/ui/panels/panel-manager.tsx +33 -74
  114. package/src/components/ui/panels/panel-wrapper.tsx +217 -12
  115. package/src/components/ui/panels/parametric-inspector.tsx +469 -0
  116. package/src/components/ui/panels/reference-panel.tsx +28 -13
  117. package/src/components/ui/primitives/dropdown-menu.tsx +12 -8
  118. package/src/components/ui/primitives/number-input.tsx +1 -1
  119. package/src/components/ui/primitives/sidebar.tsx +0 -0
  120. package/src/components/ui/scene-loader.tsx +1 -3
  121. package/src/components/ui/sidebar/app-sidebar.tsx +0 -0
  122. package/src/components/ui/sidebar/icon-rail.tsx +0 -0
  123. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +263 -0
  124. package/src/components/ui/sidebar/panels/items-panel/index.tsx +404 -0
  125. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +59 -9
  126. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +6 -1
  127. package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +259 -0
  128. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -1
  129. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +5 -2
  130. package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +83 -0
  131. package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +89 -0
  132. package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +75 -0
  133. package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +2 -2
  134. package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +83 -0
  135. package/src/components/ui/sidebar/panels/site-panel/index.tsx +8 -12
  136. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -1
  137. package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +93 -0
  138. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +41 -5
  139. package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +128 -0
  140. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +5 -2
  141. package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +83 -0
  142. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -1
  143. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +68 -34
  144. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +3 -1
  145. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +52 -3
  146. package/src/components/ui/sidebar/tab-bar.tsx +61 -1
  147. package/src/components/ui/slider.tsx +1 -1
  148. package/src/components/viewer-overlay.tsx +169 -52
  149. package/src/components/viewer-zone-system.tsx +1 -1
  150. package/src/hooks/use-auto-save.ts +24 -3
  151. package/src/hooks/use-ceiling-events.ts +175 -0
  152. package/src/hooks/use-drag-action.ts +124 -0
  153. package/src/hooks/use-keyboard.ts +125 -62
  154. package/src/hooks/use-selection.ts +64 -0
  155. package/src/index.tsx +243 -4
  156. package/src/lib/constants.ts +6 -2
  157. package/src/lib/editor-api.ts +62 -0
  158. package/src/lib/elevator-support.ts +96 -0
  159. package/src/lib/floorplan/apply-alignment.ts +111 -0
  160. package/src/lib/floorplan/index.ts +7 -0
  161. package/src/lib/floorplan/selection-tool.ts +40 -0
  162. package/src/lib/floorplan/stairs.ts +12 -1
  163. package/src/lib/fresh-planar-placement.test.ts +102 -0
  164. package/src/lib/fresh-planar-placement.ts +63 -0
  165. package/src/lib/history.ts +2 -1
  166. package/src/lib/level-duplication.test.ts +0 -2
  167. package/src/lib/level-duplication.ts +55 -43
  168. package/src/lib/level-name.ts +11 -0
  169. package/src/lib/material-paint.ts +212 -20
  170. package/src/lib/placement-metadata.ts +29 -0
  171. package/src/lib/planar-cursor-placement.test.ts +43 -0
  172. package/src/lib/planar-cursor-placement.ts +42 -0
  173. package/src/lib/roof-duplication.ts +2 -2
  174. package/src/lib/scene-bounds.ts +1 -1
  175. package/src/lib/scene-clipboard.ts +267 -0
  176. package/src/lib/scene.ts +13 -4
  177. package/src/lib/sfx-bus.ts +25 -1
  178. package/src/lib/sfx-player.ts +75 -27
  179. package/src/lib/site-boundary.ts +1 -0
  180. package/src/lib/stair-duplication.ts +2 -2
  181. package/src/lib/stair-levels.test.ts +146 -0
  182. package/src/lib/stair-levels.ts +177 -0
  183. package/src/lib/world-grid-snap.ts +199 -0
  184. package/src/lib/zone-content.ts +130 -0
  185. package/src/store/use-alignment-guides.ts +21 -0
  186. package/src/store/use-editor.tsx +342 -91
  187. package/src/store/use-placement-preview.ts +31 -0
  188. package/src/store/use-wall-move-ghosts.ts +36 -0
  189. package/src/store/use-wall-snap-indicator.ts +32 -0
  190. package/tsconfig.json +2 -1
  191. package/src/components/editor/preset-thumbnail-generator.tsx +0 -125
  192. package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +0 -113
  193. package/src/components/feedback-dialog.tsx +0 -265
  194. package/src/components/pascal-radio.tsx +0 -280
  195. package/src/components/preview-button.tsx +0 -16
  196. package/src/components/tools/building/move-building-tool.tsx +0 -157
  197. package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +0 -43
  198. package/src/components/tools/ceiling/ceiling-hole-editor.tsx +0 -49
  199. package/src/components/tools/ceiling/ceiling-tool.tsx +0 -465
  200. package/src/components/tools/ceiling/move-ceiling-tool.tsx +0 -257
  201. package/src/components/tools/column/column-tool.tsx +0 -97
  202. package/src/components/tools/column/move-column-tool.tsx +0 -105
  203. package/src/components/tools/door/door-math.ts +0 -110
  204. package/src/components/tools/door/door-tool.tsx +0 -324
  205. package/src/components/tools/door/move-door-tool.tsx +0 -412
  206. package/src/components/tools/fence/curve-fence-tool.tsx +0 -179
  207. package/src/components/tools/fence/fence-tool.tsx +0 -346
  208. package/src/components/tools/fence/move-fence-endpoint-tool.tsx +0 -438
  209. package/src/components/tools/fence/move-fence-tool.tsx +0 -298
  210. package/src/components/tools/item/item-tool.tsx +0 -26
  211. package/src/components/tools/roof/move-roof-tool.tsx +0 -359
  212. package/src/components/tools/slab/move-slab-tool.tsx +0 -182
  213. package/src/components/tools/slab/slab-boundary-editor.tsx +0 -43
  214. package/src/components/tools/slab/slab-hole-editor.tsx +0 -49
  215. package/src/components/tools/slab/slab-tool.tsx +0 -322
  216. package/src/components/tools/spawn/move-spawn-tool.tsx +0 -101
  217. package/src/components/tools/spawn/spawn-tool.tsx +0 -130
  218. package/src/components/tools/wall/curve-wall-tool.tsx +0 -176
  219. package/src/components/tools/wall/move-wall-endpoint-tool.tsx +0 -423
  220. package/src/components/tools/wall/move-wall-tool.tsx +0 -356
  221. package/src/components/tools/wall/wall-tool.tsx +0 -332
  222. package/src/components/tools/window/move-window-tool.tsx +0 -447
  223. package/src/components/tools/window/window-math.ts +0 -117
  224. package/src/components/tools/window/window-tool.tsx +0 -332
  225. package/src/components/ui/helpers/ceiling-helper.tsx +0 -20
  226. package/src/components/ui/helpers/slab-helper.tsx +0 -20
  227. package/src/components/ui/helpers/wall-helper.tsx +0 -20
  228. package/src/components/ui/panels/ceiling-panel.tsx +0 -250
  229. package/src/components/ui/panels/column-panel.tsx +0 -715
  230. package/src/components/ui/panels/door-panel.tsx +0 -1292
  231. package/src/components/ui/panels/fence-panel.tsx +0 -227
  232. package/src/components/ui/panels/item-panel.tsx +0 -306
  233. package/src/components/ui/panels/presets/presets-popover.tsx +0 -511
  234. package/src/components/ui/panels/roof-panel.tsx +0 -230
  235. package/src/components/ui/panels/roof-segment-panel.tsx +0 -311
  236. package/src/components/ui/panels/slab-panel.tsx +0 -250
  237. package/src/components/ui/panels/spawn-panel.tsx +0 -155
  238. package/src/components/ui/panels/stair-panel.tsx +0 -525
  239. package/src/components/ui/panels/stair-segment-panel.tsx +0 -325
  240. package/src/components/ui/panels/wall-panel.tsx +0 -177
  241. package/src/components/ui/panels/window-panel.tsx +0 -960
  242. package/src/components/ui/viewer-toolbar.tsx +0 -436
  243. package/src/contexts/presets-context.tsx +0 -121
  244. package/src/hooks/use-contextual-tools.ts +0 -61
@@ -1,64 +1,22 @@
1
1
  'use client'
2
2
 
3
- import { useScene } from '@pascal-app/core'
4
- import { AnimatePresence, motion } from 'motion/react'
5
- import { useEffect, useMemo } from 'react'
6
3
  import { useViewer } from '@pascal-app/viewer'
7
- import { useReducedMotion } from './../../../hooks/use-reduced-motion'
8
- import { useIsMobile } from './../../../hooks/use-mobile'
4
+ import { motion } from 'motion/react'
9
5
  import { TooltipProvider } from './../../../components/ui/primitives/tooltip'
10
- import { MaterialPicker } from './../../../components/ui/controls/material-picker'
11
- import { resolvePaintTargetFromSelection } from './../../../lib/material-paint'
6
+ import { useIsMobile } from './../../../hooks/use-mobile'
7
+ import { useReducedMotion } from './../../../hooks/use-reduced-motion'
12
8
  import { cn } from './../../../lib/utils'
13
9
  import useEditor from './../../../store/use-editor'
14
- import { ItemCatalog } from '../item-catalog/item-catalog'
15
10
  import { CameraActions } from './camera-actions'
16
11
  import { ControlModes } from './control-modes'
17
- import { FurnishTools } from './furnish-tools'
18
- import { StructureTools } from './structure-tools'
19
- import { ViewToggles } from './view-toggles'
20
-
21
- function PaintMaterialTray() {
22
- const activePaintMaterial = useEditor((state) => state.activePaintMaterial)
23
- const activePaintTarget = useEditor((state) => state.activePaintTarget)
24
- const setActivePaintMaterial = useEditor((state) => state.setActivePaintMaterial)
25
- const setActivePaintTarget = useEditor((state) => state.setActivePaintTarget)
26
- const selectedIds = useViewer((state) => state.selection.selectedIds)
27
- const nodes = useScene((state) => state.nodes)
28
- const selectedId = selectedIds.length === 1 ? (selectedIds[0] ?? null) : null
29
-
30
- useEffect(() => {
31
- const selectedPaintTarget = resolvePaintTargetFromSelection({
32
- nodes,
33
- selectedId,
34
- })
35
-
36
- if (selectedPaintTarget) {
37
- setActivePaintTarget(selectedPaintTarget)
38
- }
39
- }, [nodes, selectedId, setActivePaintTarget])
12
+ import { GridSnapControl, SecondaryToggles } from './view-toggles'
40
13
 
41
- return (
42
- <div className="w-[42rem] max-w-[calc(100vw-2rem)]">
43
- <MaterialPicker
44
- onChange={(material) => {
45
- setActivePaintMaterial({ material, sourceTarget: activePaintTarget })
46
- }}
47
- onSelectMaterialPreset={(materialPreset) => {
48
- setActivePaintMaterial({ materialPreset, sourceTarget: activePaintTarget })
49
- }}
50
- selectedMaterialPreset={activePaintMaterial?.materialPreset}
51
- value={activePaintMaterial?.material}
52
- />
53
- </div>
54
- )
55
- }
14
+ // Mobile bottom offset matches the viewer's overlap behind the sheet's
15
+ // rounded corners (SHEET_OVERLAP_PX in editor-layout-mobile) so the menu sits
16
+ // just above that strip instead of inside it.
17
+ const MOBILE_BOTTOM_OFFSET = 24
56
18
 
57
19
  export function ActionMenu({ className }: { className?: string }) {
58
- const phase = useEditor((state) => state.phase)
59
- const mode = useEditor((state) => state.mode)
60
- const tool = useEditor((state) => state.tool)
61
- const catalogCategory = useEditor((state) => state.catalogCategory)
62
20
  const isMobile = useIsMobile()
63
21
  const hasSelectionOnMobile = useViewer((s) => isMobile && s.selection.selectedIds.length > 0)
64
22
  const hasReferenceOnMobile = useEditor((s) => isMobile && Boolean(s.selectedReferenceId))
@@ -67,7 +25,6 @@ export function ActionMenu({ className }: { className?: string }) {
67
25
  (s) => isMobile && CONTEXTUAL_TABS.has(s.activeSidebarPanel),
68
26
  )
69
27
  const reducedMotion = useReducedMotion()
70
- const showPaintTray = useMemo(() => mode === 'material-paint', [mode])
71
28
 
72
29
  // On mobile, defer the bottom rail to the selection bar when something
73
30
  // is selected — the contextual actions take priority over mode controls.
@@ -84,158 +41,38 @@ export function ActionMenu({ className }: { className?: string }) {
84
41
  <TooltipProvider>
85
42
  <motion.div
86
43
  className={cn(
87
- 'fixed bottom-6 left-1/2 z-50 -translate-x-1/2',
44
+ 'left-1/2 z-50 -translate-x-1/2',
45
+ isMobile ? 'absolute origin-bottom scale-90' : 'fixed bottom-6',
88
46
  'rounded-2xl border border-border bg-background/90 shadow-2xl backdrop-blur-md',
89
47
  'transition-colors duration-200 ease-out',
90
48
  className,
91
49
  )}
92
50
  layout
51
+ style={isMobile ? { bottom: MOBILE_BOTTOM_OFFSET } : undefined}
93
52
  transition={transition}
94
53
  >
95
- {/* Item Catalog Row - Only show when in build mode with item tool */}
96
- <AnimatePresence>
97
- {mode === 'build' && tool === 'item' && catalogCategory && (
98
- <motion.div
99
- animate={{
100
- opacity: 1,
101
- maxHeight: 160,
102
- paddingTop: 8,
103
- paddingBottom: 8,
104
- borderBottomWidth: 1,
105
- }}
106
- className={cn('overflow-hidden border-border border-b px-2 py-2')}
107
- exit={{
108
- opacity: 0,
109
- maxHeight: 0,
110
- paddingTop: 0,
111
- paddingBottom: 0,
112
- borderBottomWidth: 0,
113
- }}
114
- initial={{
115
- opacity: 0,
116
- maxHeight: 0,
117
- paddingTop: 0,
118
- paddingBottom: 0,
119
- borderBottomWidth: 0,
120
- }}
121
- transition={transition}
122
- >
123
- <ItemCatalog category={catalogCategory} key={catalogCategory} />
124
- </motion.div>
125
- )}
126
- </AnimatePresence>
127
-
128
- <AnimatePresence>
129
- {phase === 'furnish' && mode === 'build' && (
130
- <motion.div
131
- animate={{
132
- opacity: 1,
133
- maxHeight: 80,
134
- paddingTop: 8,
135
- paddingBottom: 8,
136
- borderBottomWidth: 1,
137
- }}
138
- className={cn(
139
- 'overflow-hidden border-border',
140
- 'max-h-20 border-b px-2 py-2 opacity-100',
141
- )}
142
- exit={{
143
- opacity: 0,
144
- maxHeight: 0,
145
- paddingTop: 0,
146
- paddingBottom: 0,
147
- borderBottomWidth: 0,
148
- }}
149
- initial={{
150
- opacity: 0,
151
- maxHeight: 0,
152
- paddingTop: 0,
153
- paddingBottom: 0,
154
- borderBottomWidth: 0,
155
- }}
156
- transition={transition}
157
- >
158
- <div className="mx-auto w-max">
159
- <FurnishTools />
160
- </div>
161
- </motion.div>
162
- )}
163
- </AnimatePresence>
164
-
165
- {/* Structure Tools Row - Animated */}
166
- <AnimatePresence>
167
- {phase === 'structure' && mode === 'build' && (
168
- <motion.div
169
- animate={{
170
- opacity: 1,
171
- maxHeight: 80,
172
- paddingTop: 8,
173
- paddingBottom: 8,
174
- borderBottomWidth: 1,
175
- }}
176
- className={cn('max-h-20 overflow-hidden border-border border-b px-2 py-2')}
177
- exit={{
178
- opacity: 0,
179
- maxHeight: 0,
180
- paddingTop: 0,
181
- paddingBottom: 0,
182
- borderBottomWidth: 0,
183
- }}
184
- initial={{
185
- opacity: 0,
186
- maxHeight: 0,
187
- paddingTop: 0,
188
- paddingBottom: 0,
189
- borderBottomWidth: 0,
190
- }}
191
- transition={transition}
192
- >
193
- <div className="w-max">
194
- <StructureTools />
195
- </div>
196
- </motion.div>
197
- )}
198
- </AnimatePresence>
199
-
200
- <AnimatePresence>
201
- {showPaintTray && (
202
- <motion.div
203
- animate={{
204
- opacity: 1,
205
- maxHeight: 96,
206
- paddingTop: 8,
207
- paddingBottom: 8,
208
- borderBottomWidth: 1,
209
- }}
210
- className={cn('overflow-hidden border-border border-b px-3')}
211
- exit={{
212
- opacity: 0,
213
- maxHeight: 0,
214
- paddingTop: 0,
215
- paddingBottom: 0,
216
- borderBottomWidth: 0,
217
- }}
218
- initial={{
219
- opacity: 0,
220
- maxHeight: 0,
221
- paddingTop: 0,
222
- paddingBottom: 0,
223
- borderBottomWidth: 0,
224
- }}
225
- transition={transition}
226
- >
227
- <PaintMaterialTray />
228
- </motion.div>
229
- )}
230
- </AnimatePresence>
231
- {/* Control Mode Row - Always visible, centered */}
232
- <div className="flex items-center justify-center gap-1 px-2 py-1.5">
233
- <ControlModes />
234
- <div className="mx-1 h-5 w-px bg-border" />
235
- <ViewToggles />
236
- <div className="mx-1 h-5 w-px bg-border" />
237
- <CameraActions />
238
- </div>
54
+ {isMobile ? (
55
+ <div className="flex flex-col items-stretch gap-0.5 px-2 py-1.5">
56
+ {/* Row 1: control modes only */}
57
+ <div className="flex items-center justify-center gap-1">
58
+ <ControlModes />
59
+ </div>
60
+ {/* Row 2: grid snap + secondary toggles (orbit + top view hidden) */}
61
+ <div className="flex items-center justify-center gap-1 border-border/50 border-t pt-1">
62
+ <GridSnapControl />
63
+ <SecondaryToggles />
64
+ </div>
65
+ </div>
66
+ ) : (
67
+ <div className="flex items-center justify-center gap-1 px-2 py-1.5">
68
+ <ControlModes />
69
+ <div className="mx-1 h-5 w-px bg-border" />
70
+ <GridSnapControl />
71
+ <SecondaryToggles />
72
+ <div className="mx-1 h-5 w-px bg-border" />
73
+ <CameraActions />
74
+ </div>
75
+ )}
239
76
  </motion.div>
240
77
  </TooltipProvider>
241
78
  )
@@ -1,15 +1,4 @@
1
- 'use client'
2
-
3
- import NextImage from 'next/image'
4
- import { useContextualTools } from '../../../hooks/use-contextual-tools'
5
-
6
- import { cn } from '../../../lib/utils'
7
- import useEditor, {
8
- type CatalogCategory,
9
- type StructureTool,
10
- type Tool,
11
- } from '../../../store/use-editor'
12
- import { ActionButton } from './action-button'
1
+ import type { CatalogCategory, StructureTool } from '../../../store/use-editor'
13
2
 
14
3
  export type ToolConfig = {
15
4
  id: StructureTool
@@ -18,86 +7,22 @@ export type ToolConfig = {
18
7
  catalogCategory?: CatalogCategory
19
8
  }
20
9
 
10
+ // Shared structure-tool metadata (icons + labels). The build palette now lives
11
+ // in the community Build sidebar; this list survives only as the lookup table
12
+ // for cursor/floorplan indicators. Roof-mounted accessories are intentionally
13
+ // absent — they're placed from the roof inspector's "Add element" section.
21
14
  export const tools: ToolConfig[] = [
22
15
  { id: 'wall', iconSrc: '/icons/wall.png', label: 'Wall' },
23
- // { id: 'room', iconSrc: '/icons/room.png', label: 'Room' },
24
- // { id: 'custom-room', iconSrc: '/icons/custom-room.png', label: 'Custom Room' },
25
- { id: 'slab', iconSrc: '/icons/floor.png', label: 'Slab' },
26
- { id: 'ceiling', iconSrc: '/icons/ceiling.png', label: 'Ceiling' },
27
- { id: 'column', iconSrc: '/icons/column.png', label: 'Column' },
28
- { id: 'roof', iconSrc: '/icons/roof.png', label: 'Gable Roof' },
29
- { id: 'stair', iconSrc: '/icons/stairs.png', label: 'Stairs' },
30
16
  { id: 'door', iconSrc: '/icons/door.png', label: 'Door' },
31
17
  { id: 'window', iconSrc: '/icons/window.png', label: 'Window' },
18
+ { id: 'stair', iconSrc: '/icons/stairs.png', label: 'Stairs' },
19
+ { id: 'roof', iconSrc: '/icons/roof.png', label: 'Gable Roof' },
32
20
  { id: 'fence', iconSrc: '/icons/fence.png', label: 'Fence' },
21
+ { id: 'column', iconSrc: '/icons/column.png', label: 'Column' },
22
+ { id: 'elevator', iconSrc: '/icons/elevator.png', label: 'Elevator' },
23
+ { id: 'slab', iconSrc: '/icons/floor.png', label: 'Slab' },
24
+ { id: 'ceiling', iconSrc: '/icons/ceiling.png', label: 'Ceiling' },
33
25
  { id: 'zone', iconSrc: '/icons/zone.png', label: 'Zone' },
34
- { id: 'spawn', iconSrc: '/icons/site.png', label: 'Spawn Point' },
26
+ { id: 'spawn', iconSrc: '/icons/spawn-point.png', label: 'Spawn Point' },
27
+ { id: 'shelf', iconSrc: '/icons/shelf.png', label: 'Shelf' },
35
28
  ]
36
-
37
- export function StructureTools() {
38
- const activeTool = useEditor((state) => state.tool)
39
- const catalogCategory = useEditor((state) => state.catalogCategory)
40
- const structureLayer = useEditor((state) => state.structureLayer)
41
- const setTool = useEditor((state) => state.setTool)
42
- const setCatalogCategory = useEditor((state) => state.setCatalogCategory)
43
-
44
- const contextualTools = useContextualTools()
45
-
46
- // Filter tools based on structureLayer
47
- const visibleTools =
48
- structureLayer === 'zones'
49
- ? tools.filter((t) => t.id === 'zone')
50
- : tools.filter((t) => t.id !== 'zone')
51
-
52
- const hasActiveTool = visibleTools.some(
53
- (t) =>
54
- activeTool === t.id && (t.catalogCategory ? catalogCategory === t.catalogCategory : true),
55
- )
56
-
57
- return (
58
- <div className="flex items-center gap-1.5 px-1">
59
- {visibleTools.map((tool, index) => {
60
- // For item tools with catalog category, check both tool and category match
61
- const isActive =
62
- activeTool === tool.id &&
63
- (tool.catalogCategory ? catalogCategory === tool.catalogCategory : true)
64
-
65
- const isContextual = contextualTools.includes(tool.id)
66
-
67
- return (
68
- <ActionButton
69
- className={cn(
70
- 'rounded-lg duration-300',
71
- isActive
72
- ? 'z-10 scale-110 bg-black/40 hover:bg-black/40'
73
- : 'scale-95 bg-transparent opacity-60 grayscale hover:bg-black/20 hover:opacity-100 hover:grayscale-0',
74
- )}
75
- key={`${tool.id}-${tool.catalogCategory ?? index}`}
76
- label={tool.label}
77
- onClick={() => {
78
- if (!isActive) {
79
- setTool(tool.id)
80
- setCatalogCategory(tool.catalogCategory ?? null)
81
-
82
- // Automatically switch to build mode if we select a tool
83
- if (useEditor.getState().mode !== 'build') {
84
- useEditor.getState().setMode('build')
85
- }
86
- }
87
- }}
88
- size="icon"
89
- variant="ghost"
90
- >
91
- <NextImage
92
- alt={tool.label}
93
- className="size-full object-contain"
94
- height={28}
95
- src={tool.iconSrc}
96
- width={28}
97
- />
98
- </ActionButton>
99
- )
100
- })}
101
- </div>
102
- )
103
- }