@pascal-app/editor 1.0.6 → 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 +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 +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
|
@@ -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
|
)
|
|
@@ -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 (
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type MessageDescriptor, usePascalI18n } from '@pascal-app/core'
|
|
1
2
|
import { Keyboard } from 'lucide-react'
|
|
2
3
|
import { Button } from './../../../../../components/ui/primitives/button'
|
|
3
4
|
import {
|
|
@@ -12,27 +13,31 @@ import {
|
|
|
12
13
|
ShortcutToken,
|
|
13
14
|
shortcutDisplayValue,
|
|
14
15
|
} from './../../../../../components/ui/primitives/shortcut-token'
|
|
16
|
+
import { TOOL_SHORTCUT_KEYS } from './../../../../../lib/tool-shortcuts'
|
|
15
17
|
|
|
16
18
|
type Shortcut = {
|
|
17
|
-
keys: string[]
|
|
19
|
+
keys: readonly string[]
|
|
18
20
|
action: string
|
|
19
21
|
note?: string
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
type ShortcutCategory = {
|
|
23
|
-
title:
|
|
25
|
+
title: MessageDescriptor
|
|
24
26
|
shortcuts: Shortcut[]
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
28
30
|
{
|
|
29
|
-
title:
|
|
31
|
+
title: /* i18n */ {
|
|
32
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.editorNavigation',
|
|
33
|
+
message: 'Editor Navigation',
|
|
34
|
+
},
|
|
30
35
|
shortcuts: [
|
|
31
36
|
{ keys: ['1'], action: 'Switch to Site' },
|
|
32
37
|
{ keys: ['2'], action: 'Switch to Building' },
|
|
33
38
|
{ keys: ['\\'], action: 'Open or close the split view' },
|
|
34
|
-
{ keys:
|
|
35
|
-
{ keys:
|
|
39
|
+
{ keys: TOOL_SHORTCUT_KEYS.item, action: 'Open the items catalog with the item tool' },
|
|
40
|
+
{ keys: TOOL_SHORTCUT_KEYS.zone, action: 'Draw a zone' },
|
|
36
41
|
{
|
|
37
42
|
keys: ['Cmd/Ctrl', 'Arrow Up'],
|
|
38
43
|
action: 'Select next level in the active building',
|
|
@@ -45,12 +50,21 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
45
50
|
],
|
|
46
51
|
},
|
|
47
52
|
{
|
|
48
|
-
title:
|
|
53
|
+
title: /* i18n */ {
|
|
54
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.modesHistory',
|
|
55
|
+
message: 'Modes & History',
|
|
56
|
+
},
|
|
49
57
|
shortcuts: [
|
|
50
|
-
{ keys:
|
|
51
|
-
{
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
{ keys: TOOL_SHORTCUT_KEYS.select, action: 'Switch to Select mode' },
|
|
59
|
+
{
|
|
60
|
+
keys: TOOL_SHORTCUT_KEYS.wall,
|
|
61
|
+
action: 'Switch to Build mode, drawing with the last Rooms shape',
|
|
62
|
+
note: 'Rectangle, Polygon or Walls, whichever you picked last; Rectangle the first time.',
|
|
63
|
+
},
|
|
64
|
+
{ keys: TOOL_SHORTCUT_KEYS.measurement, action: 'Activate the last measurement tool' },
|
|
65
|
+
{ keys: TOOL_SHORTCUT_KEYS.paint, action: 'Switch to Paint mode' },
|
|
66
|
+
{ keys: TOOL_SHORTCUT_KEYS.terrain, action: 'Sculpt the ground (terrain)' },
|
|
67
|
+
{ keys: TOOL_SHORTCUT_KEYS.delete, action: 'Switch to Delete mode' },
|
|
54
68
|
{
|
|
55
69
|
keys: ['Esc'],
|
|
56
70
|
action: 'Cancel the active tool and return to Select mode',
|
|
@@ -63,7 +77,30 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
63
77
|
],
|
|
64
78
|
},
|
|
65
79
|
{
|
|
66
|
-
title:
|
|
80
|
+
title: /* i18n */ {
|
|
81
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.buildTools',
|
|
82
|
+
message: 'Build Tools',
|
|
83
|
+
},
|
|
84
|
+
shortcuts: [
|
|
85
|
+
{
|
|
86
|
+
keys: TOOL_SHORTCUT_KEYS.door,
|
|
87
|
+
action: 'Place a door',
|
|
88
|
+
note: 'D alone pans the view. While placing, the door swings toward the side of the wall the cursor is on; R flips it.',
|
|
89
|
+
},
|
|
90
|
+
{ keys: TOOL_SHORTCUT_KEYS.window, action: 'Place a window', note: 'W alone pans the view.' },
|
|
91
|
+
{ keys: TOOL_SHORTCUT_KEYS.stair, action: 'Place stairs', note: 'S alone pans the view.' },
|
|
92
|
+
{
|
|
93
|
+
keys: TOOL_SHORTCUT_KEYS.roof,
|
|
94
|
+
action: 'Draw a roof',
|
|
95
|
+
note: "R alone still rotates or flips the selection, and while placing it stays the placement's own R.",
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
title: /* i18n */ {
|
|
101
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.selection',
|
|
102
|
+
message: 'Selection',
|
|
103
|
+
},
|
|
67
104
|
shortcuts: [
|
|
68
105
|
{
|
|
69
106
|
keys: ['Cmd/Ctrl', 'C'],
|
|
@@ -78,8 +115,7 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
78
115
|
{
|
|
79
116
|
keys: ['Cmd/Ctrl', 'V'],
|
|
80
117
|
action: 'Paste and place copied objects',
|
|
81
|
-
note:
|
|
82
|
-
'Carries a preview under the cursor. Click to place it, or press Escape to cancel.',
|
|
118
|
+
note: 'Carries a preview under the cursor. Click to place it, or press Escape to cancel.',
|
|
83
119
|
},
|
|
84
120
|
{
|
|
85
121
|
keys: ['Cmd/Ctrl', 'Left click'],
|
|
@@ -105,14 +141,23 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
105
141
|
{
|
|
106
142
|
keys: ['Cmd/Ctrl', 'G'],
|
|
107
143
|
action: 'Group the multi-selection (session only)',
|
|
108
|
-
note:
|
|
109
|
-
'Editor-only. Plain click a member later to reselect the whole group. Not saved with the project.',
|
|
144
|
+
note: 'Editor-only. Plain click a member later to reselect the whole group. Not saved with the project.',
|
|
110
145
|
},
|
|
111
146
|
{
|
|
112
147
|
keys: ['Cmd/Ctrl', 'Shift', 'G'],
|
|
113
148
|
action: 'Ungroup the session selection',
|
|
114
149
|
note: 'Keeps the current selection; only dissolves the session group.',
|
|
115
150
|
},
|
|
151
|
+
{
|
|
152
|
+
keys: ['Arrow keys'],
|
|
153
|
+
action: 'Nudge the selected wall 1 in (1 cm in metric)',
|
|
154
|
+
note: 'In the floor plan. Stops where it lines up with a parallel wall; joined walls follow.',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
keys: ['Tab'],
|
|
158
|
+
action: 'Select the next object under the pointer',
|
|
159
|
+
note: 'In the floor plan, for walls that overlap or hide under another. Shift+Tab goes back.',
|
|
160
|
+
},
|
|
116
161
|
{
|
|
117
162
|
keys: ['Esc'],
|
|
118
163
|
action: 'Clear the selection',
|
|
@@ -121,7 +166,10 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
121
166
|
],
|
|
122
167
|
},
|
|
123
168
|
{
|
|
124
|
-
title:
|
|
169
|
+
title: /* i18n */ {
|
|
170
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.directManipulation',
|
|
171
|
+
message: 'Direct Manipulation',
|
|
172
|
+
},
|
|
125
173
|
shortcuts: [
|
|
126
174
|
{
|
|
127
175
|
keys: ['Cmd/Ctrl', 'Left click'],
|
|
@@ -141,7 +189,10 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
141
189
|
],
|
|
142
190
|
},
|
|
143
191
|
{
|
|
144
|
-
title:
|
|
192
|
+
title: /* i18n */ {
|
|
193
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.drawingTools',
|
|
194
|
+
message: 'Drawing Tools',
|
|
195
|
+
},
|
|
145
196
|
shortcuts: [
|
|
146
197
|
// Shift and Ctrl each mean one thing held and another tapped, and only
|
|
147
198
|
// the hold was documented — which read as the taps not existing. Both
|
|
@@ -157,9 +208,9 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
157
208
|
note: 'Tap and release on its own. Use it when the default half-metre grid is too coarse — placing a window, for instance.',
|
|
158
209
|
},
|
|
159
210
|
{
|
|
160
|
-
keys: ['
|
|
161
|
-
action: 'Bypass
|
|
162
|
-
note: 'Hold during the active gesture. Passive guide or measurement feedback may stay visible.',
|
|
211
|
+
keys: ['Alt'],
|
|
212
|
+
action: 'Bypass snapping, angle constraints and collisions',
|
|
213
|
+
note: 'Hold during the active gesture; release to snap again. Passive guide or measurement feedback may stay visible.',
|
|
163
214
|
},
|
|
164
215
|
{
|
|
165
216
|
keys: ['Shift'],
|
|
@@ -169,7 +220,10 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
169
220
|
],
|
|
170
221
|
},
|
|
171
222
|
{
|
|
172
|
-
title:
|
|
223
|
+
title: /* i18n */ {
|
|
224
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.itemPlacement',
|
|
225
|
+
message: 'Item Placement',
|
|
226
|
+
},
|
|
173
227
|
shortcuts: [
|
|
174
228
|
{
|
|
175
229
|
keys: ['R', 'T'],
|
|
@@ -177,7 +231,8 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
177
231
|
},
|
|
178
232
|
{
|
|
179
233
|
keys: ['E'],
|
|
180
|
-
action:
|
|
234
|
+
action:
|
|
235
|
+
'Operate the selected node — doors, windows, and cabinet doors/drawers animate open/closed',
|
|
181
236
|
},
|
|
182
237
|
{
|
|
183
238
|
keys: ['Shift'],
|
|
@@ -187,12 +242,15 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
187
242
|
],
|
|
188
243
|
},
|
|
189
244
|
{
|
|
190
|
-
title:
|
|
245
|
+
title: /* i18n */ {
|
|
246
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.camera',
|
|
247
|
+
message: 'Camera',
|
|
248
|
+
},
|
|
191
249
|
shortcuts: [
|
|
192
250
|
{
|
|
193
251
|
keys: ['W', 'A', 'S', 'D'],
|
|
194
252
|
action: 'Pan camera',
|
|
195
|
-
note: 'Moves in screen space, similar to dragging the camera view.',
|
|
253
|
+
note: 'Moves in screen space, similar to dragging the camera view. With Shift, W, S and D place a window, stairs and a door instead.',
|
|
196
254
|
},
|
|
197
255
|
{
|
|
198
256
|
keys: ['Middle click'],
|
|
@@ -208,7 +266,7 @@ const SHORTCUT_CATEGORIES: ShortcutCategory[] = [
|
|
|
208
266
|
},
|
|
209
267
|
]
|
|
210
268
|
|
|
211
|
-
function ShortcutKeys({ keys }: { keys: string[] }) {
|
|
269
|
+
function ShortcutKeys({ keys }: { keys: readonly string[] }) {
|
|
212
270
|
return (
|
|
213
271
|
<div className="flex flex-wrap items-center gap-1">
|
|
214
272
|
{keys.map((key, index) => (
|
|
@@ -222,32 +280,44 @@ function ShortcutKeys({ keys }: { keys: string[] }) {
|
|
|
222
280
|
}
|
|
223
281
|
|
|
224
282
|
export function KeyboardShortcutsDialog() {
|
|
283
|
+
const i18n = usePascalI18n()
|
|
225
284
|
return (
|
|
226
285
|
<Dialog>
|
|
227
286
|
<DialogTrigger asChild>
|
|
228
287
|
<Button className="w-full justify-start gap-2" variant="outline">
|
|
229
288
|
<Keyboard className="size-4" />
|
|
230
|
-
|
|
289
|
+
{i18n._({
|
|
290
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.keyboardShortcuts',
|
|
291
|
+
message: 'Keyboard shortcuts',
|
|
292
|
+
})}
|
|
231
293
|
</Button>
|
|
232
294
|
</DialogTrigger>
|
|
233
295
|
<DialogContent className="flex max-h-[85vh] flex-col overflow-hidden p-0 sm:max-w-3xl">
|
|
234
296
|
<DialogHeader className="shrink-0 border-b px-6 py-4">
|
|
235
|
-
<DialogTitle>
|
|
297
|
+
<DialogTitle>
|
|
298
|
+
{i18n._({
|
|
299
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.keyboardShortcuts',
|
|
300
|
+
message: 'Keyboard shortcuts',
|
|
301
|
+
})}
|
|
302
|
+
</DialogTitle>
|
|
236
303
|
<DialogDescription>
|
|
237
|
-
|
|
238
|
-
|
|
304
|
+
{i18n._({
|
|
305
|
+
id: 'editor.sidebar.panels.settingsPanel.keyboardShortcutsDialog.shortcutsAreContextAwareGuidedConstraints',
|
|
306
|
+
message:
|
|
307
|
+
'Shortcuts are context-aware. Snapping is a visible mode: tap Shift to cycle it, hold Alt during a gesture to place freely.',
|
|
308
|
+
})}
|
|
239
309
|
</DialogDescription>
|
|
240
310
|
</DialogHeader>
|
|
241
311
|
|
|
242
312
|
<div className="flex-1 space-y-5 overflow-y-auto px-6 py-4">
|
|
243
313
|
{SHORTCUT_CATEGORIES.map((category) => (
|
|
244
|
-
<section className="space-y-2" key={category.title}>
|
|
245
|
-
<h3 className="font-medium text-sm">{category.title}</h3>
|
|
314
|
+
<section className="space-y-2" key={category.title.id}>
|
|
315
|
+
<h3 className="font-medium text-sm">{i18n._(category.title)}</h3>
|
|
246
316
|
<div className="overflow-hidden rounded-md border border-border/80">
|
|
247
317
|
{category.shortcuts.map((shortcut, index) => (
|
|
248
318
|
<div
|
|
249
319
|
className="grid grid-cols-[minmax(130px,220px)_1fr] gap-3 px-3 py-2"
|
|
250
|
-
key={`${category.title}-${shortcut.action}`}
|
|
320
|
+
key={`${category.title.id}-${shortcut.action}`}
|
|
251
321
|
>
|
|
252
322
|
<ShortcutKeys keys={shortcut.keys} />
|
|
253
323
|
<div>
|