@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
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getSelectableKinds,
|
|
12
12
|
type ItemNode,
|
|
13
13
|
isRegistrySelectable,
|
|
14
|
+
isSelectionHighlightEnabled,
|
|
14
15
|
type NodeEvent,
|
|
15
16
|
nodeRegistry,
|
|
16
17
|
type RoofEvent,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
type StairSurfaceMaterialRole,
|
|
24
25
|
sceneRegistry,
|
|
25
26
|
useLiveNodeOverrides,
|
|
27
|
+
useRegistryVersion,
|
|
26
28
|
useScene,
|
|
27
29
|
} from '@pascal-app/core'
|
|
28
30
|
|
|
@@ -30,6 +32,7 @@ import {
|
|
|
30
32
|
createMaterial,
|
|
31
33
|
createMaterialFromPresetRef,
|
|
32
34
|
getRoofMaterialArray,
|
|
35
|
+
registerMaterialCacheCleanup,
|
|
33
36
|
useViewer,
|
|
34
37
|
} from '@pascal-app/viewer'
|
|
35
38
|
import { useThree } from '@react-three/fiber'
|
|
@@ -38,10 +41,14 @@ import { type BufferGeometry, Color, type Material, type Mesh, type Object3D, Ve
|
|
|
38
41
|
import {
|
|
39
42
|
canDirectMoveNode,
|
|
40
43
|
canDirectRotateNode,
|
|
44
|
+
pointerEventHitsEditorHandle,
|
|
45
|
+
resolveDirectManipulationNode,
|
|
41
46
|
resolveDirectRotationDragDelta,
|
|
42
47
|
resolveDirectRotationPatch,
|
|
48
|
+
shouldStartDirectMoveDrag,
|
|
43
49
|
} from '../../lib/direct-manipulation'
|
|
44
50
|
import { createEditorApi } from '../../lib/editor-api'
|
|
51
|
+
import { selectionEnabled } from '../../lib/interaction/scope'
|
|
45
52
|
import {
|
|
46
53
|
type ActivePaintMaterial,
|
|
47
54
|
buildRoofSegmentSurfaceMaterialPatch,
|
|
@@ -49,6 +56,11 @@ import {
|
|
|
49
56
|
hasActivePaintMaterial,
|
|
50
57
|
resolveActivePaintMaterialFromSelection,
|
|
51
58
|
} from '../../lib/material-paint'
|
|
59
|
+
import {
|
|
60
|
+
combinePaintPreviews,
|
|
61
|
+
createPaintPreviewOwner,
|
|
62
|
+
type PaintPreviewCleanup,
|
|
63
|
+
} from '../../lib/paint-preview-owner'
|
|
52
64
|
import {
|
|
53
65
|
availablePaintScopes,
|
|
54
66
|
commitPaintScopeFanout,
|
|
@@ -77,6 +89,7 @@ import useInteractionScope, {
|
|
|
77
89
|
useIsCurveReshape,
|
|
78
90
|
useMovingNode,
|
|
79
91
|
} from '../../store/use-interaction-scope'
|
|
92
|
+
import { expandSessionSelectionForNode } from '../../store/use-session-groups'
|
|
80
93
|
import { boxSelectHandled, suppressBoxSelectForPointer } from '../tools/select/box-select-state'
|
|
81
94
|
import { armGroupMove3d } from './group-move-3d'
|
|
82
95
|
import { classifyParticipant } from './group-transform-shared'
|
|
@@ -110,8 +123,6 @@ type SelectableNodeType =
|
|
|
110
123
|
| 'window'
|
|
111
124
|
| 'door'
|
|
112
125
|
|
|
113
|
-
type PaintPreviewCleanup = () => void
|
|
114
|
-
|
|
115
126
|
type PaintInteraction = {
|
|
116
127
|
key: string
|
|
117
128
|
apply: (() => void) | null
|
|
@@ -590,6 +601,7 @@ const computeNextIds = (
|
|
|
590
601
|
currentSelectedIds: selectedIds,
|
|
591
602
|
modifierKeys: selectionModifiersFromEvent(event, modifierKeys),
|
|
592
603
|
nodeId: node.id,
|
|
604
|
+
expandIdsForNode: expandSessionSelectionForNode,
|
|
593
605
|
})
|
|
594
606
|
}
|
|
595
607
|
|
|
@@ -774,11 +786,18 @@ export const SelectionManager = () => {
|
|
|
774
786
|
meta: false,
|
|
775
787
|
ctrl: false,
|
|
776
788
|
shift: false,
|
|
789
|
+
alt: false,
|
|
777
790
|
})
|
|
778
791
|
const clickHandledRef = useRef(false)
|
|
779
792
|
|
|
780
793
|
const movingNode = useMovingNode()
|
|
781
794
|
const isCurveReshape = useIsCurveReshape()
|
|
795
|
+
// Plugin kinds register AFTER mount (async dynamic-import discovery), so
|
|
796
|
+
// every effect below that snapshots `getSelectableKinds()` into an emitter
|
|
797
|
+
// subscription list depends on this version — a late plugin load re-runs
|
|
798
|
+
// them and picks up the new kinds (hover / click / double-click / paint /
|
|
799
|
+
// pointerdown). Without it, plugin nodes select-but-never-hover in prod.
|
|
800
|
+
const registryVersion = useRegistryVersion()
|
|
782
801
|
|
|
783
802
|
useEffect(() => {
|
|
784
803
|
const nextHoverMode: HoverHighlightMode = mode === 'delete' ? 'delete' : 'default'
|
|
@@ -790,9 +809,12 @@ export const SelectionManager = () => {
|
|
|
790
809
|
}, [mode, setHoverHighlightMode])
|
|
791
810
|
|
|
792
811
|
useEffect(() => {
|
|
812
|
+
// re-subscribe when plugin kinds register after mount (async plugin load)
|
|
813
|
+
void registryVersion
|
|
793
814
|
if (mode !== 'material-paint') return
|
|
794
815
|
if (movingNode || isCurveReshape) return
|
|
795
816
|
|
|
817
|
+
const previewOwner = createPaintPreviewOwner()
|
|
796
818
|
let activePreview: { key: string; restore: PaintPreviewCleanup } | null = null
|
|
797
819
|
// The last hover event, replayed when the application scope cycles so the
|
|
798
820
|
// preview + chip update under a stationary cursor (Shift fires no pointer move).
|
|
@@ -814,7 +836,7 @@ export const SelectionManager = () => {
|
|
|
814
836
|
selectedMaterialTarget: useEditor.getState().selectedMaterialTarget,
|
|
815
837
|
})
|
|
816
838
|
|
|
817
|
-
const
|
|
839
|
+
const resolvePaintInteraction = (event: NodeEvent): PaintInteraction | null => {
|
|
818
840
|
const eraser = useEditor.getState().paintEraser
|
|
819
841
|
const activePaintMaterial = resolveActivePaintMaterial()
|
|
820
842
|
const node = event.node
|
|
@@ -937,27 +959,29 @@ export const SelectionManager = () => {
|
|
|
937
959
|
// paint capability builds the preview; restores combine.
|
|
938
960
|
const restores: PaintPreviewCleanup[] = []
|
|
939
961
|
const sceneNodes = useScene.getState().nodes
|
|
940
|
-
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
962
|
+
try {
|
|
963
|
+
for (const target of scopeTargets) {
|
|
964
|
+
const targetNode = sceneNodes[target.nodeId]
|
|
965
|
+
const targetRoot = getRegisteredNodeObject(target.nodeId)
|
|
966
|
+
const targetCap = targetNode
|
|
967
|
+
? nodeRegistry.get(targetNode.type)?.capabilities?.paint
|
|
968
|
+
: null
|
|
969
|
+
if (!(targetNode && targetRoot && targetCap)) continue
|
|
970
|
+
const restore = targetCap.applyPreview({
|
|
971
|
+
node: targetNode,
|
|
972
|
+
role: target.role,
|
|
973
|
+
material: paintSpec.material,
|
|
974
|
+
materialPreset: paintSpec.materialPreset,
|
|
975
|
+
root: targetRoot,
|
|
976
|
+
})
|
|
977
|
+
if (restore) restores.push(restore)
|
|
978
|
+
}
|
|
979
|
+
} catch (error) {
|
|
980
|
+
combinePaintPreviews(restores)()
|
|
981
|
+
throw error
|
|
955
982
|
}
|
|
956
983
|
if (restores.length === 0) return null
|
|
957
|
-
return ()
|
|
958
|
-
for (let index = restores.length - 1; index >= 0; index -= 1)
|
|
959
|
-
restores[index]?.()
|
|
960
|
-
}
|
|
984
|
+
return combinePaintPreviews(restores)
|
|
961
985
|
}
|
|
962
986
|
: () => previewCursor('not-allowed'),
|
|
963
987
|
}
|
|
@@ -1057,6 +1081,9 @@ export const SelectionManager = () => {
|
|
|
1057
1081
|
return null
|
|
1058
1082
|
}
|
|
1059
1083
|
|
|
1084
|
+
const getPaintInteraction = (event: NodeEvent) =>
|
|
1085
|
+
previewOwner.wrap(resolvePaintInteraction(event))
|
|
1086
|
+
|
|
1060
1087
|
const onEnter = (event: NodeEvent) => {
|
|
1061
1088
|
// A host-driven drag (handle resize/rotate) sets `inputDragging`.
|
|
1062
1089
|
// useNodeEvents now emits hover events during such a drag so surface
|
|
@@ -1185,25 +1212,28 @@ export const SelectionManager = () => {
|
|
|
1185
1212
|
setHoverHighlightMode('default')
|
|
1186
1213
|
useEditor.getState().setPaintHover(null)
|
|
1187
1214
|
}
|
|
1188
|
-
}, [isCurveReshape, mode, movingNode, setHoverHighlightMode])
|
|
1215
|
+
}, [isCurveReshape, mode, movingNode, setHoverHighlightMode, registryVersion])
|
|
1189
1216
|
|
|
1190
1217
|
useEffect(() => {
|
|
1191
1218
|
const onKeyDown = (event: KeyboardEvent) => {
|
|
1192
1219
|
if (event.key === 'Meta') modifierKeysRef.current.meta = true
|
|
1193
1220
|
if (event.key === 'Control') modifierKeysRef.current.ctrl = true
|
|
1194
1221
|
if (event.key === 'Shift') modifierKeysRef.current.shift = true
|
|
1222
|
+
if (event.key === 'Alt') modifierKeysRef.current.alt = true
|
|
1195
1223
|
}
|
|
1196
1224
|
|
|
1197
1225
|
const onKeyUp = (event: KeyboardEvent) => {
|
|
1198
1226
|
if (event.key === 'Meta') modifierKeysRef.current.meta = false
|
|
1199
1227
|
if (event.key === 'Control') modifierKeysRef.current.ctrl = false
|
|
1200
1228
|
if (event.key === 'Shift') modifierKeysRef.current.shift = false
|
|
1229
|
+
if (event.key === 'Alt') modifierKeysRef.current.alt = false
|
|
1201
1230
|
}
|
|
1202
1231
|
|
|
1203
1232
|
const clearModifiers = () => {
|
|
1204
1233
|
modifierKeysRef.current.meta = false
|
|
1205
1234
|
modifierKeysRef.current.ctrl = false
|
|
1206
1235
|
modifierKeysRef.current.shift = false
|
|
1236
|
+
modifierKeysRef.current.alt = false
|
|
1207
1237
|
}
|
|
1208
1238
|
|
|
1209
1239
|
window.addEventListener('keydown', onKeyDown)
|
|
@@ -1218,12 +1248,16 @@ export const SelectionManager = () => {
|
|
|
1218
1248
|
}, [])
|
|
1219
1249
|
|
|
1220
1250
|
useEffect(() => {
|
|
1251
|
+
// re-subscribe when plugin kinds register after mount (async plugin load)
|
|
1252
|
+
void registryVersion
|
|
1221
1253
|
if (mode !== 'select') return
|
|
1222
1254
|
if (movingNode || isCurveReshape) return
|
|
1223
1255
|
|
|
1224
1256
|
const onPointerDown = (event: NodeEvent) => {
|
|
1257
|
+
if (!selectionEnabled(useInteractionScope.getState().scope)) return
|
|
1225
1258
|
const pointer = pointerEventFromNodeEvent(event)
|
|
1226
1259
|
if (pointer.button !== 0) return
|
|
1260
|
+
const handleOwnsPointer = pointerEventHitsEditorHandle(event.nativeEvent)
|
|
1227
1261
|
|
|
1228
1262
|
// Plain press on a transformable member of a multi-selection arms the
|
|
1229
1263
|
// group move — dragging slides the whole selection on the ground plane
|
|
@@ -1231,6 +1265,7 @@ export const SelectionManager = () => {
|
|
|
1231
1265
|
// group-move gizmo cross). A plain click (no drag) still falls through
|
|
1232
1266
|
// to the normal click handling, which collapses to the pressed node.
|
|
1233
1267
|
if (
|
|
1268
|
+
!handleOwnsPointer &&
|
|
1234
1269
|
!(pointer.shiftKey || pointer.altKey || isCommandModifier(pointer)) &&
|
|
1235
1270
|
armGroupMove3d({
|
|
1236
1271
|
nodeId: event.node.id as AnyNodeId,
|
|
@@ -1246,8 +1281,6 @@ export const SelectionManager = () => {
|
|
|
1246
1281
|
return
|
|
1247
1282
|
}
|
|
1248
1283
|
|
|
1249
|
-
if (!isCommandModifier(pointer)) return
|
|
1250
|
-
|
|
1251
1284
|
const eventNode = useScene.getState().nodes[event.node.id as AnyNodeId] ?? event.node
|
|
1252
1285
|
const node = resolveCanvasSelectionNode({
|
|
1253
1286
|
node: eventNode,
|
|
@@ -1255,18 +1288,26 @@ export const SelectionManager = () => {
|
|
|
1255
1288
|
selectedIds: useViewer.getState().selection.selectedIds,
|
|
1256
1289
|
})
|
|
1257
1290
|
if (!canDirectMoveNode(node)) return
|
|
1258
|
-
// Sole selection only: per-node direct manipulation stands down for a
|
|
1259
|
-
// multi-selection (the group sessions own plain drags there, and Cmd is
|
|
1260
|
-
// the selection-toggle key — a wobbly Cmd+click must not yank one
|
|
1261
|
-
// member out of the group).
|
|
1262
1291
|
const currentSelectedIds = useViewer.getState().selection.selectedIds
|
|
1263
|
-
|
|
1292
|
+
const allowPlainDrag = nodeRegistry.get(node.type)?.capabilities?.movable?.directDrag === true
|
|
1293
|
+
if (
|
|
1294
|
+
!shouldStartDirectMoveDrag({
|
|
1295
|
+
allowPlainDrag,
|
|
1296
|
+
commandModifier: isCommandModifier(pointer),
|
|
1297
|
+
handleOwnsPointer,
|
|
1298
|
+
nodeId: node.id,
|
|
1299
|
+
selectedIds: currentSelectedIds,
|
|
1300
|
+
})
|
|
1301
|
+
) {
|
|
1302
|
+
return
|
|
1303
|
+
}
|
|
1264
1304
|
|
|
1265
1305
|
const startX = pointer.clientX
|
|
1266
1306
|
const startY = pointer.clientY
|
|
1267
1307
|
const pointerId = pointer.pointerId
|
|
1268
1308
|
const pointerTarget = pointer.target instanceof EventTarget ? pointer.target : null
|
|
1269
1309
|
let engaged = false
|
|
1310
|
+
let engagedTargetId: AnyNodeId | null = null
|
|
1270
1311
|
|
|
1271
1312
|
const cleanup = () => {
|
|
1272
1313
|
window.removeEventListener('pointermove', onMove)
|
|
@@ -1288,8 +1329,9 @@ export const SelectionManager = () => {
|
|
|
1288
1329
|
useViewer.getState().setInputDragging(true)
|
|
1289
1330
|
swallowNextClick()
|
|
1290
1331
|
createEditorApi().engageMoveDrag(node)
|
|
1332
|
+
engagedTargetId = (getMovingNode()?.id as AnyNodeId | undefined) ?? null
|
|
1291
1333
|
requestAnimationFrame(() => {
|
|
1292
|
-
if (getMovingNode()
|
|
1334
|
+
if (!getMovingNode()) return
|
|
1293
1335
|
pointerTarget?.dispatchEvent(
|
|
1294
1336
|
new PointerEvent('pointermove', {
|
|
1295
1337
|
altKey: moveEvent.altKey,
|
|
@@ -1313,7 +1355,7 @@ export const SelectionManager = () => {
|
|
|
1313
1355
|
if (engaged) {
|
|
1314
1356
|
requestAnimationFrame(() => {
|
|
1315
1357
|
const editor = useEditor.getState()
|
|
1316
|
-
if (getMovingNode()?.id !==
|
|
1358
|
+
if (getMovingNode()?.id !== engagedTargetId || !editor.placementDragMode) return
|
|
1317
1359
|
editor.setMovingNode(null)
|
|
1318
1360
|
})
|
|
1319
1361
|
}
|
|
@@ -1357,7 +1399,7 @@ export const SelectionManager = () => {
|
|
|
1357
1399
|
emitter.off(`${type}:pointerdown` as any, onPointerDown as any)
|
|
1358
1400
|
}
|
|
1359
1401
|
}
|
|
1360
|
-
}, [isCurveReshape, mode, movingNode, camera, raycaster, glDomElement])
|
|
1402
|
+
}, [isCurveReshape, mode, movingNode, camera, raycaster, glDomElement, registryVersion])
|
|
1361
1403
|
|
|
1362
1404
|
// Move cursor over the selected movable node: the visual cue that clicking it
|
|
1363
1405
|
// picks it up (replaces the removed move-cross gizmo). Reacts only when the
|
|
@@ -1375,7 +1417,7 @@ export const SelectionManager = () => {
|
|
|
1375
1417
|
if (key === prevKey) return
|
|
1376
1418
|
prevKey = key
|
|
1377
1419
|
let wantsMove = false
|
|
1378
|
-
if (hoveredId && !getMovingNode()) {
|
|
1420
|
+
if (hoveredId && !getMovingNode() && selectionEnabled(useInteractionScope.getState().scope)) {
|
|
1379
1421
|
if (sole === hoveredId) {
|
|
1380
1422
|
const node = useScene.getState().nodes[sole as AnyNodeId]
|
|
1381
1423
|
wantsMove = !!node && canDirectMoveNode(node)
|
|
@@ -1523,6 +1565,8 @@ export const SelectionManager = () => {
|
|
|
1523
1565
|
}, [isCurveReshape, mode, movingNode])
|
|
1524
1566
|
|
|
1525
1567
|
useEffect(() => {
|
|
1568
|
+
// re-subscribe when plugin kinds register after mount (async plugin load)
|
|
1569
|
+
void registryVersion
|
|
1526
1570
|
if (mode !== 'select') return
|
|
1527
1571
|
if (movingNode || isCurveReshape) return
|
|
1528
1572
|
|
|
@@ -1538,6 +1582,7 @@ export const SelectionManager = () => {
|
|
|
1538
1582
|
// body click so only the reshape tool handles the release. (Scoped to
|
|
1539
1583
|
// `endpoint`: hole-edit relies on node clicks to exit, just below.)
|
|
1540
1584
|
const activeScope = useInteractionScope.getState().scope
|
|
1585
|
+
if (activeScope.kind === 'mesh-editing') return
|
|
1541
1586
|
if (activeScope.kind === 'reshaping' && activeScope.reshape === 'endpoint') return
|
|
1542
1587
|
|
|
1543
1588
|
if (dispatchSceneAction(event.node, getEventObject(event))) {
|
|
@@ -1636,9 +1681,16 @@ export const SelectionManager = () => {
|
|
|
1636
1681
|
const hasModifier = nativeEvent.shiftKey || isCommandModifier(nativeEvent)
|
|
1637
1682
|
const isAlreadySole =
|
|
1638
1683
|
selectedIdsBeforeRouting.length === 1 && selectedIdsBeforeRouting[0] === nodeToSelect.id
|
|
1639
|
-
if (
|
|
1684
|
+
if (
|
|
1685
|
+
useEditor.getState().mode !== 'delete' &&
|
|
1686
|
+
!hasModifier &&
|
|
1687
|
+
isAlreadySole &&
|
|
1688
|
+
!getMovingNode() &&
|
|
1689
|
+
canDirectMoveNode(nodeToSelect)
|
|
1690
|
+
) {
|
|
1640
1691
|
sfxEmitter.emit('sfx:item-pick')
|
|
1641
|
-
|
|
1692
|
+
const moveTarget = resolveDirectManipulationNode(nodeToSelect, useScene.getState().nodes)
|
|
1693
|
+
useEditor.getState().setMovingNode(moveTarget as never)
|
|
1642
1694
|
useViewer.getState().setSelection({ selectedIds: [] })
|
|
1643
1695
|
return
|
|
1644
1696
|
}
|
|
@@ -1754,6 +1806,7 @@ export const SelectionManager = () => {
|
|
|
1754
1806
|
const onGridClick = (event: GridEvent) => {
|
|
1755
1807
|
if (clickHandledRef.current) return
|
|
1756
1808
|
if (boxSelectHandled) return
|
|
1809
|
+
if (useInteractionScope.getState().scope.kind === 'mesh-editing') return
|
|
1757
1810
|
const nativeEvent = event.nativeEvent
|
|
1758
1811
|
if (nativeEvent?.metaKey || nativeEvent?.ctrlKey || nativeEvent?.shiftKey) return
|
|
1759
1812
|
const { phase, structureLayer } = useEditor.getState()
|
|
@@ -1775,14 +1828,17 @@ export const SelectionManager = () => {
|
|
|
1775
1828
|
})
|
|
1776
1829
|
emitter.off('grid:click', onGridClick)
|
|
1777
1830
|
}
|
|
1778
|
-
}, [isCurveReshape, mode, movingNode])
|
|
1831
|
+
}, [isCurveReshape, mode, movingNode, registryVersion])
|
|
1779
1832
|
|
|
1780
1833
|
// Global double-click handler for auto-switching phases and cross-phase hover
|
|
1781
1834
|
useEffect(() => {
|
|
1835
|
+
// re-subscribe when plugin kinds register after mount (async plugin load)
|
|
1836
|
+
void registryVersion
|
|
1782
1837
|
if (mode !== 'select') return
|
|
1783
1838
|
if (movingNode || isCurveReshape) return
|
|
1784
1839
|
|
|
1785
1840
|
const onEnter = (event: NodeEvent) => {
|
|
1841
|
+
if (useInteractionScope.getState().scope.kind === 'mesh-editing') return
|
|
1786
1842
|
// A host-driven drag (handle resize/rotate, box-select) sets
|
|
1787
1843
|
// `inputDragging`. useNodeEvents still emits hover events during it so
|
|
1788
1844
|
// surface move tools keep tracking — but the select-hover outline must
|
|
@@ -1831,6 +1887,7 @@ export const SelectionManager = () => {
|
|
|
1831
1887
|
}
|
|
1832
1888
|
|
|
1833
1889
|
const onDoubleClick = (event: NodeEvent) => {
|
|
1890
|
+
if (useInteractionScope.getState().scope.kind === 'mesh-editing') return
|
|
1834
1891
|
let node = resolveCanvasSelectionNode({
|
|
1835
1892
|
node: resolveSelectModeNodeTarget(event),
|
|
1836
1893
|
nodes: useScene.getState().nodes,
|
|
@@ -1930,10 +1987,12 @@ export const SelectionManager = () => {
|
|
|
1930
1987
|
emitter.off(`${type}:double-click` as any, onDoubleClick as any)
|
|
1931
1988
|
})
|
|
1932
1989
|
}
|
|
1933
|
-
}, [isCurveReshape, mode, movingNode])
|
|
1990
|
+
}, [isCurveReshape, mode, movingNode, registryVersion])
|
|
1934
1991
|
|
|
1935
1992
|
// Delete mode: click-to-delete (sledgehammer tool)
|
|
1936
1993
|
useEffect(() => {
|
|
1994
|
+
// re-subscribe when plugin kinds register after mount (async plugin load)
|
|
1995
|
+
void registryVersion
|
|
1937
1996
|
if (mode !== 'delete') return
|
|
1938
1997
|
|
|
1939
1998
|
const onClick = (event: NodeEvent) => {
|
|
@@ -2006,7 +2065,7 @@ export const SelectionManager = () => {
|
|
|
2006
2065
|
}
|
|
2007
2066
|
useViewer.setState({ hoveredId: null })
|
|
2008
2067
|
}
|
|
2009
|
-
}, [mode])
|
|
2068
|
+
}, [mode, registryVersion])
|
|
2010
2069
|
|
|
2011
2070
|
return (
|
|
2012
2071
|
<>
|
|
@@ -2096,6 +2155,7 @@ const SelectionMaterialSync = () => {
|
|
|
2096
2155
|
const previewSelectedIds = useViewer((s) => s.previewSelectedIds)
|
|
2097
2156
|
const hoveredId = useViewer((s) => s.hoveredId)
|
|
2098
2157
|
const hoverHighlightMode = useViewer((s) => s.hoverHighlightMode)
|
|
2158
|
+
const registryVersion = useRegistryVersion()
|
|
2099
2159
|
const geometryRevision = useViewer((s) => s.geometryRevision)
|
|
2100
2160
|
const activeHighlightKindsRef = useRef(new Map<string, HighlightKind>())
|
|
2101
2161
|
const highlightedMaterialsRef = useRef(
|
|
@@ -2118,6 +2178,10 @@ const SelectionMaterialSync = () => {
|
|
|
2118
2178
|
continue
|
|
2119
2179
|
}
|
|
2120
2180
|
|
|
2181
|
+
if (node && !isSelectionHighlightEnabled(node.type)) {
|
|
2182
|
+
continue
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2121
2185
|
const rootObject = sceneRegistry.nodes.get(id)
|
|
2122
2186
|
if (!rootObject) {
|
|
2123
2187
|
continue
|
|
@@ -2173,6 +2237,7 @@ const SelectionMaterialSync = () => {
|
|
|
2173
2237
|
}, [])
|
|
2174
2238
|
|
|
2175
2239
|
useEffect(() => {
|
|
2240
|
+
void registryVersion
|
|
2176
2241
|
void geometryRevision
|
|
2177
2242
|
const nextHighlightKinds = new Map<string, HighlightKind>()
|
|
2178
2243
|
|
|
@@ -2188,6 +2253,7 @@ const SelectionMaterialSync = () => {
|
|
|
2188
2253
|
syncSelectionMaterials()
|
|
2189
2254
|
}, [
|
|
2190
2255
|
geometryRevision,
|
|
2256
|
+
registryVersion,
|
|
2191
2257
|
hoverHighlightMode,
|
|
2192
2258
|
hoveredId,
|
|
2193
2259
|
previewSelectedIds,
|
|
@@ -2228,7 +2294,7 @@ const SelectionMaterialSync = () => {
|
|
|
2228
2294
|
}, [])
|
|
2229
2295
|
|
|
2230
2296
|
useEffect(() => {
|
|
2231
|
-
|
|
2297
|
+
const clearHighlights = () => {
|
|
2232
2298
|
for (const [mesh, entry] of highlightedMaterialsRef.current.entries()) {
|
|
2233
2299
|
if (mesh.material === entry.highlightedMaterial) {
|
|
2234
2300
|
mesh.material = entry.originalMaterial
|
|
@@ -2238,6 +2304,11 @@ const SelectionMaterialSync = () => {
|
|
|
2238
2304
|
|
|
2239
2305
|
highlightedMaterialsRef.current.clear()
|
|
2240
2306
|
}
|
|
2307
|
+
const unsubscribe = registerMaterialCacheCleanup(clearHighlights)
|
|
2308
|
+
return () => {
|
|
2309
|
+
unsubscribe()
|
|
2310
|
+
clearHighlights()
|
|
2311
|
+
}
|
|
2241
2312
|
}, [])
|
|
2242
2313
|
|
|
2243
2314
|
return null
|
|
@@ -2249,11 +2320,13 @@ const EditorOutlinerSync = () => {
|
|
|
2249
2320
|
const previewSelectedIds = useViewer((s) => s.previewSelectedIds)
|
|
2250
2321
|
const hoveredId = useViewer((s) => s.hoveredId)
|
|
2251
2322
|
const geometryRevision = useViewer((s) => s.geometryRevision)
|
|
2323
|
+
const registryVersion = useRegistryVersion()
|
|
2252
2324
|
const outliner = useViewer((s) => s.outliner)
|
|
2253
2325
|
const nodes = useScene((s) => s.nodes)
|
|
2254
2326
|
|
|
2255
2327
|
useEffect(() => {
|
|
2256
2328
|
void geometryRevision
|
|
2329
|
+
void registryVersion
|
|
2257
2330
|
let idsToHighlight: string[] = []
|
|
2258
2331
|
|
|
2259
2332
|
// 1. Determine what should be highlighted based on Phase
|
|
@@ -2288,7 +2361,8 @@ const EditorOutlinerSync = () => {
|
|
|
2288
2361
|
// 2. Sync with the imperative outliner arrays (mutate in place to keep references)
|
|
2289
2362
|
outliner.selectedObjects.length = 0
|
|
2290
2363
|
for (const id of idsToHighlight) {
|
|
2291
|
-
|
|
2364
|
+
const node = nodes[id as AnyNodeId]
|
|
2365
|
+
if (!(node && isSelectionHighlightEnabled(node.type))) continue
|
|
2292
2366
|
const obj = sceneRegistry.nodes.get(id)
|
|
2293
2367
|
if (obj?.parent) outliner.selectedObjects.push(obj)
|
|
2294
2368
|
}
|
|
@@ -2299,14 +2373,25 @@ const EditorOutlinerSync = () => {
|
|
|
2299
2373
|
useViewer.setState({ hoveredId: null })
|
|
2300
2374
|
} else {
|
|
2301
2375
|
const hoveredNode = nodes[hoveredId as AnyNodeId]
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2376
|
+
if (hoveredNode && isSelectionHighlightEnabled(hoveredNode.type)) {
|
|
2377
|
+
const obj =
|
|
2378
|
+
hoveredNode.type === 'roof-segment'
|
|
2379
|
+
? (getHoveredRoofSegmentOutlineProxy(hoveredId) ?? sceneRegistry.nodes.get(hoveredId))
|
|
2380
|
+
: sceneRegistry.nodes.get(hoveredId)
|
|
2381
|
+
if (obj?.parent) outliner.hoveredObjects.push(obj)
|
|
2382
|
+
}
|
|
2307
2383
|
}
|
|
2308
2384
|
}
|
|
2309
|
-
}, [
|
|
2385
|
+
}, [
|
|
2386
|
+
geometryRevision,
|
|
2387
|
+
registryVersion,
|
|
2388
|
+
phase,
|
|
2389
|
+
previewSelectedIds,
|
|
2390
|
+
selection,
|
|
2391
|
+
hoveredId,
|
|
2392
|
+
outliner,
|
|
2393
|
+
nodes,
|
|
2394
|
+
])
|
|
2310
2395
|
|
|
2311
2396
|
return null
|
|
2312
2397
|
}
|
|
@@ -50,6 +50,7 @@ export function SiteEdgeLabels() {
|
|
|
50
50
|
})
|
|
51
51
|
const activeHandleDrag = useActiveHandleDrag()
|
|
52
52
|
const unit = useViewer((state) => state.unit)
|
|
53
|
+
const metricNotation = useViewer((state) => state.metricNotation)
|
|
53
54
|
const cameraMode = useViewer((state) => state.cameraMode)
|
|
54
55
|
const isNight = useViewer((state) => getSceneTheme(state.sceneTheme).appearance === 'dark')
|
|
55
56
|
const camera = useThree((state) => state.camera)
|
|
@@ -132,7 +133,7 @@ export function SiteEdgeLabels() {
|
|
|
132
133
|
textShadow: `-1.5px -1.5px 0 ${shadowColor}, 1.5px -1.5px 0 ${shadowColor}, -1.5px 1.5px 0 ${shadowColor}, 1.5px 1.5px 0 ${shadowColor}, 0 0 4px ${shadowColor}, 0 0 4px ${shadowColor}`,
|
|
133
134
|
}}
|
|
134
135
|
>
|
|
135
|
-
{formatLinearMeasurement(edge.dist, unit)}
|
|
136
|
+
{formatLinearMeasurement(edge.dist, unit, metricNotation)}
|
|
136
137
|
</div>
|
|
137
138
|
</Html>
|
|
138
139
|
))}
|