@pascal-app/editor 1.0.0-beta.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/bunfig.toml +4 -0
- package/package.json +15 -8
- package/scripts/generate-print-golden-house.ts +89 -0
- package/src/components/editor/alignment-3d-guide-layer.tsx +12 -3
- package/src/components/editor/bake-exporter.tsx +1 -0
- package/src/components/editor/camera-dragging-lifecycle.test.ts +41 -0
- package/src/components/editor/camera-dragging-lifecycle.ts +10 -2
- package/src/components/editor/capture-camera-rig.tsx +59 -0
- package/src/components/editor/custom-camera-controls.tsx +20 -11
- package/src/components/editor/editor-layout-mobile.tsx +6 -6
- package/src/components/editor/editor-layout-v2.tsx +17 -5
- package/src/components/editor/elevation-3d-guide-layer.tsx +3 -2
- package/src/components/editor/export-manager.tsx +175 -33
- package/src/components/editor/first-person/build-collider-world.test.ts +30 -0
- package/src/components/editor/first-person-controls.tsx +182 -57
- package/src/components/editor/floating-action-menu.tsx +55 -55
- package/src/components/editor/floorplan-background-selection.test.ts +31 -4
- package/src/components/editor/floorplan-background-selection.ts +27 -7
- package/src/components/editor/floorplan-panel.tsx +81 -57
- package/src/components/editor/grid.tsx +12 -7
- package/src/components/editor/group-actions.ts +34 -50
- package/src/components/editor/group-floating-action-menu.tsx +30 -15
- package/src/components/editor/group-move-3d.ts +51 -21
- package/src/components/editor/group-transform-shared.test.ts +78 -0
- package/src/components/editor/group-transform-shared.ts +74 -7
- package/src/components/editor/handles/handle-arrow-raycast.test.ts +86 -0
- package/src/components/editor/handles/handle-arrow.tsx +109 -48
- package/src/components/editor/handles/linear-resize-drag.ts +67 -0
- package/src/components/editor/handles/resize-snap.test.ts +46 -0
- package/src/components/editor/handles/resize-snap.ts +10 -1
- package/src/components/editor/handles/use-handle-drag.ts +18 -1
- package/src/components/editor/index.tsx +252 -46
- package/src/components/editor/measurement-pill.tsx +12 -12
- package/src/components/editor/node-action-menu.tsx +42 -1
- package/src/components/editor/node-arrow-handles.tsx +47 -58
- package/src/components/editor/opening-guides-3d-layer.tsx +13 -4
- package/src/components/editor/selection-manager.tsx +132 -47
- package/src/components/editor/site-edge-labels.tsx +2 -1
- package/src/components/editor/snapshot-capture-overlay.tsx +309 -41
- package/src/components/editor/snapshot-capture.test.ts +422 -0
- package/src/components/editor/snapshot-capture.ts +145 -0
- package/src/components/editor/thumbnail-generator.tsx +295 -232
- package/src/components/editor/use-floorplan-background-placement.ts +53 -5
- package/src/components/editor/wall-measurement-label.tsx +3 -2
- package/src/components/editor/wall-move-side-handles.tsx +299 -7
- package/src/components/editor-2d/floorplan-group-action-menu.tsx +23 -16
- package/src/components/editor-2d/floorplan-group-move.tsx +67 -33
- package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +3 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +129 -38
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +14 -1
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +16 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +2 -0
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +52 -3
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +196 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +204 -114
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +402 -0
- package/src/components/systems/ceiling/ceiling-bracket-batch.ts +371 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +347 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +281 -305
- package/src/components/systems/roof/roof-edit-system.test.tsx +120 -0
- package/src/components/systems/roof/roof-edit-system.tsx +10 -19
- package/src/components/systems/selection-affordance-manager.tsx +59 -9
- package/src/components/systems/selection-affordance-services.ts +19 -0
- package/src/components/systems/stair/stair-edit-system.tsx +5 -2
- package/src/components/tools/fence/fence-drafting.test.ts +100 -0
- package/src/components/tools/fence/fence-drafting.ts +6 -25
- package/src/components/tools/item/draft-creation.test.ts +12 -0
- package/src/components/tools/item/draft-creation.ts +10 -0
- package/src/components/tools/item/face-host-commit.test.ts +65 -0
- package/src/components/tools/item/face-host-commit.ts +56 -0
- package/src/components/tools/item/face-host-preview.test.ts +77 -0
- package/src/components/tools/item/face-host-preview.ts +82 -0
- package/src/components/tools/item/move-tool.tsx +4 -3
- package/src/components/tools/item/placement-math.test.ts +11 -1
- package/src/components/tools/item/placement-math.ts +0 -20
- package/src/components/tools/item/placement-strategies.test.ts +494 -0
- package/src/components/tools/item/placement-strategies.ts +192 -24
- package/src/components/tools/item/placement-surface.test.ts +61 -0
- package/src/components/tools/item/placement-surface.ts +24 -0
- package/src/components/tools/item/placement-types.ts +6 -0
- package/src/components/tools/item/test-face-host.ts +121 -0
- package/src/components/tools/item/use-draft-node.test.tsx +336 -0
- package/src/components/tools/item/use-draft-node.ts +26 -4
- package/src/components/tools/item/use-placement-coordinator.tsx +447 -180
- package/src/components/tools/registered-tool-install-lifecycle.test.tsx +256 -0
- package/src/components/tools/registry/move-registry-node-tool.test.ts +10 -0
- package/src/components/tools/registry/move-registry-node-tool.tsx +306 -110
- package/src/components/tools/registry-tool-context.tsx +30 -0
- package/src/components/tools/select/box-select-tool.tsx +3 -1
- package/src/components/tools/select/marquee-footprint.test.ts +53 -0
- package/src/components/tools/select/marquee-footprint.ts +29 -0
- package/src/components/tools/shared/placement-box.tsx +63 -8
- package/src/components/tools/shared/placement-dimension-guides.tsx +121 -0
- package/src/components/tools/shared/pointer-support-cap.test.ts +319 -17
- package/src/components/tools/shared/pointer-support-cap.ts +45 -8
- package/src/components/tools/site/terrain-brush-cursor.tsx +2 -1
- package/src/components/tools/site/terrain-sculpt-grid.tsx +9 -5
- package/src/components/tools/site/terrain-sculpt-tool.tsx +1 -1
- package/src/components/tools/stair/stair-tool.tsx +127 -74
- package/src/components/tools/tool-manager.tsx +25 -8
- package/src/components/tools/wall/wall-drafting.test.ts +527 -4
- package/src/components/tools/wall/wall-drafting.ts +55 -462
- package/src/components/ui/action-menu/control-modes.tsx +9 -6
- package/src/components/ui/action-menu/index.tsx +11 -1
- package/src/components/ui/command-palette/editor-commands.tsx +12 -13
- package/src/components/ui/command-palette/index.tsx +7 -2
- package/src/components/ui/controls/material-paint-panel.tsx +6 -4
- package/src/components/ui/controls/material-picker.tsx +4 -4
- package/src/components/ui/controls/scene-material-list.tsx +6 -5
- package/src/components/ui/controls/segmented-control.tsx +8 -1
- package/src/components/ui/controls/slider-control.tsx +19 -3
- package/src/components/ui/controls/toggle-control.tsx +18 -5
- package/src/components/ui/controls/tool-options-panel.tsx +126 -0
- package/src/components/ui/floating-level-selector.tsx +23 -6
- package/src/components/ui/helpers/contextual-helper-panel.tsx +9 -0
- package/src/components/ui/helpers/helper-manager.tsx +47 -11
- package/src/components/ui/helpers/item-helper.tsx +1 -1
- package/src/components/ui/helpers/registered-tool-helper.tsx +21 -0
- package/src/components/ui/panels/homogeneous-selection.test.ts +80 -0
- package/src/components/ui/panels/homogeneous-selection.ts +47 -0
- package/src/components/ui/panels/mobile-selection-bar.tsx +11 -5
- package/src/components/ui/panels/multi-field-value.test.ts +209 -0
- package/src/components/ui/panels/multi-field-value.ts +163 -0
- package/src/components/ui/panels/multi-height-mode.tsx +185 -0
- package/src/components/ui/panels/multi-parametric-inspector.tsx +263 -0
- package/src/components/ui/panels/multi-selection-panel.tsx +78 -26
- package/src/components/ui/panels/node-display.ts +4 -0
- package/src/components/ui/panels/panel-manager.tsx +77 -3
- package/src/components/ui/panels/panel-wrapper.tsx +228 -12
- package/src/components/ui/panels/parametric-field-control.tsx +173 -0
- package/src/components/ui/panels/parametric-field-utils.ts +18 -0
- package/src/components/ui/panels/parametric-inspector.tsx +29 -162
- package/src/components/ui/panels/reference-panel.tsx +31 -1
- package/src/components/ui/primitives/shortcut-token.tsx +23 -1
- package/src/components/ui/scene-loader.tsx +35 -0
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +7 -2
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +390 -36
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +31 -23
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.test.ts +140 -0
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +109 -0
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +19 -5
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +128 -46
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +5 -2
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +28 -11
- package/src/components/ui/sidebar/panels/site-panel/tree-node.test.ts +8 -0
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +22 -7
- package/src/components/ui/sidebar/tab-bar.tsx +9 -1
- package/src/components/ui/sidebar/use-plugin-panels.tsx +17 -2
- package/src/components/viewer/floorplan-compass-button.tsx +50 -0
- package/src/components/viewer/floorplan-preview-geometry.test.ts +107 -0
- package/src/components/viewer/floorplan-preview-geometry.ts +281 -0
- package/src/components/viewer/floorplan-preview-navigation.test.ts +43 -0
- package/src/components/viewer/floorplan-preview-navigation.ts +62 -0
- package/src/components/viewer/floorplan-preview.tsx +1069 -0
- package/src/components/viewer/use-viewer-camera-navigation-sync.ts +140 -0
- package/src/components/viewer/viewer-scene-header.tsx +2 -1
- package/src/components/viewer/viewer-stage-modes.test.ts +38 -0
- package/src/components/viewer/viewer-stage-modes.ts +49 -0
- package/src/components/viewer/viewer-stage-switcher.tsx +95 -0
- package/src/components/viewer/viewer-stage.test.tsx +47 -0
- package/src/components/viewer/viewer-stage.tsx +221 -0
- package/src/components/viewer-overlay.tsx +13 -30
- package/src/components/viewer-zone-system.tsx +6 -2
- package/src/components/walkthrough-hud.tsx +19 -10
- package/src/hooks/use-auto-save.test.ts +122 -1
- package/src/hooks/use-auto-save.ts +119 -13
- package/src/hooks/use-ceiling-events.test.ts +122 -0
- package/src/hooks/use-drag-action.ts +17 -3
- package/src/hooks/use-grid-events.test.ts +240 -0
- package/src/hooks/use-grid-events.ts +190 -11
- package/src/hooks/use-keyboard.test.ts +244 -0
- package/src/hooks/use-keyboard.ts +164 -63
- package/src/hooks/use-registered-tool-enabled.ts +20 -0
- package/src/hooks/use-save-shortcut.ts +33 -0
- package/src/index.tsx +109 -4
- package/src/lib/active-placement-surface.test.ts +16 -0
- package/src/lib/active-placement-surface.ts +14 -0
- package/src/lib/ceiling-surface-raycast.test.ts +64 -0
- package/src/lib/ceiling-surface-raycast.ts +12 -0
- package/src/lib/contextual-help-extension.ts +17 -0
- package/src/lib/contextual-help.test.ts +8 -0
- package/src/lib/contextual-help.ts +8 -0
- package/src/lib/continuation.test.ts +11 -0
- package/src/lib/continuation.ts +8 -1
- package/src/lib/direct-manipulation.test.ts +132 -0
- package/src/lib/direct-manipulation.ts +38 -1
- package/src/lib/editor-api.ts +6 -3
- package/src/lib/elevation-guides.test.ts +28 -1
- package/src/lib/elevation-guides.ts +22 -7
- package/src/lib/floating-menu-scale.test.ts +29 -0
- package/src/lib/floating-menu-scale.ts +14 -0
- package/src/lib/floorplan/apply-alignment.test.ts +47 -2
- package/src/lib/floorplan/apply-alignment.ts +10 -1
- package/src/lib/floorplan/floorplan-export.test.ts +397 -1
- package/src/lib/floorplan/floorplan-export.tsx +111 -16
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +26 -0
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +32 -24
- package/src/lib/floorplan/floorplan-readonly.test.ts +47 -0
- package/src/lib/floorplan/floorplan-readonly.ts +85 -0
- package/src/lib/floorplan-grid-event-point.test.ts +29 -0
- package/src/lib/floorplan-grid-event-point.ts +13 -0
- package/src/lib/glb-export.test.ts +1169 -21
- package/src/lib/glb-export.ts +629 -101
- package/src/lib/grid-event-presentation.ts +16 -0
- package/src/lib/history.test.ts +523 -77
- package/src/lib/history.ts +133 -14
- package/src/lib/host-tree-children.test.ts +27 -0
- package/src/lib/host-tree-children.ts +79 -0
- package/src/lib/inspector-card-mode.test.ts +92 -0
- package/src/lib/inspector-card-mode.ts +60 -0
- package/src/lib/interaction/hot-set.test.ts +10 -1
- package/src/lib/interaction/hot-set.ts +3 -1
- package/src/lib/interaction/overlay-policy.test.ts +33 -1
- package/src/lib/interaction/overlay-policy.ts +16 -0
- package/src/lib/interaction/registered-drafting.ts +36 -0
- package/src/lib/interaction/scope.ts +31 -0
- package/src/lib/level-print-export.test.ts +484 -0
- package/src/lib/level-print-export.ts +526 -0
- package/src/lib/local-project-presentation-persistence.test.ts +302 -0
- package/src/lib/local-project-presentation-persistence.ts +335 -0
- package/src/lib/material-paint.ts +2 -0
- package/src/lib/measurement-parser.ts +3 -0
- package/src/lib/measurements.test.ts +4 -0
- package/src/lib/model-export.ts +29 -0
- package/src/lib/paint-preview-owner.ts +71 -0
- package/src/lib/paint-scope.test.ts +58 -2
- package/src/lib/paint-scope.ts +8 -2
- package/src/lib/planar-cursor-placement.test.ts +178 -1
- package/src/lib/planar-cursor-placement.ts +61 -5
- package/src/lib/plugin-panels.test.ts +92 -1
- package/src/lib/plugin-panels.ts +48 -0
- package/src/lib/portable-export.test.ts +366 -0
- package/src/lib/portable-export.ts +969 -0
- package/src/lib/print-3mf.test.ts +86 -0
- package/src/lib/print-3mf.ts +161 -0
- package/src/lib/print-content-scope.test.ts +107 -0
- package/src/lib/print-content-scope.ts +90 -0
- package/src/lib/print-export.test.ts +294 -0
- package/src/lib/print-export.ts +735 -0
- package/src/lib/print-feature-thickness.test.ts +147 -0
- package/src/lib/print-feature-thickness.ts +180 -0
- package/src/lib/print-golden-house.test-fixture.ts +352 -0
- package/src/lib/print-golden-house.test.ts +249 -0
- package/src/lib/print-roof-solids.test.ts +133 -0
- package/src/lib/print-roof-solids.ts +630 -0
- package/src/lib/print-shell-compiler-baseline.test.ts +419 -0
- package/src/lib/print-shell-compiler-baseline.ts +225 -0
- package/src/lib/print-shell-compiler-manifold-core.ts +330 -0
- package/src/lib/print-shell-compiler-manifold-worker.ts +187 -0
- package/src/lib/print-shell-compiler-manifold.worker.ts +20 -0
- package/src/lib/print-shell-compiler-mesh-data.ts +32 -0
- package/src/lib/print-shell-compiler-protocol.ts +30 -0
- package/src/lib/print-shell-compiler.ts +273 -0
- package/src/lib/print-wall-solids.test.ts +194 -0
- package/src/lib/print-wall-solids.ts +328 -0
- package/src/lib/rigid-plan-svg-transform.test.ts +35 -0
- package/src/lib/rigid-plan-svg-transform.ts +24 -0
- package/src/lib/scene.ts +22 -8
- package/src/lib/selection-routing.test.ts +125 -6
- package/src/lib/selection-routing.ts +34 -1
- package/src/lib/session-groups.test.ts +88 -0
- package/src/lib/session-groups.ts +201 -0
- package/src/lib/sfx-bus.ts +4 -0
- package/src/lib/sfx-player.ts +8 -0
- package/src/lib/snapping-mode.test.ts +32 -4
- package/src/lib/snapping-mode.ts +11 -7
- package/src/lib/structured-clone-fallback.test.ts +45 -0
- package/src/lib/structured-clone-fallback.ts +24 -0
- package/src/lib/usdz-export.ts +38 -0
- package/src/lib/walkthrough-pointer-lock.ts +47 -0
- package/src/store/live-draft-preview-stores.test.ts +3 -0
- package/src/store/terrain-sculpt-mode.test.ts +3 -6
- package/src/store/tool-mode.test.ts +87 -0
- package/src/store/use-camera-hint-focus.ts +48 -0
- package/src/store/use-editor.tsx +299 -188
- package/src/store/use-floorplan-draft-preview.ts +9 -0
- package/src/store/use-interaction-scope.test.ts +23 -1
- package/src/store/use-interaction-scope.ts +63 -2
- package/src/store/use-placement-preview.ts +56 -3
- package/src/store/use-session-groups.ts +103 -0
- package/src/components/tools/roof/roof-tool.tsx +0 -656
- package/src/components/ui/helpers/roof-helper.tsx +0 -14
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AnyNode,
|
|
3
3
|
collectAlignmentAnchors,
|
|
4
|
+
createDefaultStairSegment,
|
|
4
5
|
createSurfaceOpeningPreviewController,
|
|
5
|
-
|
|
6
|
+
DEFAULT_LEVEL_HEIGHT,
|
|
6
7
|
emitter,
|
|
7
8
|
type GridEvent,
|
|
9
|
+
getFloorStackedPosition,
|
|
10
|
+
getLevelFloorToFloorHeight,
|
|
8
11
|
type LevelNode,
|
|
9
12
|
movingAlignmentAnchors,
|
|
10
13
|
type NodeEvent,
|
|
11
14
|
resolveAlignment,
|
|
15
|
+
resolveFrozenFloorPlacementPatch,
|
|
12
16
|
resolveSupportSlabPatch,
|
|
13
17
|
StairNode,
|
|
14
|
-
StairSegmentNode,
|
|
18
|
+
type StairSegmentNode,
|
|
15
19
|
syncAutoStairOpenings,
|
|
16
20
|
useScene,
|
|
17
21
|
} from '@pascal-app/core'
|
|
@@ -36,7 +40,10 @@ import useFacingPose from '../../../store/use-facing-pose'
|
|
|
36
40
|
import { useStairBuildPreview } from '../../../store/use-stair-build-preview'
|
|
37
41
|
import { CursorSphere } from '../shared/cursor-sphere'
|
|
38
42
|
import { getFloorStackPreviewPosition } from '../shared/floor-stack-preview'
|
|
39
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
type PointerSupportSurface,
|
|
45
|
+
resolvePointerSupportSurface,
|
|
46
|
+
} from '../shared/pointer-support-cap'
|
|
40
47
|
import { createStairCommitGate, swallowFollowUpBrowserClick } from './stair-click-guard'
|
|
41
48
|
import {
|
|
42
49
|
DEFAULT_CURVED_STAIR_INNER_RADIUS,
|
|
@@ -47,7 +54,6 @@ import {
|
|
|
47
54
|
DEFAULT_SPIRAL_TOP_LANDING_MODE,
|
|
48
55
|
DEFAULT_STAIR_ATTACHMENT_SIDE,
|
|
49
56
|
DEFAULT_STAIR_FILL_TO_FLOOR,
|
|
50
|
-
DEFAULT_STAIR_HEIGHT,
|
|
51
57
|
DEFAULT_STAIR_LENGTH,
|
|
52
58
|
DEFAULT_STAIR_OPENING_OFFSET,
|
|
53
59
|
DEFAULT_STAIR_RAILING_HEIGHT,
|
|
@@ -64,26 +70,12 @@ const ALIGNMENT_THRESHOLD_M = 0.08
|
|
|
64
70
|
type ClickTriggerEvent = GridEvent | NodeEvent<AnyNode>
|
|
65
71
|
type MoveTriggerEvent = GridEvent | NodeEvent<AnyNode>
|
|
66
72
|
|
|
67
|
-
const CLICK_TRIGGER_KINDS = [
|
|
68
|
-
'shelf',
|
|
69
|
-
'item',
|
|
70
|
-
'slab',
|
|
71
|
-
'ceiling',
|
|
72
|
-
'wall',
|
|
73
|
-
'fence',
|
|
74
|
-
'column',
|
|
75
|
-
'roof',
|
|
76
|
-
'roof-segment',
|
|
77
|
-
'stair',
|
|
78
|
-
'stair-segment',
|
|
79
|
-
] as const
|
|
80
|
-
|
|
81
73
|
/**
|
|
82
74
|
* Generates the step-profile geometry for the ghost preview.
|
|
83
75
|
* Same algorithm as StairSystem's generateStairSegmentGeometry.
|
|
84
76
|
*/
|
|
85
|
-
function createStairPreviewGeometry(): THREE.BufferGeometry {
|
|
86
|
-
const riserHeight =
|
|
77
|
+
function createStairPreviewGeometry(rise: number): THREE.BufferGeometry {
|
|
78
|
+
const riserHeight = rise / DEFAULT_STAIR_STEP_COUNT
|
|
87
79
|
const treadDepth = DEFAULT_STAIR_LENGTH / DEFAULT_STAIR_STEP_COUNT
|
|
88
80
|
|
|
89
81
|
const shape = new THREE.Shape()
|
|
@@ -114,19 +106,40 @@ function createStairPreviewGeometry(): THREE.BufferGeometry {
|
|
|
114
106
|
}
|
|
115
107
|
|
|
116
108
|
/**
|
|
117
|
-
* Creates a default straight stair segment
|
|
109
|
+
* Creates a default straight stair segment climbing `rise` — the storey it is
|
|
110
|
+
* dropped on, not a constant: the placed stair has no explicit `totalRise`, so
|
|
111
|
+
* this is the height `syncStairRises` immediately converges it to anyway.
|
|
118
112
|
*/
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
113
|
+
function resolvePlacedStairRise(
|
|
114
|
+
nodes: Record<string, AnyNode>,
|
|
115
|
+
levelId: LevelNode['id'],
|
|
116
|
+
stair: StairNode,
|
|
117
|
+
supportSurface: PointerSupportSurface | null,
|
|
118
|
+
): number {
|
|
119
|
+
// Same contract as `resolveStairTotalRise` for a stair that is not in the
|
|
120
|
+
// scene yet: the storey height minus whatever slab lifts the drop point,
|
|
121
|
+
// capped by the surface the pointer actually aims at (a floor under an
|
|
122
|
+
// overlapping deck must not elect the deck).
|
|
123
|
+
const base = getFloorStackedPosition({
|
|
124
|
+
node: stair,
|
|
125
|
+
nodes,
|
|
126
|
+
position: stair.position,
|
|
127
|
+
rotation: stair.rotation,
|
|
128
|
+
levelId,
|
|
129
|
+
maxElevation: supportSurface?.elevation ?? null,
|
|
130
|
+
})[1]
|
|
131
|
+
return getLevelFloorToFloorHeight(levelId, nodes) - base
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function createSeedStairSegment(rise: number) {
|
|
135
|
+
return createDefaultStairSegment({
|
|
122
136
|
width: DEFAULT_STAIR_WIDTH,
|
|
123
137
|
length: DEFAULT_STAIR_LENGTH,
|
|
124
|
-
height:
|
|
138
|
+
height: rise,
|
|
125
139
|
stepCount: DEFAULT_STAIR_STEP_COUNT,
|
|
126
140
|
attachmentSide: DEFAULT_STAIR_ATTACHMENT_SIDE,
|
|
127
141
|
fillToFloor: DEFAULT_STAIR_FILL_TO_FLOOR,
|
|
128
142
|
thickness: DEFAULT_STAIR_THICKNESS,
|
|
129
|
-
position: [0, 0, 0],
|
|
130
143
|
})
|
|
131
144
|
}
|
|
132
145
|
|
|
@@ -177,7 +190,7 @@ function commitStairPlacement(
|
|
|
177
190
|
levelId: LevelNode['id'],
|
|
178
191
|
position: [number, number, number],
|
|
179
192
|
rotation: number,
|
|
180
|
-
|
|
193
|
+
supportSurface: PointerSupportSurface | null,
|
|
181
194
|
): void {
|
|
182
195
|
const { createNodes, nodes } = useScene.getState()
|
|
183
196
|
const placementLevelId = resolveStairPlacementLevelId(
|
|
@@ -189,7 +202,7 @@ function commitStairPlacement(
|
|
|
189
202
|
|
|
190
203
|
const stairCount = Object.values(nodes).filter((n) => n.type === 'stair').length
|
|
191
204
|
const name = `Staircase ${stairCount + 1}`
|
|
192
|
-
const
|
|
205
|
+
const seed = createSeedStairSegment(getLevelFloorToFloorHeight(placementLevelId, nodes))
|
|
193
206
|
|
|
194
207
|
const destinationPlan = resolveStairDestinationLevel({
|
|
195
208
|
createMissing: true,
|
|
@@ -205,20 +218,35 @@ function commitStairPlacement(
|
|
|
205
218
|
nextLevelId,
|
|
206
219
|
position,
|
|
207
220
|
rotation,
|
|
208
|
-
segmentId:
|
|
221
|
+
segmentId: seed.id,
|
|
209
222
|
}),
|
|
210
223
|
parentId: placementLevelId,
|
|
211
224
|
})
|
|
225
|
+
const segment = {
|
|
226
|
+
...seed,
|
|
227
|
+
height: resolvePlacedStairRise(nodes, placementLevelId, stair, supportSurface),
|
|
228
|
+
}
|
|
212
229
|
const prospectiveNodes = {
|
|
213
230
|
...nodes,
|
|
214
231
|
[stair.id]: stair,
|
|
215
232
|
[segment.id]: { ...segment, parentId: stair.id },
|
|
216
233
|
} as Record<string, AnyNode>
|
|
234
|
+
const placementPatch = supportSurface?.sourceNodeId
|
|
235
|
+
? resolveFrozenFloorPlacementPatch(stair, prospectiveNodes, {
|
|
236
|
+
position,
|
|
237
|
+
rotation,
|
|
238
|
+
elevation: supportSurface.elevation,
|
|
239
|
+
preferredSlabId: supportSurface.supportSlabId,
|
|
240
|
+
})
|
|
241
|
+
: {
|
|
242
|
+
position,
|
|
243
|
+
...resolveSupportSlabPatch(stair, prospectiveNodes, {
|
|
244
|
+
maxElevation: supportSurface?.elevation,
|
|
245
|
+
}),
|
|
246
|
+
}
|
|
217
247
|
const committedStair = StairNode.parse({
|
|
218
248
|
...stair,
|
|
219
|
-
...
|
|
220
|
-
maxElevation: supportElevationCap,
|
|
221
|
-
}),
|
|
249
|
+
...placementPatch,
|
|
222
250
|
})
|
|
223
251
|
|
|
224
252
|
const createdLevel = destinationPlan?.createdLevel
|
|
@@ -243,12 +271,18 @@ export const StairTool: React.FC = () => {
|
|
|
243
271
|
const cursorRef = useRef<THREE.Group>(null)
|
|
244
272
|
const previewRef = useRef<THREE.Group>(null)
|
|
245
273
|
const rotationRef = useRef(0)
|
|
246
|
-
const
|
|
274
|
+
const supportSurfaceRef = useRef<PointerSupportSurface | null>(null)
|
|
247
275
|
const previousGridPosRef = useRef<[number, number] | null>(null)
|
|
248
276
|
const lastCanonicalPositionRef = useRef<[number, number, number] | null>(null)
|
|
249
277
|
const currentLevelId = useViewer((state) => state.selection.levelId)
|
|
250
278
|
|
|
251
|
-
const
|
|
279
|
+
const previewRise = useScene((state) =>
|
|
280
|
+
currentLevelId ? getLevelFloorToFloorHeight(currentLevelId, state.nodes) : DEFAULT_LEVEL_HEIGHT,
|
|
281
|
+
)
|
|
282
|
+
const previewRiseRef = useRef(previewRise)
|
|
283
|
+
previewRiseRef.current = previewRise
|
|
284
|
+
const previewGeometry = useMemo(() => createStairPreviewGeometry(previewRise), [previewRise])
|
|
285
|
+
useEffect(() => () => previewGeometry.dispose(), [previewGeometry])
|
|
252
286
|
|
|
253
287
|
useEffect(() => {
|
|
254
288
|
if (!currentLevelId) return
|
|
@@ -261,11 +295,18 @@ export const StairTool: React.FC = () => {
|
|
|
261
295
|
// Reset rotation when tool activates
|
|
262
296
|
rotationRef.current = 0
|
|
263
297
|
useStairBuildPreview.getState().reset()
|
|
264
|
-
if (previewRef.current)
|
|
298
|
+
if (previewRef.current) {
|
|
299
|
+
previewRef.current.rotation.y = 0
|
|
300
|
+
previewRef.current.scale.y = 1
|
|
301
|
+
}
|
|
265
302
|
lastCanonicalPositionRef.current = null
|
|
266
|
-
|
|
303
|
+
supportSurfaceRef.current = null
|
|
267
304
|
|
|
268
|
-
const buildPreviewScene = (
|
|
305
|
+
const buildPreviewScene = (
|
|
306
|
+
position: [number, number, number],
|
|
307
|
+
rotation: number,
|
|
308
|
+
supportSurface: PointerSupportSurface | null,
|
|
309
|
+
) => {
|
|
269
310
|
const nodes = useScene.getState().nodes
|
|
270
311
|
const placementLevelId = resolveStairPlacementLevelId(
|
|
271
312
|
nodes,
|
|
@@ -280,15 +321,19 @@ export const StairTool: React.FC = () => {
|
|
|
280
321
|
nodes,
|
|
281
322
|
})
|
|
282
323
|
const nextLevelId = destinationPlan?.toLevel.id ?? placementLevelId
|
|
283
|
-
const
|
|
324
|
+
const seed = createSeedStairSegment(getLevelFloorToFloorHeight(placementLevelId, nodes))
|
|
284
325
|
const stair = createDefaultStairNode({
|
|
285
326
|
name: 'Staircase Preview',
|
|
286
327
|
levelId: placementLevelId,
|
|
287
328
|
nextLevelId,
|
|
288
329
|
position,
|
|
289
330
|
rotation,
|
|
290
|
-
segmentId:
|
|
331
|
+
segmentId: seed.id,
|
|
291
332
|
})
|
|
333
|
+
const segment = {
|
|
334
|
+
...seed,
|
|
335
|
+
height: resolvePlacedStairRise(nodes, placementLevelId, stair, supportSurface),
|
|
336
|
+
}
|
|
292
337
|
const previewNodes = {
|
|
293
338
|
...nodes,
|
|
294
339
|
...(destinationPlan?.createdLevel
|
|
@@ -298,7 +343,7 @@ export const StairTool: React.FC = () => {
|
|
|
298
343
|
[segment.id]: { ...segment, parentId: stair.id },
|
|
299
344
|
} as Record<string, AnyNode>
|
|
300
345
|
|
|
301
|
-
return { placementLevelId, previewNodes, stair }
|
|
346
|
+
return { placementLevelId, previewNodes, stair, rise: segment.height }
|
|
302
347
|
}
|
|
303
348
|
|
|
304
349
|
// The preview rebuild (full-scene copy + destination-level resolution +
|
|
@@ -313,23 +358,37 @@ export const StairTool: React.FC = () => {
|
|
|
313
358
|
const applyDraftPreview = (
|
|
314
359
|
position: [number, number, number],
|
|
315
360
|
rotation: number,
|
|
316
|
-
|
|
361
|
+
supportSurface: PointerSupportSurface | null,
|
|
317
362
|
) => {
|
|
318
|
-
const key = `${position[0].toFixed(3)},${position[2].toFixed(3)},${rotation.toFixed(4)},${
|
|
363
|
+
const key = `${position[0].toFixed(3)},${position[2].toFixed(3)},${rotation.toFixed(4)},${supportSurface?.elevation.toFixed(3) ?? 'none'},${supportSurface?.sourceNodeId ?? 'floor'}`
|
|
319
364
|
if (key === lastPreviewKey) return
|
|
320
365
|
lastPreviewKey = key
|
|
321
366
|
useStairBuildPreview.getState().setPreview([position[0], position[2]], rotation)
|
|
322
|
-
const preview = buildPreviewScene(position, rotation)
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
367
|
+
const preview = buildPreviewScene(position, rotation, supportSurface)
|
|
368
|
+
const frozenPatch =
|
|
369
|
+
preview && supportSurface?.sourceNodeId
|
|
370
|
+
? resolveFrozenFloorPlacementPatch(preview.stair, preview.previewNodes, {
|
|
371
|
+
position,
|
|
372
|
+
rotation,
|
|
373
|
+
elevation: supportSurface.elevation,
|
|
374
|
+
preferredSlabId: supportSurface.supportSlabId,
|
|
375
|
+
})
|
|
376
|
+
: null
|
|
377
|
+
const previewPosition = frozenPatch?.position ?? position
|
|
378
|
+
const previewStair = frozenPatch
|
|
379
|
+
? ({ ...preview?.stair, ...frozenPatch } as AnyNode)
|
|
380
|
+
: preview?.stair
|
|
381
|
+
const visualPosition =
|
|
382
|
+
preview && previewStair
|
|
383
|
+
? getFloorStackPreviewPosition({
|
|
384
|
+
node: previewStair,
|
|
385
|
+
position: previewPosition,
|
|
386
|
+
rotation,
|
|
387
|
+
levelId: preview.placementLevelId,
|
|
388
|
+
nodes: preview.previewNodes,
|
|
389
|
+
maxElevation: supportSurface?.sourceNodeId ? null : supportSurface?.elevation,
|
|
390
|
+
})
|
|
391
|
+
: previewPosition
|
|
333
392
|
if (cursorRef.current) {
|
|
334
393
|
cursorRef.current.position.set(
|
|
335
394
|
visualPosition[0],
|
|
@@ -341,6 +400,9 @@ export const StairTool: React.FC = () => {
|
|
|
341
400
|
if (previewRef.current) {
|
|
342
401
|
previewRef.current.position.set(...visualPosition)
|
|
343
402
|
previewRef.current.rotation.y = rotation
|
|
403
|
+
// The ghost geometry is built for the storey height; squash it to the
|
|
404
|
+
// rise the placed flight will get on this surface.
|
|
405
|
+
previewRef.current.scale.y = preview ? preview.rise / previewRiseRef.current : 1
|
|
344
406
|
}
|
|
345
407
|
|
|
346
408
|
// Forward-facing triangle (editor-side overlay). The run ascends along
|
|
@@ -374,7 +436,7 @@ export const StairTool: React.FC = () => {
|
|
|
374
436
|
z: number,
|
|
375
437
|
rotation: number,
|
|
376
438
|
): ReturnType<typeof resolveAlignment> | null => {
|
|
377
|
-
const preview = buildPreviewScene([x, 0, z], rotation)
|
|
439
|
+
const preview = buildPreviewScene([x, 0, z], rotation, supportSurfaceRef.current)
|
|
378
440
|
const moving = preview
|
|
379
441
|
? movingAlignmentAnchors(preview.stair, preview.previewNodes, x, z, rotation)
|
|
380
442
|
: []
|
|
@@ -424,8 +486,10 @@ export const StairTool: React.FC = () => {
|
|
|
424
486
|
}
|
|
425
487
|
|
|
426
488
|
const resolveStairPosition = (event: MoveTriggerEvent): [number, number, number] | null => {
|
|
427
|
-
const pointed = resolvePointerSupportSurface(cameraRef.current, event.position
|
|
428
|
-
|
|
489
|
+
const pointed = resolvePointerSupportSurface(cameraRef.current, event.position, {
|
|
490
|
+
includeNodeTopSurfaces: true,
|
|
491
|
+
})
|
|
492
|
+
supportSurfaceRef.current = pointed
|
|
429
493
|
const fallbackPosition =
|
|
430
494
|
'node' in event ? lastCanonicalPositionRef.current : event.localPosition
|
|
431
495
|
if (!pointed?.localPoint && !fallbackPosition) return null
|
|
@@ -450,7 +514,7 @@ export const StairTool: React.FC = () => {
|
|
|
450
514
|
if (!position) return
|
|
451
515
|
const [gridX, , gridZ] = position
|
|
452
516
|
lastCanonicalPositionRef.current = position
|
|
453
|
-
applyDraftPreview(position, rotationRef.current,
|
|
517
|
+
applyDraftPreview(position, rotationRef.current, supportSurfaceRef.current)
|
|
454
518
|
|
|
455
519
|
if (
|
|
456
520
|
(isGridSnapActive() || isMagneticSnapActive()) &&
|
|
@@ -484,7 +548,7 @@ export const StairTool: React.FC = () => {
|
|
|
484
548
|
const position = resolveStairPosition(event)
|
|
485
549
|
if (!position) return
|
|
486
550
|
|
|
487
|
-
commitStairPlacement(currentLevelId, position, rotationRef.current,
|
|
551
|
+
commitStairPlacement(currentLevelId, position, rotationRef.current, supportSurfaceRef.current)
|
|
488
552
|
openingPreview.clear()
|
|
489
553
|
// Commit cleared the opening preview, so force the next hover (even on the
|
|
490
554
|
// same cell) to rebuild rather than dedupe against the just-placed key.
|
|
@@ -526,7 +590,7 @@ export const StairTool: React.FC = () => {
|
|
|
526
590
|
applyDraftPreview(
|
|
527
591
|
lastCanonicalPositionRef.current,
|
|
528
592
|
rotationRef.current,
|
|
529
|
-
|
|
593
|
+
supportSurfaceRef.current,
|
|
530
594
|
)
|
|
531
595
|
} else if (previewRef.current) {
|
|
532
596
|
previewRef.current.rotation.y = rotationRef.current
|
|
@@ -536,26 +600,15 @@ export const StairTool: React.FC = () => {
|
|
|
536
600
|
|
|
537
601
|
emitter.on('grid:move', onPointerMove)
|
|
538
602
|
emitter.on('grid:click', commitAtCursor)
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
type MoveKey = SuffixedKey<(typeof CLICK_TRIGGER_KINDS)[number]>
|
|
542
|
-
for (const kind of CLICK_TRIGGER_KINDS) {
|
|
543
|
-
const key = `${kind}:click` as ClickKey
|
|
544
|
-
emitter.on(key, commitAtCursor as never)
|
|
545
|
-
const moveKey = `${kind}:move` as MoveKey
|
|
546
|
-
emitter.on(moveKey, onPointerMove as never)
|
|
547
|
-
}
|
|
603
|
+
emitter.on('node:click', commitAtCursor)
|
|
604
|
+
emitter.on('node:move', onPointerMove)
|
|
548
605
|
window.addEventListener('keydown', onKeyDown)
|
|
549
606
|
|
|
550
607
|
return () => {
|
|
551
608
|
emitter.off('grid:move', onPointerMove)
|
|
552
609
|
emitter.off('grid:click', commitAtCursor)
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
emitter.off(key, commitAtCursor as never)
|
|
556
|
-
const moveKey = `${kind}:move` as MoveKey
|
|
557
|
-
emitter.off(moveKey, onPointerMove as never)
|
|
558
|
-
}
|
|
610
|
+
emitter.off('node:click', commitAtCursor)
|
|
611
|
+
emitter.off('node:move', onPointerMove)
|
|
559
612
|
window.removeEventListener('keydown', onKeyDown)
|
|
560
613
|
useAlignmentGuides.getState().clear()
|
|
561
614
|
openingPreview.clear()
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type AnyNodeId,
|
|
4
4
|
type BuildingNode,
|
|
5
5
|
type CeilingNode,
|
|
6
|
+
createSceneApi,
|
|
6
7
|
type FenceNode,
|
|
7
8
|
nodeRegistry,
|
|
8
9
|
type SlabNode,
|
|
@@ -11,9 +12,10 @@ import {
|
|
|
11
12
|
} from '@pascal-app/core'
|
|
12
13
|
import { useViewer } from '@pascal-app/viewer'
|
|
13
14
|
import { type ComponentType, lazy, Suspense, useMemo } from 'react'
|
|
15
|
+
import { useRegisteredToolEnabled } from '../../hooks/use-registered-tool-enabled'
|
|
14
16
|
import { siteBoundaryHandlesEnabled } from '../../lib/site-boundary'
|
|
15
17
|
import useEditor, { type Phase, type Tool } from '../../store/use-editor'
|
|
16
|
-
import {
|
|
18
|
+
import useInteractionScope, {
|
|
17
19
|
useControlPointReshape,
|
|
18
20
|
useEditingHole,
|
|
19
21
|
useEndpointReshape,
|
|
@@ -30,7 +32,7 @@ import { OpeningGuides3DLayer } from '../editor/opening-guides-3d-layer'
|
|
|
30
32
|
import { WallSnapBeaconLayer } from '../editor/wall-snap-beacon-layer'
|
|
31
33
|
import { ElevatorTool } from './elevator/elevator-tool'
|
|
32
34
|
import { MoveTool } from './item/move-tool'
|
|
33
|
-
import {
|
|
35
|
+
import { RegistryToolProvider } from './registry-tool-context'
|
|
34
36
|
import { getRegistryAffordanceTool } from './shared/affordance-dispatch'
|
|
35
37
|
import { FacingPoseIndicator } from './shared/facing-pose-indicator'
|
|
36
38
|
import { SiteBoundaryEditor } from './site/site-boundary-editor'
|
|
@@ -91,7 +93,6 @@ const tools: Record<Phase, Partial<Record<Tool, React.FC>>> = {
|
|
|
91
93
|
'property-line': SiteBoundaryEditor,
|
|
92
94
|
},
|
|
93
95
|
structure: {
|
|
94
|
-
roof: RoofTool,
|
|
95
96
|
stair: StairTool,
|
|
96
97
|
zone: ZoneTool,
|
|
97
98
|
},
|
|
@@ -102,7 +103,11 @@ export const ToolManager: React.FC = () => {
|
|
|
102
103
|
const phase = useEditor((state) => state.phase)
|
|
103
104
|
const mode = useEditor((state) => state.mode)
|
|
104
105
|
const tool = useEditor((state) => state.tool)
|
|
106
|
+
const registeredToolEnabled = useRegisteredToolEnabled(tool)
|
|
105
107
|
const movingNode = useMovingNode()
|
|
108
|
+
const registryToolOwnsPlacement = useInteractionScope(
|
|
109
|
+
(state) => state.scope.kind === 'placing' && state.scope.driver === 'registry-tool',
|
|
110
|
+
)
|
|
106
111
|
const movingNodeOrigin = useEditor((state) => state.movingNodeOrigin)
|
|
107
112
|
const endpointReshape = useEndpointReshape()
|
|
108
113
|
const controlPointReshape = useControlPointReshape()
|
|
@@ -139,8 +144,20 @@ export const ToolManager: React.FC = () => {
|
|
|
139
144
|
const selectedIds = useViewer((state) => state.selection.selectedIds)
|
|
140
145
|
const buildingId = useViewer((state) => state.selection.buildingId)
|
|
141
146
|
const activeLevelId = useViewer((state) => state.selection.levelId)
|
|
147
|
+
const unit = useViewer((state) => state.unit)
|
|
142
148
|
const setSelection = useViewer((state) => state.setSelection)
|
|
143
149
|
const nodes = useScene((state) => state.nodes)
|
|
150
|
+
const registrySceneApi = useMemo(() => createSceneApi(useScene), [])
|
|
151
|
+
const registryToolContext = useMemo(
|
|
152
|
+
() => ({
|
|
153
|
+
activeLevelId: activeLevelId ?? null,
|
|
154
|
+
isCameraDragging: () => useViewer.getState().cameraDragging,
|
|
155
|
+
sceneApi: registrySceneApi,
|
|
156
|
+
selectNode: (nodeId: AnyNodeId) => setSelection({ selectedIds: [nodeId] }),
|
|
157
|
+
unit,
|
|
158
|
+
}),
|
|
159
|
+
[activeLevelId, registrySceneApi, setSelection, unit],
|
|
160
|
+
)
|
|
144
161
|
|
|
145
162
|
// Building transform for the local group — all building-relative tools live inside this group
|
|
146
163
|
// so their cursor positions and committed data are naturally in building-local space.
|
|
@@ -223,7 +240,7 @@ export const ToolManager: React.FC = () => {
|
|
|
223
240
|
!showCeilingBoundaryEditor
|
|
224
241
|
|
|
225
242
|
// Show build tools when in build mode
|
|
226
|
-
const showBuildTool = mode === 'build' && tool !== null
|
|
243
|
+
const showBuildTool = mode === 'build' && tool !== null && registeredToolEnabled
|
|
227
244
|
|
|
228
245
|
// A move initiated from the 2D floor-plan (orange move-dot) is owned end-to-
|
|
229
246
|
// end by `FloorplanRegistryMoveOverlay`, which marks the origin `'2d'` at
|
|
@@ -233,7 +250,7 @@ export const ToolManager: React.FC = () => {
|
|
|
233
250
|
// (the scene writes the overlay makes still mirror into the 3D view). A
|
|
234
251
|
// 3D-initiated move leaves the origin null until its own commit, so this only
|
|
235
252
|
// suppresses the 3D tool for genuinely 2D-owned moves.
|
|
236
|
-
const showMover = movingNode != null && movingNodeOrigin !== '2d'
|
|
253
|
+
const showMover = movingNode != null && movingNodeOrigin !== '2d' && !registryToolOwnsPlacement
|
|
237
254
|
|
|
238
255
|
// Registry-first: if the active tool's kind has a NodeDefinition with a
|
|
239
256
|
// tool contribution, the registry-driven tool takes over.
|
|
@@ -261,7 +278,7 @@ export const ToolManager: React.FC = () => {
|
|
|
261
278
|
}
|
|
262
279
|
|
|
263
280
|
return (
|
|
264
|
-
|
|
281
|
+
<RegistryToolProvider value={registryToolContext}>
|
|
265
282
|
{/* World-space tools: site boundary and building movement operate in world coordinates */}
|
|
266
283
|
{showSiteBoundaryEditor && <SiteBoundaryEditor />}
|
|
267
284
|
{/* Terrain sculpting is a mode rather than a `tools[phase][tool]` entry —
|
|
@@ -375,7 +392,7 @@ export const ToolManager: React.FC = () => {
|
|
|
375
392
|
)}
|
|
376
393
|
{/* Registry-first: when the active tool's kind has a registered
|
|
377
394
|
NodeDefinition with a tool contribution, mount it here. */}
|
|
378
|
-
{!movingNode && useRegistryTool && RegistryToolComponent && (
|
|
395
|
+
{(!movingNode || registryToolOwnsPlacement) && useRegistryTool && RegistryToolComponent && (
|
|
379
396
|
<Suspense fallback={null}>
|
|
380
397
|
<RegistryToolComponent />
|
|
381
398
|
</Suspense>
|
|
@@ -405,6 +422,6 @@ export const ToolManager: React.FC = () => {
|
|
|
405
422
|
{/* "Magnetic" beacon at the active wall-draft snap point. */}
|
|
406
423
|
<WallSnapBeaconLayer />
|
|
407
424
|
</group>
|
|
408
|
-
|
|
425
|
+
</RegistryToolProvider>
|
|
409
426
|
)
|
|
410
427
|
}
|