@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
|
@@ -9,12 +9,16 @@ import {
|
|
|
9
9
|
type SlabNode,
|
|
10
10
|
useScene,
|
|
11
11
|
type WallNode,
|
|
12
|
+
type ZoneNode,
|
|
12
13
|
} from '@pascal-app/core'
|
|
13
14
|
import { useViewer } from '@pascal-app/viewer'
|
|
14
15
|
import { type ComponentType, lazy, Suspense, useMemo } from 'react'
|
|
15
16
|
import { useRegisteredToolEnabled } from '../../hooks/use-registered-tool-enabled'
|
|
17
|
+
import { useCeilingEditCeilingId } from '../../lib/ceiling-edit-session'
|
|
18
|
+
import type { ReshapeKind } from '../../lib/interaction/scope'
|
|
19
|
+
import { paintRegionTargets, usePaintRegionMode } from '../../lib/paint-region-mode'
|
|
16
20
|
import { siteBoundaryHandlesEnabled } from '../../lib/site-boundary'
|
|
17
|
-
import useEditor, { type
|
|
21
|
+
import useEditor, { type Tool } from '../../store/use-editor'
|
|
18
22
|
import useInteractionScope, {
|
|
19
23
|
useControlPointReshape,
|
|
20
24
|
useEditingHole,
|
|
@@ -28,12 +32,19 @@ import useInteractionScope, {
|
|
|
28
32
|
} from '../../store/use-interaction-scope'
|
|
29
33
|
import { Alignment3DGuideLayer } from '../editor/alignment-3d-guide-layer'
|
|
30
34
|
import { Elevation3DGuideLayer } from '../editor/elevation-3d-guide-layer'
|
|
35
|
+
import { FloorOpeningsOverlay3D } from '../editor/floor-openings-overlay'
|
|
36
|
+
import { FloorRegionControls3D } from '../editor/floor-region-controls'
|
|
37
|
+
import { OpenWallEnds3DLayer } from '../editor/open-wall-ends-3d-layer'
|
|
31
38
|
import { OpeningGuides3DLayer } from '../editor/opening-guides-3d-layer'
|
|
32
39
|
import { WallSnapBeaconLayer } from '../editor/wall-snap-beacon-layer'
|
|
33
40
|
import { ElevatorTool } from './elevator/elevator-tool'
|
|
34
41
|
import { MoveTool } from './item/move-tool'
|
|
42
|
+
import { WallPaintRegionTool } from './paint-region/wall-paint-region-tool'
|
|
35
43
|
import { RegistryToolProvider } from './registry-tool-context'
|
|
36
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
getRegistryAffordanceTool,
|
|
46
|
+
preloadRegistryAffordanceTools,
|
|
47
|
+
} from './shared/affordance-dispatch'
|
|
37
48
|
import { FacingPoseIndicator } from './shared/facing-pose-indicator'
|
|
38
49
|
import { SiteBoundaryEditor } from './site/site-boundary-editor'
|
|
39
50
|
import { TerrainSculptTool } from './site/terrain-sculpt-tool'
|
|
@@ -44,6 +55,15 @@ import { ZoneTool } from './zone/zone-tool'
|
|
|
44
55
|
// Cache lazy tool components keyed by their loader so React.lazy isn't
|
|
45
56
|
// re-invoked across renders.
|
|
46
57
|
const lazyToolCache = new WeakMap<() => Promise<unknown>, ComponentType>()
|
|
58
|
+
// Reshapes with their own arm below; anything else resolves by name.
|
|
59
|
+
const DEDICATED_RESHAPES = new Set<ReshapeKind>([
|
|
60
|
+
'curve',
|
|
61
|
+
'hole',
|
|
62
|
+
'endpoint',
|
|
63
|
+
'boundary',
|
|
64
|
+
'control-point',
|
|
65
|
+
'tangent',
|
|
66
|
+
])
|
|
47
67
|
const registryToolPreloadCache = new WeakMap<AnyNodeDefinition, Promise<void>>()
|
|
48
68
|
|
|
49
69
|
export function preloadRegistryToolModules(tool: string | null): Promise<void> {
|
|
@@ -60,8 +80,7 @@ export function preloadRegistryToolModules(tool: string | null): Promise<void> {
|
|
|
60
80
|
if (def.system) loaders.push(def.system.module)
|
|
61
81
|
if (def.parametrics?.customPanel) loaders.push(def.parametrics.customPanel)
|
|
62
82
|
if (def.parametrics?.trailingSection) loaders.push(def.parametrics.trailingSection)
|
|
63
|
-
|
|
64
|
-
if (moveTool) loaders.push(moveTool)
|
|
83
|
+
loaders.push(() => preloadRegistryAffordanceTools(tool))
|
|
65
84
|
|
|
66
85
|
const preload = Promise.allSettled(loaders.map((loader) => loader())).then(() => undefined)
|
|
67
86
|
registryToolPreloadCache.set(def, preload)
|
|
@@ -88,21 +107,17 @@ function getRegistryTool(tool: Tool | null): ComponentType | null {
|
|
|
88
107
|
// Legacy tool fallbacks — kinds whose placement tools haven't migrated
|
|
89
108
|
// to `def.tool` yet. Wall / fence / slab / ceiling / door / window /
|
|
90
109
|
// item / shelf / spawn now go through the registry path above.
|
|
91
|
-
const tools:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
structure: {
|
|
96
|
-
stair: StairTool,
|
|
97
|
-
zone: ZoneTool,
|
|
98
|
-
},
|
|
99
|
-
furnish: {},
|
|
110
|
+
const tools: Partial<Record<Tool, React.FC>> = {
|
|
111
|
+
'property-line': SiteBoundaryEditor,
|
|
112
|
+
stair: StairTool,
|
|
113
|
+
zone: ZoneTool,
|
|
100
114
|
}
|
|
101
115
|
|
|
102
116
|
export const ToolManager: React.FC = () => {
|
|
103
117
|
const phase = useEditor((state) => state.phase)
|
|
104
118
|
const mode = useEditor((state) => state.mode)
|
|
105
119
|
const tool = useEditor((state) => state.tool)
|
|
120
|
+
const paintsWallRegions = usePaintRegionMode((state) => paintRegionTargets(state.mode).wall)
|
|
106
121
|
const registeredToolEnabled = useRegisteredToolEnabled(tool)
|
|
107
122
|
const movingNode = useMovingNode()
|
|
108
123
|
const registryToolOwnsPlacement = useInteractionScope(
|
|
@@ -114,6 +129,11 @@ export const ToolManager: React.FC = () => {
|
|
|
114
129
|
const tangentReshape = useTangentReshape()
|
|
115
130
|
const isCurveReshape = useIsCurveReshape()
|
|
116
131
|
const isToolDrivenReshape = useIsToolDrivenReshape()
|
|
132
|
+
const registryReshape = useInteractionScope((state) =>
|
|
133
|
+
state.scope.kind === 'reshaping' && !DEDICATED_RESHAPES.has(state.scope.reshape)
|
|
134
|
+
? state.scope.reshape
|
|
135
|
+
: null,
|
|
136
|
+
)
|
|
117
137
|
const isFloorplanDrivenReshape = useIsFloorplanDrivenReshape()
|
|
118
138
|
const reshapingNode = useReshapingNode()
|
|
119
139
|
// The endpoint affordance tool's `target` is kind-specific
|
|
@@ -140,7 +160,18 @@ export const ToolManager: React.FC = () => {
|
|
|
140
160
|
}
|
|
141
161
|
}, [reshapingNode, tangentReshape])
|
|
142
162
|
const editingHole = useEditingHole()
|
|
143
|
-
const
|
|
163
|
+
const editCeilingId = useCeilingEditCeilingId()
|
|
164
|
+
// A zone picked as a zone, from the Scene list (zoneId) or its area or pill (sole selection).
|
|
165
|
+
const zoneSelectionId = useViewer((state) => state.selection.zoneId)
|
|
166
|
+
const soleSelectedId = useViewer((state) =>
|
|
167
|
+
state.selection.selectedIds.length === 1 ? state.selection.selectedIds[0] : undefined,
|
|
168
|
+
)
|
|
169
|
+
const soleSelectedZone = useScene((state) =>
|
|
170
|
+
soleSelectedId && state.nodes[soleSelectedId as AnyNodeId]?.type === 'zone'
|
|
171
|
+
? (soleSelectedId as ZoneNode['id'])
|
|
172
|
+
: null,
|
|
173
|
+
)
|
|
174
|
+
const selectedZoneId = zoneSelectionId ?? soleSelectedZone
|
|
144
175
|
const selectedIds = useViewer((state) => state.selection.selectedIds)
|
|
145
176
|
const buildingId = useViewer((state) => state.selection.buildingId)
|
|
146
177
|
const activeLevelId = useViewer((state) => state.selection.levelId)
|
|
@@ -196,12 +227,14 @@ export const ToolManager: React.FC = () => {
|
|
|
196
227
|
// edge handles — mounts only for a sole selection.
|
|
197
228
|
const isSoleSelection = selectedIds.length === 1
|
|
198
229
|
|
|
199
|
-
// Show slab boundary editor
|
|
230
|
+
// Show slab boundary editor in select mode with a slab selected (but not editing a hole)
|
|
200
231
|
const showSlabBoundaryEditor =
|
|
201
|
-
phase === 'structure' &&
|
|
202
232
|
mode === 'select' &&
|
|
203
233
|
isSoleSelection &&
|
|
204
234
|
selectedSlabId !== undefined &&
|
|
235
|
+
// Derived plates (a footprint's floor, a room's raised plate) follow their
|
|
236
|
+
// rooms; only user-drawn slabs get the outline editor.
|
|
237
|
+
!selectedSlab?.plateRole &&
|
|
205
238
|
!isFloorplanDrivenReshape &&
|
|
206
239
|
!editingSlabHoleIsManual
|
|
207
240
|
|
|
@@ -213,26 +246,28 @@ export const ToolManager: React.FC = () => {
|
|
|
213
246
|
!isFloorplanDrivenReshape &&
|
|
214
247
|
editingSlabHoleIsManual
|
|
215
248
|
|
|
216
|
-
// Show ceiling boundary editor
|
|
249
|
+
// Show ceiling boundary editor only inside the ceiling's Edit ceiling session (not while editing a hole)
|
|
217
250
|
const showCeilingBoundaryEditor =
|
|
218
|
-
phase === 'structure' &&
|
|
219
251
|
mode === 'select' &&
|
|
220
252
|
isSoleSelection &&
|
|
221
253
|
selectedCeilingId !== undefined &&
|
|
254
|
+
selectedCeilingId === editCeilingId &&
|
|
222
255
|
!isFloorplanDrivenReshape &&
|
|
223
256
|
(!editingHole || editingHole.nodeId !== selectedCeilingId)
|
|
224
257
|
|
|
225
|
-
// Show ceiling hole editor when editing a hole
|
|
258
|
+
// Show ceiling hole editor when editing a manual hole inside the ceiling's Edit ceiling session
|
|
226
259
|
const showCeilingHoleEditor =
|
|
227
260
|
selectedCeilingId !== undefined &&
|
|
261
|
+
selectedCeilingId === editCeilingId &&
|
|
228
262
|
editingHole !== null &&
|
|
229
263
|
editingHole.nodeId === selectedCeilingId &&
|
|
264
|
+
((nodes[selectedCeilingId as AnyNodeId] as CeilingNode).holeMetadata?.[editingHole.holeIndex]
|
|
265
|
+
?.source ?? 'manual') === 'manual' &&
|
|
230
266
|
!isFloorplanDrivenReshape
|
|
231
267
|
|
|
232
|
-
// Show zone boundary editor
|
|
268
|
+
// Show zone boundary editor in select mode with a zone selected
|
|
233
269
|
// Hide when editing a slab or ceiling to avoid overlapping handles
|
|
234
270
|
const showZoneBoundaryEditor =
|
|
235
|
-
phase === 'structure' &&
|
|
236
271
|
mode === 'select' &&
|
|
237
272
|
selectedZoneId !== null &&
|
|
238
273
|
!isFloorplanDrivenReshape &&
|
|
@@ -257,7 +292,7 @@ export const ToolManager: React.FC = () => {
|
|
|
257
292
|
const RegistryToolComponent = showBuildTool ? getRegistryTool(tool) : null
|
|
258
293
|
const useRegistryTool = RegistryToolComponent != null
|
|
259
294
|
|
|
260
|
-
const BuildToolComponent = showBuildTool && !useRegistryTool ? tools[
|
|
295
|
+
const BuildToolComponent = showBuildTool && !useRegistryTool ? tools[tool] : null
|
|
261
296
|
const handlePlacedNodeSelected = (nodeId: AnyNodeId) => {
|
|
262
297
|
setSelection({ selectedIds: [nodeId] })
|
|
263
298
|
}
|
|
@@ -281,10 +316,16 @@ export const ToolManager: React.FC = () => {
|
|
|
281
316
|
<RegistryToolProvider value={registryToolContext}>
|
|
282
317
|
{/* World-space tools: site boundary and building movement operate in world coordinates */}
|
|
283
318
|
{showSiteBoundaryEditor && <SiteBoundaryEditor />}
|
|
284
|
-
{/* Terrain sculpting is a mode rather than a `tools[
|
|
319
|
+
{/* Terrain sculpting is a mode rather than a `tools[tool]` entry —
|
|
285
320
|
it places no node — so it gets its own gate here. World-space, because
|
|
286
321
|
the ground is not building-local. */}
|
|
287
322
|
{sculpting && <TerrainSculptTool />}
|
|
323
|
+
{/* "Paint part of the floor": level-space, follows the level matrix itself. */}
|
|
324
|
+
<FloorRegionControls3D />
|
|
325
|
+
{/* Floor and ceiling openings: level-space, follows the level matrix itself. */}
|
|
326
|
+
<FloorOpeningsOverlay3D />
|
|
327
|
+
{/* "Paint part of the wall": follows the wall mesh's own matrix. */}
|
|
328
|
+
{mode === 'material-paint' && paintsWallRegions && <WallPaintRegionTool />}
|
|
288
329
|
{showMover && movingNode?.type === 'building' && (
|
|
289
330
|
<MoveTool onNodeMoved={handlePlacedNodeSelected} onSpawnMoved={handlePlacedNodeSelected} />
|
|
290
331
|
)}
|
|
@@ -384,6 +425,22 @@ export const ToolManager: React.FC = () => {
|
|
|
384
425
|
</Suspense>
|
|
385
426
|
) : null
|
|
386
427
|
})()}
|
|
428
|
+
{/* Reshape kinds without an arm above mount the kind's own affordance tool
|
|
429
|
+
under the reshape's name (`def.affordanceTools[reshape]`, e.g. wall 'split'). */}
|
|
430
|
+
{isToolDrivenReshape &&
|
|
431
|
+
reshapingNode &&
|
|
432
|
+
registryReshape &&
|
|
433
|
+
(() => {
|
|
434
|
+
const RegistryAffordance = getRegistryAffordanceTool(
|
|
435
|
+
reshapingNode.type,
|
|
436
|
+
registryReshape,
|
|
437
|
+
)
|
|
438
|
+
return RegistryAffordance ? (
|
|
439
|
+
<Suspense fallback={null}>
|
|
440
|
+
<RegistryAffordance node={reshapingNode} />
|
|
441
|
+
</Suspense>
|
|
442
|
+
) : null
|
|
443
|
+
})()}
|
|
387
444
|
{showMover && movingNode.type !== 'building' && (
|
|
388
445
|
<MoveTool
|
|
389
446
|
onNodeMoved={handlePlacedNodeSelected}
|
|
@@ -421,6 +478,8 @@ export const ToolManager: React.FC = () => {
|
|
|
421
478
|
<Elevation3DGuideLayer />
|
|
422
479
|
{/* "Magnetic" beacon at the active wall-draft snap point. */}
|
|
423
480
|
<WallSnapBeaconLayer />
|
|
481
|
+
{/* Wall ends that aren't joined, with a one-click join — twin of the floor plan's. */}
|
|
482
|
+
<OpenWallEnds3DLayer />
|
|
424
483
|
</group>
|
|
425
484
|
</RegistryToolProvider>
|
|
426
485
|
)
|
|
@@ -22,6 +22,11 @@ import {
|
|
|
22
22
|
type WallNode,
|
|
23
23
|
WallNode as WallSchema,
|
|
24
24
|
} from '@pascal-app/core'
|
|
25
|
+
import {
|
|
26
|
+
migrateCeilingRoomLinks,
|
|
27
|
+
migrateFloorPlates,
|
|
28
|
+
migrateRoomZones,
|
|
29
|
+
} from '@pascal-app/core/scene-migrations'
|
|
25
30
|
import { useViewer } from '@pascal-app/viewer'
|
|
26
31
|
import useEditor from '../../../store/use-editor'
|
|
27
32
|
import useInteractionScope from '../../../store/use-interaction-scope'
|
|
@@ -721,7 +726,9 @@ describe('createWallOnCurrentLevel', () => {
|
|
|
721
726
|
expect(useScene.temporal.getState().pastStates.length - before).toBe(1)
|
|
722
727
|
})
|
|
723
728
|
|
|
724
|
-
test(
|
|
729
|
+
test.each([
|
|
730
|
+
0.05, 0.18,
|
|
731
|
+
])('a room divider preserves the footprint floor at %s and splits ceilings', (elevation) => {
|
|
725
732
|
const walls = [
|
|
726
733
|
makeWall([0, 0], [8, 0], 'wall_bottom'),
|
|
727
734
|
makeWall([8, 0], [8, 6], 'wall_right'),
|
|
@@ -737,7 +744,8 @@ describe('createWallOnCurrentLevel', () => {
|
|
|
737
744
|
[8, 6],
|
|
738
745
|
[0, 6],
|
|
739
746
|
],
|
|
740
|
-
elevation
|
|
747
|
+
elevation,
|
|
748
|
+
floorHeight: elevation,
|
|
741
749
|
thickness: 0.32,
|
|
742
750
|
autoFromWalls: true,
|
|
743
751
|
})
|
|
@@ -748,6 +756,11 @@ describe('createWallOnCurrentLevel', () => {
|
|
|
748
756
|
autoFromWalls: true,
|
|
749
757
|
})
|
|
750
758
|
seedLevel(walls, [slab, ceiling])
|
|
759
|
+
useScene.setState({
|
|
760
|
+
nodes: migrateFloorPlates(
|
|
761
|
+
migrateCeilingRoomLinks(migrateRoomZones(useScene.getState().nodes).nodes).nodes,
|
|
762
|
+
).nodes as Record<AnyNodeId, AnyNode>,
|
|
763
|
+
})
|
|
751
764
|
const stopDetection = initSpaceDetectionSync(useScene, useEditor)
|
|
752
765
|
|
|
753
766
|
try {
|
|
@@ -756,11 +769,13 @@ describe('createWallOnCurrentLevel', () => {
|
|
|
756
769
|
const nodes = Object.values(useScene.getState().nodes)
|
|
757
770
|
const slabs = nodes.filter((node) => node.type === 'slab')
|
|
758
771
|
const ceilings = nodes.filter((node) => node.type === 'ceiling')
|
|
759
|
-
expect(slabs).toHaveLength(
|
|
772
|
+
expect(slabs).toHaveLength(1)
|
|
773
|
+
expect(slabs[0]).toMatchObject({ plateRole: 'base', floorHeight: elevation })
|
|
774
|
+
expect(slabs[0]!.zoneIds).toHaveLength(2)
|
|
760
775
|
expect(ceilings).toHaveLength(2)
|
|
761
776
|
expect(
|
|
762
777
|
slabs.every(
|
|
763
|
-
(node) => node.autoFromWalls && node.elevation ===
|
|
778
|
+
(node) => node.autoFromWalls && node.elevation === elevation && node.thickness === 0.32,
|
|
764
779
|
),
|
|
765
780
|
).toBe(true)
|
|
766
781
|
expect(ceilings.every((node) => node.autoFromWalls)).toBe(true)
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
emitter,
|
|
4
4
|
type GridEvent,
|
|
5
5
|
type LevelNode,
|
|
6
|
+
newZone,
|
|
6
7
|
runAsSingleSceneHistoryStep,
|
|
7
8
|
snapPointAlongAngleRay,
|
|
8
9
|
useScene,
|
|
9
|
-
ZoneNode,
|
|
10
10
|
} from '@pascal-app/core'
|
|
11
11
|
import { useViewer } from '@pascal-app/viewer'
|
|
12
12
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from './../../../lib/surface-plan-snap'
|
|
20
20
|
import { focusedUnitNode } from './../../../lib/units'
|
|
21
21
|
import { snapWorldXZForActiveBuilding } from './../../../lib/world-grid-snap'
|
|
22
|
+
import { nextZoneName } from '../../../lib/zone-name'
|
|
22
23
|
import useEditor, { isAngleSnapActive, isGridSnapActive } from './../../../store/use-editor'
|
|
23
24
|
import { useFloorplanDraftPreview } from './../../../store/use-floorplan-draft-preview'
|
|
24
25
|
import { CursorSphere } from '../shared/cursor-sphere'
|
|
@@ -31,18 +32,9 @@ const Y_OFFSET = 0.02
|
|
|
31
32
|
const commitZoneDrawing = (levelId: LevelNode['id'], points: Array<[number, number]>) => {
|
|
32
33
|
const { createNode, updateNode, nodes } = useScene.getState()
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
const zoneCount = Object.values(nodes).filter((n) => n.type === 'zone').length
|
|
36
|
-
const name = `Zone ${zoneCount + 1}`
|
|
35
|
+
const name = nextZoneName(nodes)
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
const color = '#3b82f6'
|
|
40
|
-
|
|
41
|
-
const zone = ZoneNode.parse({
|
|
42
|
-
name,
|
|
43
|
-
polygon: points,
|
|
44
|
-
color,
|
|
45
|
-
})
|
|
37
|
+
const zone = newZone({ name, polygon: points })
|
|
46
38
|
|
|
47
39
|
const focusedUnit = focusedUnitNode()
|
|
48
40
|
|
|
@@ -36,6 +36,9 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp
|
|
|
36
36
|
<Tooltip>
|
|
37
37
|
<TooltipTrigger asChild>
|
|
38
38
|
<Button
|
|
39
|
+
// These are toggles, not commands: the armed/unarmed state has to be
|
|
40
|
+
// readable without decoding the active-state styling.
|
|
41
|
+
aria-pressed={isActive}
|
|
39
42
|
className={cn('relative h-11 w-11 transition-all', className)}
|
|
40
43
|
onClick={(event) => {
|
|
41
44
|
triggerSFX('sfx:menu-click')
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
import { emitter } from '@pascal-app/core'
|
|
4
4
|
import Image from 'next/image'
|
|
5
5
|
import useEditor from '../../../store/use-editor'
|
|
6
|
+
import { visibleScene } from '../../../store/view-layout'
|
|
6
7
|
import { ActionButton } from './action-button'
|
|
7
8
|
|
|
8
9
|
export function CameraActions({ hideOrbit = false }: { hideOrbit?: boolean }) {
|
|
9
10
|
// Orbit stays useful in 2D-only (it spins the synced floorplan view), but
|
|
10
11
|
// top view only tilts the hidden 3D camera — pointless without the canvas.
|
|
11
|
-
const is2dOnly = useEditor((s) => s
|
|
12
|
+
const is2dOnly = useEditor((s) => visibleScene(s) === '2d')
|
|
12
13
|
|
|
13
14
|
const goToTopView = () => {
|
|
14
15
|
emitter.emit('camera-controls:top-view')
|
|
@@ -9,7 +9,7 @@ import useEditor from './../../../store/use-editor'
|
|
|
9
9
|
import { ActionButton } from './action-button'
|
|
10
10
|
import { MeasurementControl } from './measurement-control'
|
|
11
11
|
|
|
12
|
-
type ControlId = 'select' | 'box-select' | '
|
|
12
|
+
type ControlId = 'select' | 'box-select' | 'delete'
|
|
13
13
|
|
|
14
14
|
type ControlConfig = {
|
|
15
15
|
id: ControlId
|
|
@@ -32,14 +32,6 @@ const controls: ControlConfig[] = [
|
|
|
32
32
|
color: 'hover:bg-blue-500/20 hover:text-blue-400',
|
|
33
33
|
activeColor: 'bg-blue-500/20 text-blue-400',
|
|
34
34
|
},
|
|
35
|
-
{
|
|
36
|
-
id: 'zone',
|
|
37
|
-
imageSrc: '/icons/zone.webp',
|
|
38
|
-
label: 'Zone',
|
|
39
|
-
shortcut: 'Z',
|
|
40
|
-
color: 'hover:bg-green-500/20 hover:text-green-400',
|
|
41
|
-
activeColor: 'bg-green-500/20 text-green-400',
|
|
42
|
-
},
|
|
43
35
|
{
|
|
44
36
|
id: 'delete',
|
|
45
37
|
icon: Trash2,
|
|
@@ -52,49 +44,23 @@ const controls: ControlConfig[] = [
|
|
|
52
44
|
|
|
53
45
|
export function ControlModes() {
|
|
54
46
|
const mode = useEditor((state) => state.mode)
|
|
55
|
-
const phase = useEditor((state) => state.phase)
|
|
56
47
|
const selectionTool = useEditor((state) => state.floorplanSelectionTool)
|
|
57
48
|
const armToolMode = useEditor((state) => state.armToolMode)
|
|
58
|
-
const setPhase = useEditor((state) => state.setPhase)
|
|
59
|
-
const setStructureLayer = useEditor((state) => state.setStructureLayer)
|
|
60
49
|
const setSelectionTool = useEditor((state) => state.setFloorplanSelectionTool)
|
|
61
50
|
|
|
62
|
-
const isSiteEditing = phase === 'site'
|
|
63
|
-
|
|
64
|
-
const structureLayer = useEditor((state) => state.structureLayer)
|
|
65
|
-
|
|
66
51
|
const getIsActive = (id: ControlId): boolean => {
|
|
67
52
|
if (id === 'select') return mode === 'select' && selectionTool === 'click'
|
|
68
53
|
if (id === 'box-select') return mode === 'select' && selectionTool === 'marquee'
|
|
69
|
-
if (id === 'zone')
|
|
70
|
-
return mode === 'build' && phase === 'structure' && structureLayer === 'zones'
|
|
71
54
|
return mode === id
|
|
72
55
|
}
|
|
73
56
|
|
|
74
57
|
const handleClick = (id: ControlId) => {
|
|
75
|
-
// Exit site editing first if needed. Sculpting is a site-phase mode, so
|
|
76
|
-
// leaving the phase is exactly the right way to leave the brush — but the
|
|
77
|
-
// order matters: `setPhase` resets the mode, and setting the mode first
|
|
78
|
-
// would have it overwritten below.
|
|
79
|
-
if (isSiteEditing) {
|
|
80
|
-
setPhase('structure')
|
|
81
|
-
setStructureLayer('elements')
|
|
82
|
-
}
|
|
83
|
-
|
|
84
58
|
if (id === 'select') {
|
|
85
59
|
armToolMode({ mode: 'select' })
|
|
86
60
|
setSelectionTool('click')
|
|
87
61
|
} else if (id === 'box-select') {
|
|
88
62
|
armToolMode({ mode: 'select' })
|
|
89
63
|
setSelectionTool('marquee')
|
|
90
|
-
} else if (id === 'zone') {
|
|
91
|
-
if (getIsActive('zone')) {
|
|
92
|
-
armToolMode({ mode: 'select' })
|
|
93
|
-
} else {
|
|
94
|
-
setPhase('structure')
|
|
95
|
-
setStructureLayer('zones')
|
|
96
|
-
armToolMode({ mode: 'build', tool: 'zone' })
|
|
97
|
-
}
|
|
98
64
|
} else {
|
|
99
65
|
armToolMode({ mode: id })
|
|
100
66
|
}
|
|
@@ -121,6 +87,7 @@ export function ControlModes() {
|
|
|
121
87
|
// A static hook for a host app that wants to point a first-run
|
|
122
88
|
// tour at this button. Nothing here reads it.
|
|
123
89
|
data-guide-target={c.id === 'select' ? 'mode-select' : undefined}
|
|
90
|
+
isActive={isActive}
|
|
124
91
|
label={c.label}
|
|
125
92
|
onClick={() => handleClick(c.id)}
|
|
126
93
|
shortcut={c.shortcut}
|
|
@@ -22,6 +22,7 @@ import { useState } from 'react'
|
|
|
22
22
|
import type { CreatableMeasurementKind } from '../../../lib/measurement-kind'
|
|
23
23
|
import { cn } from '../../../lib/utils'
|
|
24
24
|
import useEditor from '../../../store/use-editor'
|
|
25
|
+
import { VIEW_2D } from '../../../store/view-layout'
|
|
25
26
|
import useFloorplanMode from '../../../store/use-floorplan-mode'
|
|
26
27
|
import { Popover, PopoverContent, PopoverTrigger } from '../primitives/popover'
|
|
27
28
|
import { ActionButton } from './action-button'
|
|
@@ -74,12 +75,10 @@ export function MeasurementControl() {
|
|
|
74
75
|
(state) => state.toolDefaults['construction-dimension']?.mode,
|
|
75
76
|
)
|
|
76
77
|
const setMode = useEditor((state) => state.setMode)
|
|
77
|
-
const setPhase = useEditor((state) => state.setPhase)
|
|
78
78
|
const setLastMeasurementKind = useEditor((state) => state.setLastMeasurementKind)
|
|
79
|
-
const setStructureLayer = useEditor((state) => state.setStructureLayer)
|
|
80
79
|
const setTool = useEditor((state) => state.setTool)
|
|
81
80
|
const setToolDefaults = useEditor((state) => state.setToolDefaults)
|
|
82
|
-
const
|
|
81
|
+
const showView = useEditor((state) => state.showView)
|
|
83
82
|
|
|
84
83
|
const selectedOption =
|
|
85
84
|
measurementOptions.find((option) => option.kind === selectedKind) ?? measurementOptions[0]
|
|
@@ -104,8 +103,6 @@ export function MeasurementControl() {
|
|
|
104
103
|
: selectedOption.label
|
|
105
104
|
|
|
106
105
|
const activateMeasurement = (kind: CreatableMeasurementKind) => {
|
|
107
|
-
setPhase('structure')
|
|
108
|
-
setStructureLayer('elements')
|
|
109
106
|
setLastMeasurementKind(kind)
|
|
110
107
|
setToolDefaults('measurement', { kind })
|
|
111
108
|
setMode('build')
|
|
@@ -121,8 +118,6 @@ export function MeasurementControl() {
|
|
|
121
118
|
}
|
|
122
119
|
|
|
123
120
|
const activateSmartMeasurement = () => {
|
|
124
|
-
setPhase('structure')
|
|
125
|
-
setStructureLayer('elements')
|
|
126
121
|
setToolDefaults('measurement', { kind: 'smart' })
|
|
127
122
|
setMode('build')
|
|
128
123
|
setTool('measurement')
|
|
@@ -136,9 +131,7 @@ export function MeasurementControl() {
|
|
|
136
131
|
useFloorplanMode.getState().showExpertModeNotice('Construction Dimension')
|
|
137
132
|
return
|
|
138
133
|
}
|
|
139
|
-
|
|
140
|
-
setStructureLayer('elements')
|
|
141
|
-
setViewMode('2d')
|
|
134
|
+
showView(VIEW_2D)
|
|
142
135
|
setToolDefaults('construction-dimension', { chainMode, mode: dimensionMode })
|
|
143
136
|
setMode('build')
|
|
144
137
|
setTool('construction-dimension')
|