@pascal-app/editor 1.0.1 → 1.0.4
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 +16 -9
- package/src/components/editor/action-menu-button.tsx +83 -0
- package/src/components/editor/bake-thumbnail.tsx +315 -4
- package/src/components/editor/capture-level-picker.tsx +72 -0
- package/src/components/editor/custom-camera-controls.tsx +221 -128
- package/src/components/editor/delete-confirmation-dialog.tsx +297 -7
- package/src/components/editor/duplicate-node.ts +244 -0
- package/src/components/editor/editor-layout-mobile.tsx +37 -25
- package/src/components/editor/editor-layout-v2.tsx +131 -58
- package/src/components/editor/export-manager.tsx +34 -8
- package/src/components/editor/first-person/build-collider-world.test.ts +51 -0
- package/src/components/editor/first-person/build-collider-world.ts +6 -2
- package/src/components/editor/first-person/mechanism-targets.test.ts +75 -0
- package/src/components/editor/first-person/mechanism-targets.ts +36 -0
- package/src/components/editor/first-person-controls.tsx +205 -2
- package/src/components/editor/floating-action-menu.tsx +63 -207
- package/src/components/editor/floor-aware-camera-controls.test.ts +442 -0
- package/src/components/editor/floor-aware-camera-controls.ts +324 -0
- package/src/components/editor/floor-openings-overlay.tsx +243 -0
- package/src/components/editor/floor-region-controls.tsx +846 -0
- package/src/components/editor/floor-region-webgpu-materials.test.ts +12 -0
- package/src/components/editor/floor-surface.test.ts +165 -0
- package/src/components/editor/floor-surface.ts +182 -0
- package/src/components/editor/floorplan-background-selection.test.ts +11 -2
- package/src/components/editor/floorplan-background-selection.ts +1 -3
- package/src/components/editor/floorplan-panel.tsx +536 -487
- package/src/components/editor/footprint-height-handle.tsx +204 -0
- package/src/components/editor/grid.tsx +31 -7
- package/src/components/editor/group-actions.ts +59 -11
- 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-hit-priority.test.tsx +63 -0
- package/src/components/editor/handles/handle-hit-priority.tsx +37 -0
- package/src/components/editor/handles/swallow-next-click.test.ts +28 -0
- package/src/components/editor/handles/use-handle-drag.ts +272 -131
- package/src/components/editor/index.tsx +316 -176
- package/src/components/editor/join-walls-pill.tsx +44 -0
- package/src/components/editor/node-action-menu.tsx +70 -70
- package/src/components/editor/node-arrow-handles.tsx +167 -14
- package/src/components/editor/open-wall-ends-3d-layer.tsx +273 -0
- package/src/components/editor/open-wall-ends-hint.tsx +89 -0
- package/src/components/editor/quick-measurement-hud.tsx +3 -2
- package/src/components/editor/registry-action-contributions.test.tsx +63 -0
- package/src/components/editor/registry-action-contributions.tsx +132 -0
- package/src/components/editor/room-controls.tsx +847 -0
- package/src/components/editor/room-floor-highlight.tsx +128 -0
- package/src/components/editor/room-handles-2d.tsx +354 -0
- package/src/components/editor/room-handles.tsx +675 -0
- package/src/components/editor/room-highlight.tsx +225 -0
- package/src/components/editor/room-labels.test.ts +357 -0
- package/src/components/editor/room-labels.tsx +990 -0
- package/src/components/editor/room-transform-ghost.tsx +256 -0
- package/src/components/editor/room-webgpu-materials.test.ts +27 -0
- package/src/components/editor/selection-manager.tsx +451 -317
- package/src/components/editor/slab-hole-highlights.tsx +3 -0
- package/src/components/editor/snapshot-capture-overlay.tsx +4 -0
- package/src/components/editor/snapshot-capture.test.ts +52 -4
- package/src/components/editor/snapshot-capture.ts +30 -5
- package/src/components/editor/thumbnail-generator.test.ts +308 -0
- package/src/components/editor/thumbnail-generator.tsx +1035 -229
- package/src/components/editor/use-floorplan-background-placement.ts +31 -11
- package/src/components/editor/vector-edge-extractor.tsx +121 -0
- package/src/components/editor/views/host-view.tsx +61 -0
- package/src/components/editor/views/plugins-view.tsx +670 -0
- package/src/components/editor/views/scene-region.tsx +59 -0
- package/src/components/editor/views/use-editor-views.ts +57 -0
- package/src/components/editor/views/view-bar.tsx +220 -0
- package/src/components/editor/wall-justification.test.ts +140 -0
- package/src/components/editor/wall-move-side-handles.tsx +114 -163
- package/src/components/editor/wall-opening-highlights.tsx +2 -3
- package/src/components/editor/wall-push-arrow.tsx +92 -0
- package/src/components/editor/wall-region-handles.tsx +509 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +3 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
- package/src/components/editor-2d/floorplan-group-move.tsx +17 -24
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.test.tsx +86 -0
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +290 -0
- package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +33 -12
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +242 -126
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.test.ts +156 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +278 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +77 -0
- package/src/components/editor-2d/renderers/floorplan-draft-measurement.tsx +166 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +87 -8
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +355 -245
- package/src/components/editor-2d/svg-paths.ts +12 -15
- package/src/components/procedural-items/inspector.tsx +40 -15
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +97 -14
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +18 -18
- package/src/components/systems/zone/zone-system.test.ts +47 -0
- package/src/components/systems/zone/zone-system.tsx +31 -66
- package/src/components/tools/__fixtures__/hosting.ts +10 -0
- package/src/components/tools/item/item-surface-placement.test.ts +3 -9
- package/src/components/tools/item/item-surface-strategy-resolver.test.ts +3 -9
- package/src/components/tools/item/placement-history.test.tsx +474 -0
- package/src/components/tools/item/placement-strategies.ts +63 -47
- package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +3 -3
- package/src/components/tools/item/use-draft-node.test.tsx +432 -1
- package/src/components/tools/item/use-draft-node.ts +207 -97
- package/src/components/tools/item/use-placement-coordinator.tsx +251 -111
- package/src/components/tools/paint-region/use-paint-tint.ts +37 -0
- package/src/components/tools/paint-region/wall-paint-region-tool.tsx +466 -0
- package/src/components/tools/registry/item-surface-move.test.ts +10 -2
- package/src/components/tools/registry/item-surface-move.ts +46 -24
- package/src/components/tools/registry/item-surface-resolver.test.ts +2 -2
- package/src/components/tools/registry/move-registry-node-tool.tsx +26 -10
- package/src/components/tools/select/box-select-tool.tsx +2 -12
- package/src/components/tools/select/plane-box-select-tool.test.tsx +79 -0
- package/src/components/tools/select/plane-box-select-tool.tsx +35 -22
- package/src/components/tools/select/select-candidates.test.ts +37 -4
- package/src/components/tools/select/select-candidates.ts +15 -11
- 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/cursor-sphere.tsx +36 -12
- 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 +17 -5
- package/src/components/tools/shared/surface-rejection.test.tsx +5 -4
- package/src/components/tools/shared/surface-rejection.tsx +14 -0
- package/src/components/tools/site/site-boundary-editor.tsx +1 -2
- package/src/components/tools/stair/stair-defaults.ts +0 -3
- package/src/components/tools/stair/stair-tool.tsx +90 -42
- package/src/components/tools/tool-manager.tsx +82 -23
- package/src/components/tools/wall/wall-drafting.test.ts +19 -4
- package/src/components/tools/zone/zone-tool.tsx +4 -12
- package/src/components/ui/action-menu/action-button.tsx +3 -0
- package/src/components/ui/action-menu/camera-actions.tsx +2 -1
- package/src/components/ui/action-menu/control-modes.tsx +2 -35
- package/src/components/ui/action-menu/measurement-control.tsx +3 -10
- package/src/components/ui/build-panel/build-panel.tsx +235 -0
- package/src/components/ui/command-palette/editor-commands.tsx +4 -30
- package/src/components/ui/command-palette/index.tsx +5 -24
- package/src/components/ui/controls/finish-swatch.tsx +110 -0
- package/src/components/ui/controls/material-paint-panel.tsx +101 -91
- package/src/components/ui/controls/material-picker.tsx +8 -67
- package/src/components/ui/controls/scene-material-list.tsx +4 -32
- package/src/components/ui/controls/select-control.tsx +65 -0
- package/src/components/ui/controls/slider-control.tsx +76 -31
- package/src/components/ui/floating-level-selector.tsx +9 -14
- package/src/components/ui/helpers/building-helper.tsx +4 -1
- package/src/components/ui/helpers/contextual-helper-panel.tsx +154 -70
- package/src/components/ui/helpers/helper-manager.test.tsx +69 -0
- package/src/components/ui/helpers/helper-manager.tsx +348 -19
- package/src/components/ui/helpers/item-helper.tsx +8 -0
- package/src/components/ui/helpers/registered-tool-helper.tsx +10 -33
- package/src/components/ui/icon-ref.tsx +45 -1
- package/src/components/ui/item-catalog/item-catalog.tsx +5 -30
- package/src/components/ui/level-duplicate-dialog.tsx +1 -1
- package/src/components/ui/node-icon-image.tsx +16 -0
- package/src/components/ui/panels/collections/collections-popover.tsx +20 -14
- package/src/components/ui/panels/floor-foundation-panel.tsx +551 -0
- package/src/components/ui/panels/floor-opening-panel.tsx +143 -0
- package/src/components/ui/panels/mobile-panel-sheet.tsx +2 -2
- package/src/components/ui/panels/mobile-selection-bar.tsx +2 -2
- package/src/components/ui/panels/multi-field-value.test.ts +26 -3
- package/src/components/ui/panels/multi-field-value.ts +6 -3
- package/src/components/ui/panels/multi-height-mode.tsx +27 -21
- package/src/components/ui/panels/multi-parametric-inspector.tsx +52 -2
- package/src/components/ui/panels/multi-selection-panel.tsx +43 -4
- package/src/components/ui/panels/node-display.ts +8 -3
- package/src/components/ui/panels/panel-manager.tsx +103 -68
- package/src/components/ui/panels/panel-wrapper.tsx +78 -36
- package/src/components/ui/panels/parametric-field-control.tsx +1 -1
- package/src/components/ui/panels/parametric-inspector.tsx +7 -19
- package/src/components/ui/panels/reference-panel.tsx +2 -3
- package/src/components/ui/panels/room-construction-rows.tsx +774 -0
- package/src/components/ui/panels/room-name-options.test.ts +55 -0
- package/src/components/ui/panels/room-name-options.ts +47 -0
- package/src/components/ui/panels/room-openings-field.tsx +110 -0
- package/src/components/ui/panels/room-panel.tsx +725 -0
- package/src/components/ui/panels/shape-choice.tsx +58 -0
- package/src/components/ui/panels/wall-region-list.tsx +109 -0
- package/src/components/ui/primitives/color-dot.tsx +36 -24
- package/src/components/ui/primitives/shortcut-token.tsx +4 -0
- package/src/components/ui/primitives/sidebar.tsx +2 -0
- package/src/components/ui/right-stack.test.ts +29 -0
- package/src/components/ui/right-stack.tsx +145 -0
- package/src/components/ui/sidebar/icon-rail.tsx +5 -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 +615 -372
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +7 -7
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -4
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +197 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +24 -11
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +176 -211
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +30 -8
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +4 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +1 -4
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +21 -13
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +0 -27
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -3
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +9 -4
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +0 -2
- package/src/components/ui/sidebar/tab-bar.tsx +10 -0
- package/src/components/ui/sidebar/use-plugin-panels.tsx +15 -5
- package/src/components/viewer/floorplan-compass-button.tsx +9 -1
- 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 +6 -18
- package/src/components/viewer/viewer-hover-label.tsx +34 -0
- package/src/components/viewer/viewer-rooms.tsx +175 -0
- package/src/components/viewer/viewer-scene-header.tsx +181 -145
- package/src/components/viewer/viewer-selection-manager.tsx +124 -0
- package/src/components/viewer/viewer-stage.tsx +2 -2
- package/src/components/viewer-zone-system.tsx +9 -9
- package/src/hooks/use-auto-save.integration.test.tsx +311 -0
- package/src/hooks/use-auto-save.ts +55 -14
- package/src/hooks/use-ceiling-events.test.ts +1 -0
- package/src/hooks/use-ceiling-events.ts +2 -0
- 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 +182 -0
- package/src/hooks/use-keyboard.ts +122 -91
- package/src/hooks/use-open-wall-ends.test.ts +99 -0
- package/src/hooks/use-open-wall-ends.ts +238 -0
- package/src/hooks/use-placement-notice.test.ts +48 -0
- package/src/hooks/use-placement-notice.ts +42 -0
- package/src/hooks/use-selected-room.ts +143 -0
- package/src/index.tsx +306 -39
- 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/ceiling-edit-session.test.ts +321 -0
- package/src/lib/ceiling-edit-session.ts +206 -0
- package/src/lib/ceiling-region-session.test.ts +179 -0
- package/src/lib/contextual-help.test.ts +27 -1
- package/src/lib/contextual-help.ts +16 -9
- package/src/lib/continuation.test.ts +19 -1
- package/src/lib/continuation.ts +25 -4
- package/src/lib/door-interaction.ts +14 -0
- package/src/lib/editor-level-display.test.ts +96 -0
- package/src/lib/editor-level-display.ts +48 -0
- package/src/lib/editor-views.ts +144 -0
- package/src/lib/export-geometry-wait.test.ts +90 -0
- package/src/lib/floor-edit-session.ts +153 -0
- package/src/lib/floor-footprints.test.ts +563 -0
- package/src/lib/floor-footprints.ts +395 -0
- package/src/lib/floor-foundation-support.test.ts +283 -0
- package/src/lib/floor-opening-draft.ts +218 -0
- package/src/lib/floor-openings.test.ts +216 -0
- package/src/lib/floor-region-geometry.ts +191 -0
- package/src/lib/floor-region-session.test.ts +485 -0
- package/src/lib/floor-region-session.ts +589 -0
- package/src/lib/floor-region-snap.ts +101 -0
- package/src/lib/floor-step-hover.test.ts +32 -0
- package/src/lib/floorplan/annotation-visibility.ts +17 -0
- package/src/lib/floorplan/dimension-drive.test.ts +360 -0
- package/src/lib/floorplan/dimension-drive.ts +479 -0
- package/src/lib/floorplan/floorplan-export.test.ts +15 -9
- package/src/lib/floorplan/floorplan-export.tsx +32 -8
- package/src/lib/floorplan/floorplan-extension.ts +48 -0
- package/src/lib/floorplan/floorplan-mode.test.ts +4 -0
- package/src/lib/floorplan/floorplan-mode.ts +2 -0
- package/src/lib/floorplan/floorplan-pdfkit-document.ts +339 -18
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +138 -8
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +56 -25
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +138 -0
- package/src/lib/floorplan/floorplan-readonly.ts +6 -0
- package/src/lib/floorplan/geometry.ts +13 -6
- package/src/lib/floorplan/index.ts +0 -9
- package/src/lib/floorplan/items.ts +5 -2
- package/src/lib/floorplan/marks.test.ts +213 -0
- package/src/lib/floorplan/marks.ts +238 -0
- package/src/lib/floorplan/open-wall-ends.test.ts +268 -0
- package/src/lib/floorplan/open-wall-ends.ts +162 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Regular.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-GeistMono.txt +92 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-LiberationSans.txt +102 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Regular.ttf +0 -0
- package/src/lib/floorplan/schedules.test.ts +189 -0
- package/src/lib/floorplan/schedules.ts +339 -0
- package/src/lib/floorplan/sheet-export.test.ts +100 -0
- package/src/lib/floorplan/sheet-export.ts +318 -0
- package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +779 -0
- package/src/lib/floorplan/site-plan/contours.test.ts +43 -0
- package/src/lib/floorplan/site-plan/contributors.ts +97 -0
- package/src/lib/floorplan/site-plan/coverage.ts +232 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.test.ts +15 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.tsx +86 -0
- package/src/lib/floorplan/site-plan/front-edge.test.ts +103 -0
- package/src/lib/floorplan/site-plan/front-edge.ts +156 -0
- package/src/lib/floorplan/site-plan/geometry.test.ts +275 -0
- package/src/lib/floorplan/site-plan/geometry.ts +557 -0
- package/src/lib/floorplan/site-plan/index.ts +71 -0
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +366 -0
- package/src/lib/floorplan/site-plan/site-annotations.ts +1083 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +379 -0
- package/src/lib/floorplan/site-plan/site-plan-layer.tsx +138 -0
- package/src/lib/floorplan/site-plan/street-name.test.ts +69 -0
- package/src/lib/floorplan/site-plan/street-name.ts +302 -0
- package/src/lib/floorplan/site-plan/yards.test.ts +69 -0
- package/src/lib/floorplan/types.ts +1 -27
- package/src/lib/fresh-planar-placement.test.ts +36 -0
- package/src/lib/fresh-planar-placement.ts +191 -36
- package/src/lib/geometry-script/author.ts +152 -0
- package/src/lib/geometry-script/client.ts +152 -0
- package/src/lib/geometry-script/protocol.ts +12 -0
- package/src/lib/gesture-lifecycle.test.ts +595 -0
- package/src/lib/gesture-lifecycle.ts +290 -0
- package/src/lib/glb-export-zone-holes.test.ts +41 -0
- package/src/lib/glb-export.test.ts +544 -16
- package/src/lib/glb-export.ts +256 -125
- package/src/lib/grid-frame.test.ts +19 -0
- package/src/lib/grid-frame.ts +18 -0
- package/src/lib/history.test.ts +6 -0
- package/src/lib/history.ts +7 -0
- package/src/lib/hud-title.ts +109 -0
- package/src/lib/hydration-gated-storage.ts +49 -0
- package/src/lib/ifc-export.test.ts +174 -0
- package/src/lib/ifc-export.ts +201 -0
- package/src/lib/ifc-parts.ts +15 -0
- package/src/lib/inspector-expanded.test.ts +44 -0
- package/src/lib/inspector-expanded.ts +36 -0
- package/src/lib/interaction/scope.ts +27 -1
- package/src/lib/keyboard-pan.test.ts +184 -0
- package/src/lib/keyboard-pan.ts +238 -0
- package/src/lib/live-gesture-round-trip.test.ts +360 -0
- package/src/lib/lot/__fixtures__/map-dossier-miami-shores.json +1 -0
- package/src/lib/lot/__fixtures__/map-dossier-stpete.json +1 -0
- package/src/lib/lot/clean-ring.test.ts +186 -0
- package/src/lib/lot/clean-ring.ts +217 -0
- package/src/lib/lot/dossier.test.ts +202 -0
- package/src/lib/lot/dossier.ts +498 -0
- package/src/lib/lot/drop-in.test.ts +326 -0
- package/src/lib/lot/drop-in.ts +407 -0
- package/src/lib/lot/index.ts +43 -0
- package/src/lib/lot/lot-patch.test.ts +340 -0
- package/src/lib/lot/lot-patch.ts +394 -0
- package/src/lib/lot/parcel-provider.ts +43 -0
- package/src/lib/lot/terrain.test.ts +161 -0
- package/src/lib/lot/terrain.ts +266 -0
- package/src/lib/material-catalog-model.ts +74 -0
- package/src/lib/material-paint-panel-model.ts +80 -0
- package/src/lib/material-paint.ts +4 -0
- package/src/lib/mezzanine-actions.test.ts +342 -0
- package/src/lib/mezzanine-draft.test.ts +501 -0
- package/src/lib/mezzanine-draft.ts +278 -0
- package/src/lib/mezzanine-messages.ts +26 -0
- package/src/lib/mezzanine-selection.test.ts +207 -0
- package/src/lib/mezzanine-stairs.ts +46 -0
- package/src/lib/model-export.ts +3 -1
- package/src/lib/node-action-movement.ts +17 -6
- package/src/lib/node-display-label.ts +37 -0
- package/src/lib/orbit-floor-clearance.test.ts +165 -0
- package/src/lib/orbit-floor-clearance.ts +106 -0
- package/src/lib/overlay-transform.test.ts +28 -0
- package/src/lib/overlay-transform.ts +12 -0
- package/src/lib/paint-commit-route.test.ts +37 -0
- package/src/lib/paint-commit-route.ts +17 -0
- package/src/lib/paint-eyedropper.test.tsx +165 -0
- package/src/lib/paint-eyedropper.ts +99 -0
- package/src/lib/paint-pass-through.test.ts +111 -0
- package/src/lib/paint-pass-through.ts +76 -0
- package/src/lib/paint-region-hover.ts +25 -0
- package/src/lib/paint-region-mode.ts +154 -0
- package/src/lib/paint-regions.test.ts +166 -0
- package/src/lib/paint-regions.ts +192 -0
- package/src/lib/paint-scope.test.ts +317 -35
- package/src/lib/paint-scope.ts +266 -25
- package/src/lib/panel-rows.ts +96 -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/plate-paint-affected.test.ts +300 -0
- package/src/lib/plate-paint-affected.ts +177 -0
- package/src/lib/plugin-chat.ts +50 -0
- package/src/lib/plugin-panels.test.ts +25 -0
- package/src/lib/plugin-panels.ts +94 -0
- package/src/lib/print-shell-compiler.ts +1 -0
- package/src/lib/registered-draft-snap.ts +12 -0
- package/src/lib/roof-duplication.ts +10 -2
- package/src/lib/room-assembly-overlay.test.ts +275 -0
- package/src/lib/room-assembly-overlay.ts +266 -0
- package/src/lib/room-built-on.test.ts +379 -0
- package/src/lib/room-built-on.ts +138 -0
- package/src/lib/room-construction-commands.ts +712 -0
- package/src/lib/room-construction.test.ts +746 -0
- package/src/lib/room-construction.ts +385 -0
- package/src/lib/room-controls-phase6.test.ts +630 -0
- package/src/lib/room-divide-session.ts +523 -0
- package/src/lib/room-handle-drag.ts +1068 -0
- package/src/lib/room-name-catalog.test.ts +90 -0
- package/src/lib/room-name-catalog.ts +168 -0
- package/src/lib/room-push-dimensions.test.ts +311 -0
- package/src/lib/room-push-dimensions.ts +251 -0
- package/src/lib/room-selection-commands.ts +62 -0
- package/src/lib/room-selection.test.ts +973 -0
- package/src/lib/room-selection.ts +417 -0
- package/src/lib/room-structure-commands.ts +94 -0
- package/src/lib/room-structure.test.ts +921 -0
- package/src/lib/room-transform-session.ts +570 -0
- package/src/lib/room-zone-routing.test.ts +294 -0
- package/src/lib/room-zone-routing.ts +197 -0
- package/src/lib/scene-clipboard.test.ts +364 -0
- package/src/lib/scene-clipboard.ts +229 -48
- package/src/lib/scene-material-refs.test.ts +79 -0
- package/src/lib/scene-material-refs.ts +79 -0
- package/src/lib/scene.test.ts +87 -0
- package/src/lib/scene.ts +39 -45
- package/src/lib/selection-routing.test.ts +129 -45
- package/src/lib/selection-routing.ts +53 -74
- package/src/lib/session-writes.test.ts +120 -0
- package/src/lib/session-writes.ts +205 -0
- package/src/lib/sidebar-panel.ts +62 -0
- package/src/lib/site-boundary.test.ts +4 -6
- package/src/lib/site-boundary.ts +1 -1
- package/src/lib/snapping-mode.test.ts +17 -2
- package/src/lib/snapping-mode.ts +19 -7
- 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/terrace-draft.test.ts +145 -0
- package/src/lib/terrace-draft.ts +213 -0
- package/src/lib/terrain-panel-model.ts +117 -0
- package/src/lib/tool-cursor-icon.test.ts +16 -0
- package/src/lib/tool-cursor-icon.ts +11 -0
- package/src/lib/typing-target.ts +9 -0
- package/src/lib/units.ts +77 -39
- package/src/lib/vector-edges.test.ts +58 -0
- package/src/lib/vector-edges.ts +400 -0
- package/src/lib/viewer-selection.test.ts +577 -0
- package/src/lib/viewer-selection.ts +569 -0
- package/src/lib/wall-draw-variant.test.ts +71 -0
- package/src/lib/wall-draw-variant.ts +91 -0
- package/src/lib/wall-paint-region-session.ts +246 -0
- package/src/lib/wall-paint-region.test.ts +373 -0
- package/src/lib/wall-polygon-draft.test.ts +262 -0
- package/src/lib/wall-polygon-draft.ts +286 -0
- package/src/lib/wall-push.test.ts +453 -0
- package/src/lib/wall-region-face.ts +352 -0
- package/src/lib/wall-region-handles.test.ts +304 -0
- package/src/lib/wall-region-handles.ts +368 -0
- package/src/lib/wall-region-list.test.ts +119 -0
- package/src/lib/wall-region-list.ts +136 -0
- package/src/lib/wall-region-snap.ts +110 -0
- package/src/lib/window-interaction.ts +14 -0
- package/src/lib/world-grid-snap.test.ts +48 -0
- package/src/lib/world-grid-snap.ts +9 -5
- package/src/lib/zone-content.ts +4 -1
- package/src/lib/zone-name.test.ts +17 -0
- package/src/lib/zone-name.ts +17 -0
- package/src/store/opens-in-select-mode.test.ts +223 -0
- package/src/store/paint-mode.test.ts +188 -0
- package/src/store/rotation-snapping.test.ts +53 -0
- package/src/store/terrain-sculpt-mode.test.ts +19 -21
- package/src/store/tool-mode.test.ts +137 -6
- package/src/store/use-delete-confirmation.ts +28 -1
- package/src/store/use-drawing-view.test.ts +24 -3
- package/src/store/use-drawing-view.ts +29 -1
- package/src/store/use-editor.tsx +442 -323
- package/src/store/use-floorplan-draft-preview.ts +23 -1
- package/src/store/use-floorplan-marquee.ts +12 -2
- package/src/store/use-interaction-scope.ts +155 -6
- package/src/store/use-quick-measurement-hud.ts +3 -3
- package/src/store/use-stair-build-preview.ts +13 -5
- package/src/store/use-wall-move-ghosts.ts +4 -2
- package/src/store/use-wall-region-handles-selection.test.ts +222 -0
- package/src/store/use-wall-region-handles-selection.ts +139 -0
- package/src/store/view-layout.test.ts +175 -0
- package/src/store/view-layout.ts +196 -0
- package/src/test-utils/immediate-animation-frames.ts +16 -0
- package/src/test-utils/live-gesture-round-trip.ts +224 -0
- package/src/test-utils/selection-harness.ts +82 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +0 -499
- package/src/components/systems/zone/zone-label-editor-system.tsx +0 -333
- package/src/components/tools/fence/fence-drafting.test.ts +0 -100
- package/src/components/tools/fence/fence-drafting.ts +0 -266
- package/src/components/ui/sidebar/panels/plugins-panel.tsx +0 -218
- package/src/components/viewer/viewer-units-panel.tsx +0 -116
- package/src/lib/floorplan/stairs.ts +0 -489
- package/src/lib/level-duplication.test.ts +0 -144
- package/src/lib/level-duplication.ts +0 -195
|
@@ -1,23 +1,78 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AnyNodeId,
|
|
3
3
|
type AssetInput,
|
|
4
|
+
beginSceneHistoryDraft,
|
|
4
5
|
ItemNode,
|
|
5
6
|
resolveSupportSlabPatch,
|
|
7
|
+
runSceneHistoryDraftWrite,
|
|
8
|
+
type SurfaceRejectReason,
|
|
9
|
+
sceneHistoryDraftRevertUpdates,
|
|
6
10
|
sceneRegistry,
|
|
7
11
|
useScene,
|
|
12
|
+
withSceneHistoryDraftSuspended,
|
|
8
13
|
} from '@pascal-app/core'
|
|
9
14
|
import { beginPerfAction, commitPerfAction, useViewer } from '@pascal-app/viewer'
|
|
10
15
|
import { useCallback, useMemo, useRef } from 'react'
|
|
11
16
|
import type { Vector3 } from 'three'
|
|
17
|
+
import { commitFreshPlacementSubtree } from '../../../lib/fresh-planar-placement'
|
|
18
|
+
import { isFreshPlacementMetadata } from '../../../lib/placement-metadata'
|
|
12
19
|
import {
|
|
13
20
|
surfaceAttachmentId,
|
|
14
21
|
surfaceAttachmentUpdates,
|
|
15
22
|
surfaceFramePose,
|
|
16
23
|
updateSurfaceNode,
|
|
17
24
|
} from '../../../lib/surface-attachment'
|
|
25
|
+
import useInteractionScope, {
|
|
26
|
+
isInteractionSubtreeDraft,
|
|
27
|
+
} from '../../../store/use-interaction-scope'
|
|
18
28
|
import usePlacementPreview from '../../../store/use-placement-preview'
|
|
19
29
|
import { stripTransient } from './placement-math'
|
|
20
30
|
|
|
31
|
+
function releaseHistoryDraft(end: { current: (() => void) | null }): void {
|
|
32
|
+
end.current?.()
|
|
33
|
+
end.current = null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A placement's own write: under a short pause, and the carried draft's fields it changes stay
|
|
38
|
+
* recorded as they were before the carry (core's `runSceneHistoryDraftWrite`).
|
|
39
|
+
*/
|
|
40
|
+
export const pausedDraftWrite = runSceneHistoryDraftWrite
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Puts back what the carry still holds on an adopted item (its own writes, core's
|
|
44
|
+
* `sceneHistoryDraftRevertUpdates`), as a carry write: a position, name or host a collaborator
|
|
45
|
+
* wrote meanwhile stays. A host deleted mid-carry is never a parent again (nor its face, roof
|
|
46
|
+
* or surface): the item stays on its live parent, else the level. Returns its parent after.
|
|
47
|
+
*/
|
|
48
|
+
function restoreOriginalState(
|
|
49
|
+
id: AnyNodeId,
|
|
50
|
+
original: OriginalState,
|
|
51
|
+
draftParentId: string | null | undefined,
|
|
52
|
+
): string | null {
|
|
53
|
+
const nodes = useScene.getState().nodes
|
|
54
|
+
const exists = (parentId: string | null | undefined): parentId is string =>
|
|
55
|
+
Boolean(parentId && nodes[parentId as AnyNodeId])
|
|
56
|
+
const hostGone = original.parentId != null && !exists(original.parentId)
|
|
57
|
+
const updates = sceneHistoryDraftRevertUpdates([id])
|
|
58
|
+
const own = updates.find((update) => update.id === id)
|
|
59
|
+
if (hostGone && own) {
|
|
60
|
+
for (const key of ['roofSegmentId', 'roofFace', 'blockFaceId']) delete own.data[key]
|
|
61
|
+
}
|
|
62
|
+
const liveParentId = nodes[id]?.parentId
|
|
63
|
+
if (!exists(liveParentId)) {
|
|
64
|
+
const fallback = [draftParentId, useViewer.getState().selection.levelId].find(exists)
|
|
65
|
+
if (fallback) {
|
|
66
|
+
if (own) own.data.parentId = fallback
|
|
67
|
+
else updates.push({ id, data: { parentId: fallback } })
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (updates.length > 0) {
|
|
71
|
+
pausedDraftWrite(() => useScene.getState().updateNodes(updates as never))
|
|
72
|
+
}
|
|
73
|
+
return useScene.getState().nodes[id]?.parentId ?? null
|
|
74
|
+
}
|
|
75
|
+
|
|
21
76
|
interface OriginalState {
|
|
22
77
|
surfaceId: string | null
|
|
23
78
|
position: [number, number, number]
|
|
@@ -32,6 +87,9 @@ interface OriginalState {
|
|
|
32
87
|
metadata: ItemNode['metadata']
|
|
33
88
|
}
|
|
34
89
|
|
|
90
|
+
/** What a duplicate keeps from the item it copies. */
|
|
91
|
+
export type ItemCopy = Pick<ItemNode, 'slots' | 'collectionIds'>
|
|
92
|
+
|
|
35
93
|
export interface DraftNodeHandle {
|
|
36
94
|
updateSurface: (data: Partial<ItemNode>, surfaceId: string | null) => void
|
|
37
95
|
/** Current draft item, or null */
|
|
@@ -39,17 +97,19 @@ export interface DraftNodeHandle {
|
|
|
39
97
|
/** Whether the current draft was adopted (move mode) vs created (create mode) */
|
|
40
98
|
readonly isAdopted: boolean
|
|
41
99
|
/** Create a new draft item at the given position. Returns the created node or null.
|
|
42
|
-
* `
|
|
100
|
+
* `copied` is the item a duplicate copies: it keeps its painted slots and collections. */
|
|
43
101
|
create: (
|
|
44
102
|
gridPosition: Vector3,
|
|
45
103
|
asset: AssetInput,
|
|
46
104
|
rotation?: [number, number, number],
|
|
47
105
|
scale?: [number, number, number],
|
|
48
|
-
|
|
106
|
+
copied?: ItemCopy,
|
|
49
107
|
) => ItemNode | null
|
|
50
108
|
/** Take ownership of an existing scene node as the draft (for move mode). */
|
|
51
109
|
adopt: (node: ItemNode) => void
|
|
52
110
|
/** Commit the current draft. Create mode: delete+recreate. Move mode: update in place.
|
|
111
|
+
* The final write is the one undo step, recorded only while no owner pauses history:
|
|
112
|
+
* a caller holding a pause lifts it around this call (`SceneHistoryPauseSession.commitStep`).
|
|
53
113
|
* `supportElevationCap` (floor commits) is the pointer-decided surface
|
|
54
114
|
* elevation — it caps the persisted `supportSlabId` election so the
|
|
55
115
|
* commit lands on the surface the cursor pointed at. */
|
|
@@ -58,6 +118,7 @@ export interface DraftNodeHandle {
|
|
|
58
118
|
options?: {
|
|
59
119
|
supportElevationCap?: number | null
|
|
60
120
|
preferredSupportSlabId?: string | null
|
|
121
|
+
onReject?: (reason: SurfaceRejectReason) => void
|
|
61
122
|
pinSupport?: boolean
|
|
62
123
|
},
|
|
63
124
|
) => string | null
|
|
@@ -67,7 +128,11 @@ export interface DraftNodeHandle {
|
|
|
67
128
|
|
|
68
129
|
/**
|
|
69
130
|
* Hook that manages the lifecycle of a transient (draft) item node.
|
|
70
|
-
*
|
|
131
|
+
* The draft is registered with core's history drafts from create/adopt until
|
|
132
|
+
* commit/destroy, so history records it as absent (created) or as it was
|
|
133
|
+
* (adopted) and no draft write becomes an undo step; the draft's own writes
|
|
134
|
+
* also run under a short balanced pause. An adopted `commit` suspends the registration for
|
|
135
|
+
* its tracked write and ends it only after success, so a rejected drop can still be retried.
|
|
71
136
|
*
|
|
72
137
|
* Supports two modes:
|
|
73
138
|
* - Create mode (via `create()`): draft is a new transient node. Commit = delete+recreate (undo removes node).
|
|
@@ -76,7 +141,9 @@ export interface DraftNodeHandle {
|
|
|
76
141
|
export function useDraftNode(): DraftNodeHandle {
|
|
77
142
|
const draftRef = useRef<ItemNode | null>(null)
|
|
78
143
|
const adoptedRef = useRef(false)
|
|
144
|
+
const ownsSubtreeRef = useRef(false)
|
|
79
145
|
const originalStateRef = useRef<OriginalState | null>(null)
|
|
146
|
+
const endHistoryDraftRef = useRef<(() => void) | null>(null)
|
|
80
147
|
|
|
81
148
|
const create = useCallback(
|
|
82
149
|
(
|
|
@@ -84,7 +151,7 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
84
151
|
asset: AssetInput,
|
|
85
152
|
rotation?: [number, number, number],
|
|
86
153
|
scale?: [number, number, number],
|
|
87
|
-
|
|
154
|
+
copied?: ItemCopy,
|
|
88
155
|
): ItemNode | null => {
|
|
89
156
|
const currentLevelId = useViewer.getState().selection.levelId
|
|
90
157
|
if (!currentLevelId) return null
|
|
@@ -97,15 +164,30 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
97
164
|
asset,
|
|
98
165
|
parentId: currentLevelId,
|
|
99
166
|
metadata: { isTransient: true },
|
|
100
|
-
...(slots ? { slots } : {}),
|
|
167
|
+
...(copied?.slots ? { slots: copied.slots } : {}),
|
|
168
|
+
...(copied?.collectionIds ? { collectionIds: copied.collectionIds } : {}),
|
|
101
169
|
})
|
|
102
170
|
|
|
103
|
-
|
|
171
|
+
releaseHistoryDraft(endHistoryDraftRef)
|
|
172
|
+
endHistoryDraftRef.current = beginSceneHistoryDraft(node.id, null)
|
|
173
|
+
try {
|
|
174
|
+
pausedDraftWrite(() => useScene.getState().createNode(node, currentLevelId))
|
|
175
|
+
} catch (error) {
|
|
176
|
+
try {
|
|
177
|
+
if (useScene.getState().nodes[node.id]) {
|
|
178
|
+
pausedDraftWrite(() => useScene.getState().deleteNode(node.id))
|
|
179
|
+
}
|
|
180
|
+
} finally {
|
|
181
|
+
releaseHistoryDraft(endHistoryDraftRef)
|
|
182
|
+
}
|
|
183
|
+
throw error
|
|
184
|
+
}
|
|
104
185
|
usePlacementPreview
|
|
105
186
|
.getState()
|
|
106
187
|
.set(node, useScene.getState().nodes[currentLevelId as AnyNodeId] ?? null)
|
|
107
188
|
draftRef.current = node
|
|
108
189
|
adoptedRef.current = false
|
|
190
|
+
ownsSubtreeRef.current = false
|
|
109
191
|
originalStateRef.current = null
|
|
110
192
|
return node
|
|
111
193
|
},
|
|
@@ -113,6 +195,13 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
113
195
|
)
|
|
114
196
|
|
|
115
197
|
const adopt = useCallback((node: ItemNode): void => {
|
|
198
|
+
releaseHistoryDraft(endHistoryDraftRef)
|
|
199
|
+
endHistoryDraftRef.current = beginSceneHistoryDraft(
|
|
200
|
+
node.id,
|
|
201
|
+
isFreshPlacementMetadata(node.metadata) ? null : node,
|
|
202
|
+
)
|
|
203
|
+
ownsSubtreeRef.current =
|
|
204
|
+
useInteractionScope.getState().adoptSubtree(node.id) || isInteractionSubtreeDraft(node.id)
|
|
116
205
|
// Save original state so destroy() can restore it
|
|
117
206
|
const meta =
|
|
118
207
|
typeof node.metadata === 'object' && node.metadata !== null && !Array.isArray(node.metadata)
|
|
@@ -138,9 +227,11 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
138
227
|
adoptedRef.current = true
|
|
139
228
|
|
|
140
229
|
// Mark as transient so it renders as a draft
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
230
|
+
pausedDraftWrite(() =>
|
|
231
|
+
useScene.getState().updateNode(node.id, {
|
|
232
|
+
metadata: { ...meta, isTransient: true },
|
|
233
|
+
}),
|
|
234
|
+
)
|
|
144
235
|
usePlacementPreview
|
|
145
236
|
.getState()
|
|
146
237
|
.set(
|
|
@@ -155,11 +246,16 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
155
246
|
options?: {
|
|
156
247
|
supportElevationCap?: number | null
|
|
157
248
|
preferredSupportSlabId?: string | null
|
|
249
|
+
onReject?: (reason: SurfaceRejectReason) => void
|
|
158
250
|
pinSupport?: boolean
|
|
159
251
|
},
|
|
160
252
|
): string | null => {
|
|
161
253
|
const draft = draftRef.current
|
|
162
254
|
if (!draft) return null
|
|
255
|
+
if (finalUpdate.parentId && !useScene.getState().nodes[finalUpdate.parentId as AnyNodeId]) {
|
|
256
|
+
options?.onReject?.('no-surface')
|
|
257
|
+
return null
|
|
258
|
+
}
|
|
163
259
|
|
|
164
260
|
const surfaceId = surfaceAttachmentId(useScene.getState().nodes[draft.id] ?? draft)
|
|
165
261
|
const stored = surfaceFramePose(
|
|
@@ -169,34 +265,46 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
169
265
|
true,
|
|
170
266
|
)
|
|
171
267
|
finalUpdate = { ...finalUpdate, ...stored }
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const original = originalStateRef.current!
|
|
180
|
-
|
|
181
|
-
// Restore original state while paused — so the undo baseline is clean
|
|
182
|
-
updateSurfaceNode(
|
|
268
|
+
// The drop ends the carry on the item's live metadata: placement strategies hand in the
|
|
269
|
+
// adoption-time snapshot, which would drop anything an agent wrote meanwhile.
|
|
270
|
+
const liveDraft = useScene.getState().nodes[draft.id]
|
|
271
|
+
if (liveDraft) finalUpdate.metadata = stripTransient(liveDraft.metadata)
|
|
272
|
+
if (isFreshPlacementMetadata(originalStateRef.current?.metadata)) {
|
|
273
|
+
const effectiveNode = ItemNode.parse({ ...draft, ...finalUpdate })
|
|
274
|
+
const id = commitFreshPlacementSubtree(
|
|
183
275
|
draft.id,
|
|
184
276
|
{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
blockFaceId: original.blockFaceId,
|
|
192
|
-
metadata: original.metadata,
|
|
277
|
+
...finalUpdate,
|
|
278
|
+
...resolveSupportSlabPatch(effectiveNode, useScene.getState().nodes, {
|
|
279
|
+
maxElevation: options?.supportElevationCap,
|
|
280
|
+
preferredSlabId: options?.preferredSupportSlabId,
|
|
281
|
+
pinSupport: options?.pinSupport,
|
|
282
|
+
}),
|
|
193
283
|
},
|
|
194
|
-
|
|
284
|
+
options?.onReject,
|
|
195
285
|
)
|
|
286
|
+
if (!id) return null
|
|
287
|
+
releaseHistoryDraft(endHistoryDraftRef)
|
|
288
|
+
if (usePlacementPreview.getState().node?.id === draft.id) {
|
|
289
|
+
usePlacementPreview.getState().clear()
|
|
290
|
+
}
|
|
291
|
+
draftRef.current = null
|
|
292
|
+
adoptedRef.current = false
|
|
293
|
+
originalStateRef.current = null
|
|
294
|
+
return id
|
|
295
|
+
}
|
|
296
|
+
if (adoptedRef.current) {
|
|
297
|
+
// Move mode: update in place (single undoable action)
|
|
298
|
+
const { parentId: newParentId, ...updateProps } = finalUpdate
|
|
299
|
+
const original = originalStateRef.current!
|
|
300
|
+
const nodesNow = useScene.getState().nodes
|
|
301
|
+
const restoredParentId = restoreOriginalState(draft.id, original, draft.parentId)
|
|
302
|
+
const parentId =
|
|
303
|
+
[newParentId, restoredParentId].find((id) => id && nodesNow[id as AnyNodeId]) ??
|
|
304
|
+
restoredParentId
|
|
196
305
|
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
306
|
+
// The original is restored above (a carry write), so the one tracked write below has
|
|
307
|
+
// the true baseline as its undo state.
|
|
200
308
|
const effectiveNode = ItemNode.parse({
|
|
201
309
|
...draft,
|
|
202
310
|
...updateProps,
|
|
@@ -204,34 +312,35 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
204
312
|
metadata: updateProps.metadata ?? stripTransient(draft.metadata),
|
|
205
313
|
})
|
|
206
314
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
315
|
+
withSceneHistoryDraftSuspended(draft.id, () => {
|
|
316
|
+
updateSurfaceNode(
|
|
317
|
+
draft.id,
|
|
318
|
+
{
|
|
319
|
+
position: updateProps.position ?? draft.position,
|
|
320
|
+
rotation: updateProps.rotation ?? draft.rotation,
|
|
321
|
+
side: updateProps.side ?? draft.side,
|
|
322
|
+
metadata: updateProps.metadata ?? stripTransient(draft.metadata),
|
|
323
|
+
parentId: parentId as string,
|
|
324
|
+
// Forward the roof host explicitly: strategies set it on every
|
|
325
|
+
// commit (segment id on a roof face, undefined elsewhere), and
|
|
326
|
+
// dropping it here strands the item in the roof frame without
|
|
327
|
+
// the segment transform.
|
|
328
|
+
roofSegmentId: updateProps.roofSegmentId,
|
|
329
|
+
roofFace: updateProps.roofFace,
|
|
330
|
+
blockFaceId: updateProps.blockFaceId,
|
|
331
|
+
// Only when the strategy decided about wallId (roof commits clear
|
|
332
|
+
// it) — floor/ceiling commits never managed the field.
|
|
333
|
+
...('wallId' in updateProps ? { wallId: updateProps.wallId } : {}),
|
|
334
|
+
...resolveSupportSlabPatch(effectiveNode, useScene.getState().nodes, {
|
|
335
|
+
maxElevation: options?.supportElevationCap,
|
|
336
|
+
preferredSlabId: options?.preferredSupportSlabId,
|
|
337
|
+
pinSupport: options?.pinSupport,
|
|
338
|
+
}),
|
|
339
|
+
},
|
|
340
|
+
surfaceId,
|
|
341
|
+
)
|
|
342
|
+
})
|
|
343
|
+
releaseHistoryDraft(endHistoryDraftRef)
|
|
235
344
|
|
|
236
345
|
const id = draft.id
|
|
237
346
|
if (usePlacementPreview.getState().node?.id === id) {
|
|
@@ -243,20 +352,19 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
243
352
|
return id
|
|
244
353
|
}
|
|
245
354
|
|
|
246
|
-
// Create mode: delete draft (paused),
|
|
355
|
+
// Create mode: delete the draft (paused), then create the fresh node (the tracked write)
|
|
247
356
|
const { parentId: newParentId, ...updateProps } = finalUpdate
|
|
248
357
|
const parentId = (newParentId ?? useViewer.getState().selection.levelId) as AnyNodeId
|
|
249
358
|
if (!parentId) return null
|
|
250
359
|
|
|
251
360
|
beginPerfAction('place:item', draft.id)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
361
|
+
pausedDraftWrite(() => {
|
|
362
|
+
updateSurfaceNode(draft.id, {}, null)
|
|
363
|
+
useScene.getState().deleteNode(draft.id)
|
|
364
|
+
})
|
|
365
|
+
releaseHistoryDraft(endHistoryDraftRef)
|
|
255
366
|
draftRef.current = null
|
|
256
367
|
|
|
257
|
-
// Briefly resume → create fresh node (the single undoable action)
|
|
258
|
-
useScene.temporal.getState().resume()
|
|
259
|
-
|
|
260
368
|
const finalNode = ItemNode.parse({
|
|
261
369
|
name: draft.name,
|
|
262
370
|
asset: draft.asset,
|
|
@@ -264,8 +372,9 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
264
372
|
rotation: updateProps.rotation ?? draft.rotation,
|
|
265
373
|
scale: updateProps.scale ?? draft.scale,
|
|
266
374
|
side: updateProps.side ?? draft.side,
|
|
267
|
-
//
|
|
375
|
+
// A duplicated item keeps its painted slots and its collections.
|
|
268
376
|
...(draft.slots ? { slots: draft.slots } : {}),
|
|
377
|
+
...(draft.collectionIds ? { collectionIds: draft.collectionIds } : {}),
|
|
269
378
|
// Roof host — see the move-mode commit above for why this must be
|
|
270
379
|
// forwarded explicitly.
|
|
271
380
|
roofSegmentId: updateProps.roofSegmentId,
|
|
@@ -296,9 +405,6 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
296
405
|
usePlacementPreview.getState().clear()
|
|
297
406
|
}
|
|
298
407
|
|
|
299
|
-
// Re-pause for next draft cycle
|
|
300
|
-
useScene.temporal.getState().pause()
|
|
301
|
-
|
|
302
408
|
adoptedRef.current = false
|
|
303
409
|
originalStateRef.current = null
|
|
304
410
|
commitPerfAction()
|
|
@@ -311,15 +417,21 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
311
417
|
if (!draftRef.current) return
|
|
312
418
|
|
|
313
419
|
const draftId = draftRef.current.id
|
|
314
|
-
|
|
315
|
-
|
|
420
|
+
if (ownsSubtreeRef.current) {
|
|
421
|
+
releaseHistoryDraft(endHistoryDraftRef)
|
|
422
|
+
draftRef.current = null
|
|
423
|
+
adoptedRef.current = false
|
|
424
|
+
originalStateRef.current = null
|
|
425
|
+
return
|
|
426
|
+
} else if (adoptedRef.current && originalStateRef.current) {
|
|
316
427
|
// Move mode: restore original state instead of deleting — but only
|
|
317
428
|
// if no other system has already committed a new position for this
|
|
318
429
|
// node. The 2D `FloorplanRegistryMoveOverlay` commits via
|
|
319
430
|
// `useScene.updateNodes` before unmounting the legacy mover, and
|
|
320
431
|
// an unconditional restore here would wipe that commit. By
|
|
321
432
|
// comparing the live state to the snapshot we took in `adopt()`,
|
|
322
|
-
// we let an external committer's write stick.
|
|
433
|
+
// we let an external committer's write stick. A preview after a rejected drop may
|
|
434
|
+
// lack the transient marker, but its still-owned position must be restored on cancel.
|
|
323
435
|
const original = originalStateRef.current
|
|
324
436
|
const id = draftRef.current.id
|
|
325
437
|
const live = useScene.getState().nodes[id as AnyNodeId] as ItemNode | undefined
|
|
@@ -327,10 +439,14 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
327
439
|
const externallyMoved =
|
|
328
440
|
!live?.metadata?.isTransient &&
|
|
329
441
|
!!livePosition &&
|
|
442
|
+
!sceneHistoryDraftRevertUpdates([id]).some(
|
|
443
|
+
(update) => update.id === id && 'position' in update.data,
|
|
444
|
+
) &&
|
|
330
445
|
(livePosition[0] !== original.position[0] ||
|
|
331
446
|
livePosition[1] !== original.position[1] ||
|
|
332
447
|
livePosition[2] !== original.position[2])
|
|
333
448
|
if (externallyMoved) {
|
|
449
|
+
releaseHistoryDraft(endHistoryDraftRef)
|
|
334
450
|
draftRef.current = null
|
|
335
451
|
adoptedRef.current = false
|
|
336
452
|
originalStateRef.current = null
|
|
@@ -340,36 +456,28 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
340
456
|
return
|
|
341
457
|
}
|
|
342
458
|
|
|
343
|
-
|
|
344
|
-
id,
|
|
345
|
-
{
|
|
346
|
-
position: original.position,
|
|
347
|
-
rotation: original.rotation,
|
|
348
|
-
side: original.side,
|
|
349
|
-
parentId: original.parentId,
|
|
350
|
-
roofSegmentId: original.roofSegmentId,
|
|
351
|
-
roofFace: original.roofFace,
|
|
352
|
-
blockFaceId: original.blockFaceId,
|
|
353
|
-
metadata: original.metadata,
|
|
354
|
-
},
|
|
355
|
-
original.surfaceId,
|
|
356
|
-
)
|
|
459
|
+
restoreOriginalState(id, original, draftRef.current.parentId)
|
|
357
460
|
|
|
358
461
|
// Also reset the Three.js mesh directly — the store update triggers a React
|
|
359
462
|
// re-render but the mesh position was mutated by useFrame and may not reset
|
|
360
463
|
// until the next render cycle, leaving a visual glitch.
|
|
361
464
|
const mesh = sceneRegistry.nodes.get(id as AnyNodeId)
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
mesh.
|
|
465
|
+
const restored = useScene.getState().nodes[id as AnyNodeId] as ItemNode | undefined
|
|
466
|
+
if (mesh && restored) {
|
|
467
|
+
mesh.position.set(restored.position[0], restored.position[1], restored.position[2])
|
|
468
|
+
mesh.rotation.y = restored.rotation[1] ?? 0
|
|
365
469
|
mesh.visible = true
|
|
366
470
|
}
|
|
367
471
|
} else {
|
|
368
472
|
// Create mode: delete the transient node
|
|
369
|
-
|
|
370
|
-
|
|
473
|
+
const id = draftRef.current.id
|
|
474
|
+
pausedDraftWrite(() => {
|
|
475
|
+
updateSurfaceNode(id, {}, null)
|
|
476
|
+
useScene.getState().deleteNode(id)
|
|
477
|
+
})
|
|
371
478
|
}
|
|
372
479
|
|
|
480
|
+
releaseHistoryDraft(endHistoryDraftRef)
|
|
373
481
|
draftRef.current = null
|
|
374
482
|
adoptedRef.current = false
|
|
375
483
|
originalStateRef.current = null
|
|
@@ -382,10 +490,12 @@ export function useDraftNode(): DraftNodeHandle {
|
|
|
382
490
|
const draft = draftRef.current
|
|
383
491
|
if (!draft) return
|
|
384
492
|
const pose = { ...draft, ...data }
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
493
|
+
pausedDraftWrite(() =>
|
|
494
|
+
updateSurfaceNode(
|
|
495
|
+
draft.id,
|
|
496
|
+
{ ...data, ...surfaceFramePose(pose.parentId, surfaceId, pose, true) },
|
|
497
|
+
surfaceId,
|
|
498
|
+
),
|
|
389
499
|
)
|
|
390
500
|
}, [])
|
|
391
501
|
|