@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
|
@@ -6,14 +6,15 @@ import {
|
|
|
6
6
|
type BuildingNode,
|
|
7
7
|
resolveBuildingForLevel,
|
|
8
8
|
runAsSingleSceneHistoryStep,
|
|
9
|
-
useScene,
|
|
9
|
+
useScene, usePascalI18n,
|
|
10
|
+
type PascalI18n,
|
|
10
11
|
} from '@pascal-app/core'
|
|
11
12
|
import { unwrapCorner, windowMergeOffer } from '@pascal-app/core/building'
|
|
12
13
|
import { useViewer } from '@pascal-app/viewer'
|
|
13
14
|
import { Building2, Copy, Group, Merge, Split, Trash2, Ungroup } from 'lucide-react'
|
|
14
15
|
import { useMemo } from 'react'
|
|
15
16
|
import { deleteSelection, duplicateSelectionAndPickUp } from '../../editor/group-actions'
|
|
16
|
-
import { formatLinearMeasurement } from '
|
|
17
|
+
import { formatLinearMeasurement } from '@pascal-app/core'
|
|
17
18
|
import { triggerSFX } from '../../../lib/sfx-bus'
|
|
18
19
|
import { writeWindowChanges } from '../../../lib/window-changes'
|
|
19
20
|
import { collectSelectableCandidateIds } from '../../tools/select/select-candidates'
|
|
@@ -57,36 +58,71 @@ function mergeRefusalNote(
|
|
|
57
58
|
refusal: { code: string; details: Record<string, unknown> },
|
|
58
59
|
nodes: Record<string, AnyNode>,
|
|
59
60
|
length: (meters: number) => string,
|
|
61
|
+
i18n: PascalI18n,
|
|
60
62
|
): string {
|
|
61
63
|
const { blockingId, limit } = refusal.details as { blockingId?: string; limit?: number }
|
|
62
64
|
const reach = length(limit ?? 0)
|
|
63
65
|
const blocking = nodes[blockingId ?? '']
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
: blocking.type === 'item'
|
|
67
|
-
? 'an item'
|
|
68
|
-
: `a ${blocking.type}`
|
|
66
|
+
// A wall's children: the openings and wall-hosted items the merge can run into.
|
|
67
|
+
const kind = blocking?.type ?? 'unknown'
|
|
69
68
|
switch (refusal.code) {
|
|
70
69
|
case 'window_already_joined':
|
|
71
|
-
return
|
|
70
|
+
return i18n._({
|
|
71
|
+
id: 'editor.panels.mergeRefusal.alreadyJoined',
|
|
72
|
+
message: 'One of them already wraps another corner. Unwrap it first.',
|
|
73
|
+
})
|
|
72
74
|
case 'window_not_on_wall':
|
|
73
|
-
return
|
|
75
|
+
return i18n._({
|
|
76
|
+
id: 'editor.panels.mergeRefusal.notOnWall',
|
|
77
|
+
message: 'A roof or dormer window cannot join another window.',
|
|
78
|
+
})
|
|
74
79
|
case 'windows_not_adjacent':
|
|
75
|
-
return
|
|
80
|
+
return i18n._({
|
|
81
|
+
id: 'editor.panels.mergeRefusal.notAdjacent',
|
|
82
|
+
message: 'They are too far apart. Windows merge with at most {reach} between them.',
|
|
83
|
+
values: { reach },
|
|
84
|
+
})
|
|
76
85
|
case 'opening_between':
|
|
77
|
-
return
|
|
86
|
+
return i18n._({
|
|
87
|
+
id: 'editor.panels.mergeRefusal.between',
|
|
88
|
+
message:
|
|
89
|
+
'{kind, select, item {There is an item between them.} door {There is a door between them.} window {There is a window between them.} shelf {There is a shelf between them.} other {There is something between them.}}',
|
|
90
|
+
values: { kind },
|
|
91
|
+
})
|
|
78
92
|
case 'walls_not_meeting':
|
|
79
|
-
return
|
|
93
|
+
return i18n._({
|
|
94
|
+
id: 'editor.panels.mergeRefusal.wallsNotMeeting',
|
|
95
|
+
message: 'Their walls do not meet at a corner.',
|
|
96
|
+
})
|
|
80
97
|
case 'corner_angle':
|
|
81
|
-
return
|
|
98
|
+
return i18n._({
|
|
99
|
+
id: 'editor.panels.mergeRefusal.cornerAngle',
|
|
100
|
+
message: 'Their walls meet at too sharp or too flat an angle for a corner window.',
|
|
101
|
+
})
|
|
82
102
|
case 'window_not_at_corner':
|
|
83
|
-
return
|
|
103
|
+
return i18n._({
|
|
104
|
+
id: 'editor.panels.mergeRefusal.notAtCorner',
|
|
105
|
+
message: 'One of them is too far from the corner. A window joins one within {reach} of it.',
|
|
106
|
+
values: { reach },
|
|
107
|
+
})
|
|
84
108
|
case 'height_exceeds_wall':
|
|
85
|
-
return
|
|
109
|
+
return i18n._({
|
|
110
|
+
id: 'editor.panels.mergeRefusal.heightExceedsWall',
|
|
111
|
+
message:
|
|
112
|
+
'The window selected first is taller than the other wall. Select the other one first.',
|
|
113
|
+
})
|
|
86
114
|
case 'opening_overlap':
|
|
87
|
-
return
|
|
115
|
+
return i18n._({
|
|
116
|
+
id: 'editor.panels.mergeRefusal.overlap',
|
|
117
|
+
message:
|
|
118
|
+
'{kind, select, item {Sliding them to the corner would overlap an item.} door {Sliding them to the corner would overlap a door.} window {Sliding them to the corner would overlap a window.} shelf {Sliding them to the corner would overlap a shelf.} other {Sliding them to the corner would overlap something.}}',
|
|
119
|
+
values: { kind },
|
|
120
|
+
})
|
|
88
121
|
default:
|
|
89
|
-
return
|
|
122
|
+
return i18n._({
|
|
123
|
+
id: 'editor.panels.mergeRefusal.cannotMerge',
|
|
124
|
+
message: 'These windows cannot be merged.',
|
|
125
|
+
})
|
|
90
126
|
}
|
|
91
127
|
}
|
|
92
128
|
|
|
@@ -97,6 +133,7 @@ function mergeRefusalNote(
|
|
|
97
133
|
* Unwrap instead; two that cannot join, the reason.
|
|
98
134
|
*/
|
|
99
135
|
function MergeWindowsAction() {
|
|
136
|
+
const i18n = usePascalI18n()
|
|
100
137
|
const selectedIds = useViewer((s) => s.selection.selectedIds)
|
|
101
138
|
const nodes = useScene((s) => s.nodes)
|
|
102
139
|
const readOnly = useScene((s) => s.readOnly)
|
|
@@ -110,7 +147,7 @@ function MergeWindowsAction() {
|
|
|
110
147
|
className="basis-full whitespace-nowrap disabled:cursor-not-allowed disabled:opacity-40"
|
|
111
148
|
disabled={readOnly}
|
|
112
149
|
icon={<Split className="h-4 w-4" />}
|
|
113
|
-
label=
|
|
150
|
+
label={i18n._({ id: 'editor.panels.multiSelectionPanel.unwrapTheCorner', message: 'Unwrap the corner' })}
|
|
114
151
|
onClick={() => {
|
|
115
152
|
writeWindowChanges({
|
|
116
153
|
create: null,
|
|
@@ -119,13 +156,16 @@ function MergeWindowsAction() {
|
|
|
119
156
|
})
|
|
120
157
|
triggerSFX('sfx:structure-build')
|
|
121
158
|
}}
|
|
122
|
-
title=
|
|
159
|
+
title={i18n._({ id: 'editor.panels.multiSelectionPanel.unwrapTheCornerBothWindowsStay', message: 'Unwrap the corner: both windows stay, each framed at the corner' })}
|
|
123
160
|
/>
|
|
124
161
|
)
|
|
125
162
|
const { merge } = offer
|
|
126
163
|
const note = offer.refusal
|
|
127
|
-
? mergeRefusalNote(
|
|
128
|
-
|
|
164
|
+
? mergeRefusalNote(
|
|
165
|
+
offer.refusal,
|
|
166
|
+
nodes,
|
|
167
|
+
(meters) => formatLinearMeasurement(meters, unit, metricNotation),
|
|
168
|
+
i18n,
|
|
129
169
|
)
|
|
130
170
|
: null
|
|
131
171
|
return (
|
|
@@ -134,7 +174,7 @@ function MergeWindowsAction() {
|
|
|
134
174
|
className="basis-full whitespace-nowrap disabled:cursor-not-allowed disabled:opacity-40"
|
|
135
175
|
disabled={readOnly || !merge}
|
|
136
176
|
icon={<Merge className="h-4 w-4" />}
|
|
137
|
-
label=
|
|
177
|
+
label={i18n._({ id: 'editor.panels.multiSelectionPanel.mergeWindows', message: 'Merge windows' })}
|
|
138
178
|
onClick={() => {
|
|
139
179
|
if (!merge) return
|
|
140
180
|
runAsSingleSceneHistoryStep(useScene, () =>
|
|
@@ -150,8 +190,8 @@ function MergeWindowsAction() {
|
|
|
150
190
|
title={
|
|
151
191
|
note ??
|
|
152
192
|
(merge?.kind === 'corner'
|
|
153
|
-
? 'Merge windows into a corner window: both take the height, sill and type of the one you selected first'
|
|
154
|
-
: 'Merge windows into one across both: it keeps the type and style of the one you selected first')
|
|
193
|
+
? i18n._({ id: 'editor.panels.multiSelectionPanel.mergeWindowsIntoACornerWindow', message: 'Merge windows into a corner window: both take the height, sill and type of the one you selected first' })
|
|
194
|
+
: i18n._({ id: 'editor.panels.multiSelectionPanel.mergeWindowsIntoOneAcrossBoth', message: 'Merge windows into one across both: it keeps the type and style of the one you selected first' }))
|
|
155
195
|
}
|
|
156
196
|
/>
|
|
157
197
|
{note ? <p className="basis-full px-1 text-muted-foreground text-xs">{note}</p> : null}
|
|
@@ -160,6 +200,7 @@ function MergeWindowsAction() {
|
|
|
160
200
|
}
|
|
161
201
|
|
|
162
202
|
export function MultiSelectionActions() {
|
|
203
|
+
const i18n = usePascalI18n()
|
|
163
204
|
const selectedIds = useViewer((s) => s.selection.selectedIds)
|
|
164
205
|
const sessionGroups = useSessionGroups((s) => s.groups)
|
|
165
206
|
const sceneNodes = useScene((s) => s.nodes)
|
|
@@ -182,39 +223,39 @@ export function MultiSelectionActions() {
|
|
|
182
223
|
<ActionButton
|
|
183
224
|
className="basis-full whitespace-nowrap"
|
|
184
225
|
icon={<Building2 className="h-4 w-4" />}
|
|
185
|
-
label=
|
|
226
|
+
label={i18n._({ id: 'editor.panels.multiSelectionPanel.selectBuilding', message: 'Select building' })}
|
|
186
227
|
onClick={() => selectBuilding(buildingId)}
|
|
187
|
-
title=
|
|
228
|
+
title={i18n._({ id: 'editor.panels.multiSelectionPanel.selectTheWholeBuildingToMove', message: 'Select the whole building to move all its levels' })}
|
|
188
229
|
/>
|
|
189
230
|
)}
|
|
190
231
|
{showGroup && (
|
|
191
232
|
<ActionButton
|
|
192
233
|
className="min-w-20 px-2"
|
|
193
234
|
icon={<Group className="h-4 w-4" />}
|
|
194
|
-
label=
|
|
235
|
+
label={i18n._({ id: 'editor.panels.multiSelectionPanel.group', message: 'Group' })}
|
|
195
236
|
onClick={() => groupCurrentSelection()}
|
|
196
|
-
title=
|
|
237
|
+
title={i18n._({ id: 'editor.panels.multiSelectionPanel.groupCtrlCmdG', message: 'Group (Ctrl/Cmd+G)' })}
|
|
197
238
|
/>
|
|
198
239
|
)}
|
|
199
240
|
{showUngroup && (
|
|
200
241
|
<ActionButton
|
|
201
242
|
className="min-w-20 px-2"
|
|
202
243
|
icon={<Ungroup className="h-4 w-4" />}
|
|
203
|
-
label=
|
|
244
|
+
label={i18n._({ id: 'editor.panels.multiSelectionPanel.ungroup', message: 'Ungroup' })}
|
|
204
245
|
onClick={() => ungroupCurrentSelection()}
|
|
205
|
-
title=
|
|
246
|
+
title={i18n._({ id: 'editor.panels.multiSelectionPanel.ungroupCtrlCmdShiftG', message: 'Ungroup (Ctrl/Cmd+Shift+G)' })}
|
|
206
247
|
/>
|
|
207
248
|
)}
|
|
208
249
|
<ActionButton
|
|
209
250
|
className="min-w-20 px-2"
|
|
210
251
|
icon={<Copy className="h-4 w-4" />}
|
|
211
|
-
label=
|
|
252
|
+
label={i18n._({ id: 'editor.panels.multiSelectionPanel.duplicate', message: 'Duplicate' })}
|
|
212
253
|
onClick={() => duplicateSelectionAndPickUp()}
|
|
213
254
|
/>
|
|
214
255
|
<ActionButton
|
|
215
256
|
className="min-w-20 px-2 border-red-500/40 text-red-700 dark:text-red-200 hover:bg-red-500/15"
|
|
216
257
|
icon={<Trash2 className="h-4 w-4 text-red-600 dark:text-red-400" />}
|
|
217
|
-
label=
|
|
258
|
+
label={i18n._({ id: 'editor.panels.multiSelectionPanel.delete', message: 'Delete' })}
|
|
218
259
|
onClick={() => deleteSelection()}
|
|
219
260
|
/>
|
|
220
261
|
</ActionGroup>
|
|
@@ -225,6 +266,7 @@ export function MultiSelectionActions() {
|
|
|
225
266
|
* Docked multi-selection panel. Includes Group / Ungroup for session selection sets.
|
|
226
267
|
*/
|
|
227
268
|
export function MultiSelectionPanel({ footer }: { footer?: React.ReactNode }) {
|
|
269
|
+
const i18n = usePascalI18n()
|
|
228
270
|
const selectedIds = useViewer((s) => s.selection.selectedIds)
|
|
229
271
|
const setSelection = useViewer((s) => s.setSelection)
|
|
230
272
|
const sessionGroups = useSessionGroups((s) => s.groups)
|
|
@@ -246,16 +288,14 @@ export function MultiSelectionPanel({ footer }: { footer?: React.ReactNode }) {
|
|
|
246
288
|
title={
|
|
247
289
|
matchedGroup
|
|
248
290
|
? `${matchedGroup.label} · ${selectedIds.length}`
|
|
249
|
-
:
|
|
291
|
+
: i18n._({ id: 'editor.panels.multiSelectionPanel.lengthSelected', message: '{length} selected', values: { length: selectedIds.length } })
|
|
250
292
|
}
|
|
251
293
|
width={320}
|
|
252
294
|
>
|
|
253
295
|
{breakdown && <div className="px-3 py-3 text-muted-foreground text-xs">{breakdown}</div>}
|
|
254
296
|
{matchedGroup && (
|
|
255
297
|
<div className="border-border/50 border-t px-3 py-2 text-muted-foreground text-xs">
|
|
256
|
-
{
|
|
257
|
-
project.
|
|
258
|
-
</div>
|
|
298
|
+
{i18n._({ id: 'editor.panels.multiSelectionPanel.labelSessionOnlyPlainClickReselects', message: '{label} (session only). Plain click reselects all members. Not saved with the project.', values: { label: matchedGroup.label } })}</div>
|
|
259
299
|
)}
|
|
260
300
|
<div className="border-border/50 border-t p-3">
|
|
261
301
|
<MultiSelectionActions />
|
|
@@ -1,36 +1,42 @@
|
|
|
1
|
-
import { type AnyNode, scriptImages } from '@pascal-app/core'
|
|
1
|
+
import { type AnyNode, type MessageDescriptor, type PascalI18n, scriptImages } from '@pascal-app/core'
|
|
2
|
+
import { displayRoomName } from '../../../lib/room-name-messages'
|
|
2
3
|
|
|
3
4
|
export type NodeDisplay = {
|
|
4
5
|
icon: string
|
|
5
6
|
label: string
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
const TYPE_DEFAULTS: Record<string,
|
|
9
|
-
item: { icon: '/icons/item.webp', label: 'Item' },
|
|
10
|
-
wall: { icon: '/icons/wall.webp', label: 'Wall' },
|
|
11
|
-
door: { icon: '/icons/door.webp', label: 'Door' },
|
|
12
|
-
window: { icon: '/icons/window.webp', label: 'Window' },
|
|
13
|
-
slab: { icon: '/icons/floor.webp', label: 'Slab' },
|
|
14
|
-
ceiling: { icon: '/icons/ceiling.webp', label: 'Ceiling' },
|
|
15
|
-
column: { icon: '/icons/column.webp', label: 'Column' },
|
|
16
|
-
elevator: { icon: '/icons/elevator.webp', label: 'Elevator' },
|
|
17
|
-
fence: { icon: '/icons/fence.webp', label: 'Fence' },
|
|
18
|
-
roof: { icon: '/icons/roof.webp', label: 'Roof' },
|
|
19
|
-
'roof-segment': { icon: '/icons/roof.webp', label: 'Roof segment' },
|
|
20
|
-
stair: { icon: '/icons/stairs.webp', label: 'Stair' },
|
|
21
|
-
'stair-segment': { icon: '/icons/stairs.webp', label: 'Stair segment' },
|
|
22
|
-
scan: { icon: '/icons/mesh.webp', label: '3D Scan' },
|
|
23
|
-
guide: { icon: '/icons/floorplan.webp', label: 'Guide image' },
|
|
24
|
-
zone: { icon: '/icons/zone.webp', label: 'Zone' },
|
|
9
|
+
const TYPE_DEFAULTS: Record<string, { icon: string; label: MessageDescriptor }> = {
|
|
10
|
+
item: { icon: '/icons/item.webp', label: /* i18n */ { id: 'editor.nodeDisplay.item', message: 'Item' } },
|
|
11
|
+
wall: { icon: '/icons/wall.webp', label: /* i18n */ { id: 'editor.nodeDisplay.wall', message: 'Wall' } },
|
|
12
|
+
door: { icon: '/icons/door.webp', label: /* i18n */ { id: 'editor.nodeDisplay.door', message: 'Door' } },
|
|
13
|
+
window: { icon: '/icons/window.webp', label: /* i18n */ { id: 'editor.nodeDisplay.window', message: 'Window' } },
|
|
14
|
+
slab: { icon: '/icons/floor.webp', label: /* i18n */ { id: 'editor.nodeDisplay.slab', message: 'Slab' } },
|
|
15
|
+
ceiling: { icon: '/icons/ceiling.webp', label: /* i18n */ { id: 'editor.nodeDisplay.ceiling', message: 'Ceiling' } },
|
|
16
|
+
column: { icon: '/icons/column.webp', label: /* i18n */ { id: 'editor.nodeDisplay.column', message: 'Column' } },
|
|
17
|
+
elevator: { icon: '/icons/elevator.webp', label: /* i18n */ { id: 'editor.nodeDisplay.elevator', message: 'Elevator' } },
|
|
18
|
+
fence: { icon: '/icons/fence.webp', label: /* i18n */ { id: 'editor.nodeDisplay.fence', message: 'Fence' } },
|
|
19
|
+
roof: { icon: '/icons/roof.webp', label: /* i18n */ { id: 'editor.nodeDisplay.roof', message: 'Roof' } },
|
|
20
|
+
'roof-segment': { icon: '/icons/roof.webp', label: /* i18n */ { id: 'editor.nodeDisplay.roofSegment', message: 'Roof segment' } },
|
|
21
|
+
stair: { icon: '/icons/stairs.webp', label: /* i18n */ { id: 'editor.nodeDisplay.stair', message: 'Stair' } },
|
|
22
|
+
'stair-segment': { icon: '/icons/stairs.webp', label: /* i18n */ { id: 'editor.nodeDisplay.stairSegment', message: 'Stair segment' } },
|
|
23
|
+
scan: { icon: '/icons/mesh.webp', label: /* i18n */ { id: 'editor.nodeDisplay.scan', message: '3D Scan' } },
|
|
24
|
+
guide: { icon: '/icons/floorplan.webp', label: /* i18n */ { id: 'editor.nodeDisplay.guide', message: 'Guide image' } },
|
|
25
|
+
zone: { icon: '/icons/zone.webp', label: /* i18n */ { id: 'editor.nodeDisplay.zone', message: 'Zone' } },
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
export function getTypeDisplay(type: string): NodeDisplay {
|
|
28
|
-
|
|
28
|
+
export function getTypeDisplay(type: string, i18n: PascalI18n): NodeDisplay {
|
|
29
|
+
const known = TYPE_DEFAULTS[type]
|
|
30
|
+
return known ? { icon: known.icon, label: i18n._(known.label) } : { icon: '/icons/select.webp', label: type }
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
export function getNodeDisplay(node: AnyNode | null | undefined): NodeDisplay {
|
|
32
|
-
if (!node)
|
|
33
|
-
|
|
33
|
+
export function getNodeDisplay(node: AnyNode | null | undefined, i18n: PascalI18n): NodeDisplay {
|
|
34
|
+
if (!node)
|
|
35
|
+
return {
|
|
36
|
+
icon: '/icons/select.webp',
|
|
37
|
+
label: i18n._({ id: 'editor.nodeDisplay.selection', message: 'Selection' }),
|
|
38
|
+
}
|
|
39
|
+
const fallback = getTypeDisplay(node.type, i18n)
|
|
34
40
|
const scripted = scriptImages(node)?.thumbnail
|
|
35
41
|
// Item nodes carry an asset with its own thumbnail/name
|
|
36
42
|
if (node.type === 'item') {
|
|
@@ -40,7 +46,12 @@ export function getNodeDisplay(node: AnyNode | null | undefined): NodeDisplay {
|
|
|
40
46
|
}
|
|
41
47
|
}
|
|
42
48
|
if (node.type === 'zone' && node.spaceRole === 'room') {
|
|
43
|
-
return {
|
|
49
|
+
return {
|
|
50
|
+
icon: '/icons/kitchen.webp',
|
|
51
|
+
label:
|
|
52
|
+
(node.name && displayRoomName(node.name, i18n)) ||
|
|
53
|
+
i18n._({ id: 'editor.nodeDisplay.room', message: 'Room' }),
|
|
54
|
+
}
|
|
44
55
|
}
|
|
45
56
|
return {
|
|
46
57
|
icon: scripted ?? fallback.icon,
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
type DormerNode,
|
|
12
12
|
type ElevatorNode,
|
|
13
13
|
type FenceNode,
|
|
14
|
+
isMoveLocked,
|
|
15
|
+
isNodeDuplicable,
|
|
14
16
|
type ItemNode,
|
|
15
17
|
type RoofNode,
|
|
16
18
|
type RoofSegmentNode,
|
|
@@ -19,7 +21,7 @@ import {
|
|
|
19
21
|
type StairSegmentNode,
|
|
20
22
|
useScene,
|
|
21
23
|
type WallNode,
|
|
22
|
-
type WindowNode,
|
|
24
|
+
type WindowNode, usePascalI18n,
|
|
23
25
|
} from '@pascal-app/core'
|
|
24
26
|
import { useViewer } from '@pascal-app/viewer'
|
|
25
27
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
@@ -107,6 +109,7 @@ function MobilePanelLayer({
|
|
|
107
109
|
panel: React.ReactNode
|
|
108
110
|
isReference: boolean
|
|
109
111
|
}) {
|
|
112
|
+
const i18n = usePascalI18n()
|
|
110
113
|
const setSelection = useViewer((s) => s.setSelection)
|
|
111
114
|
const setSelectedReferenceId = useEditor((s) => s.setSelectedReferenceId)
|
|
112
115
|
const setMovingNode = useEditor((s) => s.setMovingNode)
|
|
@@ -146,7 +149,7 @@ function MobilePanelLayer({
|
|
|
146
149
|
|
|
147
150
|
if (!(node || isReference)) return null
|
|
148
151
|
|
|
149
|
-
const display = getNodeDisplay(node)
|
|
152
|
+
const display = getNodeDisplay(node, i18n)
|
|
150
153
|
|
|
151
154
|
return (
|
|
152
155
|
<>
|
|
@@ -154,9 +157,9 @@ function MobilePanelLayer({
|
|
|
154
157
|
<MobileSelectionBar
|
|
155
158
|
node={node}
|
|
156
159
|
onDelete={handleDelete}
|
|
157
|
-
onDuplicate={handleDuplicate}
|
|
160
|
+
onDuplicate={isNodeDuplicable(node) ? handleDuplicate : undefined}
|
|
158
161
|
onEdit={() => setIsSheetOpen((v) => !v)}
|
|
159
|
-
onMove={handleMove}
|
|
162
|
+
onMove={isMoveLocked(node) ? undefined : handleMove}
|
|
160
163
|
/>
|
|
161
164
|
)}
|
|
162
165
|
<MobilePanelSheet
|
|
@@ -180,8 +183,9 @@ function MobileMultiPanelLayer({
|
|
|
180
183
|
panel: React.ReactNode
|
|
181
184
|
type: string | null
|
|
182
185
|
}) {
|
|
186
|
+
const i18n = usePascalI18n()
|
|
183
187
|
const [isSheetOpen, setIsSheetOpen] = useState(false)
|
|
184
|
-
const display = type ? getTypeDisplay(type) : { icon: '/icons/select.webp', label: 'Selection' }
|
|
188
|
+
const display = type ? getTypeDisplay(type, i18n) : { icon: '/icons/select.webp', label: i18n._({ id: 'editor.panels.panelManager.selection', message: 'Selection' }) }
|
|
185
189
|
const title = breakdown || display.label
|
|
186
190
|
|
|
187
191
|
useEffect(() => {
|