@pascal-app/editor 1.0.6 → 1.1.0
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 +18 -15
- package/src/components/editor/action-menu-button.tsx +4 -1
- package/src/components/editor/alignment-3d-guide-layer.tsx +102 -11
- package/src/components/editor/bake-thumbnail.tsx +3 -2
- package/src/components/editor/custom-camera-controls.tsx +110 -42
- package/src/components/editor/duplicate-node.ts +2 -1
- package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
- package/src/components/editor/first-person-controls.tsx +77 -68
- package/src/components/editor/fit-scene-framing.test.ts +318 -0
- package/src/components/editor/fit-scene-framing.ts +142 -0
- package/src/components/editor/floating-action-menu.tsx +31 -11
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floor-openings-overlay.tsx +1 -1
- package/src/components/editor/floor-region-controls.tsx +2 -2
- package/src/components/editor/floorplan-panel.test.ts +220 -0
- package/src/components/editor/floorplan-panel.tsx +458 -140
- package/src/components/editor/floorplan-wall-typing-keys.test.tsx +152 -0
- package/src/components/editor/floorplan-wall-typing-keys.ts +79 -0
- package/src/components/editor/footprint-height-handle.tsx +2 -2
- package/src/components/editor/grid.tsx +24 -15
- package/src/components/editor/group-actions.ts +19 -1
- package/src/components/editor/group-floating-action-menu.tsx +1 -1
- package/src/components/editor/group-move-3d.ts +27 -2
- package/src/components/editor/group-selection-box-3d.tsx +4 -1
- package/src/components/editor/group-transform-shared.ts +23 -1
- package/src/components/editor/index.tsx +150 -20
- package/src/components/editor/inspector-extension-actions.tsx +36 -0
- package/src/components/editor/localized-html.tsx +22 -0
- package/src/components/editor/measurement-pill.tsx +1 -1
- package/src/components/editor/node-action-menu.tsx +46 -12
- package/src/components/editor/node-arrow-handles.tsx +2 -3
- package/src/components/editor/open-wall-ends-3d-layer.tsx +2 -3
- package/src/components/editor/open-wall-ends-hint.tsx +1 -1
- package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
- package/src/components/editor/quick-measurement-card.tsx +1 -1
- package/src/components/editor/registry-action-contributions.test.tsx +67 -6
- package/src/components/editor/registry-action-contributions.tsx +115 -15
- package/src/components/editor/riser-diagram-panel.tsx +59 -9
- package/src/components/editor/room-controls.tsx +4 -3
- package/src/components/editor/room-handles-2d.tsx +158 -7
- package/src/components/editor/room-handles.tsx +244 -54
- package/src/components/editor/room-highlight.tsx +2 -0
- package/src/components/editor/room-labels.tsx +33 -17
- package/src/components/editor/selection-manager.tsx +15 -102
- package/src/components/editor/separator-end-grip.tsx +74 -0
- package/src/components/editor/site-edge-labels.tsx +7 -3
- package/src/components/editor/thumbnail-generator.test.ts +15 -6
- package/src/components/editor/thumbnail-generator.tsx +26 -1
- package/src/components/editor/use-floorplan-background-placement.ts +55 -52
- package/src/components/editor/views/view-bar.tsx +111 -26
- package/src/components/editor/wall-measurement-label.tsx +31 -15
- package/src/components/editor/wall-move-side-handles.tsx +15 -7
- package/src/components/editor/wall-pull-grip.tsx +193 -0
- package/src/components/editor/wall-region-handles.tsx +2 -2
- package/src/components/editor/wall-snap-beacon-layer.tsx +134 -33
- package/src/components/editor-2d/floorplan-adjacent-walls-layer.test.ts +76 -0
- package/src/components/editor-2d/floorplan-adjacent-walls-layer.tsx +186 -0
- package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +63 -3
- package/src/components/editor-2d/floorplan-group-move.tsx +32 -4
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +6 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +33 -24
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +12 -5
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +2 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +29 -5
- package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +96 -4
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +52 -12
- package/src/components/editor-2d/renderers/floorplan-draft-measurement.tsx +7 -1
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +13 -1
- package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.test.tsx +35 -0
- package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.tsx +65 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +34 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +211 -39
- package/src/components/procedural-items/inspector.tsx +59 -12
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +2 -0
- package/src/components/systems/ceiling/ceiling-system.test.ts +141 -0
- package/src/components/systems/ceiling/ceiling-system.tsx +53 -18
- package/src/components/systems/roof/roof-edit-system.test.tsx +15 -1
- package/src/components/systems/roof/roof-edit-system.tsx +30 -19
- package/src/components/tools/distribution/distribution-run-tool.test.tsx +371 -3
- package/src/components/tools/distribution/distribution-run-tool.tsx +200 -58
- package/src/components/tools/item/move-tool.tsx +1 -6
- package/src/components/tools/item/placement-history.test.tsx +1 -1
- package/src/components/tools/item/placement-strategies.test.ts +64 -1
- package/src/components/tools/item/placement-strategies.ts +5 -9
- package/src/components/tools/item/use-placement-coordinator.tsx +31 -35
- package/src/components/tools/paint-region/wall-paint-region-tool.tsx +1 -1
- package/src/components/tools/registry/item-surface-move.test.ts +11 -0
- package/src/components/tools/registry/move-registry-node-tool.test.ts +42 -1
- package/src/components/tools/registry/move-registry-node-tool.tsx +38 -14
- package/src/components/tools/select/select-candidates.test.ts +45 -0
- package/src/components/tools/select/select-candidates.ts +13 -2
- package/src/components/tools/shared/cursor-sphere.tsx +10 -3
- package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
- package/src/components/tools/shared/placement-box.tsx +2 -6
- package/src/components/tools/shared/placement-coordinate-input.tsx +94 -0
- package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
- package/src/components/tools/shared/segment-angle.ts +0 -4
- package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
- package/src/components/tools/tool-manager.tsx +1 -24
- package/src/components/tools/wall/wall-drafting.test.ts +299 -23
- package/src/components/tools/wall/wall-drafting.ts +132 -24
- package/src/components/tools/wall/wall-pull-snap.test.ts +274 -0
- package/src/components/tools/wall/wall-pull-snap.ts +563 -0
- package/src/components/tools/wall/wall-snap-geometry.test.ts +113 -1
- package/src/components/tools/wall/wall-snap-geometry.ts +137 -0
- package/src/components/tools/wall/wall-typed-entry.test.ts +99 -0
- package/src/components/tools/wall/wall-typed-entry.ts +70 -0
- package/src/components/tools/zone/zone-tool.tsx +3 -3
- package/src/components/ui/action-menu/action-button.tsx +2 -0
- package/src/components/ui/action-menu/camera-actions.tsx +8 -7
- package/src/components/ui/action-menu/control-modes.tsx +9 -5
- package/src/components/ui/action-menu/follow-pascal-pill.tsx +3 -1
- package/src/components/ui/action-menu/follow-pascal-toggle.tsx +7 -1
- package/src/components/ui/action-menu/furnish-tools.tsx +7 -6
- package/src/components/ui/action-menu/index.tsx +1 -0
- package/src/components/ui/action-menu/measurement-control.tsx +35 -29
- package/src/components/ui/action-menu/point-ask-button.tsx +4 -3
- package/src/components/ui/action-menu/structure-tools.tsx +25 -23
- package/src/components/ui/action-menu/view-toggles.test.ts +18 -0
- package/src/components/ui/action-menu/view-toggles.tsx +256 -51
- package/src/components/ui/build-panel/build-panel.tsx +163 -25
- package/src/components/ui/command-palette/editor-commands.tsx +38 -11
- package/src/components/ui/controls/finish-swatch.tsx +45 -19
- package/src/components/ui/controls/material-picker.tsx +24 -17
- package/src/components/ui/controls/metric-control.test.ts +52 -0
- package/src/components/ui/controls/metric-control.tsx +62 -24
- package/src/components/ui/controls/segmented-control.tsx +5 -2
- package/src/components/ui/controls/slider-control.test.tsx +23 -0
- package/src/components/ui/controls/slider-control.tsx +47 -19
- package/src/components/ui/controls/tool-options-panel.tsx +74 -44
- package/src/components/ui/floating-level-selector.tsx +100 -48
- package/src/components/ui/helpers/building-helper.tsx +5 -3
- package/src/components/ui/helpers/contextual-helper-panel.tsx +97 -37
- package/src/components/ui/helpers/helper-manager.test.tsx +106 -3
- package/src/components/ui/helpers/helper-manager.tsx +136 -57
- package/src/components/ui/helpers/item-helper.tsx +6 -4
- package/src/components/ui/helpers/registered-tool-helper.tsx +3 -1
- package/src/components/ui/level-duplicate-dialog.tsx +32 -23
- package/src/components/ui/level-wall-copy-prompt.tsx +124 -0
- package/src/components/ui/panels/collections/collections-popover.tsx +11 -11
- package/src/components/ui/panels/deck-boards-fields.tsx +136 -0
- package/src/components/ui/panels/floor-foundation-panel.tsx +114 -39
- package/src/components/ui/panels/floor-opening-panel.tsx +74 -26
- package/src/components/ui/panels/inspector-extensions.tsx +66 -0
- package/src/components/ui/panels/mobile-panel-sheet.tsx +3 -1
- package/src/components/ui/panels/mobile-selection-bar.tsx +34 -28
- package/src/components/ui/panels/multi-field-value.test.ts +27 -0
- package/src/components/ui/panels/multi-field-value.ts +20 -1
- package/src/components/ui/panels/multi-height-mode.tsx +11 -10
- package/src/components/ui/panels/multi-parametric-inspector.tsx +122 -34
- package/src/components/ui/panels/multi-selection-panel.tsx +76 -36
- package/src/components/ui/panels/node-display.ts +35 -24
- package/src/components/ui/panels/panel-manager.tsx +9 -5
- package/src/components/ui/panels/panel-wrapper.tsx +63 -127
- package/src/components/ui/panels/parametric-field-control.tsx +13 -10
- package/src/components/ui/panels/parametric-inspector.tsx +18 -12
- package/src/components/ui/panels/reference-panel.tsx +51 -35
- package/src/components/ui/panels/room-construction-rows.tsx +465 -66
- package/src/components/ui/panels/room-name-options.test.ts +36 -3
- package/src/components/ui/panels/room-name-options.ts +110 -11
- package/src/components/ui/panels/room-openings-field.tsx +22 -13
- package/src/components/ui/panels/room-panel.tsx +207 -100
- package/src/components/ui/panels/shape-choice.tsx +7 -5
- package/src/components/ui/panels/wall-region-list.tsx +7 -7
- package/src/components/ui/primitives/shortcut-token.tsx +10 -6
- package/src/components/ui/right-stack.test.ts +13 -6
- package/src/components/ui/right-stack.tsx +21 -10
- package/src/components/ui/sidebar/icon-rail.tsx +11 -9
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +10 -10
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +10 -9
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +3 -0
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +102 -32
- package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +4 -3
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +5 -3
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +7 -5
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/default-names.ts +75 -0
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +118 -72
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +36 -3
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +7 -4
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +8 -2
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +8 -5
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +9 -4
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +8 -7
- package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +7 -6
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +16 -1
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +15 -8
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +4 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-membership-checkbox.tsx +3 -1
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +19 -9
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +25 -14
- package/src/components/ui/sidebar/tab-bar.tsx +4 -1
- package/src/components/ui/sidebar/use-plugin-panels.tsx +19 -10
- package/src/components/ui/snap-target-badge.tsx +7 -6
- package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +721 -0
- package/src/components/ui/wall-type/wall-type-menu.tsx +167 -0
- package/src/components/viewer/floorplan-compass-button.tsx +12 -3
- package/src/components/viewer/floorplan-preview.tsx +27 -19
- package/src/components/viewer/render-settings-menu.tsx +294 -0
- package/src/components/viewer/sky-control.tsx +710 -0
- package/src/components/viewer/use-viewer-ui-sounds.test.ts +15 -0
- package/src/components/viewer/use-viewer-ui-sounds.ts +46 -0
- package/src/components/viewer/viewer-controls-bar.tsx +278 -260
- package/src/components/viewer/viewer-hover-label.tsx +12 -4
- package/src/components/viewer/viewer-rooms.tsx +62 -5
- package/src/components/viewer/viewer-scene-header.tsx +28 -17
- package/src/components/viewer/viewer-stage-switcher.tsx +4 -2
- package/src/components/viewer-overlay.tsx +8 -0
- package/src/components/walkthrough-hud.tsx +75 -2
- package/src/hooks/use-keyboard.test.ts +85 -4
- package/src/hooks/use-keyboard.ts +65 -7
- package/src/hooks/use-selected-room.ts +12 -1
- package/src/i18n/floor-footprint-name.ts +30 -0
- package/src/i18n/level-name.ts +39 -0
- package/src/i18n/locales/en/messages.po +15849 -0
- package/src/i18n/locales/en/messages.ts +1 -0
- package/src/i18n/locales/en-XA/messages.ts +1 -0
- package/src/i18n/locales/es/messages.po +39331 -0
- package/src/i18n/locales/es/messages.ts +1 -0
- package/src/i18n/locales/fr/messages.po +39331 -0
- package/src/i18n/locales/fr/messages.ts +1 -0
- package/src/i18n/locales/ja/messages.po +39331 -0
- package/src/i18n/locales/ja/messages.ts +1 -0
- package/src/i18n/locales/zh-CN/messages.po +39331 -0
- package/src/i18n/locales/zh-CN/messages.ts +1 -0
- package/src/i18n/locales.test.ts +63 -0
- package/src/i18n/locales.ts +32 -0
- package/src/i18n/message-slot.tsx +25 -0
- package/src/i18n/node-messages.ts +169 -0
- package/src/i18n/pseudo-locale.ts +17 -0
- package/src/i18n/published-entry.test.ts +17 -0
- package/src/i18n/scene-theme-name.ts +16 -0
- package/src/index.tsx +224 -45
- package/src/lib/add-storey.test.ts +3 -3
- package/src/lib/contextual-help.test.ts +20 -1
- package/src/lib/contextual-help.ts +101 -19
- package/src/lib/continuation.ts +38 -11
- package/src/lib/elevation-guides.ts +21 -6
- package/src/lib/elevator-support.ts +1 -72
- package/src/lib/floor-footprints.test.ts +3 -2
- package/src/lib/floor-footprints.ts +1 -9
- package/src/lib/floor-foundation-support.test.ts +15 -6
- package/src/lib/floor-opening-draft.ts +1 -1
- package/src/lib/floor-region-session.ts +1 -1
- package/src/lib/floorplan/dimension-drive.test.ts +36 -2
- package/src/lib/floorplan/dimension-drive.ts +68 -3
- package/src/lib/floorplan/floorplan-export.test.ts +16 -5
- package/src/lib/floorplan/floorplan-export.tsx +13 -16
- package/src/lib/floorplan/floorplan-extension.test.ts +11 -12
- package/src/lib/floorplan/floorplan-extension.ts +37 -18
- package/src/lib/floorplan/floorplan-mode.test.ts +13 -6
- package/src/lib/floorplan/floorplan-mode.ts +14 -12
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +1 -1
- package/src/lib/floorplan/floorplan-readonly.ts +3 -3
- package/src/lib/floorplan/open-wall-ends.ts +2 -1
- package/src/lib/floorplan/schedules.ts +10 -6
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +46 -1
- package/src/lib/floorplan/site-plan/site-annotations.ts +3 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +6 -3
- package/src/lib/geometry-script/author.test.ts +114 -0
- package/src/lib/geometry-script/author.ts +55 -23
- package/src/lib/geometry-script/client.ts +3 -0
- package/src/lib/geometry-script/finish-scripts.ts +7 -7
- package/src/lib/gesture-lifecycle.ts +3 -1
- package/src/lib/glb-export.test.ts +327 -0
- package/src/lib/glb-export.ts +70 -2
- package/src/lib/grid-frame.test.ts +56 -3
- package/src/lib/grid-frame.ts +28 -1
- package/src/lib/history.test.ts +2 -1
- package/src/lib/hud-title.ts +48 -24
- package/src/lib/ifc-export.test.ts +41 -1
- package/src/lib/inspector-card-mode.test.ts +3 -31
- package/src/lib/inspector-card-mode.ts +5 -10
- package/src/lib/inspector-expanded.test.ts +13 -0
- package/src/lib/inspector-expanded.ts +13 -1
- package/src/lib/interaction/scope.ts +1 -0
- package/src/lib/keyboard-pan.test.ts +7 -0
- package/src/lib/keyboard-pan.ts +9 -2
- package/src/lib/layer-thickness.test.ts +22 -0
- package/src/lib/layer-thickness.ts +79 -0
- package/src/lib/level-selection.test.ts +18 -2
- package/src/lib/level-selection.ts +31 -8
- package/src/lib/linear-display.ts +1 -1
- package/src/lib/live-gesture-round-trip.test.ts +1 -1
- package/src/lib/lot/drop-in.test.ts +59 -1
- package/src/lib/lot/drop-in.ts +42 -50
- package/src/lib/material-swatch-style.ts +24 -0
- package/src/lib/measurement-label.test.ts +1 -1
- package/src/lib/measurement-parser.ts +7 -3
- package/src/lib/measurements.test.ts +24 -6
- package/src/lib/measurements.ts +1 -203
- package/src/lib/mezzanine-selection.test.ts +3 -1
- package/src/lib/node-action-movement.test.tsx +115 -0
- package/src/lib/node-action-movement.ts +2 -0
- package/src/lib/orbit-near-plane.test.ts +12 -0
- package/src/lib/orbit-near-plane.ts +13 -0
- package/src/lib/paint-regions.test.ts +8 -5
- package/src/lib/paint-scope.test.ts +53 -0
- package/src/lib/paint-scope.ts +18 -4
- package/src/lib/panel-rows.ts +17 -8
- package/src/lib/placement-metadata.ts +4 -0
- package/src/lib/point-ask/describe-target.ts +6 -8
- package/src/lib/point-ask/region.ts +1 -1
- package/src/lib/print-shell-compiler.ts +12 -1
- package/src/lib/print-wall-solids.test.ts +11 -0
- package/src/lib/print-wall-solids.ts +9 -0
- package/src/lib/reference-scale.ts +83 -0
- package/src/lib/room-assembly-overlay.ts +1 -1
- package/src/lib/room-built-on.test.ts +28 -17
- package/src/lib/room-built-on.ts +21 -17
- package/src/lib/room-construction-commands.ts +3 -2
- package/src/lib/room-construction.test.ts +28 -1
- package/src/lib/room-construction.ts +14 -0
- package/src/lib/room-controls-phase6.test.ts +141 -1
- package/src/lib/room-handle-drag.ts +554 -58
- package/src/lib/room-name-catalog.test.ts +8 -0
- package/src/lib/room-name-catalog.ts +8 -1
- package/src/lib/room-name-messages.test.ts +132 -0
- package/src/lib/room-name-messages.ts +147 -0
- package/src/lib/room-name-storage.test.ts +334 -0
- package/src/lib/room-push-dimensions.test.ts +38 -1
- package/src/lib/room-push-dimensions.ts +226 -2
- package/src/lib/room-selection.test.ts +23 -6
- package/src/lib/scene-bounds.test.ts +144 -0
- package/src/lib/scene-bounds.ts +79 -15
- package/src/lib/scene-clipboard.test.ts +70 -49
- package/src/lib/scene-clipboard.ts +11 -5
- package/src/lib/selection-cycle.ts +63 -0
- package/src/lib/separator-edit.test.ts +431 -0
- package/src/lib/separator-edit.ts +179 -0
- package/src/lib/separator-lines.ts +38 -0
- package/src/lib/sfx-bus.ts +27 -1
- package/src/lib/sfx-player.ts +16 -0
- package/src/lib/snapping-mode.test.ts +23 -1
- package/src/lib/tool-cursor-icon.ts +5 -1
- package/src/lib/tool-shortcuts.ts +130 -0
- package/src/lib/viewer-selection.test.ts +28 -24
- package/src/lib/viewer-selection.ts +21 -6
- package/src/lib/wall-draw-variant.test.ts +7 -6
- package/src/lib/wall-draw-variant.ts +107 -6
- package/src/lib/wall-nudge.test.ts +44 -0
- package/src/lib/wall-nudge.ts +131 -0
- package/src/lib/wall-parallel-snap.test.ts +83 -0
- package/src/lib/wall-parallel-snap.ts +176 -0
- package/src/lib/wall-placement-pointer.test.ts +23 -0
- package/src/lib/wall-placement-pointer.ts +18 -0
- package/src/lib/wall-pull.test.ts +245 -0
- package/src/lib/wall-pull.ts +209 -0
- package/src/lib/wall-push-line-up.test.ts +132 -0
- package/src/lib/wall-region-list.ts +17 -6
- package/src/lib/wall-run-snap.test.ts +188 -0
- package/src/lib/wall-run-snap.ts +83 -0
- package/src/lib/wall-type.test.ts +125 -0
- package/src/lib/wall-type.ts +119 -0
- package/src/lib/window-interaction.test.ts +44 -0
- package/src/lib/world-grid-snap.test.ts +139 -2
- package/src/lib/world-grid-snap.ts +102 -105
- package/src/store/use-alignment-guides.ts +27 -1
- package/src/store/use-editor.tsx +53 -0
- package/src/store/use-floorplan-annotation-visibility.test.ts +64 -0
- package/src/store/use-floorplan-annotation-visibility.ts +47 -22
- package/src/store/use-placement-typing.test.ts +165 -0
- package/src/store/use-placement-typing.ts +186 -0
- package/src/store/use-wall-snap-indicator.ts +15 -2
- package/src/components/tools/elevator/elevator-defaults.ts +0 -8
- package/src/components/tools/elevator/elevator-tool.tsx +0 -372
- package/src/components/tools/elevator/move-elevator-tool.tsx +0 -262
- package/src/lib/door-interaction.ts +0 -102
- package/src/lib/measurement-label.ts +0 -68
- package/src/lib/window-interaction.ts +0 -100
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type AnyNode,
|
|
5
|
+
type AnyNodeId,
|
|
6
|
+
getLevelAbove,
|
|
7
|
+
getLevelBelow,
|
|
8
|
+
getLevelDisplayName,
|
|
9
|
+
useScene, usePascalI18n,
|
|
10
|
+
} from '@pascal-app/core'
|
|
11
|
+
import { buildExteriorWallCopy, levelExteriorWalls } from '@pascal-app/core/building'
|
|
12
|
+
import { useViewer } from '@pascal-app/viewer'
|
|
13
|
+
import { create } from 'zustand'
|
|
14
|
+
|
|
15
|
+
type Offer = { levelId: string; sourceLevelId: string; direction: 'below' | 'above' }
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The one open "Copy exterior walls?" offer, for the level just added. While it is open the plan
|
|
19
|
+
* traces the source level's walls on the new one; that trace is the offer's, never the saved
|
|
20
|
+
* Walls below / above preference, and it goes with the offer.
|
|
21
|
+
*/
|
|
22
|
+
export const useLevelWallCopyOffer = create<{
|
|
23
|
+
offer: Offer | null
|
|
24
|
+
setOffer: (offer: Offer | null) => void
|
|
25
|
+
}>((set) => ({ offer: null, setOffer: (offer) => set({ offer }) }))
|
|
26
|
+
|
|
27
|
+
let stopWatchingLevel: (() => void) | null = null
|
|
28
|
+
|
|
29
|
+
/** Opens `offer` (or closes the open one); picking another level closes it. */
|
|
30
|
+
function setOffer(offer: Offer | null) {
|
|
31
|
+
stopWatchingLevel?.()
|
|
32
|
+
stopWatchingLevel = null
|
|
33
|
+
useLevelWallCopyOffer.getState().setOffer(offer)
|
|
34
|
+
if (!offer) return
|
|
35
|
+
stopWatchingLevel = useViewer.subscribe((state) => {
|
|
36
|
+
if (state.selection.levelId !== offer.levelId) setOffer(null)
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* After a level is added next to `sourceLevelId` (by default the level under it, else the one over
|
|
42
|
+
* it, for a basement), and selected: when that level has an outline, offer to copy its exterior
|
|
43
|
+
* walls up or down, tracing them dashed on the plan while the offer is open.
|
|
44
|
+
*/
|
|
45
|
+
export function offerExteriorWallCopy(levelId: string, sourceLevelId?: string) {
|
|
46
|
+
const nodes = useScene.getState().nodes as Record<string, AnyNode>
|
|
47
|
+
const graph = nodes as Record<AnyNodeId, AnyNode>
|
|
48
|
+
sourceLevelId ??= (getLevelBelow(levelId, graph) ?? getLevelAbove(levelId, graph))?.id
|
|
49
|
+
const source = sourceLevelId ? nodes[sourceLevelId] : undefined
|
|
50
|
+
const level = nodes[levelId]
|
|
51
|
+
if (!sourceLevelId || source?.type !== 'level' || level?.type !== 'level') return setOffer(null)
|
|
52
|
+
const direction = source.level < level.level ? 'below' : 'above'
|
|
53
|
+
setOffer(
|
|
54
|
+
levelExteriorWalls(nodes, sourceLevelId).length ? { levelId, sourceLevelId, direction } : null,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Closes the open offer without copying. */
|
|
59
|
+
export function dismissExteriorWallCopy() {
|
|
60
|
+
setOffer(null)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Copies the offered walls in place, as one undo step of its own. */
|
|
64
|
+
export function acceptExteriorWallCopy(offer: Offer): number {
|
|
65
|
+
const walls = buildExteriorWallCopy({
|
|
66
|
+
nodes: useScene.getState().nodes as Record<string, AnyNode>,
|
|
67
|
+
fromLevelId: offer.sourceLevelId,
|
|
68
|
+
toLevelId: offer.levelId,
|
|
69
|
+
})
|
|
70
|
+
if (walls.length) useScene.getState().applyNodeChanges({ create: walls })
|
|
71
|
+
setOffer(null)
|
|
72
|
+
return walls.length
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* "Copy exterior walls from the level below?" — a small card under the level list, never in the
|
|
77
|
+
* way: it goes when answered or when another level is picked, and hides once the level has walls.
|
|
78
|
+
*/
|
|
79
|
+
export function LevelWallCopyPrompt() {
|
|
80
|
+
const i18n = usePascalI18n()
|
|
81
|
+
const offer = useLevelWallCopyOffer((state) => state.offer)
|
|
82
|
+
const activeLevelId = useViewer((state) => state.selection.levelId)
|
|
83
|
+
const sourceName = useScene((state) => {
|
|
84
|
+
const source = offer ? state.nodes[offer.sourceLevelId as never] : undefined
|
|
85
|
+
return source?.type === 'level' ? getLevelDisplayName(source) : null
|
|
86
|
+
})
|
|
87
|
+
const levelHasWalls = useScene((state) => {
|
|
88
|
+
const level = offer ? state.nodes[offer.levelId as AnyNodeId] : undefined
|
|
89
|
+
return (
|
|
90
|
+
level?.type === 'level' &&
|
|
91
|
+
level.children.some((id) => state.nodes[id as AnyNodeId]?.type === 'wall')
|
|
92
|
+
)
|
|
93
|
+
})
|
|
94
|
+
if (!offer || !sourceName || activeLevelId !== offer.levelId || levelHasWalls) return null
|
|
95
|
+
return (
|
|
96
|
+
<div
|
|
97
|
+
className="mt-2 w-56 rounded-xl border border-border bg-background/95 p-3 text-xs shadow-2xl backdrop-blur-md"
|
|
98
|
+
data-level-wall-copy={offer.levelId}
|
|
99
|
+
role="status"
|
|
100
|
+
>
|
|
101
|
+
<p className="font-medium text-foreground text-sm">
|
|
102
|
+
{i18n._({ id: 'editor.levelWallCopyPrompt.copyExteriorWallsFromTheLevel', message: 'Copy exterior walls from the level {direction}?', values: { direction: offer.direction } })}</p>
|
|
103
|
+
<p className="mt-0.5 text-muted-foreground">
|
|
104
|
+
{i18n._({ id: 'editor.levelWallCopyPrompt.sourcenameSOutsideWallsInPlace', message: '{sourceName}’s outside walls, in place, so this level lines up with it.', values: { sourceName } })}</p>
|
|
105
|
+
<div className="mt-2.5 flex gap-2">
|
|
106
|
+
<button
|
|
107
|
+
className="flex-1 rounded-full bg-foreground px-3 py-1.5 font-medium text-background transition-opacity hover:opacity-90"
|
|
108
|
+
data-level-wall-copy-accept
|
|
109
|
+
onClick={() => acceptExteriorWallCopy(offer)}
|
|
110
|
+
type="button"
|
|
111
|
+
>
|
|
112
|
+
{i18n._({ id: 'editor.levelWallCopyPrompt.copy', message: 'Copy' })}
|
|
113
|
+
</button>
|
|
114
|
+
<button
|
|
115
|
+
className="flex-1 rounded-full border border-border px-3 py-1.5 text-foreground transition-colors hover:bg-accent"
|
|
116
|
+
onClick={dismissExteriorWallCopy}
|
|
117
|
+
type="button"
|
|
118
|
+
>
|
|
119
|
+
{i18n._({ id: 'editor.levelWallCopyPrompt.noThanks', message: 'No thanks' })}
|
|
120
|
+
</button>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import type { AnyNodeId, CollectionId } from '@pascal-app/core'
|
|
4
|
-
import { useScene } from '@pascal-app/core'
|
|
4
|
+
import { useScene, usePascalI18n } from '@pascal-app/core'
|
|
5
5
|
import {
|
|
6
6
|
Check,
|
|
7
7
|
ChevronDown,
|
|
@@ -36,6 +36,7 @@ interface CollectionsPopoverProps {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProps) {
|
|
39
|
+
const i18n = usePascalI18n()
|
|
39
40
|
const collections = useScene((s) => s.collections)
|
|
40
41
|
const nodes = useScene((s) => s.nodes)
|
|
41
42
|
const createCollection = useScene((s) => s.createCollection)
|
|
@@ -102,7 +103,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
102
103
|
<div className="flex items-center gap-1.5">
|
|
103
104
|
<Layers className="h-3.5 w-3.5 text-muted-foreground" />
|
|
104
105
|
<span className="font-semibold text-foreground text-xs tracking-tight">
|
|
105
|
-
Collections
|
|
106
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.collections', message: 'Collections' })}
|
|
106
107
|
</span>
|
|
107
108
|
</div>
|
|
108
109
|
<button
|
|
@@ -114,7 +115,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
114
115
|
type="button"
|
|
115
116
|
>
|
|
116
117
|
<Plus className="h-3 w-3" />
|
|
117
|
-
New
|
|
118
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.new', message: 'New' })}
|
|
118
119
|
</button>
|
|
119
120
|
</div>
|
|
120
121
|
|
|
@@ -132,7 +133,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
132
133
|
setCreateName('')
|
|
133
134
|
}
|
|
134
135
|
}}
|
|
135
|
-
placeholder=
|
|
136
|
+
placeholder={i18n._({ id: 'editor.panels.collections.collectionsPopover.collectionName', message: 'Collection name…' })}
|
|
136
137
|
value={createName}
|
|
137
138
|
/>
|
|
138
139
|
<button
|
|
@@ -162,7 +163,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
162
163
|
<div className="flex flex-col items-center justify-center gap-2 px-4 py-8 text-center">
|
|
163
164
|
<Layers className="h-6 w-6 text-muted-foreground/40" />
|
|
164
165
|
<p className="text-muted-foreground text-xs">
|
|
165
|
-
No collections yet. Create one to group elements together.
|
|
166
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.noCollectionsYetCreateOneTo', message: 'No collections yet. Create one to group elements together.' })}
|
|
166
167
|
</p>
|
|
167
168
|
</div>
|
|
168
169
|
) : (
|
|
@@ -182,8 +183,7 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
182
183
|
key={collection.id}
|
|
183
184
|
>
|
|
184
185
|
<span className="truncate text-foreground/80 text-xs">
|
|
185
|
-
Delete "{collection.name}
|
|
186
|
-
</span>
|
|
186
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.deleteName', message: 'Delete "{name}"?', values: { name: collection.name } })}</span>
|
|
187
187
|
<div className="flex shrink-0 items-center gap-1">
|
|
188
188
|
<button
|
|
189
189
|
className="rounded-md bg-red-500/20 px-2 py-0.5 font-medium text-[11px] text-red-600 dark:text-red-400 transition-colors hover:bg-red-500/30"
|
|
@@ -193,14 +193,14 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
193
193
|
}}
|
|
194
194
|
type="button"
|
|
195
195
|
>
|
|
196
|
-
Delete
|
|
196
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.delete', message: 'Delete' })}
|
|
197
197
|
</button>
|
|
198
198
|
<button
|
|
199
199
|
className="rounded-md px-2 py-0.5 font-medium text-[11px] text-muted-foreground transition-colors hover:bg-black/6 dark:hover:bg-white/10"
|
|
200
200
|
onClick={() => setDeletingId(null)}
|
|
201
201
|
type="button"
|
|
202
202
|
>
|
|
203
|
-
Cancel
|
|
203
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.cancel', message: 'Cancel' })}
|
|
204
204
|
</button>
|
|
205
205
|
</div>
|
|
206
206
|
</li>
|
|
@@ -314,14 +314,14 @@ export function CollectionsPopover({ nodeIds, children }: CollectionsPopoverProp
|
|
|
314
314
|
}}
|
|
315
315
|
>
|
|
316
316
|
<Pencil className="h-3.5 w-3.5" />
|
|
317
|
-
Rename
|
|
317
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.rename', message: 'Rename' })}
|
|
318
318
|
</DropdownMenuItem>
|
|
319
319
|
<DropdownMenuItem
|
|
320
320
|
onClick={() => setDeletingId(collection.id)}
|
|
321
321
|
variant="destructive"
|
|
322
322
|
>
|
|
323
323
|
<Trash2 className="h-3.5 w-3.5" />
|
|
324
|
-
Delete
|
|
324
|
+
{i18n._({ id: 'editor.panels.collections.collectionsPopover.delete', message: 'Delete' })}
|
|
325
325
|
</DropdownMenuItem>
|
|
326
326
|
</DropdownMenuContent>
|
|
327
327
|
</DropdownMenu>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type AnyNodeId,
|
|
5
|
+
DECK_BOARD_GAPS,
|
|
6
|
+
DECK_BOARD_SIZES,
|
|
7
|
+
DECK_BOARD_STOCK,
|
|
8
|
+
type DeckBoardDirection,
|
|
9
|
+
type DeckBoardSize,
|
|
10
|
+
deckBoardSettings,
|
|
11
|
+
deckJoistSpacingLimit,
|
|
12
|
+
inchFractionLabel,
|
|
13
|
+
type MessageDescriptor,
|
|
14
|
+
setZoneIntent,
|
|
15
|
+
usePascalI18n,
|
|
16
|
+
useScene,
|
|
17
|
+
} from '@pascal-app/core'
|
|
18
|
+
import type { ReactNode } from 'react'
|
|
19
|
+
import { useShallow } from 'zustand/react/shallow'
|
|
20
|
+
import { applyRoomPlan } from '../../../lib/room-structure-commands'
|
|
21
|
+
import { SegmentedControl } from '../controls/segmented-control'
|
|
22
|
+
|
|
23
|
+
const IN = 0.0254
|
|
24
|
+
|
|
25
|
+
const BOARD_OPTIONS = DECK_BOARD_SIZES.map((value) => ({
|
|
26
|
+
label: DECK_BOARD_STOCK[value].label,
|
|
27
|
+
value,
|
|
28
|
+
}))
|
|
29
|
+
|
|
30
|
+
const GAP_OPTIONS = DECK_BOARD_GAPS.map((gap, index) => ({
|
|
31
|
+
label: `${inchFractionLabel(gap)} in`,
|
|
32
|
+
value: String(index),
|
|
33
|
+
}))
|
|
34
|
+
|
|
35
|
+
const RUN_OPTIONS: ReadonlyArray<{ label: MessageDescriptor; value: DeckBoardDirection }> = [
|
|
36
|
+
{
|
|
37
|
+
label: /* i18n */ {
|
|
38
|
+
id: 'editor.panels.deckBoardsFields.alongHouse',
|
|
39
|
+
message: 'Along house',
|
|
40
|
+
},
|
|
41
|
+
value: 'parallel',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: /* i18n */ { id: 'editor.panels.deckBoardsFields.diagonal', message: 'Diagonal' },
|
|
45
|
+
value: 'diagonal',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: /* i18n */ {
|
|
49
|
+
id: 'editor.panels.deckBoardsFields.outFromHouse',
|
|
50
|
+
message: 'Out from house',
|
|
51
|
+
},
|
|
52
|
+
value: 'perpendicular',
|
|
53
|
+
},
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
function Field({ label, children }: { label: string; children: ReactNode }) {
|
|
57
|
+
return (
|
|
58
|
+
<div className="flex min-h-7 items-center justify-between gap-3">
|
|
59
|
+
<span className="shrink-0">{label}</span>
|
|
60
|
+
{children}
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A deck's boards (`decking`, through core's `setZoneIntent`): their stock, the gap between them
|
|
67
|
+
* and the way they run against the house. The engine lays them over the deck's frame, and the
|
|
68
|
+
* frame follows: joists 12 in on centre under diagonal 5/4 or composite boards (IRC R507.7), and
|
|
69
|
+
* along the house on beams square to it under boards run out from it.
|
|
70
|
+
*/
|
|
71
|
+
export function DeckBoardsFields({ zoneId }: { zoneId: string }) {
|
|
72
|
+
const i18n = usePascalI18n()
|
|
73
|
+
const deck = useScene(
|
|
74
|
+
useShallow((scene) => {
|
|
75
|
+
const zone = scene.nodes[zoneId as AnyNodeId]
|
|
76
|
+
if (zone?.type !== 'zone' || zone.roomType !== 'deck') return null
|
|
77
|
+
return deckBoardSettings(zone)
|
|
78
|
+
}),
|
|
79
|
+
)
|
|
80
|
+
if (!deck) return null
|
|
81
|
+
const set = (decking: NonNullable<Parameters<typeof setZoneIntent>[1]['patch']['decking']>) =>
|
|
82
|
+
applyRoomPlan(setZoneIntent(useScene.getState().nodes, { zoneId, patch: { decking } }))
|
|
83
|
+
const stock = DECK_BOARD_STOCK[deck.boardSize]
|
|
84
|
+
const gap = DECK_BOARD_GAPS.findIndex((option) => Math.abs(option - deck.gap) < 1e-6)
|
|
85
|
+
const spacing = Math.min(16 * IN, deckJoistSpacingLimit(deck))
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
className="mt-3 flex flex-col gap-1.5 text-muted-foreground text-xs"
|
|
89
|
+
data-deck-boards={deck.boardSize}
|
|
90
|
+
>
|
|
91
|
+
<h3 className="font-medium text-xs">
|
|
92
|
+
{i18n._({ id: 'editor.panels.deckBoardsFields.deckBoards', message: 'Deck boards' })}
|
|
93
|
+
</h3>
|
|
94
|
+
<Field label={i18n._({ id: 'editor.panels.deckBoardsFields.boards', message: 'Boards' })}>
|
|
95
|
+
<SegmentedControl
|
|
96
|
+
onChange={(boardSize: DeckBoardSize) => set({ boardSize })}
|
|
97
|
+
options={BOARD_OPTIONS}
|
|
98
|
+
value={deck.boardSize}
|
|
99
|
+
/>
|
|
100
|
+
</Field>
|
|
101
|
+
<Field label={i18n._({ id: 'editor.panels.deckBoardsFields.gap', message: 'Gap' })}>
|
|
102
|
+
<SegmentedControl
|
|
103
|
+
mixed={gap < 0}
|
|
104
|
+
onChange={(index: string) => set({ gap: DECK_BOARD_GAPS[Number(index)] })}
|
|
105
|
+
options={GAP_OPTIONS}
|
|
106
|
+
value={String(Math.max(gap, 0))}
|
|
107
|
+
/>
|
|
108
|
+
</Field>
|
|
109
|
+
<Field label={i18n._({ id: 'editor.panels.deckBoardsFields.run', message: 'Run' })}>
|
|
110
|
+
<SegmentedControl
|
|
111
|
+
onChange={(direction: DeckBoardDirection) => set({ direction })}
|
|
112
|
+
options={RUN_OPTIONS.map((option) => ({
|
|
113
|
+
label: i18n._(option.label),
|
|
114
|
+
value: option.value,
|
|
115
|
+
}))}
|
|
116
|
+
value={deck.direction}
|
|
117
|
+
/>
|
|
118
|
+
</Field>
|
|
119
|
+
<p>
|
|
120
|
+
{i18n._({
|
|
121
|
+
id: 'editor.panels.deckBoardsFields.boardsSummary',
|
|
122
|
+
message:
|
|
123
|
+
'{label} boards {thickness} × {width} in, {gap} in apart, {direction, select, parallel {along the house, on joists square to it} diagonal {at 45° to the house} other {out from the house, on joists along it, carried by beams square to the house}}; joists {spacing} in on centre.',
|
|
124
|
+
values: {
|
|
125
|
+
label: stock.label,
|
|
126
|
+
thickness: inchFractionLabel(stock.thickness),
|
|
127
|
+
width: inchFractionLabel(stock.width),
|
|
128
|
+
gap: inchFractionLabel(deck.gap),
|
|
129
|
+
direction: deck.direction,
|
|
130
|
+
spacing: Math.round(spacing / IN),
|
|
131
|
+
},
|
|
132
|
+
})}
|
|
133
|
+
</p>
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
}
|