@pascal-app/editor 1.0.5 → 1.0.7
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 +20 -17
- 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 +20 -8
- package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
- package/src/components/editor/first-person-controls.tsx +72 -61
- package/src/components/editor/floating-action-menu.tsx +27 -9
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floorplan-panel.test.ts +220 -0
- package/src/components/editor/floorplan-panel.tsx +285 -132
- package/src/components/editor/footprint-height-handle.tsx +1 -1
- 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 +20 -0
- package/src/components/editor/index.tsx +130 -20
- package/src/components/editor/inspector-extension-actions.tsx +36 -0
- package/src/components/editor/localized-html.tsx +22 -0
- package/src/components/editor/node-action-menu.tsx +46 -12
- package/src/components/editor/node-arrow-handles.tsx +1 -2
- package/src/components/editor/open-wall-ends-3d-layer.tsx +1 -1
- package/src/components/editor/opening-guides-3d-layer.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 +1 -1
- package/src/components/editor/room-handles-2d.tsx +156 -6
- package/src/components/editor/room-handles.tsx +241 -51
- package/src/components/editor/room-highlight.tsx +2 -0
- package/src/components/editor/room-labels.tsx +29 -15
- 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 +1 -1
- 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 +1 -1
- package/src/components/editor/wall-move-side-handles.tsx +5 -1
- package/src/components/editor/wall-pull-grip.tsx +193 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +81 -1
- 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 +31 -3
- package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +6 -0
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +22 -13
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +11 -3
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +7 -1
- 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-registry-layer.test.ts +12 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +197 -37
- 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/tools/distribution/distribution-run-tool.tsx +50 -26
- 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 +30 -34
- package/src/components/tools/registry/item-surface-move.test.ts +11 -0
- 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 +1 -1
- package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
- package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
- 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/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 +30 -14
- 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 +53 -3
- package/src/components/ui/helpers/helper-manager.tsx +82 -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 +113 -38
- package/src/components/ui/panels/floor-opening-panel.tsx +73 -25
- 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 +8 -6
- 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 +8 -7
- package/src/components/ui/panels/multi-parametric-inspector.tsx +122 -34
- package/src/components/ui/panels/multi-selection-panel.tsx +75 -35
- package/src/components/ui/panels/node-display.ts +35 -24
- package/src/components/ui/panels/panel-manager.tsx +5 -3
- package/src/components/ui/panels/panel-wrapper.tsx +22 -109
- 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 +464 -65
- 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 +21 -12
- package/src/components/ui/panels/room-panel.tsx +206 -99
- 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/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/site-panel/building-tree-node.tsx +4 -3
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -2
- 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 +117 -64
- 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 +8 -3
- 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 +18 -8
- 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 +61 -4
- 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 +15117 -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 +37501 -0
- package/src/i18n/locales/es/messages.ts +1 -0
- package/src/i18n/locales/fr/messages.po +37501 -0
- package/src/i18n/locales/fr/messages.ts +1 -0
- package/src/i18n/locales/ja/messages.po +37501 -0
- package/src/i18n/locales/ja/messages.ts +1 -0
- package/src/i18n/locales/zh-CN/messages.po +37501 -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 +156 -4
- package/src/lib/add-storey.test.ts +3 -3
- package/src/lib/contextual-help.test.ts +20 -1
- package/src/lib/contextual-help.ts +95 -19
- package/src/lib/continuation.ts +38 -11
- package/src/lib/floor-footprints.test.ts +2 -1
- package/src/lib/floor-foundation-support.test.ts +7 -3
- 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/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/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/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/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-parser.ts +7 -3
- package/src/lib/measurements.test.ts +17 -0
- package/src/lib/measurements.ts +20 -5
- package/src/lib/mezzanine-selection.test.ts +3 -1
- 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/reference-scale.ts +83 -0
- package/src/lib/room-built-on.test.ts +26 -12
- package/src/lib/room-built-on.ts +20 -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 +140 -0
- package/src/lib/room-handle-drag.ts +557 -42
- 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 +37 -0
- package/src/lib/room-push-dimensions.ts +226 -2
- package/src/lib/room-selection.test.ts +23 -6
- package/src/lib/scene-clipboard.test.ts +70 -49
- package/src/lib/scene-clipboard.ts +9 -4
- 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/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 +20 -5
- 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 +14 -5
- 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 +42 -0
- package/src/lib/window-interaction.ts +47 -5
- 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 +48 -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-wall-snap-indicator.ts +15 -2
|
@@ -55,11 +55,14 @@ import {
|
|
|
55
55
|
type LevelDuplicatePreset,
|
|
56
56
|
} from '@pascal-app/core/building'
|
|
57
57
|
import {
|
|
58
|
-
|
|
59
|
-
getLevelDisplayName,
|
|
58
|
+
formatNumber,
|
|
60
59
|
roofLevelOrderBreach,
|
|
61
60
|
servesAsRoofLevel,
|
|
61
|
+
useLocale,
|
|
62
|
+
usePascalI18n,
|
|
62
63
|
} from '@pascal-app/core'
|
|
64
|
+
import { authoredLevelName, localizedLevelName } from '../../i18n/level-name'
|
|
65
|
+
import { messageWithSlot } from '../../i18n/message-slot'
|
|
63
66
|
import { addStoreyBelow, addStoreyOnTop, setGroundFloor } from '../../lib/add-storey'
|
|
64
67
|
import { deleteLevelWithFallbackSelection } from '../../lib/level-selection'
|
|
65
68
|
import { unitMemberLevels, leaveUnitFocus } from '../../lib/units'
|
|
@@ -69,6 +72,7 @@ import { useIsMobile } from '../../hooks/use-mobile'
|
|
|
69
72
|
import { ActionButton } from './controls/action-button'
|
|
70
73
|
import { SliderControl } from './controls/slider-control'
|
|
71
74
|
import { LevelDuplicateDialog } from './level-duplicate-dialog'
|
|
75
|
+
import { LevelWallCopyPrompt, offerExteriorWallCopy } from './level-wall-copy-prompt'
|
|
72
76
|
import {
|
|
73
77
|
Dialog,
|
|
74
78
|
DialogContent,
|
|
@@ -90,28 +94,31 @@ function LevelInlineRename({
|
|
|
90
94
|
isEditing: boolean
|
|
91
95
|
onStopEditing: () => void
|
|
92
96
|
}) {
|
|
97
|
+
const i18n = usePascalI18n()
|
|
98
|
+
const locale = useLocale()
|
|
93
99
|
const updateNode = useScene((s) => s.updateNode)
|
|
94
|
-
const defaultName =
|
|
95
|
-
const
|
|
100
|
+
const defaultName = localizedLevelName({ level: level.level }, i18n, locale)
|
|
101
|
+
const authoredName = authoredLevelName(level)
|
|
102
|
+
const [value, setValue] = useState(authoredName ?? '')
|
|
96
103
|
const inputRef = useRef<HTMLInputElement>(null)
|
|
97
104
|
|
|
98
105
|
useEffect(() => {
|
|
99
106
|
if (isEditing) {
|
|
100
|
-
setValue(
|
|
107
|
+
setValue(authoredName ?? '')
|
|
101
108
|
setTimeout(() => {
|
|
102
109
|
inputRef.current?.focus()
|
|
103
110
|
inputRef.current?.select()
|
|
104
111
|
}, 0)
|
|
105
112
|
}
|
|
106
|
-
}, [isEditing,
|
|
113
|
+
}, [isEditing, authoredName])
|
|
107
114
|
|
|
108
115
|
const handleSave = useCallback(() => {
|
|
109
116
|
const trimmed = value.trim()
|
|
110
|
-
if (trimmed !==
|
|
117
|
+
if ((trimmed || undefined) !== authoredName) {
|
|
111
118
|
updateNode(level.id, { name: trimmed || undefined })
|
|
112
119
|
}
|
|
113
120
|
onStopEditing()
|
|
114
|
-
}, [value, level.id,
|
|
121
|
+
}, [value, level.id, authoredName, updateNode, onStopEditing])
|
|
115
122
|
|
|
116
123
|
if (!isEditing) return null
|
|
117
124
|
|
|
@@ -173,14 +180,19 @@ function LevelRow({
|
|
|
173
180
|
const [duplicateDialogOpen, setDuplicateDialogOpen] = useState(false)
|
|
174
181
|
const [isEditing, setIsEditing] = useState(false)
|
|
175
182
|
const updateNode = useScene((s) => s.updateNode)
|
|
183
|
+
const i18n = usePascalI18n()
|
|
184
|
+
const locale = useLocale()
|
|
176
185
|
const { isImperial, toDisplay, displayUnit, precision: displayPrecision } = useLinearDisplay('m', 2)
|
|
177
186
|
|
|
178
187
|
const storeyHeight = getStoredLevelHeight(level)
|
|
179
188
|
// Decimal units keep the compact readout; integer millimeters must retain trailing zeroes.
|
|
180
|
-
const formattedStoreyHeight = toDisplay(storeyHeight)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
189
|
+
const formattedStoreyHeight = formatNumber(toDisplay(storeyHeight), {
|
|
190
|
+
locale,
|
|
191
|
+
min: Math.max(0, displayPrecision - 1),
|
|
192
|
+
max: displayPrecision,
|
|
193
|
+
})
|
|
194
|
+
const storeyHeightLabel = `${formattedStoreyHeight} ${displayUnit}`
|
|
195
|
+
const levelName = localizedLevelName(level, i18n, locale)
|
|
184
196
|
// Same rule as the site panel and command palette: the ordinal-0 ground
|
|
185
197
|
// floor is the vertical model's zero anchor and must never be deletable.
|
|
186
198
|
const canDeleteLevel = level.level !== 0
|
|
@@ -219,7 +231,7 @@ function LevelRow({
|
|
|
219
231
|
>
|
|
220
232
|
<button
|
|
221
233
|
{...dragHandleProps}
|
|
222
|
-
aria-label={
|
|
234
|
+
aria-label={i18n._({ id: 'editor.floatingLevelSelector.reorderLevelname', message: 'Reorder {levelName}', values: { levelName } })}
|
|
223
235
|
className={cn(
|
|
224
236
|
'ml-0.5 flex h-6 w-4 shrink-0 cursor-grab touch-none items-center justify-center rounded-md text-muted-foreground/35 opacity-0 transition-colors hover:bg-black/4 dark:hover:bg-white/5 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 group-hover/level:opacity-100',
|
|
225
237
|
isDragging && 'cursor-grabbing opacity-100',
|
|
@@ -229,7 +241,7 @@ function LevelRow({
|
|
|
229
241
|
dragHandleProps?.onClick?.(e)
|
|
230
242
|
}}
|
|
231
243
|
ref={dragHandleRef}
|
|
232
|
-
title=
|
|
244
|
+
title={i18n._({ id: 'editor.floatingLevelSelector.dragToReorder', message: 'Drag to reorder' })}
|
|
233
245
|
type="button"
|
|
234
246
|
>
|
|
235
247
|
<GripVertical className="h-3.5 w-3.5" />
|
|
@@ -242,15 +254,15 @@ function LevelRow({
|
|
|
242
254
|
e.stopPropagation()
|
|
243
255
|
setIsEditing(true)
|
|
244
256
|
}}
|
|
245
|
-
title={
|
|
257
|
+
title={levelName}
|
|
246
258
|
type="button"
|
|
247
259
|
>
|
|
248
|
-
<span className="truncate">{
|
|
260
|
+
<span className="truncate">{levelName}</span>
|
|
249
261
|
{unitDotColor && (
|
|
250
262
|
<span
|
|
251
263
|
className="ml-1.5 h-1.5 w-1.5 shrink-0 rounded-full"
|
|
252
264
|
style={{ backgroundColor: unitDotColor }}
|
|
253
|
-
title=
|
|
265
|
+
title={i18n._({ id: 'editor.floatingLevelSelector.focusedUnitHasZonesHere', message: 'Focused unit has zones here' })}
|
|
254
266
|
/>
|
|
255
267
|
)}
|
|
256
268
|
</button>
|
|
@@ -261,7 +273,7 @@ function LevelRow({
|
|
|
261
273
|
<button
|
|
262
274
|
className="mr-0.5 shrink-0 whitespace-nowrap rounded px-1 py-0.5 font-mono text-[10px] text-muted-foreground tabular-nums transition-colors hover:bg-black/4 hover:text-foreground dark:text-muted-foreground/50 dark:hover:bg-white/5 dark:hover:text-foreground"
|
|
263
275
|
onClick={(e) => e.stopPropagation()}
|
|
264
|
-
title=
|
|
276
|
+
title={i18n._({ id: 'editor.floatingLevelSelector.levelHeight', message: 'Level height' })}
|
|
265
277
|
type="button"
|
|
266
278
|
>
|
|
267
279
|
{storeyHeightLabel}
|
|
@@ -275,7 +287,7 @@ function LevelRow({
|
|
|
275
287
|
sideOffset={8}
|
|
276
288
|
>
|
|
277
289
|
<SliderControl
|
|
278
|
-
label=
|
|
290
|
+
label={i18n._({ id: 'editor.floatingLevelSelector.levelHeight', message: 'Level height' })}
|
|
279
291
|
max={20}
|
|
280
292
|
min={1}
|
|
281
293
|
onChange={(v) => updateNode(level.id, { height: v })}
|
|
@@ -301,14 +313,19 @@ function LevelRow({
|
|
|
301
313
|
<Popover>
|
|
302
314
|
<PopoverTrigger asChild>
|
|
303
315
|
<button
|
|
304
|
-
|
|
316
|
+
aria-label={i18n._({
|
|
317
|
+
id: 'editor.layers.levelOptions',
|
|
318
|
+
message: '{levelName} options',
|
|
319
|
+
values: { levelName },
|
|
320
|
+
})}
|
|
321
|
+
className="flex h-5 w-4 shrink-0 items-center justify-center text-muted-foreground/40 opacity-0 transition-all hover:text-foreground group-hover/level:opacity-100 focus-visible:opacity-100"
|
|
305
322
|
onClick={(e) => e.stopPropagation()}
|
|
306
323
|
type="button"
|
|
307
324
|
>
|
|
308
325
|
<MoreVertical className="h-3 w-3" />
|
|
309
326
|
</button>
|
|
310
327
|
</PopoverTrigger>
|
|
311
|
-
<PopoverContent align="start" className="w-44 p-1" side="right" sideOffset={8}>
|
|
328
|
+
<PopoverContent align="start" className="w-max min-w-44 max-w-72 p-1" side="right" sideOffset={8}>
|
|
312
329
|
<button
|
|
313
330
|
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors hover:bg-black/6 dark:hover:bg-white/10 hover:text-foreground"
|
|
314
331
|
onClick={(e) => {
|
|
@@ -318,7 +335,7 @@ function LevelRow({
|
|
|
318
335
|
type="button"
|
|
319
336
|
>
|
|
320
337
|
<Copy className="h-3 w-3" />
|
|
321
|
-
Duplicate level
|
|
338
|
+
{i18n._({ id: 'editor.floatingLevelSelector.duplicateLevel', message: 'Duplicate level' })}
|
|
322
339
|
</button>
|
|
323
340
|
<button
|
|
324
341
|
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors hover:bg-black/6 dark:hover:bg-white/10 hover:text-foreground"
|
|
@@ -329,7 +346,7 @@ function LevelRow({
|
|
|
329
346
|
type="button"
|
|
330
347
|
>
|
|
331
348
|
<Copy className="h-3 w-3" />
|
|
332
|
-
Duplicate with options...
|
|
349
|
+
{i18n._({ id: 'editor.floatingLevelSelector.duplicateWithOptions', message: 'Duplicate with options...' })}
|
|
333
350
|
</button>
|
|
334
351
|
<button
|
|
335
352
|
className="flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-muted-foreground text-xs transition-colors enabled:hover:bg-black/6 dark:enabled:hover:bg-white/10 enabled:hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
@@ -340,13 +357,13 @@ function LevelRow({
|
|
|
340
357
|
}}
|
|
341
358
|
title={
|
|
342
359
|
isGround
|
|
343
|
-
? 'This floor stands at grade'
|
|
344
|
-
: 'Seat the building so this floor stands at grade; the floors under it are below grade'
|
|
360
|
+
? i18n._({ id: 'editor.floatingLevelSelector.thisFloorStandsAtGrade', message: 'This floor stands at grade' })
|
|
361
|
+
: i18n._({ id: 'editor.floatingLevelSelector.seatTheBuildingSoThisFloor', message: 'Seat the building so this floor stands at grade; the floors under it are below grade' })
|
|
345
362
|
}
|
|
346
363
|
type="button"
|
|
347
364
|
>
|
|
348
365
|
<ArrowDownToLine className="h-3 w-3" />
|
|
349
|
-
Set as ground floor
|
|
366
|
+
{i18n._({ id: 'editor.floatingLevelSelector.setAsGroundFloor', message: 'Set as ground floor' })}
|
|
350
367
|
</button>
|
|
351
368
|
{onPaste && (
|
|
352
369
|
<button
|
|
@@ -358,7 +375,7 @@ function LevelRow({
|
|
|
358
375
|
type="button"
|
|
359
376
|
>
|
|
360
377
|
<ClipboardPaste className="h-3 w-3" />
|
|
361
|
-
Paste copied selection
|
|
378
|
+
{i18n._({ id: 'editor.floatingLevelSelector.pasteCopiedSelection', message: 'Paste copied selection' })}
|
|
362
379
|
</button>
|
|
363
380
|
)}
|
|
364
381
|
<button
|
|
@@ -368,11 +385,11 @@ function LevelRow({
|
|
|
368
385
|
e.stopPropagation()
|
|
369
386
|
onRequestDelete()
|
|
370
387
|
}}
|
|
371
|
-
title={canDeleteLevel ? 'Delete level' : 'The ground level cannot be deleted'}
|
|
388
|
+
title={canDeleteLevel ? i18n._({ id: 'editor.floatingLevelSelector.deleteLevel', message: 'Delete level' }) : i18n._({ id: 'editor.floatingLevelSelector.theGroundLevelCannotBeDeleted', message: 'The ground level cannot be deleted' })}
|
|
372
389
|
type="button"
|
|
373
390
|
>
|
|
374
391
|
<Trash2 className="h-3 w-3" />
|
|
375
|
-
Delete level
|
|
392
|
+
{i18n._({ id: 'editor.floatingLevelSelector.deleteLevel', message: 'Delete level' })}
|
|
376
393
|
</button>
|
|
377
394
|
</PopoverContent>
|
|
378
395
|
</Popover>
|
|
@@ -455,7 +472,6 @@ function SortableLevelRow({
|
|
|
455
472
|
type GroundLineState = {
|
|
456
473
|
levelId: string
|
|
457
474
|
seated: boolean
|
|
458
|
-
atFoundationTop: boolean
|
|
459
475
|
offset: number
|
|
460
476
|
}
|
|
461
477
|
|
|
@@ -466,9 +482,15 @@ type GroundLineState = {
|
|
|
466
482
|
* a click seats the building.
|
|
467
483
|
*/
|
|
468
484
|
function GroundLine({ ground, onSeat }: { ground: GroundLineState; onSeat: () => void }) {
|
|
485
|
+
const i18n = usePascalI18n()
|
|
469
486
|
const { toDisplay, displayUnit, precision } = useLinearDisplay('m', 2)
|
|
470
487
|
const over = toDisplay(ground.offset)
|
|
471
|
-
const label =
|
|
488
|
+
const label = i18n._({
|
|
489
|
+
id: 'editor.floatingLevelSelector.groundOffset',
|
|
490
|
+
message: 'Ground · {offset}',
|
|
491
|
+
values: { offset: `${over > 0 ? '+' : ''}${over.toFixed(precision)}` },
|
|
492
|
+
})
|
|
493
|
+
const distance = `${Math.abs(over).toFixed(precision)} ${displayUnit}`
|
|
472
494
|
return (
|
|
473
495
|
<div
|
|
474
496
|
className="relative flex items-center gap-1.5 px-2 py-0.5"
|
|
@@ -477,7 +499,7 @@ function GroundLine({ ground, onSeat }: { ground: GroundLineState; onSeat: () =>
|
|
|
477
499
|
{ground.seated ? (
|
|
478
500
|
<span
|
|
479
501
|
className="shrink-0 font-mono text-[10px] text-muted-foreground/70 tabular-nums"
|
|
480
|
-
title=
|
|
502
|
+
title={i18n._({ id: 'editor.floatingLevelSelector.groundLineTheLevelsUnderIt', message: 'Ground line: the levels under it are below grade' })}
|
|
481
503
|
>
|
|
482
504
|
{label}
|
|
483
505
|
</span>
|
|
@@ -485,7 +507,21 @@ function GroundLine({ ground, onSeat }: { ground: GroundLineState; onSeat: () =>
|
|
|
485
507
|
<button
|
|
486
508
|
className="shrink-0 rounded-full px-1.5 font-mono text-[10px] text-amber-400 tabular-nums transition-colors hover:bg-amber-400/10"
|
|
487
509
|
onClick={onSeat}
|
|
488
|
-
title={
|
|
510
|
+
title={
|
|
511
|
+
ground.offset > 0
|
|
512
|
+
? i18n._({
|
|
513
|
+
id: 'editor.floatingLevelSelector.groundFloorOverGrade',
|
|
514
|
+
message:
|
|
515
|
+
'The ground floor stands {distance} over grade. Seat the ground floor 8 in over grade',
|
|
516
|
+
values: { distance },
|
|
517
|
+
})
|
|
518
|
+
: i18n._({
|
|
519
|
+
id: 'editor.floatingLevelSelector.groundFloorUnderGrade',
|
|
520
|
+
message:
|
|
521
|
+
'The ground floor stands {distance} under grade. Seat the ground floor 8 in over grade',
|
|
522
|
+
values: { distance },
|
|
523
|
+
})
|
|
524
|
+
}
|
|
489
525
|
type="button"
|
|
490
526
|
>
|
|
491
527
|
{label}
|
|
@@ -504,6 +540,7 @@ function GroundLine({ ground, onSeat }: { ground: GroundLineState; onSeat: () =>
|
|
|
504
540
|
// ── Unit focus chip: name + × to end focus, only while a unit is focused ────
|
|
505
541
|
|
|
506
542
|
function UnitFocusChip() {
|
|
543
|
+
const i18n = usePascalI18n()
|
|
507
544
|
const focusedUnitId = useViewer((s) => s.focusedUnitId)
|
|
508
545
|
const focusedUnit = useScene((s) => {
|
|
509
546
|
const unit = focusedUnitId ? s.nodes[focusedUnitId] : undefined
|
|
@@ -521,12 +558,12 @@ function UnitFocusChip() {
|
|
|
521
558
|
className="h-2 w-2 shrink-0 rounded-full"
|
|
522
559
|
style={{ backgroundColor: focusedUnit.color }}
|
|
523
560
|
/>
|
|
524
|
-
<span className="truncate">{focusedUnit.name || 'Unit'}</span>
|
|
561
|
+
<span className="truncate">{focusedUnit.name || i18n._({ id: 'editor.floatingLevelSelector.unit', message: 'Unit' })}</span>
|
|
525
562
|
<button
|
|
526
|
-
aria-label=
|
|
563
|
+
aria-label={i18n._({ id: 'editor.floatingLevelSelector.exitUnitFocus', message: 'Exit unit focus' })}
|
|
527
564
|
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-black/6 dark:hover:bg-white/10 hover:text-foreground"
|
|
528
565
|
onClick={() => leaveUnitFocus()}
|
|
529
|
-
title=
|
|
566
|
+
title={i18n._({ id: 'editor.floatingLevelSelector.exitUnitFocus', message: 'Exit unit focus' })}
|
|
530
567
|
type="button"
|
|
531
568
|
>
|
|
532
569
|
<X className="h-3 w-3" />
|
|
@@ -538,6 +575,8 @@ function UnitFocusChip() {
|
|
|
538
575
|
// ── Main component ──────────────────────────────────────────────────────────
|
|
539
576
|
|
|
540
577
|
export function FloatingLevelSelector() {
|
|
578
|
+
const i18n = usePascalI18n()
|
|
579
|
+
const locale = useLocale()
|
|
541
580
|
// Desktop's view bar is a row above the stage; mobile's floats over it.
|
|
542
581
|
const isMobile = useIsMobile()
|
|
543
582
|
const selectedBuildingId = useViewer((s) => s.selection.buildingId)
|
|
@@ -596,7 +635,6 @@ export function FloatingLevelSelector() {
|
|
|
596
635
|
? {
|
|
597
636
|
levelId: seat.levelId,
|
|
598
637
|
seated: seat.seated,
|
|
599
|
-
atFoundationTop: seat.atFoundationTop,
|
|
600
638
|
offset: seat.offset,
|
|
601
639
|
}
|
|
602
640
|
: null
|
|
@@ -616,12 +654,14 @@ export function FloatingLevelSelector() {
|
|
|
616
654
|
if (!resolvedBuildingId) return
|
|
617
655
|
const newLevel = addStoreyOnTop(resolvedBuildingId)
|
|
618
656
|
setSelection({ buildingId: resolvedBuildingId, levelId: newLevel.id })
|
|
657
|
+
offerExteriorWallCopy(newLevel.id)
|
|
619
658
|
}, [resolvedBuildingId, setSelection])
|
|
620
659
|
|
|
621
660
|
const handleAddBelow = useCallback(() => {
|
|
622
661
|
if (!resolvedBuildingId) return
|
|
623
662
|
const newLevel = addStoreyBelow(resolvedBuildingId)
|
|
624
663
|
setSelection({ buildingId: resolvedBuildingId, levelId: newLevel.id })
|
|
664
|
+
offerExteriorWallCopy(newLevel.id)
|
|
625
665
|
}, [resolvedBuildingId, setSelection])
|
|
626
666
|
|
|
627
667
|
const handleInsertBetween = useCallback(
|
|
@@ -645,6 +685,7 @@ export function FloatingLevelSelector() {
|
|
|
645
685
|
.map((l) => ({ id: l.id as AnyNodeId, data: { level: l.level + 1 } as Partial<AnyNode> })),
|
|
646
686
|
})
|
|
647
687
|
setSelection({ buildingId: resolvedBuildingId, levelId: newLevel.id })
|
|
688
|
+
offerExteriorWallCopy(newLevel.id, lower.id)
|
|
648
689
|
},
|
|
649
690
|
[resolvedBuildingId, levels, applyNodeChanges, setSelection],
|
|
650
691
|
)
|
|
@@ -735,7 +776,10 @@ export function FloatingLevelSelector() {
|
|
|
735
776
|
|
|
736
777
|
return (
|
|
737
778
|
<>
|
|
738
|
-
<div
|
|
779
|
+
<div
|
|
780
|
+
className={cn('pointer-events-auto absolute left-3 z-20', isMobile ? 'top-14' : 'top-3')}
|
|
781
|
+
data-surface="level-selector"
|
|
782
|
+
>
|
|
739
783
|
<div className="relative">
|
|
740
784
|
{/* Floating + at top edge: a floor on top, under the roof level when one tops the stack */}
|
|
741
785
|
{!draggingLevelId && (
|
|
@@ -745,7 +789,7 @@ export function FloatingLevelSelector() {
|
|
|
745
789
|
// read only from outside: nothing here depends on it.
|
|
746
790
|
data-guide-target="level-add"
|
|
747
791
|
onClick={handleAddAbove}
|
|
748
|
-
title={topIsRoofLevel ? 'Add a floor under the roof' : 'Add level above'}
|
|
792
|
+
title={topIsRoofLevel ? i18n._({ id: 'editor.floatingLevelSelector.addAFloorUnderTheRoof', message: 'Add a floor under the roof' }) : i18n._({ id: 'editor.floatingLevelSelector.addLevelAbove', message: 'Add level above' })}
|
|
749
793
|
type="button"
|
|
750
794
|
>
|
|
751
795
|
<Plus className="h-2.5 w-2.5" />
|
|
@@ -757,7 +801,7 @@ export function FloatingLevelSelector() {
|
|
|
757
801
|
<button
|
|
758
802
|
className={cn(addButtonClass, 'bottom-0 translate-y-1/2')}
|
|
759
803
|
onClick={handleAddBelow}
|
|
760
|
-
title=
|
|
804
|
+
title={i18n._({ id: 'editor.floatingLevelSelector.addLevelBelow', message: 'Add level below' })}
|
|
761
805
|
type="button"
|
|
762
806
|
>
|
|
763
807
|
<Plus className="h-2.5 w-2.5" />
|
|
@@ -799,7 +843,7 @@ export function FloatingLevelSelector() {
|
|
|
799
843
|
}
|
|
800
844
|
onDuplicate={(preset) => handleDuplicateLevel(level, preset)}
|
|
801
845
|
onPaste={() => handlePasteToLevel(level)}
|
|
802
|
-
isGround={ground?.levelId === level.id && ground.
|
|
846
|
+
isGround={ground?.levelId === level.id && ground.seated}
|
|
803
847
|
onRequestDelete={() => setDeletingLevel(level)}
|
|
804
848
|
onSetGround={() => handleSetGround(level.id)}
|
|
805
849
|
onSelect={() => {
|
|
@@ -816,7 +860,7 @@ export function FloatingLevelSelector() {
|
|
|
816
860
|
<button
|
|
817
861
|
className={cn(addButtonClass, 'bottom-0 translate-y-1/2')}
|
|
818
862
|
onClick={() => handleInsertBetween(sortedIndex - 1)}
|
|
819
|
-
title=
|
|
863
|
+
title={i18n._({ id: 'editor.floatingLevelSelector.insertLevelHere', message: 'Insert level here' })}
|
|
820
864
|
type="button"
|
|
821
865
|
>
|
|
822
866
|
<Plus className="h-2.5 w-2.5" />
|
|
@@ -834,17 +878,25 @@ export function FloatingLevelSelector() {
|
|
|
834
878
|
</DndContext>
|
|
835
879
|
</div>
|
|
836
880
|
<UnitFocusChip />
|
|
881
|
+
<LevelWallCopyPrompt />
|
|
837
882
|
</div>
|
|
838
883
|
|
|
839
884
|
{/* Delete confirmation dialog */}
|
|
840
885
|
<Dialog onOpenChange={(open) => !open && setDeletingLevel(null)} open={!!deletingLevel}>
|
|
841
886
|
<DialogContent showCloseButton={false}>
|
|
842
887
|
<DialogHeader>
|
|
843
|
-
<DialogTitle>Delete level</DialogTitle>
|
|
888
|
+
<DialogTitle>{i18n._({ id: 'editor.floatingLevelSelector.deleteLevel', message: 'Delete level' })}</DialogTitle>
|
|
844
889
|
<DialogDescription>
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
890
|
+
{messageWithSlot(
|
|
891
|
+
i18n,
|
|
892
|
+
/* i18n */ {
|
|
893
|
+
id: 'editor.floatingLevelSelector.deleteLevelConfirm',
|
|
894
|
+
message:
|
|
895
|
+
'Are you sure you want to delete {levelName}? All walls, floors, and objects on this level will be permanently removed.',
|
|
896
|
+
},
|
|
897
|
+
'levelName',
|
|
898
|
+
<strong>{deletingLevel ? localizedLevelName(deletingLevel, i18n, locale) : ''}</strong>,
|
|
899
|
+
)}
|
|
848
900
|
</DialogDescription>
|
|
849
901
|
</DialogHeader>
|
|
850
902
|
<DialogFooter>
|
|
@@ -853,14 +905,14 @@ export function FloatingLevelSelector() {
|
|
|
853
905
|
onClick={() => setDeletingLevel(null)}
|
|
854
906
|
type="button"
|
|
855
907
|
>
|
|
856
|
-
Cancel
|
|
908
|
+
{i18n._({ id: 'editor.floatingLevelSelector.cancel', message: 'Cancel' })}
|
|
857
909
|
</button>
|
|
858
910
|
<button
|
|
859
911
|
className="rounded-full bg-red-600 px-4 py-2 text-sm text-white transition-colors hover:bg-red-700"
|
|
860
912
|
onClick={handleConfirmDelete}
|
|
861
913
|
type="button"
|
|
862
914
|
>
|
|
863
|
-
Delete
|
|
915
|
+
{i18n._({ id: 'editor.floatingLevelSelector.delete', message: 'Delete' })}
|
|
864
916
|
</button>
|
|
865
917
|
</DialogFooter>
|
|
866
918
|
</DialogContent>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { usePascalI18n } from '@pascal-app/core'
|
|
1
2
|
import type { HudTitle } from '../../../lib/hud-title'
|
|
2
3
|
import { ContextualHelperPanel } from './contextual-helper-panel'
|
|
3
4
|
|
|
@@ -9,13 +10,14 @@ interface BuildingHelperProps {
|
|
|
9
10
|
// Rotate is one hint with both keys (R / T) — never two separate
|
|
10
11
|
// counterclockwise / clockwise rows — to match every other placement helper.
|
|
11
12
|
export function BuildingHelper({ showRotate, title = null }: BuildingHelperProps) {
|
|
13
|
+
const i18n = usePascalI18n()
|
|
12
14
|
return (
|
|
13
15
|
<ContextualHelperPanel
|
|
14
16
|
title={title}
|
|
15
17
|
hints={[
|
|
16
|
-
{ keys: ['Left click'], label: 'Place building' },
|
|
17
|
-
...(showRotate ? [{ keys: ['R', 'T'], label: 'Rotate' }] : []),
|
|
18
|
-
{ keys: ['Esc'], label: 'Cancel' },
|
|
18
|
+
{ keys: ['Left click'], label: i18n._({ id: 'editor.helpers.buildingHelper.placeBuilding', message: 'Place building' }) },
|
|
19
|
+
...(showRotate ? [{ keys: ['R', 'T'], label: i18n._({ id: 'editor.helpers.buildingHelper.rotate', message: 'Rotate' }) }] : []),
|
|
20
|
+
{ keys: ['Esc'], label: i18n._({ id: 'editor.helpers.buildingHelper.cancel', message: 'Cancel' }) },
|
|
19
21
|
]}
|
|
20
22
|
/>
|
|
21
23
|
)
|