@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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
type AnyNode,
|
|
3
5
|
type AnyNodeId,
|
|
@@ -9,6 +11,8 @@ import {
|
|
|
9
11
|
type SiteNode,
|
|
10
12
|
useScene,
|
|
11
13
|
type ZoneNode,
|
|
14
|
+
useLocale,
|
|
15
|
+
usePascalI18n,
|
|
12
16
|
} from '@pascal-app/core'
|
|
13
17
|
import { markPerfAction, useViewer } from '@pascal-app/viewer'
|
|
14
18
|
import {
|
|
@@ -53,24 +57,16 @@ import {
|
|
|
53
57
|
buildLevelDuplicateCreateOps,
|
|
54
58
|
type LevelDuplicatePreset,
|
|
55
59
|
} from '@pascal-app/core/building'
|
|
56
|
-
import { getDefaultLevelName, getLevelDisplayName } from '@pascal-app/core'
|
|
57
60
|
import { addStoreyOnTop } from './../../../../../lib/add-storey'
|
|
61
|
+
import { offerExteriorWallCopy } from './../../../../../components/ui/level-wall-copy-prompt'
|
|
58
62
|
import { deleteLevelWithFallbackSelection } from './../../../../../lib/level-selection'
|
|
59
|
-
import {
|
|
60
|
-
formatAreaLabel,
|
|
61
|
-
getAreaUnitLabel,
|
|
62
|
-
getLinearUnitLabel,
|
|
63
|
-
linearUnitToMeters,
|
|
64
|
-
metersToLinearUnit,
|
|
65
|
-
squareMetersToAreaUnit,
|
|
66
|
-
} from './../../../../../lib/measurements'
|
|
63
|
+
import { formatAreaLabel, getAreaUnitLabel, getLinearUnitLabel, linearUnitToMeters, metersToLinearUnit, squareMetersToAreaUnit } from '@pascal-app/core'
|
|
67
64
|
import { createLocalGuideImage, createLocalScan } from './../../../../../lib/local-guide-image'
|
|
68
65
|
import { editorHostTreeChildrenRegistry } from './../../../../../lib/host-tree-children'
|
|
69
66
|
import { requestRoomDeletion } from './../../../../../lib/room-structure-commands'
|
|
70
67
|
import {
|
|
71
68
|
selectZoneOrRoom,
|
|
72
69
|
useZoneSelected,
|
|
73
|
-
zoneKindLabel,
|
|
74
70
|
} from './../../../../../lib/room-zone-routing'
|
|
75
71
|
import { createUnitInBuilding, toggleZoneMembership } from './../../../../../lib/units'
|
|
76
72
|
import { cn } from './../../../../../lib/utils'
|
|
@@ -79,10 +75,13 @@ import { useUploadStore } from '../../../../../store/use-upload'
|
|
|
79
75
|
import { MetricControl } from '../../../controls/metric-control'
|
|
80
76
|
import { LevelDuplicateDialog } from '../../../level-duplicate-dialog'
|
|
81
77
|
import { CollectionsSection } from './collection-tree-node'
|
|
82
|
-
import { InlineRenameInput } from './inline-rename-input'
|
|
78
|
+
import { InlineRenameInput, RoomRenameInput } from './inline-rename-input'
|
|
83
79
|
import { ZoneMembershipCheckbox } from './zone-membership-checkbox'
|
|
84
80
|
import { focusTreeNode, TreeNode, TreeNodeWrapper } from './tree-node'
|
|
85
81
|
import { TreeNodeDragProvider } from './tree-node-drag'
|
|
82
|
+
import { zoneDefaultName } from './default-names'
|
|
83
|
+
import { nodeLabelDescriptor } from '../../../../../i18n/node-messages'
|
|
84
|
+
import { authoredLevelName, localizedLevelName } from '../../../../../i18n/level-name'
|
|
86
85
|
|
|
87
86
|
// ============================================================================
|
|
88
87
|
// PROPERTY LINE SECTION
|
|
@@ -126,6 +125,7 @@ function useSiteNode(): SiteNode | null {
|
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
const PropertyLineSection = memo(function PropertyLineSection() {
|
|
128
|
+
const i18n = usePascalI18n()
|
|
129
129
|
const siteNode = useSiteNode()
|
|
130
130
|
const updateNode = useScene((state) => state.updateNode)
|
|
131
131
|
const mode = useEditor((state) => state.mode)
|
|
@@ -194,7 +194,7 @@ const PropertyLineSection = memo(function PropertyLineSection() {
|
|
|
194
194
|
|
|
195
195
|
<div className="flex items-center gap-2">
|
|
196
196
|
<Pentagon className="h-4 w-4 text-muted-foreground" />
|
|
197
|
-
<span className="font-medium text-sm">Property Line</span>
|
|
197
|
+
<span className="font-medium text-sm">{i18n._({ id: 'editor.sidebar.panels.sitePanel.propertyLine', message: 'Property Line' })}</span>
|
|
198
198
|
</div>
|
|
199
199
|
<button
|
|
200
200
|
className={cn(
|
|
@@ -212,13 +212,13 @@ const PropertyLineSection = memo(function PropertyLineSection() {
|
|
|
212
212
|
{/* Measurements */}
|
|
213
213
|
<div className="relative flex gap-3 pr-3 pb-2 pl-10">
|
|
214
214
|
<div className="text-muted-foreground text-xs">
|
|
215
|
-
Area:{' '}
|
|
215
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.area', message: 'Area:' })}{' '}
|
|
216
216
|
<span className="text-foreground">
|
|
217
217
|
{displayArea.toFixed(1)} {getAreaUnitLabel(viewerUnit)}
|
|
218
218
|
</span>
|
|
219
219
|
</div>
|
|
220
220
|
<div className="text-muted-foreground text-xs">
|
|
221
|
-
Perimeter:{' '}
|
|
221
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.perimeter', message: 'Perimeter:' })}{' '}
|
|
222
222
|
<span className="text-foreground">
|
|
223
223
|
{displayPerimeter.toFixed(1)} {linearLabel}
|
|
224
224
|
</span>
|
|
@@ -272,7 +272,7 @@ const PropertyLineSection = memo(function PropertyLineSection() {
|
|
|
272
272
|
onClick={handleAddPoint}
|
|
273
273
|
>
|
|
274
274
|
<Plus className="h-3 w-3" />
|
|
275
|
-
Add point
|
|
275
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.addPoint', message: 'Add point' })}
|
|
276
276
|
</button>
|
|
277
277
|
</div>
|
|
278
278
|
)}
|
|
@@ -297,6 +297,7 @@ const CameraPopover = memo(function CameraPopover({
|
|
|
297
297
|
onOpenChange: (open: boolean) => void
|
|
298
298
|
buttonClassName?: string
|
|
299
299
|
}) {
|
|
300
|
+
const i18n = usePascalI18n()
|
|
300
301
|
const updateNode = useScene((state) => state.updateNode)
|
|
301
302
|
return (
|
|
302
303
|
<Popover onOpenChange={onOpenChange} open={open}>
|
|
@@ -307,7 +308,7 @@ const CameraPopover = memo(function CameraPopover({
|
|
|
307
308
|
buttonClassName,
|
|
308
309
|
)}
|
|
309
310
|
onClick={(e) => e.stopPropagation()}
|
|
310
|
-
title=
|
|
311
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.cameraSnapshot', message: 'Camera snapshot' })}
|
|
311
312
|
>
|
|
312
313
|
<Camera className="h-3.5 w-3.5" />
|
|
313
314
|
{hasCamera && (
|
|
@@ -332,7 +333,7 @@ const CameraPopover = memo(function CameraPopover({
|
|
|
332
333
|
}}
|
|
333
334
|
>
|
|
334
335
|
<Camera className="h-3.5 w-3.5" />
|
|
335
|
-
View snapshot
|
|
336
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.viewSnapshot', message: 'View snapshot' })}
|
|
336
337
|
</button>
|
|
337
338
|
)}
|
|
338
339
|
<button
|
|
@@ -344,7 +345,7 @@ const CameraPopover = memo(function CameraPopover({
|
|
|
344
345
|
}}
|
|
345
346
|
>
|
|
346
347
|
<Camera className="h-3.5 w-3.5" />
|
|
347
|
-
{hasCamera ? 'Update snapshot' : 'Take snapshot'}
|
|
348
|
+
{hasCamera ? i18n._({ id: 'editor.sidebar.panels.sitePanel.updateSnapshot', message: 'Update snapshot' }) : i18n._({ id: 'editor.sidebar.panels.sitePanel.takeSnapshot', message: 'Take snapshot' })}
|
|
348
349
|
</button>
|
|
349
350
|
{hasCamera && (
|
|
350
351
|
<button
|
|
@@ -356,7 +357,7 @@ const CameraPopover = memo(function CameraPopover({
|
|
|
356
357
|
}}
|
|
357
358
|
>
|
|
358
359
|
<Trash2 className="h-3.5 w-3.5" />
|
|
359
|
-
Clear snapshot
|
|
360
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.clearSnapshot', message: 'Clear snapshot' })}
|
|
360
361
|
</button>
|
|
361
362
|
)}
|
|
362
363
|
</div>
|
|
@@ -376,6 +377,7 @@ const ReferenceItem = memo(function ReferenceItem({
|
|
|
376
377
|
setSelectedReferenceId: (id: string) => void
|
|
377
378
|
handleDelete: (id: string, e: React.MouseEvent) => void
|
|
378
379
|
}) {
|
|
380
|
+
const i18n = usePascalI18n()
|
|
379
381
|
const [isEditing, setIsEditing] = useState(false)
|
|
380
382
|
const [isExpanded, setIsExpanded] = useState(true)
|
|
381
383
|
const updateNode = useScene((state) => state.updateNode)
|
|
@@ -450,13 +452,13 @@ const ReferenceItem = memo(function ReferenceItem({
|
|
|
450
452
|
>
|
|
451
453
|
{isCapture ? (
|
|
452
454
|
<img
|
|
453
|
-
alt=
|
|
455
|
+
alt={i18n._({ id: 'editor.sidebar.panels.sitePanel.capture', message: 'Capture' })}
|
|
454
456
|
className="h-3.5 w-3.5 shrink-0 object-contain opacity-70 transition-opacity group-hover/ref:opacity-100"
|
|
455
457
|
src="/icons/mesh.webp"
|
|
456
458
|
/>
|
|
457
459
|
) : (
|
|
458
460
|
<img
|
|
459
|
-
alt=
|
|
461
|
+
alt={i18n._({ id: 'editor.sidebar.panels.sitePanel.guide', message: 'Guide' })}
|
|
460
462
|
className="h-3.5 w-3.5 shrink-0 object-contain opacity-70 transition-opacity group-hover/ref:opacity-100"
|
|
461
463
|
src="/icons/floorplan.webp"
|
|
462
464
|
/>
|
|
@@ -477,7 +479,7 @@ const ReferenceItem = memo(function ReferenceItem({
|
|
|
477
479
|
event.stopPropagation()
|
|
478
480
|
updateNode(refNode.id, { visible: !isVisible })
|
|
479
481
|
}}
|
|
480
|
-
title={isVisible ? 'Hide' : 'Show'}
|
|
482
|
+
title={isVisible ? i18n._({ id: 'editor.sidebar.panels.sitePanel.hide', message: 'Hide' }) : i18n._({ id: 'editor.sidebar.panels.sitePanel.show', message: 'Show' })}
|
|
481
483
|
type="button"
|
|
482
484
|
>
|
|
483
485
|
{isVisible ? <Eye className="h-3 w-3" /> : <EyeOff className="h-3 w-3" />}
|
|
@@ -486,7 +488,7 @@ const ReferenceItem = memo(function ReferenceItem({
|
|
|
486
488
|
<button
|
|
487
489
|
className="z-20 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground opacity-0 transition-colors hover:bg-black/5 hover:text-foreground group-hover/ref:opacity-100 dark:hover:bg-white/10"
|
|
488
490
|
onClick={(e) => handleDelete(refNode.id, e)}
|
|
489
|
-
title=
|
|
491
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.delete', message: 'Delete' })}
|
|
490
492
|
type="button"
|
|
491
493
|
>
|
|
492
494
|
<Trash2 className="h-3 w-3" />
|
|
@@ -516,6 +518,7 @@ const LevelReferences = memo(function LevelReferences({
|
|
|
516
518
|
onUploadAsset,
|
|
517
519
|
onDeleteAsset,
|
|
518
520
|
}: LevelReferencesProps) {
|
|
521
|
+
const i18n = usePascalI18n()
|
|
519
522
|
const createNode = useScene((s) => s.createNode)
|
|
520
523
|
const deleteNode = useScene((s) => s.deleteNode)
|
|
521
524
|
const setSelection = useViewer((s) => s.setSelection)
|
|
@@ -676,7 +679,12 @@ const LevelReferences = memo(function LevelReferences({
|
|
|
676
679
|
) : (
|
|
677
680
|
<Plus className="h-3.5 w-3.5" />
|
|
678
681
|
)}
|
|
679
|
-
{uploading ?
|
|
682
|
+
{uploading ? i18n._({
|
|
683
|
+
id: 'editor.sidebar.panels.sitePanel.uploadingProgress',
|
|
684
|
+
message:
|
|
685
|
+
'{assetType, select, guide {Uploading guide... {progress}%} other {Uploading scan... {progress}%}}',
|
|
686
|
+
values: { assetType: uploadingType, progress },
|
|
687
|
+
}) : i18n._({ id: 'editor.sidebar.panels.sitePanel.uploadScanFloorplan', message: 'Upload scan/floorplan' })}
|
|
680
688
|
</button>
|
|
681
689
|
|
|
682
690
|
<input
|
|
@@ -736,6 +744,8 @@ const LevelItem = memo(function LevelItem({
|
|
|
736
744
|
onUploadAsset?: (projectId: string, levelId: string, file: File, type: 'scan' | 'guide') => void
|
|
737
745
|
onDeleteAsset?: (projectId: string, url: string) => void
|
|
738
746
|
}) {
|
|
747
|
+
const i18n = usePascalI18n()
|
|
748
|
+
const locale = useLocale()
|
|
739
749
|
const [cameraPopoverOpen, setCameraPopoverOpen] = useState(false)
|
|
740
750
|
const [duplicateDialogOpen, setDuplicateDialogOpen] = useState(false)
|
|
741
751
|
const [isEditing, setIsEditing] = useState(false)
|
|
@@ -820,7 +830,14 @@ const LevelItem = memo(function LevelItem({
|
|
|
820
830
|
|
|
821
831
|
<div className="relative z-20 flex h-8 items-center pr-1 pl-[28px]">
|
|
822
832
|
<button
|
|
833
|
+
aria-expanded={isExpanded}
|
|
834
|
+
aria-label={
|
|
835
|
+
isExpanded
|
|
836
|
+
? i18n._({ id: 'editor.layers.collapse', message: 'Collapse' })
|
|
837
|
+
: i18n._({ id: 'editor.layers.expand', message: 'Expand' })
|
|
838
|
+
}
|
|
823
839
|
className="z-20 flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center bg-inherit"
|
|
840
|
+
type="button"
|
|
824
841
|
onClick={(e) => {
|
|
825
842
|
e.stopPropagation()
|
|
826
843
|
if (isSelected) {
|
|
@@ -840,7 +857,7 @@ const LevelItem = memo(function LevelItem({
|
|
|
840
857
|
|
|
841
858
|
<div className="flex h-8 min-w-0 flex-1 cursor-pointer items-center gap-2 py-0 pl-0.5 text-sm">
|
|
842
859
|
<img
|
|
843
|
-
alt=
|
|
860
|
+
alt={i18n._({ id: 'editor.sidebar.panels.sitePanel.level', message: 'Level' })}
|
|
844
861
|
className={cn(
|
|
845
862
|
'h-4 w-4 shrink-0 object-contain transition-all duration-200',
|
|
846
863
|
!isSelected && 'opacity-60 grayscale',
|
|
@@ -848,8 +865,9 @@ const LevelItem = memo(function LevelItem({
|
|
|
848
865
|
src="/icons/level.webp"
|
|
849
866
|
/>
|
|
850
867
|
<InlineRenameInput
|
|
851
|
-
defaultName={
|
|
852
|
-
displayName={
|
|
868
|
+
defaultName={localizedLevelName({ level: level.level }, i18n, locale)}
|
|
869
|
+
displayName={localizedLevelName(level, i18n, locale)}
|
|
870
|
+
editableName={(name) => authoredLevelName({ name, level: level.level })}
|
|
853
871
|
isEditing={isEditing}
|
|
854
872
|
nodeId={level.id}
|
|
855
873
|
onStartEditing={() => setIsEditing(true)}
|
|
@@ -867,7 +885,7 @@ const LevelItem = memo(function LevelItem({
|
|
|
867
885
|
: 'text-muted-foreground hover:bg-accent hover:text-foreground',
|
|
868
886
|
)}
|
|
869
887
|
onClick={(e) => e.stopPropagation()}
|
|
870
|
-
title=
|
|
888
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.cameraSnapshot', message: 'Camera snapshot' })}
|
|
871
889
|
>
|
|
872
890
|
<Camera className="h-3.5 w-3.5" />
|
|
873
891
|
{level.camera && (
|
|
@@ -892,7 +910,7 @@ const LevelItem = memo(function LevelItem({
|
|
|
892
910
|
}}
|
|
893
911
|
>
|
|
894
912
|
<Camera className="h-3.5 w-3.5" />
|
|
895
|
-
View snapshot
|
|
913
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.viewSnapshot', message: 'View snapshot' })}
|
|
896
914
|
</button>
|
|
897
915
|
)}
|
|
898
916
|
<button
|
|
@@ -904,7 +922,7 @@ const LevelItem = memo(function LevelItem({
|
|
|
904
922
|
}}
|
|
905
923
|
>
|
|
906
924
|
<Camera className="h-3.5 w-3.5" />
|
|
907
|
-
{level.camera ? 'Update snapshot' : 'Take snapshot'}
|
|
925
|
+
{level.camera ? i18n._({ id: 'editor.sidebar.panels.sitePanel.updateSnapshot', message: 'Update snapshot' }) : i18n._({ id: 'editor.sidebar.panels.sitePanel.takeSnapshot', message: 'Take snapshot' })}
|
|
908
926
|
</button>
|
|
909
927
|
{level.camera && (
|
|
910
928
|
<button
|
|
@@ -916,7 +934,7 @@ const LevelItem = memo(function LevelItem({
|
|
|
916
934
|
}}
|
|
917
935
|
>
|
|
918
936
|
<Trash2 className="h-3.5 w-3.5" />
|
|
919
|
-
Clear snapshot
|
|
937
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.clearSnapshot', message: 'Clear snapshot' })}
|
|
920
938
|
</button>
|
|
921
939
|
)}
|
|
922
940
|
</div>
|
|
@@ -925,6 +943,11 @@ const LevelItem = memo(function LevelItem({
|
|
|
925
943
|
<Popover>
|
|
926
944
|
<PopoverTrigger asChild>
|
|
927
945
|
<button
|
|
946
|
+
aria-label={i18n._({
|
|
947
|
+
id: 'editor.layers.levelOptions',
|
|
948
|
+
message: '{levelName} options',
|
|
949
|
+
values: { levelName: localizedLevelName(level, i18n, locale) },
|
|
950
|
+
})}
|
|
928
951
|
className={cn(
|
|
929
952
|
'mr-1 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-md opacity-0 transition-colors group-hover/level:opacity-100',
|
|
930
953
|
selectedLevelId === level.id
|
|
@@ -936,31 +959,31 @@ const LevelItem = memo(function LevelItem({
|
|
|
936
959
|
<MoreHorizontal className="h-3.5 w-3.5" />
|
|
937
960
|
</button>
|
|
938
961
|
</PopoverTrigger>
|
|
939
|
-
<PopoverContent align="start" className="w-48 p-1" side="right">
|
|
962
|
+
<PopoverContent align="start" className="w-max min-w-48 max-w-72 p-1" side="right">
|
|
940
963
|
<button
|
|
941
964
|
className="flex w-full cursor-pointer items-center gap-2 rounded px-3 py-1.5 text-left text-sm transition-colors hover:bg-accent"
|
|
942
965
|
onClick={() => handleDuplicateLevel()}
|
|
943
|
-
title=
|
|
966
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.duplicateLevel', message: 'Duplicate level' })}
|
|
944
967
|
>
|
|
945
968
|
<Copy className="h-3.5 w-3.5" />
|
|
946
|
-
Duplicate
|
|
969
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.duplicate', message: 'Duplicate' })}
|
|
947
970
|
</button>
|
|
948
971
|
<button
|
|
949
972
|
className="flex w-full cursor-pointer items-center gap-2 rounded px-3 py-1.5 text-left text-sm transition-colors hover:bg-accent"
|
|
950
973
|
onClick={() => setDuplicateDialogOpen(true)}
|
|
951
|
-
title=
|
|
974
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.duplicateLevelWithOptions', message: 'Duplicate level with options' })}
|
|
952
975
|
>
|
|
953
976
|
<Copy className="h-3.5 w-3.5" />
|
|
954
|
-
Duplicate with options...
|
|
977
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.duplicateWithOptions', message: 'Duplicate with options...' })}
|
|
955
978
|
</button>
|
|
956
979
|
<button
|
|
957
980
|
className="flex w-full items-center gap-2 rounded px-3 py-1.5 text-left text-sm transition-colors enabled:cursor-pointer enabled:hover:bg-accent enabled:hover:text-red-600 disabled:cursor-not-allowed disabled:opacity-50"
|
|
958
981
|
disabled={!canDeleteLevel}
|
|
959
982
|
onClick={() => deleteLevelWithFallbackSelection(level.id)}
|
|
960
|
-
title={canDeleteLevel ? 'Delete level' : 'The ground level cannot be deleted'}
|
|
983
|
+
title={canDeleteLevel ? i18n._({ id: 'editor.sidebar.panels.sitePanel.deleteLevel', message: 'Delete level' }) : i18n._({ id: 'editor.sidebar.panels.sitePanel.theGroundLevelCannotBeDeleted', message: 'The ground level cannot be deleted' })}
|
|
961
984
|
>
|
|
962
985
|
<Trash2 className="h-3.5 w-3.5" />
|
|
963
|
-
Delete
|
|
986
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.delete', message: 'Delete' })}
|
|
964
987
|
</button>
|
|
965
988
|
</PopoverContent>
|
|
966
989
|
</Popover>
|
|
@@ -977,7 +1000,7 @@ const LevelItem = memo(function LevelItem({
|
|
|
977
1000
|
<div className="relative border-border/50 border-b py-2 pr-3 pl-[60px]">
|
|
978
1001
|
<div className="pointer-events-none absolute top-0 bottom-0 left-[45px] z-10 w-px bg-border/50" />
|
|
979
1002
|
<MetricControl
|
|
980
|
-
label=
|
|
1003
|
+
label={i18n._({ id: 'editor.sidebar.panels.sitePanel.baseElevation', message: 'Base elevation' })}
|
|
981
1004
|
onChange={(value) => updateNode(level.id, { baseElevation: value })}
|
|
982
1005
|
precision={2}
|
|
983
1006
|
step={0.05}
|
|
@@ -1014,6 +1037,7 @@ const LevelsSection = memo(function LevelsSection({
|
|
|
1014
1037
|
onUploadAsset?: (projectId: string, levelId: string, file: File, type: 'scan' | 'guide') => void
|
|
1015
1038
|
onDeleteAsset?: (projectId: string, url: string) => void
|
|
1016
1039
|
} = {}) {
|
|
1040
|
+
const i18n = usePascalI18n()
|
|
1017
1041
|
const updateNode = useScene((state) => state.updateNode)
|
|
1018
1042
|
const selectedBuildingId = useViewer((state) => state.selection.buildingId)
|
|
1019
1043
|
const selectedLevelId = useViewer((state) => state.selection.levelId)
|
|
@@ -1038,6 +1062,7 @@ const LevelsSection = memo(function LevelsSection({
|
|
|
1038
1062
|
const handleAddLevel = () => {
|
|
1039
1063
|
const newLevel = addStoreyOnTop(building.id)
|
|
1040
1064
|
setSelection({ buildingId: building.id, levelId: newLevel.id })
|
|
1065
|
+
offerExteriorWallCopy(newLevel.id)
|
|
1041
1066
|
}
|
|
1042
1067
|
|
|
1043
1068
|
return (
|
|
@@ -1056,7 +1081,7 @@ const LevelsSection = memo(function LevelsSection({
|
|
|
1056
1081
|
<div className="relative z-10 flex items-center pr-1 pl-[38px]">
|
|
1057
1082
|
<Plus className="h-3.5 w-3.5" />
|
|
1058
1083
|
</div>
|
|
1059
|
-
<span className="truncate">Add level</span>
|
|
1084
|
+
<span className="truncate">{i18n._({ id: 'editor.sidebar.panels.sitePanel.addLevel', message: 'Add level' })}</span>
|
|
1060
1085
|
</button>
|
|
1061
1086
|
{levels.length === 0 && (
|
|
1062
1087
|
<div className="relative flex h-8 select-none items-center border-border/50 border-b py-0 pr-2 pl-[38px] text-muted-foreground text-xs">
|
|
@@ -1064,7 +1089,7 @@ const LevelsSection = memo(function LevelsSection({
|
|
|
1064
1089
|
<div className="pointer-events-none absolute top-0 bottom-1/2 left-[21px] w-px bg-border/50" />
|
|
1065
1090
|
{/* Horizontal branch line */}
|
|
1066
1091
|
<div className="pointer-events-none absolute top-1/2 left-[21px] h-px w-[11px] bg-border/50" />
|
|
1067
|
-
No levels yet
|
|
1092
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.noLevelsYet', message: 'No levels yet' })}
|
|
1068
1093
|
</div>
|
|
1069
1094
|
)}
|
|
1070
1095
|
{[...levels].reverse().map((level, index) => (
|
|
@@ -1091,6 +1116,7 @@ const UnitsSection = memo(function UnitsSection({
|
|
|
1091
1116
|
}: {
|
|
1092
1117
|
buildingId: BuildingNode['id']
|
|
1093
1118
|
}) {
|
|
1119
|
+
const i18n = usePascalI18n()
|
|
1094
1120
|
const unitIds = useScene(
|
|
1095
1121
|
useShallow((s) => {
|
|
1096
1122
|
const building = s.nodes[buildingId] as BuildingNode | undefined
|
|
@@ -1117,7 +1143,7 @@ const UnitsSection = memo(function UnitsSection({
|
|
|
1117
1143
|
<button
|
|
1118
1144
|
className="flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-black/5 hover:text-foreground dark:hover:bg-white/10"
|
|
1119
1145
|
onClick={handleNewUnit}
|
|
1120
|
-
title=
|
|
1146
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.newUnit', message: 'New unit' })}
|
|
1121
1147
|
type="button"
|
|
1122
1148
|
>
|
|
1123
1149
|
<Plus className="h-3 w-3" />
|
|
@@ -1129,7 +1155,7 @@ const UnitsSection = memo(function UnitsSection({
|
|
|
1129
1155
|
icon={<Group className="h-3.5 w-3.5" />}
|
|
1130
1156
|
label={
|
|
1131
1157
|
<span className="flex items-center gap-1.5">
|
|
1132
|
-
Units
|
|
1158
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.units', message: 'Units' })}
|
|
1133
1159
|
{hasUnits && <span className="text-muted-foreground text-xs">{unitIds.length}</span>}
|
|
1134
1160
|
</span>
|
|
1135
1161
|
}
|
|
@@ -1145,7 +1171,7 @@ const UnitsSection = memo(function UnitsSection({
|
|
|
1145
1171
|
hasChildren={false}
|
|
1146
1172
|
icon={<Plus className="h-3.5 w-3.5" />}
|
|
1147
1173
|
isLast
|
|
1148
|
-
label=
|
|
1174
|
+
label={i18n._({ id: 'editor.sidebar.panels.sitePanel.newUnit', message: 'New unit' })}
|
|
1149
1175
|
onClick={handleNewUnit}
|
|
1150
1176
|
onToggle={() => {}}
|
|
1151
1177
|
/>
|
|
@@ -1163,6 +1189,7 @@ const ZoneItem = memo(function ZoneItem({
|
|
|
1163
1189
|
depth: number
|
|
1164
1190
|
isLast?: boolean
|
|
1165
1191
|
}) {
|
|
1192
|
+
const i18n = usePascalI18n()
|
|
1166
1193
|
const [isEditing, setIsEditing] = useState(false)
|
|
1167
1194
|
const [cameraPopoverOpen, setCameraPopoverOpen] = useState(false)
|
|
1168
1195
|
const deleteNode = useScene((state) => state.deleteNode)
|
|
@@ -1190,7 +1217,7 @@ const ZoneItem = memo(function ZoneItem({
|
|
|
1190
1217
|
}
|
|
1191
1218
|
}, [isSelected])
|
|
1192
1219
|
|
|
1193
|
-
const defaultName =
|
|
1220
|
+
const defaultName = zoneDefaultName(i18n, zone, formatAreaLabel(calculatePolygonArea(zone.polygon), unit))
|
|
1194
1221
|
|
|
1195
1222
|
const handleClick = () => {
|
|
1196
1223
|
setMode('select')
|
|
@@ -1250,14 +1277,22 @@ const ZoneItem = memo(function ZoneItem({
|
|
|
1250
1277
|
<ZoneMembershipCheckbox
|
|
1251
1278
|
checked={focusedUnit.members.includes(zone.id)}
|
|
1252
1279
|
onToggle={() => toggleZoneMembership(focusedUnit.id, zone.id)}
|
|
1253
|
-
unitName={focusedUnit.name || 'Unit'}
|
|
1280
|
+
unitName={focusedUnit.name || i18n._(nodeLabelDescriptor('unit', 'Unit'))}
|
|
1254
1281
|
/>
|
|
1255
1282
|
)}
|
|
1256
1283
|
<span className={cn('mr-2', !isSelected && 'opacity-40')}>
|
|
1257
|
-
<ColorDot
|
|
1284
|
+
<ColorDot
|
|
1285
|
+
color={zone.color}
|
|
1286
|
+
label={i18n._({
|
|
1287
|
+
id: 'editor.layers.spaceColour',
|
|
1288
|
+
message: '{role, select, room {Room colour} other {Zone colour}}',
|
|
1289
|
+
values: { role: zone.spaceRole ?? 'generic' },
|
|
1290
|
+
})}
|
|
1291
|
+
onChange={handleColorChange}
|
|
1292
|
+
/>
|
|
1258
1293
|
</span>
|
|
1259
1294
|
<div className="min-w-0 flex-1 pr-1">
|
|
1260
|
-
<
|
|
1295
|
+
<RoomRenameInput
|
|
1261
1296
|
defaultName={defaultName}
|
|
1262
1297
|
isEditing={isEditing}
|
|
1263
1298
|
nodeId={zone.id}
|
|
@@ -1272,7 +1307,7 @@ const ZoneItem = memo(function ZoneItem({
|
|
|
1272
1307
|
<button
|
|
1273
1308
|
className="relative flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground opacity-0 transition-colors hover:bg-black/5 hover:text-foreground group-hover/row:opacity-100 dark:hover:bg-white/10"
|
|
1274
1309
|
onClick={(e) => e.stopPropagation()}
|
|
1275
|
-
title=
|
|
1310
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.cameraSnapshot', message: 'Camera snapshot' })}
|
|
1276
1311
|
>
|
|
1277
1312
|
<Camera className="h-3 w-3" />
|
|
1278
1313
|
{zone.camera && (
|
|
@@ -1297,7 +1332,7 @@ const ZoneItem = memo(function ZoneItem({
|
|
|
1297
1332
|
}}
|
|
1298
1333
|
>
|
|
1299
1334
|
<Camera className="h-3.5 w-3.5" />
|
|
1300
|
-
View snapshot
|
|
1335
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.viewSnapshot', message: 'View snapshot' })}
|
|
1301
1336
|
</button>
|
|
1302
1337
|
)}
|
|
1303
1338
|
<button
|
|
@@ -1309,7 +1344,7 @@ const ZoneItem = memo(function ZoneItem({
|
|
|
1309
1344
|
}}
|
|
1310
1345
|
>
|
|
1311
1346
|
<Camera className="h-3.5 w-3.5" />
|
|
1312
|
-
{zone.camera ? 'Update snapshot' : 'Take snapshot'}
|
|
1347
|
+
{zone.camera ? i18n._({ id: 'editor.sidebar.panels.sitePanel.updateSnapshot', message: 'Update snapshot' }) : i18n._({ id: 'editor.sidebar.panels.sitePanel.takeSnapshot', message: 'Take snapshot' })}
|
|
1313
1348
|
</button>
|
|
1314
1349
|
{zone.camera && (
|
|
1315
1350
|
<button
|
|
@@ -1321,15 +1356,17 @@ const ZoneItem = memo(function ZoneItem({
|
|
|
1321
1356
|
}}
|
|
1322
1357
|
>
|
|
1323
1358
|
<Trash2 className="h-3.5 w-3.5" />
|
|
1324
|
-
Clear snapshot
|
|
1359
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.clearSnapshot', message: 'Clear snapshot' })}
|
|
1325
1360
|
</button>
|
|
1326
1361
|
)}
|
|
1327
1362
|
</div>
|
|
1328
1363
|
</PopoverContent>
|
|
1329
1364
|
</Popover>
|
|
1330
1365
|
<button
|
|
1331
|
-
|
|
1366
|
+
aria-label={i18n._({ id: 'editor.sidebar.panels.sitePanel.delete', message: 'Delete' })}
|
|
1367
|
+
className="flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground opacity-0 transition-colors hover:bg-black/5 hover:text-foreground group-hover/row:opacity-100 focus-visible:opacity-100 dark:hover:bg-white/10"
|
|
1332
1368
|
onClick={handleDelete}
|
|
1369
|
+
type="button"
|
|
1333
1370
|
>
|
|
1334
1371
|
<Trash2 className="h-3 w-3" />
|
|
1335
1372
|
</button>
|
|
@@ -1339,6 +1376,7 @@ const ZoneItem = memo(function ZoneItem({
|
|
|
1339
1376
|
})
|
|
1340
1377
|
|
|
1341
1378
|
const MultiSelectionBadge = memo(function MultiSelectionBadge() {
|
|
1379
|
+
const i18n = usePascalI18n()
|
|
1342
1380
|
const selectedIds = useViewer((state) => state.selection.selectedIds)
|
|
1343
1381
|
const setSelection = useViewer((state) => state.setSelection)
|
|
1344
1382
|
|
|
@@ -1347,11 +1385,15 @@ const MultiSelectionBadge = memo(function MultiSelectionBadge() {
|
|
|
1347
1385
|
return (
|
|
1348
1386
|
<div className="pointer-events-none sticky top-4 z-50 flex h-0 w-full justify-center overflow-visible">
|
|
1349
1387
|
<div className="pointer-events-auto flex items-center gap-2.5 rounded-full border border-primary/20 bg-primary px-0.5 py-4 pl-2 font-medium text-primary-foreground text-xs shadow-black/10 shadow-lg backdrop-blur-md">
|
|
1350
|
-
<span>{
|
|
1388
|
+
<span>{i18n._({
|
|
1389
|
+
id: 'editor.sidebar.panels.sitePanel.objectsSelected',
|
|
1390
|
+
message: '{count, plural, one {# object selected} other {# objects selected}}',
|
|
1391
|
+
values: { count: selectedIds.length },
|
|
1392
|
+
})}</span>
|
|
1351
1393
|
<button
|
|
1352
1394
|
className="cursor-pointer rounded-full p-1.5 transition-colors hover:bg-primary-foreground/20"
|
|
1353
1395
|
onClick={() => setSelection({ selectedIds: [] })}
|
|
1354
|
-
title=
|
|
1396
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.clearSelection', message: 'Clear selection' })}
|
|
1355
1397
|
>
|
|
1356
1398
|
<X className="h-4 w-4" />
|
|
1357
1399
|
</button>
|
|
@@ -1361,6 +1403,7 @@ const MultiSelectionBadge = memo(function MultiSelectionBadge() {
|
|
|
1361
1403
|
})
|
|
1362
1404
|
|
|
1363
1405
|
const RoomsSection = memo(function RoomsSection() {
|
|
1406
|
+
const i18n = usePascalI18n()
|
|
1364
1407
|
const selectedLevelId = useViewer((state) => state.selection.levelId)
|
|
1365
1408
|
const armToolMode = useEditor((state) => state.armToolMode)
|
|
1366
1409
|
const levelZones = useScene(
|
|
@@ -1390,7 +1433,7 @@ const RoomsSection = memo(function RoomsSection() {
|
|
|
1390
1433
|
<button
|
|
1391
1434
|
className="flex h-6 w-6 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-black/5 hover:text-foreground dark:hover:bg-white/10"
|
|
1392
1435
|
onClick={handleDrawZone}
|
|
1393
|
-
title=
|
|
1436
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.drawAZone', message: 'Draw a zone' })}
|
|
1394
1437
|
type="button"
|
|
1395
1438
|
>
|
|
1396
1439
|
<Plus className="h-3 w-3" />
|
|
@@ -1402,7 +1445,7 @@ const RoomsSection = memo(function RoomsSection() {
|
|
|
1402
1445
|
icon={<LayoutGrid className="h-3.5 w-3.5" />}
|
|
1403
1446
|
label={
|
|
1404
1447
|
<span className="flex items-center gap-1.5">
|
|
1405
|
-
Rooms
|
|
1448
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.rooms', message: 'Rooms' })}
|
|
1406
1449
|
{rooms.length > 0 && (
|
|
1407
1450
|
<span className="text-muted-foreground text-xs">{rooms.length}</span>
|
|
1408
1451
|
)}
|
|
@@ -1413,7 +1456,7 @@ const RoomsSection = memo(function RoomsSection() {
|
|
|
1413
1456
|
>
|
|
1414
1457
|
{levelZones.length === 0 && (
|
|
1415
1458
|
<div className="border-border/50 border-b py-2 pr-3 pl-[52px] text-muted-foreground text-xs">
|
|
1416
|
-
Close walls around a space to make a room.
|
|
1459
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.closeWallsAroundASpaceTo', message: 'Close walls around a space to make a room.' })}
|
|
1417
1460
|
</div>
|
|
1418
1461
|
)}
|
|
1419
1462
|
{rooms.map((zone, index) => (
|
|
@@ -1430,7 +1473,7 @@ const RoomsSection = memo(function RoomsSection() {
|
|
|
1430
1473
|
className="pt-3 pb-1 pl-[52px] font-medium text-[11px] text-muted-foreground"
|
|
1431
1474
|
data-other-zones
|
|
1432
1475
|
>
|
|
1433
|
-
Other zones
|
|
1476
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.otherZones', message: 'Other zones' })}
|
|
1434
1477
|
</div>
|
|
1435
1478
|
{others.map((zone, index) => (
|
|
1436
1479
|
<ZoneItem depth={2} isLast={index === others.length - 1} key={zone.id} zone={zone} />
|
|
@@ -1443,6 +1486,7 @@ const RoomsSection = memo(function RoomsSection() {
|
|
|
1443
1486
|
})
|
|
1444
1487
|
|
|
1445
1488
|
const ContentSection = memo(function ContentSection() {
|
|
1489
|
+
const i18n = usePascalI18n()
|
|
1446
1490
|
const selectedLevelId = useViewer((state) => state.selection.levelId)
|
|
1447
1491
|
const level = useScene((s) =>
|
|
1448
1492
|
selectedLevelId ? ((s.nodes[selectedLevelId] as LevelNode | undefined) ?? null) : null,
|
|
@@ -1462,7 +1506,7 @@ const ContentSection = memo(function ContentSection() {
|
|
|
1462
1506
|
|
|
1463
1507
|
if (!level) {
|
|
1464
1508
|
return (
|
|
1465
|
-
<div className="px-3 py-4 text-muted-foreground text-sm">Select a level to view content</div>
|
|
1509
|
+
<div className="px-3 py-4 text-muted-foreground text-sm">{i18n._({ id: 'editor.sidebar.panels.sitePanel.selectALevelToViewContent', message: 'Select a level to view content' })}</div>
|
|
1466
1510
|
)
|
|
1467
1511
|
}
|
|
1468
1512
|
|
|
@@ -1474,7 +1518,7 @@ const ContentSection = memo(function ContentSection() {
|
|
|
1474
1518
|
icon={<Boxes className="h-3.5 w-3.5" />}
|
|
1475
1519
|
label={
|
|
1476
1520
|
<span className="flex items-center gap-1.5">
|
|
1477
|
-
Elements
|
|
1521
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.elements', message: 'Elements' })}
|
|
1478
1522
|
{elementChildren.length > 0 && (
|
|
1479
1523
|
<span className="text-muted-foreground text-xs">{elementChildren.length}</span>
|
|
1480
1524
|
)}
|
|
@@ -1485,7 +1529,7 @@ const ContentSection = memo(function ContentSection() {
|
|
|
1485
1529
|
>
|
|
1486
1530
|
{elementChildren.length === 0 ? (
|
|
1487
1531
|
<div className="border-border/50 border-b py-2 pr-3 pl-[52px] text-muted-foreground text-xs">
|
|
1488
|
-
No elements on this level
|
|
1532
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.noElementsOnThisLevel', message: 'No elements on this level' })}
|
|
1489
1533
|
</div>
|
|
1490
1534
|
) : (
|
|
1491
1535
|
<TreeNodeDragProvider>
|
|
@@ -1520,6 +1564,7 @@ const BuildingItem = memo(function BuildingItem({
|
|
|
1520
1564
|
onUploadAsset?: (projectId: string, levelId: string, file: File, type: 'scan' | 'guide') => void
|
|
1521
1565
|
onDeleteAsset?: (projectId: string, url: string) => void
|
|
1522
1566
|
}) {
|
|
1567
|
+
const i18n = usePascalI18n()
|
|
1523
1568
|
const setSelection = useViewer((state) => state.setSelection)
|
|
1524
1569
|
const phase = useEditor((state) => state.phase)
|
|
1525
1570
|
const setPhase = useEditor((state) => state.setPhase)
|
|
@@ -1560,14 +1605,14 @@ const BuildingItem = memo(function BuildingItem({
|
|
|
1560
1605
|
>
|
|
1561
1606
|
<div className="flex h-full min-w-0 flex-1 cursor-pointer items-center gap-2 py-2 pl-3">
|
|
1562
1607
|
<img
|
|
1563
|
-
alt=
|
|
1608
|
+
alt={i18n._({ id: 'editor.sidebar.panels.sitePanel.building', message: 'Building' })}
|
|
1564
1609
|
className={cn(
|
|
1565
1610
|
'h-5 w-5 object-contain transition-all',
|
|
1566
1611
|
!isBuildingActive && 'opacity-60 grayscale',
|
|
1567
1612
|
)}
|
|
1568
1613
|
src="/icons/building.webp"
|
|
1569
1614
|
/>
|
|
1570
|
-
<span className="truncate font-medium text-sm">{building.name || 'Building'}</span>
|
|
1615
|
+
<span className="truncate font-medium text-sm">{building.name || i18n._({ id: 'editor.sidebar.panels.sitePanel.building', message: 'Building' })}</span>
|
|
1571
1616
|
</div>
|
|
1572
1617
|
<Popover
|
|
1573
1618
|
onOpenChange={(open) => setBuildingCameraOpen(open ? building.id : null)}
|
|
@@ -1582,7 +1627,7 @@ const BuildingItem = memo(function BuildingItem({
|
|
|
1582
1627
|
: 'text-muted-foreground hover:bg-accent hover:text-foreground',
|
|
1583
1628
|
)}
|
|
1584
1629
|
onClick={(e) => e.stopPropagation()}
|
|
1585
|
-
title=
|
|
1630
|
+
title={i18n._({ id: 'editor.sidebar.panels.sitePanel.cameraSnapshot', message: 'Camera snapshot' })}
|
|
1586
1631
|
>
|
|
1587
1632
|
<Camera className="h-4 w-4" />
|
|
1588
1633
|
{building.camera && (
|
|
@@ -1607,7 +1652,7 @@ const BuildingItem = memo(function BuildingItem({
|
|
|
1607
1652
|
}}
|
|
1608
1653
|
>
|
|
1609
1654
|
<Camera className="h-3.5 w-3.5" />
|
|
1610
|
-
View snapshot
|
|
1655
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.viewSnapshot', message: 'View snapshot' })}
|
|
1611
1656
|
</button>
|
|
1612
1657
|
)}
|
|
1613
1658
|
<button
|
|
@@ -1619,7 +1664,7 @@ const BuildingItem = memo(function BuildingItem({
|
|
|
1619
1664
|
}}
|
|
1620
1665
|
>
|
|
1621
1666
|
<Camera className="h-3.5 w-3.5" />
|
|
1622
|
-
{building.camera ? 'Update snapshot' : 'Take snapshot'}
|
|
1667
|
+
{building.camera ? i18n._({ id: 'editor.sidebar.panels.sitePanel.updateSnapshot', message: 'Update snapshot' }) : i18n._({ id: 'editor.sidebar.panels.sitePanel.takeSnapshot', message: 'Take snapshot' })}
|
|
1623
1668
|
</button>
|
|
1624
1669
|
{building.camera && (
|
|
1625
1670
|
<button
|
|
@@ -1631,7 +1676,7 @@ const BuildingItem = memo(function BuildingItem({
|
|
|
1631
1676
|
}}
|
|
1632
1677
|
>
|
|
1633
1678
|
<Trash2 className="h-3.5 w-3.5" />
|
|
1634
|
-
Clear snapshot
|
|
1679
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.clearSnapshot', message: 'Clear snapshot' })}
|
|
1635
1680
|
</button>
|
|
1636
1681
|
)}
|
|
1637
1682
|
</div>
|
|
@@ -1679,6 +1724,7 @@ export interface SitePanelProps {
|
|
|
1679
1724
|
}
|
|
1680
1725
|
|
|
1681
1726
|
export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanelProps = {}) {
|
|
1727
|
+
const i18n = usePascalI18n()
|
|
1682
1728
|
const rootNodeIds = useScene((state) => state.rootNodeIds)
|
|
1683
1729
|
const updateNode = useScene((state) => state.updateNode)
|
|
1684
1730
|
const selectedBuildingId = useViewer((state) => state.selection.buildingId)
|
|
@@ -1703,7 +1749,7 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel
|
|
|
1703
1749
|
|
|
1704
1750
|
return (
|
|
1705
1751
|
<LayoutGroup>
|
|
1706
|
-
<div className="flex h-full flex-col">
|
|
1752
|
+
<div className="flex h-full flex-col" data-surface="site-panel">
|
|
1707
1753
|
{/* Site Header */}
|
|
1708
1754
|
{siteNode && (
|
|
1709
1755
|
<motion.div
|
|
@@ -1725,14 +1771,14 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel
|
|
|
1725
1771
|
>
|
|
1726
1772
|
<div className="flex items-center gap-2">
|
|
1727
1773
|
<img
|
|
1728
|
-
alt=
|
|
1774
|
+
alt={i18n._({ id: 'editor.sidebar.panels.sitePanel.site', message: 'Site' })}
|
|
1729
1775
|
className={cn(
|
|
1730
1776
|
'h-5 w-5 object-contain transition-all',
|
|
1731
1777
|
phase !== 'site' && 'opacity-60 grayscale',
|
|
1732
1778
|
)}
|
|
1733
1779
|
src="/icons/site-flag.webp"
|
|
1734
1780
|
/>
|
|
1735
|
-
<span className="font-medium text-sm">{siteNode.name || 'Site'}</span>
|
|
1781
|
+
<span className="font-medium text-sm">{siteNode.name || i18n._({ id: 'editor.sidebar.panels.sitePanel.site', message: 'Site' })}</span>
|
|
1736
1782
|
</div>
|
|
1737
1783
|
<CameraPopover
|
|
1738
1784
|
buttonClassName={cn(
|
|
@@ -1776,7 +1822,7 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel
|
|
|
1776
1822
|
{/* Buildings List */}
|
|
1777
1823
|
{buildings.length === 0 ? (
|
|
1778
1824
|
<motion.div className="px-3 py-4 text-muted-foreground text-sm" layout="position">
|
|
1779
|
-
No buildings yet
|
|
1825
|
+
{i18n._({ id: 'editor.sidebar.panels.sitePanel.noBuildingsYet', message: 'No buildings yet' })}
|
|
1780
1826
|
</motion.div>
|
|
1781
1827
|
) : (
|
|
1782
1828
|
<div className="flex min-h-0 flex-1 flex-col">
|