@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
|
@@ -24,11 +24,13 @@ import {
|
|
|
24
24
|
type BuildingNode,
|
|
25
25
|
DEFAULT_LEVEL_HEIGHT,
|
|
26
26
|
getStoredLevelHeight,
|
|
27
|
+
groundSeat,
|
|
27
28
|
LevelNode,
|
|
28
29
|
useScene,
|
|
29
30
|
} from '@pascal-app/core'
|
|
30
31
|
import { markPerfAction, useViewer } from '@pascal-app/viewer'
|
|
31
32
|
import {
|
|
33
|
+
ArrowDownToLine,
|
|
32
34
|
ClipboardPaste,
|
|
33
35
|
Copy,
|
|
34
36
|
GripVertical,
|
|
@@ -40,6 +42,7 @@ import {
|
|
|
40
42
|
import {
|
|
41
43
|
type ButtonHTMLAttributes,
|
|
42
44
|
type CSSProperties,
|
|
45
|
+
Fragment,
|
|
43
46
|
useCallback,
|
|
44
47
|
useEffect,
|
|
45
48
|
useRef,
|
|
@@ -50,12 +53,19 @@ import { pasteSelectionAndPickUp } from '../editor/group-actions'
|
|
|
50
53
|
import {
|
|
51
54
|
buildLevelDuplicateCreateOps,
|
|
52
55
|
type LevelDuplicatePreset,
|
|
53
|
-
} from '
|
|
54
|
-
import {
|
|
56
|
+
} from '@pascal-app/core/building'
|
|
57
|
+
import {
|
|
58
|
+
getDefaultLevelName,
|
|
59
|
+
getLevelDisplayName,
|
|
60
|
+
roofLevelOrderBreach,
|
|
61
|
+
servesAsRoofLevel,
|
|
62
|
+
} from '@pascal-app/core'
|
|
63
|
+
import { addStoreyBelow, addStoreyOnTop, setGroundFloor } from '../../lib/add-storey'
|
|
55
64
|
import { deleteLevelWithFallbackSelection } from '../../lib/level-selection'
|
|
56
65
|
import { unitMemberLevels, leaveUnitFocus } from '../../lib/units'
|
|
57
66
|
import { useLinearDisplay } from '../../lib/use-linear-display'
|
|
58
67
|
import { cn } from '../../lib/utils'
|
|
68
|
+
import { useIsMobile } from '../../hooks/use-mobile'
|
|
59
69
|
import { ActionButton } from './controls/action-button'
|
|
60
70
|
import { SliderControl } from './controls/slider-control'
|
|
61
71
|
import { LevelDuplicateDialog } from './level-duplicate-dialog'
|
|
@@ -140,6 +150,8 @@ function LevelRow({
|
|
|
140
150
|
onDuplicate,
|
|
141
151
|
onPaste,
|
|
142
152
|
onRequestDelete,
|
|
153
|
+
onSetGround,
|
|
154
|
+
isGround,
|
|
143
155
|
unitDotColor,
|
|
144
156
|
}: {
|
|
145
157
|
level: LevelNode
|
|
@@ -153,6 +165,10 @@ function LevelRow({
|
|
|
153
165
|
onDuplicate: (preset?: LevelDuplicatePreset) => void
|
|
154
166
|
onPaste?: () => void
|
|
155
167
|
onRequestDelete: () => void
|
|
168
|
+
/** Makes this level the ground floor: the line moves under it. */
|
|
169
|
+
onSetGround: () => void
|
|
170
|
+
/** This level is the ground floor, seated at grade: nothing left to set. */
|
|
171
|
+
isGround: boolean
|
|
156
172
|
}) {
|
|
157
173
|
const [duplicateDialogOpen, setDuplicateDialogOpen] = useState(false)
|
|
158
174
|
const [isEditing, setIsEditing] = useState(false)
|
|
@@ -195,17 +211,17 @@ function LevelRow({
|
|
|
195
211
|
<div
|
|
196
212
|
className={cn(
|
|
197
213
|
'flex items-center rounded-lg transition-colors',
|
|
198
|
-
isDragging && 'bg-white/10 text-foreground shadow-lg',
|
|
214
|
+
isDragging && 'bg-black/6 dark:bg-white/10 text-foreground shadow-lg',
|
|
199
215
|
isSelected
|
|
200
|
-
? 'bg-white/10 text-foreground'
|
|
201
|
-
: 'text-muted-foreground/70 hover:bg-white/5 hover:text-muted-foreground',
|
|
216
|
+
? 'bg-black/6 dark:bg-white/10 text-foreground'
|
|
217
|
+
: 'text-muted-foreground/70 hover:bg-black/4 dark:hover:bg-white/5 hover:text-muted-foreground',
|
|
202
218
|
)}
|
|
203
219
|
>
|
|
204
220
|
<button
|
|
205
221
|
{...dragHandleProps}
|
|
206
222
|
aria-label={`Reorder ${getLevelDisplayName(level)}`}
|
|
207
223
|
className={cn(
|
|
208
|
-
'ml-0.5 flex h-6 w-4 shrink-0 cursor-grab touch-none items-center justify-center rounded-md text-muted-foreground/35 opacity-0 transition-colors hover:bg-white/5 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 group-hover/level:opacity-100',
|
|
224
|
+
'ml-0.5 flex h-6 w-4 shrink-0 cursor-grab touch-none items-center justify-center rounded-md text-muted-foreground/35 opacity-0 transition-colors hover:bg-black/4 dark:hover:bg-white/5 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 group-hover/level:opacity-100',
|
|
209
225
|
isDragging && 'cursor-grabbing opacity-100',
|
|
210
226
|
)}
|
|
211
227
|
onClick={(e) => {
|
|
@@ -243,7 +259,7 @@ function LevelRow({
|
|
|
243
259
|
<Popover>
|
|
244
260
|
<PopoverTrigger asChild>
|
|
245
261
|
<button
|
|
246
|
-
className="mr-0.5 shrink-0 whitespace-nowrap rounded px-1 py-0.5 font-mono text-[10px] text-muted-foreground
|
|
262
|
+
className="mr-0.5 shrink-0 whitespace-nowrap rounded px-1 py-0.5 font-mono text-[10px] text-muted-foreground tabular-nums transition-colors hover:bg-black/4 hover:text-foreground dark:text-muted-foreground/50 dark:hover:bg-white/5 dark:hover:text-foreground"
|
|
247
263
|
onClick={(e) => e.stopPropagation()}
|
|
248
264
|
title="Level height"
|
|
249
265
|
type="button"
|
|
@@ -294,7 +310,7 @@ function LevelRow({
|
|
|
294
310
|
</PopoverTrigger>
|
|
295
311
|
<PopoverContent align="start" className="w-44 p-1" side="right" sideOffset={8}>
|
|
296
312
|
<button
|
|
297
|
-
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors hover:bg-white/10 hover:text-foreground"
|
|
313
|
+
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors hover:bg-black/6 dark:hover:bg-white/10 hover:text-foreground"
|
|
298
314
|
onClick={(e) => {
|
|
299
315
|
e.stopPropagation()
|
|
300
316
|
onDuplicate()
|
|
@@ -305,7 +321,7 @@ function LevelRow({
|
|
|
305
321
|
Duplicate level
|
|
306
322
|
</button>
|
|
307
323
|
<button
|
|
308
|
-
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors hover:bg-white/10 hover:text-foreground"
|
|
324
|
+
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors hover:bg-black/6 dark:hover:bg-white/10 hover:text-foreground"
|
|
309
325
|
onClick={(e) => {
|
|
310
326
|
e.stopPropagation()
|
|
311
327
|
setDuplicateDialogOpen(true)
|
|
@@ -315,9 +331,26 @@ function LevelRow({
|
|
|
315
331
|
<Copy className="h-3 w-3" />
|
|
316
332
|
Duplicate with options...
|
|
317
333
|
</button>
|
|
334
|
+
<button
|
|
335
|
+
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors enabled:hover:bg-black/6 dark:enabled:hover:bg-white/10 enabled:hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
336
|
+
disabled={isGround}
|
|
337
|
+
onClick={(e) => {
|
|
338
|
+
e.stopPropagation()
|
|
339
|
+
onSetGround()
|
|
340
|
+
}}
|
|
341
|
+
title={
|
|
342
|
+
isGround
|
|
343
|
+
? 'This floor stands at grade'
|
|
344
|
+
: 'Seat the building so this floor stands at grade; the floors under it are below grade'
|
|
345
|
+
}
|
|
346
|
+
type="button"
|
|
347
|
+
>
|
|
348
|
+
<ArrowDownToLine className="h-3 w-3" />
|
|
349
|
+
Set as ground floor
|
|
350
|
+
</button>
|
|
318
351
|
{onPaste && (
|
|
319
352
|
<button
|
|
320
|
-
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors hover:bg-white/10 hover:text-foreground"
|
|
353
|
+
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors hover:bg-black/6 dark:hover:bg-white/10 hover:text-foreground"
|
|
321
354
|
onClick={(e) => {
|
|
322
355
|
e.stopPropagation()
|
|
323
356
|
onPaste()
|
|
@@ -329,7 +362,7 @@ function LevelRow({
|
|
|
329
362
|
</button>
|
|
330
363
|
)}
|
|
331
364
|
<button
|
|
332
|
-
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors enabled:hover:bg-white/10 enabled:hover:text-red-400 disabled:cursor-not-allowed disabled:opacity-50"
|
|
365
|
+
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors enabled:hover:bg-black/6 dark:enabled:hover:bg-white/10 enabled:hover:text-red-600 dark:enabled:hover:text-red-400 disabled:cursor-not-allowed disabled:opacity-50"
|
|
333
366
|
disabled={!canDeleteLevel}
|
|
334
367
|
onClick={(e) => {
|
|
335
368
|
e.stopPropagation()
|
|
@@ -365,6 +398,8 @@ function SortableLevelRow({
|
|
|
365
398
|
onDuplicate,
|
|
366
399
|
onPaste,
|
|
367
400
|
onRequestDelete,
|
|
401
|
+
onSetGround,
|
|
402
|
+
isGround,
|
|
368
403
|
unitDotColor,
|
|
369
404
|
}: {
|
|
370
405
|
level: LevelNode
|
|
@@ -374,6 +409,8 @@ function SortableLevelRow({
|
|
|
374
409
|
onDuplicate: (preset?: LevelDuplicatePreset) => void
|
|
375
410
|
onPaste?: () => void
|
|
376
411
|
onRequestDelete: () => void
|
|
412
|
+
onSetGround: () => void
|
|
413
|
+
isGround: boolean
|
|
377
414
|
}) {
|
|
378
415
|
const {
|
|
379
416
|
attributes,
|
|
@@ -403,14 +440,67 @@ function SortableLevelRow({
|
|
|
403
440
|
level={level}
|
|
404
441
|
onDuplicate={onDuplicate}
|
|
405
442
|
onPaste={onPaste}
|
|
443
|
+
isGround={isGround}
|
|
406
444
|
onRequestDelete={onRequestDelete}
|
|
407
445
|
onSelect={onSelect}
|
|
446
|
+
onSetGround={onSetGround}
|
|
408
447
|
unitDotColor={unitDotColor}
|
|
409
448
|
/>
|
|
410
449
|
</div>
|
|
411
450
|
)
|
|
412
451
|
}
|
|
413
452
|
|
|
453
|
+
// ── The ground line: between the level at grade and the ones under it ───────
|
|
454
|
+
|
|
455
|
+
type GroundLineState = {
|
|
456
|
+
levelId: string
|
|
457
|
+
seated: boolean
|
|
458
|
+
atFoundationTop: boolean
|
|
459
|
+
offset: number
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* "Ground · +0.20": where the stack meets grade, read as the ground floor's datum over the ground
|
|
464
|
+
* (the top of the foundation, 8 in over it once seated). The levels under it are below grade. When
|
|
465
|
+
* the datum stands outside the seat (a stack that lifted it, or sank it) the line turns amber and
|
|
466
|
+
* a click seats the building.
|
|
467
|
+
*/
|
|
468
|
+
function GroundLine({ ground, onSeat }: { ground: GroundLineState; onSeat: () => void }) {
|
|
469
|
+
const { toDisplay, displayUnit, precision } = useLinearDisplay('m', 2)
|
|
470
|
+
const over = toDisplay(ground.offset)
|
|
471
|
+
const label = `Ground · ${over > 0 ? '+' : ''}${over.toFixed(precision)}`
|
|
472
|
+
return (
|
|
473
|
+
<div
|
|
474
|
+
className="relative flex items-center gap-1.5 px-2 py-0.5"
|
|
475
|
+
data-guide-target="level-ground-line"
|
|
476
|
+
>
|
|
477
|
+
{ground.seated ? (
|
|
478
|
+
<span
|
|
479
|
+
className="shrink-0 font-mono text-[10px] text-muted-foreground/70 tabular-nums"
|
|
480
|
+
title="Ground line: the levels under it are below grade"
|
|
481
|
+
>
|
|
482
|
+
{label}
|
|
483
|
+
</span>
|
|
484
|
+
) : (
|
|
485
|
+
<button
|
|
486
|
+
className="shrink-0 rounded-full px-1.5 font-mono text-[10px] text-amber-400 tabular-nums transition-colors hover:bg-amber-400/10"
|
|
487
|
+
onClick={onSeat}
|
|
488
|
+
title={`The ground floor stands ${Math.abs(toDisplay(ground.offset)).toFixed(precision)} ${displayUnit} ${ground.offset > 0 ? 'over' : 'under'} grade. Seat the ground floor 8 in over grade`}
|
|
489
|
+
type="button"
|
|
490
|
+
>
|
|
491
|
+
{label}
|
|
492
|
+
</button>
|
|
493
|
+
)}
|
|
494
|
+
<span
|
|
495
|
+
className={cn(
|
|
496
|
+
'h-px flex-1',
|
|
497
|
+
ground.seated ? 'bg-foreground/25' : 'bg-amber-400/60',
|
|
498
|
+
)}
|
|
499
|
+
/>
|
|
500
|
+
</div>
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
|
|
414
504
|
// ── Unit focus chip: name + × to end focus, only while a unit is focused ────
|
|
415
505
|
|
|
416
506
|
function UnitFocusChip() {
|
|
@@ -434,7 +524,7 @@ function UnitFocusChip() {
|
|
|
434
524
|
<span className="truncate">{focusedUnit.name || 'Unit'}</span>
|
|
435
525
|
<button
|
|
436
526
|
aria-label="Exit unit focus"
|
|
437
|
-
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-white/10 hover:text-foreground"
|
|
527
|
+
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-black/6 dark:hover:bg-white/10 hover:text-foreground"
|
|
438
528
|
onClick={() => leaveUnitFocus()}
|
|
439
529
|
title="Exit unit focus"
|
|
440
530
|
type="button"
|
|
@@ -448,11 +538,13 @@ function UnitFocusChip() {
|
|
|
448
538
|
// ── Main component ──────────────────────────────────────────────────────────
|
|
449
539
|
|
|
450
540
|
export function FloatingLevelSelector() {
|
|
541
|
+
// Desktop's view bar is a row above the stage; mobile's floats over it.
|
|
542
|
+
const isMobile = useIsMobile()
|
|
451
543
|
const selectedBuildingId = useViewer((s) => s.selection.buildingId)
|
|
452
544
|
const levelId = useViewer((s) => s.selection.levelId)
|
|
453
545
|
const setSelection = useViewer((s) => s.setSelection)
|
|
454
546
|
const createNode = useScene((s) => s.createNode)
|
|
455
|
-
const
|
|
547
|
+
const applyNodeChanges = useScene((s) => s.applyNodeChanges)
|
|
456
548
|
const updateNodes = useScene((s) => s.updateNodes)
|
|
457
549
|
|
|
458
550
|
const [deletingLevel, setDeletingLevel] = useState<LevelNode | null>(null)
|
|
@@ -497,31 +589,40 @@ export function FloatingLevelSelector() {
|
|
|
497
589
|
}),
|
|
498
590
|
)
|
|
499
591
|
|
|
592
|
+
const ground = useScene(
|
|
593
|
+
useShallow((state): GroundLineState | null => {
|
|
594
|
+
const seat = resolvedBuildingId ? groundSeat(state.nodes, resolvedBuildingId) : null
|
|
595
|
+
return seat
|
|
596
|
+
? {
|
|
597
|
+
levelId: seat.levelId,
|
|
598
|
+
seated: seat.seated,
|
|
599
|
+
atFoundationTop: seat.atFoundationTop,
|
|
600
|
+
offset: seat.offset,
|
|
601
|
+
}
|
|
602
|
+
: null
|
|
603
|
+
}),
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
const handleSetGround = useCallback((levelId: string) => {
|
|
607
|
+
setGroundFloor(levelId as LevelNode['id'])
|
|
608
|
+
}, [])
|
|
609
|
+
|
|
610
|
+
const topIsRoofLevel = useScene((state) => {
|
|
611
|
+
const top = levels.at(-1)
|
|
612
|
+
return top ? servesAsRoofLevel(state.nodes, top) : false
|
|
613
|
+
})
|
|
614
|
+
|
|
500
615
|
const handleAddAbove = useCallback(() => {
|
|
501
616
|
if (!resolvedBuildingId) return
|
|
502
|
-
const
|
|
503
|
-
const newLevel = LevelNode.parse({
|
|
504
|
-
level: maxLevel + 1,
|
|
505
|
-
height: DEFAULT_LEVEL_HEIGHT,
|
|
506
|
-
children: [],
|
|
507
|
-
parentId: resolvedBuildingId,
|
|
508
|
-
})
|
|
509
|
-
createNode(newLevel, resolvedBuildingId)
|
|
617
|
+
const newLevel = addStoreyOnTop(resolvedBuildingId)
|
|
510
618
|
setSelection({ buildingId: resolvedBuildingId, levelId: newLevel.id })
|
|
511
|
-
}, [resolvedBuildingId,
|
|
619
|
+
}, [resolvedBuildingId, setSelection])
|
|
512
620
|
|
|
513
621
|
const handleAddBelow = useCallback(() => {
|
|
514
622
|
if (!resolvedBuildingId) return
|
|
515
|
-
const
|
|
516
|
-
const newLevel = LevelNode.parse({
|
|
517
|
-
level: minLevel - 1,
|
|
518
|
-
height: DEFAULT_LEVEL_HEIGHT,
|
|
519
|
-
children: [],
|
|
520
|
-
parentId: resolvedBuildingId,
|
|
521
|
-
})
|
|
522
|
-
createNode(newLevel, resolvedBuildingId)
|
|
623
|
+
const newLevel = addStoreyBelow(resolvedBuildingId)
|
|
523
624
|
setSelection({ buildingId: resolvedBuildingId, levelId: newLevel.id })
|
|
524
|
-
}, [resolvedBuildingId,
|
|
625
|
+
}, [resolvedBuildingId, setSelection])
|
|
525
626
|
|
|
526
627
|
const handleInsertBetween = useCallback(
|
|
527
628
|
(lowerIndex: number) => {
|
|
@@ -530,26 +631,22 @@ export function FloatingLevelSelector() {
|
|
|
530
631
|
if (!lower) return
|
|
531
632
|
|
|
532
633
|
const newLevelNumber = lower.level + 1
|
|
533
|
-
const toShift = levels.filter((l) => l.level >= newLevelNumber)
|
|
534
|
-
if (toShift.length > 0) {
|
|
535
|
-
updateNodes(
|
|
536
|
-
toShift.map((l) => ({
|
|
537
|
-
id: l.id as AnyNodeId,
|
|
538
|
-
data: { level: l.level + 1 } as Partial<AnyNode>,
|
|
539
|
-
})),
|
|
540
|
-
)
|
|
541
|
-
}
|
|
542
|
-
|
|
543
634
|
const newLevel = LevelNode.parse({
|
|
544
635
|
level: newLevelNumber,
|
|
545
636
|
height: DEFAULT_LEVEL_HEIGHT,
|
|
546
637
|
children: [],
|
|
547
638
|
parentId: resolvedBuildingId,
|
|
548
639
|
})
|
|
549
|
-
|
|
640
|
+
// One undo step: undoing the floor also moves the levels above back down.
|
|
641
|
+
applyNodeChanges({
|
|
642
|
+
create: [{ node: newLevel, parentId: resolvedBuildingId }],
|
|
643
|
+
update: levels
|
|
644
|
+
.filter((l) => l.level >= newLevelNumber)
|
|
645
|
+
.map((l) => ({ id: l.id as AnyNodeId, data: { level: l.level + 1 } as Partial<AnyNode> })),
|
|
646
|
+
})
|
|
550
647
|
setSelection({ buildingId: resolvedBuildingId, levelId: newLevel.id })
|
|
551
648
|
},
|
|
552
|
-
[resolvedBuildingId, levels,
|
|
649
|
+
[resolvedBuildingId, levels, applyNodeChanges, setSelection],
|
|
553
650
|
)
|
|
554
651
|
|
|
555
652
|
const handleConfirmDelete = useCallback(() => {
|
|
@@ -560,29 +657,21 @@ export function FloatingLevelSelector() {
|
|
|
560
657
|
|
|
561
658
|
const handleDuplicateLevel = useCallback(
|
|
562
659
|
(level: LevelNode, preset: LevelDuplicatePreset = 'everything') => {
|
|
563
|
-
const { createOps, newLevelId,
|
|
660
|
+
const { createOps, newLevelId, updateOps } = buildLevelDuplicateCreateOps({
|
|
564
661
|
nodes: useScene.getState().nodes,
|
|
565
662
|
level,
|
|
566
663
|
levels,
|
|
567
664
|
preset,
|
|
568
665
|
})
|
|
569
666
|
|
|
570
|
-
|
|
571
|
-
updateNodes(
|
|
572
|
-
shiftedLevels.map((shiftedLevel) => ({
|
|
573
|
-
id: shiftedLevel.id as AnyNodeId,
|
|
574
|
-
data: { level: shiftedLevel.level } as Partial<AnyNode>,
|
|
575
|
-
})),
|
|
576
|
-
)
|
|
577
|
-
}
|
|
578
|
-
createNodes(createOps)
|
|
667
|
+
applyNodeChanges({ create: createOps, update: updateOps })
|
|
579
668
|
|
|
580
669
|
setSelection({
|
|
581
670
|
buildingId: resolvedBuildingId ?? undefined,
|
|
582
671
|
levelId: newLevelId as LevelNode['id'],
|
|
583
672
|
})
|
|
584
673
|
},
|
|
585
|
-
[
|
|
674
|
+
[applyNodeChanges, levels, resolvedBuildingId, setSelection],
|
|
586
675
|
)
|
|
587
676
|
|
|
588
677
|
const handlePasteToLevel = useCallback((level: LevelNode) => {
|
|
@@ -607,6 +696,11 @@ export function FloatingLevelSelector() {
|
|
|
607
696
|
|
|
608
697
|
const reorderedVisualLevels = arrayMove(visualLevels, oldIndex, newIndex)
|
|
609
698
|
const levelNumbersDescending = levels.map((level) => level.level).sort((a, b) => b - a)
|
|
699
|
+
// The roof level stays above every storey: a drop putting it under one it stood over is refused.
|
|
700
|
+
const floors = new Map(
|
|
701
|
+
reorderedVisualLevels.map((level, index) => [level.id, levelNumbersDescending[index]!]),
|
|
702
|
+
)
|
|
703
|
+
if (roofLevelOrderBreach(useScene.getState().nodes, floors)) return
|
|
610
704
|
|
|
611
705
|
const updates = reorderedVisualLevels
|
|
612
706
|
.map((level, index) => ({
|
|
@@ -637,13 +731,13 @@ export function FloatingLevelSelector() {
|
|
|
637
731
|
const sortableLevelIds = reversedLevels.map((level) => level.id)
|
|
638
732
|
|
|
639
733
|
const addButtonClass =
|
|
640
|
-
'absolute left-1/2 z-10 flex h-4 w-4 -translate-x-1/2 items-center justify-center rounded-full border border-border/80 bg-
|
|
734
|
+
'absolute left-1/2 z-10 flex h-4 w-4 -translate-x-1/2 items-center justify-center rounded-full border border-border/80 bg-white text-muted-foreground/60 shadow-md transition-colors hover:bg-neutral-100 hover:text-foreground dark:bg-neutral-800 dark:hover:bg-neutral-700'
|
|
641
735
|
|
|
642
736
|
return (
|
|
643
737
|
<>
|
|
644
|
-
<div className=
|
|
738
|
+
<div className={cn('pointer-events-auto absolute left-3 z-20', isMobile ? 'top-14' : 'top-3')}>
|
|
645
739
|
<div className="relative">
|
|
646
|
-
{/* Floating + at top edge */}
|
|
740
|
+
{/* Floating + at top edge: a floor on top, under the roof level when one tops the stack */}
|
|
647
741
|
{!draggingLevelId && (
|
|
648
742
|
<button
|
|
649
743
|
className={cn(addButtonClass, 'top-0 -translate-y-1/2')}
|
|
@@ -651,7 +745,7 @@ export function FloatingLevelSelector() {
|
|
|
651
745
|
// read only from outside: nothing here depends on it.
|
|
652
746
|
data-guide-target="level-add"
|
|
653
747
|
onClick={handleAddAbove}
|
|
654
|
-
title=
|
|
748
|
+
title={topIsRoofLevel ? 'Add a floor under the roof' : 'Add level above'}
|
|
655
749
|
type="button"
|
|
656
750
|
>
|
|
657
751
|
<Plus className="h-2.5 w-2.5" />
|
|
@@ -686,6 +780,7 @@ export function FloatingLevelSelector() {
|
|
|
686
780
|
const showGapBelow = i < reversedLevels.length - 1
|
|
687
781
|
|
|
688
782
|
return (
|
|
783
|
+
<Fragment key={level.id}>
|
|
689
784
|
<div
|
|
690
785
|
className="relative"
|
|
691
786
|
// A stable hook for host-app onboarding to point at, on
|
|
@@ -693,7 +788,6 @@ export function FloatingLevelSelector() {
|
|
|
693
788
|
// without knowing the building. Static, and read only
|
|
694
789
|
// from outside: nothing here depends on it.
|
|
695
790
|
data-guide-target={level.level === 0 ? 'level-ground' : undefined}
|
|
696
|
-
key={level.id}
|
|
697
791
|
>
|
|
698
792
|
<SortableLevelRow
|
|
699
793
|
isSelected={isSelected}
|
|
@@ -705,7 +799,9 @@ export function FloatingLevelSelector() {
|
|
|
705
799
|
}
|
|
706
800
|
onDuplicate={(preset) => handleDuplicateLevel(level, preset)}
|
|
707
801
|
onPaste={() => handlePasteToLevel(level)}
|
|
802
|
+
isGround={ground?.levelId === level.id && ground.atFoundationTop}
|
|
708
803
|
onRequestDelete={() => setDeletingLevel(level)}
|
|
804
|
+
onSetGround={() => handleSetGround(level.id)}
|
|
709
805
|
onSelect={() => {
|
|
710
806
|
if (!isSelected) markPerfAction('level-switch', level.id)
|
|
711
807
|
setSelection(
|
|
@@ -727,6 +823,10 @@ export function FloatingLevelSelector() {
|
|
|
727
823
|
</button>
|
|
728
824
|
)}
|
|
729
825
|
</div>
|
|
826
|
+
{ground?.levelId === level.id && !draggingLevelId && (
|
|
827
|
+
<GroundLine ground={ground} onSeat={() => handleSetGround(level.id)} />
|
|
828
|
+
)}
|
|
829
|
+
</Fragment>
|
|
730
830
|
)
|
|
731
831
|
})}
|
|
732
832
|
</div>
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
import type { HudTitle } from '../../../lib/hud-title'
|
|
1
2
|
import { ContextualHelperPanel } from './contextual-helper-panel'
|
|
2
3
|
|
|
3
4
|
interface BuildingHelperProps {
|
|
4
5
|
showRotate?: boolean
|
|
6
|
+
title?: HudTitle | null
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
// Rotate is one hint with both keys (R / T) — never two separate
|
|
8
10
|
// counterclockwise / clockwise rows — to match every other placement helper.
|
|
9
|
-
export function BuildingHelper({ showRotate }: BuildingHelperProps) {
|
|
11
|
+
export function BuildingHelper({ showRotate, title = null }: BuildingHelperProps) {
|
|
10
12
|
return (
|
|
11
13
|
<ContextualHelperPanel
|
|
14
|
+
title={title}
|
|
12
15
|
hints={[
|
|
13
16
|
{ keys: ['Left click'], label: 'Place building' },
|
|
14
17
|
...(showRotate ? [{ keys: ['R', 'T'], label: 'Rotate' }] : []),
|