@pascal-app/editor 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -9
- package/src/components/editor/action-menu-button.tsx +83 -0
- package/src/components/editor/bake-thumbnail.tsx +315 -4
- package/src/components/editor/capture-level-picker.tsx +72 -0
- package/src/components/editor/custom-camera-controls.tsx +202 -57
- package/src/components/editor/delete-confirmation-dialog.tsx +297 -7
- package/src/components/editor/duplicate-node.ts +244 -0
- package/src/components/editor/editor-layout-mobile.tsx +36 -25
- package/src/components/editor/editor-layout-v2.tsx +131 -58
- package/src/components/editor/export-manager.tsx +34 -8
- package/src/components/editor/first-person/build-collider-world.test.ts +51 -0
- package/src/components/editor/first-person/build-collider-world.ts +6 -2
- package/src/components/editor/first-person/mechanism-targets.test.ts +75 -0
- package/src/components/editor/first-person/mechanism-targets.ts +36 -0
- package/src/components/editor/first-person-controls.tsx +205 -2
- package/src/components/editor/floating-action-menu.tsx +63 -207
- package/src/components/editor/floor-aware-camera-controls.test.ts +442 -0
- package/src/components/editor/floor-aware-camera-controls.ts +324 -0
- package/src/components/editor/floor-openings-overlay.tsx +243 -0
- package/src/components/editor/floor-region-controls.tsx +846 -0
- package/src/components/editor/floor-region-webgpu-materials.test.ts +12 -0
- package/src/components/editor/floor-surface.test.ts +165 -0
- package/src/components/editor/floor-surface.ts +182 -0
- package/src/components/editor/floorplan-background-selection.test.ts +11 -2
- package/src/components/editor/floorplan-background-selection.ts +1 -3
- package/src/components/editor/floorplan-panel.tsx +344 -342
- package/src/components/editor/footprint-height-handle.tsx +204 -0
- package/src/components/editor/group-actions.ts +54 -2
- package/src/components/editor/handles/handle-hit-priority.test.tsx +63 -0
- package/src/components/editor/handles/handle-hit-priority.tsx +37 -0
- package/src/components/editor/handles/swallow-next-click.test.ts +28 -0
- package/src/components/editor/handles/use-handle-drag.ts +269 -197
- package/src/components/editor/index.tsx +294 -169
- package/src/components/editor/join-walls-pill.tsx +44 -0
- package/src/components/editor/node-action-menu.tsx +68 -70
- package/src/components/editor/node-arrow-handles.tsx +25 -6
- package/src/components/editor/open-wall-ends-3d-layer.tsx +273 -0
- package/src/components/editor/open-wall-ends-hint.tsx +89 -0
- package/src/components/editor/quick-measurement-hud.tsx +3 -2
- package/src/components/editor/registry-action-contributions.test.tsx +63 -0
- package/src/components/editor/registry-action-contributions.tsx +80 -1
- package/src/components/editor/room-controls.tsx +847 -0
- package/src/components/editor/room-floor-highlight.tsx +128 -0
- package/src/components/editor/room-handles-2d.tsx +354 -0
- package/src/components/editor/room-handles.tsx +675 -0
- package/src/components/editor/room-highlight.tsx +225 -0
- package/src/components/editor/room-labels.test.ts +357 -0
- package/src/components/editor/room-labels.tsx +990 -0
- package/src/components/editor/room-transform-ghost.tsx +256 -0
- package/src/components/editor/room-webgpu-materials.test.ts +27 -0
- package/src/components/editor/selection-manager.tsx +451 -317
- package/src/components/editor/slab-hole-highlights.tsx +3 -0
- package/src/components/editor/snapshot-capture-overlay.tsx +4 -0
- package/src/components/editor/snapshot-capture.test.ts +52 -4
- package/src/components/editor/snapshot-capture.ts +30 -5
- package/src/components/editor/thumbnail-generator.test.ts +308 -0
- package/src/components/editor/thumbnail-generator.tsx +1035 -229
- package/src/components/editor/use-floorplan-background-placement.ts +31 -11
- package/src/components/editor/vector-edge-extractor.tsx +121 -0
- package/src/components/editor/views/host-view.tsx +61 -0
- package/src/components/editor/views/plugins-view.tsx +670 -0
- package/src/components/editor/views/scene-region.tsx +59 -0
- package/src/components/editor/views/use-editor-views.ts +57 -0
- package/src/components/editor/views/view-bar.tsx +220 -0
- package/src/components/editor/wall-justification.test.ts +140 -0
- package/src/components/editor/wall-move-side-handles.tsx +43 -142
- package/src/components/editor/wall-push-arrow.tsx +92 -0
- package/src/components/editor/wall-region-handles.tsx +509 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +3 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.test.tsx +86 -0
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +290 -0
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +113 -102
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.test.ts +156 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +278 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +77 -0
- package/src/components/editor-2d/renderers/{floorplan-draft-wall-measurement.tsx → floorplan-draft-measurement.tsx} +8 -8
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +38 -8
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +291 -227
- package/src/components/editor-2d/svg-paths.ts +12 -15
- package/src/components/procedural-items/inspector.tsx +40 -15
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +97 -14
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +18 -18
- package/src/components/systems/zone/zone-system.test.ts +47 -0
- package/src/components/systems/zone/zone-system.tsx +29 -68
- package/src/components/tools/item/placement-history.test.tsx +474 -0
- package/src/components/tools/item/placement-strategies.ts +8 -4
- package/src/components/tools/item/use-draft-node.test.tsx +432 -1
- package/src/components/tools/item/use-draft-node.ts +166 -95
- package/src/components/tools/item/use-placement-coordinator.tsx +93 -46
- package/src/components/tools/paint-region/use-paint-tint.ts +37 -0
- package/src/components/tools/paint-region/wall-paint-region-tool.tsx +466 -0
- package/src/components/tools/registry/move-registry-node-tool.tsx +3 -1
- package/src/components/tools/select/box-select-tool.tsx +2 -12
- package/src/components/tools/select/plane-box-select-tool.test.tsx +79 -0
- package/src/components/tools/select/plane-box-select-tool.tsx +35 -22
- package/src/components/tools/select/select-candidates.test.ts +37 -4
- package/src/components/tools/select/select-candidates.ts +15 -11
- package/src/components/tools/shared/cursor-sphere.tsx +36 -12
- package/src/components/tools/site/site-boundary-editor.tsx +1 -2
- package/src/components/tools/stair/stair-defaults.ts +0 -3
- package/src/components/tools/stair/stair-tool.tsx +90 -42
- package/src/components/tools/tool-manager.tsx +46 -20
- package/src/components/tools/wall/wall-drafting.test.ts +19 -4
- package/src/components/tools/zone/zone-tool.tsx +4 -12
- package/src/components/ui/action-menu/action-button.tsx +3 -0
- package/src/components/ui/action-menu/camera-actions.tsx +2 -1
- package/src/components/ui/action-menu/control-modes.tsx +2 -35
- package/src/components/ui/action-menu/measurement-control.tsx +3 -10
- package/src/components/ui/build-panel/build-panel.tsx +235 -0
- package/src/components/ui/command-palette/editor-commands.tsx +4 -30
- package/src/components/ui/command-palette/index.tsx +5 -24
- package/src/components/ui/controls/finish-swatch.tsx +110 -0
- package/src/components/ui/controls/material-paint-panel.tsx +97 -26
- package/src/components/ui/controls/material-picker.tsx +4 -1
- package/src/components/ui/controls/scene-material-list.tsx +4 -32
- package/src/components/ui/controls/select-control.tsx +65 -0
- package/src/components/ui/floating-level-selector.tsx +9 -14
- package/src/components/ui/helpers/building-helper.tsx +4 -1
- package/src/components/ui/helpers/contextual-helper-panel.tsx +154 -70
- package/src/components/ui/helpers/helper-manager.test.tsx +69 -0
- package/src/components/ui/helpers/helper-manager.tsx +325 -14
- package/src/components/ui/helpers/item-helper.tsx +8 -0
- package/src/components/ui/helpers/registered-tool-helper.tsx +8 -1
- package/src/components/ui/icon-ref.tsx +45 -1
- package/src/components/ui/level-duplicate-dialog.tsx +1 -1
- package/src/components/ui/node-icon-image.tsx +16 -0
- package/src/components/ui/panels/collections/collections-popover.tsx +20 -14
- package/src/components/ui/panels/floor-foundation-panel.tsx +551 -0
- package/src/components/ui/panels/floor-opening-panel.tsx +143 -0
- package/src/components/ui/panels/mobile-panel-sheet.tsx +2 -2
- package/src/components/ui/panels/mobile-selection-bar.tsx +2 -2
- package/src/components/ui/panels/multi-field-value.test.ts +26 -3
- package/src/components/ui/panels/multi-field-value.ts +6 -3
- package/src/components/ui/panels/multi-parametric-inspector.tsx +52 -2
- package/src/components/ui/panels/multi-selection-panel.tsx +43 -4
- package/src/components/ui/panels/node-display.ts +8 -3
- package/src/components/ui/panels/panel-manager.tsx +103 -68
- package/src/components/ui/panels/panel-wrapper.tsx +78 -36
- package/src/components/ui/panels/parametric-field-control.tsx +1 -1
- package/src/components/ui/panels/parametric-inspector.tsx +4 -4
- package/src/components/ui/panels/reference-panel.tsx +2 -3
- package/src/components/ui/panels/room-construction-rows.tsx +774 -0
- package/src/components/ui/panels/room-name-options.test.ts +55 -0
- package/src/components/ui/panels/room-name-options.ts +47 -0
- package/src/components/ui/panels/room-openings-field.tsx +110 -0
- package/src/components/ui/panels/room-panel.tsx +725 -0
- package/src/components/ui/panels/shape-choice.tsx +58 -0
- package/src/components/ui/panels/wall-region-list.tsx +109 -0
- package/src/components/ui/primitives/color-dot.tsx +36 -24
- package/src/components/ui/primitives/sidebar.tsx +2 -0
- package/src/components/ui/right-stack.test.ts +29 -0
- package/src/components/ui/right-stack.tsx +145 -0
- package/src/components/ui/sidebar/icon-rail.tsx +5 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +31 -4
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +5 -5
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -4
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +197 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +24 -11
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +176 -211
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +30 -8
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +4 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +1 -4
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +21 -13
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +0 -27
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -3
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +9 -4
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +0 -2
- package/src/components/ui/sidebar/tab-bar.tsx +10 -0
- package/src/components/ui/sidebar/use-plugin-panels.tsx +15 -5
- package/src/components/viewer/floorplan-compass-button.tsx +9 -1
- package/src/components/viewer/floorplan-preview.tsx +5 -6
- package/src/components/viewer/viewer-hover-label.tsx +34 -0
- package/src/components/viewer/viewer-rooms.tsx +175 -0
- package/src/components/viewer/viewer-scene-header.tsx +181 -145
- package/src/components/viewer/viewer-selection-manager.tsx +124 -0
- package/src/components/viewer/viewer-stage.tsx +2 -2
- package/src/components/viewer-zone-system.tsx +3 -8
- package/src/hooks/use-auto-save.integration.test.tsx +123 -9
- package/src/hooks/use-auto-save.ts +34 -8
- package/src/hooks/use-keyboard.test.ts +178 -3
- package/src/hooks/use-keyboard.ts +89 -71
- package/src/hooks/use-open-wall-ends.test.ts +99 -0
- package/src/hooks/use-open-wall-ends.ts +238 -0
- package/src/hooks/use-placement-notice.test.ts +48 -0
- package/src/hooks/use-placement-notice.ts +42 -0
- package/src/hooks/use-selected-room.ts +143 -0
- package/src/index.tsx +246 -39
- package/src/lib/ceiling-edit-session.test.ts +321 -0
- package/src/lib/ceiling-edit-session.ts +206 -0
- package/src/lib/ceiling-region-session.test.ts +179 -0
- package/src/lib/continuation.test.ts +19 -1
- package/src/lib/continuation.ts +25 -4
- package/src/lib/door-interaction.ts +14 -0
- package/src/lib/editor-level-display.test.ts +96 -0
- package/src/lib/editor-level-display.ts +48 -0
- package/src/lib/editor-views.ts +144 -0
- package/src/lib/export-geometry-wait.test.ts +90 -0
- package/src/lib/floor-edit-session.ts +153 -0
- package/src/lib/floor-footprints.test.ts +563 -0
- package/src/lib/floor-footprints.ts +395 -0
- package/src/lib/floor-foundation-support.test.ts +283 -0
- package/src/lib/floor-opening-draft.ts +218 -0
- package/src/lib/floor-openings.test.ts +216 -0
- package/src/lib/floor-region-geometry.ts +191 -0
- package/src/lib/floor-region-session.test.ts +485 -0
- package/src/lib/floor-region-session.ts +589 -0
- package/src/lib/floor-region-snap.ts +101 -0
- package/src/lib/floor-step-hover.test.ts +32 -0
- package/src/lib/floorplan/annotation-visibility.ts +17 -0
- package/src/lib/floorplan/dimension-drive.test.ts +360 -0
- package/src/lib/floorplan/dimension-drive.ts +479 -0
- package/src/lib/floorplan/floorplan-export.test.ts +12 -8
- package/src/lib/floorplan/floorplan-export.tsx +32 -8
- package/src/lib/floorplan/floorplan-extension.ts +32 -0
- package/src/lib/floorplan/floorplan-mode.test.ts +4 -0
- package/src/lib/floorplan/floorplan-mode.ts +2 -0
- package/src/lib/floorplan/floorplan-pdfkit-document.ts +339 -18
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +138 -8
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +56 -25
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +138 -0
- package/src/lib/floorplan/floorplan-readonly.ts +6 -0
- package/src/lib/floorplan/geometry.ts +13 -6
- package/src/lib/floorplan/index.ts +0 -9
- package/src/lib/floorplan/items.ts +5 -2
- package/src/lib/floorplan/marks.test.ts +213 -0
- package/src/lib/floorplan/marks.ts +238 -0
- package/src/lib/floorplan/open-wall-ends.test.ts +268 -0
- package/src/lib/floorplan/open-wall-ends.ts +162 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Regular.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-GeistMono.txt +92 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-LiberationSans.txt +102 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Regular.ttf +0 -0
- package/src/lib/floorplan/schedules.test.ts +189 -0
- package/src/lib/floorplan/schedules.ts +339 -0
- package/src/lib/floorplan/sheet-export.test.ts +100 -0
- package/src/lib/floorplan/sheet-export.ts +318 -0
- package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +779 -0
- package/src/lib/floorplan/site-plan/contours.test.ts +43 -0
- package/src/lib/floorplan/site-plan/contributors.ts +97 -0
- package/src/lib/floorplan/site-plan/coverage.ts +232 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.test.ts +15 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.tsx +86 -0
- package/src/lib/floorplan/site-plan/front-edge.test.ts +103 -0
- package/src/lib/floorplan/site-plan/front-edge.ts +156 -0
- package/src/lib/floorplan/site-plan/geometry.test.ts +275 -0
- package/src/lib/floorplan/site-plan/geometry.ts +557 -0
- package/src/lib/floorplan/site-plan/index.ts +71 -0
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +366 -0
- package/src/lib/floorplan/site-plan/site-annotations.ts +1083 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +379 -0
- package/src/lib/floorplan/site-plan/site-plan-layer.tsx +138 -0
- package/src/lib/floorplan/site-plan/street-name.test.ts +69 -0
- package/src/lib/floorplan/site-plan/street-name.ts +302 -0
- package/src/lib/floorplan/site-plan/yards.test.ts +69 -0
- package/src/lib/floorplan/types.ts +1 -27
- package/src/lib/fresh-planar-placement.test.ts +36 -0
- package/src/lib/fresh-planar-placement.ts +59 -36
- package/src/lib/geometry-script/author.ts +152 -0
- package/src/lib/geometry-script/client.ts +152 -0
- package/src/lib/geometry-script/protocol.ts +12 -0
- package/src/lib/gesture-lifecycle.test.ts +595 -0
- package/src/lib/gesture-lifecycle.ts +290 -0
- package/src/lib/glb-export-zone-holes.test.ts +41 -0
- package/src/lib/glb-export.test.ts +485 -0
- package/src/lib/glb-export.ts +237 -119
- package/src/lib/history.test.ts +6 -0
- package/src/lib/history.ts +7 -0
- package/src/lib/hud-title.ts +109 -0
- package/src/lib/hydration-gated-storage.ts +49 -0
- package/src/lib/ifc-export.test.ts +174 -0
- package/src/lib/ifc-export.ts +201 -0
- package/src/lib/ifc-parts.ts +15 -0
- package/src/lib/inspector-expanded.test.ts +44 -0
- package/src/lib/inspector-expanded.ts +36 -0
- package/src/lib/interaction/scope.ts +18 -0
- package/src/lib/keyboard-pan.test.ts +129 -7
- package/src/lib/keyboard-pan.ts +161 -12
- package/src/lib/live-gesture-round-trip.test.ts +360 -0
- package/src/lib/lot/__fixtures__/map-dossier-miami-shores.json +1 -0
- package/src/lib/lot/__fixtures__/map-dossier-stpete.json +1 -0
- package/src/lib/lot/clean-ring.test.ts +186 -0
- package/src/lib/lot/clean-ring.ts +217 -0
- package/src/lib/lot/dossier.test.ts +202 -0
- package/src/lib/lot/dossier.ts +498 -0
- package/src/lib/lot/drop-in.test.ts +326 -0
- package/src/lib/lot/drop-in.ts +407 -0
- package/src/lib/lot/index.ts +43 -0
- package/src/lib/lot/lot-patch.test.ts +340 -0
- package/src/lib/lot/lot-patch.ts +394 -0
- package/src/lib/lot/parcel-provider.ts +43 -0
- package/src/lib/lot/terrain.test.ts +161 -0
- package/src/lib/lot/terrain.ts +266 -0
- package/src/lib/material-catalog-model.ts +3 -1
- package/src/lib/material-paint-panel-model.ts +9 -5
- package/src/lib/material-paint.ts +4 -0
- package/src/lib/mezzanine-actions.test.ts +342 -0
- package/src/lib/mezzanine-draft.test.ts +501 -0
- package/src/lib/mezzanine-draft.ts +278 -0
- package/src/lib/mezzanine-messages.ts +26 -0
- package/src/lib/mezzanine-selection.test.ts +207 -0
- package/src/lib/mezzanine-stairs.ts +46 -0
- package/src/lib/model-export.ts +3 -1
- package/src/lib/node-action-movement.ts +17 -6
- package/src/lib/node-display-label.ts +37 -0
- package/src/lib/orbit-floor-clearance.test.ts +165 -0
- package/src/lib/orbit-floor-clearance.ts +106 -0
- package/src/lib/paint-commit-route.test.ts +37 -0
- package/src/lib/paint-commit-route.ts +17 -0
- package/src/lib/paint-eyedropper.test.tsx +165 -0
- package/src/lib/paint-eyedropper.ts +99 -0
- package/src/lib/paint-pass-through.test.ts +111 -0
- package/src/lib/paint-pass-through.ts +76 -0
- package/src/lib/paint-region-hover.ts +25 -0
- package/src/lib/paint-region-mode.ts +154 -0
- package/src/lib/paint-regions.test.ts +166 -0
- package/src/lib/paint-regions.ts +192 -0
- package/src/lib/paint-scope.test.ts +317 -35
- package/src/lib/paint-scope.ts +266 -25
- package/src/lib/panel-rows.ts +21 -0
- package/src/lib/parametric-node-update.ts +1 -1
- package/src/lib/plate-paint-affected.test.ts +300 -0
- package/src/lib/plate-paint-affected.ts +177 -0
- package/src/lib/plugin-chat.ts +50 -0
- package/src/lib/plugin-panels.test.ts +25 -0
- package/src/lib/plugin-panels.ts +94 -0
- package/src/lib/print-shell-compiler.ts +1 -0
- package/src/lib/registered-draft-snap.ts +12 -0
- package/src/lib/roof-duplication.ts +10 -2
- package/src/lib/room-assembly-overlay.test.ts +275 -0
- package/src/lib/room-assembly-overlay.ts +266 -0
- package/src/lib/room-built-on.test.ts +379 -0
- package/src/lib/room-built-on.ts +138 -0
- package/src/lib/room-construction-commands.ts +712 -0
- package/src/lib/room-construction.test.ts +746 -0
- package/src/lib/room-construction.ts +385 -0
- package/src/lib/room-controls-phase6.test.ts +630 -0
- package/src/lib/room-divide-session.ts +523 -0
- package/src/lib/room-handle-drag.ts +1068 -0
- package/src/lib/room-name-catalog.test.ts +90 -0
- package/src/lib/room-name-catalog.ts +168 -0
- package/src/lib/room-push-dimensions.test.ts +311 -0
- package/src/lib/room-push-dimensions.ts +251 -0
- package/src/lib/room-selection-commands.ts +62 -0
- package/src/lib/room-selection.test.ts +973 -0
- package/src/lib/room-selection.ts +417 -0
- package/src/lib/room-structure-commands.ts +94 -0
- package/src/lib/room-structure.test.ts +921 -0
- package/src/lib/room-transform-session.ts +570 -0
- package/src/lib/room-zone-routing.test.ts +294 -0
- package/src/lib/room-zone-routing.ts +197 -0
- package/src/lib/scene-clipboard.test.ts +364 -0
- package/src/lib/scene-clipboard.ts +229 -48
- package/src/lib/scene-material-refs.test.ts +79 -0
- package/src/lib/scene-material-refs.ts +79 -0
- package/src/lib/scene.test.ts +24 -36
- package/src/lib/scene.ts +33 -57
- package/src/lib/selection-routing.test.ts +129 -45
- package/src/lib/selection-routing.ts +53 -74
- package/src/lib/session-writes.test.ts +120 -0
- package/src/lib/session-writes.ts +205 -0
- package/src/lib/sidebar-panel.ts +62 -0
- package/src/lib/site-boundary.test.ts +4 -6
- package/src/lib/site-boundary.ts +1 -1
- package/src/lib/snapping-mode.test.ts +4 -0
- package/src/lib/snapping-mode.ts +8 -1
- package/src/lib/terrace-draft.test.ts +145 -0
- package/src/lib/terrace-draft.ts +213 -0
- package/src/lib/tool-cursor-icon.test.ts +16 -0
- package/src/lib/tool-cursor-icon.ts +11 -0
- package/src/lib/typing-target.ts +9 -0
- package/src/lib/units.ts +77 -39
- package/src/lib/vector-edges.test.ts +58 -0
- package/src/lib/vector-edges.ts +400 -0
- package/src/lib/viewer-selection.test.ts +577 -0
- package/src/lib/viewer-selection.ts +569 -0
- package/src/lib/wall-draw-variant.test.ts +71 -0
- package/src/lib/wall-draw-variant.ts +91 -0
- package/src/lib/wall-paint-region-session.ts +246 -0
- package/src/lib/wall-paint-region.test.ts +373 -0
- package/src/lib/wall-polygon-draft.test.ts +262 -0
- package/src/lib/wall-polygon-draft.ts +286 -0
- package/src/lib/wall-push.test.ts +453 -0
- package/src/lib/wall-region-face.ts +352 -0
- package/src/lib/wall-region-handles.test.ts +304 -0
- package/src/lib/wall-region-handles.ts +368 -0
- package/src/lib/wall-region-list.test.ts +119 -0
- package/src/lib/wall-region-list.ts +136 -0
- package/src/lib/wall-region-snap.ts +110 -0
- package/src/lib/window-interaction.ts +14 -0
- package/src/lib/zone-content.ts +4 -1
- package/src/lib/zone-name.test.ts +17 -0
- package/src/lib/zone-name.ts +17 -0
- package/src/store/opens-in-select-mode.test.ts +223 -0
- package/src/store/paint-mode.test.ts +188 -0
- package/src/store/terrain-sculpt-mode.test.ts +19 -21
- package/src/store/tool-mode.test.ts +137 -6
- package/src/store/use-delete-confirmation.ts +28 -1
- package/src/store/use-drawing-view.test.ts +24 -3
- package/src/store/use-drawing-view.ts +29 -1
- package/src/store/use-editor.tsx +432 -321
- package/src/store/use-floorplan-draft-preview.ts +13 -0
- package/src/store/use-floorplan-marquee.ts +12 -2
- package/src/store/use-quick-measurement-hud.ts +3 -3
- package/src/store/use-stair-build-preview.ts +13 -5
- package/src/store/use-wall-move-ghosts.ts +4 -2
- package/src/store/use-wall-region-handles-selection.test.ts +222 -0
- package/src/store/use-wall-region-handles-selection.ts +139 -0
- package/src/store/view-layout.test.ts +175 -0
- package/src/store/view-layout.ts +196 -0
- package/src/test-utils/immediate-animation-frames.ts +16 -0
- package/src/test-utils/live-gesture-round-trip.ts +224 -0
- package/src/test-utils/selection-harness.ts +82 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +0 -499
- package/src/components/systems/zone/zone-label-editor-system.tsx +0 -333
- package/src/components/tools/fence/fence-drafting.test.ts +0 -100
- package/src/components/tools/fence/fence-drafting.ts +0 -266
- package/src/components/ui/sidebar/panels/plugins-panel.tsx +0 -218
- package/src/components/viewer/viewer-units-panel.tsx +0 -116
- package/src/lib/floorplan/stairs.ts +0 -489
- package/src/lib/level-duplication.test.ts +0 -144
- package/src/lib/level-duplication.ts +0 -195
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test } from 'bun:test'
|
|
2
2
|
import useEditor, { isBrushMode, type Mode, normalizePersistedEditorUiState } from './use-editor'
|
|
3
3
|
import useInteractionScope from './use-interaction-scope'
|
|
4
|
+
import { isViewVisible, sceneLayout, VIEW_2D, visibleScene } from './view-layout'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* The mode-lifecycle contract for terrain sculpting.
|
|
@@ -17,9 +18,9 @@ function reset() {
|
|
|
17
18
|
useEditor.getState().setPreviewMode(false)
|
|
18
19
|
useEditor.getState().setFirstPersonMode(false)
|
|
19
20
|
useEditor.getState().setWorkspaceMode('edit')
|
|
20
|
-
// Before the mode:
|
|
21
|
+
// Before the mode: hiding 3D disarms sculpt, and leaving a stale layout
|
|
21
22
|
// behind would silently change what the next test's `setMode` does.
|
|
22
|
-
useEditor.getState().
|
|
23
|
+
useEditor.getState().setViewLayout(sceneLayout('3d'))
|
|
23
24
|
useEditor.getState().setMode('select')
|
|
24
25
|
useEditor.getState().setTerrainVerb('flatten')
|
|
25
26
|
useEditor.getState().setTerrainSampling(false)
|
|
@@ -41,8 +42,8 @@ describe('terrain-sculpt mode lifecycle', () => {
|
|
|
41
42
|
expect(useInteractionScope.getState().scope.kind).toBe('idle')
|
|
42
43
|
})
|
|
43
44
|
|
|
44
|
-
test('arming sculpt from
|
|
45
|
-
useEditor.getState().setPhase('
|
|
45
|
+
test('arming sculpt from the building moves to site', () => {
|
|
46
|
+
useEditor.getState().setPhase('building')
|
|
46
47
|
useEditor.getState().setMode('terrain-sculpt')
|
|
47
48
|
// Otherwise `normalizeModeForPhase` rejects the mode on the next rehydrate
|
|
48
49
|
// and the UI shows a mode the store does not hold.
|
|
@@ -53,7 +54,7 @@ describe('terrain-sculpt mode lifecycle', () => {
|
|
|
53
54
|
test('a phase switch out of site drops the scope', () => {
|
|
54
55
|
useEditor.getState().setMode('terrain-sculpt')
|
|
55
56
|
// The phase setter delegates its mode rewrite to the ToolMode transition.
|
|
56
|
-
useEditor.getState().setPhase('
|
|
57
|
+
useEditor.getState().setPhase('building')
|
|
57
58
|
expect(useInteractionScope.getState().scope.kind).toBe('idle')
|
|
58
59
|
})
|
|
59
60
|
})
|
|
@@ -71,7 +72,7 @@ describe('the eyedropper arm is not allowed to outlive sculpt mode', () => {
|
|
|
71
72
|
test('leaving via a phase switch disarms it too', () => {
|
|
72
73
|
useEditor.getState().setMode('terrain-sculpt')
|
|
73
74
|
useEditor.getState().setTerrainSampling(true)
|
|
74
|
-
useEditor.getState().setPhase('
|
|
75
|
+
useEditor.getState().setPhase('building')
|
|
75
76
|
expect(useEditor.getState().terrainSampling).toBe(false)
|
|
76
77
|
})
|
|
77
78
|
|
|
@@ -106,17 +107,17 @@ describe('the brush and the 3D canvas travel together', () => {
|
|
|
106
107
|
// moves), while choosing the floorplan asks for the floorplan (so the mode
|
|
107
108
|
// yields).
|
|
108
109
|
test('arming from the 2D floorplan opens a 3D pane beside it', () => {
|
|
109
|
-
useEditor.getState().
|
|
110
|
+
useEditor.getState().setViewLayout(sceneLayout('2d'))
|
|
110
111
|
useEditor.getState().setMode('terrain-sculpt')
|
|
111
112
|
// `split`, not `3d`: the floorplan was a deliberate choice, so keep it.
|
|
112
|
-
expect(useEditor.getState()
|
|
113
|
-
expect(useEditor.getState()
|
|
113
|
+
expect(visibleScene(useEditor.getState())).toBe('split')
|
|
114
|
+
expect(isViewVisible(useEditor.getState(), VIEW_2D)).toBe(true)
|
|
114
115
|
expect(useEditor.getState().mode).toBe('terrain-sculpt')
|
|
115
116
|
})
|
|
116
117
|
|
|
117
118
|
test('switching to the 2D floorplan disarms the brush', () => {
|
|
118
119
|
useEditor.getState().setMode('terrain-sculpt')
|
|
119
|
-
useEditor.getState().
|
|
120
|
+
useEditor.getState().setViewLayout(sceneLayout('2d'))
|
|
120
121
|
expect(useEditor.getState().mode).toBe('select')
|
|
121
122
|
// And the scope goes with it — otherwise selection stays suppressed in the
|
|
122
123
|
// floorplan the user just asked for, with no visible way out.
|
|
@@ -125,10 +126,16 @@ describe('the brush and the 3D canvas travel together', () => {
|
|
|
125
126
|
|
|
126
127
|
test('split keeps it armed — the canvas is right there', () => {
|
|
127
128
|
useEditor.getState().setMode('terrain-sculpt')
|
|
128
|
-
useEditor.getState().
|
|
129
|
+
useEditor.getState().setViewLayout(sceneLayout('split'))
|
|
129
130
|
expect(useEditor.getState().mode).toBe('terrain-sculpt')
|
|
130
131
|
})
|
|
131
132
|
|
|
133
|
+
test('another view in the 3D pane disarms it, as the plan does', () => {
|
|
134
|
+
useEditor.getState().setMode('terrain-sculpt')
|
|
135
|
+
useEditor.getState().showView('gallery')
|
|
136
|
+
expect(useEditor.getState().mode).toBe('select')
|
|
137
|
+
})
|
|
138
|
+
|
|
132
139
|
test('rehydrating the pair resolves it to select', () => {
|
|
133
140
|
// Mode and view persist independently and rehydrate runs neither setter, so
|
|
134
141
|
// this is the one reconciliation the setters cannot cover.
|
|
@@ -172,20 +179,11 @@ describe('the modes that hide the editing canvas release the brush', () => {
|
|
|
172
179
|
useEditor.getState().setWorkspaceMode('studio')
|
|
173
180
|
expect(useInteractionScope.getState().scope.kind).toBe('idle')
|
|
174
181
|
})
|
|
175
|
-
|
|
176
|
-
test('a structure-layer switch releases it', () => {
|
|
177
|
-
useEditor.getState().setMode('terrain-sculpt')
|
|
178
|
-
// Reachable from the layer toggle while sculpt is armed, and it takes the
|
|
179
|
-
// same raw-`set` path — the mode resets, so the scope has to follow.
|
|
180
|
-
useEditor.getState().setStructureLayer('zones')
|
|
181
|
-
expect(useEditor.getState().mode).toBe('select')
|
|
182
|
-
expect(useInteractionScope.getState().scope.kind).toBe('idle')
|
|
183
|
-
})
|
|
184
182
|
})
|
|
185
183
|
|
|
186
184
|
describe('entering sculpt mode stands other affordances down', () => {
|
|
187
185
|
test('the armed build tool is cleared', () => {
|
|
188
|
-
useEditor.getState().setPhase('
|
|
186
|
+
useEditor.getState().setPhase('building')
|
|
189
187
|
useEditor.getState().setMode('build')
|
|
190
188
|
useEditor.getState().setTool('wall')
|
|
191
189
|
useEditor.getState().setMode('terrain-sculpt')
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
|
|
2
|
+
import { type AnyNode, useScene } from '@pascal-app/core'
|
|
3
|
+
import { useViewer } from '@pascal-app/viewer'
|
|
4
|
+
import { beginGesture } from '../lib/gesture-lifecycle'
|
|
2
5
|
import useEditor, { normalizePersistedEditorUiState } from './use-editor'
|
|
6
|
+
import useInteractionScope from './use-interaction-scope'
|
|
3
7
|
|
|
4
8
|
function resetToolMode() {
|
|
5
|
-
useEditor.getState().
|
|
6
|
-
|
|
9
|
+
useEditor.getState().clearRoom()
|
|
10
|
+
useViewer.getState().setSelection({ selectedIds: [], zoneId: null })
|
|
11
|
+
useEditor.getState().setPhase('building')
|
|
7
12
|
useEditor.getState().armToolMode({ mode: 'select' })
|
|
8
13
|
useEditor.getState().setActivePaintMaterial(null)
|
|
9
14
|
}
|
|
@@ -12,6 +17,18 @@ beforeEach(resetToolMode)
|
|
|
12
17
|
afterEach(resetToolMode)
|
|
13
18
|
|
|
14
19
|
describe('ToolMode transition', () => {
|
|
20
|
+
test('arming building tools preserves the current phase', () => {
|
|
21
|
+
for (const phase of ['site', 'building'] as const) {
|
|
22
|
+
useEditor.getState().setPhase(phase)
|
|
23
|
+
useEditor.getState().armToolMode({ mode: 'select' })
|
|
24
|
+
for (const tool of ['item', 'wall', 'measurement', 'zone'] as const) {
|
|
25
|
+
useEditor.getState().armToolMode({ mode: 'build', tool })
|
|
26
|
+
expect(useEditor.getState().phase).toBe(phase)
|
|
27
|
+
expect(useEditor.getState().tool).toBe(tool)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
15
32
|
test('choosing a paint swatch while selected arms material paint', () => {
|
|
16
33
|
useEditor.getState().armToolMode({ mode: 'select' })
|
|
17
34
|
useEditor.getState().armMaterialPaint({
|
|
@@ -56,14 +73,129 @@ describe('ToolMode transition', () => {
|
|
|
56
73
|
})
|
|
57
74
|
})
|
|
58
75
|
|
|
76
|
+
describe('leaving Select ends the selection', () => {
|
|
77
|
+
const ROOM = { levelId: 'level_1', zoneId: 'zone_1' }
|
|
78
|
+
|
|
79
|
+
function selectRoomAndNode() {
|
|
80
|
+
useEditor.getState().armToolMode({ mode: 'select' })
|
|
81
|
+
useViewer
|
|
82
|
+
.getState()
|
|
83
|
+
.setSelection({ selectedIds: ['wall_1' as never], zoneId: 'zone_1' as never })
|
|
84
|
+
useEditor.getState().selectRoom(ROOM)
|
|
85
|
+
useEditor.getState().setSelectedReferenceId(null)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function expectNothingSelected() {
|
|
89
|
+
expect(useViewer.getState().selection.selectedIds).toEqual([])
|
|
90
|
+
expect(useViewer.getState().selection.zoneId).toBeNull()
|
|
91
|
+
expect(useEditor.getState().room).toBeNull()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
test('picking a catalog item (the item tool) clears the room and node selection', () => {
|
|
95
|
+
selectRoomAndNode()
|
|
96
|
+
useEditor.getState().setSelectedItem({ id: 'chair' } as never)
|
|
97
|
+
useEditor.getState().setTool('item')
|
|
98
|
+
expectNothingSelected()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('the door and window tools clear it', () => {
|
|
102
|
+
for (const tool of ['door', 'window'] as const) {
|
|
103
|
+
selectRoomAndNode()
|
|
104
|
+
useEditor.getState().armToolMode({ mode: 'build', tool })
|
|
105
|
+
expectNothingSelected()
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('draw tools clear it, and so does switching from one tool to another', () => {
|
|
110
|
+
selectRoomAndNode()
|
|
111
|
+
useEditor.getState().setMode('build')
|
|
112
|
+
expect(useEditor.getState().tool).toBe('wall')
|
|
113
|
+
expectNothingSelected()
|
|
114
|
+
|
|
115
|
+
// A tool that selects what it just drew (the roof, to draw into it next)
|
|
116
|
+
// loses that selection when another tool is picked.
|
|
117
|
+
useViewer.getState().setSelection({ selectedIds: ['roof_1' as never] })
|
|
118
|
+
useEditor.getState().armToolMode({ mode: 'build', tool: 'slab' })
|
|
119
|
+
expectNothingSelected()
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('re-arming the tool already in hand is not a transition', () => {
|
|
123
|
+
useEditor.getState().armToolMode({ mode: 'build', tool: 'roof' })
|
|
124
|
+
useViewer.getState().setSelection({ selectedIds: ['roof_1' as never] })
|
|
125
|
+
useEditor.getState().setTool('roof')
|
|
126
|
+
expect(useViewer.getState().selection.selectedIds).toEqual(['roof_1'])
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test('paint clears it, after taking its target from the selection', () => {
|
|
130
|
+
const previousNodes = useScene.getState().nodes
|
|
131
|
+
useScene.setState({
|
|
132
|
+
nodes: { roof_1: { id: 'roof_1', type: 'roof' } as unknown as AnyNode } as never,
|
|
133
|
+
})
|
|
134
|
+
try {
|
|
135
|
+
useEditor.getState().setActivePaintTarget('wall')
|
|
136
|
+
selectRoomAndNode()
|
|
137
|
+
useViewer.getState().setSelection({ selectedIds: ['roof_1' as never] })
|
|
138
|
+
useEditor.getState().armMaterialPaint()
|
|
139
|
+
expect(useEditor.getState().mode).toBe('material-paint')
|
|
140
|
+
expect(useEditor.getState().activePaintTarget).toBe('roof')
|
|
141
|
+
expectNothingSelected()
|
|
142
|
+
} finally {
|
|
143
|
+
useScene.setState({ nodes: previousNodes })
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test('terrain sculpt and delete clear it', () => {
|
|
148
|
+
for (const mode of ['terrain-sculpt', 'delete'] as const) {
|
|
149
|
+
selectRoomAndNode()
|
|
150
|
+
useEditor.getState().armToolMode({ mode })
|
|
151
|
+
expectNothingSelected()
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('the level and building context survive', () => {
|
|
156
|
+
useViewer
|
|
157
|
+
.getState()
|
|
158
|
+
.setSelection({ buildingId: 'building_1' as never, levelId: 'level_1' as never })
|
|
159
|
+
selectRoomAndNode()
|
|
160
|
+
useEditor.getState().armToolMode({ mode: 'build', tool: 'door' })
|
|
161
|
+
expect(useViewer.getState().selection.levelId).toBe('level_1')
|
|
162
|
+
expect(useViewer.getState().selection.buildingId).toBe('building_1')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
test('Select, and a move or handle drag inside it, keep the selection', () => {
|
|
166
|
+
selectRoomAndNode()
|
|
167
|
+
useEditor.getState().armToolMode({ mode: 'select' })
|
|
168
|
+
expect(useViewer.getState().selection.selectedIds).toEqual(['wall_1'])
|
|
169
|
+
expect(useEditor.getState().room).toEqual(ROOM)
|
|
170
|
+
|
|
171
|
+
let cancelled = false
|
|
172
|
+
const handle = beginGesture({
|
|
173
|
+
kind: 'test-handle',
|
|
174
|
+
onCancel: () => {
|
|
175
|
+
cancelled = true
|
|
176
|
+
},
|
|
177
|
+
})
|
|
178
|
+
useEditor.getState().armToolMode({ mode: 'select' })
|
|
179
|
+
expect(handle.active).toBe(true)
|
|
180
|
+
handle.end()
|
|
181
|
+
expect(cancelled).toBe(false)
|
|
182
|
+
|
|
183
|
+
useEditor.getState().setMovingNode({ id: 'wall_1', type: 'wall' } as unknown as AnyNode)
|
|
184
|
+
expect(useInteractionScope.getState().scope.kind).toBe('moving')
|
|
185
|
+
expect(useEditor.getState().mode).toBe('select')
|
|
186
|
+
expect(useViewer.getState().selection.selectedIds).toEqual(['wall_1'])
|
|
187
|
+
useEditor.getState().setMovingNode(null)
|
|
188
|
+
expect(useViewer.getState().selection.selectedIds).toEqual(['wall_1'])
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
59
192
|
describe('persisted ToolMode normalization', () => {
|
|
60
193
|
test('elects a default for build with a null tool', () => {
|
|
61
194
|
const state = normalizePersistedEditorUiState({
|
|
62
|
-
phase: '
|
|
195
|
+
phase: 'building',
|
|
63
196
|
toolMode: { mode: 'build', tool: null as never },
|
|
64
197
|
mode: 'select',
|
|
65
198
|
tool: 'slab',
|
|
66
|
-
structureLayer: 'elements',
|
|
67
199
|
})
|
|
68
200
|
|
|
69
201
|
expect(state.toolMode).toEqual({ mode: 'build', tool: 'wall' })
|
|
@@ -73,11 +205,10 @@ describe('persisted ToolMode normalization', () => {
|
|
|
73
205
|
|
|
74
206
|
test('clears a persisted tool from a non-build mode', () => {
|
|
75
207
|
const state = normalizePersistedEditorUiState({
|
|
76
|
-
phase: '
|
|
208
|
+
phase: 'building',
|
|
77
209
|
toolMode: { mode: 'select' },
|
|
78
210
|
mode: 'build',
|
|
79
211
|
tool: 'wall',
|
|
80
|
-
structureLayer: 'elements',
|
|
81
212
|
})
|
|
82
213
|
|
|
83
214
|
expect(state.toolMode).toEqual({ mode: 'select' })
|
|
@@ -1,6 +1,33 @@
|
|
|
1
|
+
import type { DeleteZonePayload } from '@pascal-app/core'
|
|
1
2
|
import { create } from 'zustand'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
/** Changing one of a room's construction parts, with what it takes along. */
|
|
5
|
+
export type RoomConstructionChange = {
|
|
6
|
+
part: 'floor' | 'walls' | 'ceiling'
|
|
7
|
+
/** Walls can be confirmed on the way in too; everything else asks on removal. */
|
|
8
|
+
action?: 'add' | 'remove'
|
|
9
|
+
roomName: string
|
|
10
|
+
/**
|
|
11
|
+
* Walls: hosted openings and objects removed with them. Ceiling: items
|
|
12
|
+
* hanging from it. Floor: items standing on it.
|
|
13
|
+
*/
|
|
14
|
+
hostedIds: string[]
|
|
15
|
+
/** Walls turned into separators, or built on them. */
|
|
16
|
+
wallCount?: number
|
|
17
|
+
sharedWalls?: boolean
|
|
18
|
+
/** Walls added: clear floor area the room gives up to them, m². */
|
|
19
|
+
areaLoss?: number
|
|
20
|
+
/** Ceiling: names of the hand-drawn ceilings removed ('' when unnamed). */
|
|
21
|
+
manualCeilings?: string[]
|
|
22
|
+
/** Ceiling: other rooms those hand-drawn ceilings also cover. */
|
|
23
|
+
alsoCovers?: string[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type DeleteConfirmationRequest = {
|
|
27
|
+
room?: DeleteZonePayload
|
|
28
|
+
construction?: RoomConstructionChange
|
|
29
|
+
conflict?: string
|
|
30
|
+
onKeepContents?: () => void
|
|
4
31
|
count: number
|
|
5
32
|
onConfirm: () => void
|
|
6
33
|
}
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
import useDrawingView, {
|
|
2
|
+
import useDrawingView, {
|
|
3
|
+
EDITOR_DRAWING_TYPE_OPTIONS,
|
|
4
|
+
normalizeAnnotationLayoutOverrides,
|
|
5
|
+
normalizeEditorDrawingType,
|
|
6
|
+
} from './use-drawing-view'
|
|
3
7
|
|
|
4
8
|
describe('drawing type', () => {
|
|
5
|
-
test('
|
|
9
|
+
test('starts on the floor plan', () => {
|
|
6
10
|
expect(useDrawingView.getState().drawingType).toBe('floor-plan')
|
|
7
|
-
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('switches between the drawing types the editor can render', () => {
|
|
14
|
+
useDrawingView.getState().setDrawingType('site-plan')
|
|
15
|
+
expect(useDrawingView.getState().drawingType).toBe('site-plan')
|
|
16
|
+
useDrawingView.getState().setDrawingType('floor-plan')
|
|
17
|
+
expect(useDrawingView.getState().drawingType).toBe('floor-plan')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('only offers the two types that have a renderer', () => {
|
|
21
|
+
expect(EDITOR_DRAWING_TYPE_OPTIONS.map((o) => o.id)).toEqual(['floor-plan', 'site-plan'])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('a persisted value the editor cannot render falls back to the floor plan', () => {
|
|
25
|
+
expect(normalizeEditorDrawingType('site-plan')).toBe('site-plan')
|
|
26
|
+
expect(normalizeEditorDrawingType('roof-plan')).toBe('floor-plan')
|
|
27
|
+
expect(normalizeEditorDrawingType(undefined)).toBe('floor-plan')
|
|
28
|
+
expect(normalizeEditorDrawingType(42)).toBe('floor-plan')
|
|
8
29
|
})
|
|
9
30
|
})
|
|
10
31
|
|
|
@@ -20,8 +20,24 @@ export type DrawingAnnotationLayoutOverride = {
|
|
|
20
20
|
|
|
21
21
|
export type DrawingAnnotationLayoutOverrides = Record<string, DrawingAnnotationLayoutOverride>
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Drawing types the 2D editor can actually render today. `floor-plan` is the
|
|
25
|
+
* default; `site-plan` renders through
|
|
26
|
+
* `lib/floorplan/site-plan/buildSitePlanDrawing`. The remaining
|
|
27
|
+
* `ConstructionDrawingType` members exist on annotations (they gate dimension
|
|
28
|
+
* visibility) but have no editor renderer yet, so the switch does not offer
|
|
29
|
+
* them — widen this union as each one lands.
|
|
30
|
+
*/
|
|
31
|
+
export type EditorDrawingType = Extract<ConstructionDrawingType, 'floor-plan' | 'site-plan'>
|
|
32
|
+
|
|
33
|
+
export const EDITOR_DRAWING_TYPE_OPTIONS = [
|
|
34
|
+
{ id: 'floor-plan', label: 'Floor plan' },
|
|
35
|
+
{ id: 'site-plan', label: 'Site plan' },
|
|
36
|
+
] as const satisfies readonly { id: EditorDrawingType; label: string }[]
|
|
37
|
+
|
|
23
38
|
type DrawingViewState = {
|
|
24
|
-
drawingType:
|
|
39
|
+
drawingType: EditorDrawingType
|
|
40
|
+
setDrawingType: (drawingType: EditorDrawingType) => void
|
|
25
41
|
annotationLayoutOverrides: DrawingAnnotationLayoutOverrides
|
|
26
42
|
setAnnotationLayoutOverride: (
|
|
27
43
|
id: string,
|
|
@@ -52,10 +68,18 @@ export function normalizeAnnotationLayoutOverrides(
|
|
|
52
68
|
return out
|
|
53
69
|
}
|
|
54
70
|
|
|
71
|
+
/** Persisted value → a drawing type the editor can render. Unknown → floor-plan. */
|
|
72
|
+
export function normalizeEditorDrawingType(value: unknown): EditorDrawingType {
|
|
73
|
+
return EDITOR_DRAWING_TYPE_OPTIONS.some((o) => o.id === value)
|
|
74
|
+
? (value as EditorDrawingType)
|
|
75
|
+
: 'floor-plan'
|
|
76
|
+
}
|
|
77
|
+
|
|
55
78
|
const useDrawingView = create<DrawingViewState>()(
|
|
56
79
|
persist(
|
|
57
80
|
(set) => ({
|
|
58
81
|
drawingType: 'floor-plan',
|
|
82
|
+
setDrawingType: (drawingType) => set({ drawingType }),
|
|
59
83
|
annotationLayoutOverrides: {},
|
|
60
84
|
setAnnotationLayoutOverride: (id, override) =>
|
|
61
85
|
set((state) => {
|
|
@@ -69,12 +93,16 @@ const useDrawingView = create<DrawingViewState>()(
|
|
|
69
93
|
name: 'pascal-floorplan-drawing-view',
|
|
70
94
|
merge: (persistedState, currentState) => ({
|
|
71
95
|
...currentState,
|
|
96
|
+
drawingType: normalizeEditorDrawingType(
|
|
97
|
+
(persistedState as { drawingType?: unknown } | undefined)?.drawingType,
|
|
98
|
+
),
|
|
72
99
|
annotationLayoutOverrides: normalizeAnnotationLayoutOverrides(
|
|
73
100
|
(persistedState as { annotationLayoutOverrides?: unknown } | undefined)
|
|
74
101
|
?.annotationLayoutOverrides,
|
|
75
102
|
),
|
|
76
103
|
}),
|
|
77
104
|
partialize: (state) => ({
|
|
105
|
+
drawingType: state.drawingType,
|
|
78
106
|
annotationLayoutOverrides: state.annotationLayoutOverrides,
|
|
79
107
|
}),
|
|
80
108
|
},
|