@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,13 +1,14 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { usePascalI18n } from '@pascal-app/core'
|
|
3
4
|
import { Pentagon, Square } from 'lucide-react'
|
|
4
5
|
import { cn } from '../../../lib/utils'
|
|
5
6
|
|
|
6
7
|
export type OutlineShape = 'rectangle' | 'polygon'
|
|
7
8
|
|
|
8
9
|
const SHAPES = [
|
|
9
|
-
{ id: 'rectangle', label: 'Rectangle', Icon: Square },
|
|
10
|
-
{ id: 'polygon', label: 'Polygon', Icon: Pentagon },
|
|
10
|
+
{ id: 'rectangle', label: /* i18n */ { id: 'editor.panels.shapeChoice.rectangle', message: 'Rectangle' }, Icon: Square },
|
|
11
|
+
{ id: 'polygon', label: /* i18n */ { id: 'editor.panels.shapeChoice.polygon', message: 'Polygon' }, Icon: Pentagon },
|
|
11
12
|
] as const
|
|
12
13
|
|
|
13
14
|
const pill =
|
|
@@ -32,13 +33,14 @@ export function ShapeChoice({
|
|
|
32
33
|
/** `data-shape-choice` hook for tests and proofs. */
|
|
33
34
|
data?: string
|
|
34
35
|
}) {
|
|
36
|
+
const i18n = usePascalI18n()
|
|
35
37
|
return (
|
|
36
38
|
<div
|
|
37
|
-
className={cn('flex min-h-7 items-center justify-between gap-2', className)}
|
|
39
|
+
className={cn('flex min-h-7 flex-wrap items-center justify-between gap-2', className)}
|
|
38
40
|
data-shape-choice={data}
|
|
39
41
|
>
|
|
40
42
|
<span className="whitespace-nowrap">{label}</span>
|
|
41
|
-
<div className="flex items-center gap-1.5">
|
|
43
|
+
<div className="flex flex-wrap items-center gap-1.5">
|
|
42
44
|
{SHAPES.map(({ id, label: shapeLabel, Icon }) => (
|
|
43
45
|
<button
|
|
44
46
|
className={pill}
|
|
@@ -49,7 +51,7 @@ export function ShapeChoice({
|
|
|
49
51
|
type="button"
|
|
50
52
|
>
|
|
51
53
|
<Icon aria-hidden className="h-3 w-3" />
|
|
52
|
-
{shapeLabel}
|
|
54
|
+
{i18n._(shapeLabel)}
|
|
53
55
|
</button>
|
|
54
56
|
))}
|
|
55
57
|
</div>
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type AnyNodeId,
|
|
5
5
|
useLiveNodeOverrides,
|
|
6
6
|
useScene,
|
|
7
|
-
type WallFaceRegion,
|
|
7
|
+
type WallFaceRegion, usePascalI18n,
|
|
8
8
|
} from '@pascal-app/core'
|
|
9
9
|
import { useViewer } from '@pascal-app/viewer'
|
|
10
10
|
import { Trash2 } from 'lucide-react'
|
|
@@ -25,6 +25,7 @@ const ROW_ACTION_CLASS =
|
|
|
25
25
|
|
|
26
26
|
/** The wall panel's "Paint regions" section: one row per region, select or delete. */
|
|
27
27
|
export function WallPaintRegionList({ wallId }: { wallId: string }) {
|
|
28
|
+
const i18n = usePascalI18n()
|
|
28
29
|
const nodes = useScene((state) => state.nodes)
|
|
29
30
|
const materials = useScene((state) => state.materials)
|
|
30
31
|
const unit = useViewer((state) => state.unit)
|
|
@@ -48,7 +49,7 @@ export function WallPaintRegionList({ wallId }: { wallId: string }) {
|
|
|
48
49
|
return (
|
|
49
50
|
// Focus inside this list still lets Delete target the active region.
|
|
50
51
|
<div className="contents" data-wall-region-list={wallId}>
|
|
51
|
-
<PanelSection title=
|
|
52
|
+
<PanelSection title={i18n._({ id: 'editor.panels.wallRegionList.paintRegions', message: 'Paint regions' })}>
|
|
52
53
|
<div className="flex flex-col gap-1">
|
|
53
54
|
{rows.map((row) => {
|
|
54
55
|
const isActive = active?.wallId === wallId && active.regionId === row.id
|
|
@@ -75,6 +76,7 @@ export function WallPaintRegionList({ wallId }: { wallId: string }) {
|
|
|
75
76
|
backgroundImage: row.finish.imageUrl
|
|
76
77
|
? `url(${row.finish.imageUrl})`
|
|
77
78
|
: undefined,
|
|
79
|
+
backgroundBlendMode: row.finish.tinted ? 'multiply' : undefined,
|
|
78
80
|
}}
|
|
79
81
|
/>
|
|
80
82
|
<span className="flex min-w-0 flex-col leading-tight">
|
|
@@ -85,13 +87,13 @@ export function WallPaintRegionList({ wallId }: { wallId: string }) {
|
|
|
85
87
|
</span>
|
|
86
88
|
</button>
|
|
87
89
|
<button
|
|
88
|
-
aria-label=
|
|
90
|
+
aria-label={i18n._({ id: 'editor.panels.wallRegionList.deleteRegion', message: 'Delete region' })}
|
|
89
91
|
className={ROW_ACTION_CLASS}
|
|
90
92
|
onClick={() => {
|
|
91
93
|
if (isActive) setActive(null)
|
|
92
94
|
if (removeWallRegion(wallId, row.id)) sfxEmitter.emit('sfx:structure-delete')
|
|
93
95
|
}}
|
|
94
|
-
title=
|
|
96
|
+
title={i18n._({ id: 'editor.panels.wallRegionList.deleteRegion', message: 'Delete region' })}
|
|
95
97
|
type="button"
|
|
96
98
|
>
|
|
97
99
|
<Trash2 className="h-3 w-3" />
|
|
@@ -99,9 +101,7 @@ export function WallPaintRegionList({ wallId }: { wallId: string }) {
|
|
|
99
101
|
</div>
|
|
100
102
|
)
|
|
101
103
|
})}
|
|
102
|
-
{capNotice &&
|
|
103
|
-
<div className="px-1 text-[11px] text-muted-foreground">{capNotice}</div>
|
|
104
|
-
)}
|
|
104
|
+
{capNotice && <div className="px-1 text-[11px] text-muted-foreground">{capNotice}</div>}
|
|
105
105
|
</div>
|
|
106
106
|
</PanelSection>
|
|
107
107
|
</div>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Icon } from '@iconify/react'
|
|
2
|
+
import { usePascalI18n } from '@pascal-app/core'
|
|
2
3
|
import type * as React from 'react'
|
|
3
4
|
|
|
5
|
+
import { toolHintKey } from '../../../i18n/node-messages'
|
|
4
6
|
import { cn } from '../../../lib/utils'
|
|
5
7
|
|
|
6
8
|
const MOUSE_SHORTCUTS = {
|
|
@@ -70,20 +72,22 @@ function ShortcutToken({ className, displayValue, value, ...props }: ShortcutTok
|
|
|
70
72
|
const isCommand = COMMAND_VALUES.has(value)
|
|
71
73
|
const isShift = value === 'Shift'
|
|
72
74
|
const commandDisplay = IS_MAC ? '⌘' : 'Ctrl'
|
|
73
|
-
const
|
|
75
|
+
const i18n = usePascalI18n()
|
|
76
|
+
const commandLabel = toolHintKey(IS_MAC ? 'Command' : 'Control', i18n)
|
|
77
|
+
const mouseLabel = mouseShortcut ? toolHintKey(mouseShortcut.label, i18n) : undefined
|
|
78
|
+
const shown = displayValue ?? toolHintKey(value, i18n)
|
|
74
79
|
|
|
75
80
|
return (
|
|
76
81
|
<kbd
|
|
77
82
|
aria-label={
|
|
78
|
-
|
|
79
|
-
(isCommand ? commandLabel : isShift ? 'Shift' : (displayValue ?? value))
|
|
83
|
+
mouseLabel ?? (isCommand ? commandLabel : isShift ? 'Shift' : shown)
|
|
80
84
|
}
|
|
81
85
|
className={cn(
|
|
82
86
|
'inline-flex h-6 items-center rounded border border-border bg-muted px-2 font-medium font-mono text-[11px] text-muted-foreground',
|
|
83
87
|
(mouseShortcut || isShift) && 'justify-center px-1.5',
|
|
84
88
|
className,
|
|
85
89
|
)}
|
|
86
|
-
title={
|
|
90
|
+
title={mouseLabel ?? (isCommand ? commandLabel : isShift ? 'Shift' : shown)}
|
|
87
91
|
{...props}
|
|
88
92
|
>
|
|
89
93
|
{mouseShortcut ? (
|
|
@@ -96,7 +100,7 @@ function ShortcutToken({ className, displayValue, value, ...props }: ShortcutTok
|
|
|
96
100
|
icon={mouseShortcut.icon}
|
|
97
101
|
width={14}
|
|
98
102
|
/>
|
|
99
|
-
<span className="sr-only">{
|
|
103
|
+
<span className="sr-only">{mouseLabel}</span>
|
|
100
104
|
</>
|
|
101
105
|
) : isShift ? (
|
|
102
106
|
// Icon rather than the ⇧ text glyph — the font renders the glyph's
|
|
@@ -117,7 +121,7 @@ function ShortcutToken({ className, displayValue, value, ...props }: ShortcutTok
|
|
|
117
121
|
// it up a touch on Mac. "Ctrl" stays at the token's normal size.
|
|
118
122
|
<span className={IS_MAC ? 'text-[13px] leading-none' : undefined}>{commandDisplay}</span>
|
|
119
123
|
) : (
|
|
120
|
-
|
|
124
|
+
shown
|
|
121
125
|
)}
|
|
122
126
|
</kbd>
|
|
123
127
|
)
|
|
@@ -14,16 +14,23 @@ test('the inspector keeps 180 px, or its own height if shorter, above the full c
|
|
|
14
14
|
).toEqual({ inspector: 0, card: 240 })
|
|
15
15
|
})
|
|
16
16
|
|
|
17
|
-
test('on a short window the
|
|
17
|
+
test('on a short window the help card scrolls to preserve editing space', () => {
|
|
18
18
|
expect(rightStackFloors({ ...tall, naturalHeight: 500, stackHeight: 400 })).toEqual({
|
|
19
|
-
inspector:
|
|
20
|
-
card:
|
|
19
|
+
inspector: 180,
|
|
20
|
+
card: 212,
|
|
21
21
|
})
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
-
test('
|
|
24
|
+
test('expanded hints cannot squeeze the inspector down to just its header', () => {
|
|
25
25
|
expect(rightStackFloors({ ...tall, naturalHeight: 500, stackHeight: 258 })).toEqual({
|
|
26
|
-
inspector:
|
|
27
|
-
card:
|
|
26
|
+
inspector: 180,
|
|
27
|
+
card: 70,
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('a column shorter than the inspector minimum still bounds both cards', () => {
|
|
32
|
+
expect(rightStackFloors({ ...tall, naturalHeight: 500, stackHeight: 100 })).toEqual({
|
|
33
|
+
inspector: 92,
|
|
34
|
+
card: 0,
|
|
28
35
|
})
|
|
29
36
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { createContext, type ReactNode, useContext,
|
|
3
|
+
import { createContext, type ReactNode, useContext, useLayoutEffect, useRef } from 'react'
|
|
4
4
|
import { useIsMobile } from '../../hooks/use-mobile'
|
|
5
5
|
import { cn } from '../../lib/utils'
|
|
6
6
|
import { TOP_MENU_CLEARANCE } from './action-menu/placement'
|
|
@@ -20,9 +20,8 @@ const GAP = 8
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* The heights neither panel shrinks below. The inspector keeps 180 px (or its
|
|
23
|
-
* own height, if shorter)
|
|
24
|
-
*
|
|
25
|
-
* very short window it gives way and scrolls on itself — both stay usable.
|
|
23
|
+
* own height, if shorter) and never less than its header row when the column allows.
|
|
24
|
+
* The card uses the remaining room and scrolls on itself on a short window — both stay usable.
|
|
26
25
|
*/
|
|
27
26
|
export function rightStackFloors(args: {
|
|
28
27
|
naturalHeight: number
|
|
@@ -33,9 +32,10 @@ export function rightStackFloors(args: {
|
|
|
33
32
|
const { naturalHeight, headerHeight, cardHeight, stackHeight } = args
|
|
34
33
|
const gap = naturalHeight > 0 && cardHeight > 0 ? GAP : 0
|
|
35
34
|
const header = Math.min(headerHeight, naturalHeight)
|
|
36
|
-
const inspector = Math.
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const inspector = Math.min(
|
|
36
|
+
naturalHeight,
|
|
37
|
+
stackHeight,
|
|
38
|
+
Math.max(header, Math.min(INSPECTOR_MIN_HEIGHT, stackHeight - gap)),
|
|
39
39
|
)
|
|
40
40
|
const card = Math.max(0, Math.min(cardHeight, stackHeight - gap - inspector))
|
|
41
41
|
return { inspector, card }
|
|
@@ -78,7 +78,7 @@ export function RightStack({
|
|
|
78
78
|
|
|
79
79
|
// `min(180px, its own height)` has no CSS form for a flex item that scrolls,
|
|
80
80
|
// so both floors are measured (`rightStackFloors`).
|
|
81
|
-
|
|
81
|
+
useLayoutEffect(() => {
|
|
82
82
|
if (isMobile) return
|
|
83
83
|
const stack = stackRef.current
|
|
84
84
|
const slot = inspectorRef.current
|
|
@@ -88,18 +88,29 @@ export function RightStack({
|
|
|
88
88
|
const fit = () => {
|
|
89
89
|
frame = 0
|
|
90
90
|
const { natural, header } = inspectorHeights(slot)
|
|
91
|
+
const cardHeight = Array.from(card.children).reduce(
|
|
92
|
+
(height, child) => height + child.scrollHeight + (child instanceof HTMLElement ? child.offsetHeight - child.clientHeight : 0),
|
|
93
|
+
0,
|
|
94
|
+
)
|
|
91
95
|
const floors = rightStackFloors({
|
|
92
96
|
naturalHeight: natural,
|
|
93
97
|
headerHeight: header,
|
|
94
|
-
cardHeight
|
|
98
|
+
cardHeight,
|
|
95
99
|
stackHeight: stack.clientHeight,
|
|
96
100
|
})
|
|
97
101
|
const inspectorMin = `${floors.inspector}px`
|
|
98
102
|
const cardMin = `${floors.card}px`
|
|
103
|
+
// Custom panels may wrap PanelWrapper in a non-flex element. Give the
|
|
104
|
+
// card an explicit inherited limit so those wrappers cannot bypass it.
|
|
105
|
+
const available = Math.max(0, stack.clientHeight - floors.card - (natural > 0 && floors.card > 0 ? GAP : 0))
|
|
106
|
+
const limit = `${available}px`
|
|
107
|
+
if (slot.style.getPropertyValue('--inspector-available-height') !== limit) {
|
|
108
|
+
slot.style.setProperty('--inspector-available-height', limit)
|
|
109
|
+
}
|
|
99
110
|
if (slot.style.minHeight !== inspectorMin) slot.style.minHeight = inspectorMin
|
|
100
111
|
if (card.style.minHeight !== cardMin) card.style.minHeight = cardMin
|
|
101
112
|
// The card is click-through; it takes the pointer only when it has to scroll.
|
|
102
|
-
const pointer = floors.card <
|
|
113
|
+
const pointer = floors.card < cardHeight ? 'auto' : ''
|
|
103
114
|
if (card.style.pointerEvents !== pointer) card.style.pointerEvents = pointer
|
|
104
115
|
}
|
|
105
116
|
const schedule = () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { type MessageDescriptor, usePascalI18n } from '@pascal-app/core'
|
|
3
4
|
import { Plus } from 'lucide-react'
|
|
4
5
|
import type { ComponentType, ReactNode } from 'react'
|
|
5
6
|
import {
|
|
@@ -32,19 +33,19 @@ interface IconRailProps {
|
|
|
32
33
|
className?: string
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
const sitePanel: { id: PanelId; iconSrc: string; label:
|
|
36
|
+
const sitePanel: { id: PanelId; iconSrc: string; label: MessageDescriptor } = {
|
|
36
37
|
id: 'site',
|
|
37
38
|
iconSrc: '/icons/level.webp',
|
|
38
|
-
label: 'Site',
|
|
39
|
+
label: /* i18n */ { id: 'editor.sidebar.iconRail.site', message: 'Site' },
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
const settingsPanel: { id: PanelId; iconSrc: string; label:
|
|
42
|
+
const settingsPanel: { id: PanelId; iconSrc: string; label: MessageDescriptor } = {
|
|
42
43
|
id: 'settings',
|
|
43
44
|
iconSrc: '/icons/settings.webp',
|
|
44
|
-
label: 'Settings',
|
|
45
|
+
label: /* i18n */ { id: 'editor.sidebar.iconRail.settings', message: 'Settings' },
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
const panels: { id: PanelId; iconSrc: string; label:
|
|
48
|
+
const panels: { id: PanelId; iconSrc: string; label: MessageDescriptor }[] = [sitePanel, settingsPanel]
|
|
48
49
|
|
|
49
50
|
export function IconRail({
|
|
50
51
|
activePanel,
|
|
@@ -53,6 +54,7 @@ export function IconRail({
|
|
|
53
54
|
extraPanels,
|
|
54
55
|
className,
|
|
55
56
|
}: IconRailProps) {
|
|
57
|
+
const i18n = usePascalI18n()
|
|
56
58
|
const regularExtraPanels = extraPanels?.filter((panel) => !panel.pluginId && panel.id !== 'plugins')
|
|
57
59
|
const pluginPanels = extraPanels?.filter((panel) => panel.pluginId)
|
|
58
60
|
const pluginsPanel = extraPanels?.find((panel) => panel.id === 'plugins')
|
|
@@ -113,7 +115,7 @@ export function IconRail({
|
|
|
113
115
|
type="button"
|
|
114
116
|
>
|
|
115
117
|
<img
|
|
116
|
-
alt={panel.label}
|
|
118
|
+
alt={i18n._(panel.label)}
|
|
117
119
|
className={cn(
|
|
118
120
|
'h-6 w-6 object-contain transition-all',
|
|
119
121
|
!isActive && 'opacity-50 saturate-0',
|
|
@@ -122,7 +124,7 @@ export function IconRail({
|
|
|
122
124
|
/>
|
|
123
125
|
</button>
|
|
124
126
|
</TooltipTrigger>
|
|
125
|
-
<TooltipContent side="right">{panel.label}</TooltipContent>
|
|
127
|
+
<TooltipContent side="right">{i18n._(panel.label)}</TooltipContent>
|
|
126
128
|
</Tooltip>
|
|
127
129
|
)
|
|
128
130
|
})}
|
|
@@ -144,7 +146,7 @@ export function IconRail({
|
|
|
144
146
|
type="button"
|
|
145
147
|
>
|
|
146
148
|
<img
|
|
147
|
-
alt={panel.label}
|
|
149
|
+
alt={i18n._(panel.label)}
|
|
148
150
|
className={cn(
|
|
149
151
|
'h-6 w-6 object-contain transition-all',
|
|
150
152
|
!isActive && 'opacity-50 saturate-0',
|
|
@@ -153,7 +155,7 @@ export function IconRail({
|
|
|
153
155
|
/>
|
|
154
156
|
</button>
|
|
155
157
|
</TooltipTrigger>
|
|
156
|
-
<TooltipContent side="right">{panel.label}</TooltipContent>
|
|
158
|
+
<TooltipContent side="right">{i18n._(panel.label)}</TooltipContent>
|
|
157
159
|
</Tooltip>
|
|
158
160
|
)
|
|
159
161
|
})}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import type
|
|
3
|
+
import { type AssetInput, type MessageDescriptor, usePascalI18n } from '@pascal-app/core'
|
|
4
4
|
import { Root as TooltipRoot } from '@radix-ui/react-tooltip'
|
|
5
5
|
import NextImage from 'next/image'
|
|
6
6
|
import { useMemo, useState } from 'react'
|
|
@@ -21,10 +21,10 @@ export type FunctionTreeNode = {
|
|
|
21
21
|
children: FunctionTreeNode[]
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const SOURCE_CHIPS: Array<{ id: NonNullable<AssetInput['source']>; label:
|
|
25
|
-
{ id: 'library', label: 'Library' },
|
|
26
|
-
{ id: 'community', label: 'Community' },
|
|
27
|
-
{ id: 'mine', label: 'Mine' },
|
|
24
|
+
const SOURCE_CHIPS: Array<{ id: NonNullable<AssetInput['source']>; label: MessageDescriptor }> = [
|
|
25
|
+
{ id: 'library', label: /* i18n */ { id: 'editor.sidebar.panels.itemsPanel.functionTreePanel.library', message: 'Library' } },
|
|
26
|
+
{ id: 'community', label: /* i18n */ { id: 'editor.sidebar.panels.itemsPanel.functionTreePanel.community', message: 'Community' } },
|
|
27
|
+
{ id: 'mine', label: /* i18n */ { id: 'editor.sidebar.panels.itemsPanel.functionTreePanel.mine', message: 'Mine' } },
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
/** Every slug at or below `node`, so a non-leaf selection matches descendants. */
|
|
@@ -64,6 +64,7 @@ export function FunctionTreePanel({
|
|
|
64
64
|
leadingTile?: React.ReactNode
|
|
65
65
|
emptyState?: React.ReactNode
|
|
66
66
|
}) {
|
|
67
|
+
const i18n = usePascalI18n()
|
|
67
68
|
const [activeRootSlug, setActiveRootSlug] = useState<string | null>(
|
|
68
69
|
functionTree[0]?.slug ?? null,
|
|
69
70
|
)
|
|
@@ -178,7 +179,7 @@ export function FunctionTreePanel({
|
|
|
178
179
|
setSearch(e.target.value)
|
|
179
180
|
onSearchChange?.(e.target.value)
|
|
180
181
|
}}
|
|
181
|
-
placeholder=
|
|
182
|
+
placeholder={i18n._({ id: 'editor.sidebar.panels.itemsPanel.functionTreePanel.search', message: 'Search...' })}
|
|
182
183
|
type="text"
|
|
183
184
|
value={search}
|
|
184
185
|
/>
|
|
@@ -197,7 +198,7 @@ export function FunctionTreePanel({
|
|
|
197
198
|
onClick={() => setActiveSource(isActive ? null : chip.id)}
|
|
198
199
|
type="button"
|
|
199
200
|
>
|
|
200
|
-
{chip.label}
|
|
201
|
+
{i18n._(chip.label)}
|
|
201
202
|
</button>
|
|
202
203
|
)
|
|
203
204
|
})}
|
|
@@ -217,7 +218,7 @@ export function FunctionTreePanel({
|
|
|
217
218
|
onClick={() => setActiveChildSlug(null)}
|
|
218
219
|
type="button"
|
|
219
220
|
>
|
|
220
|
-
All
|
|
221
|
+
{i18n._({ id: 'editor.sidebar.panels.itemsPanel.functionTreePanel.all', message: 'All' })}
|
|
221
222
|
</button>
|
|
222
223
|
{activeRoot.children.map((child) => {
|
|
223
224
|
const isActive = activeChildSlug === child.slug
|
|
@@ -250,8 +251,7 @@ export function FunctionTreePanel({
|
|
|
250
251
|
) : isServerSearch && search && searchResults?.length === 0 ? (
|
|
251
252
|
(emptyState ?? (
|
|
252
253
|
<div className="flex h-full items-center justify-center text-muted-foreground text-xs">
|
|
253
|
-
No results for
|
|
254
|
-
</div>
|
|
254
|
+
{i18n._({ id: 'editor.sidebar.panels.itemsPanel.functionTreePanel.noResultsForSearch', message: 'No results for “{search}”', values: { search } })}</div>
|
|
255
255
|
))
|
|
256
256
|
) : (
|
|
257
257
|
<ItemCatalog
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { usePascalI18n } from '@pascal-app/core'
|
|
3
4
|
import type { AssetInput } from '@pascal-app/core'
|
|
4
5
|
import NextImage from 'next/image'
|
|
5
6
|
import { useEffect, useState } from 'react'
|
|
@@ -98,6 +99,7 @@ function LegacyItemsPanel({
|
|
|
98
99
|
showSourceFilter?: boolean
|
|
99
100
|
showTagFilters?: boolean
|
|
100
101
|
}) {
|
|
102
|
+
const i18n = usePascalI18n()
|
|
101
103
|
const mode = useEditor((s) => s.mode)
|
|
102
104
|
const catalogCategory = useEditor((s) => s.catalogCategory)
|
|
103
105
|
const setMode = useEditor((s) => s.setMode)
|
|
@@ -167,9 +169,9 @@ function LegacyItemsPanel({
|
|
|
167
169
|
// filter even before they own any items. Selecting "Mine" with no
|
|
168
170
|
// matching items falls through to the empty/no-results state.
|
|
169
171
|
const sourceChips: Array<{ id: AssetInput['source']; label: string }> = [
|
|
170
|
-
{ id: 'library', label: 'Library' },
|
|
171
|
-
{ id: 'community', label: 'Community' },
|
|
172
|
-
{ id: 'mine', label: 'Mine' },
|
|
172
|
+
{ id: 'library', label: i18n._({ id: 'editor.sidebar.panels.itemsPanel.library', message: 'Library' }) },
|
|
173
|
+
{ id: 'community', label: i18n._({ id: 'editor.sidebar.panels.itemsPanel.community', message: 'Community' }) },
|
|
174
|
+
{ id: 'mine', label: i18n._({ id: 'editor.sidebar.panels.itemsPanel.mine', message: 'Mine' }) },
|
|
173
175
|
]
|
|
174
176
|
const allTags = Array.from(new Set(categoryItems.flatMap((item) => item.tags ?? [])))
|
|
175
177
|
const placementTags = allTags.filter((t) => PLACEMENT_TAGS.has(t))
|
|
@@ -215,13 +217,13 @@ function LegacyItemsPanel({
|
|
|
215
217
|
type="button"
|
|
216
218
|
>
|
|
217
219
|
<NextImage
|
|
218
|
-
alt={cat.label}
|
|
220
|
+
alt={i18n._(cat.label)}
|
|
219
221
|
className={cn('size-7 object-contain', !isActive && 'opacity-60 grayscale')}
|
|
220
222
|
height={28}
|
|
221
223
|
src={cat.iconSrc}
|
|
222
224
|
width={28}
|
|
223
225
|
/>
|
|
224
|
-
<span className="font-medium text-[10px] leading-none">{cat.label}</span>
|
|
226
|
+
<span className="font-medium text-[10px] leading-none">{i18n._(cat.label)}</span>
|
|
225
227
|
</button>
|
|
226
228
|
)
|
|
227
229
|
})}
|
|
@@ -242,7 +244,7 @@ function LegacyItemsPanel({
|
|
|
242
244
|
setSearch(e.target.value)
|
|
243
245
|
onSearchChange?.(e.target.value)
|
|
244
246
|
}}
|
|
245
|
-
placeholder=
|
|
247
|
+
placeholder={i18n._({ id: 'editor.sidebar.panels.itemsPanel.search', message: 'Search...' })}
|
|
246
248
|
type="text"
|
|
247
249
|
value={search}
|
|
248
250
|
/>
|
|
@@ -284,7 +286,7 @@ function LegacyItemsPanel({
|
|
|
284
286
|
onClick={() => setActivePlacementTag(null)}
|
|
285
287
|
type="button"
|
|
286
288
|
>
|
|
287
|
-
All
|
|
289
|
+
{i18n._({ id: 'editor.sidebar.panels.itemsPanel.all', message: 'All' })}
|
|
288
290
|
</button>
|
|
289
291
|
{placementTags.map((tag) => {
|
|
290
292
|
const count = placementCount(tag)
|
|
@@ -376,8 +378,7 @@ function LegacyItemsPanel({
|
|
|
376
378
|
) : isServerSearch && search && searchResults?.length === 0 ? (
|
|
377
379
|
(emptyState ?? (
|
|
378
380
|
<div className="flex h-full items-center justify-center text-muted-foreground text-xs">
|
|
379
|
-
No results for
|
|
380
|
-
</div>
|
|
381
|
+
{i18n._({ id: 'editor.sidebar.panels.itemsPanel.noResultsForSearch', message: 'No results for “{search}”', values: { search } })}</div>
|
|
381
382
|
))
|
|
382
383
|
) : (
|
|
383
384
|
<ItemCatalog
|
|
@@ -73,6 +73,7 @@ import { Switch } from './../../../../../components/ui/primitives/switch'
|
|
|
73
73
|
import { cn } from './../../../../../lib/utils'
|
|
74
74
|
import { refusedObjectsNotice } from './../../../../../components/editor/group-actions'
|
|
75
75
|
import { addStoreyBelow, addStoreyOnTop } from './../../../../../lib/add-storey'
|
|
76
|
+
import { offerExteriorWallCopy } from './../../../../../components/ui/level-wall-copy-prompt'
|
|
76
77
|
import { deleteLevelWithFallbackSelection } from './../../../../../lib/level-selection'
|
|
77
78
|
import { bringBuildArtifacts } from './../../../../../lib/scene-clipboard'
|
|
78
79
|
import useEditor, { selectDefaultBuildingAndLevel } from './../../../../../store/use-editor'
|
|
@@ -423,10 +424,12 @@ function FloorSettingsSection() {
|
|
|
423
424
|
const addFloor = () => {
|
|
424
425
|
const newLevel = addStoreyOnTop(building.id)
|
|
425
426
|
setSelection({ buildingId: building.id, levelId: newLevel.id })
|
|
427
|
+
offerExteriorWallCopy(newLevel.id)
|
|
426
428
|
}
|
|
427
429
|
const addBasement = () => {
|
|
428
430
|
const newLevel = addStoreyBelow(building.id)
|
|
429
431
|
setSelection({ buildingId: building.id, levelId: newLevel.id })
|
|
432
|
+
offerExteriorWallCopy(newLevel.id)
|
|
430
433
|
}
|
|
431
434
|
|
|
432
435
|
return (
|