@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,16 +1,11 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { Icon } from '@iconify/react'
|
|
4
3
|
import {
|
|
5
4
|
type AnyNode,
|
|
6
5
|
type AnyNodeId,
|
|
7
|
-
getInspectorExtensions,
|
|
8
|
-
type IconRef,
|
|
9
6
|
type InspectorExtension,
|
|
10
|
-
|
|
11
|
-
useScene,
|
|
7
|
+
useScene, usePascalI18n,
|
|
12
8
|
} from '@pascal-app/core'
|
|
13
|
-
import { useViewer } from '@pascal-app/viewer'
|
|
14
9
|
import { ChevronDown, ChevronLeft, GripHorizontal, RotateCcw, X } from 'lucide-react'
|
|
15
10
|
import {
|
|
16
11
|
type ComponentType,
|
|
@@ -21,18 +16,12 @@ import {
|
|
|
21
16
|
useContext,
|
|
22
17
|
useEffect,
|
|
23
18
|
useLayoutEffect,
|
|
24
|
-
useMemo,
|
|
25
19
|
useRef,
|
|
26
20
|
useState,
|
|
27
21
|
} from 'react'
|
|
28
22
|
import { useIsMobile } from '../../../hooks/use-mobile'
|
|
29
|
-
import { IconRefImage } from '../icon-ref'
|
|
30
23
|
import { NodeIconImage } from '../node-icon-image'
|
|
31
|
-
import {
|
|
32
|
-
resolveActiveExtension,
|
|
33
|
-
toggleCard,
|
|
34
|
-
toggleExtension,
|
|
35
|
-
} from '../../../lib/inspector-card-mode'
|
|
24
|
+
import { resolveActiveExtension, toggleCard } from '../../../lib/inspector-card-mode'
|
|
36
25
|
import { cn } from '../../../lib/utils'
|
|
37
26
|
import { useInspectorExpanded } from '../../../lib/inspector-expanded'
|
|
38
27
|
import { ActionButton, ActionGroup } from '../controls/action-button'
|
|
@@ -40,6 +29,7 @@ import { PanelSection } from '../controls/panel-section'
|
|
|
40
29
|
import { ErrorBoundary } from '../primitives/error-boundary'
|
|
41
30
|
import { useInRightStack } from '../right-stack'
|
|
42
31
|
import { CollectionsPopover } from './collections/collections-popover'
|
|
32
|
+
import { renderExtensionIcon, useInspectorExtensions } from './inspector-extensions'
|
|
43
33
|
|
|
44
34
|
const DRAG_MARGIN = 8
|
|
45
35
|
// Pointer travel (px) below which a header press is treated as a click
|
|
@@ -119,6 +109,7 @@ export function PanelWrapper({
|
|
|
119
109
|
className,
|
|
120
110
|
width = 320, // default width
|
|
121
111
|
}: PanelWrapperProps) {
|
|
112
|
+
const i18n = usePascalI18n()
|
|
122
113
|
const isMobile = useIsMobile()
|
|
123
114
|
const inStack = useInRightStack()
|
|
124
115
|
const contextFooter = useContext(InspectorFooterContext)
|
|
@@ -131,35 +122,16 @@ export function PanelWrapper({
|
|
|
131
122
|
// The wrapper self-resolves the selected node instead of taking a prop:
|
|
132
123
|
// kind-owned `customPanel`s (wall, slab, …) render their own
|
|
133
124
|
// <PanelWrapper>, so this is the one spot every inspector card flows
|
|
134
|
-
// through. Extensions only apply to a single-node selection
|
|
135
|
-
|
|
136
|
-
const selectedId =
|
|
137
|
-
s.selection.selectedIds.length === 1 ? s.selection.selectedIds[0] : undefined,
|
|
138
|
-
) as AnyNodeId | undefined
|
|
139
|
-
// Subscribe to the selected node's *type* only — a string primitive that
|
|
140
|
-
// doesn't change as fields are edited (same trick as ParametricInspector).
|
|
141
|
-
const selectedType = useScene((s) => (selectedId ? s.nodes[selectedId]?.type ?? null : null))
|
|
142
|
-
const installedPlugins = useScene((s) => s.installedPlugins)
|
|
143
|
-
const extensions = useMemo(() => {
|
|
144
|
-
// re-derive when plugin extensions register after mount (async plugin load)
|
|
145
|
-
void registryVersion
|
|
146
|
-
if (!selectedType) return []
|
|
147
|
-
return getInspectorExtensions(selectedType).filter(
|
|
148
|
-
(extension) => !extension.pluginId || installedPlugins.includes(extension.pluginId),
|
|
149
|
-
)
|
|
150
|
-
}, [selectedType, installedPlugins, registryVersion])
|
|
125
|
+
// through. Extensions only apply to a single-node selection; the action
|
|
126
|
+
// menu opens them (`InspectorExtensionActions`).
|
|
127
|
+
const { selectedId, extensions, primaryExtension } = useInspectorExtensions()
|
|
151
128
|
|
|
152
129
|
// Which extension's content fills the card body (extension mode). The two
|
|
153
130
|
// expanded modes are EITHER/OR: extension mode replaces the regular
|
|
154
131
|
// controls; null shows the regular controls with no extension sections.
|
|
155
|
-
// See `lib/inspector-card-mode.ts` for the transition table.
|
|
156
|
-
const [activeExtensionId, setActiveExtensionId] = useState<string | null>(null)
|
|
157
132
|
// Stale ids (kind changed, plugin gated off) fall back to regular mode.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return node ? extensions.find((extension) => extension.primaryWhen?.(node))?.id : undefined
|
|
161
|
-
})
|
|
162
|
-
const primaryExtension = extensions.find((extension) => extension.id === primaryExtensionId)
|
|
133
|
+
// See `lib/inspector-card-mode.ts` for the transition table.
|
|
134
|
+
const activeExtensionId = useInspectorExpanded((state) => state.activeExtensionId)
|
|
163
135
|
const activeExtension = primaryExtension ?? resolveActiveExtension(activeExtensionId, extensions)
|
|
164
136
|
|
|
165
137
|
// The panel is collapsed to just its header until the user expands it. The
|
|
@@ -174,14 +146,6 @@ export function PanelWrapper({
|
|
|
174
146
|
if (primaryExtension) setCollapsed(false)
|
|
175
147
|
}, [primaryExtension, selectedId, setCollapsed])
|
|
176
148
|
|
|
177
|
-
const applyMode = useCallback(
|
|
178
|
-
(next: { collapsed: boolean; activeExtensionId: string | null }) => {
|
|
179
|
-
setCollapsed(next.collapsed)
|
|
180
|
-
setActiveExtensionId(next.activeExtensionId)
|
|
181
|
-
},
|
|
182
|
-
[setCollapsed],
|
|
183
|
-
)
|
|
184
|
-
|
|
185
149
|
// Chevron / header press — collapsed → regular, regular → collapsed,
|
|
186
150
|
// extension mode → regular (exit the extension first, stay expanded).
|
|
187
151
|
const handleCardToggle = useCallback(() => {
|
|
@@ -189,14 +153,12 @@ export function PanelWrapper({
|
|
|
189
153
|
setCollapsed(!collapsed)
|
|
190
154
|
return
|
|
191
155
|
}
|
|
192
|
-
applyMode(toggleCard({ collapsed, activeExtensionId }))
|
|
193
|
-
}, [
|
|
156
|
+
useInspectorExpanded.getState().applyMode(toggleCard({ collapsed, activeExtensionId }))
|
|
157
|
+
}, [collapsed, activeExtensionId, primaryExtension, setCollapsed])
|
|
194
158
|
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
useEffect(() => {
|
|
198
|
-
if (collapsed) setActiveExtensionId(null)
|
|
199
|
-
}, [collapsed])
|
|
159
|
+
// Extension mode is an affordance of the card it was opened on: a card that
|
|
160
|
+
// closes (selection cleared or changed kind) leaves the next one regular.
|
|
161
|
+
useEffect(() => () => useInspectorExpanded.setState({ activeExtensionId: null }), [])
|
|
200
162
|
|
|
201
163
|
// Drag-to-reposition from the header. `offset` is a translation applied on
|
|
202
164
|
// top of the default `top-20 right-4` anchor; null until first dragged.
|
|
@@ -341,6 +303,7 @@ export function PanelWrapper({
|
|
|
341
303
|
<div className={cn('flex min-w-0 items-center gap-2', titleContent && 'flex-1 pr-2')}>
|
|
342
304
|
{onBack && (
|
|
343
305
|
<button
|
|
306
|
+
aria-label={i18n._({ id: 'editor.panels.panelWrapper.back', message: 'Back' })}
|
|
344
307
|
className="mr-1 flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground"
|
|
345
308
|
onClick={onBack}
|
|
346
309
|
type="button"
|
|
@@ -388,6 +351,7 @@ export function PanelWrapper({
|
|
|
388
351
|
<div className="flex items-center gap-1">
|
|
389
352
|
{onReset && (
|
|
390
353
|
<button
|
|
354
|
+
aria-label={i18n._({ id: 'editor.panels.panelWrapper.reset', message: 'Reset' })}
|
|
391
355
|
className="flex h-7 w-7 items-center justify-center rounded-md bg-black/4 dark:bg-[#2C2C2E] text-muted-foreground transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground"
|
|
392
356
|
onClick={onReset}
|
|
393
357
|
type="button"
|
|
@@ -395,37 +359,9 @@ export function PanelWrapper({
|
|
|
395
359
|
<RotateCcw className="h-4 w-4" />
|
|
396
360
|
</button>
|
|
397
361
|
)}
|
|
398
|
-
{/* Extension mode buttons — one icon per registered inspector
|
|
399
|
-
extension, left of the chevron. Click swaps the card body to
|
|
400
|
-
ONLY that extension's content (either/or with the regular
|
|
401
|
-
controls); the active icon (highlighted) or the chevron
|
|
402
|
-
returns to the regular controls. */}
|
|
403
|
-
{extensions.filter((extension) => extension !== primaryExtension).map((extension) => {
|
|
404
|
-
const isActive = !collapsed && activeExtensionId === extension.id
|
|
405
|
-
return (
|
|
406
|
-
<button
|
|
407
|
-
aria-label={isActive ? `Close ${extension.title}` : `Open ${extension.title}`}
|
|
408
|
-
aria-pressed={isActive}
|
|
409
|
-
className={cn(
|
|
410
|
-
'flex h-7 w-7 items-center justify-center rounded-md transition-colors',
|
|
411
|
-
isActive
|
|
412
|
-
? 'bg-cyan-500/20 text-cyan-700 dark:text-cyan-400 hover:bg-cyan-500/30'
|
|
413
|
-
: 'bg-black/4 dark:bg-[#2C2C2E] text-muted-foreground hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground',
|
|
414
|
-
)}
|
|
415
|
-
key={extension.id}
|
|
416
|
-
onClick={() =>
|
|
417
|
-
applyMode(toggleExtension({ collapsed, activeExtensionId }, extension.id))
|
|
418
|
-
}
|
|
419
|
-
title={extension.title}
|
|
420
|
-
type="button"
|
|
421
|
-
>
|
|
422
|
-
{renderExtensionIcon(extension.icon)}
|
|
423
|
-
</button>
|
|
424
|
-
)
|
|
425
|
-
})}
|
|
426
362
|
<button
|
|
427
363
|
aria-expanded={!collapsed}
|
|
428
|
-
aria-label={collapsed ? 'Expand panel' : 'Collapse panel'}
|
|
364
|
+
aria-label={collapsed ? i18n._({ id: 'editor.panels.panelWrapper.expandPanel', message: 'Expand panel' }) : i18n._({ id: 'editor.panels.panelWrapper.collapsePanel', message: 'Collapse panel' })}
|
|
429
365
|
className="flex h-7 w-7 items-center justify-center rounded-md bg-black/4 dark:bg-[#2C2C2E] text-muted-foreground transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground"
|
|
430
366
|
onClick={handleCardToggle}
|
|
431
367
|
type="button"
|
|
@@ -436,6 +372,7 @@ export function PanelWrapper({
|
|
|
436
372
|
</button>
|
|
437
373
|
{onClose && (
|
|
438
374
|
<button
|
|
375
|
+
aria-label={i18n._({ id: 'editor.panels.panelWrapper.close', message: 'Close' })}
|
|
439
376
|
className="flex h-7 w-7 items-center justify-center rounded-md bg-black/4 dark:bg-[#2C2C2E] text-muted-foreground transition-colors hover:bg-black/8 dark:hover:bg-[#3e3e3e] hover:text-foreground"
|
|
440
377
|
onClick={onClose}
|
|
441
378
|
type="button"
|
|
@@ -473,10 +410,10 @@ export function PanelWrapper({
|
|
|
473
410
|
)}
|
|
474
411
|
{children}
|
|
475
412
|
{inspectedIds.length > 0 && (
|
|
476
|
-
<PanelSection title=
|
|
413
|
+
<PanelSection title={i18n._({ id: 'editor.panels.panelWrapper.collections', message: 'Collections' })}>
|
|
477
414
|
<ActionGroup>
|
|
478
415
|
<CollectionsPopover nodeIds={inspectedIds}>
|
|
479
|
-
<ActionButton label=
|
|
416
|
+
<ActionButton label={i18n._({ id: 'editor.panels.panelWrapper.manageCollections', message: 'Manage collections…' })} />
|
|
480
417
|
</CollectionsPopover>
|
|
481
418
|
</ActionGroup>
|
|
482
419
|
</PanelSection>
|
|
@@ -507,30 +444,6 @@ export function PanelWrapper({
|
|
|
507
444
|
|
|
508
445
|
// ─── Plugin inspector extensions ──────────────────────────────────────
|
|
509
446
|
|
|
510
|
-
/** 16px icon for an extension's header button — mirrors the parametric
|
|
511
|
-
* inspector's `renderIcon` (plain <img> so no next/image server deps). */
|
|
512
|
-
function renderExtensionIcon(ref: IconRef): React.ReactNode {
|
|
513
|
-
if (ref.kind === 'url') {
|
|
514
|
-
return <IconRefImage className="h-4 w-4 shrink-0" src={ref.src} />
|
|
515
|
-
}
|
|
516
|
-
if (ref.kind === 'iconify') {
|
|
517
|
-
return <Icon height={16} icon={ref.name} width={16} />
|
|
518
|
-
}
|
|
519
|
-
if (ref.kind === 'svg') {
|
|
520
|
-
return (
|
|
521
|
-
<svg height={16} viewBox={ref.viewBox} width={16}>
|
|
522
|
-
<path d={ref.path} fill="currentColor" />
|
|
523
|
-
</svg>
|
|
524
|
-
)
|
|
525
|
-
}
|
|
526
|
-
const LazyIcon = lazy(ref.module)
|
|
527
|
-
return (
|
|
528
|
-
<Suspense fallback={null}>
|
|
529
|
-
<LazyIcon />
|
|
530
|
-
</Suspense>
|
|
531
|
-
)
|
|
532
|
-
}
|
|
533
|
-
|
|
534
447
|
// `React.lazy` once per loader so the resolved component keeps a stable
|
|
535
448
|
// identity across renders — same WeakMap pattern as `resolveCustomPanel`
|
|
536
449
|
// in parametric-inspector.tsx.
|
|
@@ -582,14 +495,14 @@ function InspectorExtensionContent({
|
|
|
582
495
|
extension: InspectorExtension
|
|
583
496
|
nodeId: AnyNodeId
|
|
584
497
|
}) {
|
|
498
|
+
const i18n = usePascalI18n()
|
|
585
499
|
const node = useScene((s) => s.nodes[nodeId])
|
|
586
500
|
if (!node) return null
|
|
587
501
|
const Extension = resolveExtensionComponent(extension)
|
|
588
502
|
return (
|
|
589
503
|
<ErrorBoundary fallback={
|
|
590
504
|
<p className="p-1 text-muted-foreground text-xs">
|
|
591
|
-
“{
|
|
592
|
-
</p>
|
|
505
|
+
{i18n._({ id: 'editor.panels.panelWrapper.titleHitAnErrorAndWas', message: '“{title}” hit an error and was unloaded for this session.', values: { title: extension.title } })}</p>
|
|
593
506
|
}>
|
|
594
507
|
<Suspense fallback={null}>
|
|
595
508
|
<Extension node={node} />
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { usePascalI18n } from '@pascal-app/core'
|
|
3
4
|
import type { AnyNode, ParamField } from '@pascal-app/core'
|
|
4
5
|
import { SegmentedControl } from '../controls/segmented-control'
|
|
5
6
|
import { SliderControl } from '../controls/slider-control'
|
|
6
7
|
import { ToggleControl } from '../controls/toggle-control'
|
|
7
|
-
import {
|
|
8
|
+
import { inspectorFieldDescriptor, inspectorOptionDescriptor } from '../../../i18n/node-messages'
|
|
9
|
+
import { precisionForStep } from './parametric-field-utils'
|
|
8
10
|
|
|
9
11
|
interface ParametricFieldControlProps {
|
|
10
12
|
field: ParamField<AnyNode>
|
|
@@ -21,16 +23,17 @@ export function ParametricFieldControl({
|
|
|
21
23
|
onChange,
|
|
22
24
|
onCommit,
|
|
23
25
|
}: ParametricFieldControlProps) {
|
|
26
|
+
const i18n = usePascalI18n()
|
|
24
27
|
const key = String(field.key)
|
|
25
28
|
|
|
26
29
|
switch (field.kind) {
|
|
27
30
|
case 'number': {
|
|
28
|
-
const num = typeof value === 'number' ? value : 0
|
|
31
|
+
const num = typeof value === 'number' ? value : (field.default ?? 0)
|
|
29
32
|
const step = field.step ?? 0.01
|
|
30
33
|
const precision = precisionForStep(step)
|
|
31
34
|
return (
|
|
32
35
|
<SliderControl
|
|
33
|
-
label={
|
|
36
|
+
label={i18n._(inspectorFieldDescriptor(key))}
|
|
34
37
|
max={field.max}
|
|
35
38
|
min={field.min}
|
|
36
39
|
mixed={mixed}
|
|
@@ -50,7 +53,7 @@ export function ParametricFieldControl({
|
|
|
50
53
|
return (
|
|
51
54
|
<ToggleControl
|
|
52
55
|
checked={checked}
|
|
53
|
-
label={
|
|
56
|
+
label={i18n._(inspectorFieldDescriptor(key))}
|
|
54
57
|
mixed={mixed}
|
|
55
58
|
onChange={(next) => {
|
|
56
59
|
const patch = { [key]: next } as Partial<AnyNode>
|
|
@@ -73,14 +76,14 @@ export function ParametricFieldControl({
|
|
|
73
76
|
<SegmentedControl
|
|
74
77
|
mixed={mixed}
|
|
75
78
|
onChange={apply}
|
|
76
|
-
options={field.options.map((opt) => ({ label:
|
|
79
|
+
options={field.options.map((opt) => ({ label: i18n._(inspectorOptionDescriptor(opt)), value: opt }))}
|
|
77
80
|
value={str}
|
|
78
81
|
/>
|
|
79
82
|
)
|
|
80
83
|
}
|
|
81
84
|
return (
|
|
82
85
|
<div className="flex items-center justify-between px-3 py-2">
|
|
83
|
-
<span className="text-foreground/80 text-xs">{
|
|
86
|
+
<span className="text-foreground/80 text-xs">{i18n._(inspectorFieldDescriptor(key))}</span>
|
|
84
87
|
<select
|
|
85
88
|
className="rounded-md border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-2 py-1 text-foreground text-xs focus:outline-none focus:ring-1 focus:ring-foreground/30"
|
|
86
89
|
onChange={(e) => apply(e.target.value)}
|
|
@@ -88,12 +91,12 @@ export function ParametricFieldControl({
|
|
|
88
91
|
>
|
|
89
92
|
{mixed && (
|
|
90
93
|
<option disabled value="">
|
|
91
|
-
Mixed
|
|
94
|
+
{i18n._({ id: 'editor.panels.parametricFieldControl.mixed', message: 'Mixed' })}
|
|
92
95
|
</option>
|
|
93
96
|
)}
|
|
94
97
|
{field.options.map((opt) => (
|
|
95
98
|
<option key={opt} value={opt}>
|
|
96
|
-
{
|
|
99
|
+
{i18n._(inspectorOptionDescriptor(opt))}
|
|
97
100
|
</option>
|
|
98
101
|
))}
|
|
99
102
|
</select>
|
|
@@ -110,7 +113,7 @@ export function ParametricFieldControl({
|
|
|
110
113
|
}
|
|
111
114
|
return (
|
|
112
115
|
<div className="flex items-center justify-between px-3 py-2">
|
|
113
|
-
<span className="text-foreground/80 text-xs">{
|
|
116
|
+
<span className="text-foreground/80 text-xs">{i18n._(inspectorFieldDescriptor(key))}</span>
|
|
114
117
|
<div className="flex items-center gap-2">
|
|
115
118
|
<input
|
|
116
119
|
className="h-6 w-8 cursor-pointer rounded border border-border/50 bg-transparent"
|
|
@@ -122,7 +125,7 @@ export function ParametricFieldControl({
|
|
|
122
125
|
className="w-20 rounded-md border border-border/50 bg-black/4 dark:bg-[#2C2C2E] px-2 py-1 text-foreground text-xs focus:outline-none focus:ring-1 focus:ring-foreground/30"
|
|
123
126
|
onChange={(e) => apply(e.target.value)}
|
|
124
127
|
type="text"
|
|
125
|
-
value={mixed ? 'Mixed' : str}
|
|
128
|
+
value={mixed ? i18n._({ id: 'editor.panels.mixed', message: 'Mixed' }) : str}
|
|
126
129
|
/>
|
|
127
130
|
</div>
|
|
128
131
|
</div>
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type ParamAction,
|
|
9
9
|
type ParamField,
|
|
10
10
|
useScene,
|
|
11
|
-
type ZoneNode,
|
|
11
|
+
type ZoneNode, usePascalI18n,
|
|
12
12
|
} from '@pascal-app/core'
|
|
13
13
|
import { useViewer } from '@pascal-app/viewer'
|
|
14
14
|
import { Icon } from '@iconify/react'
|
|
@@ -24,6 +24,7 @@ import { ActionButton, ActionGroup } from '../controls/action-button'
|
|
|
24
24
|
import { PanelSection } from '../controls/panel-section'
|
|
25
25
|
import { ParametricFieldControl } from './parametric-field-control'
|
|
26
26
|
import { InspectorFooterContext, PanelWrapper } from './panel-wrapper'
|
|
27
|
+
import { inspectorActionDescriptor, inspectorGroupDescriptor, nodeLabel } from '../../../i18n/node-messages'
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Auto-derived right-panel inspector for any registry-backed node.
|
|
@@ -46,6 +47,7 @@ export function ParametricInspector({
|
|
|
46
47
|
nodeId,
|
|
47
48
|
onClose,
|
|
48
49
|
}: { footer?: React.ReactNode; nodeId?: AnyNodeId; onClose?: () => void } = {}) {
|
|
50
|
+
const i18n = usePascalI18n()
|
|
49
51
|
const selectedIdFromSelection = useViewer((s) => s.selection.selectedIds[0]) as
|
|
50
52
|
| AnyNodeId
|
|
51
53
|
| undefined
|
|
@@ -127,7 +129,7 @@ export function ParametricInspector({
|
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
const presentation = def.presentation
|
|
130
|
-
const title = presentation?.label
|
|
132
|
+
const title = presentation?.label && nodeType ? nodeLabel(nodeType, presentation.label, i18n) : (nodeType ?? '')
|
|
131
133
|
const iconNode = renderIcon(presentation?.icon)
|
|
132
134
|
const canMove = !!def.capabilities.movable
|
|
133
135
|
const canDelete = def.capabilities.deletable !== false
|
|
@@ -149,7 +151,7 @@ export function ParametricInspector({
|
|
|
149
151
|
<PanelSection
|
|
150
152
|
defaultExpanded={group.defaultExpanded}
|
|
151
153
|
key={`${nodeType}-${group.label}-${gi}`}
|
|
152
|
-
title={group.label}
|
|
154
|
+
title={i18n._(inspectorGroupDescriptor(group.label))}
|
|
153
155
|
>
|
|
154
156
|
{group.fields.map((field, fi) => (
|
|
155
157
|
<FieldRenderer
|
|
@@ -167,10 +169,10 @@ export function ParametricInspector({
|
|
|
167
169
|
</Suspense>
|
|
168
170
|
)}
|
|
169
171
|
{(canMove || canDelete || (parametrics.actions && parametrics.actions.length > 0)) && (
|
|
170
|
-
<PanelSection title=
|
|
172
|
+
<PanelSection title={i18n._({ id: 'editor.panels.parametricInspector.actions', message: 'Actions' })}>
|
|
171
173
|
<ActionGroup className={isZone ? 'flex-col' : parametrics.actions?.length ? 'grid grid-cols-2 gap-2' : undefined}>
|
|
172
174
|
{canMove && (
|
|
173
|
-
<ActionButton icon={<Move className="h-4 w-4" />} label=
|
|
175
|
+
<ActionButton icon={<Move className="h-4 w-4" />} label={i18n._({ id: 'editor.panels.parametricInspector.move', message: 'Move' })} onClick={handleMove} />
|
|
174
176
|
)}
|
|
175
177
|
{parametrics.actions?.map((action, i) => (
|
|
176
178
|
<ParamActionButton action={action} key={`paramaction-${i}`} nodeId={selectedId} />
|
|
@@ -181,13 +183,13 @@ export function ParametricInspector({
|
|
|
181
183
|
<ActionButton
|
|
182
184
|
className="w-full flex-none"
|
|
183
185
|
icon={<Trash2 className="h-4 w-4 text-red-600 dark:text-red-400" />}
|
|
184
|
-
label=
|
|
186
|
+
label={i18n._({ id: 'editor.panels.parametricInspector.delete', message: 'Delete' })}
|
|
185
187
|
onClick={() => handleDelete(false)}
|
|
186
188
|
/>
|
|
187
189
|
<ActionButton
|
|
188
190
|
className="w-full flex-none"
|
|
189
191
|
icon={<Trash2 className="h-4 w-4 text-red-600 dark:text-red-400" />}
|
|
190
|
-
label=
|
|
192
|
+
label={i18n._({ id: 'editor.panels.parametricInspector.deleteWithContents', message: 'Delete with contents' })}
|
|
191
193
|
onClick={() => handleDelete(true)}
|
|
192
194
|
/>
|
|
193
195
|
</>
|
|
@@ -195,7 +197,7 @@ export function ParametricInspector({
|
|
|
195
197
|
<ActionButton
|
|
196
198
|
className="border-red-500/40 text-red-700 dark:text-red-200 hover:bg-red-500/15"
|
|
197
199
|
icon={<Trash2 className="h-4 w-4" />}
|
|
198
|
-
label=
|
|
200
|
+
label={i18n._({ id: 'editor.panels.parametricInspector.delete', message: 'Delete' })}
|
|
199
201
|
onClick={() => handleDelete()}
|
|
200
202
|
/>
|
|
201
203
|
))}
|
|
@@ -212,6 +214,7 @@ export function ParametricInspector({
|
|
|
212
214
|
// from re-rendering it. The click handler re-reads the live node so the
|
|
213
215
|
// handler always acts on current state.
|
|
214
216
|
function ParamActionButton({ action, nodeId }: { action: ParamAction<AnyNode>; nodeId: AnyNodeId }) {
|
|
217
|
+
const i18n = usePascalI18n()
|
|
215
218
|
const disabled = useScene((s) => {
|
|
216
219
|
if (!action.enabledIf) return false
|
|
217
220
|
const n = s.nodes[nodeId]
|
|
@@ -225,7 +228,7 @@ function ParamActionButton({ action, nodeId }: { action: ParamAction<AnyNode>; n
|
|
|
225
228
|
<img alt="" className="h-4 w-4 shrink-0 object-contain" src={action.iconSrc} />
|
|
226
229
|
) : undefined
|
|
227
230
|
}
|
|
228
|
-
label={action.label}
|
|
231
|
+
label={i18n._(inspectorActionDescriptor(action.label))}
|
|
229
232
|
onClick={() => {
|
|
230
233
|
const live = useScene.getState().nodes[nodeId]
|
|
231
234
|
if (live) action.onClick(live as AnyNode)
|
|
@@ -311,10 +314,13 @@ function FieldRenderer({ field, nodeId, onUpdate }: FieldRendererProps) {
|
|
|
311
314
|
// visibleIf may consult other fields on the node — subscribe to its boolean
|
|
312
315
|
// result so we re-evaluate when relevant.
|
|
313
316
|
const visible = useScene((s) => {
|
|
314
|
-
const visibleIf =
|
|
315
|
-
|
|
317
|
+
const { visibleIf, appliesTo } = field as {
|
|
318
|
+
visibleIf?: (n: AnyNode) => boolean
|
|
319
|
+
appliesTo?: (n: AnyNode) => boolean
|
|
320
|
+
}
|
|
321
|
+
if (!(visibleIf || appliesTo)) return true
|
|
316
322
|
const n = s.nodes[nodeId]
|
|
317
|
-
return n ? visibleIf(n as AnyNode) : false
|
|
323
|
+
return n ? (visibleIf?.(n as AnyNode) ?? true) && (appliesTo?.(n as AnyNode) ?? true) : false
|
|
318
324
|
})
|
|
319
325
|
if (!visible) return null
|
|
320
326
|
|