@pascal-app/editor 0.9.1 → 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.
- package/package.json +12 -9
- package/src/components/editor/alignment-3d-guide-layer.tsx +10 -2
- package/src/components/editor/bake-exporter.tsx +47 -0
- package/src/components/editor/custom-camera-controls.tsx +63 -25
- package/src/components/editor/editor-layout-mobile.tsx +5 -0
- package/src/components/editor/editor-layout-v2.tsx +18 -0
- package/src/components/editor/export-manager.tsx +71 -63
- package/src/components/editor/fence-tangent-lines-3d.tsx +87 -0
- package/src/components/editor/first-person/build-collider-world.test.ts +36 -3
- package/src/components/editor/first-person/build-collider-world.ts +32 -5
- package/src/components/editor/first-person-controls.tsx +118 -7
- package/src/components/editor/floating-action-menu.tsx +410 -33
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floorplan-background-selection.test.ts +58 -0
- package/src/components/editor/floorplan-background-selection.ts +3 -2
- package/src/components/editor/floorplan-panel.tsx +1804 -849
- package/src/components/editor/grid.tsx +166 -39
- package/src/components/editor/group-actions.ts +457 -0
- package/src/components/editor/group-floating-action-menu.tsx +130 -0
- package/src/components/editor/group-move-3d.ts +397 -0
- package/src/components/editor/group-rotate-handle.tsx +102 -64
- package/src/components/editor/group-selection-box-3d.tsx +173 -0
- package/src/components/editor/group-transform-shared.test.ts +192 -1
- package/src/components/editor/group-transform-shared.ts +240 -7
- package/src/components/editor/handles/handle-arrow.tsx +81 -23
- package/src/components/editor/handles/use-handle-drag.ts +37 -4
- package/src/components/editor/index.tsx +248 -27
- package/src/components/editor/measurement-pill.tsx +62 -22
- package/src/components/editor/node-action-menu.tsx +14 -1
- package/src/components/editor/node-arrow-handles.tsx +366 -173
- package/src/components/editor/opening-guides-3d-layer.tsx +144 -0
- package/src/components/editor/quick-measurement-card.tsx +70 -0
- package/src/components/editor/quick-measurement-hud.tsx +28 -0
- package/src/components/editor/riser-diagram-panel.tsx +137 -0
- package/src/components/editor/selection-manager.tsx +812 -466
- package/src/components/editor/site-edge-labels.tsx +4 -4
- package/src/components/editor/slab-hole-highlights.tsx +13 -5
- package/src/components/editor/snapshot-capture-overlay.tsx +255 -115
- package/src/components/editor/three-context-bridge.ts +22 -0
- package/src/components/editor/thumbnail-generator.tsx +136 -43
- package/src/components/editor/use-floorplan-background-placement.ts +113 -45
- package/src/components/editor/use-floorplan-scene-data.ts +5 -5
- package/src/components/editor/use-mesh-settle-epoch.ts +26 -0
- package/src/components/editor/wall-measurement-label.tsx +3 -13
- package/src/components/editor/wall-move-side-handles.tsx +36 -21
- package/src/components/editor/wall-snap-beacon-layer.tsx +160 -6
- package/src/components/editor-2d/floorplan-action-menu-layer.tsx +9 -5
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +1 -1
- package/src/components/editor-2d/floorplan-group-action-menu.tsx +87 -0
- package/src/components/editor-2d/floorplan-group-move.tsx +701 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +113 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +1656 -0
- package/src/components/editor-2d/floorplan-quick-measure-layer.tsx +212 -0
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +169 -4
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +300 -59
- package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +5 -3
- package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +15 -0
- package/src/components/editor-2d/renderers/floorplan-label-angle.ts +14 -0
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +4 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +308 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +1704 -342
- package/src/components/editor-2d/renderers/floorplan-voronoi-layer.tsx +128 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +419 -60
- package/src/components/systems/ceiling/ceiling-system.tsx +97 -4
- package/src/components/systems/roof/roof-edit-system.tsx +1768 -13
- package/src/components/systems/selection-affordance-manager.tsx +38 -0
- package/src/components/systems/zone/zone-label-editor-system.tsx +14 -1
- package/src/components/systems/zone/zone-system.tsx +42 -13
- package/src/components/tools/elevator/elevator-tool.tsx +34 -8
- package/src/components/tools/elevator/move-elevator-tool.tsx +13 -2
- package/src/components/tools/fence/fence-drafting.ts +80 -8
- package/src/components/tools/item/move-tool.tsx +17 -13
- package/src/components/tools/item/placement-math.test.ts +13 -1
- package/src/components/tools/item/placement-math.ts +28 -2
- package/src/components/tools/item/placement-strategies.ts +246 -5
- package/src/components/tools/item/placement-types.ts +13 -1
- package/src/components/tools/item/use-draft-node.ts +31 -1
- package/src/components/tools/item/use-placement-coordinator.tsx +761 -121
- package/src/components/tools/registry/move-registry-node-tool.tsx +561 -99
- package/src/components/tools/roof/roof-tool.tsx +320 -94
- package/src/components/tools/select/box-select-state.ts +18 -7
- package/src/components/tools/select/box-select-tool.tsx +176 -40
- package/src/components/tools/select/marquee-geometry.test.ts +161 -0
- package/src/components/tools/select/marquee-geometry.ts +155 -0
- package/src/components/tools/select/plane-box-select-tool.tsx +1 -8
- package/src/components/tools/select/select-candidates.ts +1 -1
- package/src/components/tools/shared/cursor-sphere.tsx +62 -26
- package/src/components/tools/shared/drag-bounding-box.tsx +28 -4
- package/src/components/tools/shared/facing-indicator.tsx +87 -0
- package/src/components/tools/shared/facing-pose-indicator.tsx +54 -0
- package/src/components/tools/shared/placement-box.tsx +183 -1
- package/src/components/tools/shared/polygon-editor.tsx +276 -35
- package/src/components/tools/site/site-boundary-editor.tsx +88 -36
- package/src/components/tools/stair/stair-defaults.ts +1 -0
- package/src/components/tools/stair/stair-tool.tsx +82 -11
- package/src/components/tools/tool-manager.tsx +86 -25
- package/src/components/tools/wall/wall-drafting.test.ts +330 -0
- package/src/components/tools/wall/wall-drafting.ts +180 -103
- package/src/components/tools/wall/wall-snap-geometry.test.ts +44 -0
- package/src/components/tools/wall/wall-snap-geometry.ts +67 -6
- package/src/components/tools/zone/zone-boundary-editor.tsx +5 -1
- package/src/components/tools/zone/zone-tool.tsx +82 -88
- package/src/components/ui/action-menu/camera-actions.tsx +24 -17
- package/src/components/ui/action-menu/control-modes.tsx +40 -93
- package/src/components/ui/action-menu/furnish-tools.tsx +5 -5
- package/src/components/ui/action-menu/index.tsx +2 -4
- package/src/components/ui/action-menu/measurement-control.tsx +188 -0
- package/src/components/ui/action-menu/structure-tools.tsx +22 -13
- package/src/components/ui/action-menu/view-toggles.tsx +42 -90
- package/src/components/ui/command-palette/editor-commands.tsx +2 -2
- package/src/components/ui/command-palette/index.tsx +4 -3
- package/src/components/ui/controls/material-paint-panel.tsx +86 -17
- package/src/components/ui/controls/material-picker.tsx +83 -152
- package/src/components/ui/controls/material-properties-editor.tsx +108 -0
- package/src/components/ui/controls/metric-control.tsx +118 -44
- package/src/components/ui/controls/scene-material-list.tsx +247 -0
- package/src/components/ui/controls/slider-control.tsx +88 -38
- package/src/components/ui/floating-level-selector.tsx +1 -1
- package/src/components/ui/helpers/building-helper.tsx +10 -23
- package/src/components/ui/helpers/contextual-helper-panel.tsx +428 -0
- package/src/components/ui/helpers/helper-manager.tsx +227 -14
- package/src/components/ui/helpers/item-helper.tsx +28 -32
- package/src/components/ui/helpers/registered-tool-helper.tsx +45 -11
- package/src/components/ui/helpers/roof-helper.tsx +10 -12
- package/src/components/ui/icon-ref.tsx +47 -0
- package/src/components/ui/item-catalog/catalog-items.tsx +39 -1
- package/src/components/ui/item-catalog/item-catalog.tsx +13 -36
- package/src/components/ui/level-duplicate-dialog.tsx +1 -1
- package/src/components/ui/panels/node-display.ts +17 -17
- package/src/components/ui/panels/panel-manager.tsx +22 -13
- package/src/components/ui/panels/panel-wrapper.tsx +24 -3
- package/src/components/ui/panels/parametric-inspector.tsx +15 -2
- package/src/components/ui/panels/reference-panel.tsx +54 -19
- package/src/components/ui/primitives/shortcut-token.tsx +39 -3
- package/src/components/ui/primitives/sidebar.tsx +1 -0
- package/src/components/ui/sidebar/app-sidebar.tsx +5 -1
- package/src/components/ui/sidebar/icon-rail.tsx +50 -31
- package/src/components/ui/sidebar/panels/plugins-panel.tsx +218 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +71 -40
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +60 -6
- package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +12 -4
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -3
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +10 -7
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +7 -2
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +10 -7
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +10 -7
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +40 -17
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +31 -17
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +72 -25
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +7 -3
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +12 -8
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +4 -3
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +10 -7
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +44 -3
- package/src/components/ui/sidebar/panels/site-panel/tree-structure.ts +36 -0
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +7 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +3 -3
- package/src/components/ui/sidebar/tab-bar.tsx +42 -28
- package/src/components/ui/sidebar/use-plugin-panels.tsx +132 -0
- package/src/components/ui/snap-target-badge.test.tsx +40 -0
- package/src/components/ui/snap-target-badge.tsx +88 -0
- package/src/components/viewer-overlay.tsx +85 -10
- package/src/components/viewer-zone-system.tsx +6 -1
- package/src/hooks/use-auto-save.test.ts +14 -0
- package/src/hooks/use-auto-save.ts +54 -7
- package/src/hooks/use-ceiling-events.ts +3 -2
- package/src/hooks/use-drag-action.ts +4 -1
- package/src/hooks/use-keyboard.ts +385 -40
- package/src/index.tsx +219 -6
- package/src/lib/active-placement-surface.ts +35 -0
- package/src/lib/ceiling-plan-snap.ts +24 -0
- package/src/lib/contextual-help.test.ts +112 -0
- package/src/lib/contextual-help.ts +144 -0
- package/src/lib/continuation.ts +64 -0
- package/src/lib/direct-manipulation.test.ts +192 -0
- package/src/lib/direct-manipulation.ts +109 -0
- package/src/lib/editor-api.ts +23 -35
- package/src/lib/floorplan/apply-alignment.test.ts +25 -0
- package/src/lib/floorplan/apply-alignment.ts +18 -12
- package/src/lib/floorplan/floorplan-export.tsx +364 -0
- package/src/lib/floorplan/geometry.ts +53 -0
- package/src/lib/floorplan/index.ts +3 -0
- package/src/lib/floorplan/items.ts +5 -2
- package/src/lib/floorplan/plan-coords.ts +21 -0
- package/src/lib/fresh-planar-placement.test.ts +240 -3
- package/src/lib/fresh-planar-placement.ts +68 -1
- package/src/lib/glb-export.test.ts +441 -0
- package/src/lib/glb-export.ts +874 -0
- package/src/lib/history.ts +9 -0
- package/src/lib/interaction/hot-set.test.ts +133 -0
- package/src/lib/interaction/hot-set.ts +67 -0
- package/src/lib/interaction/overlay-policy.test.ts +51 -0
- package/src/lib/interaction/overlay-policy.ts +59 -0
- package/src/lib/interaction/scope.ts +211 -0
- package/src/lib/level-selection.ts +2 -2
- package/src/lib/material-paint.ts +36 -49
- package/src/lib/measurement-kind.test.ts +30 -0
- package/src/lib/measurement-kind.ts +19 -0
- package/src/lib/measurement-label.test.ts +47 -0
- package/src/lib/measurement-label.ts +68 -0
- package/src/lib/measurement-parser.ts +116 -0
- package/src/lib/measurements.test.ts +183 -0
- package/src/lib/measurements.ts +199 -0
- package/src/lib/paint-scope.test.ts +454 -0
- package/src/lib/paint-scope.ts +461 -0
- package/src/lib/placement-drag-release.ts +23 -0
- package/src/lib/planar-cursor-placement.test.ts +57 -0
- package/src/lib/plugin-panels.test.ts +19 -0
- package/src/lib/plugin-panels.ts +91 -0
- package/src/lib/quick-measurement.test.ts +77 -0
- package/src/lib/quick-measurement.ts +109 -0
- package/src/lib/roof-duplication.ts +6 -6
- package/src/lib/roof-hover-outline-proxy.ts +22 -0
- package/src/lib/roof-wall-hit.ts +164 -0
- package/src/lib/scene-clipboard.test.ts +196 -0
- package/src/lib/scene-clipboard.ts +73 -8
- package/src/lib/scene.ts +28 -5
- package/src/lib/selection-routing.test.ts +298 -0
- package/src/lib/selection-routing.ts +175 -0
- package/src/lib/sfx/index.ts +1 -0
- package/src/lib/sfx/movement-tick.test.ts +65 -0
- package/src/lib/sfx/movement-tick.ts +18 -0
- package/src/lib/sfx-bus.ts +50 -13
- package/src/lib/sfx-player.test.ts +124 -0
- package/src/lib/sfx-player.ts +111 -66
- package/src/lib/slab-plan-snap.test.ts +93 -0
- package/src/lib/slab-plan-snap.ts +108 -0
- package/src/lib/snapping-mode.test.ts +138 -0
- package/src/lib/snapping-mode.ts +179 -0
- package/src/lib/stair-duplication.ts +4 -4
- package/src/lib/surface-plan-snap.test.ts +71 -0
- package/src/lib/surface-plan-snap.ts +256 -0
- package/src/lib/use-linear-display.ts +40 -0
- package/src/lib/world-grid-snap.ts +37 -5
- package/src/store/use-direct-manipulation-feedback.ts +20 -0
- package/src/store/use-editor.tsx +484 -114
- package/src/store/use-facing-pose.ts +44 -0
- package/src/store/use-fence-curve-draft.ts +21 -0
- package/src/store/use-floorplan-draft-preview.ts +101 -0
- package/src/store/use-floorplan-marquee.ts +50 -0
- package/src/store/use-interaction-scope.test.ts +186 -0
- package/src/store/use-interaction-scope.ts +132 -0
- package/src/store/use-measurement-draft.test.ts +530 -0
- package/src/store/use-measurement-draft.ts +543 -0
- package/src/store/use-opening-guides.ts +41 -0
- package/src/store/use-placement-preview.ts +11 -3
- package/src/store/use-quick-measurement-hud.test.ts +53 -0
- package/src/store/use-quick-measurement-hud.ts +77 -0
- package/src/store/use-segment-draft-chain.ts +28 -0
- package/src/store/use-stair-build-preview.ts +43 -0
- package/src/store/use-wall-snap-indicator.ts +2 -0
- package/src/components/editor/first-person/bvh-ecctrl.tsx +0 -860
- package/src/components/editor/group-move-handle.tsx +0 -316
- package/src/components/ui/panels/paint-panel.tsx +0 -163
- package/src/lib/level-name.ts +0 -11
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useViewer } from '@pascal-app/viewer'
|
|
4
|
+
import {
|
|
5
|
+
Box,
|
|
6
|
+
Check,
|
|
7
|
+
ChevronDown,
|
|
8
|
+
Eye,
|
|
9
|
+
EyeOff,
|
|
10
|
+
Ruler,
|
|
11
|
+
ScanSearch,
|
|
12
|
+
Square,
|
|
13
|
+
Triangle,
|
|
14
|
+
Waypoints,
|
|
15
|
+
} from 'lucide-react'
|
|
16
|
+
import { useState } from 'react'
|
|
17
|
+
import type { CreatableMeasurementKind } from '../../../lib/measurement-kind'
|
|
18
|
+
import { cn } from '../../../lib/utils'
|
|
19
|
+
import useEditor from '../../../store/use-editor'
|
|
20
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../primitives/popover'
|
|
21
|
+
import { ActionButton } from './action-button'
|
|
22
|
+
|
|
23
|
+
const measurementOptions = [
|
|
24
|
+
{ kind: 'distance', label: 'Distance', icon: Ruler },
|
|
25
|
+
{ kind: 'angle', label: 'Angle', icon: Triangle },
|
|
26
|
+
{ kind: 'area', label: 'Area', icon: Square },
|
|
27
|
+
{ kind: 'perimeter', label: 'Perimeter', icon: Waypoints },
|
|
28
|
+
{ kind: 'volume', label: 'Volume', icon: Box },
|
|
29
|
+
] as const satisfies readonly {
|
|
30
|
+
kind: CreatableMeasurementKind
|
|
31
|
+
label: string
|
|
32
|
+
icon: typeof Ruler
|
|
33
|
+
}[]
|
|
34
|
+
|
|
35
|
+
const measurementMenuOptions = [
|
|
36
|
+
{ kind: 'smart', label: 'Smart', icon: ScanSearch },
|
|
37
|
+
...measurementOptions,
|
|
38
|
+
] as const
|
|
39
|
+
|
|
40
|
+
export function MeasurementControl() {
|
|
41
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
42
|
+
const mode = useEditor((state) => state.mode)
|
|
43
|
+
const tool = useEditor((state) => state.tool)
|
|
44
|
+
const selectedKind = useEditor((state) => state.lastMeasurementKind)
|
|
45
|
+
const activeToolKind = useEditor((state) => state.toolDefaults.measurement?.kind)
|
|
46
|
+
const setMode = useEditor((state) => state.setMode)
|
|
47
|
+
const setPhase = useEditor((state) => state.setPhase)
|
|
48
|
+
const setLastMeasurementKind = useEditor((state) => state.setLastMeasurementKind)
|
|
49
|
+
const setStructureLayer = useEditor((state) => state.setStructureLayer)
|
|
50
|
+
const setTool = useEditor((state) => state.setTool)
|
|
51
|
+
const setToolDefaults = useEditor((state) => state.setToolDefaults)
|
|
52
|
+
const showMeasurements = useViewer((state) => state.showMeasurements)
|
|
53
|
+
const setShowMeasurements = useViewer((state) => state.setShowMeasurements)
|
|
54
|
+
|
|
55
|
+
const selectedOption =
|
|
56
|
+
measurementOptions.find((option) => option.kind === selectedKind) ?? measurementOptions[0]
|
|
57
|
+
const isActive = mode === 'build' && tool === 'measurement'
|
|
58
|
+
const isSmartActive = isActive && activeToolKind === 'smart'
|
|
59
|
+
const SelectedIcon = isSmartActive ? ScanSearch : selectedOption.icon
|
|
60
|
+
const selectedLabel = isSmartActive ? 'Smart' : selectedOption.label
|
|
61
|
+
|
|
62
|
+
const activateMeasurement = (kind: CreatableMeasurementKind) => {
|
|
63
|
+
setPhase('structure')
|
|
64
|
+
setStructureLayer('elements')
|
|
65
|
+
setLastMeasurementKind(kind)
|
|
66
|
+
setToolDefaults('measurement', { kind })
|
|
67
|
+
setMode('build')
|
|
68
|
+
setTool('measurement')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const handlePrimaryClick = () => {
|
|
72
|
+
if (isActive) {
|
|
73
|
+
setMode('select')
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
activateMeasurement(selectedKind)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const activateSmartMeasurement = () => {
|
|
80
|
+
setPhase('structure')
|
|
81
|
+
setStructureLayer('elements')
|
|
82
|
+
setToolDefaults('measurement', { kind: 'smart' })
|
|
83
|
+
setMode('build')
|
|
84
|
+
setTool('measurement')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Popover onOpenChange={setIsOpen} open={isOpen}>
|
|
89
|
+
<div className="flex items-center">
|
|
90
|
+
<ActionButton
|
|
91
|
+
aria-label={`Measure: ${selectedLabel}`}
|
|
92
|
+
aria-pressed={isActive}
|
|
93
|
+
className={cn(
|
|
94
|
+
'rounded-r-none p-0 text-muted-foreground',
|
|
95
|
+
isActive
|
|
96
|
+
? 'bg-cyan-500/20 text-cyan-400 hover:bg-cyan-500/20'
|
|
97
|
+
: 'hover:bg-cyan-500/15 hover:text-cyan-400',
|
|
98
|
+
)}
|
|
99
|
+
label={`Measure: ${selectedLabel}`}
|
|
100
|
+
onClick={handlePrimaryClick}
|
|
101
|
+
shortcut="M"
|
|
102
|
+
size="icon"
|
|
103
|
+
variant="ghost"
|
|
104
|
+
>
|
|
105
|
+
<SelectedIcon aria-hidden="true" className="h-5 w-5" />
|
|
106
|
+
</ActionButton>
|
|
107
|
+
|
|
108
|
+
<PopoverTrigger asChild>
|
|
109
|
+
<button
|
|
110
|
+
aria-expanded={isOpen}
|
|
111
|
+
aria-haspopup="menu"
|
|
112
|
+
aria-label="Measurement options"
|
|
113
|
+
className={cn(
|
|
114
|
+
'flex h-11 w-6 items-center justify-center rounded-r-lg text-muted-foreground transition-colors',
|
|
115
|
+
isOpen
|
|
116
|
+
? 'bg-cyan-500/15 text-cyan-400'
|
|
117
|
+
: 'hover:bg-cyan-500/10 hover:text-cyan-400',
|
|
118
|
+
)}
|
|
119
|
+
type="button"
|
|
120
|
+
>
|
|
121
|
+
<ChevronDown
|
|
122
|
+
aria-hidden="true"
|
|
123
|
+
className={cn('h-3 w-3 transition-transform', isOpen && 'rotate-180')}
|
|
124
|
+
/>
|
|
125
|
+
</button>
|
|
126
|
+
</PopoverTrigger>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<PopoverContent
|
|
130
|
+
align="center"
|
|
131
|
+
className="w-56 rounded-lg border-border/45 bg-background/96 p-2 shadow-elevation-3 backdrop-blur-xl"
|
|
132
|
+
side="top"
|
|
133
|
+
sideOffset={14}
|
|
134
|
+
>
|
|
135
|
+
<div aria-label="Measurement type" className="space-y-1" role="menu">
|
|
136
|
+
{measurementMenuOptions.map((option) => {
|
|
137
|
+
const OptionIcon = option.icon
|
|
138
|
+
const isSmart = option.kind === 'smart'
|
|
139
|
+
const isSelected = isSmart
|
|
140
|
+
? isSmartActive
|
|
141
|
+
: !isSmartActive && option.kind === selectedKind
|
|
142
|
+
return (
|
|
143
|
+
<button
|
|
144
|
+
aria-checked={isSelected}
|
|
145
|
+
className={cn(
|
|
146
|
+
'flex h-9 w-full items-center gap-2 rounded-md px-2.5 text-left text-sm transition-colors',
|
|
147
|
+
isSelected
|
|
148
|
+
? 'bg-white/10 text-foreground'
|
|
149
|
+
: 'text-muted-foreground hover:bg-white/8 hover:text-foreground',
|
|
150
|
+
)}
|
|
151
|
+
key={option.kind}
|
|
152
|
+
onClick={() => {
|
|
153
|
+
if (isSmart) activateSmartMeasurement()
|
|
154
|
+
else activateMeasurement(option.kind)
|
|
155
|
+
setIsOpen(false)
|
|
156
|
+
}}
|
|
157
|
+
role="menuitemradio"
|
|
158
|
+
type="button"
|
|
159
|
+
>
|
|
160
|
+
<OptionIcon aria-hidden="true" className="h-4 w-4" />
|
|
161
|
+
<span>{option.label}</span>
|
|
162
|
+
{isSelected ? <Check aria-hidden="true" className="ml-auto h-4 w-4" /> : null}
|
|
163
|
+
</button>
|
|
164
|
+
)
|
|
165
|
+
})}
|
|
166
|
+
|
|
167
|
+
<div className="my-1.5 h-px bg-border/60" />
|
|
168
|
+
|
|
169
|
+
<button
|
|
170
|
+
aria-checked={showMeasurements}
|
|
171
|
+
className="flex h-9 w-full items-center gap-2 rounded-md px-2.5 text-left text-muted-foreground text-sm transition-colors hover:bg-white/8 hover:text-foreground"
|
|
172
|
+
onClick={() => setShowMeasurements(!showMeasurements)}
|
|
173
|
+
role="menuitemcheckbox"
|
|
174
|
+
type="button"
|
|
175
|
+
>
|
|
176
|
+
{showMeasurements ? (
|
|
177
|
+
<Eye aria-hidden="true" className="h-4 w-4" />
|
|
178
|
+
) : (
|
|
179
|
+
<EyeOff aria-hidden="true" className="h-4 w-4" />
|
|
180
|
+
)}
|
|
181
|
+
<span>Show measurements</span>
|
|
182
|
+
<span className="ml-auto text-xs">{showMeasurements ? 'On' : 'Off'}</span>
|
|
183
|
+
</button>
|
|
184
|
+
</div>
|
|
185
|
+
</PopoverContent>
|
|
186
|
+
</Popover>
|
|
187
|
+
)
|
|
188
|
+
}
|
|
@@ -12,17 +12,26 @@ export type ToolConfig = {
|
|
|
12
12
|
// for cursor/floorplan indicators. Roof-mounted accessories are intentionally
|
|
13
13
|
// absent — they're placed from the roof inspector's "Add element" section.
|
|
14
14
|
export const tools: ToolConfig[] = [
|
|
15
|
-
{ id: 'wall', iconSrc: '/icons/wall.
|
|
16
|
-
{ id: 'door', iconSrc: '/icons/door.
|
|
17
|
-
{ id: 'window', iconSrc: '/icons/window.
|
|
18
|
-
{ id: 'stair', iconSrc: '/icons/stairs.
|
|
19
|
-
{ id: 'roof', iconSrc: '/icons/roof.
|
|
20
|
-
{ id: 'fence', iconSrc: '/icons/fence.
|
|
21
|
-
{ id: 'column', iconSrc: '/icons/column.
|
|
22
|
-
{ id: 'elevator', iconSrc: '/icons/elevator.
|
|
23
|
-
{ id: 'slab', iconSrc: '/icons/floor.
|
|
24
|
-
{ id: 'ceiling', iconSrc: '/icons/ceiling.
|
|
25
|
-
{ id: 'zone', iconSrc: '/icons/zone.
|
|
26
|
-
{ id: 'spawn', iconSrc: '/icons/spawn-point.
|
|
27
|
-
{ id: 'shelf', iconSrc: '/icons/shelf.
|
|
15
|
+
{ id: 'wall', iconSrc: '/icons/wall.webp', label: 'Wall' },
|
|
16
|
+
{ id: 'door', iconSrc: '/icons/door.webp', label: 'Door' },
|
|
17
|
+
{ id: 'window', iconSrc: '/icons/window.webp', label: 'Window' },
|
|
18
|
+
{ id: 'stair', iconSrc: '/icons/stairs.webp', label: 'Stairs' },
|
|
19
|
+
{ id: 'roof', iconSrc: '/icons/roof.webp', label: 'Gable Roof' },
|
|
20
|
+
{ id: 'fence', iconSrc: '/icons/fence.webp', label: 'Fence' },
|
|
21
|
+
{ id: 'column', iconSrc: '/icons/column.webp', label: 'Column' },
|
|
22
|
+
{ id: 'elevator', iconSrc: '/icons/elevator.webp', label: 'Elevator' },
|
|
23
|
+
{ id: 'slab', iconSrc: '/icons/floor.webp', label: 'Slab' },
|
|
24
|
+
{ id: 'ceiling', iconSrc: '/icons/ceiling.webp', label: 'Ceiling' },
|
|
25
|
+
{ id: 'zone', iconSrc: '/icons/zone.webp', label: 'Zone' },
|
|
26
|
+
{ id: 'spawn', iconSrc: '/icons/spawn-point.webp', label: 'Spawn Point' },
|
|
27
|
+
{ id: 'shelf', iconSrc: '/icons/shelf.webp', label: 'Shelf' },
|
|
28
|
+
{ id: 'duct-segment', iconSrc: '/icons/duct.webp', label: 'Duct' },
|
|
29
|
+
{ id: 'duct-fitting', iconSrc: '/icons/duct-fitting.webp', label: 'Duct Fitting' },
|
|
30
|
+
{ id: 'duct-terminal', iconSrc: '/icons/registers.webp', label: 'Register' },
|
|
31
|
+
{ id: 'hvac-equipment', iconSrc: '/icons/HVAC.webp', label: 'HVAC Unit' },
|
|
32
|
+
{ id: 'pipe-segment', iconSrc: '/icons/dwv-pipes.webp', label: 'DWV Pipe' },
|
|
33
|
+
{ id: 'pipe-trap', iconSrc: '/icons/dwv-pipes.webp', label: 'Trap' },
|
|
34
|
+
{ id: 'pipe-fitting', iconSrc: '/icons/duct-fitting.webp', label: 'Pipe Fitting' },
|
|
35
|
+
{ id: 'lineset', iconSrc: '/icons/lineset.webp', label: 'Lineset' },
|
|
36
|
+
{ id: 'liquid-line', iconSrc: '/icons/lineset.webp', label: 'Liquid Line' },
|
|
28
37
|
]
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { Icon } from '@iconify/react'
|
|
4
3
|
import {
|
|
5
4
|
type AnyNodeId,
|
|
6
5
|
type BuildingNode,
|
|
@@ -10,26 +9,22 @@ import {
|
|
|
10
9
|
useScene,
|
|
11
10
|
} from '@pascal-app/core'
|
|
12
11
|
import { useViewer } from '@pascal-app/viewer'
|
|
13
|
-
import { Check, ChevronDown, Eye, EyeOff, Layers2, Plus, Trash2 } from 'lucide-react'
|
|
12
|
+
import { Check, ChevronDown, Eye, EyeOff, Layers2, Plus, Trash2, Waypoints } from 'lucide-react'
|
|
14
13
|
import { useCallback, useRef, useState } from 'react'
|
|
15
14
|
import { useShallow } from 'zustand/react/shallow'
|
|
16
|
-
import { getLevelDisplayName } from '
|
|
15
|
+
import { getLevelDisplayName } from '@pascal-app/core'
|
|
17
16
|
import { createLocalGuideImage } from '../../../lib/local-guide-image'
|
|
18
17
|
import { cn } from '../../../lib/utils'
|
|
19
|
-
import useEditor
|
|
18
|
+
import useEditor from '../../../store/use-editor'
|
|
20
19
|
import { useUploadStore } from '../../../store/use-upload'
|
|
21
20
|
import { SliderControl } from '../controls/slider-control'
|
|
22
21
|
import { Popover, PopoverContent, PopoverTrigger } from '../primitives/popover'
|
|
23
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from '../primitives/tooltip'
|
|
24
22
|
import { ActionButton } from './action-button'
|
|
25
23
|
|
|
26
24
|
const MAX_FILE_SIZE = 200 * 1024 * 1024 // 200MB
|
|
27
25
|
const ACCEPTED_FILE_TYPES = '.glb,.gltf,image/jpeg,image/png,image/webp,image/gif'
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
function formatGridSnapStep(step: GridSnapStep) {
|
|
31
|
-
return step.toFixed(2)
|
|
32
|
-
}
|
|
26
|
+
const REFERENCES_EMPTY_TEXT =
|
|
27
|
+
'Upload GLB meshes as scan references or blueprint images as guide references.'
|
|
33
28
|
|
|
34
29
|
// ── Helper: get guide images for the current level ──────────────────────────
|
|
35
30
|
|
|
@@ -224,7 +219,7 @@ function GuidesControl() {
|
|
|
224
219
|
<img
|
|
225
220
|
alt="Guides"
|
|
226
221
|
className="h-[28px] w-[28px] object-contain"
|
|
227
|
-
src="/icons/floorplan.
|
|
222
|
+
src="/icons/floorplan.webp"
|
|
228
223
|
/>
|
|
229
224
|
<span className="absolute -right-1.5 -bottom-1 min-w-[14px] rounded-full bg-white/20 px-[3px] text-center font-medium text-[9px] text-white/70 leading-[14px]">
|
|
230
225
|
{guides.length}
|
|
@@ -263,7 +258,7 @@ function GuidesControl() {
|
|
|
263
258
|
<div className="space-y-3">
|
|
264
259
|
<div className="flex items-center gap-2">
|
|
265
260
|
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-background/80">
|
|
266
|
-
<img alt="" className="h-4 w-4 object-contain" src="/icons/floorplan.
|
|
261
|
+
<img alt="" className="h-4 w-4 object-contain" src="/icons/floorplan.webp" />
|
|
267
262
|
</span>
|
|
268
263
|
<div className="min-w-0 flex-1">
|
|
269
264
|
<p className="font-medium text-foreground text-sm">Guide images</p>
|
|
@@ -303,7 +298,7 @@ function GuidesControl() {
|
|
|
303
298
|
<img
|
|
304
299
|
alt=""
|
|
305
300
|
className="h-3.5 w-3.5 shrink-0 object-contain opacity-70"
|
|
306
|
-
src="/icons/floorplan.
|
|
301
|
+
src="/icons/floorplan.webp"
|
|
307
302
|
/>
|
|
308
303
|
<p className="truncate font-medium text-foreground text-sm">
|
|
309
304
|
{guide.name || `Guide image ${index + 1}`}
|
|
@@ -342,7 +337,7 @@ function GuidesControl() {
|
|
|
342
337
|
</div>
|
|
343
338
|
) : (
|
|
344
339
|
<div className="rounded-xl border border-border/45 border-dashed bg-background/60 px-3 py-4 text-muted-foreground text-sm">
|
|
345
|
-
|
|
340
|
+
{REFERENCES_EMPTY_TEXT}
|
|
346
341
|
</div>
|
|
347
342
|
)}
|
|
348
343
|
</div>
|
|
@@ -351,70 +346,6 @@ function GuidesControl() {
|
|
|
351
346
|
)
|
|
352
347
|
}
|
|
353
348
|
|
|
354
|
-
// ── Grid snap toggle ────────────────────────────────────────────────────────
|
|
355
|
-
|
|
356
|
-
function GridSnapControl() {
|
|
357
|
-
const [isOpen, setIsOpen] = useState(false)
|
|
358
|
-
const gridSnapStep = useEditor((state) => state.gridSnapStep)
|
|
359
|
-
const setGridSnapStep = useEditor((state) => state.setGridSnapStep)
|
|
360
|
-
|
|
361
|
-
return (
|
|
362
|
-
<Popover onOpenChange={setIsOpen} open={isOpen}>
|
|
363
|
-
<Tooltip>
|
|
364
|
-
<TooltipTrigger asChild>
|
|
365
|
-
<PopoverTrigger asChild>
|
|
366
|
-
<button
|
|
367
|
-
aria-expanded={isOpen}
|
|
368
|
-
aria-label={`Grid snap: ${formatGridSnapStep(gridSnapStep)}`}
|
|
369
|
-
className={cn(
|
|
370
|
-
'flex h-11 w-11 flex-col items-center justify-center rounded-lg text-muted-foreground transition-all hover:bg-white/5 hover:text-foreground',
|
|
371
|
-
isOpen && 'bg-white/10 text-foreground',
|
|
372
|
-
)}
|
|
373
|
-
type="button"
|
|
374
|
-
>
|
|
375
|
-
<Icon height={16} icon="lucide:grid-2x2" width={16} />
|
|
376
|
-
<span className="mt-1 font-medium text-[9px] leading-none">
|
|
377
|
-
{formatGridSnapStep(gridSnapStep)}
|
|
378
|
-
</span>
|
|
379
|
-
</button>
|
|
380
|
-
</PopoverTrigger>
|
|
381
|
-
</TooltipTrigger>
|
|
382
|
-
<TooltipContent side="top">Grid snap: {formatGridSnapStep(gridSnapStep)}</TooltipContent>
|
|
383
|
-
</Tooltip>
|
|
384
|
-
|
|
385
|
-
<PopoverContent
|
|
386
|
-
align="center"
|
|
387
|
-
className="w-36 rounded-xl border-border/45 bg-background/96 p-2 shadow-elevation-3 backdrop-blur-xl"
|
|
388
|
-
side="top"
|
|
389
|
-
sideOffset={14}
|
|
390
|
-
>
|
|
391
|
-
<div className="space-y-1">
|
|
392
|
-
{GRID_SNAP_STEPS.map((step) => {
|
|
393
|
-
const isActive = step === gridSnapStep
|
|
394
|
-
return (
|
|
395
|
-
<button
|
|
396
|
-
className={cn(
|
|
397
|
-
'flex w-full items-center justify-between rounded-lg px-2.5 py-2 text-left text-sm transition-colors hover:bg-white/8',
|
|
398
|
-
isActive && 'bg-white/10 text-foreground',
|
|
399
|
-
)}
|
|
400
|
-
key={step}
|
|
401
|
-
onClick={() => {
|
|
402
|
-
setGridSnapStep(step)
|
|
403
|
-
setIsOpen(false)
|
|
404
|
-
}}
|
|
405
|
-
type="button"
|
|
406
|
-
>
|
|
407
|
-
<span>{formatGridSnapStep(step)}</span>
|
|
408
|
-
{isActive ? <Check className="h-3.5 w-3.5" /> : <span className="h-3.5 w-3.5" />}
|
|
409
|
-
</button>
|
|
410
|
-
)
|
|
411
|
-
})}
|
|
412
|
-
</div>
|
|
413
|
-
</PopoverContent>
|
|
414
|
-
</Popover>
|
|
415
|
-
)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
349
|
// ── Scans toggle + dropdown ─────────────────────────────────────────────────
|
|
419
350
|
|
|
420
351
|
function ScansControl() {
|
|
@@ -464,7 +395,7 @@ function ScansControl() {
|
|
|
464
395
|
variant="ghost"
|
|
465
396
|
>
|
|
466
397
|
<div className="relative">
|
|
467
|
-
<img alt="Scans" className="h-[28px] w-[28px] object-contain" src="/icons/mesh.
|
|
398
|
+
<img alt="Scans" className="h-[28px] w-[28px] object-contain" src="/icons/mesh.webp" />
|
|
468
399
|
<span className="absolute -right-1.5 -bottom-1 min-w-[14px] rounded-full bg-white/20 px-[3px] text-center font-medium text-[9px] text-white/70 leading-[14px]">
|
|
469
400
|
{scans.length}
|
|
470
401
|
</span>
|
|
@@ -502,7 +433,7 @@ function ScansControl() {
|
|
|
502
433
|
<div className="space-y-3">
|
|
503
434
|
<div className="flex items-center gap-2">
|
|
504
435
|
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-background/80">
|
|
505
|
-
<img alt="" className="h-4 w-4 object-contain" src="/icons/mesh.
|
|
436
|
+
<img alt="" className="h-4 w-4 object-contain" src="/icons/mesh.webp" />
|
|
506
437
|
</span>
|
|
507
438
|
<div className="min-w-0 flex-1">
|
|
508
439
|
<p className="font-medium text-foreground text-sm">Scans</p>
|
|
@@ -542,7 +473,7 @@ function ScansControl() {
|
|
|
542
473
|
<img
|
|
543
474
|
alt=""
|
|
544
475
|
className="h-3.5 w-3.5 shrink-0 object-contain opacity-70"
|
|
545
|
-
src="/icons/mesh.
|
|
476
|
+
src="/icons/mesh.webp"
|
|
546
477
|
/>
|
|
547
478
|
<p className="truncate font-medium text-foreground text-sm">
|
|
548
479
|
{scan.name || `Scan ${index + 1}`}
|
|
@@ -581,7 +512,7 @@ function ScansControl() {
|
|
|
581
512
|
</div>
|
|
582
513
|
) : (
|
|
583
514
|
<div className="rounded-xl border border-border/45 border-dashed bg-background/60 px-3 py-4 text-muted-foreground text-sm">
|
|
584
|
-
|
|
515
|
+
{REFERENCES_EMPTY_TEXT}
|
|
585
516
|
</div>
|
|
586
517
|
)}
|
|
587
518
|
</div>
|
|
@@ -763,7 +694,7 @@ function ReferencesControl() {
|
|
|
763
694
|
<img
|
|
764
695
|
alt="References"
|
|
765
696
|
className="h-[28px] w-[28px] object-contain"
|
|
766
|
-
src="/icons/floorplan.
|
|
697
|
+
src="/icons/floorplan.webp"
|
|
767
698
|
/>
|
|
768
699
|
<span className="absolute -right-1.5 -bottom-1 min-w-[14px] rounded-full bg-white/20 px-[3px] text-center font-medium text-[9px] text-white/70 leading-[14px]">
|
|
769
700
|
{total}
|
|
@@ -805,8 +736,8 @@ function ReferencesControl() {
|
|
|
805
736
|
</div>
|
|
806
737
|
)}
|
|
807
738
|
<ReferenceListSection
|
|
808
|
-
emptyText=
|
|
809
|
-
iconSrc="/icons/mesh.
|
|
739
|
+
emptyText={REFERENCES_EMPTY_TEXT}
|
|
740
|
+
iconSrc="/icons/mesh.webp"
|
|
810
741
|
nodes={scans}
|
|
811
742
|
noun="scan"
|
|
812
743
|
onError={setUploadError}
|
|
@@ -816,8 +747,8 @@ function ReferencesControl() {
|
|
|
816
747
|
/>
|
|
817
748
|
<div className="h-px bg-border/45" />
|
|
818
749
|
<ReferenceListSection
|
|
819
|
-
emptyText=
|
|
820
|
-
iconSrc="/icons/floorplan.
|
|
750
|
+
emptyText={REFERENCES_EMPTY_TEXT}
|
|
751
|
+
iconSrc="/icons/floorplan.webp"
|
|
821
752
|
nodes={guides}
|
|
822
753
|
noun="guide image"
|
|
823
754
|
onError={setUploadError}
|
|
@@ -987,9 +918,30 @@ function ReferenceFloorControl() {
|
|
|
987
918
|
)
|
|
988
919
|
}
|
|
989
920
|
|
|
990
|
-
// ──
|
|
921
|
+
// ── Riser diagram control ────────────────────────────────────────────────────
|
|
922
|
+
|
|
923
|
+
function RiserControl() {
|
|
924
|
+
const isRiserOpen = useEditor((state) => state.isRiserOpen)
|
|
925
|
+
const toggleRiserOpen = useEditor((state) => state.toggleRiserOpen)
|
|
926
|
+
|
|
927
|
+
return (
|
|
928
|
+
<ActionButton
|
|
929
|
+
className={cn(
|
|
930
|
+
isRiserOpen
|
|
931
|
+
? 'bg-white/15'
|
|
932
|
+
: 'opacity-60 grayscale hover:bg-white/5 hover:opacity-100 hover:grayscale-0',
|
|
933
|
+
)}
|
|
934
|
+
label="Riser diagram"
|
|
935
|
+
onClick={toggleRiserOpen}
|
|
936
|
+
size="icon"
|
|
937
|
+
variant="ghost"
|
|
938
|
+
>
|
|
939
|
+
<Waypoints className="h-4 w-4" />
|
|
940
|
+
</ActionButton>
|
|
941
|
+
)
|
|
942
|
+
}
|
|
991
943
|
|
|
992
|
-
|
|
944
|
+
// ── Exports ─────────────────────────────────────────────────────────────────
|
|
993
945
|
|
|
994
946
|
export function SecondaryToggles() {
|
|
995
947
|
return (
|
|
@@ -1002,10 +954,10 @@ export function SecondaryToggles() {
|
|
|
1002
954
|
export function ViewToggles() {
|
|
1003
955
|
return (
|
|
1004
956
|
<div className="flex items-center gap-1">
|
|
1005
|
-
<GridSnapControl />
|
|
1006
957
|
<ScansControl />
|
|
1007
958
|
<GuidesControl />
|
|
1008
959
|
<ReferenceFloorControl />
|
|
960
|
+
<RiserControl />
|
|
1009
961
|
</div>
|
|
1010
962
|
)
|
|
1011
963
|
}
|
|
@@ -245,10 +245,10 @@ export function EditorCommands() {
|
|
|
245
245
|
label: 'Wall Mode',
|
|
246
246
|
group: 'Viewer Controls',
|
|
247
247
|
icon: <Layers className="h-4 w-4" />,
|
|
248
|
-
keywords: ['wall', 'cutaway', 'up', 'down', 'view'],
|
|
248
|
+
keywords: ['wall', 'cutaway', 'up', 'down', 'translucent', 'view'],
|
|
249
249
|
badge: () => {
|
|
250
250
|
const mode = useViewer.getState().wallMode
|
|
251
|
-
return { cutaway: 'Cutaway', up: 'Up', down: 'Down' }[mode]
|
|
251
|
+
return { cutaway: 'Cutaway', up: 'Up', down: 'Down', translucent: 'Translucent' }[mode]
|
|
252
252
|
},
|
|
253
253
|
navigate: true,
|
|
254
254
|
execute: () => navigateTo('wall-mode'),
|
|
@@ -10,7 +10,7 @@ import { useEffect, useState } from 'react'
|
|
|
10
10
|
import { create } from 'zustand'
|
|
11
11
|
import { useShallow } from 'zustand/shallow'
|
|
12
12
|
import { Dialog, DialogContent, DialogTitle } from './../../../components/ui/primitives/dialog'
|
|
13
|
-
import { getLevelDisplayName } from '
|
|
13
|
+
import { getLevelDisplayName } from '@pascal-app/core'
|
|
14
14
|
import { useCommandRegistry } from '../../../store/use-command-registry'
|
|
15
15
|
import { usePaletteViewRegistry } from '../../../store/use-palette-view-registry'
|
|
16
16
|
|
|
@@ -244,10 +244,11 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
244
244
|
setOpen(false)
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
const wallModeLabel: Record<'cutaway' | 'up' | 'down', string> = {
|
|
247
|
+
const wallModeLabel: Record<'cutaway' | 'up' | 'down' | 'translucent', string> = {
|
|
248
248
|
cutaway: 'Cutaway',
|
|
249
249
|
up: 'Up',
|
|
250
250
|
down: 'Down',
|
|
251
|
+
translucent: 'Translucent',
|
|
251
252
|
}
|
|
252
253
|
const levelModeLabel: Record<'manual' | 'stacked' | 'exploded' | 'solo', string> = {
|
|
253
254
|
manual: 'Manual',
|
|
@@ -373,7 +374,7 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
373
374
|
{/* ── Wall Mode sub-page ────────────────────────────────────── */}
|
|
374
375
|
{page === 'wall-mode' && (
|
|
375
376
|
<Command.Group heading="Wall Mode">
|
|
376
|
-
{(['cutaway', 'up', 'down'] as const).map((mode) => (
|
|
377
|
+
{(['cutaway', 'up', 'down', 'translucent'] as const).map((mode) => (
|
|
377
378
|
<OptionItem
|
|
378
379
|
isActive={wallMode === mode}
|
|
379
380
|
key={mode}
|