@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
|
@@ -19,7 +19,9 @@ import {
|
|
|
19
19
|
Copy,
|
|
20
20
|
Eye,
|
|
21
21
|
EyeOff,
|
|
22
|
+
Boxes,
|
|
22
23
|
Group,
|
|
24
|
+
LayoutGrid,
|
|
23
25
|
Loader2,
|
|
24
26
|
MoreHorizontal,
|
|
25
27
|
Pencil,
|
|
@@ -29,7 +31,18 @@ import {
|
|
|
29
31
|
X,
|
|
30
32
|
} from 'lucide-react'
|
|
31
33
|
import { AnimatePresence, LayoutGroup, motion } from 'motion/react'
|
|
32
|
-
import { memo, useEffect, useRef, useState, useSyncExternalStore } from 'react'
|
|
34
|
+
import { type ComponentType, lazy, memo, Suspense, useEffect, useRef, useState, useSyncExternalStore } from 'react'
|
|
35
|
+
import { nodeRegistry } from '@pascal-app/core'
|
|
36
|
+
|
|
37
|
+
/** The site kind's inspector (`parametrics.customPanel`), mounted under the Site header. */
|
|
38
|
+
const SiteKindPanel = lazy(async () => {
|
|
39
|
+
const def = nodeRegistry.get('site') as
|
|
40
|
+
| { parametrics?: { customPanel?: () => Promise<{ default: ComponentType }> } }
|
|
41
|
+
| undefined
|
|
42
|
+
const loader = def?.parametrics?.customPanel
|
|
43
|
+
if (!loader) return { default: () => null }
|
|
44
|
+
return loader()
|
|
45
|
+
})
|
|
33
46
|
import { useShallow } from 'zustand/react/shallow'
|
|
34
47
|
import { ColorDot } from './../../../../../components/ui/primitives/color-dot'
|
|
35
48
|
import {
|
|
@@ -40,8 +53,8 @@ import {
|
|
|
40
53
|
import {
|
|
41
54
|
buildLevelDuplicateCreateOps,
|
|
42
55
|
type LevelDuplicatePreset,
|
|
43
|
-
} from '
|
|
44
|
-
import { getDefaultLevelName } from '@pascal-app/core'
|
|
56
|
+
} from '@pascal-app/core/building'
|
|
57
|
+
import { getDefaultLevelName, getLevelDisplayName } from '@pascal-app/core'
|
|
45
58
|
import { deleteLevelWithFallbackSelection } from './../../../../../lib/level-selection'
|
|
46
59
|
import {
|
|
47
60
|
formatAreaLabel,
|
|
@@ -53,12 +66,19 @@ import {
|
|
|
53
66
|
} from './../../../../../lib/measurements'
|
|
54
67
|
import { createLocalGuideImage, createLocalScan } from './../../../../../lib/local-guide-image'
|
|
55
68
|
import { editorHostTreeChildrenRegistry } from './../../../../../lib/host-tree-children'
|
|
69
|
+
import { requestRoomDeletion } from './../../../../../lib/room-structure-commands'
|
|
70
|
+
import {
|
|
71
|
+
selectZoneOrRoom,
|
|
72
|
+
useZoneSelected,
|
|
73
|
+
zoneKindLabel,
|
|
74
|
+
} from './../../../../../lib/room-zone-routing'
|
|
56
75
|
import { createUnitInBuilding, toggleZoneMembership } from './../../../../../lib/units'
|
|
57
76
|
import { cn } from './../../../../../lib/utils'
|
|
58
77
|
import useEditor from './../../../../../store/use-editor'
|
|
59
78
|
import { useUploadStore } from '../../../../../store/use-upload'
|
|
60
79
|
import { MetricControl } from '../../../controls/metric-control'
|
|
61
80
|
import { LevelDuplicateDialog } from '../../../level-duplicate-dialog'
|
|
81
|
+
import { CollectionsSection } from './collection-tree-node'
|
|
62
82
|
import { InlineRenameInput } from './inline-rename-input'
|
|
63
83
|
import { ZoneMembershipCheckbox } from './zone-membership-checkbox'
|
|
64
84
|
import { focusTreeNode, TreeNode, TreeNodeWrapper } from './tree-node'
|
|
@@ -719,8 +739,7 @@ const LevelItem = memo(function LevelItem({
|
|
|
719
739
|
const [cameraPopoverOpen, setCameraPopoverOpen] = useState(false)
|
|
720
740
|
const [duplicateDialogOpen, setDuplicateDialogOpen] = useState(false)
|
|
721
741
|
const [isEditing, setIsEditing] = useState(false)
|
|
722
|
-
const
|
|
723
|
-
const updateNodes = useScene((s) => s.updateNodes)
|
|
742
|
+
const applyNodeChanges = useScene((s) => s.applyNodeChanges)
|
|
724
743
|
const itemRef = useRef<HTMLDivElement>(null)
|
|
725
744
|
const isSelected = selectedLevelId === level.id
|
|
726
745
|
const canDeleteLevel = level.level !== 0
|
|
@@ -754,22 +773,14 @@ const LevelItem = memo(function LevelItem({
|
|
|
754
773
|
}
|
|
755
774
|
|
|
756
775
|
const handleDuplicateLevel = (preset: LevelDuplicatePreset = 'everything') => {
|
|
757
|
-
const { createOps, newLevelId,
|
|
776
|
+
const { createOps, newLevelId, updateOps } = buildLevelDuplicateCreateOps({
|
|
758
777
|
nodes: useScene.getState().nodes,
|
|
759
778
|
level,
|
|
760
779
|
levels,
|
|
761
780
|
preset,
|
|
762
781
|
})
|
|
763
782
|
|
|
764
|
-
|
|
765
|
-
updateNodes(
|
|
766
|
-
shiftedLevels.map((shiftedLevel) => ({
|
|
767
|
-
id: shiftedLevel.id as AnyNodeId,
|
|
768
|
-
data: { level: shiftedLevel.level } as Partial<AnyNode>,
|
|
769
|
-
})),
|
|
770
|
-
)
|
|
771
|
-
}
|
|
772
|
-
createNodes(createOps)
|
|
783
|
+
applyNodeChanges({ create: createOps, update: updateOps })
|
|
773
784
|
selectLevel(newLevelId as LevelNode['id'], false)
|
|
774
785
|
setDuplicateDialogOpen(false)
|
|
775
786
|
}
|
|
@@ -838,6 +849,7 @@ const LevelItem = memo(function LevelItem({
|
|
|
838
849
|
/>
|
|
839
850
|
<InlineRenameInput
|
|
840
851
|
defaultName={getDefaultLevelName(level.level)}
|
|
852
|
+
displayName={getLevelDisplayName(level)}
|
|
841
853
|
isEditing={isEditing}
|
|
842
854
|
nodeId={level.id}
|
|
843
855
|
onStartEditing={() => setIsEditing(true)}
|
|
@@ -1149,137 +1161,15 @@ const UnitsSection = memo(function UnitsSection({
|
|
|
1149
1161
|
)
|
|
1150
1162
|
})
|
|
1151
1163
|
|
|
1152
|
-
const
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
: phase === 'furnish'
|
|
1162
|
-
? 'furnish'
|
|
1163
|
-
: phase === 'structure' && structureLayer === 'zones'
|
|
1164
|
-
? 'zones'
|
|
1165
|
-
: 'none'
|
|
1166
|
-
|
|
1167
|
-
return (
|
|
1168
|
-
<div className="relative flex items-center gap-1 border-border/50 border-b bg-[#2C2C2E] p-1">
|
|
1169
|
-
<button
|
|
1170
|
-
className={cn(
|
|
1171
|
-
'relative flex flex-1 cursor-pointer flex-col items-center justify-center rounded-md py-2 font-medium text-[10px] transition-all duration-200',
|
|
1172
|
-
activeTab === 'structure'
|
|
1173
|
-
? 'text-foreground'
|
|
1174
|
-
: 'text-muted-foreground hover:bg-white/5 hover:text-foreground',
|
|
1175
|
-
)}
|
|
1176
|
-
onClick={() => {
|
|
1177
|
-
setPhase('structure')
|
|
1178
|
-
setStructureLayer('elements')
|
|
1179
|
-
}}
|
|
1180
|
-
>
|
|
1181
|
-
{activeTab === 'structure' && (
|
|
1182
|
-
<motion.div
|
|
1183
|
-
className="absolute inset-0 rounded-md bg-[#3e3e3e] shadow-sm ring-1 ring-border/50"
|
|
1184
|
-
layoutId="layerToggleActiveBg"
|
|
1185
|
-
transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }}
|
|
1186
|
-
/>
|
|
1187
|
-
)}
|
|
1188
|
-
<div className="relative z-10 flex flex-col items-center">
|
|
1189
|
-
<img
|
|
1190
|
-
alt="Structure"
|
|
1191
|
-
className={cn(
|
|
1192
|
-
'mb-1 h-6 w-6 transition-all',
|
|
1193
|
-
activeTab !== 'structure' && 'opacity-50 grayscale',
|
|
1194
|
-
)}
|
|
1195
|
-
src="/icons/room.webp"
|
|
1196
|
-
/>
|
|
1197
|
-
Structure
|
|
1198
|
-
</div>
|
|
1199
|
-
<div className="absolute right-1.5 bottom-1 z-10 rounded border border-border/40 bg-background/40 px-1 py-[2px] backdrop-blur-md">
|
|
1200
|
-
<span className="block font-medium font-mono text-[9px] text-muted-foreground/70 leading-none">
|
|
1201
|
-
B
|
|
1202
|
-
</span>
|
|
1203
|
-
</div>
|
|
1204
|
-
</button>
|
|
1205
|
-
|
|
1206
|
-
<button
|
|
1207
|
-
className={cn(
|
|
1208
|
-
'relative flex flex-1 cursor-pointer flex-col items-center justify-center rounded-md py-2 font-medium text-[10px] transition-all duration-200',
|
|
1209
|
-
activeTab === 'furnish'
|
|
1210
|
-
? 'text-foreground'
|
|
1211
|
-
: 'text-muted-foreground hover:bg-white/5 hover:text-foreground',
|
|
1212
|
-
)}
|
|
1213
|
-
onClick={() => {
|
|
1214
|
-
setPhase('furnish')
|
|
1215
|
-
}}
|
|
1216
|
-
>
|
|
1217
|
-
{activeTab === 'furnish' && (
|
|
1218
|
-
<motion.div
|
|
1219
|
-
className="absolute inset-0 rounded-md bg-[#3e3e3e] shadow-sm ring-1 ring-border/50"
|
|
1220
|
-
layoutId="layerToggleActiveBg"
|
|
1221
|
-
transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }}
|
|
1222
|
-
/>
|
|
1223
|
-
)}
|
|
1224
|
-
<div className="relative z-10 flex flex-col items-center">
|
|
1225
|
-
<img
|
|
1226
|
-
alt="Furnish"
|
|
1227
|
-
className={cn(
|
|
1228
|
-
'mb-1 h-6 w-6 transition-all',
|
|
1229
|
-
activeTab !== 'furnish' && 'opacity-50 grayscale',
|
|
1230
|
-
)}
|
|
1231
|
-
src="/icons/couch.webp"
|
|
1232
|
-
/>
|
|
1233
|
-
Furnish
|
|
1234
|
-
</div>
|
|
1235
|
-
<div className="absolute right-1.5 bottom-1 z-10 rounded border border-border/40 bg-background/40 px-1 py-[2px] backdrop-blur-md">
|
|
1236
|
-
<span className="block font-medium font-mono text-[9px] text-muted-foreground/70 leading-none">
|
|
1237
|
-
F
|
|
1238
|
-
</span>
|
|
1239
|
-
</div>
|
|
1240
|
-
</button>
|
|
1241
|
-
|
|
1242
|
-
<button
|
|
1243
|
-
className={cn(
|
|
1244
|
-
'relative flex flex-1 cursor-pointer flex-col items-center justify-center rounded-md py-2 font-medium text-[10px] transition-all duration-200',
|
|
1245
|
-
activeTab === 'zones'
|
|
1246
|
-
? 'text-foreground'
|
|
1247
|
-
: 'text-muted-foreground hover:bg-white/5 hover:text-foreground',
|
|
1248
|
-
)}
|
|
1249
|
-
onClick={() => {
|
|
1250
|
-
setPhase('structure')
|
|
1251
|
-
setStructureLayer('zones')
|
|
1252
|
-
}}
|
|
1253
|
-
>
|
|
1254
|
-
{activeTab === 'zones' && (
|
|
1255
|
-
<motion.div
|
|
1256
|
-
className="absolute inset-0 rounded-md bg-[#3e3e3e] shadow-sm ring-1 ring-border/50"
|
|
1257
|
-
layoutId="layerToggleActiveBg"
|
|
1258
|
-
transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }}
|
|
1259
|
-
/>
|
|
1260
|
-
)}
|
|
1261
|
-
<div className="relative z-10 flex flex-col items-center">
|
|
1262
|
-
<img
|
|
1263
|
-
alt="Zones"
|
|
1264
|
-
className={cn(
|
|
1265
|
-
'mb-1 h-6 w-6 transition-all',
|
|
1266
|
-
activeTab !== 'zones' && 'opacity-50 grayscale',
|
|
1267
|
-
)}
|
|
1268
|
-
src="/icons/kitchen.webp"
|
|
1269
|
-
/>
|
|
1270
|
-
Zones
|
|
1271
|
-
</div>
|
|
1272
|
-
<div className="absolute right-1.5 bottom-1 z-10 rounded border border-border/40 bg-background/40 px-1 py-[2px] backdrop-blur-md">
|
|
1273
|
-
<span className="block font-medium font-mono text-[9px] text-muted-foreground/70 leading-none">
|
|
1274
|
-
Z
|
|
1275
|
-
</span>
|
|
1276
|
-
</div>
|
|
1277
|
-
</button>
|
|
1278
|
-
</div>
|
|
1279
|
-
)
|
|
1280
|
-
})
|
|
1281
|
-
|
|
1282
|
-
const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLast?: boolean }) {
|
|
1164
|
+
const ZoneItem = memo(function ZoneItem({
|
|
1165
|
+
zone,
|
|
1166
|
+
depth,
|
|
1167
|
+
isLast,
|
|
1168
|
+
}: {
|
|
1169
|
+
zone: ZoneNode
|
|
1170
|
+
depth: number
|
|
1171
|
+
isLast?: boolean
|
|
1172
|
+
}) {
|
|
1283
1173
|
const [isEditing, setIsEditing] = useState(false)
|
|
1284
1174
|
const [cameraPopoverOpen, setCameraPopoverOpen] = useState(false)
|
|
1285
1175
|
const deleteNode = useScene((state) => state.deleteNode)
|
|
@@ -1289,15 +1179,14 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
|
|
|
1289
1179
|
const unit = focusedUnitId ? s.nodes[focusedUnitId] : undefined
|
|
1290
1180
|
return unit?.type === 'unit' ? unit : null
|
|
1291
1181
|
})
|
|
1292
|
-
const selectedZoneId = useViewer((state) => state.selection.zoneId)
|
|
1293
1182
|
const hoveredId = useViewer((state) => state.hoveredId)
|
|
1294
1183
|
const setSelection = useViewer((state) => state.setSelection)
|
|
1295
1184
|
const setHoveredId = useViewer((state) => state.setHoveredId)
|
|
1296
|
-
const setPhase = useEditor((state) => state.setPhase)
|
|
1297
1185
|
const setMode = useEditor((state) => state.setMode)
|
|
1298
1186
|
const unit = useViewer((state) => state.unit)
|
|
1299
1187
|
|
|
1300
|
-
const isSelected =
|
|
1188
|
+
const isSelected = useZoneSelected(zone.id)
|
|
1189
|
+
const isRoom = zone.spaceRole === 'room'
|
|
1301
1190
|
const isHovered = hoveredId === zone.id
|
|
1302
1191
|
|
|
1303
1192
|
const itemRef = useRef<HTMLDivElement>(null)
|
|
@@ -1308,12 +1197,11 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
|
|
|
1308
1197
|
}
|
|
1309
1198
|
}, [isSelected])
|
|
1310
1199
|
|
|
1311
|
-
const defaultName =
|
|
1200
|
+
const defaultName = `${zoneKindLabel(zone)} (${formatAreaLabel(calculatePolygonArea(zone.polygon), unit)})`
|
|
1312
1201
|
|
|
1313
1202
|
const handleClick = () => {
|
|
1314
|
-
setSelection({ zoneId: zone.id })
|
|
1315
|
-
setPhase('structure')
|
|
1316
1203
|
setMode('select')
|
|
1204
|
+
selectZoneOrRoom(zone.id)
|
|
1317
1205
|
}
|
|
1318
1206
|
|
|
1319
1207
|
const handleDoubleClick = () => {
|
|
@@ -1322,6 +1210,8 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
|
|
|
1322
1210
|
|
|
1323
1211
|
const handleDelete = (e: React.MouseEvent) => {
|
|
1324
1212
|
e.stopPropagation()
|
|
1213
|
+
// A room goes the way the room panel deletes it (its walls, or merged back).
|
|
1214
|
+
if (isRoom) return requestRoomDeletion(zone.id)
|
|
1325
1215
|
deleteNode(zone.id)
|
|
1326
1216
|
if (isSelected) {
|
|
1327
1217
|
setSelection({ zoneId: null })
|
|
@@ -1335,7 +1225,7 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
|
|
|
1335
1225
|
return (
|
|
1336
1226
|
<div
|
|
1337
1227
|
className={cn(
|
|
1338
|
-
'group/row relative flex h-8 cursor-pointer select-none items-center border-border/50 border-b
|
|
1228
|
+
'group/row relative flex h-8 cursor-pointer select-none items-center border-border/50 border-b text-sm transition-all duration-200',
|
|
1339
1229
|
isSelected
|
|
1340
1230
|
? 'bg-accent/50 text-foreground'
|
|
1341
1231
|
: isHovered
|
|
@@ -1347,6 +1237,7 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
|
|
|
1347
1237
|
onMouseEnter={() => setHoveredId(zone.id)}
|
|
1348
1238
|
onMouseLeave={() => setHoveredId(null)}
|
|
1349
1239
|
ref={itemRef}
|
|
1240
|
+
style={{ paddingLeft: depth * 12 + 28, paddingRight: 12 }}
|
|
1350
1241
|
>
|
|
1351
1242
|
{/* Vertical tree line */}
|
|
1352
1243
|
<div
|
|
@@ -1354,12 +1245,12 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
|
|
|
1354
1245
|
'pointer-events-none absolute w-px bg-border/50',
|
|
1355
1246
|
isLast ? 'top-0 bottom-1/2' : 'top-0 bottom-0',
|
|
1356
1247
|
)}
|
|
1357
|
-
style={{ left:
|
|
1248
|
+
style={{ left: (depth - 1) * 12 + 20 }}
|
|
1358
1249
|
/>
|
|
1359
1250
|
{/* Horizontal branch line */}
|
|
1360
1251
|
<div
|
|
1361
1252
|
className="pointer-events-none absolute top-1/2 h-px bg-border/50"
|
|
1362
|
-
style={{ left:
|
|
1253
|
+
style={{ left: (depth - 1) * 12 + 20, width: 4 }}
|
|
1363
1254
|
/>
|
|
1364
1255
|
|
|
1365
1256
|
{focusedUnit && (
|
|
@@ -1476,17 +1367,9 @@ const MultiSelectionBadge = memo(function MultiSelectionBadge() {
|
|
|
1476
1367
|
)
|
|
1477
1368
|
})
|
|
1478
1369
|
|
|
1479
|
-
const
|
|
1370
|
+
const RoomsSection = memo(function RoomsSection() {
|
|
1480
1371
|
const selectedLevelId = useViewer((state) => state.selection.levelId)
|
|
1481
|
-
const
|
|
1482
|
-
const phase = useEditor((state) => state.phase)
|
|
1483
|
-
const setPhase = useEditor((state) => state.setPhase)
|
|
1484
|
-
const setMode = useEditor((state) => state.setMode)
|
|
1485
|
-
const setTool = useEditor((state) => state.setTool)
|
|
1486
|
-
|
|
1487
|
-
const level = useScene((s) =>
|
|
1488
|
-
selectedLevelId ? ((s.nodes[selectedLevelId] as LevelNode | undefined) ?? null) : null,
|
|
1489
|
-
)
|
|
1372
|
+
const armToolMode = useEditor((state) => state.armToolMode)
|
|
1490
1373
|
const levelZones = useScene(
|
|
1491
1374
|
useShallow((s) => {
|
|
1492
1375
|
if (!selectedLevelId) return []
|
|
@@ -1495,6 +1378,82 @@ const ContentSection = memo(function ContentSection() {
|
|
|
1495
1378
|
)
|
|
1496
1379
|
}),
|
|
1497
1380
|
)
|
|
1381
|
+
const [expanded, setExpanded] = useState(true)
|
|
1382
|
+
|
|
1383
|
+
if (!selectedLevelId) return null
|
|
1384
|
+
|
|
1385
|
+
const handleDrawZone = (event: React.MouseEvent) => {
|
|
1386
|
+
event.stopPropagation()
|
|
1387
|
+
armToolMode({ mode: 'build', tool: 'zone' })
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
const rooms = levelZones.filter((zone) => zone.spaceRole === 'room')
|
|
1391
|
+
const others = levelZones.filter((zone) => zone.spaceRole !== 'room')
|
|
1392
|
+
|
|
1393
|
+
return (
|
|
1394
|
+
<div className="subtle-scrollbar max-h-72 shrink-0 overflow-y-auto overflow-x-hidden">
|
|
1395
|
+
<TreeNodeWrapper
|
|
1396
|
+
actions={
|
|
1397
|
+
<button
|
|
1398
|
+
className="flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-black/5 hover:text-foreground dark:hover:bg-white/10"
|
|
1399
|
+
onClick={handleDrawZone}
|
|
1400
|
+
title="Draw a zone"
|
|
1401
|
+
type="button"
|
|
1402
|
+
>
|
|
1403
|
+
<Plus className="h-3 w-3" />
|
|
1404
|
+
</button>
|
|
1405
|
+
}
|
|
1406
|
+
depth={1}
|
|
1407
|
+
expanded={expanded}
|
|
1408
|
+
hasChildren
|
|
1409
|
+
icon={<LayoutGrid className="h-3.5 w-3.5" />}
|
|
1410
|
+
label={
|
|
1411
|
+
<span className="flex items-center gap-1.5">
|
|
1412
|
+
Rooms
|
|
1413
|
+
{rooms.length > 0 && (
|
|
1414
|
+
<span className="text-muted-foreground text-xs">{rooms.length}</span>
|
|
1415
|
+
)}
|
|
1416
|
+
</span>
|
|
1417
|
+
}
|
|
1418
|
+
onClick={() => setExpanded((value) => !value)}
|
|
1419
|
+
onToggle={() => setExpanded((value) => !value)}
|
|
1420
|
+
>
|
|
1421
|
+
{levelZones.length === 0 && (
|
|
1422
|
+
<div className="border-border/50 border-b py-2 pr-3 pl-[52px] text-muted-foreground text-xs">
|
|
1423
|
+
Close walls around a space to make a room.
|
|
1424
|
+
</div>
|
|
1425
|
+
)}
|
|
1426
|
+
{rooms.map((zone, index) => (
|
|
1427
|
+
<ZoneItem
|
|
1428
|
+
depth={2}
|
|
1429
|
+
isLast={others.length === 0 && index === rooms.length - 1}
|
|
1430
|
+
key={zone.id}
|
|
1431
|
+
zone={zone}
|
|
1432
|
+
/>
|
|
1433
|
+
))}
|
|
1434
|
+
{others.length > 0 && (
|
|
1435
|
+
<>
|
|
1436
|
+
<div
|
|
1437
|
+
className="pt-3 pb-1 pl-[52px] font-medium text-[11px] text-muted-foreground"
|
|
1438
|
+
data-other-zones
|
|
1439
|
+
>
|
|
1440
|
+
Other zones
|
|
1441
|
+
</div>
|
|
1442
|
+
{others.map((zone, index) => (
|
|
1443
|
+
<ZoneItem depth={2} isLast={index === others.length - 1} key={zone.id} zone={zone} />
|
|
1444
|
+
))}
|
|
1445
|
+
</>
|
|
1446
|
+
)}
|
|
1447
|
+
</TreeNodeWrapper>
|
|
1448
|
+
</div>
|
|
1449
|
+
)
|
|
1450
|
+
})
|
|
1451
|
+
|
|
1452
|
+
const ContentSection = memo(function ContentSection() {
|
|
1453
|
+
const selectedLevelId = useViewer((state) => state.selection.levelId)
|
|
1454
|
+
const level = useScene((s) =>
|
|
1455
|
+
selectedLevelId ? ((s.nodes[selectedLevelId] as LevelNode | undefined) ?? null) : null,
|
|
1456
|
+
)
|
|
1498
1457
|
const elementChildren = useScene(
|
|
1499
1458
|
useShallow((s) => {
|
|
1500
1459
|
if (!selectedLevelId) return []
|
|
@@ -1506,6 +1465,7 @@ const ContentSection = memo(function ContentSection() {
|
|
|
1506
1465
|
})
|
|
1507
1466
|
}),
|
|
1508
1467
|
)
|
|
1468
|
+
const [expanded, setExpanded] = useState(true)
|
|
1509
1469
|
|
|
1510
1470
|
if (!level) {
|
|
1511
1471
|
return (
|
|
@@ -1513,49 +1473,40 @@ const ContentSection = memo(function ContentSection() {
|
|
|
1513
1473
|
)
|
|
1514
1474
|
}
|
|
1515
1475
|
|
|
1516
|
-
if (structureLayer === 'zones') {
|
|
1517
|
-
const handleAddZone = () => {
|
|
1518
|
-
setPhase('structure')
|
|
1519
|
-
setMode('build')
|
|
1520
|
-
setTool('zone')
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
if (levelZones.length === 0) {
|
|
1524
|
-
return (
|
|
1525
|
-
<div className="px-3 py-4 text-muted-foreground text-sm">
|
|
1526
|
-
No zones on this level.{' '}
|
|
1527
|
-
<button className="cursor-pointer text-primary hover:underline" onClick={handleAddZone}>
|
|
1528
|
-
Add one
|
|
1529
|
-
</button>
|
|
1530
|
-
</div>
|
|
1531
|
-
)
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
return (
|
|
1535
|
-
<div className="flex flex-col">
|
|
1536
|
-
{levelZones.map((zone, index) => (
|
|
1537
|
-
<ZoneItem isLast={index === levelZones.length - 1} key={zone.id} zone={zone} />
|
|
1538
|
-
))}
|
|
1539
|
-
</div>
|
|
1540
|
-
)
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
if (elementChildren.length === 0) {
|
|
1544
|
-
return <div className="px-3 py-4 text-muted-foreground text-sm">No elements on this level</div>
|
|
1545
|
-
}
|
|
1546
1476
|
return (
|
|
1547
|
-
<
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1477
|
+
<TreeNodeWrapper
|
|
1478
|
+
depth={1}
|
|
1479
|
+
expanded={expanded}
|
|
1480
|
+
hasChildren
|
|
1481
|
+
icon={<Boxes className="h-3.5 w-3.5" />}
|
|
1482
|
+
label={
|
|
1483
|
+
<span className="flex items-center gap-1.5">
|
|
1484
|
+
Elements
|
|
1485
|
+
{elementChildren.length > 0 && (
|
|
1486
|
+
<span className="text-muted-foreground text-xs">{elementChildren.length}</span>
|
|
1487
|
+
)}
|
|
1488
|
+
</span>
|
|
1489
|
+
}
|
|
1490
|
+
onClick={() => setExpanded((value) => !value)}
|
|
1491
|
+
onToggle={() => setExpanded((value) => !value)}
|
|
1492
|
+
>
|
|
1493
|
+
{elementChildren.length === 0 ? (
|
|
1494
|
+
<div className="border-border/50 border-b py-2 pr-3 pl-[52px] text-muted-foreground text-xs">
|
|
1495
|
+
No elements on this level
|
|
1496
|
+
</div>
|
|
1497
|
+
) : (
|
|
1498
|
+
<TreeNodeDragProvider>
|
|
1499
|
+
{elementChildren.map((childId, index) => (
|
|
1500
|
+
<TreeNode
|
|
1501
|
+
depth={2}
|
|
1502
|
+
isLast={index === elementChildren.length - 1}
|
|
1503
|
+
key={childId}
|
|
1504
|
+
nodeId={childId}
|
|
1505
|
+
/>
|
|
1506
|
+
))}
|
|
1507
|
+
</TreeNodeDragProvider>
|
|
1508
|
+
)}
|
|
1509
|
+
</TreeNodeWrapper>
|
|
1559
1510
|
)
|
|
1560
1511
|
})
|
|
1561
1512
|
|
|
@@ -1591,7 +1542,7 @@ const BuildingItem = memo(function BuildingItem({
|
|
|
1591
1542
|
const handleSelect = () => {
|
|
1592
1543
|
setSelection({ buildingId: building.id })
|
|
1593
1544
|
if (phase === 'site') {
|
|
1594
|
-
setPhase('
|
|
1545
|
+
setPhase('building')
|
|
1595
1546
|
}
|
|
1596
1547
|
}
|
|
1597
1548
|
|
|
@@ -1713,7 +1664,8 @@ const BuildingItem = memo(function BuildingItem({
|
|
|
1713
1664
|
projectId={projectId}
|
|
1714
1665
|
/>
|
|
1715
1666
|
<UnitsSection buildingId={building.id} />
|
|
1716
|
-
<
|
|
1667
|
+
<RoomsSection />
|
|
1668
|
+
<CollectionsSection />
|
|
1717
1669
|
</div>
|
|
1718
1670
|
<div className="subtle-scrollbar relative min-h-0 flex-1 overflow-y-auto overflow-x-hidden">
|
|
1719
1671
|
<MultiSelectionBadge />
|
|
@@ -1769,7 +1721,14 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel
|
|
|
1769
1721
|
: 'text-muted-foreground hover:bg-accent/30 hover:text-foreground',
|
|
1770
1722
|
)}
|
|
1771
1723
|
layout="position"
|
|
1772
|
-
onClick={() =>
|
|
1724
|
+
onClick={() => {
|
|
1725
|
+
setPhase('site')
|
|
1726
|
+
// Also SELECT the site node so the registry inspector mounts the
|
|
1727
|
+
// site kind's panel (address / parcel / setbacks / front edge).
|
|
1728
|
+
// The site kind opts out of 3D click selection, so this header is
|
|
1729
|
+
// its only selection entry point. (WS1)
|
|
1730
|
+
setSelection({ selectedIds: [siteNode.id as AnyNodeId], zoneId: null })
|
|
1731
|
+
}}
|
|
1773
1732
|
>
|
|
1774
1733
|
<div className="flex items-center gap-2">
|
|
1775
1734
|
<img
|
|
@@ -1811,6 +1770,12 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel
|
|
|
1811
1770
|
transition={{ type: 'spring', bounce: 0, duration: 0.4 }}
|
|
1812
1771
|
>
|
|
1813
1772
|
<PropertyLineSection />
|
|
1773
|
+
{/* Address → parcel, setbacks, front edge: the site kind's own
|
|
1774
|
+
panel (packages/nodes/src/site/panel.tsx), loaded through
|
|
1775
|
+
the registry so the editor does not import the nodes package. */}
|
|
1776
|
+
<Suspense fallback={null}>
|
|
1777
|
+
<SiteKindPanel />
|
|
1778
|
+
</Suspense>
|
|
1814
1779
|
</motion.div>
|
|
1815
1780
|
)}
|
|
1816
1781
|
</AnimatePresence>
|
|
@@ -1824,7 +1789,7 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel
|
|
|
1824
1789
|
<div className="flex min-h-0 flex-1 flex-col">
|
|
1825
1790
|
{buildings.map((building) => {
|
|
1826
1791
|
const isBuildingActive =
|
|
1827
|
-
|
|
1792
|
+
phase === 'building' &&
|
|
1828
1793
|
selectedBuildingId === building.id
|
|
1829
1794
|
|
|
1830
1795
|
return (
|
|
@@ -3,25 +3,47 @@ import { Pencil } from 'lucide-react'
|
|
|
3
3
|
import { memo, useCallback, useEffect, useRef, useState } from 'react'
|
|
4
4
|
import { cn } from './../../../../../lib/utils'
|
|
5
5
|
|
|
6
|
-
interface
|
|
7
|
-
|
|
6
|
+
interface InlineRenameFieldProps {
|
|
7
|
+
name: string | undefined
|
|
8
|
+
/** Receives the trimmed name, or `undefined` when cleared. */
|
|
9
|
+
onRename: (name: string | undefined) => void
|
|
8
10
|
isEditing: boolean
|
|
9
11
|
onStopEditing: () => void
|
|
10
12
|
defaultName: string
|
|
13
|
+
/** Shown instead of the stored name when not editing (a level's display name). */
|
|
14
|
+
displayName?: string
|
|
11
15
|
className?: string
|
|
12
16
|
onStartEditing?: () => void
|
|
13
17
|
}
|
|
14
18
|
|
|
19
|
+
type InlineRenameInputProps = Omit<InlineRenameFieldProps, 'name' | 'onRename'> & {
|
|
20
|
+
nodeId: AnyNodeId
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Renames a scene node through `updateNode`. */
|
|
15
24
|
export const InlineRenameInput = memo(function InlineRenameInput({
|
|
16
25
|
nodeId,
|
|
26
|
+
...props
|
|
27
|
+
}: InlineRenameInputProps) {
|
|
28
|
+
const updateNode = useScene((s) => s.updateNode)
|
|
29
|
+
const name = useScene((s) => s.nodes[nodeId]?.name)
|
|
30
|
+
const handleRename = useCallback(
|
|
31
|
+
(next: string | undefined) => updateNode(nodeId, { name: next }),
|
|
32
|
+
[nodeId, updateNode],
|
|
33
|
+
)
|
|
34
|
+
return <InlineRenameField {...props} name={name} onRename={handleRename} />
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
export const InlineRenameField = memo(function InlineRenameField({
|
|
38
|
+
name,
|
|
39
|
+
onRename,
|
|
17
40
|
isEditing,
|
|
18
41
|
onStopEditing,
|
|
19
42
|
defaultName,
|
|
43
|
+
displayName,
|
|
20
44
|
className,
|
|
21
45
|
onStartEditing,
|
|
22
|
-
}:
|
|
23
|
-
const updateNode = useScene((s) => s.updateNode)
|
|
24
|
-
const name = useScene((s) => s.nodes[nodeId]?.name)
|
|
46
|
+
}: InlineRenameFieldProps) {
|
|
25
47
|
const [value, setValue] = useState(name || '')
|
|
26
48
|
const inputRef = useRef<HTMLInputElement>(null)
|
|
27
49
|
const inputSize = Math.max((value || defaultName).length, 1)
|
|
@@ -42,10 +64,10 @@ export const InlineRenameInput = memo(function InlineRenameInput({
|
|
|
42
64
|
const handleSave = useCallback(() => {
|
|
43
65
|
const trimmed = value.trim()
|
|
44
66
|
if (trimmed !== name) {
|
|
45
|
-
|
|
67
|
+
onRename(trimmed || undefined)
|
|
46
68
|
}
|
|
47
69
|
onStopEditing()
|
|
48
|
-
}, [value,
|
|
70
|
+
}, [value, name, onRename, onStopEditing])
|
|
49
71
|
|
|
50
72
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
51
73
|
if (e.key === 'Enter') {
|
|
@@ -61,7 +83,7 @@ export const InlineRenameInput = memo(function InlineRenameInput({
|
|
|
61
83
|
return (
|
|
62
84
|
<div className="group/rename flex h-5 min-w-0 items-center gap-1">
|
|
63
85
|
<span className={cn('truncate border-transparent border-b', className)}>
|
|
64
|
-
{name || defaultName}
|
|
86
|
+
{displayName ?? (name || defaultName)}
|
|
65
87
|
</span>
|
|
66
88
|
{onStartEditing && (
|
|
67
89
|
<button
|
|
@@ -8,7 +8,6 @@ import { InlineRenameInput } from './inline-rename-input'
|
|
|
8
8
|
import {
|
|
9
9
|
focusTreeNode,
|
|
10
10
|
handleTreeSelection,
|
|
11
|
-
routeTreeSelectionToNode,
|
|
12
11
|
TreeNode,
|
|
13
12
|
TreeNodeWrapper,
|
|
14
13
|
} from './tree-node'
|
|
@@ -71,9 +70,8 @@ export const ItemTreeNode = memo(function ItemTreeNode({
|
|
|
71
70
|
(e: React.MouseEvent) => {
|
|
72
71
|
e.stopPropagation()
|
|
73
72
|
handleTreeSelection(e, nodeId, useViewer.getState().selection.selectedIds, setSelection)
|
|
74
|
-
routeTreeSelectionToNode(node)
|
|
75
73
|
},
|
|
76
|
-
[
|
|
74
|
+
[nodeId, setSelection],
|
|
77
75
|
)
|
|
78
76
|
|
|
79
77
|
const handleDoubleClick = useCallback(() => focusTreeNode(nodeId), [nodeId])
|