@pascal-app/editor 0.8.0 → 0.9.2
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 +15 -13
- package/src/components/editor/alignment-3d-guide-layer.tsx +168 -0
- package/src/components/editor/bake-exporter.tsx +47 -0
- package/src/components/editor/custom-camera-controls.tsx +795 -28
- package/src/components/editor/editor-layout-mobile.tsx +8 -3
- package/src/components/editor/editor-layout-v2.tsx +76 -51
- package/src/components/editor/export-manager.tsx +69 -32
- package/src/components/editor/fence-tangent-lines-3d.tsx +87 -0
- package/src/components/editor/first-person/build-collider-world.test.ts +182 -0
- package/src/components/editor/first-person/build-collider-world.ts +228 -83
- package/src/components/editor/first-person-controls.tsx +943 -27
- package/src/components/editor/floating-action-menu.tsx +676 -208
- package/src/components/editor/floating-building-action-menu.tsx +2 -2
- package/src/components/editor/floorplan-background-selection.test.ts +58 -0
- package/src/components/editor/floorplan-background-selection.ts +3 -2
- package/src/components/editor/floorplan-panel.tsx +6916 -12137
- package/src/components/editor/grid.tsx +197 -36
- package/src/components/editor/group-actions.ts +457 -0
- package/src/components/editor/group-floating-action-menu.tsx +130 -0
- package/src/components/editor/group-move-3d.ts +397 -0
- package/src/components/editor/group-rotate-handle.tsx +420 -0
- package/src/components/editor/group-selection-box-3d.tsx +173 -0
- package/src/components/editor/group-transform-shared.test.ts +396 -0
- package/src/components/editor/group-transform-shared.ts +489 -0
- package/src/components/editor/handles/handle-arrow.tsx +581 -0
- package/src/components/editor/handles/use-handle-drag.ts +246 -0
- package/src/components/editor/index.tsx +412 -162
- package/src/components/editor/measurement-pill.tsx +115 -0
- package/src/components/editor/node-action-menu.tsx +14 -1
- package/src/components/editor/node-arrow-handles.tsx +1646 -0
- package/src/components/editor/opening-guides-3d-layer.tsx +144 -0
- package/src/components/editor/quick-measurement-card.tsx +70 -0
- package/src/components/editor/quick-measurement-hud.tsx +28 -0
- package/src/components/editor/riser-diagram-panel.tsx +137 -0
- package/src/components/editor/selection-manager.tsx +1106 -483
- package/src/components/editor/site-edge-labels.tsx +45 -21
- package/src/components/editor/slab-hole-highlights.tsx +474 -0
- package/src/components/editor/snapshot-capture-overlay.tsx +369 -155
- package/src/components/editor/three-context-bridge.ts +22 -0
- package/src/components/editor/thumbnail-generator.tsx +140 -41
- package/src/components/editor/use-floorplan-background-placement.ts +190 -35
- package/src/components/editor/use-floorplan-hit-testing.ts +24 -0
- package/src/components/editor/use-floorplan-scene-data.ts +19 -7
- package/src/components/editor/use-mesh-settle-epoch.ts +26 -0
- package/src/components/editor/wall-measurement-label.tsx +14 -19
- package/src/components/editor/wall-move-side-handles.tsx +870 -0
- package/src/components/editor/wall-opening-highlights.tsx +181 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +304 -0
- package/src/components/editor-2d/floorplan-action-menu-layer.tsx +15 -6
- package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +142 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +5 -3
- package/src/components/editor-2d/floorplan-group-action-menu.tsx +87 -0
- package/src/components/editor-2d/floorplan-group-move.tsx +701 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +113 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +1656 -0
- package/src/components/editor-2d/floorplan-quick-measure-layer.tsx +212 -0
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +392 -0
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +857 -0
- package/src/components/editor-2d/floorplan-render-context.tsx +62 -0
- package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +77 -0
- package/src/components/editor-2d/floorplan-wall-move-ghost-layer.tsx +47 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +266 -0
- package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +15 -0
- package/src/components/editor-2d/renderers/floorplan-label-angle.ts +14 -0
- package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +108 -66
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +62 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +308 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +3325 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +24 -9
- package/src/components/editor-2d/renderers/floorplan-voronoi-layer.tsx +128 -0
- package/src/components/editor-2d/svg-paths.ts +10 -2
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +439 -57
- package/src/components/systems/ceiling/ceiling-system.tsx +109 -12
- package/src/components/systems/roof/roof-edit-system.tsx +1851 -22
- package/src/components/systems/selection-affordance-manager.tsx +38 -0
- package/src/components/systems/stair/stair-edit-system.tsx +1 -1
- package/src/components/systems/zone/zone-label-editor-system.tsx +14 -1
- package/src/components/systems/zone/zone-system.tsx +42 -13
- package/src/components/tools/elevator/elevator-defaults.ts +8 -0
- package/src/components/tools/elevator/elevator-tool.tsx +375 -0
- package/src/components/tools/elevator/move-elevator-tool.tsx +263 -0
- package/src/components/tools/fence/fence-drafting.ts +141 -27
- package/src/components/tools/item/move-tool.tsx +45 -105
- package/src/components/tools/item/placement-math.test.ts +22 -0
- package/src/components/tools/item/placement-math.ts +59 -7
- package/src/components/tools/item/placement-strategies.ts +432 -16
- package/src/components/tools/item/placement-types.ts +22 -1
- package/src/components/tools/item/use-draft-node.ts +51 -2
- package/src/components/tools/item/use-placement-coordinator.tsx +1399 -256
- package/src/components/tools/registry/move-registry-node-tool.tsx +1064 -0
- package/src/components/tools/roof/roof-tool.tsx +350 -28
- package/src/components/tools/select/box-select-state.ts +88 -0
- package/src/components/tools/select/box-select-tool.tsx +442 -520
- package/src/components/tools/select/marquee-geometry.test.ts +161 -0
- package/src/components/tools/select/marquee-geometry.ts +155 -0
- package/src/components/tools/select/plane-box-select-tool.tsx +570 -0
- package/src/components/tools/select/screen-rectangle-selection.ts +84 -0
- package/src/components/tools/select/select-candidates.test.ts +109 -0
- package/src/components/tools/select/select-candidates.ts +85 -0
- package/src/components/tools/shared/affordance-dispatch.ts +30 -0
- package/src/components/tools/shared/cursor-sphere.tsx +62 -26
- package/src/components/tools/shared/drag-bounding-box.tsx +195 -0
- package/src/components/tools/shared/facing-indicator.tsx +87 -0
- package/src/components/tools/shared/facing-pose-indicator.tsx +54 -0
- package/src/components/tools/shared/floor-stack-preview.ts +25 -0
- package/src/components/tools/shared/fresh-placement-visibility.ts +61 -0
- package/src/components/tools/shared/placement-box-geometry.ts +123 -0
- package/src/components/tools/shared/placement-box.tsx +305 -0
- package/src/components/tools/shared/polygon-editor.tsx +866 -175
- package/src/components/tools/shared/segment-angle.ts +56 -0
- package/src/components/tools/site/site-boundary-editor.tsx +520 -16
- package/src/components/tools/site/site-flag-model.ts +18 -0
- package/src/components/tools/stair/stair-defaults.ts +2 -1
- package/src/components/tools/stair/stair-tool.tsx +342 -37
- package/src/components/tools/tool-manager.tsx +239 -62
- package/src/components/tools/wall/wall-drafting.test.ts +330 -0
- package/src/components/tools/wall/wall-drafting.ts +248 -171
- package/src/components/tools/wall/wall-snap-geometry.test.ts +123 -0
- package/src/components/tools/wall/wall-snap-geometry.ts +331 -0
- package/src/components/tools/zone/zone-boundary-editor.tsx +5 -1
- package/src/components/tools/zone/zone-tool.tsx +86 -82
- package/src/components/ui/action-menu/action-button.tsx +21 -1
- package/src/components/ui/action-menu/camera-actions.tsx +24 -17
- package/src/components/ui/action-menu/control-modes.tsx +40 -170
- package/src/components/ui/action-menu/furnish-tools.tsx +5 -5
- package/src/components/ui/action-menu/index.tsx +3 -117
- package/src/components/ui/action-menu/measurement-control.tsx +188 -0
- package/src/components/ui/action-menu/structure-tools.tsx +27 -93
- package/src/components/ui/action-menu/view-toggles.tsx +279 -90
- package/src/components/ui/command-palette/editor-commands.tsx +20 -15
- package/src/components/ui/command-palette/index.tsx +5 -3
- package/src/components/ui/controls/material-paint-panel.tsx +152 -0
- package/src/components/ui/controls/material-picker.tsx +87 -177
- package/src/components/ui/controls/material-properties-editor.tsx +108 -0
- package/src/components/ui/controls/metric-control.tsx +142 -46
- package/src/components/ui/controls/scene-material-list.tsx +247 -0
- package/src/components/ui/controls/slider-control.tsx +88 -38
- package/src/components/ui/floating-level-selector.tsx +47 -10
- package/src/components/ui/helpers/building-helper.tsx +10 -23
- package/src/components/ui/helpers/contextual-helper-panel.tsx +428 -0
- package/src/components/ui/helpers/helper-manager.tsx +235 -23
- package/src/components/ui/helpers/item-helper.tsx +28 -32
- package/src/components/ui/helpers/registered-tool-helper.tsx +59 -0
- package/src/components/ui/helpers/roof-helper.tsx +10 -12
- package/src/components/ui/icon-ref.tsx +47 -0
- package/src/components/ui/item-catalog/catalog-items.tsx +40 -1
- package/src/components/ui/item-catalog/item-catalog.tsx +16 -36
- package/src/components/ui/level-duplicate-dialog.tsx +2 -1
- package/src/components/ui/panels/node-display.ts +17 -16
- package/src/components/ui/panels/panel-manager.tsx +55 -58
- package/src/components/ui/panels/panel-wrapper.tsx +238 -12
- package/src/components/ui/panels/parametric-inspector.tsx +482 -0
- package/src/components/ui/panels/reference-panel.tsx +54 -19
- package/src/components/ui/primitives/dropdown-menu.tsx +12 -8
- package/src/components/ui/primitives/shortcut-token.tsx +39 -3
- package/src/components/ui/primitives/sidebar.tsx +1 -0
- package/src/components/ui/scene-loader.tsx +1 -3
- package/src/components/ui/sidebar/app-sidebar.tsx +5 -1
- package/src/components/ui/sidebar/icon-rail.tsx +50 -31
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +263 -0
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +82 -8
- package/src/components/ui/sidebar/panels/plugins-panel.tsx +218 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +122 -41
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +66 -7
- package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +267 -0
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -1
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +9 -5
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +86 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +7 -2
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +92 -0
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +75 -0
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +3 -3
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +86 -0
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +42 -21
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +31 -17
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +140 -0
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +48 -8
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +132 -0
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +9 -5
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +86 -0
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +7 -1
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +112 -39
- package/src/components/ui/sidebar/panels/site-panel/tree-structure.ts +36 -0
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +7 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +3 -3
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +51 -2
- package/src/components/ui/sidebar/tab-bar.tsx +75 -1
- package/src/components/ui/sidebar/use-plugin-panels.tsx +132 -0
- package/src/components/ui/snap-target-badge.test.tsx +40 -0
- package/src/components/ui/snap-target-badge.tsx +88 -0
- package/src/components/viewer-overlay.tsx +253 -61
- package/src/components/viewer-zone-system.tsx +7 -2
- package/src/hooks/use-auto-save.test.ts +14 -0
- package/src/hooks/use-auto-save.ts +64 -10
- package/src/hooks/use-ceiling-events.ts +176 -0
- package/src/hooks/use-drag-action.ts +127 -0
- package/src/hooks/use-keyboard.ts +493 -95
- package/src/hooks/use-selection.ts +64 -0
- package/src/index.tsx +451 -6
- package/src/lib/active-placement-surface.ts +35 -0
- package/src/lib/ceiling-plan-snap.ts +24 -0
- package/src/lib/constants.ts +6 -2
- package/src/lib/contextual-help.test.ts +112 -0
- package/src/lib/contextual-help.ts +144 -0
- package/src/lib/continuation.ts +64 -0
- package/src/lib/direct-manipulation.test.ts +192 -0
- package/src/lib/direct-manipulation.ts +109 -0
- package/src/lib/editor-api.ts +50 -0
- package/src/lib/elevator-support.ts +96 -0
- package/src/lib/floorplan/apply-alignment.test.ts +25 -0
- package/src/lib/floorplan/apply-alignment.ts +117 -0
- package/src/lib/floorplan/floorplan-export.tsx +364 -0
- package/src/lib/floorplan/geometry.ts +53 -0
- package/src/lib/floorplan/index.ts +10 -0
- package/src/lib/floorplan/items.ts +5 -2
- package/src/lib/floorplan/plan-coords.ts +21 -0
- package/src/lib/floorplan/selection-tool.ts +40 -0
- package/src/lib/floorplan/stairs.ts +12 -1
- package/src/lib/fresh-planar-placement.test.ts +339 -0
- package/src/lib/fresh-planar-placement.ts +130 -0
- package/src/lib/glb-export.test.ts +441 -0
- package/src/lib/glb-export.ts +874 -0
- package/src/lib/history.ts +11 -1
- package/src/lib/interaction/hot-set.test.ts +133 -0
- package/src/lib/interaction/hot-set.ts +67 -0
- package/src/lib/interaction/overlay-policy.test.ts +51 -0
- package/src/lib/interaction/overlay-policy.ts +59 -0
- package/src/lib/interaction/scope.ts +211 -0
- package/src/lib/level-duplication.ts +54 -42
- package/src/lib/level-selection.ts +2 -2
- package/src/lib/material-paint.ts +227 -48
- package/src/lib/measurement-kind.test.ts +30 -0
- package/src/lib/measurement-kind.ts +19 -0
- package/src/lib/measurement-label.test.ts +47 -0
- package/src/lib/measurement-label.ts +68 -0
- package/src/lib/measurement-parser.ts +116 -0
- package/src/lib/measurements.test.ts +183 -0
- package/src/lib/measurements.ts +199 -0
- package/src/lib/paint-scope.test.ts +454 -0
- package/src/lib/paint-scope.ts +461 -0
- package/src/lib/placement-drag-release.ts +23 -0
- package/src/lib/placement-metadata.ts +29 -0
- package/src/lib/planar-cursor-placement.test.ts +100 -0
- package/src/lib/planar-cursor-placement.ts +42 -0
- package/src/lib/plugin-panels.test.ts +19 -0
- package/src/lib/plugin-panels.ts +91 -0
- package/src/lib/quick-measurement.test.ts +77 -0
- package/src/lib/quick-measurement.ts +109 -0
- package/src/lib/roof-duplication.ts +7 -7
- package/src/lib/roof-hover-outline-proxy.ts +22 -0
- package/src/lib/roof-wall-hit.ts +164 -0
- package/src/lib/scene-clipboard.test.ts +196 -0
- package/src/lib/scene-clipboard.ts +332 -0
- package/src/lib/scene.ts +40 -8
- package/src/lib/selection-routing.test.ts +298 -0
- package/src/lib/selection-routing.ts +175 -0
- package/src/lib/sfx/index.ts +1 -0
- package/src/lib/sfx/movement-tick.test.ts +65 -0
- package/src/lib/sfx/movement-tick.ts +18 -0
- package/src/lib/sfx-bus.ts +69 -10
- package/src/lib/sfx-player.test.ts +124 -0
- package/src/lib/sfx-player.ts +148 -55
- package/src/lib/site-boundary.ts +1 -0
- package/src/lib/slab-plan-snap.test.ts +93 -0
- package/src/lib/slab-plan-snap.ts +108 -0
- package/src/lib/snapping-mode.test.ts +138 -0
- package/src/lib/snapping-mode.ts +179 -0
- package/src/lib/stair-duplication.ts +4 -4
- package/src/lib/stair-levels.test.ts +146 -0
- package/src/lib/stair-levels.ts +177 -0
- package/src/lib/surface-plan-snap.test.ts +71 -0
- package/src/lib/surface-plan-snap.ts +256 -0
- package/src/lib/use-linear-display.ts +40 -0
- package/src/lib/world-grid-snap.ts +231 -0
- package/src/lib/zone-content.ts +130 -0
- package/src/store/use-alignment-guides.ts +21 -0
- package/src/store/use-direct-manipulation-feedback.ts +20 -0
- package/src/store/use-editor.tsx +767 -114
- package/src/store/use-facing-pose.ts +44 -0
- package/src/store/use-fence-curve-draft.ts +21 -0
- package/src/store/use-floorplan-draft-preview.ts +101 -0
- package/src/store/use-floorplan-marquee.ts +50 -0
- package/src/store/use-interaction-scope.test.ts +186 -0
- package/src/store/use-interaction-scope.ts +132 -0
- package/src/store/use-measurement-draft.test.ts +530 -0
- package/src/store/use-measurement-draft.ts +543 -0
- package/src/store/use-opening-guides.ts +41 -0
- package/src/store/use-placement-preview.ts +39 -0
- package/src/store/use-quick-measurement-hud.test.ts +53 -0
- package/src/store/use-quick-measurement-hud.ts +77 -0
- package/src/store/use-segment-draft-chain.ts +28 -0
- package/src/store/use-stair-build-preview.ts +43 -0
- package/src/store/use-wall-move-ghosts.ts +36 -0
- package/src/store/use-wall-snap-indicator.ts +34 -0
- package/src/components/editor/first-person/bvh-ecctrl.tsx +0 -860
- package/src/components/editor/preset-thumbnail-generator.tsx +0 -125
- package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +0 -113
- package/src/components/tools/building/move-building-tool.tsx +0 -157
- package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +0 -43
- package/src/components/tools/ceiling/ceiling-hole-editor.tsx +0 -49
- package/src/components/tools/ceiling/ceiling-tool.tsx +0 -465
- package/src/components/tools/ceiling/move-ceiling-tool.tsx +0 -264
- package/src/components/tools/column/column-tool.tsx +0 -97
- package/src/components/tools/column/move-column-tool.tsx +0 -105
- package/src/components/tools/door/door-math.ts +0 -110
- package/src/components/tools/door/door-tool.tsx +0 -324
- package/src/components/tools/door/move-door-tool.tsx +0 -412
- package/src/components/tools/fence/curve-fence-tool.tsx +0 -178
- package/src/components/tools/fence/fence-tool.tsx +0 -346
- package/src/components/tools/fence/move-fence-endpoint-tool.tsx +0 -441
- package/src/components/tools/fence/move-fence-tool.tsx +0 -302
- package/src/components/tools/item/item-tool.tsx +0 -26
- package/src/components/tools/roof/move-roof-tool.tsx +0 -364
- package/src/components/tools/slab/move-slab-tool.tsx +0 -182
- package/src/components/tools/slab/slab-boundary-editor.tsx +0 -43
- package/src/components/tools/slab/slab-hole-editor.tsx +0 -49
- package/src/components/tools/slab/slab-tool.tsx +0 -322
- package/src/components/tools/spawn/move-spawn-tool.tsx +0 -101
- package/src/components/tools/spawn/spawn-tool.tsx +0 -130
- package/src/components/tools/wall/curve-wall-tool.tsx +0 -178
- package/src/components/tools/wall/move-wall-endpoint-tool.tsx +0 -426
- package/src/components/tools/wall/move-wall-tool.tsx +0 -358
- package/src/components/tools/wall/wall-tool.tsx +0 -332
- package/src/components/tools/window/move-window-tool.tsx +0 -447
- package/src/components/tools/window/window-math.ts +0 -117
- package/src/components/tools/window/window-tool.tsx +0 -332
- package/src/components/ui/helpers/ceiling-helper.tsx +0 -20
- package/src/components/ui/helpers/slab-helper.tsx +0 -20
- package/src/components/ui/helpers/wall-helper.tsx +0 -20
- package/src/components/ui/panels/ceiling-panel.tsx +0 -249
- package/src/components/ui/panels/column-panel.tsx +0 -759
- package/src/components/ui/panels/door-panel.tsx +0 -1299
- package/src/components/ui/panels/fence-panel.tsx +0 -222
- package/src/components/ui/panels/item-panel.tsx +0 -306
- package/src/components/ui/panels/paint-panel.tsx +0 -163
- package/src/components/ui/panels/presets/presets-popover.tsx +0 -511
- package/src/components/ui/panels/roof-panel.tsx +0 -280
- package/src/components/ui/panels/roof-segment-panel.tsx +0 -311
- package/src/components/ui/panels/slab-panel.tsx +0 -250
- package/src/components/ui/panels/spawn-panel.tsx +0 -161
- package/src/components/ui/panels/stair-panel.tsx +0 -577
- package/src/components/ui/panels/stair-segment-panel.tsx +0 -325
- package/src/components/ui/panels/wall-panel.tsx +0 -273
- package/src/components/ui/panels/window-panel.tsx +0 -968
- package/src/contexts/presets-context.tsx +0 -121
- package/src/hooks/use-contextual-tools.ts +0 -61
|
@@ -2,71 +2,107 @@ import {
|
|
|
2
2
|
type AnyNodeId,
|
|
3
3
|
type BuildingNode,
|
|
4
4
|
type CeilingNode,
|
|
5
|
+
type FenceNode,
|
|
6
|
+
nodeRegistry,
|
|
5
7
|
type SlabNode,
|
|
6
8
|
useScene,
|
|
9
|
+
type WallNode,
|
|
7
10
|
} from '@pascal-app/core'
|
|
8
11
|
import { useViewer } from '@pascal-app/viewer'
|
|
12
|
+
import { type ComponentType, lazy, Suspense, useMemo } from 'react'
|
|
9
13
|
import useEditor, { type Phase, type Tool } from '../../store/use-editor'
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
import {
|
|
15
|
+
useControlPointReshape,
|
|
16
|
+
useEditingHole,
|
|
17
|
+
useEndpointReshape,
|
|
18
|
+
useIsCurveReshape,
|
|
19
|
+
useMovingNode,
|
|
20
|
+
useReshapingNode,
|
|
21
|
+
useTangentReshape,
|
|
22
|
+
} from '../../store/use-interaction-scope'
|
|
23
|
+
import { Alignment3DGuideLayer } from '../editor/alignment-3d-guide-layer'
|
|
24
|
+
import { OpeningGuides3DLayer } from '../editor/opening-guides-3d-layer'
|
|
25
|
+
import { WallSnapBeaconLayer } from '../editor/wall-snap-beacon-layer'
|
|
26
|
+
import { ElevatorTool } from './elevator/elevator-tool'
|
|
19
27
|
import { MoveTool } from './item/move-tool'
|
|
20
28
|
import { RoofTool } from './roof/roof-tool'
|
|
29
|
+
import { getRegistryAffordanceTool } from './shared/affordance-dispatch'
|
|
30
|
+
import { FacingPoseIndicator } from './shared/facing-pose-indicator'
|
|
21
31
|
import { SiteBoundaryEditor } from './site/site-boundary-editor'
|
|
22
|
-
import { SlabBoundaryEditor } from './slab/slab-boundary-editor'
|
|
23
|
-
import { SlabHoleEditor } from './slab/slab-hole-editor'
|
|
24
|
-
import { SlabTool } from './slab/slab-tool'
|
|
25
|
-
import { SpawnTool } from './spawn/spawn-tool'
|
|
26
32
|
import { StairTool } from './stair/stair-tool'
|
|
27
|
-
import { CurveWallTool } from './wall/curve-wall-tool'
|
|
28
|
-
import { MoveWallEndpointTool } from './wall/move-wall-endpoint-tool'
|
|
29
|
-
import { WallTool } from './wall/wall-tool'
|
|
30
|
-
import { WindowTool } from './window/window-tool'
|
|
31
33
|
import { ZoneBoundaryEditor } from './zone/zone-boundary-editor'
|
|
32
34
|
import { ZoneTool } from './zone/zone-tool'
|
|
33
35
|
|
|
36
|
+
// Cache lazy tool components keyed by their loader so React.lazy isn't
|
|
37
|
+
// re-invoked across renders.
|
|
38
|
+
const lazyToolCache = new WeakMap<() => Promise<unknown>, ComponentType>()
|
|
39
|
+
|
|
40
|
+
function getRegistryTool(tool: Tool | null): ComponentType | null {
|
|
41
|
+
if (!tool) return null
|
|
42
|
+
const def = nodeRegistry.get(tool)
|
|
43
|
+
if (!def?.tool) return null
|
|
44
|
+
const cached = lazyToolCache.get(def.tool)
|
|
45
|
+
if (cached) return cached
|
|
46
|
+
const Comp = lazy(def.tool as () => Promise<{ default: ComponentType }>)
|
|
47
|
+
lazyToolCache.set(def.tool, Comp)
|
|
48
|
+
return Comp
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Legacy tool fallbacks — kinds whose placement tools haven't migrated
|
|
52
|
+
// to `def.tool` yet. Wall / fence / slab / ceiling / door / window /
|
|
53
|
+
// item / shelf / spawn now go through the registry path above.
|
|
34
54
|
const tools: Record<Phase, Partial<Record<Tool, React.FC>>> = {
|
|
35
55
|
site: {
|
|
36
56
|
'property-line': SiteBoundaryEditor,
|
|
37
57
|
},
|
|
38
58
|
structure: {
|
|
39
|
-
wall: WallTool,
|
|
40
|
-
fence: FenceTool,
|
|
41
|
-
slab: SlabTool,
|
|
42
|
-
ceiling: CeilingTool,
|
|
43
59
|
roof: RoofTool,
|
|
44
60
|
stair: StairTool,
|
|
45
|
-
door: DoorTool,
|
|
46
|
-
item: ItemTool,
|
|
47
61
|
zone: ZoneTool,
|
|
48
|
-
spawn: SpawnTool,
|
|
49
|
-
window: WindowTool,
|
|
50
|
-
},
|
|
51
|
-
furnish: {
|
|
52
|
-
item: ItemTool,
|
|
53
62
|
},
|
|
63
|
+
furnish: {},
|
|
54
64
|
}
|
|
55
65
|
|
|
56
66
|
export const ToolManager: React.FC = () => {
|
|
57
67
|
const phase = useEditor((state) => state.phase)
|
|
58
68
|
const mode = useEditor((state) => state.mode)
|
|
59
69
|
const tool = useEditor((state) => state.tool)
|
|
60
|
-
const movingNode =
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
70
|
+
const movingNode = useMovingNode()
|
|
71
|
+
const movingNodeOrigin = useEditor((state) => state.movingNodeOrigin)
|
|
72
|
+
const endpointReshape = useEndpointReshape()
|
|
73
|
+
const controlPointReshape = useControlPointReshape()
|
|
74
|
+
const tangentReshape = useTangentReshape()
|
|
75
|
+
const isCurveReshape = useIsCurveReshape()
|
|
76
|
+
const reshapingNode = useReshapingNode()
|
|
77
|
+
// The endpoint affordance tool's `target` is kind-specific
|
|
78
|
+
// (`{ wall | fence, endpoint }`); rebuild it from the (frozen) reshaped node +
|
|
79
|
+
// the scope's endpoint. Memoised so it stays referentially stable across the
|
|
80
|
+
// scene-write re-renders during the drag — otherwise a fresh object each frame
|
|
81
|
+
// re-fires the tool's setup effect (endpoint drag would loop / freeze).
|
|
82
|
+
const endpointTarget = useMemo(() => {
|
|
83
|
+
if (!(endpointReshape && reshapingNode)) return null
|
|
84
|
+
return reshapingNode.type === 'fence'
|
|
85
|
+
? { fence: reshapingNode as FenceNode, endpoint: endpointReshape.endpoint }
|
|
86
|
+
: { wall: reshapingNode as WallNode, endpoint: endpointReshape.endpoint }
|
|
87
|
+
}, [endpointReshape, reshapingNode])
|
|
88
|
+
const controlPointTarget = useMemo(() => {
|
|
89
|
+
if (!(controlPointReshape && reshapingNode?.type === 'fence')) return null
|
|
90
|
+
return { fence: reshapingNode as FenceNode, index: controlPointReshape.index }
|
|
91
|
+
}, [controlPointReshape, reshapingNode])
|
|
92
|
+
const tangentTarget = useMemo(() => {
|
|
93
|
+
if (!(tangentReshape && reshapingNode?.type === 'fence')) return null
|
|
94
|
+
return {
|
|
95
|
+
fence: reshapingNode as FenceNode,
|
|
96
|
+
index: tangentReshape.index,
|
|
97
|
+
side: tangentReshape.side,
|
|
98
|
+
}
|
|
99
|
+
}, [reshapingNode, tangentReshape])
|
|
100
|
+
const editingHole = useEditingHole()
|
|
66
101
|
const selectedZoneId = useViewer((state) => state.selection.zoneId)
|
|
67
102
|
const selectedIds = useViewer((state) => state.selection.selectedIds)
|
|
68
103
|
const buildingId = useViewer((state) => state.selection.buildingId)
|
|
69
104
|
const activeLevelId = useViewer((state) => state.selection.levelId)
|
|
105
|
+
const setSelection = useViewer((state) => state.setSelection)
|
|
70
106
|
const nodes = useScene((state) => state.nodes)
|
|
71
107
|
|
|
72
108
|
// Building transform for the local group — all building-relative tools live inside this group
|
|
@@ -81,30 +117,46 @@ export const ToolManager: React.FC = () => {
|
|
|
81
117
|
const selectedSlabId = selectedIds.find((id) => nodes[id as AnyNodeId]?.type === 'slab') as
|
|
82
118
|
| SlabNode['id']
|
|
83
119
|
| undefined
|
|
120
|
+
const selectedSlab = selectedSlabId ? (nodes[selectedSlabId as AnyNodeId] as SlabNode) : null
|
|
121
|
+
const editingSlabHoleIsManual =
|
|
122
|
+
selectedSlabId !== undefined &&
|
|
123
|
+
editingHole?.nodeId === selectedSlabId &&
|
|
124
|
+
selectedSlab?.holeMetadata?.[editingHole.holeIndex]?.source === 'manual'
|
|
84
125
|
|
|
85
126
|
// Check if a ceiling is selected
|
|
86
127
|
const selectedCeilingId = selectedIds.find((id) => nodes[id as AnyNodeId]?.type === 'ceiling') as
|
|
87
128
|
| CeilingNode['id']
|
|
88
129
|
| undefined
|
|
89
130
|
|
|
90
|
-
//
|
|
91
|
-
|
|
131
|
+
// Keep the site vertex flags available in select mode; the editor component
|
|
132
|
+
// switches to full polygon editing only after a flag activates site mode.
|
|
133
|
+
const showSiteBoundaryEditor = phase === 'site' || mode === 'select'
|
|
134
|
+
|
|
135
|
+
// A multi-selection is manipulated as one rigid group (drag / R / T), so
|
|
136
|
+
// per-node reshape chrome — the slab / ceiling boundary editors' vertex and
|
|
137
|
+
// edge handles — mounts only for a sole selection.
|
|
138
|
+
const isSoleSelection = selectedIds.length === 1
|
|
92
139
|
|
|
93
140
|
// Show slab boundary editor when in structure/select mode with a slab selected (but not editing a hole)
|
|
94
141
|
const showSlabBoundaryEditor =
|
|
95
142
|
phase === 'structure' &&
|
|
96
143
|
mode === 'select' &&
|
|
144
|
+
isSoleSelection &&
|
|
97
145
|
selectedSlabId !== undefined &&
|
|
98
|
-
|
|
146
|
+
!editingSlabHoleIsManual
|
|
99
147
|
|
|
100
148
|
// Show slab hole editor when editing a hole on the selected slab
|
|
101
149
|
const showSlabHoleEditor =
|
|
102
|
-
selectedSlabId !== undefined &&
|
|
150
|
+
selectedSlabId !== undefined &&
|
|
151
|
+
editingHole !== null &&
|
|
152
|
+
editingHole.nodeId === selectedSlabId &&
|
|
153
|
+
editingSlabHoleIsManual
|
|
103
154
|
|
|
104
155
|
// Show ceiling boundary editor when in structure/select mode with a ceiling selected (but not editing a hole)
|
|
105
156
|
const showCeilingBoundaryEditor =
|
|
106
157
|
phase === 'structure' &&
|
|
107
158
|
mode === 'select' &&
|
|
159
|
+
isSoleSelection &&
|
|
108
160
|
selectedCeilingId !== undefined &&
|
|
109
161
|
(!editingHole || editingHole.nodeId !== selectedCeilingId)
|
|
110
162
|
|
|
@@ -126,16 +178,48 @@ export const ToolManager: React.FC = () => {
|
|
|
126
178
|
// Show build tools when in build mode
|
|
127
179
|
const showBuildTool = mode === 'build' && tool !== null
|
|
128
180
|
|
|
129
|
-
|
|
181
|
+
// A move initiated from the 2D floor-plan (orange move-dot) is owned end-to-
|
|
182
|
+
// end by `FloorplanRegistryMoveOverlay`, which marks the origin `'2d'` at
|
|
183
|
+
// dot-down. Mounting the 3D affordance mover alongside it would adopt the
|
|
184
|
+
// same node and, on its unmount, restore the adopt-time position — snapping
|
|
185
|
+
// the committed 2D move back to its start. Gate the 3D mover off for 2D moves
|
|
186
|
+
// (the scene writes the overlay makes still mirror into the 3D view). A
|
|
187
|
+
// 3D-initiated move leaves the origin null until its own commit, so this only
|
|
188
|
+
// suppresses the 3D tool for genuinely 2D-owned moves.
|
|
189
|
+
const showMover = movingNode != null && movingNodeOrigin !== '2d'
|
|
190
|
+
|
|
191
|
+
// Registry-first: if the active tool's kind has a NodeDefinition with a
|
|
192
|
+
// tool contribution, the registry-driven tool takes over.
|
|
193
|
+
const RegistryToolComponent = showBuildTool ? getRegistryTool(tool) : null
|
|
194
|
+
const useRegistryTool = RegistryToolComponent != null
|
|
195
|
+
|
|
196
|
+
const BuildToolComponent = showBuildTool && !useRegistryTool ? tools[phase]?.[tool] : null
|
|
130
197
|
const handlePlacedNodeSelected = (nodeId: AnyNodeId) => {
|
|
131
|
-
|
|
198
|
+
setSelection({ selectedIds: [nodeId] })
|
|
199
|
+
}
|
|
200
|
+
const handlePlacedElevatorSelected = (
|
|
201
|
+
nodeId: AnyNodeId,
|
|
202
|
+
elevatorBuildingId: BuildingNode['id'],
|
|
203
|
+
) => {
|
|
204
|
+
// Preserve the active level. `setSelection`'s hierarchy guard nulls
|
|
205
|
+
// `levelId` whenever `buildingId` is passed without an explicit
|
|
206
|
+
// `levelId` — which deselected the current floor plan the moment an
|
|
207
|
+
// elevator was placed. Pass the current level through so the floor
|
|
208
|
+
// plan stays selected.
|
|
209
|
+
setSelection({
|
|
210
|
+
buildingId: elevatorBuildingId,
|
|
211
|
+
levelId: activeLevelId ?? null,
|
|
212
|
+
selectedIds: [nodeId],
|
|
213
|
+
})
|
|
132
214
|
}
|
|
133
215
|
|
|
134
216
|
return (
|
|
135
217
|
<>
|
|
136
218
|
{/* World-space tools: site boundary and building movement operate in world coordinates */}
|
|
137
219
|
{showSiteBoundaryEditor && <SiteBoundaryEditor />}
|
|
138
|
-
{movingNode?.type === 'building' &&
|
|
220
|
+
{showMover && movingNode?.type === 'building' && (
|
|
221
|
+
<MoveTool onNodeMoved={handlePlacedNodeSelected} onSpawnMoved={handlePlacedNodeSelected} />
|
|
222
|
+
)}
|
|
139
223
|
|
|
140
224
|
{/* Building-local group: all other tools are relative to the selected building.
|
|
141
225
|
Cursor visuals set positions in building-local space; this group applies the
|
|
@@ -145,30 +229,123 @@ export const ToolManager: React.FC = () => {
|
|
|
145
229
|
rotation={buildingRotation as [number, number, number]}
|
|
146
230
|
>
|
|
147
231
|
{showZoneBoundaryEditor && selectedZoneId && <ZoneBoundaryEditor zoneId={selectedZoneId} />}
|
|
148
|
-
{showSlabBoundaryEditor &&
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
232
|
+
{showSlabBoundaryEditor &&
|
|
233
|
+
selectedSlabId &&
|
|
234
|
+
(() => {
|
|
235
|
+
const Registry = getRegistryAffordanceTool('slab', 'boundary-edit')
|
|
236
|
+
return Registry ? (
|
|
237
|
+
<Suspense fallback={null}>
|
|
238
|
+
<Registry slabId={selectedSlabId} />
|
|
239
|
+
</Suspense>
|
|
240
|
+
) : null
|
|
241
|
+
})()}
|
|
242
|
+
{showSlabHoleEditor &&
|
|
243
|
+
selectedSlabId &&
|
|
244
|
+
editingHole &&
|
|
245
|
+
(() => {
|
|
246
|
+
const Registry = getRegistryAffordanceTool('slab', 'hole-edit')
|
|
247
|
+
return Registry ? (
|
|
248
|
+
<Suspense fallback={null}>
|
|
249
|
+
<Registry holeIndex={editingHole.holeIndex} slabId={selectedSlabId} />
|
|
250
|
+
</Suspense>
|
|
251
|
+
) : null
|
|
252
|
+
})()}
|
|
253
|
+
{showCeilingBoundaryEditor &&
|
|
254
|
+
selectedCeilingId &&
|
|
255
|
+
(() => {
|
|
256
|
+
const Registry = getRegistryAffordanceTool('ceiling', 'boundary-edit')
|
|
257
|
+
return Registry ? (
|
|
258
|
+
<Suspense fallback={null}>
|
|
259
|
+
<Registry ceilingId={selectedCeilingId} />
|
|
260
|
+
</Suspense>
|
|
261
|
+
) : null
|
|
262
|
+
})()}
|
|
263
|
+
{showCeilingHoleEditor &&
|
|
264
|
+
selectedCeilingId &&
|
|
265
|
+
editingHole &&
|
|
266
|
+
(() => {
|
|
267
|
+
const Registry = getRegistryAffordanceTool('ceiling', 'hole-edit')
|
|
268
|
+
return Registry ? (
|
|
269
|
+
<Suspense fallback={null}>
|
|
270
|
+
<Registry ceilingId={selectedCeilingId} holeIndex={editingHole.holeIndex} />
|
|
271
|
+
</Suspense>
|
|
272
|
+
) : null
|
|
273
|
+
})()}
|
|
274
|
+
{endpointTarget &&
|
|
275
|
+
reshapingNode &&
|
|
276
|
+
(() => {
|
|
277
|
+
const RegistryAffordance = getRegistryAffordanceTool(
|
|
278
|
+
reshapingNode.type,
|
|
279
|
+
'move-endpoint',
|
|
280
|
+
)
|
|
281
|
+
return RegistryAffordance ? (
|
|
282
|
+
<Suspense fallback={null}>
|
|
283
|
+
<RegistryAffordance target={endpointTarget} />
|
|
284
|
+
</Suspense>
|
|
285
|
+
) : null
|
|
286
|
+
})()}
|
|
287
|
+
{isCurveReshape &&
|
|
288
|
+
reshapingNode &&
|
|
289
|
+
(() => {
|
|
290
|
+
const RegistryAffordance = getRegistryAffordanceTool(reshapingNode.type, 'curve')
|
|
291
|
+
return RegistryAffordance ? (
|
|
292
|
+
<Suspense fallback={null}>
|
|
293
|
+
<RegistryAffordance node={reshapingNode} />
|
|
294
|
+
</Suspense>
|
|
295
|
+
) : null
|
|
296
|
+
})()}
|
|
297
|
+
{controlPointTarget &&
|
|
298
|
+
(() => {
|
|
299
|
+
const RegistryAffordance = getRegistryAffordanceTool('fence', 'move-control-point')
|
|
300
|
+
return RegistryAffordance ? (
|
|
301
|
+
<Suspense fallback={null}>
|
|
302
|
+
<RegistryAffordance target={controlPointTarget} />
|
|
303
|
+
</Suspense>
|
|
304
|
+
) : null
|
|
305
|
+
})()}
|
|
306
|
+
{tangentTarget &&
|
|
307
|
+
(() => {
|
|
308
|
+
const RegistryAffordance = getRegistryAffordanceTool('fence', 'move-tangent')
|
|
309
|
+
return RegistryAffordance ? (
|
|
310
|
+
<Suspense fallback={null}>
|
|
311
|
+
<RegistryAffordance target={tangentTarget} />
|
|
312
|
+
</Suspense>
|
|
313
|
+
) : null
|
|
314
|
+
})()}
|
|
315
|
+
{showMover && movingNode.type !== 'building' && (
|
|
316
|
+
<MoveTool
|
|
317
|
+
onNodeMoved={handlePlacedNodeSelected}
|
|
318
|
+
onSpawnMoved={handlePlacedNodeSelected}
|
|
319
|
+
/>
|
|
154
320
|
)}
|
|
155
|
-
{
|
|
156
|
-
|
|
321
|
+
{/* Registry-first: when the active tool's kind has a registered
|
|
322
|
+
NodeDefinition with a tool contribution, mount it here. */}
|
|
323
|
+
{!movingNode && useRegistryTool && RegistryToolComponent && (
|
|
324
|
+
<Suspense fallback={null}>
|
|
325
|
+
<RegistryToolComponent />
|
|
326
|
+
</Suspense>
|
|
157
327
|
)}
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
328
|
+
{!movingNode && !useRegistryTool && showBuildTool && tool === 'elevator' && (
|
|
329
|
+
<ElevatorTool
|
|
330
|
+
buildingId={buildingId as BuildingNode['id'] | null}
|
|
331
|
+
levelId={activeLevelId ?? null}
|
|
332
|
+
onPlaced={handlePlacedElevatorSelected}
|
|
333
|
+
/>
|
|
164
334
|
)}
|
|
165
|
-
{!movingNode && BuildToolComponent && tool
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
335
|
+
{!movingNode && BuildToolComponent && tool !== 'elevator' ? <BuildToolComponent /> : null}
|
|
336
|
+
{/* Figma-style alignment guides published by the move / placement
|
|
337
|
+
tools above. Lives inside the building-local group so the
|
|
338
|
+
building-local guide coords render at the right world position. */}
|
|
339
|
+
<Alignment3DGuideLayer />
|
|
340
|
+
{/* The one forward-facing triangle renderer. Placement/move tools
|
|
341
|
+
publish their ghost pose to `useFacingPose`; this draws it. Mounted
|
|
342
|
+
here so it shares the building-local frame the tools publish in. */}
|
|
343
|
+
<FacingPoseIndicator />
|
|
344
|
+
{/* Wall-plane proximity / sill / equal-spacing guides for openings,
|
|
345
|
+
published by the door/window move tools in the same world frame. */}
|
|
346
|
+
<OpeningGuides3DLayer />
|
|
347
|
+
{/* "Magnetic" beacon at the active wall-draft snap point. */}
|
|
348
|
+
<WallSnapBeaconLayer />
|
|
172
349
|
</group>
|
|
173
350
|
</>
|
|
174
351
|
)
|