@pascal-app/editor 1.0.3 → 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 +11 -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 +202 -57
- 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 +36 -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 +344 -342
- package/src/components/editor/footprint-height-handle.tsx +204 -0
- package/src/components/editor/group-actions.ts +54 -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 +269 -197
- package/src/components/editor/index.tsx +294 -169
- package/src/components/editor/join-walls-pill.tsx +44 -0
- package/src/components/editor/node-action-menu.tsx +68 -70
- package/src/components/editor/node-arrow-handles.tsx +25 -6
- 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 +80 -1
- 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 +43 -142
- 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-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-registry-action-menu.tsx +11 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +113 -102
- 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-wall-measurement.tsx → floorplan-draft-measurement.tsx} +8 -8
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +38 -8
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +291 -227
- 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 +29 -68
- package/src/components/tools/item/placement-history.test.tsx +474 -0
- package/src/components/tools/item/placement-strategies.ts +8 -4
- package/src/components/tools/item/use-draft-node.test.tsx +432 -1
- package/src/components/tools/item/use-draft-node.ts +166 -95
- package/src/components/tools/item/use-placement-coordinator.tsx +93 -46
- 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/move-registry-node-tool.tsx +3 -1
- 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/cursor-sphere.tsx +36 -12
- 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 +46 -20
- 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 +97 -26
- package/src/components/ui/controls/material-picker.tsx +4 -1
- 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/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 +325 -14
- package/src/components/ui/helpers/item-helper.tsx +8 -0
- package/src/components/ui/helpers/registered-tool-helper.tsx +8 -1
- package/src/components/ui/icon-ref.tsx +45 -1
- 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-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 +4 -4
- 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/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/index.tsx +31 -4
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +5 -5
- 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.tsx +5 -6
- 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 +3 -8
- package/src/hooks/use-auto-save.integration.test.tsx +123 -9
- package/src/hooks/use-auto-save.ts +34 -8
- package/src/hooks/use-keyboard.test.ts +178 -3
- package/src/hooks/use-keyboard.ts +89 -71
- 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 +246 -39
- 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/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 +12 -8
- package/src/lib/floorplan/floorplan-export.tsx +32 -8
- package/src/lib/floorplan/floorplan-extension.ts +32 -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 +59 -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 +485 -0
- package/src/lib/glb-export.ts +237 -119
- 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 +18 -0
- package/src/lib/keyboard-pan.test.ts +129 -7
- package/src/lib/keyboard-pan.ts +161 -12
- 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 +3 -1
- package/src/lib/material-paint-panel-model.ts +9 -5
- 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/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 +21 -0
- package/src/lib/parametric-node-update.ts +1 -1
- 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 +24 -36
- package/src/lib/scene.ts +33 -57
- 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 +4 -0
- package/src/lib/snapping-mode.ts +8 -1
- package/src/lib/terrace-draft.test.ts +145 -0
- package/src/lib/terrace-draft.ts +213 -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/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/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 +432 -321
- package/src/store/use-floorplan-draft-preview.ts +13 -0
- package/src/store/use-floorplan-marquee.ts +12 -2
- 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
|
@@ -22,11 +22,16 @@ import {
|
|
|
22
22
|
type WindowNode,
|
|
23
23
|
} from '@pascal-app/core'
|
|
24
24
|
import { useViewer } from '@pascal-app/viewer'
|
|
25
|
-
import { useCallback, useEffect, useState } from 'react'
|
|
25
|
+
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
26
|
+
import { useSelectedRoom, useZoneRoom } from '../../../hooks/use-selected-room'
|
|
27
|
+
import { FloorOpeningPanel } from './floor-opening-panel'
|
|
28
|
+
import { OpenRoomPanel, RoomPanel } from './room-panel'
|
|
26
29
|
import { useIsMobile } from '../../../hooks/use-mobile'
|
|
27
30
|
import { shouldShowEditingControls } from '../../../lib/interaction/overlay-policy'
|
|
31
|
+
import { captureElementActionOrigin, completeElementAction } from '../../../lib/room-zone-routing'
|
|
28
32
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
|
29
33
|
import useEditor from '../../../store/use-editor'
|
|
34
|
+
import { duplicateNodeAndPickUp } from '../../editor/duplicate-node'
|
|
30
35
|
import { deleteSelection, duplicateSelectionAndPickUp, startGroupPickUp } from '../../editor/group-actions'
|
|
31
36
|
import { resolveHomogeneousSelection } from './homogeneous-selection'
|
|
32
37
|
import { MobilePanelSheet } from './mobile-panel-sheet'
|
|
@@ -34,7 +39,7 @@ import { MobileSelectionBar } from './mobile-selection-bar'
|
|
|
34
39
|
import { MultiParametricInspector } from './multi-parametric-inspector'
|
|
35
40
|
import { MultiSelectionPanel } from './multi-selection-panel'
|
|
36
41
|
import { getNodeDisplay, getTypeDisplay } from './node-display'
|
|
37
|
-
import {
|
|
42
|
+
import { InspectedNodesContext } from './panel-wrapper'
|
|
38
43
|
import { ParametricInspector } from './parametric-inspector'
|
|
39
44
|
import { ReferencePanel } from './reference-panel'
|
|
40
45
|
import { formatSelectionBreakdown } from './selection-breakdown'
|
|
@@ -127,24 +132,16 @@ function MobilePanelLayer({
|
|
|
127
132
|
}, [node, setMovingNode, clearSelection])
|
|
128
133
|
|
|
129
134
|
const handleDuplicate = useCallback(() => {
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
const cloned = structuredClone(node) as MovableNode & { id?: AnyNodeId }
|
|
133
|
-
delete (cloned as { id?: AnyNodeId }).id
|
|
134
|
-
const prevMeta =
|
|
135
|
-
cloned.metadata && typeof cloned.metadata === 'object' && !Array.isArray(cloned.metadata)
|
|
136
|
-
? (cloned.metadata as Record<string, unknown>)
|
|
137
|
-
: {}
|
|
138
|
-
cloned.metadata = { ...prevMeta, isNew: true }
|
|
139
|
-
setMovingNode(cloned as MovableNode)
|
|
140
|
-
clearSelection()
|
|
141
|
-
}, [node, setMovingNode, clearSelection])
|
|
135
|
+
if (isMovableNode(node)) duplicateNodeAndPickUp(node)
|
|
136
|
+
}, [node])
|
|
142
137
|
|
|
143
138
|
const handleDelete = useCallback(() => {
|
|
144
139
|
if (!node) return
|
|
140
|
+
const origin = captureElementActionOrigin([node.id])
|
|
145
141
|
sfxEmitter.emit('sfx:item-delete')
|
|
146
142
|
deleteNode(node.id)
|
|
147
143
|
clearSelection()
|
|
144
|
+
completeElementAction(origin)
|
|
148
145
|
}, [node, deleteNode, clearSelection])
|
|
149
146
|
|
|
150
147
|
if (!(node || isReference)) return null
|
|
@@ -222,6 +219,7 @@ export function PanelManager({
|
|
|
222
219
|
multiSelectionFooter?: React.ReactNode
|
|
223
220
|
}) {
|
|
224
221
|
const isMobile = useIsMobile()
|
|
222
|
+
const room = useSelectedRoom()
|
|
225
223
|
const selectedIds = useViewer((s) => s.selection.selectedIds)
|
|
226
224
|
const selectedZoneId = useViewer((s) => s.selection.zoneId)
|
|
227
225
|
const setSelection = useViewer((s) => s.setSelection)
|
|
@@ -248,6 +246,16 @@ export function PanelManager({
|
|
|
248
246
|
: '',
|
|
249
247
|
)
|
|
250
248
|
|
|
249
|
+
// A room reached as a zone (a unit's member, a zone kept by a route that is not
|
|
250
|
+
// room-first) still gets the room panel.
|
|
251
|
+
const panelZoneId =
|
|
252
|
+
selectedIds.length === 0
|
|
253
|
+
? selectedZoneId
|
|
254
|
+
: selectedNodeType === 'zone'
|
|
255
|
+
? (selectedIds[0] ?? null)
|
|
256
|
+
: null
|
|
257
|
+
const zoneRoom = useZoneRoom(panelZoneId)
|
|
258
|
+
|
|
251
259
|
// Node and reference selection are mutually exclusive: selecting a guide
|
|
252
260
|
// clears the node selection (handleGuideSelect), but node selection never
|
|
253
261
|
// cleared a lingering reference — so clicking a wall with a floorplan
|
|
@@ -255,74 +263,101 @@ export function PanelManager({
|
|
|
255
263
|
// moment a scene selection appears.
|
|
256
264
|
const setSelectedReferenceId = useEditor((s) => s.setSelectedReferenceId)
|
|
257
265
|
useEffect(() => {
|
|
258
|
-
if (selectedIds.length > 0 || selectedZoneId) {
|
|
266
|
+
if (selectedIds.length > 0 || selectedZoneId || room) {
|
|
259
267
|
setSelectedReferenceId(null)
|
|
260
268
|
}
|
|
261
|
-
}, [selectedIds, selectedZoneId, setSelectedReferenceId])
|
|
269
|
+
}, [selectedIds, selectedZoneId, room, setSelectedReferenceId])
|
|
262
270
|
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
+
// What the card shows, for its Collections section: a room is its zone.
|
|
272
|
+
const inspectedKey =
|
|
273
|
+
room && selectedIds.length === 0
|
|
274
|
+
? room.zoneId
|
|
275
|
+
: panelZoneId && zoneRoom
|
|
276
|
+
? panelZoneId
|
|
277
|
+
: selectedReferenceId
|
|
278
|
+
? ''
|
|
279
|
+
: selectedIds.length === 0
|
|
280
|
+
? (selectedZoneId ?? '')
|
|
281
|
+
: selectedIds.join(',')
|
|
282
|
+
const inspectedIds = useMemo(
|
|
283
|
+
() => (inspectedKey ? (inspectedKey.split(',') as AnyNodeId[]) : []),
|
|
284
|
+
[inspectedKey],
|
|
285
|
+
)
|
|
271
286
|
|
|
272
287
|
if (!shouldShowEditingControls(readOnly)) return null
|
|
273
288
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
289
|
+
return (
|
|
290
|
+
<InspectedNodesContext.Provider value={inspectedIds}>
|
|
291
|
+
{renderPanel()}
|
|
292
|
+
</InspectedNodesContext.Provider>
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
function renderPanel() {
|
|
296
|
+
if (room && selectedIds.length === 0) return <RoomPanel room={room} />
|
|
297
|
+
if (selectedNodeType === 'floor-opening' && selectedIds[0])
|
|
298
|
+
return <FloorOpeningPanel key={selectedIds[0]} openingId={selectedIds[0]} />
|
|
299
|
+
if (zoneRoom && panelZoneId) {
|
|
300
|
+
const closeZone = () => setSelection({ selectedIds: [], zoneId: null })
|
|
301
|
+
return zoneRoom.record ? (
|
|
302
|
+
<RoomPanel onClose={closeZone} room={zoneRoom.record} />
|
|
303
|
+
) : (
|
|
304
|
+
<OpenRoomPanel key={panelZoneId} onClose={closeZone} zoneId={panelZoneId} />
|
|
305
|
+
)
|
|
277
306
|
}
|
|
278
|
-
|
|
307
|
+
|
|
308
|
+
if (isMobile) {
|
|
309
|
+
if (selectedReferenceId) {
|
|
310
|
+
return <MobilePanelLayer isReference={true} node={null} panel={<ReferencePanel />} />
|
|
311
|
+
}
|
|
312
|
+
if (selectedIds.length > 1) {
|
|
313
|
+
return (
|
|
314
|
+
<MobileMultiPanelLayer
|
|
315
|
+
breakdown={multiBreakdown}
|
|
316
|
+
panel={
|
|
317
|
+
homogeneousType ? (
|
|
318
|
+
<MultiParametricInspector footer={multiSelectionFooter} />
|
|
319
|
+
) : (
|
|
320
|
+
<MultiSelectionPanel footer={multiSelectionFooter} />
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
type={homogeneousType}
|
|
324
|
+
/>
|
|
325
|
+
)
|
|
326
|
+
}
|
|
279
327
|
return (
|
|
280
|
-
<
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
<MultiParametricInspector footer={multiSelectionFooter} />
|
|
285
|
-
) : (
|
|
286
|
-
<MultiSelectionPanel footer={multiSelectionFooter} />
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
type={homogeneousType}
|
|
328
|
+
<MobilePanelLayer
|
|
329
|
+
isReference={false}
|
|
330
|
+
node={selectedNode}
|
|
331
|
+
panel={panelForType(selectedNodeType)}
|
|
290
332
|
/>
|
|
291
333
|
)
|
|
292
334
|
}
|
|
293
|
-
return (
|
|
294
|
-
<MobilePanelLayer
|
|
295
|
-
isReference={false}
|
|
296
|
-
node={selectedNode}
|
|
297
|
-
panel={panelForType(selectedNodeType)}
|
|
298
|
-
/>
|
|
299
|
-
)
|
|
300
|
-
}
|
|
301
335
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
336
|
+
// Show reference panel if a reference is selected
|
|
337
|
+
if (selectedReferenceId) {
|
|
338
|
+
return <ReferencePanel />
|
|
339
|
+
}
|
|
306
340
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
341
|
+
if (selectedZoneId && selectedIds.length === 0) {
|
|
342
|
+
return (
|
|
343
|
+
<ParametricInspector
|
|
344
|
+
footer={inspectorFooter}
|
|
345
|
+
key={selectedZoneId}
|
|
346
|
+
nodeId={selectedZoneId as AnyNodeId}
|
|
347
|
+
onClose={() => setSelection({ zoneId: null })}
|
|
348
|
+
/>
|
|
349
|
+
)
|
|
350
|
+
}
|
|
317
351
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
352
|
+
// Multi-selection: parametric inspector when every resolved id shares a type,
|
|
353
|
+
// otherwise the actions-only panel. Mobile uses the same panels in a sheet.
|
|
354
|
+
if (selectedIds.length > 1) {
|
|
355
|
+
if (homogeneousType) {
|
|
356
|
+
return <MultiParametricInspector footer={multiSelectionFooter} />
|
|
357
|
+
}
|
|
358
|
+
return <MultiSelectionPanel footer={multiSelectionFooter} />
|
|
323
359
|
}
|
|
324
|
-
return <MultiSelectionPanel footer={multiSelectionFooter} />
|
|
325
|
-
}
|
|
326
360
|
|
|
327
|
-
|
|
361
|
+
return panelForType(selectedNodeType, inspectorFooter)
|
|
362
|
+
}
|
|
328
363
|
}
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from '@pascal-app/core'
|
|
13
13
|
import { useViewer } from '@pascal-app/viewer'
|
|
14
14
|
import { ChevronDown, ChevronLeft, GripHorizontal, RotateCcw, X } from 'lucide-react'
|
|
15
|
-
import Image from 'next/image'
|
|
16
15
|
import {
|
|
17
16
|
type ComponentType,
|
|
18
17
|
createContext,
|
|
@@ -27,27 +26,25 @@ import {
|
|
|
27
26
|
useState,
|
|
28
27
|
} from 'react'
|
|
29
28
|
import { useIsMobile } from '../../../hooks/use-mobile'
|
|
29
|
+
import { IconRefImage } from '../icon-ref'
|
|
30
|
+
import { NodeIconImage } from '../node-icon-image'
|
|
30
31
|
import {
|
|
31
32
|
resolveActiveExtension,
|
|
32
33
|
toggleCard,
|
|
33
34
|
toggleExtension,
|
|
34
35
|
} from '../../../lib/inspector-card-mode'
|
|
35
36
|
import { cn } from '../../../lib/utils'
|
|
37
|
+
import { useInspectorExpanded } from '../../../lib/inspector-expanded'
|
|
38
|
+
import { ActionButton, ActionGroup } from '../controls/action-button'
|
|
36
39
|
import { PanelSection } from '../controls/panel-section'
|
|
37
40
|
import { ErrorBoundary } from '../primitives/error-boundary'
|
|
41
|
+
import { useInRightStack } from '../right-stack'
|
|
42
|
+
import { CollectionsPopover } from './collections/collections-popover'
|
|
38
43
|
|
|
39
44
|
const DRAG_MARGIN = 8
|
|
40
45
|
// Pointer travel (px) below which a header press is treated as a click
|
|
41
46
|
// (toggles collapse) rather than a drag.
|
|
42
47
|
const CLICK_SLOP = 4
|
|
43
|
-
let desktopInspectorCollapsed = true
|
|
44
|
-
|
|
45
|
-
/** Forget the shared expanded state. Called when the last selection clears so
|
|
46
|
-
* a fresh selection opens the inspector collapsed — the sharing is only meant
|
|
47
|
-
* to survive swaps between panels (roof ↔ segment), not a close/reopen. */
|
|
48
|
-
export function resetDesktopInspectorCollapsed() {
|
|
49
|
-
desktopInspectorCollapsed = true
|
|
50
|
-
}
|
|
51
48
|
|
|
52
49
|
function clamp(value: number, min: number, max: number): number {
|
|
53
50
|
return Math.min(Math.max(value, min), Math.max(min, max))
|
|
@@ -81,8 +78,19 @@ function getDragBounds(el: HTMLElement | null): {
|
|
|
81
78
|
*/
|
|
82
79
|
export const InspectorFooterContext = createContext<React.ReactNode>(null)
|
|
83
80
|
|
|
81
|
+
/**
|
|
82
|
+
* The scene elements the inspector card shows (the selection, or the zone behind a room).
|
|
83
|
+
* `PanelManager` provides it so every card gets the Collections section; a card mounted
|
|
84
|
+
* elsewhere (the site panel in the sidebar) is not about the selection and gets none.
|
|
85
|
+
*/
|
|
86
|
+
export const InspectedNodesContext = createContext<AnyNodeId[]>([])
|
|
87
|
+
|
|
84
88
|
interface PanelWrapperProps {
|
|
85
89
|
title: string
|
|
90
|
+
/** A short line above the title ("Room · Ground floor"). */
|
|
91
|
+
kicker?: string
|
|
92
|
+
/** Replaces the plain title, e.g. with the name edited in place. */
|
|
93
|
+
titleContent?: React.ReactNode
|
|
86
94
|
/** Either a URL path (legacy panels pass `/icons/floor.webp` etc.,
|
|
87
95
|
* rendered via next/image) OR a React node (registry-driven
|
|
88
96
|
* inspector renders `<Icon icon="lucide:fence" />` from
|
|
@@ -100,6 +108,8 @@ interface PanelWrapperProps {
|
|
|
100
108
|
|
|
101
109
|
export function PanelWrapper({
|
|
102
110
|
title,
|
|
111
|
+
kicker,
|
|
112
|
+
titleContent,
|
|
103
113
|
icon,
|
|
104
114
|
onClose,
|
|
105
115
|
onReset,
|
|
@@ -110,7 +120,9 @@ export function PanelWrapper({
|
|
|
110
120
|
width = 320, // default width
|
|
111
121
|
}: PanelWrapperProps) {
|
|
112
122
|
const isMobile = useIsMobile()
|
|
123
|
+
const inStack = useInRightStack()
|
|
113
124
|
const contextFooter = useContext(InspectorFooterContext)
|
|
125
|
+
const inspectedIds = useContext(InspectedNodesContext)
|
|
114
126
|
const resolvedFooter = footer ?? contextFooter
|
|
115
127
|
|
|
116
128
|
const panelRef = useRef<HTMLDivElement>(null)
|
|
@@ -145,22 +157,13 @@ export function PanelWrapper({
|
|
|
145
157
|
// Stale ids (kind changed, plugin gated off) fall back to regular mode.
|
|
146
158
|
const activeExtension = resolveActiveExtension(activeExtensionId, extensions)
|
|
147
159
|
|
|
148
|
-
// The
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
(next: boolean | ((previous: boolean) => boolean)) => {
|
|
156
|
-
setCollapsedState((previous) => {
|
|
157
|
-
const resolved = typeof next === 'function' ? next(previous) : next
|
|
158
|
-
desktopInspectorCollapsed = resolved
|
|
159
|
-
return resolved
|
|
160
|
-
})
|
|
161
|
-
},
|
|
162
|
-
[],
|
|
163
|
-
)
|
|
160
|
+
// The panel is collapsed to just its header until the user expands it. The
|
|
161
|
+
// choice is one editor preference for every inspector (room, wall, item…),
|
|
162
|
+
// persisted, so every panel opens the way the user last left one.
|
|
163
|
+
const collapsed = !useInspectorExpanded((state) => state.expanded)
|
|
164
|
+
const setCollapsed = useCallback((next: boolean) => {
|
|
165
|
+
useInspectorExpanded.getState().setExpanded(!next)
|
|
166
|
+
}, [])
|
|
164
167
|
|
|
165
168
|
const applyMode = useCallback(
|
|
166
169
|
(next: { collapsed: boolean; activeExtensionId: string | null }) => {
|
|
@@ -205,8 +208,8 @@ export function PanelWrapper({
|
|
|
205
208
|
|
|
206
209
|
const handleHeaderPointerDown = useCallback(
|
|
207
210
|
(e: React.PointerEvent<HTMLDivElement>) => {
|
|
208
|
-
// Buttons (close / reset / collapse) handle their own clicks.
|
|
209
|
-
if ((e.target as HTMLElement).closest('button')) return
|
|
211
|
+
// Buttons (close / reset / collapse) and a title edited in place handle their own clicks.
|
|
212
|
+
if ((e.target as HTMLElement).closest('button, input, label')) return
|
|
210
213
|
const rect = panelRef.current?.getBoundingClientRect()
|
|
211
214
|
if (!rect) return
|
|
212
215
|
const bounds = getDragBounds(panelRef.current)
|
|
@@ -282,6 +285,10 @@ export function PanelWrapper({
|
|
|
282
285
|
className={cn(
|
|
283
286
|
isMobile
|
|
284
287
|
? 'flex h-full w-full flex-col overflow-hidden bg-transparent dark:text-foreground'
|
|
288
|
+
: inStack
|
|
289
|
+
? // In the shared right column: it grows down and scrolls on itself,
|
|
290
|
+
// leaving the shortcuts card below it its room (`right-stack.tsx`).
|
|
291
|
+
'pointer-events-auto flex max-h-full min-h-0 flex-[0_1_auto] flex-col overflow-hidden rounded-xl border border-border/50 bg-sidebar/95 shadow-2xl backdrop-blur-xl dark:text-foreground'
|
|
285
292
|
// Cap height at `100dvh - 154px` so a tall panel's bottom edge
|
|
286
293
|
// aligns flush with the top of the floating bottom action bar.
|
|
287
294
|
// Combined with `top-20` (80px), the panel's bottom sits at
|
|
@@ -292,6 +299,7 @@ export function PanelWrapper({
|
|
|
292
299
|
: 'pointer-events-auto fixed top-20 right-4 z-50 flex max-h-[calc(100dvh-154px)] flex-col overflow-hidden rounded-xl border border-border/50 bg-sidebar/95 shadow-2xl backdrop-blur-xl dark:text-foreground',
|
|
293
300
|
className,
|
|
294
301
|
)}
|
|
302
|
+
data-panel-wrapper
|
|
295
303
|
ref={panelRef}
|
|
296
304
|
style={
|
|
297
305
|
isMobile
|
|
@@ -311,11 +319,12 @@ export function PanelWrapper({
|
|
|
311
319
|
!collapsed && 'border-border/50 border-b',
|
|
312
320
|
isDragging ? 'cursor-grabbing' : 'cursor-grab',
|
|
313
321
|
)}
|
|
322
|
+
data-panel-header
|
|
314
323
|
onPointerDown={handleHeaderPointerDown}
|
|
315
324
|
onPointerMove={handleHeaderPointerMove}
|
|
316
325
|
onPointerUp={handleHeaderPointerUp}
|
|
317
326
|
>
|
|
318
|
-
<div className=
|
|
327
|
+
<div className={cn('flex min-w-0 items-center gap-2', titleContent && 'flex-1 pr-2')}>
|
|
319
328
|
{onBack && (
|
|
320
329
|
<button
|
|
321
330
|
className="mr-1 flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-[#3e3e3e] hover:text-foreground"
|
|
@@ -327,7 +336,7 @@ export function PanelWrapper({
|
|
|
327
336
|
)}
|
|
328
337
|
{icon &&
|
|
329
338
|
(typeof icon === 'string' ? (
|
|
330
|
-
<
|
|
339
|
+
<NodeIconImage
|
|
331
340
|
alt=""
|
|
332
341
|
className="shrink-0 object-contain"
|
|
333
342
|
height={16}
|
|
@@ -337,13 +346,30 @@ export function PanelWrapper({
|
|
|
337
346
|
) : (
|
|
338
347
|
<span className="flex shrink-0 items-center justify-center">{icon}</span>
|
|
339
348
|
))}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
349
|
+
{kicker || titleContent ? (
|
|
350
|
+
<div className="flex min-w-0 flex-1 flex-col">
|
|
351
|
+
{kicker && (
|
|
352
|
+
<span className="truncate text-[11px] text-muted-foreground leading-tight" data-panel-kicker>
|
|
353
|
+
{kicker}
|
|
354
|
+
</span>
|
|
355
|
+
)}
|
|
356
|
+
{titleContent ?? (
|
|
357
|
+
<h2 className="truncate font-semibold text-foreground text-sm tracking-tight">
|
|
358
|
+
{title}
|
|
359
|
+
</h2>
|
|
360
|
+
)}
|
|
361
|
+
</div>
|
|
362
|
+
) : (
|
|
363
|
+
<h2 className="truncate font-semibold text-foreground text-sm tracking-tight">
|
|
364
|
+
{title}
|
|
365
|
+
</h2>
|
|
366
|
+
)}
|
|
343
367
|
</div>
|
|
344
368
|
|
|
345
|
-
{/* Centered grip — purely a visual drag affordance. */}
|
|
346
|
-
|
|
369
|
+
{/* Centered grip — purely a visual drag affordance (a title edited in place takes its room). */}
|
|
370
|
+
{!titleContent && (
|
|
371
|
+
<GripHorizontal className="-translate-x-1/2 pointer-events-none absolute left-1/2 h-4 w-4 text-muted-foreground/40" />
|
|
372
|
+
)}
|
|
347
373
|
|
|
348
374
|
<div className="flex items-center gap-1">
|
|
349
375
|
{onReset && (
|
|
@@ -413,7 +439,7 @@ export function PanelWrapper({
|
|
|
413
439
|
controls (`children`). A stale extension id falls back to regular
|
|
414
440
|
via `resolveActiveExtension`. */}
|
|
415
441
|
{!(collapsed && !isMobile) && (
|
|
416
|
-
<div className="no-scrollbar flex min-h-0 flex-1 flex-col overflow-y-auto">
|
|
442
|
+
<div className="no-scrollbar flex min-h-0 flex-1 flex-col overflow-y-auto" data-panel-scroll>
|
|
417
443
|
{!isMobile && selectedId && activeExtension ? (
|
|
418
444
|
<InspectorExtensionSection
|
|
419
445
|
extension={activeExtension}
|
|
@@ -422,7 +448,23 @@ export function PanelWrapper({
|
|
|
422
448
|
/>
|
|
423
449
|
) : (
|
|
424
450
|
<>
|
|
451
|
+
{/* The mobile sheet has its own header: a title edited in place moves into the body. */}
|
|
452
|
+
{isMobile && titleContent && (
|
|
453
|
+
<div className="flex flex-col px-4 pt-3">
|
|
454
|
+
{kicker && <span className="text-[11px] text-muted-foreground">{kicker}</span>}
|
|
455
|
+
{titleContent}
|
|
456
|
+
</div>
|
|
457
|
+
)}
|
|
425
458
|
{children}
|
|
459
|
+
{inspectedIds.length > 0 && (
|
|
460
|
+
<PanelSection title="Collections">
|
|
461
|
+
<ActionGroup>
|
|
462
|
+
<CollectionsPopover nodeIds={inspectedIds}>
|
|
463
|
+
<ActionButton label="Manage collections…" />
|
|
464
|
+
</CollectionsPopover>
|
|
465
|
+
</ActionGroup>
|
|
466
|
+
</PanelSection>
|
|
467
|
+
)}
|
|
426
468
|
{/* Mobile sheet has no header icons to swap modes — keep the
|
|
427
469
|
plugin sections appended after the kind's controls there. */}
|
|
428
470
|
{isMobile &&
|
|
@@ -453,7 +495,7 @@ export function PanelWrapper({
|
|
|
453
495
|
* inspector's `renderIcon` (plain <img> so no next/image server deps). */
|
|
454
496
|
function renderExtensionIcon(ref: IconRef): React.ReactNode {
|
|
455
497
|
if (ref.kind === 'url') {
|
|
456
|
-
return <
|
|
498
|
+
return <IconRefImage className="h-4 w-4 shrink-0" src={ref.src} />
|
|
457
499
|
}
|
|
458
500
|
if (ref.kind === 'iconify') {
|
|
459
501
|
return <Icon height={16} icon={ref.name} width={16} />
|
|
@@ -19,6 +19,7 @@ import { commitParametricNodeFields } from '../../../lib/parametric-node-update'
|
|
|
19
19
|
import { sfxEmitter } from '../../../lib/sfx-bus'
|
|
20
20
|
import { collectZoneContentIds } from '../../../lib/zone-content'
|
|
21
21
|
import useEditor from '../../../store/use-editor'
|
|
22
|
+
import { IconRefImage } from '../icon-ref'
|
|
22
23
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
|
23
24
|
import { PanelSection } from '../controls/panel-section'
|
|
24
25
|
import { ParametricFieldControl } from './parametric-field-control'
|
|
@@ -236,10 +237,9 @@ function ParamActionButton({ action, nodeId }: { action: ParamAction<AnyNode>; n
|
|
|
236
237
|
function renderIcon(ref: IconRef | undefined): React.ReactNode | undefined {
|
|
237
238
|
if (!ref) return undefined
|
|
238
239
|
if (ref.kind === 'url') {
|
|
239
|
-
//
|
|
240
|
-
// server-only requirements
|
|
241
|
-
|
|
242
|
-
return <img alt="" className="h-4 w-4 shrink-0 object-contain" src={ref.src} />
|
|
240
|
+
// No next/image here: the inspector is `'use client'` and next/image
|
|
241
|
+
// drags in server-only requirements. Same 16x16 box the legacy panels use.
|
|
242
|
+
return <IconRefImage className="h-4 w-4 shrink-0" src={ref.src} />
|
|
243
243
|
}
|
|
244
244
|
if (ref.kind === 'iconify') {
|
|
245
245
|
return <Icon height={16} icon={ref.name} width={16} />
|
|
@@ -25,6 +25,7 @@ import { guideEmitter } from '../../../lib/guide-events'
|
|
|
25
25
|
import { getGuideImageName } from '../../../lib/local-guide-image'
|
|
26
26
|
import { cn } from '../../../lib/utils'
|
|
27
27
|
import useEditor from '../../../store/use-editor'
|
|
28
|
+
import { isViewVisible, VIEW_2D } from '../../../store/view-layout'
|
|
28
29
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
|
29
30
|
import { PanelSection } from '../controls/panel-section'
|
|
30
31
|
import { SliderControl } from '../controls/slider-control'
|
|
@@ -134,9 +135,7 @@ export function ReferencePanel() {
|
|
|
134
135
|
// The scale line is drawn on the 2D plan — starting from a 3D-only view
|
|
135
136
|
// would arm the flow invisibly inside the hidden floorplan panel.
|
|
136
137
|
const editor = useEditor.getState()
|
|
137
|
-
if (editor
|
|
138
|
-
editor.setViewMode('2d')
|
|
139
|
-
}
|
|
138
|
+
if (!isViewVisible(editor, VIEW_2D)) editor.showView(VIEW_2D)
|
|
140
139
|
|
|
141
140
|
guideEmitter.emit('guide:set-reference-scale', { guideId: node.id })
|
|
142
141
|
}, [node])
|