@pascal-app/editor 0.8.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.
- package/package.json +8 -9
- package/src/components/editor/alignment-3d-guide-layer.tsx +160 -0
- package/src/components/editor/custom-camera-controls.tsx +754 -25
- package/src/components/editor/editor-layout-mobile.tsx +3 -3
- package/src/components/editor/editor-layout-v2.tsx +58 -51
- package/src/components/editor/export-manager.tsx +32 -3
- package/src/components/editor/first-person/build-collider-world.test.ts +149 -0
- package/src/components/editor/first-person/build-collider-world.ts +201 -83
- package/src/components/editor/first-person/bvh-ecctrl.tsx +8 -8
- package/src/components/editor/first-person-controls.tsx +825 -20
- package/src/components/editor/floating-action-menu.tsx +287 -196
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floorplan-panel.tsx +5829 -12005
- package/src/components/editor/grid.tsx +51 -17
- package/src/components/editor/group-move-handle.tsx +316 -0
- package/src/components/editor/group-rotate-handle.tsx +382 -0
- package/src/components/editor/group-transform-shared.test.ts +205 -0
- package/src/components/editor/group-transform-shared.ts +256 -0
- package/src/components/editor/handles/handle-arrow.tsx +523 -0
- package/src/components/editor/handles/use-handle-drag.ts +213 -0
- package/src/components/editor/index.tsx +178 -149
- package/src/components/editor/measurement-pill.tsx +75 -0
- package/src/components/editor/node-arrow-handles.tsx +1453 -0
- package/src/components/editor/selection-manager.tsx +433 -156
- package/src/components/editor/site-edge-labels.tsx +44 -20
- package/src/components/editor/slab-hole-highlights.tsx +466 -0
- package/src/components/editor/snapshot-capture-overlay.tsx +136 -62
- package/src/components/editor/thumbnail-generator.tsx +9 -3
- package/src/components/editor/use-floorplan-background-placement.ts +119 -32
- package/src/components/editor/use-floorplan-hit-testing.ts +24 -0
- package/src/components/editor/use-floorplan-scene-data.ts +14 -2
- package/src/components/editor/wall-measurement-label.tsx +11 -6
- package/src/components/editor/wall-move-side-handles.tsx +855 -0
- package/src/components/editor/wall-opening-highlights.tsx +181 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +150 -0
- package/src/components/editor-2d/floorplan-action-menu-layer.tsx +6 -1
- package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +142 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +5 -3
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +227 -0
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +616 -0
- package/src/components/editor-2d/floorplan-render-context.tsx +62 -0
- package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +75 -0
- package/src/components/editor-2d/floorplan-wall-move-ghost-layer.tsx +47 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +266 -0
- package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +108 -66
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +59 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +1963 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +24 -9
- package/src/components/editor-2d/svg-paths.ts +10 -2
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +26 -3
- package/src/components/systems/ceiling/ceiling-system.tsx +12 -8
- package/src/components/systems/roof/roof-edit-system.tsx +86 -12
- package/src/components/systems/stair/stair-edit-system.tsx +1 -1
- package/src/components/tools/elevator/elevator-defaults.ts +8 -0
- package/src/components/tools/elevator/elevator-tool.tsx +349 -0
- package/src/components/tools/elevator/move-elevator-tool.tsx +252 -0
- package/src/components/tools/fence/fence-drafting.ts +64 -22
- package/src/components/tools/item/move-tool.tsx +41 -105
- package/src/components/tools/item/placement-math.test.ts +10 -0
- package/src/components/tools/item/placement-math.ts +31 -5
- package/src/components/tools/item/placement-strategies.ts +190 -15
- package/src/components/tools/item/placement-types.ts +10 -1
- package/src/components/tools/item/use-draft-node.ts +20 -1
- package/src/components/tools/item/use-placement-coordinator.tsx +694 -191
- package/src/components/tools/registry/move-registry-node-tool.tsx +602 -0
- package/src/components/tools/roof/roof-tool.tsx +108 -12
- package/src/components/tools/select/box-select-state.ts +77 -0
- package/src/components/tools/select/box-select-tool.tsx +324 -538
- package/src/components/tools/select/plane-box-select-tool.tsx +577 -0
- package/src/components/tools/select/screen-rectangle-selection.ts +84 -0
- package/src/components/tools/select/select-candidates.test.ts +109 -0
- package/src/components/tools/select/select-candidates.ts +85 -0
- package/src/components/tools/shared/affordance-dispatch.ts +30 -0
- package/src/components/tools/shared/drag-bounding-box.tsx +171 -0
- package/src/components/tools/shared/floor-stack-preview.ts +25 -0
- package/src/components/tools/shared/fresh-placement-visibility.ts +61 -0
- package/src/components/tools/shared/placement-box-geometry.ts +123 -0
- package/src/components/tools/shared/placement-box.tsx +123 -0
- package/src/components/tools/shared/polygon-editor.tsx +617 -167
- package/src/components/tools/shared/segment-angle.ts +56 -0
- package/src/components/tools/site/site-boundary-editor.tsx +464 -12
- package/src/components/tools/site/site-flag-model.ts +18 -0
- package/src/components/tools/stair/stair-defaults.ts +1 -1
- package/src/components/tools/stair/stair-tool.tsx +269 -35
- package/src/components/tools/tool-manager.tsx +172 -56
- package/src/components/tools/wall/wall-drafting.ts +98 -98
- package/src/components/tools/wall/wall-snap-geometry.test.ts +79 -0
- package/src/components/tools/wall/wall-snap-geometry.ts +270 -0
- package/src/components/tools/zone/zone-tool.tsx +16 -6
- package/src/components/ui/action-menu/action-button.tsx +21 -1
- package/src/components/ui/action-menu/control-modes.tsx +5 -82
- package/src/components/ui/action-menu/index.tsx +1 -113
- package/src/components/ui/action-menu/structure-tools.tsx +13 -88
- package/src/components/ui/action-menu/view-toggles.tsx +243 -6
- package/src/components/ui/command-palette/editor-commands.tsx +18 -13
- package/src/components/ui/command-palette/index.tsx +2 -1
- package/src/components/ui/controls/material-paint-panel.tsx +83 -0
- package/src/components/ui/controls/material-picker.tsx +11 -32
- package/src/components/ui/controls/metric-control.tsx +33 -11
- package/src/components/ui/floating-level-selector.tsx +47 -10
- package/src/components/ui/helpers/helper-manager.tsx +16 -17
- package/src/components/ui/helpers/registered-tool-helper.tsx +25 -0
- package/src/components/ui/item-catalog/catalog-items.tsx +1 -0
- package/src/components/ui/item-catalog/item-catalog.tsx +3 -0
- package/src/components/ui/level-duplicate-dialog.tsx +2 -1
- package/src/components/ui/panels/node-display.ts +1 -0
- package/src/components/ui/panels/panel-manager.tsx +35 -47
- package/src/components/ui/panels/panel-wrapper.tsx +217 -12
- package/src/components/ui/panels/parametric-inspector.tsx +469 -0
- package/src/components/ui/primitives/dropdown-menu.tsx +12 -8
- package/src/components/ui/scene-loader.tsx +1 -3
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +263 -0
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +82 -8
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +59 -9
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +6 -1
- package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +259 -0
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -1
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +5 -2
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +83 -0
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +89 -0
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +75 -0
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +83 -0
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +4 -6
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +93 -0
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +41 -5
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +128 -0
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +5 -2
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +83 -0
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +7 -1
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +68 -36
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +51 -2
- package/src/components/ui/sidebar/tab-bar.tsx +61 -1
- package/src/components/viewer-overlay.tsx +169 -52
- package/src/components/viewer-zone-system.tsx +1 -1
- package/src/hooks/use-auto-save.ts +10 -3
- package/src/hooks/use-ceiling-events.ts +175 -0
- package/src/hooks/use-drag-action.ts +124 -0
- package/src/hooks/use-keyboard.ts +115 -62
- package/src/hooks/use-selection.ts +64 -0
- package/src/index.tsx +237 -5
- package/src/lib/constants.ts +6 -2
- package/src/lib/editor-api.ts +62 -0
- package/src/lib/elevator-support.ts +96 -0
- package/src/lib/floorplan/apply-alignment.ts +111 -0
- package/src/lib/floorplan/index.ts +7 -0
- package/src/lib/floorplan/selection-tool.ts +40 -0
- package/src/lib/floorplan/stairs.ts +12 -1
- package/src/lib/fresh-planar-placement.test.ts +102 -0
- package/src/lib/fresh-planar-placement.ts +63 -0
- package/src/lib/history.ts +2 -1
- package/src/lib/level-duplication.ts +54 -42
- package/src/lib/level-name.ts +11 -0
- package/src/lib/material-paint.ts +211 -19
- package/src/lib/placement-metadata.ts +29 -0
- package/src/lib/planar-cursor-placement.test.ts +43 -0
- package/src/lib/planar-cursor-placement.ts +42 -0
- package/src/lib/roof-duplication.ts +1 -1
- package/src/lib/scene-clipboard.ts +267 -0
- package/src/lib/scene.ts +13 -4
- package/src/lib/sfx-bus.ts +23 -1
- package/src/lib/sfx-player.ts +70 -22
- package/src/lib/site-boundary.ts +1 -0
- package/src/lib/stair-levels.test.ts +146 -0
- package/src/lib/stair-levels.ts +177 -0
- package/src/lib/world-grid-snap.ts +199 -0
- package/src/lib/zone-content.ts +130 -0
- package/src/store/use-alignment-guides.ts +21 -0
- package/src/store/use-editor.tsx +320 -37
- package/src/store/use-placement-preview.ts +31 -0
- package/src/store/use-wall-move-ghosts.ts +36 -0
- package/src/store/use-wall-snap-indicator.ts +32 -0
- package/src/components/editor/preset-thumbnail-generator.tsx +0 -125
- package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +0 -113
- package/src/components/tools/building/move-building-tool.tsx +0 -157
- package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +0 -43
- package/src/components/tools/ceiling/ceiling-hole-editor.tsx +0 -49
- package/src/components/tools/ceiling/ceiling-tool.tsx +0 -465
- package/src/components/tools/ceiling/move-ceiling-tool.tsx +0 -264
- package/src/components/tools/column/column-tool.tsx +0 -97
- package/src/components/tools/column/move-column-tool.tsx +0 -105
- package/src/components/tools/door/door-math.ts +0 -110
- package/src/components/tools/door/door-tool.tsx +0 -324
- package/src/components/tools/door/move-door-tool.tsx +0 -412
- package/src/components/tools/fence/curve-fence-tool.tsx +0 -178
- package/src/components/tools/fence/fence-tool.tsx +0 -346
- package/src/components/tools/fence/move-fence-endpoint-tool.tsx +0 -441
- package/src/components/tools/fence/move-fence-tool.tsx +0 -302
- package/src/components/tools/item/item-tool.tsx +0 -26
- package/src/components/tools/roof/move-roof-tool.tsx +0 -364
- package/src/components/tools/slab/move-slab-tool.tsx +0 -182
- package/src/components/tools/slab/slab-boundary-editor.tsx +0 -43
- package/src/components/tools/slab/slab-hole-editor.tsx +0 -49
- package/src/components/tools/slab/slab-tool.tsx +0 -322
- package/src/components/tools/spawn/move-spawn-tool.tsx +0 -101
- package/src/components/tools/spawn/spawn-tool.tsx +0 -130
- package/src/components/tools/wall/curve-wall-tool.tsx +0 -178
- package/src/components/tools/wall/move-wall-endpoint-tool.tsx +0 -426
- package/src/components/tools/wall/move-wall-tool.tsx +0 -358
- package/src/components/tools/wall/wall-tool.tsx +0 -332
- package/src/components/tools/window/move-window-tool.tsx +0 -447
- package/src/components/tools/window/window-math.ts +0 -117
- package/src/components/tools/window/window-tool.tsx +0 -332
- package/src/components/ui/helpers/ceiling-helper.tsx +0 -20
- package/src/components/ui/helpers/slab-helper.tsx +0 -20
- package/src/components/ui/helpers/wall-helper.tsx +0 -20
- package/src/components/ui/panels/ceiling-panel.tsx +0 -249
- package/src/components/ui/panels/column-panel.tsx +0 -759
- package/src/components/ui/panels/door-panel.tsx +0 -1299
- package/src/components/ui/panels/fence-panel.tsx +0 -222
- package/src/components/ui/panels/item-panel.tsx +0 -306
- package/src/components/ui/panels/presets/presets-popover.tsx +0 -511
- package/src/components/ui/panels/roof-panel.tsx +0 -280
- package/src/components/ui/panels/roof-segment-panel.tsx +0 -311
- package/src/components/ui/panels/slab-panel.tsx +0 -250
- package/src/components/ui/panels/spawn-panel.tsx +0 -161
- package/src/components/ui/panels/stair-panel.tsx +0 -577
- package/src/components/ui/panels/stair-segment-panel.tsx +0 -325
- package/src/components/ui/panels/wall-panel.tsx +0 -273
- package/src/components/ui/panels/window-panel.tsx +0 -968
- package/src/contexts/presets-context.tsx +0 -121
- package/src/hooks/use-contextual-tools.ts +0 -61
|
@@ -8,19 +8,9 @@ import {
|
|
|
8
8
|
useScene,
|
|
9
9
|
} from '@pascal-app/core'
|
|
10
10
|
import { type HoverStyles, InteractiveSystem, useViewer, Viewer } from '@pascal-app/viewer'
|
|
11
|
-
import {
|
|
12
|
-
memo,
|
|
13
|
-
type ReactNode,
|
|
14
|
-
useCallback,
|
|
15
|
-
useEffect,
|
|
16
|
-
useLayoutEffect,
|
|
17
|
-
useRef,
|
|
18
|
-
useState,
|
|
19
|
-
} from 'react'
|
|
11
|
+
import { memo, type ReactNode, useCallback, useEffect, useRef, useState } from 'react'
|
|
20
12
|
import { ViewerOverlay } from '../../components/viewer-overlay'
|
|
21
13
|
import { ViewerZoneSystem } from '../../components/viewer-zone-system'
|
|
22
|
-
import { type PresetsAdapter, PresetsProvider } from '../../contexts/presets-context'
|
|
23
|
-
import { useAutoFrame } from '../../hooks/use-auto-frame'
|
|
24
14
|
import { type SaveStatus, useAutoSave } from '../../hooks/use-auto-save'
|
|
25
15
|
import { useKeyboard } from '../../hooks/use-keyboard'
|
|
26
16
|
import {
|
|
@@ -62,18 +52,23 @@ import { FloatingActionMenu } from './floating-action-menu'
|
|
|
62
52
|
import { FloatingBuildingActionMenu } from './floating-building-action-menu'
|
|
63
53
|
import { FloorplanPanel } from './floorplan-panel'
|
|
64
54
|
import { Grid } from './grid'
|
|
65
|
-
import {
|
|
55
|
+
import { GroupMoveHandle } from './group-move-handle'
|
|
56
|
+
import { GroupRotateHandle } from './group-rotate-handle'
|
|
57
|
+
import { NodeArrowHandles } from './node-arrow-handles'
|
|
66
58
|
import { SelectionManager } from './selection-manager'
|
|
67
59
|
import { SiteEdgeLabels } from './site-edge-labels'
|
|
60
|
+
import { SlabHoleHighlights } from './slab-hole-highlights'
|
|
68
61
|
import { SnapshotCaptureOverlay } from './snapshot-capture-overlay'
|
|
69
62
|
import { type SnapshotCameraData, ThumbnailGenerator } from './thumbnail-generator'
|
|
70
63
|
import { WallMeasurementLabel } from './wall-measurement-label'
|
|
64
|
+
import { WallMoveSideHandles } from './wall-move-side-handles'
|
|
65
|
+
import { WallOpeningHighlights } from './wall-opening-highlights'
|
|
71
66
|
|
|
72
67
|
const CAMERA_CONTROLS_HINT_DISMISSED_STORAGE_KEY = 'editor-camera-controls-hint-dismissed:v1'
|
|
73
68
|
const DELETE_CURSOR_BADGE_COLOR = '#ef4444'
|
|
74
69
|
const DELETE_CURSOR_BADGE_OFFSET_X = 14
|
|
75
70
|
const DELETE_CURSOR_BADGE_OFFSET_Y = 14
|
|
76
|
-
const PAINT_CURSOR_BADGE_COLOR = '#
|
|
71
|
+
const PAINT_CURSOR_BADGE_COLOR = '#818cf8'
|
|
77
72
|
const PAINT_CURSOR_BADGE_DISABLED_COLOR = '#94a3b8'
|
|
78
73
|
const PAINT_CURSOR_BADGE_OFFSET_X = 14
|
|
79
74
|
const PAINT_CURSOR_BADGE_OFFSET_Y = 14
|
|
@@ -88,6 +83,7 @@ const EDITOR_HOVER_STYLES: HoverStyles = {
|
|
|
88
83
|
pulse: false,
|
|
89
84
|
},
|
|
90
85
|
}
|
|
86
|
+
const EDITOR_DEFAULT_RENDER = { shading: 'solid' } as const
|
|
91
87
|
|
|
92
88
|
/**
|
|
93
89
|
* Wire up module-level singletons (spatial grid, space detection, SFX) for
|
|
@@ -125,12 +121,18 @@ export interface EditorProps {
|
|
|
125
121
|
sidebarTabs?: (SidebarTab & { component: React.ComponentType })[]
|
|
126
122
|
viewerToolbarLeft?: ReactNode
|
|
127
123
|
viewerToolbarRight?: ReactNode
|
|
124
|
+
/**
|
|
125
|
+
* Docked below the node inspector (v2). Hosts mount the "save as preset"
|
|
126
|
+
* affordance here so it reads as part of the inspector surface and shows
|
|
127
|
+
* only while a node is selected.
|
|
128
|
+
*/
|
|
129
|
+
inspectorFooter?: ReactNode
|
|
128
130
|
|
|
129
131
|
projectId?: string | null
|
|
130
132
|
|
|
131
133
|
// Persistence — defaults to localStorage when omitted
|
|
132
134
|
onLoad?: () => Promise<SceneGraph | null>
|
|
133
|
-
onSave?: (scene: SceneGraph) => Promise<void>
|
|
135
|
+
onSave?: (scene: SceneGraph, options?: { keepalive?: boolean }) => Promise<void>
|
|
134
136
|
onDirty?: () => void
|
|
135
137
|
onSaveStatusChange?: (status: SaveStatus) => void
|
|
136
138
|
|
|
@@ -153,9 +155,6 @@ export interface EditorProps {
|
|
|
153
155
|
sitePanelProps?: SitePanelProps
|
|
154
156
|
extraSidebarPanels?: ExtraPanel[]
|
|
155
157
|
|
|
156
|
-
// Presets storage backend (defaults to localStorage)
|
|
157
|
-
presetsAdapter?: PresetsAdapter
|
|
158
|
-
|
|
159
158
|
// Command palette fallback when no commands match
|
|
160
159
|
commandPaletteEmptyAction?: CommandPaletteEmptyAction
|
|
161
160
|
}
|
|
@@ -334,6 +333,7 @@ const EDITOR_CAMERA_CONTROL_HINTS: CameraControlHint[] = [
|
|
|
334
333
|
{
|
|
335
334
|
action: 'Pan',
|
|
336
335
|
keys: [{ value: 'Space' }, { value: 'Left click' }],
|
|
336
|
+
alternativeKeys: [{ value: 'Middle click' }],
|
|
337
337
|
},
|
|
338
338
|
{ action: 'Rotate', keys: [{ value: 'Right click' }] },
|
|
339
339
|
{ action: 'Zoom', keys: [{ value: 'Scroll' }] },
|
|
@@ -528,67 +528,89 @@ function DeleteCursorBadge({ position }: { position: { x: number; y: number } })
|
|
|
528
528
|
|
|
529
529
|
function PaintCursorBadge({
|
|
530
530
|
position,
|
|
531
|
-
label,
|
|
532
531
|
disabled,
|
|
533
|
-
icon,
|
|
534
532
|
}: {
|
|
535
533
|
position: { x: number; y: number }
|
|
536
|
-
label: string
|
|
537
534
|
disabled: boolean
|
|
538
|
-
icon: string
|
|
539
535
|
}) {
|
|
540
536
|
const accentColor = disabled ? PAINT_CURSOR_BADGE_DISABLED_COLOR : PAINT_CURSOR_BADGE_COLOR
|
|
537
|
+
const lineHeight = 18
|
|
541
538
|
|
|
542
539
|
return (
|
|
543
540
|
<div
|
|
544
541
|
aria-hidden="true"
|
|
545
542
|
className="pointer-events-none absolute z-40"
|
|
546
543
|
style={{
|
|
547
|
-
left: position.x
|
|
548
|
-
top: position.y
|
|
544
|
+
left: position.x,
|
|
545
|
+
top: position.y,
|
|
549
546
|
}}
|
|
550
547
|
>
|
|
551
548
|
<div
|
|
552
|
-
className="
|
|
549
|
+
className="-translate-x-1/2 -translate-y-full absolute top-0 left-1/2"
|
|
550
|
+
style={{
|
|
551
|
+
backgroundColor: accentColor,
|
|
552
|
+
boxShadow: `0 0 12px ${accentColor}cc`,
|
|
553
|
+
height: lineHeight,
|
|
554
|
+
width: 2,
|
|
555
|
+
}}
|
|
556
|
+
/>
|
|
557
|
+
<div
|
|
558
|
+
className="absolute top-0 left-1/2 flex h-8 w-8 items-center justify-center rounded-xl border border-white/5 bg-zinc-900/95 shadow-[0_8px_16px_-4px_rgba(0,0,0,0.3),0_4px_8px_-4px_rgba(0,0,0,0.2)]"
|
|
553
559
|
style={{
|
|
554
560
|
boxShadow: `0 8px 16px -4px rgba(0,0,0,0.3), 0 4px 8px -4px rgba(0,0,0,0.2), 0 0 18px ${accentColor}22`,
|
|
561
|
+
transform: `translate(-50%, calc(-100% - ${lineHeight}px))`,
|
|
555
562
|
}}
|
|
556
563
|
>
|
|
557
|
-
|
|
564
|
+
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
565
|
+
<img
|
|
566
|
+
alt=""
|
|
558
567
|
aria-hidden="true"
|
|
559
|
-
className="drop-shadow-[0_2px_4px_rgba(0,0,0,0.5)]"
|
|
560
|
-
|
|
561
|
-
height={16}
|
|
562
|
-
icon={icon}
|
|
563
|
-
width={16}
|
|
568
|
+
className="h-5 w-5 object-contain drop-shadow-[0_2px_4px_rgba(0,0,0,0.5)]"
|
|
569
|
+
src="/icons/paint.png"
|
|
564
570
|
/>
|
|
565
|
-
<span className="font-medium text-[11px]" style={{ color: accentColor }}>
|
|
566
|
-
{label}
|
|
567
|
-
</span>
|
|
568
571
|
</div>
|
|
569
572
|
</div>
|
|
570
573
|
)
|
|
571
574
|
}
|
|
572
575
|
|
|
576
|
+
// Subscribes to `gridSnapStep` so the visible grid cell size matches whatever
|
|
577
|
+
// the wall draft tool snaps to — otherwise the cursor lands between visible
|
|
578
|
+
// grid lines when the user picks a finer snap (0.25 / 0.1 / 0.05).
|
|
579
|
+
function SnapAwareGrid() {
|
|
580
|
+
const gridSnapStep = useEditor((s) => s.gridSnapStep)
|
|
581
|
+
return <Grid cellColor="#aaa" cellSize={gridSnapStep} fadeDistance={500} sectionColor="#ccc" />
|
|
582
|
+
}
|
|
583
|
+
|
|
573
584
|
// ── Viewer scene content: memoized so <Viewer> doesn't re-render on mode/viewMode changes ──
|
|
574
585
|
|
|
575
586
|
const ViewerSceneContent = memo(function ViewerSceneContent({
|
|
576
587
|
isVersionPreviewMode,
|
|
577
588
|
isLoading,
|
|
578
589
|
isFirstPersonMode,
|
|
590
|
+
isStudioMode,
|
|
579
591
|
onThumbnailCapture,
|
|
580
592
|
}: {
|
|
581
593
|
isVersionPreviewMode: boolean
|
|
582
594
|
isLoading: boolean
|
|
583
595
|
isFirstPersonMode: boolean
|
|
596
|
+
isStudioMode: boolean
|
|
584
597
|
onThumbnailCapture?: (blob: Blob, cameraData: SnapshotCameraData) => void
|
|
585
598
|
}) {
|
|
599
|
+
// Studio mode is a clean render/snapshot surface — no selection or editing
|
|
600
|
+
// affordances. It mirrors version-preview's chrome gating on the canvas.
|
|
601
|
+
const noEditing = isVersionPreviewMode || isFirstPersonMode || isStudioMode
|
|
586
602
|
return (
|
|
587
603
|
<>
|
|
588
|
-
{!isFirstPersonMode && <SelectionManager />}
|
|
589
|
-
{!
|
|
590
|
-
{!
|
|
591
|
-
{!
|
|
604
|
+
{!(isFirstPersonMode || isStudioMode) && <SelectionManager />}
|
|
605
|
+
{!noEditing && <BoxSelectTool />}
|
|
606
|
+
{!noEditing && <NodeArrowHandles />}
|
|
607
|
+
{!noEditing && <GroupRotateHandle />}
|
|
608
|
+
{!noEditing && <GroupMoveHandle />}
|
|
609
|
+
{!noEditing && <WallOpeningHighlights />}
|
|
610
|
+
{!noEditing && <SlabHoleHighlights />}
|
|
611
|
+
{!noEditing && <WallMoveSideHandles />}
|
|
612
|
+
{!noEditing && <FloatingActionMenu />}
|
|
613
|
+
{!noEditing && <FloatingBuildingActionMenu />}
|
|
592
614
|
{!isFirstPersonMode && <WallMeasurementLabel />}
|
|
593
615
|
<ExportManager />
|
|
594
616
|
{isFirstPersonMode ? <ViewerZoneSystem /> : <ZoneSystem />}
|
|
@@ -596,16 +618,13 @@ const ViewerSceneContent = memo(function ViewerSceneContent({
|
|
|
596
618
|
<CeilingSelectionAffordanceSystem />
|
|
597
619
|
<RoofEditSystem />
|
|
598
620
|
<StairEditSystem />
|
|
599
|
-
{!(isLoading || isFirstPersonMode) &&
|
|
600
|
-
|
|
601
|
-
)}
|
|
602
|
-
{!(isLoading || isVersionPreviewMode || isFirstPersonMode) && <ToolManager />}
|
|
621
|
+
{!(isLoading || isFirstPersonMode) && <SnapAwareGrid />}
|
|
622
|
+
{!(isLoading || noEditing) && <ToolManager />}
|
|
603
623
|
{isFirstPersonMode && <FirstPersonControls />}
|
|
604
624
|
<CustomCameraControls />
|
|
605
625
|
<ThumbnailGenerator onThumbnailCapture={onThumbnailCapture} />
|
|
606
|
-
<PresetThumbnailGenerator />
|
|
607
626
|
{!isFirstPersonMode && <SiteEdgeLabels />}
|
|
608
|
-
|
|
627
|
+
<InteractiveSystem />
|
|
609
628
|
</>
|
|
610
629
|
)
|
|
611
630
|
})
|
|
@@ -678,7 +697,7 @@ function DeleteCursorLayer({
|
|
|
678
697
|
|
|
679
698
|
return (
|
|
680
699
|
<div
|
|
681
|
-
className="pointer-events-none"
|
|
700
|
+
className="pointer-events-none z-40"
|
|
682
701
|
ref={badgeRef}
|
|
683
702
|
style={{ display: 'none', position: 'absolute', left: 0, top: 0 }}
|
|
684
703
|
>
|
|
@@ -696,15 +715,12 @@ function PaintCursorLayer({
|
|
|
696
715
|
}) {
|
|
697
716
|
const mode = useEditor((s) => s.mode)
|
|
698
717
|
const activePaintMaterial = useEditor((s) => s.activePaintMaterial)
|
|
699
|
-
const
|
|
700
|
-
const badgeRef = useRef<HTMLDivElement>(null)
|
|
718
|
+
const [position, setPosition] = useState<{ x: number; y: number } | null>(null)
|
|
701
719
|
const active = mode === 'material-paint' && !isVersionPreviewMode
|
|
702
720
|
|
|
703
721
|
useEffect(() => {
|
|
704
722
|
if (!active) {
|
|
705
|
-
|
|
706
|
-
badgeRef.current.style.display = 'none'
|
|
707
|
-
}
|
|
723
|
+
setPosition(null)
|
|
708
724
|
return
|
|
709
725
|
}
|
|
710
726
|
const el = containerRef.current
|
|
@@ -712,20 +728,16 @@ function PaintCursorLayer({
|
|
|
712
728
|
let frame = 0
|
|
713
729
|
let nextX = 0
|
|
714
730
|
let nextY = 0
|
|
715
|
-
const badge = badgeRef.current
|
|
716
731
|
|
|
717
732
|
const flushPosition = () => {
|
|
718
733
|
frame = 0
|
|
719
|
-
|
|
720
|
-
badge.style.display = 'block'
|
|
721
|
-
badge.style.transform = `translate(${nextX + PAINT_CURSOR_BADGE_OFFSET_X}px, ${nextY + PAINT_CURSOR_BADGE_OFFSET_Y}px)`
|
|
734
|
+
setPosition({ x: nextX, y: nextY })
|
|
722
735
|
}
|
|
723
736
|
|
|
724
|
-
const
|
|
737
|
+
const updateFromEvent = (e: PointerEvent) => {
|
|
725
738
|
const rect = el.getBoundingClientRect()
|
|
726
739
|
nextX = e.clientX - rect.left
|
|
727
740
|
nextY = e.clientY - rect.top
|
|
728
|
-
|
|
729
741
|
if (frame === 0) {
|
|
730
742
|
frame = window.requestAnimationFrame(flushPosition)
|
|
731
743
|
}
|
|
@@ -735,17 +747,19 @@ function PaintCursorLayer({
|
|
|
735
747
|
window.cancelAnimationFrame(frame)
|
|
736
748
|
frame = 0
|
|
737
749
|
}
|
|
738
|
-
|
|
739
|
-
badge.style.display = 'none'
|
|
740
|
-
}
|
|
750
|
+
setPosition(null)
|
|
741
751
|
}
|
|
742
|
-
el.addEventListener('pointermove',
|
|
752
|
+
el.addEventListener('pointermove', updateFromEvent)
|
|
753
|
+
el.addEventListener('pointerenter', updateFromEvent)
|
|
754
|
+
el.addEventListener('pointerdown', updateFromEvent)
|
|
743
755
|
el.addEventListener('pointerleave', onLeave)
|
|
744
756
|
return () => {
|
|
745
757
|
if (frame !== 0) {
|
|
746
758
|
window.cancelAnimationFrame(frame)
|
|
747
759
|
}
|
|
748
|
-
el.removeEventListener('pointermove',
|
|
760
|
+
el.removeEventListener('pointermove', updateFromEvent)
|
|
761
|
+
el.removeEventListener('pointerenter', updateFromEvent)
|
|
762
|
+
el.removeEventListener('pointerdown', updateFromEvent)
|
|
749
763
|
el.removeEventListener('pointerleave', onLeave)
|
|
750
764
|
}
|
|
751
765
|
}, [active, containerRef])
|
|
@@ -755,29 +769,15 @@ function PaintCursorLayer({
|
|
|
755
769
|
(activePaintMaterial.material !== undefined ||
|
|
756
770
|
activePaintMaterial.materialPreset !== undefined),
|
|
757
771
|
)
|
|
758
|
-
const label = hasMaterial ? `Paint ${activePaintTarget}` : 'Choose material'
|
|
759
|
-
const icon = 'mdi:format-color-fill'
|
|
760
772
|
|
|
761
|
-
|
|
762
|
-
if (!active && badgeRef.current) {
|
|
763
|
-
badgeRef.current.style.display = 'none'
|
|
764
|
-
}
|
|
765
|
-
}, [active])
|
|
766
|
-
|
|
767
|
-
if (!active) return null
|
|
773
|
+
if (!active || !position) return null
|
|
768
774
|
|
|
769
775
|
return (
|
|
770
776
|
<div
|
|
771
|
-
className="pointer-events-none"
|
|
772
|
-
|
|
773
|
-
style={{ display: 'none', position: 'absolute', left: 0, top: 0 }}
|
|
777
|
+
className="pointer-events-none absolute z-40"
|
|
778
|
+
style={{ left: 0, top: 0, transform: `translate(${position.x}px, ${position.y}px)` }}
|
|
774
779
|
>
|
|
775
|
-
<PaintCursorBadge
|
|
776
|
-
disabled={!hasMaterial}
|
|
777
|
-
icon={icon}
|
|
778
|
-
label={label}
|
|
779
|
-
position={{ x: 0, y: 0 }}
|
|
780
|
-
/>
|
|
780
|
+
<PaintCursorBadge disabled={!hasMaterial} position={{ x: 0, y: 0 }} />
|
|
781
781
|
</div>
|
|
782
782
|
)
|
|
783
783
|
}
|
|
@@ -789,15 +789,21 @@ const ViewerCanvas = memo(function ViewerCanvas({
|
|
|
789
789
|
isVersionPreviewMode,
|
|
790
790
|
isLoading,
|
|
791
791
|
isFirstPersonMode,
|
|
792
|
+
isStudioMode,
|
|
792
793
|
hasLoadedInitialScene,
|
|
793
794
|
showLoader,
|
|
795
|
+
sceneReadyKey,
|
|
796
|
+
onSceneReadyChange,
|
|
794
797
|
onThumbnailCapture,
|
|
795
798
|
}: {
|
|
796
799
|
isVersionPreviewMode: boolean
|
|
797
800
|
isLoading: boolean
|
|
798
801
|
isFirstPersonMode: boolean
|
|
802
|
+
isStudioMode: boolean
|
|
799
803
|
hasLoadedInitialScene: boolean
|
|
800
804
|
showLoader: boolean
|
|
805
|
+
sceneReadyKey: number
|
|
806
|
+
onSceneReadyChange: (ready: boolean) => void
|
|
801
807
|
onThumbnailCapture?: (blob: Blob, cameraData: SnapshotCameraData) => void
|
|
802
808
|
}) {
|
|
803
809
|
const viewMode = useEditor((s) => s.viewMode)
|
|
@@ -839,7 +845,7 @@ const ViewerCanvas = memo(function ViewerCanvas({
|
|
|
839
845
|
window.removeEventListener('pointermove', handlePointerMove)
|
|
840
846
|
window.removeEventListener('pointerup', handlePointerUp)
|
|
841
847
|
}
|
|
842
|
-
}, [])
|
|
848
|
+
}, [setFloorplanPaneRatio])
|
|
843
849
|
|
|
844
850
|
useEffect(() => {
|
|
845
851
|
setIsCameraControlsHintVisible(!readCameraControlsHintDismissed())
|
|
@@ -899,19 +905,24 @@ const ViewerCanvas = memo(function ViewerCanvas({
|
|
|
899
905
|
) : null}
|
|
900
906
|
<SelectionPersistenceManager enabled={hasLoadedInitialScene && !showLoader} />
|
|
901
907
|
<Viewer
|
|
908
|
+
defaultRender={EDITOR_DEFAULT_RENDER}
|
|
902
909
|
hoverStyles={EDITOR_HOVER_STYLES}
|
|
910
|
+
onSceneReadyChange={onSceneReadyChange}
|
|
911
|
+
renderContext="editor"
|
|
912
|
+
sceneReadyKey={sceneReadyKey}
|
|
903
913
|
selectionManager={isFirstPersonMode ? 'default' : 'custom'}
|
|
904
914
|
>
|
|
905
915
|
<ViewerSceneContent
|
|
906
916
|
isFirstPersonMode={isFirstPersonMode}
|
|
907
|
-
isLoading={
|
|
917
|
+
isLoading={showLoader}
|
|
918
|
+
isStudioMode={isStudioMode}
|
|
908
919
|
isVersionPreviewMode={isVersionPreviewMode}
|
|
909
920
|
onThumbnailCapture={onThumbnailCapture}
|
|
910
921
|
/>
|
|
911
922
|
</Viewer>
|
|
912
923
|
</div>
|
|
913
924
|
</div>
|
|
914
|
-
{!(
|
|
925
|
+
{!(showLoader || isVersionPreviewMode) && <ZoneLabelEditorSystem />}
|
|
915
926
|
</ErrorBoundary>
|
|
916
927
|
)
|
|
917
928
|
})
|
|
@@ -924,6 +935,7 @@ export default function Editor({
|
|
|
924
935
|
sidebarTabs,
|
|
925
936
|
viewerToolbarLeft,
|
|
926
937
|
viewerToolbarRight,
|
|
938
|
+
inspectorFooter,
|
|
927
939
|
projectId,
|
|
928
940
|
onLoad,
|
|
929
941
|
onSave,
|
|
@@ -938,12 +950,12 @@ export default function Editor({
|
|
|
938
950
|
settingsPanelProps,
|
|
939
951
|
sitePanelProps,
|
|
940
952
|
extraSidebarPanels,
|
|
941
|
-
presetsAdapter,
|
|
942
953
|
commandPaletteEmptyAction,
|
|
943
954
|
}: EditorProps) {
|
|
944
955
|
const isFirstPersonMode = useEditor((s) => s.isFirstPersonMode)
|
|
956
|
+
const isStudioMode = useEditor((s) => s.workspaceMode === 'studio')
|
|
945
957
|
|
|
946
|
-
useKeyboard({ isVersionPreviewMode, disabled: isFirstPersonMode })
|
|
958
|
+
useKeyboard({ isVersionPreviewMode, disabled: isFirstPersonMode || isStudioMode })
|
|
947
959
|
|
|
948
960
|
const { isLoadingSceneRef } = useAutoSave({
|
|
949
961
|
onSave,
|
|
@@ -954,6 +966,8 @@ export default function Editor({
|
|
|
954
966
|
|
|
955
967
|
const [isSceneLoading, setIsSceneLoading] = useState(false)
|
|
956
968
|
const [hasLoadedInitialScene, setHasLoadedInitialScene] = useState(false)
|
|
969
|
+
const [sceneReadyKey, setSceneReadyKey] = useState(0)
|
|
970
|
+
const [isViewerSceneReady, setIsViewerSceneReady] = useState(false)
|
|
957
971
|
const isPreviewMode = useEditor((s) => s.isPreviewMode)
|
|
958
972
|
const isCaptureMode = useEditor((s) => s.isCaptureMode)
|
|
959
973
|
|
|
@@ -980,15 +994,24 @@ export default function Editor({
|
|
|
980
994
|
async function load() {
|
|
981
995
|
isLoadingSceneRef.current = true
|
|
982
996
|
setHasLoadedInitialScene(false)
|
|
997
|
+
setIsViewerSceneReady(false)
|
|
983
998
|
setIsSceneLoading(true)
|
|
999
|
+
useScene.getState().unloadScene()
|
|
1000
|
+
useViewer.getState().resetSelection()
|
|
984
1001
|
|
|
985
1002
|
try {
|
|
986
1003
|
const sceneGraph = onLoad ? await onLoad() : loadSceneFromLocalStorage()
|
|
987
1004
|
if (!cancelled) {
|
|
988
1005
|
applySceneGraphToEditor(sceneGraph)
|
|
1006
|
+
setIsViewerSceneReady(false)
|
|
1007
|
+
setSceneReadyKey((key) => key + 1)
|
|
989
1008
|
}
|
|
990
1009
|
} catch {
|
|
991
|
-
if (!cancelled)
|
|
1010
|
+
if (!cancelled) {
|
|
1011
|
+
applySceneGraphToEditor(null)
|
|
1012
|
+
setIsViewerSceneReady(false)
|
|
1013
|
+
setSceneReadyKey((key) => key + 1)
|
|
1014
|
+
}
|
|
992
1015
|
} finally {
|
|
993
1016
|
if (!cancelled) {
|
|
994
1017
|
setIsSceneLoading(false)
|
|
@@ -1032,7 +1055,11 @@ export default function Editor({
|
|
|
1032
1055
|
}
|
|
1033
1056
|
}, [])
|
|
1034
1057
|
|
|
1035
|
-
const
|
|
1058
|
+
const handleSceneReadyChange = useCallback((ready: boolean) => {
|
|
1059
|
+
setIsViewerSceneReady(ready)
|
|
1060
|
+
}, [])
|
|
1061
|
+
|
|
1062
|
+
const showLoader = isLoading || isSceneLoading || !hasLoadedInitialScene || !isViewerSceneReady
|
|
1036
1063
|
|
|
1037
1064
|
const firstPersonPreviousLevelRef = useRef(useViewer.getState().selection.levelId)
|
|
1038
1065
|
const wasFirstPersonModeRef = useRef(isFirstPersonMode)
|
|
@@ -1071,7 +1098,12 @@ export default function Editor({
|
|
|
1071
1098
|
}, [isFirstPersonMode])
|
|
1072
1099
|
|
|
1073
1100
|
const previewViewerContent = (
|
|
1074
|
-
<Viewer
|
|
1101
|
+
<Viewer
|
|
1102
|
+
defaultRender={EDITOR_DEFAULT_RENDER}
|
|
1103
|
+
hoverStyles={EDITOR_HOVER_STYLES}
|
|
1104
|
+
renderContext="editor"
|
|
1105
|
+
selectionManager="default"
|
|
1106
|
+
>
|
|
1075
1107
|
<ExportManager />
|
|
1076
1108
|
<ViewerZoneSystem />
|
|
1077
1109
|
<CeilingSystem />
|
|
@@ -1079,7 +1111,6 @@ export default function Editor({
|
|
|
1079
1111
|
<StairEditSystem />
|
|
1080
1112
|
<CustomCameraControls />
|
|
1081
1113
|
<ThumbnailGenerator onThumbnailCapture={onThumbnailCapture} />
|
|
1082
|
-
<PresetThumbnailGenerator />
|
|
1083
1114
|
<InteractiveSystem />
|
|
1084
1115
|
</Viewer>
|
|
1085
1116
|
)
|
|
@@ -1089,8 +1120,11 @@ export default function Editor({
|
|
|
1089
1120
|
hasLoadedInitialScene={hasLoadedInitialScene}
|
|
1090
1121
|
isFirstPersonMode={isFirstPersonMode}
|
|
1091
1122
|
isLoading={isLoading}
|
|
1123
|
+
isStudioMode={isStudioMode}
|
|
1092
1124
|
isVersionPreviewMode={isVersionPreviewMode}
|
|
1125
|
+
onSceneReadyChange={handleSceneReadyChange}
|
|
1093
1126
|
onThumbnailCapture={onThumbnailCapture}
|
|
1127
|
+
sceneReadyKey={sceneReadyKey}
|
|
1094
1128
|
showLoader={showLoader}
|
|
1095
1129
|
/>
|
|
1096
1130
|
)
|
|
@@ -1115,18 +1149,19 @@ export default function Editor({
|
|
|
1115
1149
|
}
|
|
1116
1150
|
|
|
1117
1151
|
const tabBarTabs =
|
|
1118
|
-
sidebarTabs?.map(({ id, label, mobileDefaultSnap, mobileIcon }) => ({
|
|
1152
|
+
sidebarTabs?.map(({ id, label, mobileDefaultSnap, mobileIcon, icon }) => ({
|
|
1119
1153
|
id,
|
|
1120
1154
|
label,
|
|
1121
1155
|
mobileDefaultSnap,
|
|
1122
1156
|
mobileIcon,
|
|
1157
|
+
icon,
|
|
1123
1158
|
})) ?? []
|
|
1124
1159
|
|
|
1125
1160
|
return (
|
|
1126
|
-
|
|
1161
|
+
<>
|
|
1127
1162
|
{showLoader && (
|
|
1128
1163
|
<div className="fixed inset-0 z-60">
|
|
1129
|
-
<SceneLoader />
|
|
1164
|
+
<SceneLoader className="bg-background" />
|
|
1130
1165
|
</div>
|
|
1131
1166
|
)}
|
|
1132
1167
|
|
|
@@ -1142,14 +1177,14 @@ export default function Editor({
|
|
|
1142
1177
|
overlays={
|
|
1143
1178
|
<>
|
|
1144
1179
|
{!isCaptureMode && <FloatingLevelSelector />}
|
|
1145
|
-
{!(isVersionPreviewMode || isCaptureMode) && (
|
|
1180
|
+
{!(isVersionPreviewMode || isCaptureMode || isStudioMode) && (
|
|
1146
1181
|
<div className="pointer-events-auto">
|
|
1147
1182
|
<ActionMenu />
|
|
1148
1183
|
</div>
|
|
1149
1184
|
)}
|
|
1150
|
-
{!(isVersionPreviewMode || isCaptureMode) && (
|
|
1185
|
+
{!(isVersionPreviewMode || isCaptureMode || isStudioMode) && (
|
|
1151
1186
|
<div className="pointer-events-auto">
|
|
1152
|
-
<PanelManager />
|
|
1187
|
+
<PanelManager inspectorFooter={inspectorFooter} />
|
|
1153
1188
|
</div>
|
|
1154
1189
|
)}
|
|
1155
1190
|
{!isCaptureMode && (
|
|
@@ -1157,6 +1192,11 @@ export default function Editor({
|
|
|
1157
1192
|
<HelperManager />
|
|
1158
1193
|
</div>
|
|
1159
1194
|
)}
|
|
1195
|
+
{isFirstPersonMode && (
|
|
1196
|
+
<FirstPersonOverlay
|
|
1197
|
+
onExit={() => useEditor.getState().setFirstPersonMode(false)}
|
|
1198
|
+
/>
|
|
1199
|
+
)}
|
|
1160
1200
|
{viewerBanner}
|
|
1161
1201
|
{projectId ? <SnapshotCaptureOverlay projectId={projectId} /> : null}
|
|
1162
1202
|
</>
|
|
@@ -1170,15 +1210,9 @@ export default function Editor({
|
|
|
1170
1210
|
/>
|
|
1171
1211
|
<EditorCommands />
|
|
1172
1212
|
<CommandPalette emptyAction={commandPaletteEmptyAction} />
|
|
1173
|
-
{/* First-person overlay — rendered on top of normal layout */}
|
|
1174
|
-
{isFirstPersonMode && (
|
|
1175
|
-
<div className="pointer-events-none fixed inset-0 z-50">
|
|
1176
|
-
<FirstPersonOverlay onExit={() => useEditor.getState().setFirstPersonMode(false)} />
|
|
1177
|
-
</div>
|
|
1178
|
-
)}
|
|
1179
1213
|
</>
|
|
1180
1214
|
)}
|
|
1181
|
-
|
|
1215
|
+
</>
|
|
1182
1216
|
)
|
|
1183
1217
|
}
|
|
1184
1218
|
|
|
@@ -1189,57 +1223,52 @@ export default function Editor({
|
|
|
1189
1223
|
const overlayLeft = LAYOUT_PADDING + (isSidebarCollapsed ? 8 : sidebarWidth) + LAYOUT_GAP
|
|
1190
1224
|
|
|
1191
1225
|
return (
|
|
1192
|
-
<
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
<
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
)}
|
|
1226
|
+
<div className="dark flex h-full w-full gap-3 bg-neutral-100 p-3 text-foreground">
|
|
1227
|
+
{showLoader && (
|
|
1228
|
+
<div className="fixed inset-0 z-60">
|
|
1229
|
+
<SceneLoader className="bg-background" />
|
|
1230
|
+
</div>
|
|
1231
|
+
)}
|
|
1199
1232
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
</ViewerOverlays>
|
|
1234
|
-
{/* First-person overlay — rendered on top of normal layout */}
|
|
1233
|
+
{!isLoading && isPreviewMode ? (
|
|
1234
|
+
<>
|
|
1235
|
+
<ViewerOverlay onBack={() => useEditor.getState().setPreviewMode(false)} />
|
|
1236
|
+
<div className="h-full w-full">{previewViewerContent}</div>
|
|
1237
|
+
</>
|
|
1238
|
+
) : (
|
|
1239
|
+
<>
|
|
1240
|
+
{/* Sidebar */}
|
|
1241
|
+
<SidebarSlot>
|
|
1242
|
+
<AppSidebar
|
|
1243
|
+
appMenuButton={appMenuButton}
|
|
1244
|
+
commandPaletteEmptyAction={commandPaletteEmptyAction}
|
|
1245
|
+
extraPanels={extraSidebarPanels}
|
|
1246
|
+
settingsPanelProps={settingsPanelProps}
|
|
1247
|
+
sidebarTop={sidebarTop}
|
|
1248
|
+
sitePanelProps={sitePanelProps}
|
|
1249
|
+
/>
|
|
1250
|
+
</SidebarSlot>
|
|
1251
|
+
|
|
1252
|
+
{/* Viewer area */}
|
|
1253
|
+
<div className="relative flex-1 overflow-hidden rounded-xl">{viewerCanvas}</div>
|
|
1254
|
+
|
|
1255
|
+
{/* Fixed UI overlays scoped to the viewer area */}
|
|
1256
|
+
<ViewerOverlays left={overlayLeft}>
|
|
1257
|
+
<div className="pointer-events-auto">
|
|
1258
|
+
<ActionMenu />
|
|
1259
|
+
</div>
|
|
1260
|
+
<div className="pointer-events-auto">
|
|
1261
|
+
<PanelManager />
|
|
1262
|
+
</div>
|
|
1263
|
+
<div className="pointer-events-auto">
|
|
1264
|
+
<HelperManager />
|
|
1265
|
+
</div>
|
|
1235
1266
|
{isFirstPersonMode && (
|
|
1236
|
-
<
|
|
1237
|
-
<FirstPersonOverlay onExit={() => useEditor.getState().setFirstPersonMode(false)} />
|
|
1238
|
-
</div>
|
|
1267
|
+
<FirstPersonOverlay onExit={() => useEditor.getState().setFirstPersonMode(false)} />
|
|
1239
1268
|
)}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
</
|
|
1269
|
+
</ViewerOverlays>
|
|
1270
|
+
</>
|
|
1271
|
+
)}
|
|
1272
|
+
</div>
|
|
1244
1273
|
)
|
|
1245
1274
|
}
|