@pascal-app/editor 1.0.0 → 1.0.3
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 -6
- package/src/components/editor/bake-exporter.tsx +4 -0
- package/src/components/editor/bake-thumbnail.tsx +4 -1
- package/src/components/editor/custom-camera-controls.tsx +22 -74
- package/src/components/editor/editor-layout-mobile.tsx +1 -0
- package/src/components/editor/floating-action-menu.tsx +14 -1
- package/src/components/editor/floorplan-panel.tsx +233 -166
- package/src/components/editor/grid.tsx +31 -7
- package/src/components/editor/group-actions.ts +5 -9
- package/src/components/editor/group-floating-action-menu.tsx +19 -9
- package/src/components/editor/group-move-3d.ts +4 -6
- package/src/components/editor/group-rotate-handle.tsx +81 -35
- package/src/components/editor/group-selection-box-3d.tsx +25 -17
- package/src/components/editor/group-transform-shared.test.ts +126 -1
- package/src/components/editor/group-transform-shared.ts +79 -73
- package/src/components/editor/handles/handle-arrow.tsx +72 -2
- package/src/components/editor/handles/handle-placement.test.ts +111 -0
- package/src/components/editor/handles/handle-placement.ts +69 -0
- package/src/components/editor/handles/linear-resize-drag.ts +13 -2
- package/src/components/editor/handles/use-handle-drag.ts +80 -8
- package/src/components/editor/index.tsx +36 -13
- package/src/components/editor/node-action-menu.tsx +2 -0
- package/src/components/editor/node-arrow-handles.tsx +162 -69
- package/src/components/editor/registry-action-contributions.tsx +53 -0
- package/src/components/editor/selection-manager.tsx +49 -0
- package/src/components/editor/thumbnail-generator.tsx +11 -2
- package/src/components/editor/wall-move-side-handles.tsx +71 -21
- package/src/components/editor/wall-opening-highlights.tsx +2 -3
- package/src/components/editor-2d/floorplan-group-move.tsx +17 -24
- package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +33 -12
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +12 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +154 -49
- package/src/components/editor-2d/renderers/floorplan-draft-wall-measurement.tsx +166 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +49 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +113 -24
- package/src/components/procedural-items/inspector.tsx +223 -0
- package/src/components/systems/ceiling/ceiling-system.tsx +3 -1
- package/src/components/systems/zone/zone-system.tsx +8 -4
- package/src/components/tools/__fixtures__/hosting.ts +10 -0
- package/src/components/tools/item/item-surface-placement.test.ts +169 -0
- package/src/components/tools/item/item-surface-strategy-resolver.test.ts +204 -0
- package/src/components/tools/item/placement-strategies.ts +212 -200
- package/src/components/tools/item/placement-types.ts +4 -0
- package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +257 -0
- package/src/components/tools/item/use-draft-node.ts +136 -47
- package/src/components/tools/item/use-placement-coordinator.tsx +391 -157
- package/src/components/tools/registry/item-surface-child-frame.test.ts +90 -0
- package/src/components/tools/registry/item-surface-move.test.ts +509 -0
- package/src/components/tools/registry/item-surface-move.ts +463 -0
- package/src/components/tools/registry/item-surface-resolver.test.ts +257 -0
- package/src/components/tools/registry/move-registry-node-tool.tsx +237 -78
- package/src/components/tools/shared/affordance-dispatch.test.ts +24 -0
- package/src/components/tools/shared/affordance-dispatch.ts +13 -0
- package/src/components/tools/shared/draft-measurement-label.tsx +38 -0
- package/src/components/tools/shared/pointer-support-cap.test.ts +27 -0
- package/src/components/tools/shared/pointer-support-cap.ts +27 -13
- package/src/components/tools/shared/shelf-stickiness.ts +54 -0
- package/src/components/tools/shared/surface-hit.test.ts +56 -0
- package/src/components/tools/shared/surface-hit.ts +23 -0
- package/src/components/tools/shared/surface-rejection.test.tsx +107 -0
- package/src/components/tools/shared/surface-rejection.tsx +43 -0
- package/src/components/tools/tool-manager.tsx +36 -3
- package/src/components/tools/zone/zone-tool.tsx +13 -4
- package/src/components/ui/command-palette/editor-commands.tsx +54 -1
- package/src/components/ui/controls/material-paint-panel.tsx +6 -67
- package/src/components/ui/controls/material-picker.tsx +5 -67
- package/src/components/ui/controls/metric-control.tsx +29 -13
- package/src/components/ui/controls/slider-control.tsx +87 -35
- package/src/components/ui/floating-level-selector.tsx +81 -5
- package/src/components/ui/helpers/helper-manager.tsx +24 -6
- package/src/components/ui/helpers/registered-tool-helper.tsx +2 -32
- package/src/components/ui/item-catalog/item-catalog.tsx +5 -30
- package/src/components/ui/panels/multi-height-mode.tsx +27 -21
- package/src/components/ui/panels/multi-parametric-inspector.tsx +6 -3
- package/src/components/ui/panels/parametric-field-control.tsx +1 -1
- package/src/components/ui/panels/parametric-inspector.tsx +9 -17
- package/src/components/ui/panels/reference-panel.tsx +3 -3
- package/src/components/ui/primitives/checkbox.tsx +34 -0
- package/src/components/ui/primitives/shortcut-token.tsx +4 -0
- package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +2 -2
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +669 -336
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +2 -2
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.test.ts +31 -4
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +30 -11
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +8 -1
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +106 -4
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +2 -7
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +3 -8
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +6 -1
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +224 -0
- package/src/components/ui/sidebar/panels/site-panel/zone-membership-checkbox.tsx +22 -0
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +24 -7
- package/src/components/ui/snap-target-badge.tsx +2 -0
- package/src/components/viewer/floorplan-preview-visibility.test.ts +35 -0
- package/src/components/viewer/floorplan-preview-visibility.ts +19 -0
- package/src/components/viewer/floorplan-preview.tsx +1 -12
- package/src/components/viewer/viewer-scene-header.tsx +3 -0
- package/src/components/viewer/viewer-units-panel.tsx +116 -0
- package/src/components/viewer-zone-system.tsx +7 -2
- package/src/hooks/use-auto-save.integration.test.tsx +197 -0
- package/src/hooks/use-auto-save.ts +21 -6
- package/src/hooks/use-ceiling-events.test.ts +25 -2
- package/src/hooks/use-ceiling-events.ts +7 -1
- package/src/hooks/use-grid-events.test.ts +2 -0
- package/src/hooks/use-grid-events.ts +16 -11
- package/src/hooks/use-keyboard.test.ts +7 -0
- package/src/hooks/use-keyboard.ts +43 -28
- package/src/index.tsx +68 -3
- package/src/lib/catalog-panel-model.ts +39 -0
- package/src/lib/catalog-selection.test.ts +27 -0
- package/src/lib/catalog-selection.ts +8 -0
- package/src/lib/contextual-help.test.ts +27 -1
- package/src/lib/contextual-help.ts +16 -9
- package/src/lib/export-texture-utils.test.ts +167 -0
- package/src/lib/export-texture-utils.ts +121 -0
- package/src/lib/export-user-data.test.ts +48 -0
- package/src/lib/export-user-data.ts +69 -0
- package/src/lib/floorplan/floorplan-export.test.ts +142 -2
- package/src/lib/floorplan/floorplan-export.tsx +9 -3
- package/src/lib/floorplan/floorplan-extension.ts +16 -0
- package/src/lib/floorplan/floorplan-readonly.ts +4 -0
- package/src/lib/fresh-planar-placement.ts +173 -33
- package/src/lib/glb-export-serialize.test.ts +280 -0
- package/src/lib/glb-export.test.ts +265 -16
- package/src/lib/glb-export.ts +197 -37
- package/src/lib/grid-frame.test.ts +19 -0
- package/src/lib/grid-frame.ts +18 -0
- package/src/lib/interaction/scope.ts +9 -1
- package/src/lib/keyboard-pan.test.ts +62 -0
- package/src/lib/keyboard-pan.ts +89 -0
- package/src/lib/level-duplication.test.ts +73 -0
- package/src/lib/level-duplication.ts +32 -2
- package/src/lib/linear-display.test.ts +59 -0
- package/src/lib/linear-display.ts +39 -0
- package/src/lib/local-guide-image.test.ts +24 -0
- package/src/lib/local-guide-image.ts +34 -1
- package/src/lib/material-catalog-model.ts +72 -0
- package/src/lib/material-paint-panel-model.ts +76 -0
- package/src/lib/node-action-movement.ts +21 -0
- package/src/lib/overlay-transform.test.ts +28 -0
- package/src/lib/overlay-transform.ts +12 -0
- package/src/lib/panel-rows.ts +75 -0
- package/src/lib/panel-tool-options.test.ts +34 -0
- package/src/lib/panel-tool-options.ts +46 -0
- package/src/lib/parametric-node-update.ts +37 -0
- package/src/lib/portable-export.test.ts +89 -0
- package/src/lib/portable-export.ts +84 -11
- package/src/lib/print-shell-compiler-manifold-core.ts +59 -10
- package/src/lib/print-shell-compiler-manifold-worker.ts +14 -1
- package/src/lib/print-shell-compiler-manifold.worker.ts +1 -1
- package/src/lib/print-shell-compiler-protocol.ts +8 -0
- package/src/lib/scene-clipboard.ts +1 -1
- package/src/lib/scene.test.ts +99 -0
- package/src/lib/scene.ts +21 -3
- package/src/lib/send-to-app.test.ts +108 -0
- package/src/lib/send-to-app.ts +121 -0
- package/src/lib/sfx/movement-tick.ts +37 -0
- package/src/lib/snapping-mode.test.ts +13 -2
- package/src/lib/snapping-mode.ts +11 -6
- package/src/lib/spatial-drag-plane.test.ts +53 -0
- package/src/lib/spatial-drag-plane.ts +19 -0
- package/src/lib/spatial-pointer-input.test.ts +72 -0
- package/src/lib/spatial-pointer-input.ts +62 -0
- package/src/lib/spatial-snap.test.ts +47 -0
- package/src/lib/surface-attachment.ts +77 -0
- package/src/lib/terrain-panel-model.ts +117 -0
- package/src/lib/units.ts +254 -0
- package/src/lib/usdz-export.ts +43 -16
- package/src/lib/use-linear-display.ts +8 -32
- package/src/lib/world-grid-snap.test.ts +48 -0
- package/src/lib/world-grid-snap.ts +9 -5
- package/src/store/rotation-snapping.test.ts +53 -0
- package/src/store/use-editor.tsx +10 -2
- package/src/store/use-floorplan-draft-preview.ts +10 -1
- package/src/store/use-handle-group.ts +4 -0
- package/src/store/use-interaction-scope.ts +155 -6
- package/src/store/use-send-to-app.ts +21 -0
package/package.json
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/editor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Pascal building editor component",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./src/index.tsx",
|
|
9
|
-
"./catalog": "./src/components/ui/item-catalog/catalog-items.tsx"
|
|
9
|
+
"./catalog": "./src/components/ui/item-catalog/catalog-items.tsx",
|
|
10
|
+
"./procedural-items": "./src/components/procedural-items/inspector.tsx"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
13
|
"check-types": "tsgo --noEmit",
|
|
13
14
|
"test": "bun test src"
|
|
14
15
|
},
|
|
15
16
|
"peerDependencies": {
|
|
16
|
-
"@pascal-app/core": "^1.0.
|
|
17
|
-
"@pascal-app/viewer": "^1.0.
|
|
17
|
+
"@pascal-app/core": "^1.0.3",
|
|
18
|
+
"@pascal-app/viewer": "^1.0.3",
|
|
18
19
|
"@react-three/drei": "^10",
|
|
19
20
|
"@react-three/fiber": "^9",
|
|
20
21
|
"next": ">=15",
|
|
@@ -61,8 +62,8 @@
|
|
|
61
62
|
"zustand": "^5.0.11"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
|
-
"@pascal-app/core": "^1.0.
|
|
65
|
-
"@pascal-app/viewer": "^1.0.
|
|
65
|
+
"@pascal-app/core": "^1.0.3",
|
|
66
|
+
"@pascal-app/viewer": "^1.0.3",
|
|
66
67
|
"@pascal/typescript-config": "*",
|
|
67
68
|
"@react-three/test-renderer": "^9.1.0",
|
|
68
69
|
"@types/blob-stream": "^0.1.33",
|
|
@@ -74,5 +75,10 @@
|
|
|
74
75
|
"@types/three": "^0.184.0",
|
|
75
76
|
"fast-xml-parser": "^5.4.2",
|
|
76
77
|
"typescript": "6.0.3"
|
|
78
|
+
},
|
|
79
|
+
"repository": {
|
|
80
|
+
"type": "git",
|
|
81
|
+
"url": "git+https://github.com/pascalorg/editor.git",
|
|
82
|
+
"directory": "packages/editor"
|
|
77
83
|
}
|
|
78
84
|
}
|
|
@@ -32,6 +32,10 @@ export function BakeExporter({
|
|
|
32
32
|
const buffer = await exportSceneToGlb(sceneGroup, useScene.getState().nodes, {
|
|
33
33
|
textures: 'reference',
|
|
34
34
|
purpose: 'viewer',
|
|
35
|
+
// The bake worker owns the deadline (BAKE_CAPTURE_TIMEOUT_MS); under
|
|
36
|
+
// SwiftShader a moderate scene can legitimately outlast the
|
|
37
|
+
// interactive default.
|
|
38
|
+
timeoutMs: Number.POSITIVE_INFINITY,
|
|
35
39
|
})
|
|
36
40
|
onComplete(buffer)
|
|
37
41
|
} catch (err) {
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
GRID_LAYER,
|
|
7
7
|
heroCameraPose,
|
|
8
8
|
temporarilyHideNodeTypes,
|
|
9
|
+
useSceneAtmosphere,
|
|
9
10
|
useViewer,
|
|
10
11
|
} from '@pascal-app/viewer'
|
|
11
12
|
import { useThree } from '@react-three/fiber'
|
|
@@ -25,6 +26,7 @@ export function BakeThumbnail({
|
|
|
25
26
|
}) {
|
|
26
27
|
const renderer = useThree((state) => state.gl)
|
|
27
28
|
const scene = useThree((state) => state.scene)
|
|
29
|
+
const atmosphere = useSceneAtmosphere()
|
|
28
30
|
const doneRef = useRef(false)
|
|
29
31
|
|
|
30
32
|
useEffect(() => {
|
|
@@ -61,6 +63,7 @@ export function BakeThumbnail({
|
|
|
61
63
|
renderer: renderer as unknown as WebGPURenderer,
|
|
62
64
|
scene,
|
|
63
65
|
camera,
|
|
66
|
+
atmosphere,
|
|
64
67
|
})
|
|
65
68
|
if (!pipeline) {
|
|
66
69
|
onError('thumbnail pipeline failed to build')
|
|
@@ -89,7 +92,7 @@ export function BakeThumbnail({
|
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
void run()
|
|
92
|
-
}, [active, onComplete, onError, renderer, scene])
|
|
95
|
+
}, [active, atmosphere, onComplete, onError, renderer, scene])
|
|
93
96
|
|
|
94
97
|
return null
|
|
95
98
|
}
|
|
@@ -31,6 +31,17 @@ import {
|
|
|
31
31
|
withCameraPoseDistance,
|
|
32
32
|
} from '../../lib/camera-pose'
|
|
33
33
|
import { EDITOR_LAYER } from '../../lib/constants'
|
|
34
|
+
import {
|
|
35
|
+
acceptsKeyboardPan,
|
|
36
|
+
clearKeyboardPanKeys,
|
|
37
|
+
hasKeyboardPanInput,
|
|
38
|
+
isEditableKeyboardTarget,
|
|
39
|
+
isKeyboardPanKey,
|
|
40
|
+
type KeyboardPanState,
|
|
41
|
+
keyboardPanDirection,
|
|
42
|
+
keyboardPanSpeed,
|
|
43
|
+
setKeyboardPanKey,
|
|
44
|
+
} from '../../lib/keyboard-pan'
|
|
34
45
|
import { editorOwnsOneFingerDrag } from '../../lib/touch-gesture-priority'
|
|
35
46
|
import { publishCameraPose } from '../../store/camera-pose-store'
|
|
36
47
|
import useEditor from '../../store/use-editor'
|
|
@@ -51,11 +62,11 @@ const tempTarget = new Vector3()
|
|
|
51
62
|
const transitionFreezePosition = new Vector3()
|
|
52
63
|
const transitionFreezeTarget = new Vector3()
|
|
53
64
|
const keyboardPanSpherical = new Spherical()
|
|
65
|
+
// In 2D-only view the canvas is paused, so the floor plan drives WASD, orbit and
|
|
66
|
+
// top view itself (`floorplan-panel.tsx`) and the camera stands down.
|
|
67
|
+
const planOwnsNavigation = () => useEditor.getState().viewMode === '2d'
|
|
54
68
|
const DEFAULT_MAX_POLAR_ANGLE = Math.PI / 2 - 0.1
|
|
55
69
|
const DEBUG_MAX_POLAR_ANGLE = Math.PI - 0.05
|
|
56
|
-
const KEYBOARD_PAN_VIEW_WIDTH_PER_SECOND = 0.65
|
|
57
|
-
const KEYBOARD_PAN_MIN_SPEED = 2
|
|
58
|
-
const KEYBOARD_PAN_MAX_SPEED = 55
|
|
59
70
|
type CameraMode = ReturnType<typeof useViewer.getState>['cameraMode']
|
|
60
71
|
type CameraPoseSnapshot = {
|
|
61
72
|
mode: CameraMode
|
|
@@ -114,54 +125,6 @@ function freezeCameraControlTransition(control: CameraControlsImpl) {
|
|
|
114
125
|
)
|
|
115
126
|
}
|
|
116
127
|
|
|
117
|
-
function isEditableKeyboardTarget(target: EventTarget | null) {
|
|
118
|
-
return (
|
|
119
|
-
target instanceof HTMLInputElement ||
|
|
120
|
-
target instanceof HTMLTextAreaElement ||
|
|
121
|
-
target instanceof HTMLSelectElement ||
|
|
122
|
-
(target instanceof HTMLElement && target.isContentEditable)
|
|
123
|
-
)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
type KeyboardPanState = {
|
|
127
|
-
forward: boolean
|
|
128
|
-
backward: boolean
|
|
129
|
-
left: boolean
|
|
130
|
-
right: boolean
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function setKeyboardPanKey(state: KeyboardPanState, code: string, pressed: boolean): boolean {
|
|
134
|
-
if (code === 'KeyW') {
|
|
135
|
-
const changed = state.forward !== pressed
|
|
136
|
-
state.forward = pressed
|
|
137
|
-
return changed
|
|
138
|
-
}
|
|
139
|
-
if (code === 'KeyS') {
|
|
140
|
-
const changed = state.backward !== pressed
|
|
141
|
-
state.backward = pressed
|
|
142
|
-
return changed
|
|
143
|
-
}
|
|
144
|
-
if (code === 'KeyA') {
|
|
145
|
-
const changed = state.left !== pressed
|
|
146
|
-
state.left = pressed
|
|
147
|
-
return changed
|
|
148
|
-
}
|
|
149
|
-
if (code === 'KeyD') {
|
|
150
|
-
const changed = state.right !== pressed
|
|
151
|
-
state.right = pressed
|
|
152
|
-
return changed
|
|
153
|
-
}
|
|
154
|
-
return false
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function isKeyboardPanKey(code: string): boolean {
|
|
158
|
-
return code === 'KeyW' || code === 'KeyA' || code === 'KeyS' || code === 'KeyD'
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function hasKeyboardPanInput(state: KeyboardPanState): boolean {
|
|
162
|
-
return state.forward || state.backward || state.left || state.right
|
|
163
|
-
}
|
|
164
|
-
|
|
165
128
|
type CameraViewportSize = {
|
|
166
129
|
width: number
|
|
167
130
|
height: number
|
|
@@ -667,19 +630,14 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
667
630
|
}
|
|
668
631
|
}
|
|
669
632
|
|
|
670
|
-
const
|
|
671
|
-
const horizontal = (panKeys.right ? 1 : 0) - (panKeys.left ? 1 : 0)
|
|
672
|
-
const vertical = (panKeys.forward ? 1 : 0) - (panKeys.backward ? 1 : 0)
|
|
633
|
+
const { horizontal, vertical } = keyboardPanDirection(keyboardPanKeys.current)
|
|
673
634
|
if (horizontal === 0 && vertical === 0) return
|
|
674
635
|
|
|
675
636
|
const control = controls.current
|
|
676
637
|
|
|
677
638
|
control.getSpherical(keyboardPanSpherical, false)
|
|
678
639
|
const viewWidth = getCameraViewWidth(camera, keyboardPanSpherical.radius, viewportSize)
|
|
679
|
-
const speed =
|
|
680
|
-
Math.max(viewWidth * KEYBOARD_PAN_VIEW_WIDTH_PER_SECOND, KEYBOARD_PAN_MIN_SPEED),
|
|
681
|
-
KEYBOARD_PAN_MAX_SPEED,
|
|
682
|
-
)
|
|
640
|
+
const speed = keyboardPanSpeed(viewWidth)
|
|
683
641
|
const step = (speed * Math.min(delta, 0.05)) / Math.hypot(horizontal, vertical)
|
|
684
642
|
|
|
685
643
|
if (horizontal !== 0) control.truck(horizontal * step, 0, true)
|
|
@@ -766,13 +724,6 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
766
724
|
let panPointerId: number | null = null
|
|
767
725
|
let panPointerButton: number | null = null
|
|
768
726
|
|
|
769
|
-
const clearKeyboardPanKeys = () => {
|
|
770
|
-
keyboardPanKeys.current.forward = false
|
|
771
|
-
keyboardPanKeys.current.backward = false
|
|
772
|
-
keyboardPanKeys.current.left = false
|
|
773
|
-
keyboardPanKeys.current.right = false
|
|
774
|
-
}
|
|
775
|
-
|
|
776
727
|
const setNavigationCursor = (cursor: 'grab' | 'grabbing') => {
|
|
777
728
|
document.body.style.cursor = cursor
|
|
778
729
|
gl.domElement.style.cursor = cursor
|
|
@@ -835,10 +786,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
835
786
|
|
|
836
787
|
const onKeyDown = (event: KeyboardEvent) => {
|
|
837
788
|
if (isKeyboardPanKey(event.code)) {
|
|
838
|
-
if (
|
|
839
|
-
!(event.metaKey || event.ctrlKey || event.altKey) &&
|
|
840
|
-
!isEditableKeyboardTarget(event.target)
|
|
841
|
-
) {
|
|
789
|
+
if (acceptsKeyboardPan(event) && !planOwnsNavigation()) {
|
|
842
790
|
const changed = setKeyboardPanKey(keyboardPanKeys.current, event.code, true)
|
|
843
791
|
if (changed) beginLocalCameraInteraction()
|
|
844
792
|
event.preventDefault()
|
|
@@ -930,7 +878,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
930
878
|
|
|
931
879
|
const onBlur = () => {
|
|
932
880
|
keyState.space = false
|
|
933
|
-
clearKeyboardPanKeys()
|
|
881
|
+
clearKeyboardPanKeys(keyboardPanKeys.current)
|
|
934
882
|
panPointerId = null
|
|
935
883
|
panPointerButton = null
|
|
936
884
|
clearNavigationCursor()
|
|
@@ -955,7 +903,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
955
903
|
window.removeEventListener('pointercancel', onPointerUp, true)
|
|
956
904
|
window.removeEventListener('blur', onBlur)
|
|
957
905
|
gl.domElement.removeEventListener('wheel', onWheel, true)
|
|
958
|
-
clearKeyboardPanKeys()
|
|
906
|
+
clearKeyboardPanKeys(keyboardPanKeys.current)
|
|
959
907
|
clearNavigationCursor()
|
|
960
908
|
cameraDraggingLifecycle.end()
|
|
961
909
|
}
|
|
@@ -1196,7 +1144,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1196
1144
|
}
|
|
1197
1145
|
|
|
1198
1146
|
const handleTopView = () => {
|
|
1199
|
-
if (isFirstPersonMode || !controls.current) return
|
|
1147
|
+
if (isFirstPersonMode || !controls.current || planOwnsNavigation()) return
|
|
1200
1148
|
|
|
1201
1149
|
const currentPolarAngle = controls.current.polarAngle
|
|
1202
1150
|
|
|
@@ -1208,7 +1156,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1208
1156
|
}
|
|
1209
1157
|
|
|
1210
1158
|
const handleOrbitCW = () => {
|
|
1211
|
-
if (isFirstPersonMode || !controls.current) return
|
|
1159
|
+
if (isFirstPersonMode || !controls.current || planOwnsNavigation()) return
|
|
1212
1160
|
|
|
1213
1161
|
const currentAzimuth = controls.current.azimuthAngle
|
|
1214
1162
|
const currentPolar = controls.current.polarAngle
|
|
@@ -1220,7 +1168,7 @@ export const CustomCameraControls = ({ paused = false }: { paused?: boolean }) =
|
|
|
1220
1168
|
}
|
|
1221
1169
|
|
|
1222
1170
|
const handleOrbitCCW = () => {
|
|
1223
|
-
if (isFirstPersonMode || !controls.current) return
|
|
1171
|
+
if (isFirstPersonMode || !controls.current || planOwnsNavigation()) return
|
|
1224
1172
|
|
|
1225
1173
|
const currentAzimuth = controls.current.azimuthAngle
|
|
1226
1174
|
const currentPolar = controls.current.polarAngle
|
|
@@ -236,6 +236,7 @@ export function EditorLayoutMobile({
|
|
|
236
236
|
<div className="relative h-full w-full overflow-hidden">{viewerContent}</div>
|
|
237
237
|
{overlays && (
|
|
238
238
|
<div
|
|
239
|
+
data-viewer-bounds
|
|
239
240
|
className="pointer-events-none absolute inset-0 z-30"
|
|
240
241
|
style={{ transform: 'translateZ(0)' }}
|
|
241
242
|
>
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
} from '../../lib/fresh-planar-placement'
|
|
52
52
|
import { resolveFloatingActionMenuVisibility } from '../../lib/interaction/overlay-policy'
|
|
53
53
|
import { curveReshapeScope, holeEditScope } from '../../lib/interaction/scope'
|
|
54
|
+
import { duplicateWithoutMove, registryMoveDisabled } from '../../lib/node-action-movement'
|
|
54
55
|
import { playBlockedQuickActionFeedback } from '../../lib/quick-action-feedback'
|
|
55
56
|
import { collectQuickActionNodeScope } from '../../lib/quick-action-nodes'
|
|
56
57
|
import { duplicateRoofSubtree } from '../../lib/roof-duplication'
|
|
@@ -501,6 +502,16 @@ export function FloatingActionMenu() {
|
|
|
501
502
|
if (!node?.parentId) return
|
|
502
503
|
sfxEmitter.emit('sfx:item-pick')
|
|
503
504
|
|
|
505
|
+
if (registryMoveDisabled(node)) {
|
|
506
|
+
try {
|
|
507
|
+
const id = duplicateWithoutMove(node)
|
|
508
|
+
if (id) setSelection({ selectedIds: [id] })
|
|
509
|
+
} catch (error) {
|
|
510
|
+
console.error('Failed to duplicate node', error)
|
|
511
|
+
}
|
|
512
|
+
return
|
|
513
|
+
}
|
|
514
|
+
|
|
504
515
|
if (node.type === 'roof') {
|
|
505
516
|
try {
|
|
506
517
|
duplicateRoofSubtree(node.id as AnyNodeId, { mode: 'move' })
|
|
@@ -805,7 +816,9 @@ export function FloatingActionMenu() {
|
|
|
805
816
|
// `capabilities.movable`, a `floorplanMoveTarget`, or a
|
|
806
817
|
// 3D `affordanceTools.move` mover gets the Move button.
|
|
807
818
|
// Adding a new movable kind never touches this file.
|
|
808
|
-
node && isRegistryMovable(node.type)
|
|
819
|
+
node && isRegistryMovable(node.type) && !registryMoveDisabled(node)
|
|
820
|
+
? handleMove
|
|
821
|
+
: undefined
|
|
809
822
|
}
|
|
810
823
|
onDelete={handleDelete}
|
|
811
824
|
onDuplicate={
|