@pascal-app/editor 0.6.0 → 0.8.0
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.
- package/package.json +13 -9
- package/src/components/editor/bottom-sheet.tsx +149 -0
- package/src/components/editor/custom-camera-controls.tsx +74 -5
- package/src/components/editor/editor-layout-mobile.tsx +264 -0
- package/src/components/editor/editor-layout-v2.tsx +24 -3
- package/src/components/editor/first-person/build-collider-world.ts +363 -0
- package/src/components/editor/first-person/bvh-ecctrl.tsx +860 -0
- package/src/components/editor/first-person-controls.tsx +496 -143
- package/src/components/editor/floating-action-menu.tsx +32 -55
- package/src/components/editor/floorplan-background-selection.ts +113 -0
- package/src/components/editor/floorplan-panel.tsx +9861 -3297
- package/src/components/editor/index.tsx +295 -32
- package/src/components/editor/selection-manager.tsx +575 -13
- package/src/components/editor/snapshot-capture-overlay.tsx +465 -0
- package/src/components/editor/thumbnail-generator.tsx +56 -68
- package/src/components/editor/use-floorplan-background-placement.ts +257 -0
- package/src/components/editor/use-floorplan-hit-testing.ts +171 -0
- package/src/components/editor/use-floorplan-scene-data.ts +189 -0
- package/src/components/editor/wall-measurement-label.tsx +267 -36
- package/src/components/editor-2d/floorplan-action-menu-layer.tsx +95 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +160 -0
- package/src/components/editor-2d/floorplan-hotkey-handlers.tsx +92 -0
- package/src/components/editor-2d/renderers/floorplan-draft-layer.tsx +124 -0
- package/src/components/editor-2d/renderers/floorplan-marquee-layer.tsx +58 -0
- package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +202 -0
- package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +113 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +474 -0
- package/src/components/editor-2d/svg-paths.ts +119 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +10 -12
- package/src/components/systems/roof/roof-edit-system.tsx +1 -1
- package/src/components/systems/stair/stair-edit-system.tsx +1 -1
- package/src/components/systems/zone/zone-label-editor-system.tsx +0 -0
- package/src/components/systems/zone/zone-system.tsx +0 -0
- package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +1 -0
- package/src/components/tools/ceiling/ceiling-hole-editor.tsx +1 -0
- package/src/components/tools/ceiling/ceiling-tool.tsx +5 -5
- package/src/components/tools/ceiling/move-ceiling-tool.tsx +9 -2
- package/src/components/tools/column/column-tool.tsx +97 -0
- package/src/components/tools/column/move-column-tool.tsx +105 -0
- package/src/components/tools/door/door-tool.tsx +7 -0
- package/src/components/tools/door/move-door-tool.tsx +28 -8
- package/src/components/tools/fence/curve-fence-tool.tsx +4 -5
- package/src/components/tools/fence/fence-drafting.ts +10 -3
- package/src/components/tools/fence/fence-tool.tsx +160 -4
- package/src/components/tools/fence/move-fence-endpoint-tool.tsx +139 -25
- package/src/components/tools/fence/move-fence-tool.tsx +111 -40
- package/src/components/tools/item/move-tool.tsx +7 -1
- package/src/components/tools/item/placement-math.ts +32 -5
- package/src/components/tools/item/placement-strategies.ts +110 -31
- package/src/components/tools/item/placement-types.ts +7 -0
- package/src/components/tools/item/use-draft-node.ts +1 -0
- package/src/components/tools/item/use-placement-coordinator.tsx +558 -52
- package/src/components/tools/roof/move-roof-tool.tsx +29 -17
- package/src/components/tools/select/box-select-tool.tsx +12 -17
- package/src/components/tools/shared/polygon-editor.tsx +153 -28
- package/src/components/tools/shared/segment-angle.ts +156 -0
- package/src/components/tools/slab/slab-boundary-editor.tsx +1 -0
- package/src/components/tools/slab/slab-hole-editor.tsx +1 -0
- package/src/components/tools/spawn/move-spawn-tool.tsx +101 -0
- package/src/components/tools/spawn/spawn-tool.tsx +130 -0
- package/src/components/tools/tool-manager.tsx +20 -5
- package/src/components/tools/wall/curve-wall-tool.tsx +8 -6
- package/src/components/tools/wall/move-wall-endpoint-tool.tsx +131 -27
- package/src/components/tools/wall/move-wall-tool.tsx +6 -4
- package/src/components/tools/wall/wall-drafting.ts +18 -9
- package/src/components/tools/wall/wall-tool.tsx +136 -4
- package/src/components/tools/window/move-window-tool.tsx +18 -0
- package/src/components/tools/window/window-tool.tsx +5 -0
- package/src/components/tools/zone/zone-tool.tsx +20 -5
- package/src/components/ui/action-menu/camera-actions.tsx +37 -33
- package/src/components/ui/action-menu/control-modes.tsx +34 -1
- package/src/components/ui/action-menu/furnish-tools.tsx +6 -92
- package/src/components/ui/action-menu/index.tsx +98 -59
- package/src/components/ui/action-menu/structure-tools.tsx +2 -0
- package/src/components/ui/action-menu/view-toggles.tsx +418 -41
- package/src/components/ui/command-palette/editor-commands.tsx +24 -5
- package/src/components/ui/command-palette/index.tsx +4 -255
- package/src/components/ui/controls/material-picker.tsx +154 -164
- package/src/components/ui/controls/slider-control.tsx +66 -18
- package/src/components/ui/floating-level-selector.tsx +286 -55
- package/src/components/ui/helpers/helper-manager.tsx +10 -0
- package/src/components/ui/item-catalog/catalog-items.tsx +2563 -1239
- package/src/components/ui/item-catalog/item-catalog.tsx +96 -187
- package/src/components/ui/level-duplicate-dialog.tsx +113 -0
- package/src/components/ui/panels/ceiling-panel.tsx +3 -28
- package/src/components/ui/panels/column-panel.tsx +759 -0
- package/src/components/ui/panels/door-panel.tsx +989 -290
- package/src/components/ui/panels/fence-panel.tsx +2 -49
- package/src/components/ui/panels/mobile-panel-sheet.tsx +108 -0
- package/src/components/ui/panels/mobile-selection-bar.tsx +100 -0
- package/src/components/ui/panels/node-display.ts +39 -0
- package/src/components/ui/panels/paint-panel.tsx +163 -0
- package/src/components/ui/panels/panel-manager.tsx +208 -28
- package/src/components/ui/panels/panel-wrapper.tsx +48 -39
- package/src/components/ui/panels/reference-panel.tsx +253 -5
- package/src/components/ui/panels/roof-panel.tsx +13 -64
- package/src/components/ui/panels/roof-segment-panel.tsx +0 -25
- package/src/components/ui/panels/slab-panel.tsx +4 -30
- package/src/components/ui/panels/spawn-panel.tsx +161 -0
- package/src/components/ui/panels/stair-panel.tsx +20 -74
- package/src/components/ui/panels/stair-segment-panel.tsx +0 -25
- package/src/components/ui/panels/wall-panel.tsx +10 -8
- package/src/components/ui/panels/window-panel.tsx +668 -139
- package/src/components/ui/primitives/number-input.tsx +1 -1
- package/src/components/ui/primitives/sidebar.tsx +0 -0
- package/src/components/ui/sidebar/app-sidebar.tsx +0 -0
- package/src/components/ui/sidebar/icon-rail.tsx +0 -0
- package/src/components/ui/sidebar/mobile-tab-bar.tsx +46 -0
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +330 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +0 -0
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +0 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +77 -0
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +105 -22
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +0 -0
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +76 -0
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +11 -3
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +10 -5
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +1 -1
- package/src/components/ui/sidebar/tab-bar.tsx +3 -0
- package/src/components/ui/slider.tsx +1 -1
- package/src/components/viewer-overlay.tsx +0 -0
- package/src/components/viewer-zone-system.tsx +0 -0
- package/src/hooks/use-auto-frame.ts +45 -0
- package/src/hooks/use-auto-save.ts +14 -0
- package/src/hooks/use-keyboard.ts +74 -7
- package/src/hooks/use-mobile.ts +12 -12
- package/src/index.tsx +8 -1
- package/src/lib/door-interaction.ts +88 -0
- package/src/lib/floorplan/geometry.ts +263 -0
- package/src/lib/floorplan/index.ts +38 -0
- package/src/lib/floorplan/items.ts +179 -0
- package/src/lib/floorplan/selection-tool.ts +231 -0
- package/src/lib/floorplan/stairs.ts +478 -0
- package/src/lib/floorplan/types.ts +57 -0
- package/src/lib/floorplan/walls.ts +23 -0
- package/src/lib/guide-events.ts +10 -0
- package/src/lib/level-duplication.test.ts +70 -0
- package/src/lib/level-duplication.ts +153 -0
- package/src/lib/local-guide-image.ts +42 -0
- package/src/lib/material-paint.ts +284 -0
- package/src/lib/roof-duplication.ts +214 -0
- package/src/lib/scene-bounds.test.ts +183 -0
- package/src/lib/scene-bounds.ts +169 -0
- package/src/lib/scene.ts +0 -0
- package/src/lib/sfx-bus.ts +2 -0
- package/src/lib/sfx-player.ts +5 -5
- package/src/lib/stair-duplication.ts +126 -0
- package/src/lib/window-interaction.ts +86 -0
- package/src/store/use-editor.tsx +186 -62
- package/tsconfig.json +2 -1
- package/src/components/feedback-dialog.tsx +0 -265
- package/src/components/pascal-radio.tsx +0 -280
- package/src/components/preview-button.tsx +0 -16
- package/src/components/ui/viewer-toolbar.tsx +0 -395
|
@@ -1,23 +1,84 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { useScene } from '@pascal-app/core'
|
|
4
|
+
import { useViewer } from '@pascal-app/viewer'
|
|
3
5
|
import { AnimatePresence, motion } from 'motion/react'
|
|
6
|
+
import { useEffect, useMemo } from 'react'
|
|
7
|
+
import { MaterialPicker } from './../../../components/ui/controls/material-picker'
|
|
4
8
|
import { TooltipProvider } from './../../../components/ui/primitives/tooltip'
|
|
9
|
+
import { useIsMobile } from './../../../hooks/use-mobile'
|
|
5
10
|
import { useReducedMotion } from './../../../hooks/use-reduced-motion'
|
|
11
|
+
import { resolvePaintTargetFromSelection } from './../../../lib/material-paint'
|
|
6
12
|
import { cn } from './../../../lib/utils'
|
|
7
13
|
import useEditor from './../../../store/use-editor'
|
|
8
|
-
import { ItemCatalog } from '../item-catalog/item-catalog'
|
|
9
14
|
import { CameraActions } from './camera-actions'
|
|
10
15
|
import { ControlModes } from './control-modes'
|
|
11
|
-
import { FurnishTools } from './furnish-tools'
|
|
12
16
|
import { StructureTools } from './structure-tools'
|
|
13
|
-
import {
|
|
17
|
+
import { GridSnapControl, SecondaryToggles } from './view-toggles'
|
|
18
|
+
|
|
19
|
+
// Mobile bottom offset matches the viewer's overlap behind the sheet's
|
|
20
|
+
// rounded corners (SHEET_OVERLAP_PX in editor-layout-mobile) so the menu sits
|
|
21
|
+
// just above that strip instead of inside it.
|
|
22
|
+
const MOBILE_BOTTOM_OFFSET = 24
|
|
23
|
+
|
|
24
|
+
function PaintMaterialTray() {
|
|
25
|
+
const activePaintMaterial = useEditor((state) => state.activePaintMaterial)
|
|
26
|
+
const activePaintTarget = useEditor((state) => state.activePaintTarget)
|
|
27
|
+
const setActivePaintMaterial = useEditor((state) => state.setActivePaintMaterial)
|
|
28
|
+
const setActivePaintTarget = useEditor((state) => state.setActivePaintTarget)
|
|
29
|
+
const selectedIds = useViewer((state) => state.selection.selectedIds)
|
|
30
|
+
const nodes = useScene((state) => state.nodes)
|
|
31
|
+
const selectedId = selectedIds.length === 1 ? (selectedIds[0] ?? null) : null
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const selectedPaintTarget = resolvePaintTargetFromSelection({
|
|
35
|
+
nodes,
|
|
36
|
+
selectedId,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
if (selectedPaintTarget) {
|
|
40
|
+
setActivePaintTarget(selectedPaintTarget)
|
|
41
|
+
}
|
|
42
|
+
}, [nodes, selectedId, setActivePaintTarget])
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className="w-[42rem] max-w-[calc(100vw-2rem)]">
|
|
46
|
+
<MaterialPicker
|
|
47
|
+
onChange={(material) => {
|
|
48
|
+
setActivePaintMaterial({ material, sourceTarget: activePaintTarget })
|
|
49
|
+
}}
|
|
50
|
+
onSelectMaterialPreset={(materialPreset) => {
|
|
51
|
+
setActivePaintMaterial({ materialPreset, sourceTarget: activePaintTarget })
|
|
52
|
+
}}
|
|
53
|
+
selectedMaterialPreset={activePaintMaterial?.materialPreset}
|
|
54
|
+
value={activePaintMaterial?.material}
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
14
59
|
|
|
15
60
|
export function ActionMenu({ className }: { className?: string }) {
|
|
16
61
|
const phase = useEditor((state) => state.phase)
|
|
17
62
|
const mode = useEditor((state) => state.mode)
|
|
18
63
|
const tool = useEditor((state) => state.tool)
|
|
19
64
|
const catalogCategory = useEditor((state) => state.catalogCategory)
|
|
65
|
+
const isMobile = useIsMobile()
|
|
66
|
+
const hasSelectionOnMobile = useViewer((s) => isMobile && s.selection.selectedIds.length > 0)
|
|
67
|
+
const hasReferenceOnMobile = useEditor((s) => isMobile && Boolean(s.selectedReferenceId))
|
|
68
|
+
const CONTEXTUAL_TABS = new Set(['ai', 'items', 'studio'])
|
|
69
|
+
const isContextualPanelOnMobile = useEditor(
|
|
70
|
+
(s) => isMobile && CONTEXTUAL_TABS.has(s.activeSidebarPanel),
|
|
71
|
+
)
|
|
20
72
|
const reducedMotion = useReducedMotion()
|
|
73
|
+
const showPaintTray = useMemo(() => mode === 'material-paint', [mode])
|
|
74
|
+
|
|
75
|
+
// On mobile, defer the bottom rail to the selection bar when something
|
|
76
|
+
// is selected — the contextual actions take priority over mode controls.
|
|
77
|
+
// Also hide on Chat / Items / Studio tabs; those are contextual workflows
|
|
78
|
+
// (composing / picking furniture / generating renders) where the build
|
|
79
|
+
// menu is irrelevant.
|
|
80
|
+
if (hasSelectionOnMobile || hasReferenceOnMobile || isContextualPanelOnMobile) return null
|
|
81
|
+
|
|
21
82
|
const transition = reducedMotion
|
|
22
83
|
? { duration: 0 }
|
|
23
84
|
: { type: 'spring' as const, bounce: 0.2, duration: 0.4 }
|
|
@@ -26,49 +87,19 @@ export function ActionMenu({ className }: { className?: string }) {
|
|
|
26
87
|
<TooltipProvider>
|
|
27
88
|
<motion.div
|
|
28
89
|
className={cn(
|
|
29
|
-
'
|
|
90
|
+
'left-1/2 z-50 -translate-x-1/2',
|
|
91
|
+
isMobile ? 'absolute origin-bottom scale-90' : 'fixed bottom-6',
|
|
30
92
|
'rounded-2xl border border-border bg-background/90 shadow-2xl backdrop-blur-md',
|
|
31
93
|
'transition-colors duration-200 ease-out',
|
|
32
94
|
className,
|
|
33
95
|
)}
|
|
34
96
|
layout
|
|
97
|
+
style={isMobile ? { bottom: MOBILE_BOTTOM_OFFSET } : undefined}
|
|
35
98
|
transition={transition}
|
|
36
99
|
>
|
|
37
|
-
{/*
|
|
38
|
-
<AnimatePresence>
|
|
39
|
-
{mode === 'build' && tool === 'item' && catalogCategory && (
|
|
40
|
-
<motion.div
|
|
41
|
-
animate={{
|
|
42
|
-
opacity: 1,
|
|
43
|
-
maxHeight: 160,
|
|
44
|
-
paddingTop: 8,
|
|
45
|
-
paddingBottom: 8,
|
|
46
|
-
borderBottomWidth: 1,
|
|
47
|
-
}}
|
|
48
|
-
className={cn('overflow-hidden border-border border-b px-2 py-2')}
|
|
49
|
-
exit={{
|
|
50
|
-
opacity: 0,
|
|
51
|
-
maxHeight: 0,
|
|
52
|
-
paddingTop: 0,
|
|
53
|
-
paddingBottom: 0,
|
|
54
|
-
borderBottomWidth: 0,
|
|
55
|
-
}}
|
|
56
|
-
initial={{
|
|
57
|
-
opacity: 0,
|
|
58
|
-
maxHeight: 0,
|
|
59
|
-
paddingTop: 0,
|
|
60
|
-
paddingBottom: 0,
|
|
61
|
-
borderBottomWidth: 0,
|
|
62
|
-
}}
|
|
63
|
-
transition={transition}
|
|
64
|
-
>
|
|
65
|
-
<ItemCatalog category={catalogCategory} key={catalogCategory} />
|
|
66
|
-
</motion.div>
|
|
67
|
-
)}
|
|
68
|
-
</AnimatePresence>
|
|
69
|
-
|
|
100
|
+
{/* Structure Tools Row - Animated */}
|
|
70
101
|
<AnimatePresence>
|
|
71
|
-
{phase === '
|
|
102
|
+
{phase === 'structure' && mode === 'build' && (
|
|
72
103
|
<motion.div
|
|
73
104
|
animate={{
|
|
74
105
|
opacity: 1,
|
|
@@ -77,10 +108,7 @@ export function ActionMenu({ className }: { className?: string }) {
|
|
|
77
108
|
paddingBottom: 8,
|
|
78
109
|
borderBottomWidth: 1,
|
|
79
110
|
}}
|
|
80
|
-
className={cn(
|
|
81
|
-
'overflow-hidden border-border',
|
|
82
|
-
'max-h-20 border-b px-2 py-2 opacity-100',
|
|
83
|
-
)}
|
|
111
|
+
className={cn('max-h-20 overflow-hidden border-border border-b px-2 py-2')}
|
|
84
112
|
exit={{
|
|
85
113
|
opacity: 0,
|
|
86
114
|
maxHeight: 0,
|
|
@@ -97,25 +125,24 @@ export function ActionMenu({ className }: { className?: string }) {
|
|
|
97
125
|
}}
|
|
98
126
|
transition={transition}
|
|
99
127
|
>
|
|
100
|
-
<div className="
|
|
101
|
-
<
|
|
128
|
+
<div className="w-max">
|
|
129
|
+
<StructureTools />
|
|
102
130
|
</div>
|
|
103
131
|
</motion.div>
|
|
104
132
|
)}
|
|
105
133
|
</AnimatePresence>
|
|
106
134
|
|
|
107
|
-
{/* Structure Tools Row - Animated */}
|
|
108
135
|
<AnimatePresence>
|
|
109
|
-
{
|
|
136
|
+
{showPaintTray && (
|
|
110
137
|
<motion.div
|
|
111
138
|
animate={{
|
|
112
139
|
opacity: 1,
|
|
113
|
-
maxHeight:
|
|
140
|
+
maxHeight: 96,
|
|
114
141
|
paddingTop: 8,
|
|
115
142
|
paddingBottom: 8,
|
|
116
143
|
borderBottomWidth: 1,
|
|
117
144
|
}}
|
|
118
|
-
className={cn('
|
|
145
|
+
className={cn('overflow-hidden border-border border-b px-3')}
|
|
119
146
|
exit={{
|
|
120
147
|
opacity: 0,
|
|
121
148
|
maxHeight: 0,
|
|
@@ -132,20 +159,32 @@ export function ActionMenu({ className }: { className?: string }) {
|
|
|
132
159
|
}}
|
|
133
160
|
transition={transition}
|
|
134
161
|
>
|
|
135
|
-
<
|
|
136
|
-
<StructureTools />
|
|
137
|
-
</div>
|
|
162
|
+
<PaintMaterialTray />
|
|
138
163
|
</motion.div>
|
|
139
164
|
)}
|
|
140
165
|
</AnimatePresence>
|
|
141
|
-
{
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
166
|
+
{isMobile ? (
|
|
167
|
+
<div className="flex flex-col items-stretch gap-0.5 px-2 py-1.5">
|
|
168
|
+
{/* Row 1: control modes only */}
|
|
169
|
+
<div className="flex items-center justify-center gap-1">
|
|
170
|
+
<ControlModes />
|
|
171
|
+
</div>
|
|
172
|
+
{/* Row 2: grid snap + secondary toggles (orbit + top view hidden) */}
|
|
173
|
+
<div className="flex items-center justify-center gap-1 border-border/50 border-t pt-1">
|
|
174
|
+
<GridSnapControl />
|
|
175
|
+
<SecondaryToggles />
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
) : (
|
|
179
|
+
<div className="flex items-center justify-center gap-1 px-2 py-1.5">
|
|
180
|
+
<ControlModes />
|
|
181
|
+
<div className="mx-1 h-5 w-px bg-border" />
|
|
182
|
+
<GridSnapControl />
|
|
183
|
+
<SecondaryToggles />
|
|
184
|
+
<div className="mx-1 h-5 w-px bg-border" />
|
|
185
|
+
<CameraActions />
|
|
186
|
+
</div>
|
|
187
|
+
)}
|
|
149
188
|
</motion.div>
|
|
150
189
|
</TooltipProvider>
|
|
151
190
|
)
|
|
@@ -24,12 +24,14 @@ export const tools: ToolConfig[] = [
|
|
|
24
24
|
// { id: 'custom-room', iconSrc: '/icons/custom-room.png', label: 'Custom Room' },
|
|
25
25
|
{ id: 'slab', iconSrc: '/icons/floor.png', label: 'Slab' },
|
|
26
26
|
{ id: 'ceiling', iconSrc: '/icons/ceiling.png', label: 'Ceiling' },
|
|
27
|
+
{ id: 'column', iconSrc: '/icons/column.png', label: 'Column' },
|
|
27
28
|
{ id: 'roof', iconSrc: '/icons/roof.png', label: 'Gable Roof' },
|
|
28
29
|
{ id: 'stair', iconSrc: '/icons/stairs.png', label: 'Stairs' },
|
|
29
30
|
{ id: 'door', iconSrc: '/icons/door.png', label: 'Door' },
|
|
30
31
|
{ id: 'window', iconSrc: '/icons/window.png', label: 'Window' },
|
|
31
32
|
{ id: 'fence', iconSrc: '/icons/fence.png', label: 'Fence' },
|
|
32
33
|
{ id: 'zone', iconSrc: '/icons/zone.png', label: 'Zone' },
|
|
34
|
+
{ id: 'spawn', iconSrc: '/icons/site.png', label: 'Spawn Point' },
|
|
33
35
|
]
|
|
34
36
|
|
|
35
37
|
export function StructureTools() {
|