@pascal-app/editor 1.0.3 → 1.0.5
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/README.md +106 -0
- package/package.json +42 -38
- package/src/components/editor/__bench__/room-label-placement.bench.ts +108 -0
- package/src/components/editor/action-menu-button.tsx +83 -0
- package/src/components/editor/bake-thumbnail.tsx +307 -5
- package/src/components/editor/capture-level-picker.tsx +72 -0
- package/src/components/editor/construction-reveal-audio.test.tsx +150 -0
- package/src/components/editor/construction-reveal-audio.tsx +40 -0
- package/src/components/editor/custom-camera-controls.tsx +275 -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 +37 -26
- package/src/components/editor/editor-layout-v2.tsx +132 -59
- package/src/components/editor/export-manager.tsx +47 -26
- package/src/components/editor/first-person/build-collider-world.test.ts +320 -0
- package/src/components/editor/first-person/build-collider-world.ts +28 -7
- package/src/components/editor/first-person/ground-holes.test.ts +128 -0
- package/src/components/editor/first-person/ground-holes.ts +130 -0
- package/src/components/editor/first-person/mechanism-targets.test.ts +162 -0
- package/src/components/editor/first-person/mechanism-targets.ts +88 -0
- package/src/components/editor/first-person-controls.tsx +220 -9
- package/src/components/editor/floating-action-menu.tsx +63 -207
- package/src/components/editor/floor-aware-camera-controls.test.ts +466 -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-mode-coordinator.tsx +2 -2
- package/src/components/editor/floorplan-panel.tsx +414 -363
- package/src/components/editor/follow-pascal.test.tsx +912 -0
- package/src/components/editor/follow-pascal.tsx +354 -0
- package/src/components/editor/footprint-height-handle.tsx +204 -0
- package/src/components/editor/group-actions.ts +56 -2
- package/src/components/editor/group-move-3d.ts +2 -0
- package/src/components/editor/group-rotate-handle.tsx +2 -0
- package/src/components/editor/group-transform-shared.test.ts +460 -0
- 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 +409 -197
- 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/riser-diagram-panel.tsx +14 -7
- 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 +233 -0
- package/src/components/editor/room-labels.test.ts +850 -0
- package/src/components/editor/room-labels.tsx +1890 -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 +474 -419
- package/src/components/editor/slab-hole-highlights.tsx +5 -2
- package/src/components/editor/snapshot-capture-overlay.tsx +7 -2
- package/src/components/editor/snapshot-capture.test.ts +119 -60
- package/src/components/editor/snapshot-capture.ts +36 -5
- package/src/components/editor/thumbnail-generator.test.ts +654 -0
- package/src/components/editor/thumbnail-generator.tsx +1103 -223
- package/src/components/editor/use-floorplan-background-placement.ts +34 -12
- 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 +663 -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.test.tsx +54 -0
- package/src/components/editor/views/view-bar.tsx +343 -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 +8 -4
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
- package/src/components/editor-2d/floorplan-group-move.tsx +3 -0
- 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 +2 -2
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +116 -106
- 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 +319 -233
- package/src/components/editor-2d/renderers/floorplan-reveal.test.tsx +254 -0
- package/src/components/editor-2d/renderers/floorplan-reveal.ts +141 -0
- package/src/components/editor-2d/svg-paths.ts +12 -15
- package/src/components/point-ask/bubble.tsx +538 -0
- package/src/components/point-ask/hover-chip.tsx +91 -0
- package/src/components/point-ask/mode-pill.tsx +92 -0
- package/src/components/point-ask/optional-motion.ts +22 -0
- package/src/components/point-ask/outline-corners.tsx +93 -0
- package/src/components/point-ask/pins.tsx +711 -0
- package/src/components/point-ask/point-ask-layer.tsx +83 -0
- package/src/components/point-ask/point-ask-styles.ts +105 -0
- package/src/components/point-ask/region-marquee-styles.ts +15 -0
- package/src/components/point-ask/region-marquee.tsx +64 -0
- package/src/components/procedural-items/inspector.tsx +40 -15
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +12 -2
- 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/roof/roof-edit-system.tsx +1 -0
- 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/distribution/connection-compatibility.test.ts +34 -0
- package/src/components/tools/distribution/connection-compatibility.ts +43 -0
- package/src/components/tools/distribution/distribution-public.ts +9 -0
- package/src/components/tools/distribution/distribution-run-contract.test.ts +26 -0
- package/src/components/tools/distribution/distribution-run-contract.ts +94 -0
- package/src/components/tools/distribution/distribution-run-tool.test.ts +241 -0
- package/src/components/tools/distribution/distribution-run-tool.test.tsx +125 -0
- package/src/components/tools/distribution/distribution-run-tool.tsx +1323 -0
- package/src/components/tools/distribution/draw-alignment.ts +35 -0
- package/src/components/tools/distribution/ports.test.ts +56 -0
- package/src/components/tools/distribution/ports.ts +378 -0
- package/src/components/tools/distribution/run-cursor.test.ts +135 -0
- package/src/components/tools/distribution/run-cursor.ts +36 -0
- package/src/components/tools/distribution/run-direction-feedback.test.ts +60 -0
- package/src/components/tools/distribution/run-direction-feedback.tsx +251 -0
- package/src/components/tools/distribution/run-port-snap.test.ts +79 -0
- package/src/components/tools/distribution/run-port-snap.ts +25 -0
- package/src/components/tools/distribution/run-screen-direction.test.ts +23 -0
- package/src/components/tools/distribution/run-screen-direction.ts +30 -0
- package/src/components/tools/elevator/elevator-tool.tsx +3 -6
- package/src/components/tools/elevator/move-elevator-tool.tsx +2 -3
- package/src/components/tools/item/item-surface-strategy-resolver.test.ts +67 -68
- package/src/components/tools/item/placement-history.test.tsx +512 -0
- package/src/components/tools/item/placement-strategies.test.ts +46 -1
- package/src/components/tools/item/placement-strategies.ts +11 -10
- package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +56 -56
- 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 +148 -95
- 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 +62 -63
- package/src/components/tools/registry/item-surface-move.ts +2 -0
- package/src/components/tools/registry/item-surface-resolver.test.ts +26 -26
- package/src/components/tools/registry/move-registry-node-tool.test.ts +268 -1
- package/src/components/tools/registry/move-registry-node-tool.tsx +66 -30
- 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/tool-manager.tsx +59 -24
- package/src/components/tools/wall/wall-drafting.test.ts +175 -47
- package/src/components/tools/zone/zone-tool.tsx +4 -12
- package/src/components/ui/action-menu/action-button.tsx +9 -1
- package/src/components/ui/action-menu/camera-actions.tsx +9 -4
- package/src/components/ui/action-menu/control-modes.tsx +15 -42
- package/src/components/ui/action-menu/follow-pascal-pill.tsx +32 -0
- package/src/components/ui/action-menu/follow-pascal-toggle.tsx +36 -0
- package/src/components/ui/action-menu/index.tsx +56 -33
- package/src/components/ui/action-menu/measurement-control.tsx +14 -19
- package/src/components/ui/action-menu/placement.ts +20 -0
- package/src/components/ui/action-menu/point-ask-button.tsx +52 -0
- package/src/components/ui/action-menu/view-toggles.tsx +47 -42
- package/src/components/ui/build-panel/build-panel.tsx +242 -0
- package/src/components/ui/command-palette/editor-commands.tsx +7 -42
- package/src/components/ui/command-palette/index.tsx +50 -39
- package/src/components/ui/command-palette/ranked-groups.test.ts +31 -0
- package/src/components/ui/controls/action-button.tsx +5 -2
- 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/metric-control.tsx +1 -1
- package/src/components/ui/controls/scene-material-list.tsx +4 -32
- package/src/components/ui/controls/segmented-control.tsx +21 -8
- package/src/components/ui/controls/select-control.tsx +65 -0
- package/src/components/ui/controls/slider-control.tsx +1 -1
- package/src/components/ui/controls/toggle-control.tsx +3 -3
- package/src/components/ui/controls/tool-options-panel.tsx +1 -1
- package/src/components/ui/floating-level-selector.tsx +159 -59
- package/src/components/ui/helpers/building-helper.tsx +4 -1
- package/src/components/ui/helpers/contextual-helper-panel.tsx +181 -72
- package/src/components/ui/helpers/helper-manager.test.tsx +121 -0
- package/src/components/ui/helpers/helper-manager.tsx +331 -14
- package/src/components/ui/helpers/item-helper.tsx +17 -1
- 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 +28 -22
- 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 +4 -4
- package/src/components/ui/panels/mobile-selection-bar.tsx +8 -5
- 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 +161 -5
- 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 +130 -62
- package/src/components/ui/panels/parametric-field-control.tsx +3 -3
- package/src/components/ui/panels/parametric-inspector.tsx +7 -7
- 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 +790 -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/badge.tsx +8 -0
- package/src/components/ui/primitives/color-dot.tsx +36 -24
- package/src/components/ui/primitives/sidebar.tsx +3 -1
- package/src/components/ui/right-stack.test.ts +29 -0
- package/src/components/ui/right-stack.tsx +164 -0
- package/src/components/ui/sidebar/app-sidebar.tsx +1 -1
- package/src/components/ui/sidebar/icon-rail.tsx +8 -3
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +1 -1
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +6 -6
- package/src/components/ui/sidebar/panels/settings-panel/animation-speed-setting.tsx +40 -0
- package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +7 -8
- package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.test.tsx +170 -0
- package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.tsx +92 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +138 -27
- package/src/components/ui/sidebar/panels/settings-panel/interface-theme.test.tsx +20 -0
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +5 -5
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +4 -10
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -11
- 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 +181 -223
- 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 +13 -37
- 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 +2 -4
- package/src/components/ui/sidebar/tab-bar.tsx +14 -4
- 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-controls-bar.tsx +18 -6
- package/src/components/viewer/viewer-hover-label.tsx +34 -0
- package/src/components/viewer/viewer-rooms.tsx +191 -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 +215 -43
- package/src/hooks/use-auto-save.ts +60 -9
- package/src/hooks/use-ceiling-events.test.ts +123 -118
- package/src/hooks/use-grid-events.test.ts +105 -105
- package/src/hooks/use-keyboard.test.ts +243 -19
- package/src/hooks/use-keyboard.ts +118 -83
- 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 +71 -0
- package/src/hooks/use-placement-notice.ts +73 -0
- package/src/hooks/use-selected-room.ts +143 -0
- package/src/index.tsx +312 -58
- package/src/lib/__fixtures__/owner-victor-seattle-parking.json +1 -0
- package/src/lib/__fixtures__/owner-villa-aurelia-basement.json +1 -0
- package/src/lib/add-storey.test.ts +177 -0
- package/src/lib/add-storey.ts +59 -0
- package/src/lib/below-grade.test.tsx +171 -0
- package/src/lib/below-grade.ts +133 -0
- package/src/lib/camera-facing.test.ts +24 -0
- package/src/lib/camera-facing.ts +24 -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/continuation.test.ts +19 -1
- package/src/lib/continuation.ts +25 -4
- package/src/lib/direct-manipulation.ts +2 -0
- package/src/lib/door-interaction.ts +14 -0
- package/src/lib/download-project-file.ts +17 -0
- package/src/lib/editor-level-display.test.ts +96 -0
- package/src/lib/editor-level-display.ts +55 -0
- package/src/lib/editor-views.ts +178 -0
- package/src/lib/export-geometry-wait.test.ts +90 -0
- package/src/lib/export-texture-utils.ts +2 -4
- 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 +284 -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 +363 -27
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +177 -8
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +60 -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/pdfkit-standard-fonts.d.ts +5 -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 +124 -0
- package/src/lib/floorplan/sheet-export.ts +318 -0
- package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +768 -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 +230 -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 +157 -0
- package/src/lib/floorplan/site-plan/geometry.test.ts +269 -0
- package/src/lib/floorplan/site-plan/geometry.ts +142 -0
- package/src/lib/floorplan/site-plan/index.ts +55 -0
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +483 -0
- package/src/lib/floorplan/site-plan/site-annotations.ts +1084 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +287 -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 +70 -0
- package/src/lib/floorplan/types.ts +1 -27
- package/src/lib/follow-pascal-exterior.test-fixture.ts +17 -0
- package/src/lib/follow-pascal-exterior.test.ts +171 -0
- package/src/lib/follow-pascal-exterior.ts +39 -0
- package/src/lib/follow-pascal-presentation.test.ts +203 -0
- package/src/lib/follow-pascal-presentation.ts +129 -0
- package/src/lib/follow-pascal-sight.ts +84 -0
- package/src/lib/follow-pascal-targets.test.ts +406 -0
- package/src/lib/follow-pascal-targets.ts +198 -0
- package/src/lib/follow-pascal.test.ts +350 -0
- package/src/lib/follow-pascal.ts +284 -0
- package/src/lib/fresh-planar-placement.test.ts +148 -0
- package/src/lib/fresh-planar-placement.ts +114 -37
- package/src/lib/geometry-script/author.ts +157 -0
- package/src/lib/geometry-script/client.ts +159 -0
- package/src/lib/geometry-script/finish-scripts.ts +127 -0
- package/src/lib/geometry-script/protocol.ts +13 -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 +636 -126
- package/src/lib/glb-export.ts +241 -120
- package/src/lib/history.test.ts +18 -7
- 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 +22 -0
- package/src/lib/keyboard-pan.test.ts +129 -7
- package/src/lib/keyboard-pan.ts +161 -12
- package/src/lib/level-selection.test.ts +57 -0
- package/src/lib/level-selection.ts +16 -1
- 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 +187 -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 +392 -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/measurement-kind.test.ts +6 -8
- package/src/lib/measurements.test.ts +23 -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 +201 -0
- package/src/lib/orbit-floor-clearance.ts +154 -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 +122 -0
- package/src/lib/paint-pass-through.ts +78 -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 +267 -25
- package/src/lib/panel-rows.ts +21 -0
- package/src/lib/parametric-node-update.ts +16 -7
- 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 +65 -0
- package/src/lib/plugin-panels.test.ts +25 -0
- package/src/lib/plugin-panels.ts +94 -0
- package/src/lib/point-ask/__fixtures__/consumer-browser.tsx +76 -0
- package/src/lib/point-ask/__fixtures__/house.ts +81 -0
- package/src/lib/point-ask/ask-created.test.ts +195 -0
- package/src/lib/point-ask/ask-flow.test.ts +380 -0
- package/src/lib/point-ask/build-context.test.ts +173 -0
- package/src/lib/point-ask/build-context.ts +94 -0
- package/src/lib/point-ask/capture-crop.ts +129 -0
- package/src/lib/point-ask/choreography.ts +69 -0
- package/src/lib/point-ask/compare.test.ts +145 -0
- package/src/lib/point-ask/compare.ts +73 -0
- package/src/lib/point-ask/describe-target.test.ts +165 -0
- package/src/lib/point-ask/describe-target.ts +234 -0
- package/src/lib/point-ask/dictation.ts +79 -0
- package/src/lib/point-ask/edge-marker.test.ts +61 -0
- package/src/lib/point-ask/edge-marker.ts +27 -0
- package/src/lib/point-ask/frame-loop.ts +25 -0
- package/src/lib/point-ask/history-marks.test.ts +94 -0
- package/src/lib/point-ask/history-marks.ts +39 -0
- package/src/lib/point-ask/host-api.ts +32 -0
- package/src/lib/point-ask/outline-box.test.ts +30 -0
- package/src/lib/point-ask/outline-box.ts +11 -0
- package/src/lib/point-ask/overlay-metrics.ts +6 -0
- package/src/lib/point-ask/pick.test.ts +220 -0
- package/src/lib/point-ask/pick.ts +121 -0
- package/src/lib/point-ask/pin-machine.test.ts +494 -0
- package/src/lib/point-ask/pin-machine.ts +217 -0
- package/src/lib/point-ask/placement.test.ts +203 -0
- package/src/lib/point-ask/placement.ts +141 -0
- package/src/lib/point-ask/pointer-tracker.ts +34 -0
- package/src/lib/point-ask/projector.ts +83 -0
- package/src/lib/point-ask/region.test.ts +157 -0
- package/src/lib/point-ask/region.ts +136 -0
- package/src/lib/point-ask/retry.test.ts +217 -0
- package/src/lib/point-ask/retry.ts +37 -0
- package/src/lib/point-ask/scene-commit.test.ts +312 -0
- package/src/lib/point-ask/scene-commit.ts +32 -0
- package/src/lib/point-ask/session.ts +1391 -0
- package/src/lib/point-ask/spring-keyframes.test.ts +45 -0
- package/src/lib/point-ask/spring-keyframes.ts +38 -0
- package/src/lib/point-ask/spring.test.ts +63 -0
- package/src/lib/point-ask/spring.ts +107 -0
- package/src/lib/point-ask/suggestions.test.ts +80 -0
- package/src/lib/point-ask/suggestions.ts +125 -0
- package/src/lib/point-ask/target-scope.test.ts +622 -0
- package/src/lib/point-ask/target-scope.ts +338 -0
- package/src/lib/point-ask/types.ts +48 -0
- package/src/lib/portable-export.test.ts +34 -1
- package/src/lib/portable-export.ts +119 -53
- package/src/lib/print-feature-thickness.ts +10 -1
- package/src/lib/print-roof-solids.test.ts +39 -1
- package/src/lib/print-roof-solids.ts +43 -10
- package/src/lib/print-shell-compiler-manifold-core.ts +1 -1
- 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 +265 -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 +1017 -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 +610 -1
- package/src/lib/scene-clipboard.ts +269 -58
- 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 +54 -75
- package/src/lib/session-writes.test.ts +120 -0
- package/src/lib/session-writes.ts +205 -0
- package/src/lib/sfx-bus.ts +13 -0
- package/src/lib/sfx-player.test.ts +70 -9
- package/src/lib/sfx-player.ts +6 -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/stair-levels.test.ts +25 -0
- package/src/lib/stair-levels.ts +22 -3
- package/src/lib/terrace-draft.test.ts +145 -0
- package/src/lib/terrace-draft.ts +214 -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 +580 -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-placement-pointer.test.ts +42 -0
- package/src/lib/wall-placement-pointer.ts +57 -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-changes.ts +35 -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-audio.test.ts +146 -0
- package/src/store/use-audio.tsx +127 -3
- package/src/store/use-construction-reveal.test.ts +173 -0
- package/src/store/use-construction-reveal.ts +145 -0
- package/src/store/use-corner-snap-hint.test.ts +24 -0
- package/src/store/use-corner-snap-hint.ts +47 -0
- 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-follow-pascal.test.ts +62 -0
- package/src/store/use-follow-pascal.ts +53 -0
- package/src/store/use-point-ask.ts +247 -0
- package/src/store/use-quick-measurement-hud.ts +3 -3
- package/src/store/use-stair-build-preview.ts +33 -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 +201 -0
- package/src/store/view-layout.ts +199 -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/render-current-markup.tsx +16 -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/tools/stair/stair-click-guard.test.ts +0 -58
- package/src/components/tools/stair/stair-click-guard.ts +0 -72
- package/src/components/tools/stair/stair-defaults.ts +0 -18
- package/src/components/tools/stair/stair-tool.tsx +0 -634
- 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)
|
|
@@ -126,7 +138,7 @@ export function PanelWrapper({
|
|
|
126
138
|
) as AnyNodeId | undefined
|
|
127
139
|
// Subscribe to the selected node's *type* only — a string primitive that
|
|
128
140
|
// doesn't change as fields are edited (same trick as ParametricInspector).
|
|
129
|
-
const selectedType = useScene((s) => (selectedId ?
|
|
141
|
+
const selectedType = useScene((s) => (selectedId ? s.nodes[selectedId]?.type ?? null : null))
|
|
130
142
|
const installedPlugins = useScene((s) => s.installedPlugins)
|
|
131
143
|
const extensions = useMemo(() => {
|
|
132
144
|
// re-derive when plugin extensions register after mount (async plugin load)
|
|
@@ -143,24 +155,24 @@ export function PanelWrapper({
|
|
|
143
155
|
// See `lib/inspector-card-mode.ts` for the transition table.
|
|
144
156
|
const [activeExtensionId, setActiveExtensionId] = useState<string | null>(null)
|
|
145
157
|
// Stale ids (kind changed, plugin gated off) fall back to regular mode.
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
)
|
|
158
|
+
const primaryExtensionId = useScene((state) => {
|
|
159
|
+
const node = selectedId ? state.nodes[selectedId] : undefined
|
|
160
|
+
return node ? extensions.find((extension) => extension.primaryWhen?.(node))?.id : undefined
|
|
161
|
+
})
|
|
162
|
+
const primaryExtension = extensions.find((extension) => extension.id === primaryExtensionId)
|
|
163
|
+
const activeExtension = primaryExtension ?? resolveActiveExtension(activeExtensionId, extensions)
|
|
164
|
+
|
|
165
|
+
// The panel is collapsed to just its header until the user expands it. The
|
|
166
|
+
// choice is one editor preference for every inspector (room, wall, item…),
|
|
167
|
+
// persisted, so every panel opens the way the user last left one.
|
|
168
|
+
const collapsed = !useInspectorExpanded((state) => state.expanded)
|
|
169
|
+
const setCollapsed = useCallback((next: boolean) => {
|
|
170
|
+
useInspectorExpanded.getState().setExpanded(!next)
|
|
171
|
+
}, [])
|
|
172
|
+
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (primaryExtension) setCollapsed(false)
|
|
175
|
+
}, [primaryExtension, selectedId, setCollapsed])
|
|
164
176
|
|
|
165
177
|
const applyMode = useCallback(
|
|
166
178
|
(next: { collapsed: boolean; activeExtensionId: string | null }) => {
|
|
@@ -173,8 +185,12 @@ export function PanelWrapper({
|
|
|
173
185
|
// Chevron / header press — collapsed → regular, regular → collapsed,
|
|
174
186
|
// extension mode → regular (exit the extension first, stay expanded).
|
|
175
187
|
const handleCardToggle = useCallback(() => {
|
|
188
|
+
if (primaryExtension) {
|
|
189
|
+
setCollapsed(!collapsed)
|
|
190
|
+
return
|
|
191
|
+
}
|
|
176
192
|
applyMode(toggleCard({ collapsed, activeExtensionId }))
|
|
177
|
-
}, [applyMode, collapsed, activeExtensionId])
|
|
193
|
+
}, [applyMode, collapsed, activeExtensionId, primaryExtension, setCollapsed])
|
|
178
194
|
|
|
179
195
|
// Folding the card forgets the active extension — extension mode is a
|
|
180
196
|
// one-shot affordance of the header icon, not sticky panel state.
|
|
@@ -205,8 +221,8 @@ export function PanelWrapper({
|
|
|
205
221
|
|
|
206
222
|
const handleHeaderPointerDown = useCallback(
|
|
207
223
|
(e: React.PointerEvent<HTMLDivElement>) => {
|
|
208
|
-
// Buttons (close / reset / collapse) handle their own clicks.
|
|
209
|
-
if ((e.target as HTMLElement).closest('button')) return
|
|
224
|
+
// Buttons (close / reset / collapse) and a title edited in place handle their own clicks.
|
|
225
|
+
if ((e.target as HTMLElement).closest('button, input, label')) return
|
|
210
226
|
const rect = panelRef.current?.getBoundingClientRect()
|
|
211
227
|
if (!rect) return
|
|
212
228
|
const bounds = getDragBounds(panelRef.current)
|
|
@@ -279,9 +295,14 @@ export function PanelWrapper({
|
|
|
279
295
|
|
|
280
296
|
return (
|
|
281
297
|
<div
|
|
298
|
+
data-editor-inspector
|
|
282
299
|
className={cn(
|
|
283
300
|
isMobile
|
|
284
301
|
? 'flex h-full w-full flex-col overflow-hidden bg-transparent dark:text-foreground'
|
|
302
|
+
: inStack
|
|
303
|
+
? // In the shared right column: it grows down and scrolls on itself,
|
|
304
|
+
// leaving the shortcuts card below it its room (`right-stack.tsx`).
|
|
305
|
+
'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
306
|
// Cap height at `100dvh - 154px` so a tall panel's bottom edge
|
|
286
307
|
// aligns flush with the top of the floating bottom action bar.
|
|
287
308
|
// Combined with `top-20` (80px), the panel's bottom sits at
|
|
@@ -292,6 +313,7 @@ export function PanelWrapper({
|
|
|
292
313
|
: '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
314
|
className,
|
|
294
315
|
)}
|
|
316
|
+
data-panel-wrapper
|
|
295
317
|
ref={panelRef}
|
|
296
318
|
style={
|
|
297
319
|
isMobile
|
|
@@ -311,23 +333,24 @@ export function PanelWrapper({
|
|
|
311
333
|
!collapsed && 'border-border/50 border-b',
|
|
312
334
|
isDragging ? 'cursor-grabbing' : 'cursor-grab',
|
|
313
335
|
)}
|
|
336
|
+
data-panel-header
|
|
314
337
|
onPointerDown={handleHeaderPointerDown}
|
|
315
338
|
onPointerMove={handleHeaderPointerMove}
|
|
316
339
|
onPointerUp={handleHeaderPointerUp}
|
|
317
340
|
>
|
|
318
|
-
<div className=
|
|
341
|
+
<div className={cn('flex min-w-0 items-center gap-2', titleContent && 'flex-1 pr-2')}>
|
|
319
342
|
{onBack && (
|
|
320
343
|
<button
|
|
321
|
-
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"
|
|
344
|
+
className="mr-1 flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground"
|
|
322
345
|
onClick={onBack}
|
|
323
346
|
type="button"
|
|
324
347
|
>
|
|
325
348
|
<ChevronLeft className="h-4 w-4" />
|
|
326
349
|
</button>
|
|
327
350
|
)}
|
|
328
|
-
{icon &&
|
|
351
|
+
{primaryExtension ? renderExtensionIcon(primaryExtension.icon) : icon &&
|
|
329
352
|
(typeof icon === 'string' ? (
|
|
330
|
-
<
|
|
353
|
+
<NodeIconImage
|
|
331
354
|
alt=""
|
|
332
355
|
className="shrink-0 object-contain"
|
|
333
356
|
height={16}
|
|
@@ -337,18 +360,35 @@ export function PanelWrapper({
|
|
|
337
360
|
) : (
|
|
338
361
|
<span className="flex shrink-0 items-center justify-center">{icon}</span>
|
|
339
362
|
))}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
363
|
+
{kicker || titleContent ? (
|
|
364
|
+
<div className="flex min-w-0 flex-1 flex-col">
|
|
365
|
+
{kicker && (
|
|
366
|
+
<span className="truncate text-[11px] text-muted-foreground leading-tight" data-panel-kicker>
|
|
367
|
+
{kicker}
|
|
368
|
+
</span>
|
|
369
|
+
)}
|
|
370
|
+
{titleContent ?? (
|
|
371
|
+
<h2 className="truncate font-semibold text-foreground text-sm tracking-tight">
|
|
372
|
+
{primaryExtension?.title ?? title}
|
|
373
|
+
</h2>
|
|
374
|
+
)}
|
|
375
|
+
</div>
|
|
376
|
+
) : (
|
|
377
|
+
<h2 className="truncate font-semibold text-foreground text-sm tracking-tight">
|
|
378
|
+
{primaryExtension?.title ?? title}
|
|
379
|
+
</h2>
|
|
380
|
+
)}
|
|
343
381
|
</div>
|
|
344
382
|
|
|
345
|
-
{/* Centered grip — purely a visual drag affordance. */}
|
|
346
|
-
|
|
383
|
+
{/* Centered grip — purely a visual drag affordance (a title edited in place takes its room). */}
|
|
384
|
+
{!titleContent && (
|
|
385
|
+
<GripHorizontal className="-translate-x-1/2 pointer-events-none absolute left-1/2 h-4 w-4 text-muted-foreground/40" />
|
|
386
|
+
)}
|
|
347
387
|
|
|
348
388
|
<div className="flex items-center gap-1">
|
|
349
389
|
{onReset && (
|
|
350
390
|
<button
|
|
351
|
-
className="flex h-7 w-7 items-center justify-center rounded-md bg-[#2C2C2E] text-muted-foreground transition-colors hover:bg-[#3e3e3e] hover:text-foreground"
|
|
391
|
+
className="flex h-7 w-7 items-center justify-center rounded-md bg-black/4 dark:bg-[#2C2C2E] text-muted-foreground transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground"
|
|
352
392
|
onClick={onReset}
|
|
353
393
|
type="button"
|
|
354
394
|
>
|
|
@@ -360,7 +400,7 @@ export function PanelWrapper({
|
|
|
360
400
|
ONLY that extension's content (either/or with the regular
|
|
361
401
|
controls); the active icon (highlighted) or the chevron
|
|
362
402
|
returns to the regular controls. */}
|
|
363
|
-
{extensions.map((extension) => {
|
|
403
|
+
{extensions.filter((extension) => extension !== primaryExtension).map((extension) => {
|
|
364
404
|
const isActive = !collapsed && activeExtensionId === extension.id
|
|
365
405
|
return (
|
|
366
406
|
<button
|
|
@@ -369,8 +409,8 @@ export function PanelWrapper({
|
|
|
369
409
|
className={cn(
|
|
370
410
|
'flex h-7 w-7 items-center justify-center rounded-md transition-colors',
|
|
371
411
|
isActive
|
|
372
|
-
? 'bg-cyan-500/20 text-cyan-400 hover:bg-cyan-500/30'
|
|
373
|
-
: 'bg-[#2C2C2E] text-muted-foreground hover:bg-[#3e3e3e] hover:text-foreground',
|
|
412
|
+
? 'bg-cyan-500/20 text-cyan-700 dark:text-cyan-400 hover:bg-cyan-500/30'
|
|
413
|
+
: 'bg-black/4 dark:bg-[#2C2C2E] text-muted-foreground hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground',
|
|
374
414
|
)}
|
|
375
415
|
key={extension.id}
|
|
376
416
|
onClick={() =>
|
|
@@ -386,7 +426,7 @@ export function PanelWrapper({
|
|
|
386
426
|
<button
|
|
387
427
|
aria-expanded={!collapsed}
|
|
388
428
|
aria-label={collapsed ? 'Expand panel' : 'Collapse panel'}
|
|
389
|
-
className="flex h-7 w-7 items-center justify-center rounded-md bg-[#2C2C2E] text-muted-foreground transition-colors hover:bg-[#3e3e3e] hover:text-foreground"
|
|
429
|
+
className="flex h-7 w-7 items-center justify-center rounded-md bg-black/4 dark:bg-[#2C2C2E] text-muted-foreground transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground"
|
|
390
430
|
onClick={handleCardToggle}
|
|
391
431
|
type="button"
|
|
392
432
|
>
|
|
@@ -396,7 +436,7 @@ export function PanelWrapper({
|
|
|
396
436
|
</button>
|
|
397
437
|
{onClose && (
|
|
398
438
|
<button
|
|
399
|
-
className="flex h-7 w-7 items-center justify-center rounded-md bg-[#2C2C2E] text-muted-foreground transition-colors hover:bg-[#3e3e3e] hover:text-foreground"
|
|
439
|
+
className="flex h-7 w-7 items-center justify-center rounded-md bg-black/4 dark:bg-[#2C2C2E] text-muted-foreground transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground"
|
|
400
440
|
onClick={onClose}
|
|
401
441
|
type="button"
|
|
402
442
|
>
|
|
@@ -413,8 +453,10 @@ export function PanelWrapper({
|
|
|
413
453
|
controls (`children`). A stale extension id falls back to regular
|
|
414
454
|
via `resolveActiveExtension`. */}
|
|
415
455
|
{!(collapsed && !isMobile) && (
|
|
416
|
-
<div className="no-scrollbar flex min-h-0 flex-1 flex-col overflow-y-auto">
|
|
417
|
-
{
|
|
456
|
+
<div className="no-scrollbar flex min-h-0 flex-1 flex-col overflow-y-auto" data-panel-scroll>
|
|
457
|
+
{selectedId && primaryExtension ? (
|
|
458
|
+
<InspectorExtensionContent extension={primaryExtension} nodeId={selectedId} />
|
|
459
|
+
) : !isMobile && selectedId && activeExtension ? (
|
|
418
460
|
<InspectorExtensionSection
|
|
419
461
|
extension={activeExtension}
|
|
420
462
|
key={activeExtension.id}
|
|
@@ -422,7 +464,23 @@ export function PanelWrapper({
|
|
|
422
464
|
/>
|
|
423
465
|
) : (
|
|
424
466
|
<>
|
|
467
|
+
{/* The mobile sheet has its own header: a title edited in place moves into the body. */}
|
|
468
|
+
{isMobile && titleContent && (
|
|
469
|
+
<div className="flex flex-col px-4 pt-3">
|
|
470
|
+
{kicker && <span className="text-[11px] text-muted-foreground">{kicker}</span>}
|
|
471
|
+
{titleContent}
|
|
472
|
+
</div>
|
|
473
|
+
)}
|
|
425
474
|
{children}
|
|
475
|
+
{inspectedIds.length > 0 && (
|
|
476
|
+
<PanelSection title="Collections">
|
|
477
|
+
<ActionGroup>
|
|
478
|
+
<CollectionsPopover nodeIds={inspectedIds}>
|
|
479
|
+
<ActionButton label="Manage collections…" />
|
|
480
|
+
</CollectionsPopover>
|
|
481
|
+
</ActionGroup>
|
|
482
|
+
</PanelSection>
|
|
483
|
+
)}
|
|
426
484
|
{/* Mobile sheet has no header icons to swap modes — keep the
|
|
427
485
|
plugin sections appended after the kind's controls there. */}
|
|
428
486
|
{isMobile &&
|
|
@@ -453,7 +511,7 @@ export function PanelWrapper({
|
|
|
453
511
|
* inspector's `renderIcon` (plain <img> so no next/image server deps). */
|
|
454
512
|
function renderExtensionIcon(ref: IconRef): React.ReactNode {
|
|
455
513
|
if (ref.kind === 'url') {
|
|
456
|
-
return <
|
|
514
|
+
return <IconRefImage className="h-4 w-4 shrink-0" src={ref.src} />
|
|
457
515
|
}
|
|
458
516
|
if (ref.kind === 'iconify') {
|
|
459
517
|
return <Icon height={16} icon={ref.name} width={16} />
|
|
@@ -509,23 +567,33 @@ function InspectorExtensionSection({
|
|
|
509
567
|
defaultExpanded?: boolean
|
|
510
568
|
extension: InspectorExtension
|
|
511
569
|
nodeId: AnyNodeId
|
|
570
|
+
}) {
|
|
571
|
+
return (
|
|
572
|
+
<PanelSection defaultExpanded={defaultExpanded} title={extension.title}>
|
|
573
|
+
<InspectorExtensionContent extension={extension} nodeId={nodeId} />
|
|
574
|
+
</PanelSection>
|
|
575
|
+
)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function InspectorExtensionContent({
|
|
579
|
+
extension,
|
|
580
|
+
nodeId,
|
|
581
|
+
}: {
|
|
582
|
+
extension: InspectorExtension
|
|
583
|
+
nodeId: AnyNodeId
|
|
512
584
|
}) {
|
|
513
585
|
const node = useScene((s) => s.nodes[nodeId])
|
|
514
586
|
if (!node) return null
|
|
515
587
|
const Extension = resolveExtensionComponent(extension)
|
|
516
588
|
return (
|
|
517
|
-
<
|
|
518
|
-
<
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
524
|
-
>
|
|
525
|
-
|
|
526
|
-
<Extension node={node} />
|
|
527
|
-
</Suspense>
|
|
528
|
-
</ErrorBoundary>
|
|
529
|
-
</PanelSection>
|
|
589
|
+
<ErrorBoundary fallback={
|
|
590
|
+
<p className="p-1 text-muted-foreground text-xs">
|
|
591
|
+
“{extension.title}” hit an error and was unloaded for this session.
|
|
592
|
+
</p>
|
|
593
|
+
}>
|
|
594
|
+
<Suspense fallback={null}>
|
|
595
|
+
<Extension node={node} />
|
|
596
|
+
</Suspense>
|
|
597
|
+
</ErrorBoundary>
|
|
530
598
|
)
|
|
531
599
|
}
|
|
@@ -46,7 +46,7 @@ export function ParametricFieldControl({
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
case 'boolean': {
|
|
49
|
-
const checked = !mixed && value === true
|
|
49
|
+
const checked = !mixed && (value ?? field.default) === true
|
|
50
50
|
return (
|
|
51
51
|
<ToggleControl
|
|
52
52
|
checked={checked}
|
|
@@ -82,7 +82,7 @@ export function ParametricFieldControl({
|
|
|
82
82
|
<div className="flex items-center justify-between px-3 py-2">
|
|
83
83
|
<span className="text-foreground/80 text-xs">{prettifyKey(key)}</span>
|
|
84
84
|
<select
|
|
85
|
-
className="rounded-md border border-border/50 bg-[#2C2C2E] px-2 py-1 text-foreground text-xs focus:outline-none focus:ring-1 focus:ring-foreground/30"
|
|
85
|
+
className="rounded-md border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-2 py-1 text-foreground text-xs focus:outline-none focus:ring-1 focus:ring-foreground/30"
|
|
86
86
|
onChange={(e) => apply(e.target.value)}
|
|
87
87
|
value={mixed ? '' : str}
|
|
88
88
|
>
|
|
@@ -119,7 +119,7 @@ export function ParametricFieldControl({
|
|
|
119
119
|
value={str}
|
|
120
120
|
/>
|
|
121
121
|
<input
|
|
122
|
-
className="w-20 rounded-md border border-border/50 bg-[#2C2C2E] px-2 py-1 text-foreground text-xs focus:outline-none focus:ring-1 focus:ring-foreground/30"
|
|
122
|
+
className="w-20 rounded-md border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-2 py-1 text-foreground text-xs focus:outline-none focus:ring-1 focus:ring-foreground/30"
|
|
123
123
|
onChange={(e) => apply(e.target.value)}
|
|
124
124
|
type="text"
|
|
125
125
|
value={mixed ? 'Mixed' : str}
|