@pascal-app/editor 1.0.7 → 1.1.1
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 +9 -15
- package/src/components/editor/action-menu-button.tsx +1 -1
- package/src/components/editor/alignment-3d-guide-layer.tsx +1 -1
- package/src/components/editor/custom-camera-controls.tsx +101 -45
- package/src/components/editor/duplicate-node.ts +2 -1
- package/src/components/editor/editor-layout-v2.tsx +1 -1
- package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
- package/src/components/editor/first-person-controls.tsx +20 -1983
- package/src/components/editor/fit-scene-framing.test.ts +318 -0
- package/src/components/editor/fit-scene-framing.ts +142 -0
- package/src/components/editor/floating-action-menu.tsx +5 -3
- package/src/components/editor/floating-building-action-menu.tsx +1 -1
- package/src/components/editor/floor-openings-overlay.tsx +1 -1
- package/src/components/editor/floor-region-controls.tsx +8 -4
- package/src/components/editor/floorplan-navigation-presentation.test.ts +0 -40
- package/src/components/editor/floorplan-navigation-presentation.ts +0 -66
- package/src/components/editor/floorplan-panel.tsx +199 -30
- package/src/components/editor/floorplan-wall-typing-keys.test.tsx +152 -0
- package/src/components/editor/floorplan-wall-typing-keys.ts +79 -0
- package/src/components/editor/follow-pascal.test.tsx +1 -2
- package/src/components/editor/follow-pascal.tsx +1 -1
- package/src/components/editor/footprint-height-handle.tsx +2 -2
- package/src/components/editor/group-actions.ts +1 -2
- package/src/components/editor/group-floating-action-menu.tsx +1 -1
- package/src/components/editor/group-move-3d.ts +1 -2
- package/src/components/editor/group-rotate-handle.tsx +1 -2
- package/src/components/editor/group-transform-shared.ts +3 -1
- package/src/components/editor/handles/use-handle-drag.ts +1 -2
- package/src/components/editor/index.tsx +43 -13
- package/src/components/editor/inspector-extension-actions.tsx +1 -1
- package/src/components/editor/measurement-pill.tsx +1 -1
- package/src/components/editor/node-arrow-handles.tsx +2 -2
- package/src/components/editor/open-wall-ends-3d-layer.tsx +2 -3
- package/src/components/editor/open-wall-ends-hint.tsx +1 -1
- package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
- package/src/components/editor/quick-measurement-card.tsx +1 -1
- package/src/components/editor/room-controls.tsx +10 -6
- package/src/components/editor/room-handles-2d.tsx +3 -3
- package/src/components/editor/room-handles.tsx +4 -5
- package/src/components/editor/room-highlight.tsx +6 -105
- package/src/components/editor/room-labels.test.ts +8 -822
- package/src/components/editor/room-labels.tsx +41 -1670
- package/src/components/editor/room-webgpu-materials.test.ts +1 -1
- package/src/components/editor/selection-manager.tsx +5 -3
- package/src/components/editor/site-edge-labels.tsx +7 -3
- package/src/components/editor/snapshot-capture-overlay.tsx +2 -5
- package/src/components/editor/use-floorplan-background-placement.ts +2 -1
- package/src/components/editor/views/plugins-view.tsx +1 -1
- package/src/components/editor/views/view-bar.tsx +9 -9
- package/src/components/editor/wall-measurement-label.tsx +31 -15
- package/src/components/editor/wall-move-side-handles.tsx +11 -7
- package/src/components/editor/wall-pull-grip.tsx +2 -2
- package/src/components/editor/wall-region-handles.tsx +2 -2
- package/src/components/editor/wall-snap-beacon-layer.tsx +53 -32
- package/src/components/editor-2d/floorplan-group-move.tsx +3 -4
- package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +13 -13
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +2 -3
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +2 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +23 -6
- package/src/components/editor-2d/renderers/floorplan-annotation-layout.test.ts +0 -37
- package/src/components/editor-2d/renderers/floorplan-annotation-layout.ts +0 -30
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +13 -3
- package/src/components/editor-2d/renderers/floorplan-draft-measurement.tsx +7 -1
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +14 -3
- package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.test.tsx +35 -0
- package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.tsx +65 -0
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +30 -10
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +39 -35
- package/src/components/systems/roof/roof-edit-system.test.tsx +15 -1
- package/src/components/systems/roof/roof-edit-system.tsx +33 -21
- package/src/components/tools/distribution/distribution-run-tool.test.tsx +371 -3
- package/src/components/tools/distribution/distribution-run-tool.tsx +151 -33
- package/src/components/tools/item/move-tool.tsx +1 -6
- package/src/components/tools/item/placement-history.test.tsx +1 -1
- package/src/components/tools/item/use-placement-coordinator.tsx +3 -4
- package/src/components/tools/paint-region/wall-paint-region-tool.tsx +7 -3
- package/src/components/tools/registry/move-registry-node-tool.test.ts +42 -1
- package/src/components/tools/registry/move-registry-node-tool.tsx +39 -16
- package/src/components/tools/shared/cursor-sphere.tsx +1 -1
- package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
- package/src/components/tools/shared/placement-box.tsx +2 -6
- package/src/components/tools/shared/placement-coordinate-input.tsx +94 -0
- package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
- package/src/components/tools/shared/segment-angle.ts +0 -4
- package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
- package/src/components/tools/tool-manager.tsx +1 -24
- package/src/components/tools/wall/wall-typed-entry.test.ts +99 -0
- package/src/components/tools/wall/wall-typed-entry.ts +70 -0
- package/src/components/ui/action-menu/camera-actions.tsx +2 -2
- package/src/components/ui/action-menu/control-modes.tsx +1 -1
- package/src/components/ui/action-menu/follow-pascal-pill.tsx +1 -1
- package/src/components/ui/action-menu/index.tsx +3 -3
- package/src/components/ui/action-menu/measurement-control.tsx +3 -3
- package/src/components/ui/action-menu/placement.ts +0 -18
- package/src/components/ui/action-menu/point-ask-button.tsx +1 -1
- package/src/components/ui/action-menu/view-toggles.tsx +5 -5
- package/src/components/ui/controls/finish-swatch.tsx +1 -1
- package/src/components/ui/controls/material-paint-panel.tsx +2 -2
- package/src/components/ui/controls/scene-material-list.tsx +2 -2
- package/src/components/ui/controls/select-control.tsx +1 -1
- package/src/components/ui/controls/slider-control.tsx +18 -6
- package/src/components/ui/controls/terrain-sculpt-panel.tsx +1 -1
- package/src/components/ui/controls/tool-options-panel.tsx +74 -44
- package/src/components/ui/floating-level-selector.tsx +3 -3
- package/src/components/ui/helpers/contextual-helper-panel.tsx +1 -1
- package/src/components/ui/helpers/helper-manager.test.tsx +54 -1
- package/src/components/ui/helpers/helper-manager.tsx +55 -1
- package/src/components/ui/level-duplicate-dialog.tsx +1 -1
- package/src/components/ui/panels/collections/collections-popover.tsx +2 -2
- package/src/components/ui/panels/floor-foundation-panel.tsx +3 -3
- package/src/components/ui/panels/floor-opening-panel.tsx +5 -5
- package/src/components/ui/panels/mobile-selection-bar.tsx +28 -24
- package/src/components/ui/panels/multi-height-mode.tsx +3 -3
- package/src/components/ui/panels/multi-selection-panel.tsx +1 -1
- package/src/components/ui/panels/node-display.ts +1 -1
- package/src/components/ui/panels/panel-manager.tsx +5 -3
- package/src/components/ui/panels/panel-wrapper.tsx +42 -19
- package/src/components/ui/panels/parametric-inspector.tsx +1 -1
- package/src/components/ui/panels/room-construction-rows.tsx +7 -7
- package/src/components/ui/panels/room-name-options.ts +1 -5
- package/src/components/ui/panels/room-openings-field.tsx +1 -1
- package/src/components/ui/panels/room-panel.tsx +7 -9
- package/src/components/ui/primitives/color-dot.tsx +2 -2
- package/src/components/ui/primitives/opacity-control.tsx +2 -2
- package/src/components/ui/primitives/sidebar.tsx +3 -3
- package/src/components/ui/right-stack.test.ts +13 -6
- package/src/components/ui/right-stack.tsx +22 -11
- package/src/components/ui/sidebar/icon-rail.tsx +1 -1
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +1 -1
- package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +2 -2
- package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.tsx +1 -1
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +2 -2
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +1 -1
- package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +2 -6
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +3 -10
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +46 -7
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/tree-node-drag.tsx +14 -5
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -2
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +1 -1
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +3 -3
- package/src/components/ui/sidebar/tab-bar.tsx +1 -1
- package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +2 -2
- package/src/components/ui/wall-type/wall-type-menu.tsx +1 -1
- package/src/components/viewer-overlay.tsx +1 -2
- package/src/hooks/use-auto-frame.ts +1 -2
- package/src/hooks/use-keyboard.ts +1 -2
- package/src/hooks/use-selected-room.ts +5 -7
- package/src/i18n/floor-footprint-name.ts +1 -1
- package/src/i18n/locales/en/messages.po +1036 -306
- package/src/i18n/locales/en/messages.ts +1 -1
- package/src/i18n/locales/en-XA/messages.ts +1 -1
- package/src/i18n/locales/es/messages.po +2267 -439
- package/src/i18n/locales/fr/messages.po +2267 -439
- package/src/i18n/locales/ja/messages.po +2267 -439
- package/src/i18n/locales/zh-CN/messages.po +2267 -439
- package/src/i18n/locales.test.ts +1 -1
- package/src/index.tsx +183 -178
- package/src/lib/below-grade.test.tsx +4 -4
- package/src/lib/camera-facing.test.ts +1 -1
- package/src/lib/camera-facing.ts +2 -3
- package/src/lib/contextual-help.ts +6 -0
- package/src/lib/editor-level-display.ts +1 -1
- package/src/lib/elevation-guides.ts +21 -6
- package/src/lib/elevator-support.ts +1 -72
- package/src/lib/floor-footprints.test.ts +3 -3
- package/src/lib/floor-footprints.ts +1 -9
- package/src/lib/floor-foundation-support.test.ts +8 -3
- package/src/lib/floor-opening-draft.ts +2 -2
- package/src/lib/floor-region-session.ts +1 -1
- package/src/lib/floorplan/annotation-visibility.test.ts +1 -1
- package/src/lib/floorplan/annotation-visibility.ts +1 -1
- package/src/lib/floorplan/floorplan-export.test.ts +1 -1
- package/src/lib/floorplan/floorplan-export.tsx +8 -11
- package/src/lib/floorplan/floorplan-extension.ts +4 -157
- package/src/lib/floorplan/floorplan-mode.ts +2 -1
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +1 -1
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +8 -6
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +3 -4
- package/src/lib/floorplan/geometry.ts +2 -112
- package/src/lib/floorplan/index.ts +0 -9
- package/src/lib/floorplan/items.ts +2 -1
- package/src/lib/floorplan/open-wall-ends.ts +2 -1
- package/src/lib/floorplan/schedules.ts +6 -3
- package/src/lib/floorplan/sheet-export.ts +7 -3
- package/src/lib/floorplan/site-plan/site-parts.ts +7 -1
- package/src/lib/floorplan/site-plan/site-plan-layer.tsx +1 -1
- package/src/lib/follow-pascal-presentation.ts +1 -1
- package/src/lib/gesture-lifecycle.ts +3 -1
- package/src/lib/glb-export.ts +1 -1
- package/src/lib/interaction/scope.ts +1 -0
- package/src/lib/layer-thickness.ts +1 -1
- package/src/lib/level-selection.test.ts +18 -2
- package/src/lib/level-selection.ts +31 -8
- package/src/lib/linear-display.ts +1 -1
- package/src/lib/live-gesture-round-trip.test.ts +1 -1
- package/src/lib/material-paint.ts +1 -1
- package/src/lib/measurement-label.test.ts +1 -1
- package/src/lib/measurements.test.ts +7 -5
- package/src/lib/mezzanine-selection.test.ts +1 -1
- package/src/lib/node-action-movement.test.tsx +115 -0
- package/src/lib/node-action-movement.ts +2 -0
- package/src/lib/paint-region-mode.ts +1 -1
- package/src/lib/placement-metadata.ts +4 -0
- package/src/lib/point-ask/describe-target.ts +6 -8
- package/src/lib/point-ask/region.ts +1 -1
- package/src/lib/point-ask/session.ts +1 -2
- package/src/lib/print-shell-compiler.ts +12 -1
- package/src/lib/print-wall-solids.test.ts +11 -0
- package/src/lib/print-wall-solids.ts +9 -0
- package/src/lib/roof-duplication.test.ts +100 -0
- package/src/lib/roof-duplication.ts +30 -5
- package/src/lib/room-built-on.test.ts +2 -5
- package/src/lib/room-built-on.ts +1 -15
- package/src/lib/room-construction-commands.ts +3 -2
- package/src/lib/room-controls-phase6.test.ts +1 -1
- package/src/lib/room-handle-drag.ts +8 -37
- package/src/lib/room-name-catalog.test.ts +2 -1
- package/src/lib/room-name-messages.test.ts +2 -1
- package/src/lib/room-name-messages.ts +1 -118
- package/src/lib/room-name-storage.test.ts +2 -1
- package/src/lib/room-push-dimensions.test.ts +1 -1
- package/src/lib/room-selection-commands.ts +2 -2
- package/src/lib/room-selection.test.ts +6 -6
- package/src/lib/room-transform-session.ts +1 -2
- package/src/lib/room-zone-routing.ts +2 -2
- package/src/lib/scene-clipboard.ts +2 -1
- package/src/lib/selection-routing.test.ts +0 -28
- package/src/lib/selection-routing.ts +1 -40
- package/src/lib/sfx-bus.ts +8 -32
- package/src/lib/sfx-player.test.ts +72 -1
- package/src/lib/sfx-player.ts +49 -337
- package/src/lib/snapping-mode.test.ts +23 -1
- package/src/lib/viewer-selection.test.ts +38 -10
- package/src/lib/wall-pull.test.ts +1 -1
- package/src/lib/wall-pull.ts +1 -27
- package/src/lib/wall-region-list.ts +3 -1
- package/src/lib/window-interaction.test.ts +4 -2
- package/src/store/use-editor.tsx +22 -26
- package/src/store/use-floorplan-annotation-visibility.ts +5 -5
- package/src/store/use-placement-typing.test.ts +165 -0
- package/src/store/use-placement-typing.ts +186 -0
- package/src/store/use-wall-region-handles-selection.ts +1 -2
- package/src/components/editor/__bench__/room-label-placement.bench.ts +0 -108
- package/src/components/editor/first-person/build-collider-world.test.ts +0 -596
- package/src/components/editor/first-person/build-collider-world.ts +0 -549
- package/src/components/editor/first-person/ground-holes.test.ts +0 -128
- package/src/components/editor/first-person/ground-holes.ts +0 -130
- package/src/components/editor/first-person/mechanism-targets.test.ts +0 -162
- package/src/components/editor/first-person/mechanism-targets.ts +0 -88
- package/src/components/editor/first-person/terrain-collider.test.ts +0 -132
- package/src/components/editor/first-person/terrain-collider.ts +0 -212
- package/src/components/editor/floorplan-camera-sync.test.ts +0 -249
- package/src/components/editor/floorplan-camera-sync.ts +0 -213
- package/src/components/editor/localized-html.tsx +0 -22
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.test.tsx +0 -236
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +0 -708
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +0 -385
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +0 -670
- package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +0 -89
- package/src/components/editor-2d/renderers/floorplan-label-angle.ts +0 -106
- package/src/components/editor-2d/svg-paths.ts +0 -124
- package/src/components/tools/elevator/elevator-defaults.ts +0 -8
- package/src/components/tools/elevator/elevator-tool.tsx +0 -372
- package/src/components/tools/elevator/move-elevator-tool.tsx +0 -262
- package/src/components/ui/action-menu/action-button.tsx +0 -89
- package/src/components/ui/action-menu/follow-pascal-toggle.tsx +0 -42
- package/src/components/ui/primitives/button.tsx +0 -69
- package/src/components/ui/primitives/dropdown-menu.tsx +0 -232
- package/src/components/ui/primitives/popover.tsx +0 -42
- package/src/components/ui/primitives/switch.tsx +0 -29
- package/src/components/ui/primitives/tooltip.tsx +0 -57
- package/src/components/ui/scene-loader.tsx +0 -73
- package/src/components/viewer/floorplan-compass-button.tsx +0 -67
- package/src/components/viewer/floorplan-preview-geometry.test.ts +0 -107
- package/src/components/viewer/floorplan-preview-geometry.ts +0 -281
- package/src/components/viewer/floorplan-preview-navigation.test.ts +0 -43
- package/src/components/viewer/floorplan-preview-navigation.ts +0 -62
- package/src/components/viewer/floorplan-preview-visibility.test.ts +0 -35
- package/src/components/viewer/floorplan-preview-visibility.ts +0 -19
- package/src/components/viewer/floorplan-preview.tsx +0 -1065
- package/src/components/viewer/render-settings-menu.tsx +0 -294
- package/src/components/viewer/sky-control.tsx +0 -710
- package/src/components/viewer/use-viewer-camera-navigation-sync.ts +0 -140
- package/src/components/viewer/use-viewer-ui-sounds.test.ts +0 -15
- package/src/components/viewer/use-viewer-ui-sounds.ts +0 -46
- package/src/components/viewer/viewer-controls-bar.tsx +0 -496
- package/src/components/viewer/viewer-hover-label.tsx +0 -42
- package/src/components/viewer/viewer-rooms.tsx +0 -248
- package/src/components/viewer/viewer-scene-header.tsx +0 -286
- package/src/components/viewer/viewer-selection-manager.tsx +0 -124
- package/src/components/viewer/viewer-stage-modes.test.ts +0 -38
- package/src/components/viewer/viewer-stage-modes.ts +0 -49
- package/src/components/viewer/viewer-stage-switcher.tsx +0 -97
- package/src/components/viewer/viewer-stage.test.tsx +0 -47
- package/src/components/viewer/viewer-stage.tsx +0 -221
- package/src/components/walkthrough-hud.tsx +0 -193
- package/src/hooks/use-mobile.ts +0 -19
- package/src/i18n/level-name.ts +0 -39
- package/src/i18n/scene-theme-name.ts +0 -16
- package/src/lib/below-grade.ts +0 -133
- package/src/lib/camera-pose.test.ts +0 -237
- package/src/lib/camera-pose.ts +0 -172
- package/src/lib/door-interaction.ts +0 -102
- package/src/lib/floorplan/floorplan-extension.test.ts +0 -42
- package/src/lib/floorplan/floorplan-readonly.test.ts +0 -47
- package/src/lib/floorplan/floorplan-readonly.ts +0 -95
- package/src/lib/floorplan/marks.test.ts +0 -213
- package/src/lib/floorplan/marks.ts +0 -238
- package/src/lib/measurement-label.ts +0 -68
- package/src/lib/measurements.ts +0 -225
- package/src/lib/node-display-label.ts +0 -37
- package/src/lib/room-assembly-overlay.test.ts +0 -275
- package/src/lib/room-assembly-overlay.ts +0 -265
- package/src/lib/room-selection.ts +0 -417
- package/src/lib/scene-bounds.test.ts +0 -183
- package/src/lib/scene-bounds.ts +0 -169
- package/src/lib/typing-target.ts +0 -9
- package/src/lib/viewer-selection.ts +0 -595
- package/src/lib/window-interaction.ts +0 -142
- package/src/lib/zone-content.ts +0 -133
- package/src/store/camera-pose-store.test.ts +0 -29
- package/src/store/camera-pose-store.ts +0 -27
- package/src/store/navigation-sync-pose-store.test.ts +0 -34
- package/src/store/navigation-sync-pose-store.ts +0 -27
- package/src/store/use-audio.test.ts +0 -146
- package/src/store/use-audio.tsx +0 -169
- package/src/store/use-first-person-hud.ts +0 -40
- package/src/store/use-follow-pascal.test.ts +0 -62
- package/src/store/use-follow-pascal.ts +0 -53
|
@@ -35,11 +35,11 @@ import {
|
|
|
35
35
|
useWallsBelowSetting,
|
|
36
36
|
} from '../../editor-2d/floorplan-adjacent-walls-layer'
|
|
37
37
|
import { SliderControl } from '../controls/slider-control'
|
|
38
|
-
import { Popover, PopoverContent, PopoverTrigger } from '
|
|
39
|
-
import { Switch } from '
|
|
40
|
-
import { ActionButton } from '
|
|
41
|
-
import { useActionMenuPopupSide } from '
|
|
42
|
-
import { localizedLevelName } from '
|
|
38
|
+
import { Popover, PopoverContent, PopoverTrigger } from '@pascal-app/viewer/ui'
|
|
39
|
+
import { Switch } from '@pascal-app/viewer/ui'
|
|
40
|
+
import { ActionButton } from '@pascal-app/viewer/ui'
|
|
41
|
+
import { useActionMenuPopupSide } from '@pascal-app/viewer/ui'
|
|
42
|
+
import { localizedLevelName } from '@pascal-app/viewer/ui'
|
|
43
43
|
|
|
44
44
|
const MAX_FILE_SIZE = 200 * 1024 * 1024 // 200MB
|
|
45
45
|
const ACCEPTED_FILE_TYPES = '.glb,.gltf,image/jpeg,image/png,image/webp,image/gif'
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '@pascal-app/core'
|
|
14
14
|
import { useState } from 'react'
|
|
15
15
|
import { materialSwatchStyle } from '../../../lib/material-swatch-style'
|
|
16
|
-
import { Popover, PopoverContent, PopoverTrigger } from '
|
|
16
|
+
import { Popover, PopoverContent, PopoverTrigger } from '@pascal-app/viewer/ui'
|
|
17
17
|
import { MaterialPicker } from './material-picker'
|
|
18
18
|
|
|
19
19
|
type Swatch = { label: string; color: string | null; image: string | null; tinted?: boolean }
|
|
@@ -6,8 +6,8 @@ import { useState } from 'react'
|
|
|
6
6
|
import { useMaterialPaintPanelModel } from '../../../lib/material-paint-panel-model'
|
|
7
7
|
import { type PaintMode, usePaintRegionMode } from '../../../lib/paint-region-mode'
|
|
8
8
|
import useEditor, { armMaterialPaint } from '../../../store/use-editor'
|
|
9
|
-
import { Button } from '
|
|
10
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from '
|
|
9
|
+
import { Button } from '@pascal-app/viewer/ui'
|
|
10
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@pascal-app/viewer/ui'
|
|
11
11
|
import { MaterialPicker } from './material-picker'
|
|
12
12
|
import { SceneMaterialList } from './scene-material-list'
|
|
13
13
|
import { SegmentedControl } from './segmented-control'
|
|
@@ -12,9 +12,9 @@ import { Copy, Paintbrush, Pencil, Trash2 } from 'lucide-react'
|
|
|
12
12
|
import { useEffect, useMemo, useState } from 'react'
|
|
13
13
|
import { sceneMaterialUsageCounts } from '../../../lib/scene-material-refs'
|
|
14
14
|
import useEditor from '../../../store/use-editor'
|
|
15
|
-
import { Button } from '
|
|
15
|
+
import { Button } from '@pascal-app/viewer/ui'
|
|
16
16
|
import { Input } from '../primitives/input'
|
|
17
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from '
|
|
17
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@pascal-app/viewer/ui'
|
|
18
18
|
import { MaterialPropertiesEditor } from './material-properties-editor'
|
|
19
19
|
|
|
20
20
|
export function SceneMaterialList({ autoEditId }: { autoEditId?: SceneMaterialId | null }) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { formatNumber, parseLocalizedNumber, useLocale, useScene } from '@pascal-app/core'
|
|
3
|
+
import { formatNumber, parseLocalizedNumber, useLocale, useScene, usePascalI18n } from '@pascal-app/core'
|
|
4
4
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
5
5
|
import { lingoUnitSpec, measurementHint, parseMeasurement } from '../../../lib/measurement-parser'
|
|
6
6
|
import { useLinearDisplay } from '../../../lib/use-linear-display'
|
|
@@ -71,6 +71,7 @@ export function SliderControl({
|
|
|
71
71
|
restoreOnCommit = true,
|
|
72
72
|
mixed = false,
|
|
73
73
|
}: SliderControlProps) {
|
|
74
|
+
const i18n = usePascalI18n()
|
|
74
75
|
const locale = useLocale()
|
|
75
76
|
// Values and bounds stay in meters; gestures and input use the displayed unit.
|
|
76
77
|
const { isImperial, displayUnit, parseUnit, precision, step, toDisplay, toStored } =
|
|
@@ -260,6 +261,12 @@ export function SliderControl({
|
|
|
260
261
|
[],
|
|
261
262
|
)
|
|
262
263
|
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
const cancelOnBlur = () => { if (dragRef.current || editingRef.current) cancelEdit() }
|
|
266
|
+
window.addEventListener('blur', cancelOnBlur)
|
|
267
|
+
return () => window.removeEventListener('blur', cancelOnBlur)
|
|
268
|
+
}, [cancelEdit])
|
|
269
|
+
|
|
263
270
|
useEffect(() => {
|
|
264
271
|
if (!isDragging) return
|
|
265
272
|
const escape = (event: KeyboardEvent) => {
|
|
@@ -392,6 +399,7 @@ export function SliderControl({
|
|
|
392
399
|
</span>
|
|
393
400
|
)}
|
|
394
401
|
<input
|
|
402
|
+
aria-label={typeof label === 'string' ? label : undefined}
|
|
395
403
|
autoFocus
|
|
396
404
|
className="w-14 bg-transparent p-0 text-right font-mono text-foreground outline-none selection:bg-primary/30"
|
|
397
405
|
onBlur={submitValue}
|
|
@@ -418,14 +426,18 @@ export function SliderControl({
|
|
|
418
426
|
)}
|
|
419
427
|
</>
|
|
420
428
|
) : mixed && !isDragging ? (
|
|
421
|
-
<
|
|
429
|
+
<button
|
|
430
|
+
type="button"
|
|
431
|
+
aria-label={typeof label === 'string' ? i18n._({ id: 'editor.controls.sliderControl.editLabel', message: 'Edit {label}', values: { label } }) : undefined}
|
|
422
432
|
className="flex cursor-text items-center text-muted-foreground transition-colors hover:text-foreground"
|
|
423
433
|
onClick={handleValueClick}
|
|
424
434
|
>
|
|
425
|
-
<span className="font-mono tracking-tight">Mixed</span>
|
|
426
|
-
</
|
|
435
|
+
<span className="font-mono tracking-tight">{i18n._({ id: 'editor.controls.sliderControl.mixed', message: 'Mixed' })}</span>
|
|
436
|
+
</button>
|
|
427
437
|
) : (
|
|
428
|
-
<
|
|
438
|
+
<button
|
|
439
|
+
type="button"
|
|
440
|
+
aria-label={typeof label === 'string' ? i18n._({ id: 'editor.controls.sliderControl.editLabel', message: 'Edit {label}', values: { label } }) : undefined}
|
|
429
441
|
className="flex cursor-text items-center text-foreground/60 transition-colors hover:text-foreground"
|
|
430
442
|
onClick={handleValueClick}
|
|
431
443
|
>
|
|
@@ -433,7 +445,7 @@ export function SliderControl({
|
|
|
433
445
|
{formatDisplay(displayValue)}
|
|
434
446
|
</span>
|
|
435
447
|
{displayUnit && <span className="ml-[1px] text-muted-foreground">{displayUnit}</span>}
|
|
436
|
-
</
|
|
448
|
+
</button>
|
|
437
449
|
)}
|
|
438
450
|
</div>
|
|
439
451
|
</div>
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { Mountain, Pipette } from 'lucide-react'
|
|
11
11
|
import { brushRadiusRange, flattenSite, resetSiteTerrain } from '../../../lib/terrain-sculpt'
|
|
12
12
|
import useEditor from '../../../store/use-editor'
|
|
13
|
-
import { Button } from '
|
|
13
|
+
import { Button } from '@pascal-app/viewer/ui'
|
|
14
14
|
import { SegmentedControl } from './segmented-control'
|
|
15
15
|
import { SliderControl } from './slider-control'
|
|
16
16
|
|
|
@@ -34,50 +34,80 @@ function ToolOptionRow({
|
|
|
34
34
|
return (
|
|
35
35
|
<div className="flex flex-col gap-2">
|
|
36
36
|
<div className="px-0.5 font-medium text-muted-foreground text-xs">{option.label}</div>
|
|
37
|
-
|
|
38
|
-
className="
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
37
|
+
{option.input ? (
|
|
38
|
+
<label className="flex items-center gap-2 rounded-lg border border-border px-2 py-1.5">
|
|
39
|
+
<input
|
|
40
|
+
key={value}
|
|
41
|
+
aria-label={option.label}
|
|
42
|
+
className="min-w-0 flex-1 bg-transparent text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
43
|
+
type="number"
|
|
44
|
+
defaultValue={value}
|
|
45
|
+
min={option.input.min}
|
|
46
|
+
max={option.input.max}
|
|
47
|
+
step={option.input.step ?? 'any'}
|
|
48
|
+
onBlur={(event) => {
|
|
49
|
+
const next = event.target.valueAsNumber
|
|
50
|
+
if (
|
|
51
|
+
Number.isFinite(next) &&
|
|
52
|
+
!event.target.validity.rangeUnderflow &&
|
|
53
|
+
!event.target.validity.rangeOverflow
|
|
54
|
+
) {
|
|
55
|
+
option.set(String(next))
|
|
56
|
+
onSelect?.(option, String(next))
|
|
57
|
+
} else event.target.value = value
|
|
58
|
+
}}
|
|
59
|
+
onKeyDown={(event) => {
|
|
60
|
+
if (event.key === 'Enter') event.currentTarget.blur()
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
<span className="text-xs text-muted-foreground">{option.input.unit}</span>
|
|
64
|
+
</label>
|
|
65
|
+
) : (
|
|
66
|
+
<div
|
|
67
|
+
className="grid gap-1.5"
|
|
68
|
+
style={{
|
|
69
|
+
gridTemplateColumns: `repeat(${Math.min(option.choices.length, 3)}, minmax(0, 1fr))`,
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
{option.choices.map((choice) => {
|
|
73
|
+
const selected = active && choice.value === value
|
|
74
|
+
const thumbnail = getChoiceThumbnail?.(option, choice.value)
|
|
75
|
+
return (
|
|
76
|
+
<button
|
|
77
|
+
aria-pressed={selected}
|
|
78
|
+
className={cn(
|
|
79
|
+
'rounded-lg px-2 py-2 text-center font-medium text-xs transition-colors',
|
|
80
|
+
selected
|
|
81
|
+
? 'bg-primary/10 text-primary ring-1 ring-primary/50'
|
|
82
|
+
: 'bg-black/4 text-muted-foreground hover:bg-black/8 hover:text-foreground dark:bg-muted/40 dark:hover:bg-muted',
|
|
83
|
+
)}
|
|
84
|
+
key={choice.value}
|
|
85
|
+
onClick={() => {
|
|
86
|
+
triggerSFX('sfx:menu-click')
|
|
87
|
+
option.set(choice.value)
|
|
88
|
+
onSelect?.(option, choice.value)
|
|
89
|
+
}}
|
|
90
|
+
onMouseEnter={() => triggerSFX('sfx:menu-hover')}
|
|
91
|
+
type="button"
|
|
92
|
+
>
|
|
93
|
+
{thumbnail && (
|
|
94
|
+
<Image
|
|
95
|
+
alt=""
|
|
96
|
+
className={cn(
|
|
97
|
+
'mx-auto mb-1 size-14 object-contain',
|
|
98
|
+
!selected && 'opacity-70 grayscale',
|
|
99
|
+
)}
|
|
100
|
+
height={56}
|
|
101
|
+
src={thumbnail}
|
|
102
|
+
width={56}
|
|
103
|
+
/>
|
|
104
|
+
)}
|
|
105
|
+
{choice.label}
|
|
106
|
+
</button>
|
|
107
|
+
)
|
|
108
|
+
})}
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
81
111
|
{activeChoice && activeChoice.description ? (
|
|
82
112
|
<p className="px-0.5 text-[11px] text-muted-foreground leading-relaxed">
|
|
83
113
|
{activeChoice.description}
|
|
@@ -61,14 +61,14 @@ import {
|
|
|
61
61
|
useLocale,
|
|
62
62
|
usePascalI18n,
|
|
63
63
|
} from '@pascal-app/core'
|
|
64
|
-
import { authoredLevelName, localizedLevelName } from '
|
|
64
|
+
import { authoredLevelName, localizedLevelName } from '@pascal-app/viewer/ui'
|
|
65
65
|
import { messageWithSlot } from '../../i18n/message-slot'
|
|
66
66
|
import { addStoreyBelow, addStoreyOnTop, setGroundFloor } from '../../lib/add-storey'
|
|
67
67
|
import { deleteLevelWithFallbackSelection } from '../../lib/level-selection'
|
|
68
68
|
import { unitMemberLevels, leaveUnitFocus } from '../../lib/units'
|
|
69
69
|
import { useLinearDisplay } from '../../lib/use-linear-display'
|
|
70
70
|
import { cn } from '../../lib/utils'
|
|
71
|
-
import { useIsMobile } from '
|
|
71
|
+
import { useIsMobile } from '@pascal-app/viewer/ui'
|
|
72
72
|
import { ActionButton } from './controls/action-button'
|
|
73
73
|
import { SliderControl } from './controls/slider-control'
|
|
74
74
|
import { LevelDuplicateDialog } from './level-duplicate-dialog'
|
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
DialogHeader,
|
|
82
82
|
DialogTitle,
|
|
83
83
|
} from './primitives/dialog'
|
|
84
|
-
import { Popover, PopoverContent, PopoverTrigger } from '
|
|
84
|
+
import { Popover, PopoverContent, PopoverTrigger } from '@pascal-app/viewer/ui'
|
|
85
85
|
|
|
86
86
|
// ── Inline rename input for a level row ─────────────────────────────────────
|
|
87
87
|
|
|
@@ -23,7 +23,7 @@ import useEditor, { type GridSnapStep } from '../../../store/use-editor'
|
|
|
23
23
|
import useFenceCurveDraft from '../../../store/use-fence-curve-draft'
|
|
24
24
|
import { IconRefGlyph } from '../icon-ref'
|
|
25
25
|
import { ShortcutToken } from '../primitives/shortcut-token'
|
|
26
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from '
|
|
26
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@pascal-app/viewer/ui'
|
|
27
27
|
import { useInRightStack } from '../right-stack'
|
|
28
28
|
|
|
29
29
|
// One muted container holds every row — passive key hints and interactive chips
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
2
|
import { renderToStaticMarkup } from 'react-dom/server'
|
|
3
|
+
import {
|
|
4
|
+
AXIS_MOVE_DRAG_LABEL,
|
|
5
|
+
PATH_POINT_DRAG_LABEL,
|
|
6
|
+
ROTATE_HANDLE_DRAG_LABEL,
|
|
7
|
+
} from '../../../lib/contextual-help'
|
|
3
8
|
import { SELECT_HUD_TITLE, toolHudTitle } from '../../../lib/hud-title'
|
|
4
9
|
import { snapContextOf } from '../../../lib/snapping-mode'
|
|
5
10
|
import { cornerHintRows } from '../../../store/use-corner-snap-hint'
|
|
6
|
-
import { TooltipProvider } from '
|
|
11
|
+
import { TooltipProvider } from '@pascal-app/viewer/ui'
|
|
7
12
|
import { ContextualHelperPanel } from './contextual-helper-panel'
|
|
8
13
|
import {
|
|
14
|
+
mepHandleGesture,
|
|
15
|
+
mepHandleGestureHints,
|
|
9
16
|
mezzanineHints,
|
|
10
17
|
openingHints,
|
|
11
18
|
ROOM_DIVIDE_HINTS,
|
|
@@ -128,6 +135,52 @@ describe('HUD header', () => {
|
|
|
128
135
|
})
|
|
129
136
|
})
|
|
130
137
|
|
|
138
|
+
describe('MEP handle HUD', () => {
|
|
139
|
+
const english = (hints: ReturnType<typeof mepHandleGestureHints>) =>
|
|
140
|
+
hints.map((hint) => (typeof hint.label === 'string' ? hint.label : hint.label.message))
|
|
141
|
+
const drag = (label: string) => ({ nodeId: 'pipe_a', label })
|
|
142
|
+
|
|
143
|
+
test('names a run point drag and an axis slide, and leaves rotate and resize to their own HUDs', () => {
|
|
144
|
+
expect(mepHandleGesture(drag(PATH_POINT_DRAG_LABEL))).toBe('edit-path')
|
|
145
|
+
expect(mepHandleGesture(drag(AXIS_MOVE_DRAG_LABEL))).toBe('move')
|
|
146
|
+
expect(mepHandleGesture(drag(ROTATE_HANDLE_DRAG_LABEL))).toBeNull()
|
|
147
|
+
expect(mepHandleGesture(drag('resize-diameter'))).toBeNull()
|
|
148
|
+
expect(mepHandleGesture(null)).toBeNull()
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test('keeps "Alt: Detach the joint" on screen for the whole run point drag', () => {
|
|
152
|
+
const idle = mepHandleGestureHints('edit-path', false)
|
|
153
|
+
expect(english(idle)).toEqual(['Detach the joint', 'Cancel'])
|
|
154
|
+
expect(idle.map((hint) => hint.keys)).toEqual([['Alt'], ['Esc']])
|
|
155
|
+
expect(idle[0]?.active).toBe(false)
|
|
156
|
+
expect(mepHandleGestureHints('edit-path', true)[0]?.active).toBe(true)
|
|
157
|
+
expect(english(mepHandleGestureHints('move', false))).toEqual(['Cancel'])
|
|
158
|
+
|
|
159
|
+
const html = renderToStaticMarkup(
|
|
160
|
+
<TooltipProvider>
|
|
161
|
+
<ContextualHelperPanel hints={idle} snapContext="polygon" />
|
|
162
|
+
</TooltipProvider>,
|
|
163
|
+
)
|
|
164
|
+
for (const label of ['Detach the joint', 'Cancel', 'Snapping: Grid']) expect(html).toContain(label)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('a handle drag snaps in its kind context, and a rotate arc in angles', () => {
|
|
168
|
+
const contextOf = (handle: string, profile: 'item' | 'structural') =>
|
|
169
|
+
snapContextOf({
|
|
170
|
+
scope: { kind: 'handle-drag', nodeId: 'pipe_a', handle },
|
|
171
|
+
mode: 'select',
|
|
172
|
+
tool: null,
|
|
173
|
+
profileOf: () => undefined,
|
|
174
|
+
profileOfNode: () => profile,
|
|
175
|
+
})
|
|
176
|
+
expect(contextOf(PATH_POINT_DRAG_LABEL, 'structural')).toBe('polygon')
|
|
177
|
+
expect(contextOf(AXIS_MOVE_DRAG_LABEL, 'structural')).toBe('polygon')
|
|
178
|
+
expect(contextOf(AXIS_MOVE_DRAG_LABEL, 'item')).toBe('item')
|
|
179
|
+
expect(contextOf('resize-diameter', 'item')).toBe('item')
|
|
180
|
+
expect(contextOf(ROTATE_HANDLE_DRAG_LABEL, 'item')).toBe('rotation')
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
131
184
|
describe('Cut opening HUD', () => {
|
|
132
185
|
test('names the drag gesture "Draw the opening", not the mezzanine', () => {
|
|
133
186
|
// The drag row folds under "More shortcuts", so read the rows the HUD is given.
|
|
@@ -11,11 +11,13 @@ import {
|
|
|
11
11
|
import { useViewer } from '@pascal-app/viewer'
|
|
12
12
|
import { useEffect, useMemo, useState, useSyncExternalStore } from 'react'
|
|
13
13
|
import { useShallow } from 'zustand/react/shallow'
|
|
14
|
-
import { useIsMobile } from '
|
|
14
|
+
import { useIsMobile } from '@pascal-app/viewer/ui'
|
|
15
15
|
import {
|
|
16
|
+
AXIS_MOVE_DRAG_LABEL,
|
|
16
17
|
type ContextualShortcutHint,
|
|
17
18
|
GROUP_MOVE_DRAG_LABEL,
|
|
18
19
|
GROUP_ROTATE_DRAG_LABEL,
|
|
20
|
+
PATH_POINT_DRAG_LABEL,
|
|
19
21
|
ROTATE_HANDLE_DRAG_LABEL,
|
|
20
22
|
resolveRotateHandleHelpHints,
|
|
21
23
|
resolveSelectModeHelpHints,
|
|
@@ -294,6 +296,40 @@ export const MEZZANINE_GESTURE_HINTS: Record<MezzanineGesture, ContextualShortcu
|
|
|
294
296
|
],
|
|
295
297
|
}
|
|
296
298
|
|
|
299
|
+
export type MepHandleGesture = 'edit-path' | 'move'
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* A drag on an MEP handle — a run point, where Alt detaches the joint, or a
|
|
303
|
+
* fitting, run or run segment slid along an axis — or null. Its HUD names the
|
|
304
|
+
* kind and the gesture, not "Resize".
|
|
305
|
+
*/
|
|
306
|
+
export function mepHandleGesture(
|
|
307
|
+
drag: { nodeId: string; label: string } | null,
|
|
308
|
+
): MepHandleGesture | null {
|
|
309
|
+
if (drag?.label === PATH_POINT_DRAG_LABEL) return 'edit-path'
|
|
310
|
+
if (drag?.label === AXIS_MOVE_DRAG_LABEL) return 'move'
|
|
311
|
+
return null
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function mepHandleGestureHints(
|
|
315
|
+
gesture: MepHandleGesture,
|
|
316
|
+
altPressed: boolean,
|
|
317
|
+
): ContextualShortcutHint[] {
|
|
318
|
+
const cancel: ContextualShortcutHint = {
|
|
319
|
+
keys: ['Esc'],
|
|
320
|
+
label: /* i18n */ { id: 'editor.helpers.helperManager.cancel', message: 'Cancel' },
|
|
321
|
+
}
|
|
322
|
+
if (gesture === 'move') return [cancel]
|
|
323
|
+
return [
|
|
324
|
+
{
|
|
325
|
+
keys: ['Alt'],
|
|
326
|
+
label: /* i18n */ { id: 'editor.helpers.helperManager.detachTheJoint', message: 'Detach the joint' },
|
|
327
|
+
active: altPressed,
|
|
328
|
+
},
|
|
329
|
+
cancel,
|
|
330
|
+
]
|
|
331
|
+
}
|
|
332
|
+
|
|
297
333
|
/**
|
|
298
334
|
* Whether the armed tool's own hints (`toolHints`) win over the node in hand: a registry tool that
|
|
299
335
|
* holds its draft in a placing scope (the roof's footprint, a fitting, a block) says what its clicks
|
|
@@ -340,6 +376,10 @@ export function HelperManager() {
|
|
|
340
376
|
const reshapingNode = useReshapingNode()
|
|
341
377
|
const activeHandleDrag = useActiveHandleDrag()
|
|
342
378
|
const mezzanineHandle = useScene((s) => mezzanineGesture(activeHandleDrag, s.nodes))
|
|
379
|
+
const mepHandle = mepHandleGesture(activeHandleDrag)
|
|
380
|
+
const mepHandleKind = useScene((s) =>
|
|
381
|
+
mepHandle && activeHandleDrag ? s.nodes[activeHandleDrag.nodeId as AnyNodeId]?.type : undefined,
|
|
382
|
+
)
|
|
343
383
|
const selectedIds = useViewer((s) => s.selection.selectedIds)
|
|
344
384
|
const isMobile = useIsMobile()
|
|
345
385
|
const modifiers = useActiveModifierKeys()
|
|
@@ -518,6 +558,20 @@ export function HelperManager() {
|
|
|
518
558
|
)
|
|
519
559
|
}
|
|
520
560
|
|
|
561
|
+
if (mepHandle) {
|
|
562
|
+
return (
|
|
563
|
+
<ContextualHelperPanel
|
|
564
|
+
hints={mepHandleGestureHints(mepHandle, modifiers.alt)}
|
|
565
|
+
snapContext={snapContext}
|
|
566
|
+
title={
|
|
567
|
+
mepHandleKind
|
|
568
|
+
? nodeKindHudTitle(mepHandleKind, mepHandle === 'move' ? 'move' : 'edit')
|
|
569
|
+
: null
|
|
570
|
+
}
|
|
571
|
+
/>
|
|
572
|
+
)
|
|
573
|
+
}
|
|
574
|
+
|
|
521
575
|
// A single-node resize arrow is still an active snapping interaction. Roof
|
|
522
576
|
// width/depth handles opt into grid snapping, so keep the mode and grid-step
|
|
523
577
|
// controls visible for the whole drag instead of falling through to idle
|
|
@@ -4,7 +4,7 @@ import type { LevelNode, MessageDescriptor } from '@pascal-app/core'
|
|
|
4
4
|
import { useEffect, useState } from 'react'
|
|
5
5
|
import type { LevelDuplicatePreset } from '@pascal-app/core/building'
|
|
6
6
|
import { useLocale, usePascalI18n } from '@pascal-app/core'
|
|
7
|
-
import { localizedLevelName } from '
|
|
7
|
+
import { localizedLevelName } from '@pascal-app/viewer/ui'
|
|
8
8
|
import { cn } from '../../lib/utils'
|
|
9
9
|
import {
|
|
10
10
|
Dialog,
|
|
@@ -21,12 +21,12 @@ import {
|
|
|
21
21
|
DropdownMenuContent,
|
|
22
22
|
DropdownMenuItem,
|
|
23
23
|
DropdownMenuTrigger,
|
|
24
|
-
} from '
|
|
24
|
+
} from '@pascal-app/viewer/ui'
|
|
25
25
|
import {
|
|
26
26
|
Popover,
|
|
27
27
|
PopoverContent,
|
|
28
28
|
PopoverTrigger,
|
|
29
|
-
} from '
|
|
29
|
+
} from '@pascal-app/viewer/ui'
|
|
30
30
|
import { cn } from '../../../../lib/utils'
|
|
31
31
|
|
|
32
32
|
interface CollectionsPopoverProps {
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from '@pascal-app/core'
|
|
20
20
|
import { useViewer } from '@pascal-app/viewer'
|
|
21
21
|
import { localizedFloorFootprintName } from '../../../i18n/floor-footprint-name'
|
|
22
|
-
import { localizedLevelName } from '
|
|
22
|
+
import { localizedLevelName } from '@pascal-app/viewer/ui'
|
|
23
23
|
import { useRef, useState } from 'react'
|
|
24
24
|
import {
|
|
25
25
|
applyFloorFoundation,
|
|
@@ -36,11 +36,11 @@ import {
|
|
|
36
36
|
THICK_FLOOR_HINT,
|
|
37
37
|
thickFloorAdvice,
|
|
38
38
|
} from '../../../lib/floor-footprints'
|
|
39
|
-
import { formatLinearMeasurement } from '
|
|
39
|
+
import { formatLinearMeasurement } from '@pascal-app/core'
|
|
40
40
|
import { FinishSwatch } from '../controls/finish-swatch'
|
|
41
41
|
import { MetricControl } from '../controls/metric-control'
|
|
42
42
|
import { SegmentedControl } from '../controls/segmented-control'
|
|
43
|
-
import { Button } from '
|
|
43
|
+
import { Button } from '@pascal-app/viewer/ui'
|
|
44
44
|
import { PanelWrapper } from './panel-wrapper'
|
|
45
45
|
|
|
46
46
|
/** Unpainted finishes, as the plate renders them (nodes' slab slot defaults). */
|
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
finishFloorOpening,
|
|
15
15
|
openingRoom,
|
|
16
16
|
} from '../../../lib/floor-edit-session'
|
|
17
|
-
import { formatAreaLabel } from '
|
|
18
|
-
import { Button } from '
|
|
19
|
-
import { Switch } from '
|
|
17
|
+
import { formatAreaLabel } from '@pascal-app/core'
|
|
18
|
+
import { Button } from '@pascal-app/viewer/ui'
|
|
19
|
+
import { Switch } from '@pascal-app/viewer/ui'
|
|
20
20
|
import { PanelWrapper } from './panel-wrapper'
|
|
21
|
-
import { localizedLevelName } from '
|
|
21
|
+
import { localizedLevelName } from '@pascal-app/viewer/ui'
|
|
22
22
|
import { nodeLabel } from '../../../i18n/node-messages'
|
|
23
|
-
import { displayRoomName } from '
|
|
23
|
+
import { displayRoomName } from '@pascal-app/viewer/ui'
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* A floor (or ceiling) opening: where it is, what it cuts, and the switch for
|
|
@@ -12,8 +12,8 @@ interface MobileSelectionBarProps {
|
|
|
12
12
|
node: AnyNode | null
|
|
13
13
|
label?: string
|
|
14
14
|
icon?: string
|
|
15
|
-
onMove
|
|
16
|
-
onDuplicate
|
|
15
|
+
onMove?: () => void
|
|
16
|
+
onDuplicate?: () => void
|
|
17
17
|
onDelete: () => void
|
|
18
18
|
onEdit: () => void
|
|
19
19
|
}
|
|
@@ -58,28 +58,32 @@ export function MobileSelectionBar({
|
|
|
58
58
|
</button>
|
|
59
59
|
|
|
60
60
|
<div className="flex items-center gap-0.5 border-border/40 border-l pl-1">
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
61
|
+
{onMove && (
|
|
62
|
+
<button
|
|
63
|
+
aria-label={i18n._({ id: 'editor.panels.mobileSelectionBar.move', message: 'Move' })}
|
|
64
|
+
className={ACTION_BTN}
|
|
65
|
+
onClick={(e) => {
|
|
66
|
+
stop(e)
|
|
67
|
+
onMove()
|
|
68
|
+
}}
|
|
69
|
+
type="button"
|
|
70
|
+
>
|
|
71
|
+
<Move className="h-4 w-4" />
|
|
72
|
+
</button>
|
|
73
|
+
)}
|
|
74
|
+
{onDuplicate && (
|
|
75
|
+
<button
|
|
76
|
+
aria-label={i18n._({ id: 'editor.panels.mobileSelectionBar.duplicate', message: 'Duplicate' })}
|
|
77
|
+
className={ACTION_BTN}
|
|
78
|
+
onClick={(e) => {
|
|
79
|
+
stop(e)
|
|
80
|
+
onDuplicate()
|
|
81
|
+
}}
|
|
82
|
+
type="button"
|
|
83
|
+
>
|
|
84
|
+
<Copy className="h-4 w-4" />
|
|
85
|
+
</button>
|
|
86
|
+
)}
|
|
83
87
|
<button
|
|
84
88
|
aria-label={i18n._({ id: 'editor.panels.mobileSelectionBar.delete', message: 'Delete' })}
|
|
85
89
|
className={cn(
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { useViewer } from '@pascal-app/viewer'
|
|
17
17
|
import { useCallback } from 'react'
|
|
18
18
|
import { useShallow } from 'zustand/react/shallow'
|
|
19
|
-
import { formatLinearMeasurement } from '
|
|
19
|
+
import { formatLinearMeasurement } from '@pascal-app/core'
|
|
20
20
|
import { SegmentedControl } from '../controls/segmented-control'
|
|
21
21
|
import { SliderControl } from '../controls/slider-control'
|
|
22
22
|
import {
|
|
@@ -42,7 +42,7 @@ function wallFollowsLevelPatch(wall: WallNode, nodes: Record<string, AnyNode>):
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function effectiveHeight(node: AnyNode, nodes: Record<string, AnyNode>): number {
|
|
45
|
-
if (node.type === 'wall') return getWallEffectiveHeightForNodes(node, nodes)
|
|
45
|
+
if (node.type === 'wall') return getWallEffectiveHeightForNodes(node, nodes, 0)
|
|
46
46
|
if (node.type === 'ceiling') return resolveCeilingHeight(node, nodes)
|
|
47
47
|
const height = (node as { height?: number }).height
|
|
48
48
|
return typeof height === 'number' ? height : 0
|
|
@@ -72,7 +72,7 @@ export function applyMultiHeightMode(
|
|
|
72
72
|
if (isCustom) return {}
|
|
73
73
|
if (node.type === 'ceiling') return { height: ceilingCustomHeight(node, nodes) }
|
|
74
74
|
if (node.type === 'wall') {
|
|
75
|
-
return { height: Math.max(0.1, getWallEffectiveHeightForNodes(node, nodes)) }
|
|
75
|
+
return { height: Math.max(0.1, getWallEffectiveHeightForNodes(node, nodes, 0)) }
|
|
76
76
|
}
|
|
77
77
|
return {}
|
|
78
78
|
}
|
|
@@ -14,7 +14,7 @@ import { useViewer } from '@pascal-app/viewer'
|
|
|
14
14
|
import { Building2, Copy, Group, Merge, Split, Trash2, Ungroup } from 'lucide-react'
|
|
15
15
|
import { useMemo } from 'react'
|
|
16
16
|
import { deleteSelection, duplicateSelectionAndPickUp } from '../../editor/group-actions'
|
|
17
|
-
import { formatLinearMeasurement } from '
|
|
17
|
+
import { formatLinearMeasurement } from '@pascal-app/core'
|
|
18
18
|
import { triggerSFX } from '../../../lib/sfx-bus'
|
|
19
19
|
import { writeWindowChanges } from '../../../lib/window-changes'
|
|
20
20
|
import { collectSelectableCandidateIds } from '../../tools/select/select-candidates'
|