@pascal-app/editor 1.0.1 → 1.0.4
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 +16 -9
- package/src/components/editor/action-menu-button.tsx +83 -0
- package/src/components/editor/bake-thumbnail.tsx +315 -4
- package/src/components/editor/capture-level-picker.tsx +72 -0
- package/src/components/editor/custom-camera-controls.tsx +221 -128
- package/src/components/editor/delete-confirmation-dialog.tsx +297 -7
- package/src/components/editor/duplicate-node.ts +244 -0
- package/src/components/editor/editor-layout-mobile.tsx +37 -25
- package/src/components/editor/editor-layout-v2.tsx +131 -58
- package/src/components/editor/export-manager.tsx +34 -8
- package/src/components/editor/first-person/build-collider-world.test.ts +51 -0
- package/src/components/editor/first-person/build-collider-world.ts +6 -2
- package/src/components/editor/first-person/mechanism-targets.test.ts +75 -0
- package/src/components/editor/first-person/mechanism-targets.ts +36 -0
- package/src/components/editor/first-person-controls.tsx +205 -2
- package/src/components/editor/floating-action-menu.tsx +63 -207
- package/src/components/editor/floor-aware-camera-controls.test.ts +442 -0
- package/src/components/editor/floor-aware-camera-controls.ts +324 -0
- package/src/components/editor/floor-openings-overlay.tsx +243 -0
- package/src/components/editor/floor-region-controls.tsx +846 -0
- package/src/components/editor/floor-region-webgpu-materials.test.ts +12 -0
- package/src/components/editor/floor-surface.test.ts +165 -0
- package/src/components/editor/floor-surface.ts +182 -0
- package/src/components/editor/floorplan-background-selection.test.ts +11 -2
- package/src/components/editor/floorplan-background-selection.ts +1 -3
- package/src/components/editor/floorplan-panel.tsx +536 -487
- package/src/components/editor/footprint-height-handle.tsx +204 -0
- package/src/components/editor/grid.tsx +31 -7
- package/src/components/editor/group-actions.ts +59 -11
- package/src/components/editor/group-floating-action-menu.tsx +19 -9
- package/src/components/editor/group-move-3d.ts +4 -6
- package/src/components/editor/group-rotate-handle.tsx +81 -35
- package/src/components/editor/group-selection-box-3d.tsx +25 -17
- package/src/components/editor/group-transform-shared.test.ts +126 -1
- package/src/components/editor/group-transform-shared.ts +79 -73
- package/src/components/editor/handles/handle-arrow.tsx +72 -2
- package/src/components/editor/handles/handle-hit-priority.test.tsx +63 -0
- package/src/components/editor/handles/handle-hit-priority.tsx +37 -0
- package/src/components/editor/handles/swallow-next-click.test.ts +28 -0
- package/src/components/editor/handles/use-handle-drag.ts +272 -131
- package/src/components/editor/index.tsx +316 -176
- package/src/components/editor/join-walls-pill.tsx +44 -0
- package/src/components/editor/node-action-menu.tsx +70 -70
- package/src/components/editor/node-arrow-handles.tsx +167 -14
- package/src/components/editor/open-wall-ends-3d-layer.tsx +273 -0
- package/src/components/editor/open-wall-ends-hint.tsx +89 -0
- package/src/components/editor/quick-measurement-hud.tsx +3 -2
- package/src/components/editor/registry-action-contributions.test.tsx +63 -0
- package/src/components/editor/registry-action-contributions.tsx +132 -0
- package/src/components/editor/room-controls.tsx +847 -0
- package/src/components/editor/room-floor-highlight.tsx +128 -0
- package/src/components/editor/room-handles-2d.tsx +354 -0
- package/src/components/editor/room-handles.tsx +675 -0
- package/src/components/editor/room-highlight.tsx +225 -0
- package/src/components/editor/room-labels.test.ts +357 -0
- package/src/components/editor/room-labels.tsx +990 -0
- package/src/components/editor/room-transform-ghost.tsx +256 -0
- package/src/components/editor/room-webgpu-materials.test.ts +27 -0
- package/src/components/editor/selection-manager.tsx +451 -317
- package/src/components/editor/slab-hole-highlights.tsx +3 -0
- package/src/components/editor/snapshot-capture-overlay.tsx +4 -0
- package/src/components/editor/snapshot-capture.test.ts +52 -4
- package/src/components/editor/snapshot-capture.ts +30 -5
- package/src/components/editor/thumbnail-generator.test.ts +308 -0
- package/src/components/editor/thumbnail-generator.tsx +1035 -229
- package/src/components/editor/use-floorplan-background-placement.ts +31 -11
- package/src/components/editor/vector-edge-extractor.tsx +121 -0
- package/src/components/editor/views/host-view.tsx +61 -0
- package/src/components/editor/views/plugins-view.tsx +670 -0
- package/src/components/editor/views/scene-region.tsx +59 -0
- package/src/components/editor/views/use-editor-views.ts +57 -0
- package/src/components/editor/views/view-bar.tsx +220 -0
- package/src/components/editor/wall-justification.test.ts +140 -0
- package/src/components/editor/wall-move-side-handles.tsx +114 -163
- package/src/components/editor/wall-opening-highlights.tsx +2 -3
- package/src/components/editor/wall-push-arrow.tsx +92 -0
- package/src/components/editor/wall-region-handles.tsx +509 -0
- package/src/components/editor/wall-snap-beacon-layer.tsx +3 -0
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
- package/src/components/editor-2d/floorplan-group-move.tsx +17 -24
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.test.tsx +86 -0
- package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +290 -0
- package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +33 -12
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +242 -126
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.test.ts +156 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +278 -0
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +77 -0
- package/src/components/editor-2d/renderers/floorplan-draft-measurement.tsx +166 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +87 -8
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +355 -245
- package/src/components/editor-2d/svg-paths.ts +12 -15
- package/src/components/procedural-items/inspector.tsx +40 -15
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +97 -14
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +18 -18
- package/src/components/systems/zone/zone-system.test.ts +47 -0
- package/src/components/systems/zone/zone-system.tsx +31 -66
- package/src/components/tools/__fixtures__/hosting.ts +10 -0
- package/src/components/tools/item/item-surface-placement.test.ts +3 -9
- package/src/components/tools/item/item-surface-strategy-resolver.test.ts +3 -9
- package/src/components/tools/item/placement-history.test.tsx +474 -0
- package/src/components/tools/item/placement-strategies.ts +63 -47
- package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +3 -3
- package/src/components/tools/item/use-draft-node.test.tsx +432 -1
- package/src/components/tools/item/use-draft-node.ts +207 -97
- package/src/components/tools/item/use-placement-coordinator.tsx +251 -111
- package/src/components/tools/paint-region/use-paint-tint.ts +37 -0
- package/src/components/tools/paint-region/wall-paint-region-tool.tsx +466 -0
- package/src/components/tools/registry/item-surface-move.test.ts +10 -2
- package/src/components/tools/registry/item-surface-move.ts +46 -24
- package/src/components/tools/registry/item-surface-resolver.test.ts +2 -2
- package/src/components/tools/registry/move-registry-node-tool.tsx +26 -10
- package/src/components/tools/select/box-select-tool.tsx +2 -12
- package/src/components/tools/select/plane-box-select-tool.test.tsx +79 -0
- package/src/components/tools/select/plane-box-select-tool.tsx +35 -22
- package/src/components/tools/select/select-candidates.test.ts +37 -4
- package/src/components/tools/select/select-candidates.ts +15 -11
- package/src/components/tools/shared/affordance-dispatch.test.ts +24 -0
- package/src/components/tools/shared/affordance-dispatch.ts +13 -0
- package/src/components/tools/shared/cursor-sphere.tsx +36 -12
- package/src/components/tools/shared/draft-measurement-label.tsx +38 -0
- package/src/components/tools/shared/pointer-support-cap.test.ts +27 -0
- package/src/components/tools/shared/pointer-support-cap.ts +17 -5
- package/src/components/tools/shared/surface-rejection.test.tsx +5 -4
- package/src/components/tools/shared/surface-rejection.tsx +14 -0
- package/src/components/tools/site/site-boundary-editor.tsx +1 -2
- package/src/components/tools/stair/stair-defaults.ts +0 -3
- package/src/components/tools/stair/stair-tool.tsx +90 -42
- package/src/components/tools/tool-manager.tsx +82 -23
- package/src/components/tools/wall/wall-drafting.test.ts +19 -4
- package/src/components/tools/zone/zone-tool.tsx +4 -12
- package/src/components/ui/action-menu/action-button.tsx +3 -0
- package/src/components/ui/action-menu/camera-actions.tsx +2 -1
- package/src/components/ui/action-menu/control-modes.tsx +2 -35
- package/src/components/ui/action-menu/measurement-control.tsx +3 -10
- package/src/components/ui/build-panel/build-panel.tsx +235 -0
- package/src/components/ui/command-palette/editor-commands.tsx +4 -30
- package/src/components/ui/command-palette/index.tsx +5 -24
- package/src/components/ui/controls/finish-swatch.tsx +110 -0
- package/src/components/ui/controls/material-paint-panel.tsx +101 -91
- package/src/components/ui/controls/material-picker.tsx +8 -67
- package/src/components/ui/controls/scene-material-list.tsx +4 -32
- package/src/components/ui/controls/select-control.tsx +65 -0
- package/src/components/ui/controls/slider-control.tsx +76 -31
- package/src/components/ui/floating-level-selector.tsx +9 -14
- package/src/components/ui/helpers/building-helper.tsx +4 -1
- package/src/components/ui/helpers/contextual-helper-panel.tsx +154 -70
- package/src/components/ui/helpers/helper-manager.test.tsx +69 -0
- package/src/components/ui/helpers/helper-manager.tsx +348 -19
- package/src/components/ui/helpers/item-helper.tsx +8 -0
- package/src/components/ui/helpers/registered-tool-helper.tsx +10 -33
- package/src/components/ui/icon-ref.tsx +45 -1
- package/src/components/ui/item-catalog/item-catalog.tsx +5 -30
- package/src/components/ui/level-duplicate-dialog.tsx +1 -1
- package/src/components/ui/node-icon-image.tsx +16 -0
- package/src/components/ui/panels/collections/collections-popover.tsx +20 -14
- package/src/components/ui/panels/floor-foundation-panel.tsx +551 -0
- package/src/components/ui/panels/floor-opening-panel.tsx +143 -0
- package/src/components/ui/panels/mobile-panel-sheet.tsx +2 -2
- package/src/components/ui/panels/mobile-selection-bar.tsx +2 -2
- package/src/components/ui/panels/multi-field-value.test.ts +26 -3
- package/src/components/ui/panels/multi-field-value.ts +6 -3
- package/src/components/ui/panels/multi-height-mode.tsx +27 -21
- package/src/components/ui/panels/multi-parametric-inspector.tsx +52 -2
- package/src/components/ui/panels/multi-selection-panel.tsx +43 -4
- package/src/components/ui/panels/node-display.ts +8 -3
- package/src/components/ui/panels/panel-manager.tsx +103 -68
- package/src/components/ui/panels/panel-wrapper.tsx +78 -36
- package/src/components/ui/panels/parametric-field-control.tsx +1 -1
- package/src/components/ui/panels/parametric-inspector.tsx +7 -19
- package/src/components/ui/panels/reference-panel.tsx +2 -3
- package/src/components/ui/panels/room-construction-rows.tsx +774 -0
- package/src/components/ui/panels/room-name-options.test.ts +55 -0
- package/src/components/ui/panels/room-name-options.ts +47 -0
- package/src/components/ui/panels/room-openings-field.tsx +110 -0
- package/src/components/ui/panels/room-panel.tsx +725 -0
- package/src/components/ui/panels/shape-choice.tsx +58 -0
- package/src/components/ui/panels/wall-region-list.tsx +109 -0
- package/src/components/ui/primitives/color-dot.tsx +36 -24
- package/src/components/ui/primitives/shortcut-token.tsx +4 -0
- package/src/components/ui/primitives/sidebar.tsx +2 -0
- package/src/components/ui/right-stack.test.ts +29 -0
- package/src/components/ui/right-stack.tsx +145 -0
- package/src/components/ui/sidebar/icon-rail.tsx +5 -0
- package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +2 -2
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +615 -372
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +7 -7
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +1 -1
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -4
- package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +197 -0
- package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +24 -11
- package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +176 -211
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +30 -8
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +4 -1
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +1 -4
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +1 -3
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +21 -13
- package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +0 -27
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -3
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +1 -5
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +9 -4
- package/src/components/ui/sidebar/panels/zone-panel/index.tsx +0 -2
- package/src/components/ui/sidebar/tab-bar.tsx +10 -0
- package/src/components/ui/sidebar/use-plugin-panels.tsx +15 -5
- package/src/components/viewer/floorplan-compass-button.tsx +9 -1
- package/src/components/viewer/floorplan-preview-visibility.test.ts +35 -0
- package/src/components/viewer/floorplan-preview-visibility.ts +19 -0
- package/src/components/viewer/floorplan-preview.tsx +6 -18
- package/src/components/viewer/viewer-hover-label.tsx +34 -0
- package/src/components/viewer/viewer-rooms.tsx +175 -0
- package/src/components/viewer/viewer-scene-header.tsx +181 -145
- package/src/components/viewer/viewer-selection-manager.tsx +124 -0
- package/src/components/viewer/viewer-stage.tsx +2 -2
- package/src/components/viewer-zone-system.tsx +9 -9
- package/src/hooks/use-auto-save.integration.test.tsx +311 -0
- package/src/hooks/use-auto-save.ts +55 -14
- package/src/hooks/use-ceiling-events.test.ts +1 -0
- package/src/hooks/use-ceiling-events.ts +2 -0
- package/src/hooks/use-grid-events.test.ts +2 -0
- package/src/hooks/use-grid-events.ts +16 -11
- package/src/hooks/use-keyboard.test.ts +182 -0
- package/src/hooks/use-keyboard.ts +122 -91
- package/src/hooks/use-open-wall-ends.test.ts +99 -0
- package/src/hooks/use-open-wall-ends.ts +238 -0
- package/src/hooks/use-placement-notice.test.ts +48 -0
- package/src/hooks/use-placement-notice.ts +42 -0
- package/src/hooks/use-selected-room.ts +143 -0
- package/src/index.tsx +306 -39
- package/src/lib/catalog-panel-model.ts +39 -0
- package/src/lib/catalog-selection.test.ts +27 -0
- package/src/lib/catalog-selection.ts +8 -0
- package/src/lib/ceiling-edit-session.test.ts +321 -0
- package/src/lib/ceiling-edit-session.ts +206 -0
- package/src/lib/ceiling-region-session.test.ts +179 -0
- package/src/lib/contextual-help.test.ts +27 -1
- package/src/lib/contextual-help.ts +16 -9
- package/src/lib/continuation.test.ts +19 -1
- package/src/lib/continuation.ts +25 -4
- package/src/lib/door-interaction.ts +14 -0
- package/src/lib/editor-level-display.test.ts +96 -0
- package/src/lib/editor-level-display.ts +48 -0
- package/src/lib/editor-views.ts +144 -0
- package/src/lib/export-geometry-wait.test.ts +90 -0
- package/src/lib/floor-edit-session.ts +153 -0
- package/src/lib/floor-footprints.test.ts +563 -0
- package/src/lib/floor-footprints.ts +395 -0
- package/src/lib/floor-foundation-support.test.ts +283 -0
- package/src/lib/floor-opening-draft.ts +218 -0
- package/src/lib/floor-openings.test.ts +216 -0
- package/src/lib/floor-region-geometry.ts +191 -0
- package/src/lib/floor-region-session.test.ts +485 -0
- package/src/lib/floor-region-session.ts +589 -0
- package/src/lib/floor-region-snap.ts +101 -0
- package/src/lib/floor-step-hover.test.ts +32 -0
- package/src/lib/floorplan/annotation-visibility.ts +17 -0
- package/src/lib/floorplan/dimension-drive.test.ts +360 -0
- package/src/lib/floorplan/dimension-drive.ts +479 -0
- package/src/lib/floorplan/floorplan-export.test.ts +15 -9
- package/src/lib/floorplan/floorplan-export.tsx +32 -8
- package/src/lib/floorplan/floorplan-extension.ts +48 -0
- package/src/lib/floorplan/floorplan-mode.test.ts +4 -0
- package/src/lib/floorplan/floorplan-mode.ts +2 -0
- package/src/lib/floorplan/floorplan-pdfkit-document.ts +339 -18
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +138 -8
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +56 -25
- package/src/lib/floorplan/floorplan-plugin-install.test.tsx +138 -0
- package/src/lib/floorplan/floorplan-readonly.ts +6 -0
- package/src/lib/floorplan/geometry.ts +13 -6
- package/src/lib/floorplan/index.ts +0 -9
- package/src/lib/floorplan/items.ts +5 -2
- package/src/lib/floorplan/marks.test.ts +213 -0
- package/src/lib/floorplan/marks.ts +238 -0
- package/src/lib/floorplan/open-wall-ends.test.ts +268 -0
- package/src/lib/floorplan/open-wall-ends.ts +162 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/GeistMono-Regular.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-GeistMono.txt +92 -0
- package/src/lib/floorplan/pdf-fonts/LICENSE-LiberationSans.txt +102 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Bold.ttf +0 -0
- package/src/lib/floorplan/pdf-fonts/LiberationSans-Regular.ttf +0 -0
- package/src/lib/floorplan/schedules.test.ts +189 -0
- package/src/lib/floorplan/schedules.ts +339 -0
- package/src/lib/floorplan/sheet-export.test.ts +100 -0
- package/src/lib/floorplan/sheet-export.ts +318 -0
- package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +779 -0
- package/src/lib/floorplan/site-plan/contours.test.ts +43 -0
- package/src/lib/floorplan/site-plan/contributors.ts +97 -0
- package/src/lib/floorplan/site-plan/coverage.ts +232 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.test.ts +15 -0
- package/src/lib/floorplan/site-plan/drawing-type-switch.tsx +86 -0
- package/src/lib/floorplan/site-plan/front-edge.test.ts +103 -0
- package/src/lib/floorplan/site-plan/front-edge.ts +156 -0
- package/src/lib/floorplan/site-plan/geometry.test.ts +275 -0
- package/src/lib/floorplan/site-plan/geometry.ts +557 -0
- package/src/lib/floorplan/site-plan/index.ts +71 -0
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +366 -0
- package/src/lib/floorplan/site-plan/site-annotations.ts +1083 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +379 -0
- package/src/lib/floorplan/site-plan/site-plan-layer.tsx +138 -0
- package/src/lib/floorplan/site-plan/street-name.test.ts +69 -0
- package/src/lib/floorplan/site-plan/street-name.ts +302 -0
- package/src/lib/floorplan/site-plan/yards.test.ts +69 -0
- package/src/lib/floorplan/types.ts +1 -27
- package/src/lib/fresh-planar-placement.test.ts +36 -0
- package/src/lib/fresh-planar-placement.ts +191 -36
- package/src/lib/geometry-script/author.ts +152 -0
- package/src/lib/geometry-script/client.ts +152 -0
- package/src/lib/geometry-script/protocol.ts +12 -0
- package/src/lib/gesture-lifecycle.test.ts +595 -0
- package/src/lib/gesture-lifecycle.ts +290 -0
- package/src/lib/glb-export-zone-holes.test.ts +41 -0
- package/src/lib/glb-export.test.ts +544 -16
- package/src/lib/glb-export.ts +256 -125
- package/src/lib/grid-frame.test.ts +19 -0
- package/src/lib/grid-frame.ts +18 -0
- package/src/lib/history.test.ts +6 -0
- package/src/lib/history.ts +7 -0
- package/src/lib/hud-title.ts +109 -0
- package/src/lib/hydration-gated-storage.ts +49 -0
- package/src/lib/ifc-export.test.ts +174 -0
- package/src/lib/ifc-export.ts +201 -0
- package/src/lib/ifc-parts.ts +15 -0
- package/src/lib/inspector-expanded.test.ts +44 -0
- package/src/lib/inspector-expanded.ts +36 -0
- package/src/lib/interaction/scope.ts +27 -1
- package/src/lib/keyboard-pan.test.ts +184 -0
- package/src/lib/keyboard-pan.ts +238 -0
- package/src/lib/live-gesture-round-trip.test.ts +360 -0
- package/src/lib/lot/__fixtures__/map-dossier-miami-shores.json +1 -0
- package/src/lib/lot/__fixtures__/map-dossier-stpete.json +1 -0
- package/src/lib/lot/clean-ring.test.ts +186 -0
- package/src/lib/lot/clean-ring.ts +217 -0
- package/src/lib/lot/dossier.test.ts +202 -0
- package/src/lib/lot/dossier.ts +498 -0
- package/src/lib/lot/drop-in.test.ts +326 -0
- package/src/lib/lot/drop-in.ts +407 -0
- package/src/lib/lot/index.ts +43 -0
- package/src/lib/lot/lot-patch.test.ts +340 -0
- package/src/lib/lot/lot-patch.ts +394 -0
- package/src/lib/lot/parcel-provider.ts +43 -0
- package/src/lib/lot/terrain.test.ts +161 -0
- package/src/lib/lot/terrain.ts +266 -0
- package/src/lib/material-catalog-model.ts +74 -0
- package/src/lib/material-paint-panel-model.ts +80 -0
- package/src/lib/material-paint.ts +4 -0
- package/src/lib/mezzanine-actions.test.ts +342 -0
- package/src/lib/mezzanine-draft.test.ts +501 -0
- package/src/lib/mezzanine-draft.ts +278 -0
- package/src/lib/mezzanine-messages.ts +26 -0
- package/src/lib/mezzanine-selection.test.ts +207 -0
- package/src/lib/mezzanine-stairs.ts +46 -0
- package/src/lib/model-export.ts +3 -1
- package/src/lib/node-action-movement.ts +17 -6
- package/src/lib/node-display-label.ts +37 -0
- package/src/lib/orbit-floor-clearance.test.ts +165 -0
- package/src/lib/orbit-floor-clearance.ts +106 -0
- package/src/lib/overlay-transform.test.ts +28 -0
- package/src/lib/overlay-transform.ts +12 -0
- package/src/lib/paint-commit-route.test.ts +37 -0
- package/src/lib/paint-commit-route.ts +17 -0
- package/src/lib/paint-eyedropper.test.tsx +165 -0
- package/src/lib/paint-eyedropper.ts +99 -0
- package/src/lib/paint-pass-through.test.ts +111 -0
- package/src/lib/paint-pass-through.ts +76 -0
- package/src/lib/paint-region-hover.ts +25 -0
- package/src/lib/paint-region-mode.ts +154 -0
- package/src/lib/paint-regions.test.ts +166 -0
- package/src/lib/paint-regions.ts +192 -0
- package/src/lib/paint-scope.test.ts +317 -35
- package/src/lib/paint-scope.ts +266 -25
- package/src/lib/panel-rows.ts +96 -0
- package/src/lib/panel-tool-options.test.ts +34 -0
- package/src/lib/panel-tool-options.ts +46 -0
- package/src/lib/parametric-node-update.ts +37 -0
- package/src/lib/plate-paint-affected.test.ts +300 -0
- package/src/lib/plate-paint-affected.ts +177 -0
- package/src/lib/plugin-chat.ts +50 -0
- package/src/lib/plugin-panels.test.ts +25 -0
- package/src/lib/plugin-panels.ts +94 -0
- package/src/lib/print-shell-compiler.ts +1 -0
- package/src/lib/registered-draft-snap.ts +12 -0
- package/src/lib/roof-duplication.ts +10 -2
- package/src/lib/room-assembly-overlay.test.ts +275 -0
- package/src/lib/room-assembly-overlay.ts +266 -0
- package/src/lib/room-built-on.test.ts +379 -0
- package/src/lib/room-built-on.ts +138 -0
- package/src/lib/room-construction-commands.ts +712 -0
- package/src/lib/room-construction.test.ts +746 -0
- package/src/lib/room-construction.ts +385 -0
- package/src/lib/room-controls-phase6.test.ts +630 -0
- package/src/lib/room-divide-session.ts +523 -0
- package/src/lib/room-handle-drag.ts +1068 -0
- package/src/lib/room-name-catalog.test.ts +90 -0
- package/src/lib/room-name-catalog.ts +168 -0
- package/src/lib/room-push-dimensions.test.ts +311 -0
- package/src/lib/room-push-dimensions.ts +251 -0
- package/src/lib/room-selection-commands.ts +62 -0
- package/src/lib/room-selection.test.ts +973 -0
- package/src/lib/room-selection.ts +417 -0
- package/src/lib/room-structure-commands.ts +94 -0
- package/src/lib/room-structure.test.ts +921 -0
- package/src/lib/room-transform-session.ts +570 -0
- package/src/lib/room-zone-routing.test.ts +294 -0
- package/src/lib/room-zone-routing.ts +197 -0
- package/src/lib/scene-clipboard.test.ts +364 -0
- package/src/lib/scene-clipboard.ts +229 -48
- package/src/lib/scene-material-refs.test.ts +79 -0
- package/src/lib/scene-material-refs.ts +79 -0
- package/src/lib/scene.test.ts +87 -0
- package/src/lib/scene.ts +39 -45
- package/src/lib/selection-routing.test.ts +129 -45
- package/src/lib/selection-routing.ts +53 -74
- package/src/lib/session-writes.test.ts +120 -0
- package/src/lib/session-writes.ts +205 -0
- package/src/lib/sidebar-panel.ts +62 -0
- package/src/lib/site-boundary.test.ts +4 -6
- package/src/lib/site-boundary.ts +1 -1
- package/src/lib/snapping-mode.test.ts +17 -2
- package/src/lib/snapping-mode.ts +19 -7
- package/src/lib/spatial-drag-plane.test.ts +53 -0
- package/src/lib/spatial-drag-plane.ts +19 -0
- package/src/lib/spatial-pointer-input.test.ts +72 -0
- package/src/lib/spatial-pointer-input.ts +62 -0
- package/src/lib/spatial-snap.test.ts +47 -0
- package/src/lib/terrace-draft.test.ts +145 -0
- package/src/lib/terrace-draft.ts +213 -0
- package/src/lib/terrain-panel-model.ts +117 -0
- package/src/lib/tool-cursor-icon.test.ts +16 -0
- package/src/lib/tool-cursor-icon.ts +11 -0
- package/src/lib/typing-target.ts +9 -0
- package/src/lib/units.ts +77 -39
- package/src/lib/vector-edges.test.ts +58 -0
- package/src/lib/vector-edges.ts +400 -0
- package/src/lib/viewer-selection.test.ts +577 -0
- package/src/lib/viewer-selection.ts +569 -0
- package/src/lib/wall-draw-variant.test.ts +71 -0
- package/src/lib/wall-draw-variant.ts +91 -0
- package/src/lib/wall-paint-region-session.ts +246 -0
- package/src/lib/wall-paint-region.test.ts +373 -0
- package/src/lib/wall-polygon-draft.test.ts +262 -0
- package/src/lib/wall-polygon-draft.ts +286 -0
- package/src/lib/wall-push.test.ts +453 -0
- package/src/lib/wall-region-face.ts +352 -0
- package/src/lib/wall-region-handles.test.ts +304 -0
- package/src/lib/wall-region-handles.ts +368 -0
- package/src/lib/wall-region-list.test.ts +119 -0
- package/src/lib/wall-region-list.ts +136 -0
- package/src/lib/wall-region-snap.ts +110 -0
- package/src/lib/window-interaction.ts +14 -0
- package/src/lib/world-grid-snap.test.ts +48 -0
- package/src/lib/world-grid-snap.ts +9 -5
- package/src/lib/zone-content.ts +4 -1
- package/src/lib/zone-name.test.ts +17 -0
- package/src/lib/zone-name.ts +17 -0
- package/src/store/opens-in-select-mode.test.ts +223 -0
- package/src/store/paint-mode.test.ts +188 -0
- package/src/store/rotation-snapping.test.ts +53 -0
- package/src/store/terrain-sculpt-mode.test.ts +19 -21
- package/src/store/tool-mode.test.ts +137 -6
- package/src/store/use-delete-confirmation.ts +28 -1
- package/src/store/use-drawing-view.test.ts +24 -3
- package/src/store/use-drawing-view.ts +29 -1
- package/src/store/use-editor.tsx +442 -323
- package/src/store/use-floorplan-draft-preview.ts +23 -1
- package/src/store/use-floorplan-marquee.ts +12 -2
- package/src/store/use-interaction-scope.ts +155 -6
- package/src/store/use-quick-measurement-hud.ts +3 -3
- package/src/store/use-stair-build-preview.ts +13 -5
- package/src/store/use-wall-move-ghosts.ts +4 -2
- package/src/store/use-wall-region-handles-selection.test.ts +222 -0
- package/src/store/use-wall-region-handles-selection.ts +139 -0
- package/src/store/view-layout.test.ts +175 -0
- package/src/store/view-layout.ts +196 -0
- package/src/test-utils/immediate-animation-frames.ts +16 -0
- package/src/test-utils/live-gesture-round-trip.ts +224 -0
- package/src/test-utils/selection-harness.ts +82 -0
- package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +0 -499
- package/src/components/systems/zone/zone-label-editor-system.tsx +0 -333
- package/src/components/tools/fence/fence-drafting.test.ts +0 -100
- package/src/components/tools/fence/fence-drafting.ts +0 -266
- package/src/components/ui/sidebar/panels/plugins-panel.tsx +0 -218
- package/src/components/viewer/viewer-units-panel.tsx +0 -116
- package/src/lib/floorplan/stairs.ts +0 -489
- package/src/lib/level-duplication.test.ts +0 -144
- package/src/lib/level-duplication.ts +0 -195
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import {
|
|
4
4
|
type AnyNode,
|
|
5
5
|
type AnyNodeId,
|
|
6
|
+
floorPlacedCollides,
|
|
6
7
|
nodeRegistry,
|
|
7
8
|
type TerrainVerb,
|
|
8
9
|
useScene,
|
|
@@ -20,18 +21,60 @@ import {
|
|
|
20
21
|
resolveSelectModeHelpHints,
|
|
21
22
|
} from '../../../lib/contextual-help'
|
|
22
23
|
import { getContextualHelpNodeExtension } from '../../../lib/contextual-help-extension'
|
|
23
|
-
import { continuationContextOf } from '../../../lib/continuation'
|
|
24
|
+
import { continuationContextOf, keyCyclableContinuationContext } from '../../../lib/continuation'
|
|
24
25
|
import { canDirectMoveNode, canDirectRotateNode } from '../../../lib/direct-manipulation'
|
|
26
|
+
import {
|
|
27
|
+
DIVIDE_ROOM_HUD_TITLE,
|
|
28
|
+
MEZZANINE_HUD_TITLE,
|
|
29
|
+
OPENING_HUD_TITLE,
|
|
30
|
+
TERRACE_HUD_TITLE,
|
|
31
|
+
MOVE_ROOM_HUD_TITLE,
|
|
32
|
+
MOVE_SELECTION_HUD_TITLE,
|
|
33
|
+
nodeKindHudTitle,
|
|
34
|
+
ERASE_HUD_TITLE,
|
|
35
|
+
PAINT_HUD_TITLE,
|
|
36
|
+
PICK_MATERIAL_HUD_TITLE,
|
|
37
|
+
paintRegionHudTitle,
|
|
38
|
+
RESIZE_HUD_TITLE,
|
|
39
|
+
ROTATE_HUD_TITLE,
|
|
40
|
+
SELECT_HUD_TITLE,
|
|
41
|
+
SMART_MEASURE_HUD_TITLE,
|
|
42
|
+
terrainHudTitle,
|
|
43
|
+
toolHudTitle,
|
|
44
|
+
} from '../../../lib/hud-title'
|
|
25
45
|
import type { ReshapeKind } from '../../../lib/interaction/scope'
|
|
46
|
+
import {
|
|
47
|
+
type MezzanineShape,
|
|
48
|
+
useMezzanineDraft,
|
|
49
|
+
} from '../../../lib/mezzanine-draft'
|
|
50
|
+
import {
|
|
51
|
+
TERRACE_SHAPE_HINT,
|
|
52
|
+
type TerraceShape,
|
|
53
|
+
useTerraceDraft,
|
|
54
|
+
} from '../../../lib/terrace-draft'
|
|
55
|
+
import {
|
|
56
|
+
type OpeningShape,
|
|
57
|
+
useOpeningDraft,
|
|
58
|
+
} from '../../../lib/floor-opening-draft'
|
|
26
59
|
import { isFreshPlacementMetadata } from '../../../lib/placement-metadata'
|
|
60
|
+
import { MEZZANINE_EDGE_DRAG_LABEL, ROOM_ELEVATION_DRAG_LABEL } from '../../../lib/room-handle-drag'
|
|
61
|
+
import { ROOM_MOVE_DRAG_LABEL } from '../../../lib/room-transform-session'
|
|
62
|
+
import { usePaintRegionHovering } from '../../../lib/paint-region-hover'
|
|
63
|
+
import {
|
|
64
|
+
type PaintMode,
|
|
65
|
+
paintRegionHoverHint,
|
|
66
|
+
usePaintRegionMode,
|
|
67
|
+
} from '../../../lib/paint-region-mode'
|
|
27
68
|
import { snapContextOf } from '../../../lib/snapping-mode'
|
|
28
69
|
import useEditor, { getActiveContinuationContext } from '../../../store/use-editor'
|
|
29
70
|
import useInteractionScope, {
|
|
30
71
|
useActiveHandleDrag,
|
|
31
72
|
useMovingNode,
|
|
73
|
+
useReshapingNode,
|
|
32
74
|
} from '../../../store/use-interaction-scope'
|
|
33
75
|
import { BuildingHelper } from './building-helper'
|
|
34
76
|
import { ContextualHelperPanel } from './contextual-helper-panel'
|
|
77
|
+
import { transientDraftId, usePlacementNotice } from '../../../hooks/use-placement-notice'
|
|
35
78
|
import { ItemHelper } from './item-helper'
|
|
36
79
|
import { RegisteredToolHelper } from './registered-tool-helper'
|
|
37
80
|
|
|
@@ -84,15 +127,100 @@ function terrainSculptHints(verb: TerrainVerb, sampling: boolean): ContextualSho
|
|
|
84
127
|
}
|
|
85
128
|
|
|
86
129
|
type ActiveModifierKeys = {
|
|
130
|
+
alt: boolean
|
|
87
131
|
command: boolean
|
|
88
132
|
shift: boolean
|
|
89
133
|
}
|
|
90
134
|
|
|
91
135
|
const EMPTY_CONTEXTUAL_HINTS: ContextualShortcutHint[] = []
|
|
136
|
+
|
|
137
|
+
// The paint tool's region sub-modes: what one gesture draws, then Esc. A
|
|
138
|
+
// refused gesture (the per-face cap) shows as the first row.
|
|
139
|
+
/** What a click does in each whole-surface sub-mode; only painting teaches the held eyedropper. */
|
|
140
|
+
export function paintHints(mode: PaintMode): ContextualShortcutHint[] {
|
|
141
|
+
if (mode === 'pick')
|
|
142
|
+
return [
|
|
143
|
+
{ keys: ['Left click'], label: 'Paint with this material' },
|
|
144
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
145
|
+
]
|
|
146
|
+
if (mode === 'erase')
|
|
147
|
+
return [{ keys: ['Left click'], label: 'Remove a painted part, or reset a surface' }]
|
|
148
|
+
if (mode === 'surface') return [{ keys: [['Alt', 'Option']], label: 'Hold to pick a material' }]
|
|
149
|
+
return []
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function paintRegionHints(
|
|
153
|
+
mode: 'rectangle' | 'polygon',
|
|
154
|
+
notice: string | null,
|
|
155
|
+
hovering = true,
|
|
156
|
+
): ContextualShortcutHint[] {
|
|
157
|
+
const gesture: Record<typeof mode, ContextualShortcutHint> = {
|
|
158
|
+
rectangle: { keys: ['Drag'], label: 'Press anywhere, drag to the opposite corner' },
|
|
159
|
+
polygon: { keys: ['Left click'], label: 'Add point · click the first to close' },
|
|
160
|
+
}
|
|
161
|
+
// Off anything the sub-mode draws on, the HUD says where to go instead.
|
|
162
|
+
if (!hovering)
|
|
163
|
+
return [
|
|
164
|
+
...(notice ? [{ keys: ['!'], label: notice, active: true }] : []),
|
|
165
|
+
{ keys: ['Hover'], label: paintRegionHoverHint(mode), active: true },
|
|
166
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
167
|
+
]
|
|
168
|
+
return [
|
|
169
|
+
...(notice ? [{ keys: ['!'], label: notice, active: true }] : []),
|
|
170
|
+
gesture[mode],
|
|
171
|
+
...(mode === 'polygon' ? [{ keys: ['Backspace'], label: 'Remove last point' }] : []),
|
|
172
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
// "Add mezzanine": the outline gesture for the shape picked in the room panel,
|
|
176
|
+
// then Esc; the snapping chip sits below.
|
|
177
|
+
export function mezzanineHints(shape: MezzanineShape): ContextualShortcutHint[] {
|
|
178
|
+
return shape === 'rectangle'
|
|
179
|
+
? [
|
|
180
|
+
{ keys: ['Left click'], label: 'Set one corner, then the opposite' },
|
|
181
|
+
{ keys: ['Drag'], label: 'Draw the mezzanine' },
|
|
182
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
183
|
+
]
|
|
184
|
+
: [
|
|
185
|
+
{ keys: ['Left click'], label: 'Add point · click the first to close' },
|
|
186
|
+
{ keys: ['Backspace'], label: 'Remove last point' },
|
|
187
|
+
{ keys: ['Enter'], label: 'Finish' },
|
|
188
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
/** "Cut opening": the outline gesture for the chosen shape, then Esc. */
|
|
192
|
+
export function openingHints(shape: OpeningShape): ContextualShortcutHint[] {
|
|
193
|
+
return mezzanineHints(shape).map((hint) =>
|
|
194
|
+
hint.label === 'Draw the mezzanine' ? { ...hint, label: 'Draw the opening' } : hint,
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
const TERRACE_CHIP_HINTS = [TERRACE_SHAPE_HINT]
|
|
198
|
+
// The terrace tool: the outline gesture for the chosen shape, then Esc.
|
|
199
|
+
export function terraceHints(shape: TerraceShape): ContextualShortcutHint[] {
|
|
200
|
+
return shape === 'rectangle'
|
|
201
|
+
? [
|
|
202
|
+
{ keys: ['Left click'], label: 'Set one corner, then the opposite' },
|
|
203
|
+
{ keys: ['Drag'], label: 'Draw the terrace' },
|
|
204
|
+
{ keys: ['Esc'], label: 'Done' },
|
|
205
|
+
]
|
|
206
|
+
: [
|
|
207
|
+
{ keys: ['Left click'], label: 'Add point · click the first to close' },
|
|
208
|
+
{ keys: ['Backspace'], label: 'Remove last point' },
|
|
209
|
+
{ keys: ['Enter'], label: 'Finish' },
|
|
210
|
+
{ keys: ['Esc'], label: 'Done' },
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
export const ROOM_DIVIDE_HINTS: ContextualShortcutHint[] = [
|
|
214
|
+
{ keys: ['Left click'], label: 'Add point' },
|
|
215
|
+
{ keys: ['Backspace'], label: 'Remove last point' },
|
|
216
|
+
{ keys: ['Enter'], label: 'Finish at the edge' },
|
|
217
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
218
|
+
]
|
|
92
219
|
const NO_CONTEXTUAL_HELP_SUBSCRIPTION = () => () => {}
|
|
93
220
|
|
|
94
221
|
function useActiveModifierKeys(): ActiveModifierKeys {
|
|
95
222
|
const [modifiers, setModifiers] = useState<ActiveModifierKeys>({
|
|
223
|
+
alt: false,
|
|
96
224
|
command: false,
|
|
97
225
|
shift: false,
|
|
98
226
|
})
|
|
@@ -101,6 +229,7 @@ function useActiveModifierKeys(): ActiveModifierKeys {
|
|
|
101
229
|
const updateModifiers = (event: KeyboardEvent) => {
|
|
102
230
|
const isKeyDown = event.type === 'keydown'
|
|
103
231
|
setModifiers({
|
|
232
|
+
alt: event.altKey || (isKeyDown && event.key === 'Alt'),
|
|
104
233
|
command:
|
|
105
234
|
event.metaKey ||
|
|
106
235
|
event.ctrlKey ||
|
|
@@ -109,7 +238,7 @@ function useActiveModifierKeys(): ActiveModifierKeys {
|
|
|
109
238
|
})
|
|
110
239
|
}
|
|
111
240
|
const clearModifiers = () => {
|
|
112
|
-
setModifiers({ command: false, shift: false })
|
|
241
|
+
setModifiers({ alt: false, command: false, shift: false })
|
|
113
242
|
}
|
|
114
243
|
|
|
115
244
|
window.addEventListener('keydown', updateModifiers)
|
|
@@ -125,6 +254,43 @@ function useActiveModifierKeys(): ActiveModifierKeys {
|
|
|
125
254
|
return modifiers
|
|
126
255
|
}
|
|
127
256
|
|
|
257
|
+
export type MezzanineGesture = 'raise' | 'resize' | 'move'
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* A handle drag on a mezzanine — its floor handle, an edge arrow, or its pick-up
|
|
261
|
+
* (Move / Duplicate) — or null. Its HUD names the mezzanine, not "Resize" or
|
|
262
|
+
* "Move room".
|
|
263
|
+
*/
|
|
264
|
+
export function mezzanineGesture(
|
|
265
|
+
drag: { nodeId: string; label: string } | null,
|
|
266
|
+
nodes: Readonly<Record<string, AnyNode>>,
|
|
267
|
+
): MezzanineGesture | null {
|
|
268
|
+
if (!drag) return null
|
|
269
|
+
const zone = nodes[drag.nodeId]
|
|
270
|
+
if (zone?.type !== 'zone' || zone.floor?.support !== 'open') return null
|
|
271
|
+
if (drag.label === ROOM_ELEVATION_DRAG_LABEL) return 'raise'
|
|
272
|
+
if (drag.label === MEZZANINE_EDGE_DRAG_LABEL) return 'resize'
|
|
273
|
+
if (drag.label === ROOM_MOVE_DRAG_LABEL) return 'move'
|
|
274
|
+
return null
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export const MEZZANINE_GESTURE_HINTS: Record<MezzanineGesture, ContextualShortcutHint[]> = {
|
|
278
|
+
raise: [
|
|
279
|
+
{ keys: ['Drag'], label: 'Raise or lower' },
|
|
280
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
281
|
+
],
|
|
282
|
+
resize: [
|
|
283
|
+
{ keys: ['Drag'], label: 'Resize' },
|
|
284
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
285
|
+
],
|
|
286
|
+
move: [
|
|
287
|
+
{ keys: ['Left click'], label: 'Place inside the room' },
|
|
288
|
+
{ keys: [['R', 'T']], label: 'Rotate ±45°' },
|
|
289
|
+
{ keys: ['Alt'], label: 'Free move' },
|
|
290
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
291
|
+
],
|
|
292
|
+
}
|
|
293
|
+
|
|
128
294
|
export function HelperManager() {
|
|
129
295
|
const mode = useEditor((s) => s.mode)
|
|
130
296
|
const tool = useEditor((s) => s.tool)
|
|
@@ -132,10 +298,24 @@ export function HelperManager() {
|
|
|
132
298
|
const terrainSampling = useEditor((s) => s.terrainSampling)
|
|
133
299
|
const isFirstPersonMode = useEditor((s) => s.isFirstPersonMode)
|
|
134
300
|
const measurementToolKind = useEditor((s) => s.toolDefaults.measurement?.kind)
|
|
301
|
+
const wallMode = useEditor((s) => s.continuationByContext.wall)
|
|
135
302
|
const workspaceMode = useEditor((s) => s.workspaceMode)
|
|
136
303
|
const scope = useInteractionScope((s) => s.scope)
|
|
304
|
+
const paintRegionMode = usePaintRegionMode((s) => s.mode)
|
|
305
|
+
const paintRegionDrawing = paintRegionMode === 'rectangle' || paintRegionMode === 'polygon'
|
|
306
|
+
const paintRegionNotice = usePaintRegionMode((s) => s.notice)
|
|
307
|
+
const paintRegionHovering = usePaintRegionHovering()
|
|
308
|
+
const mezzanineShape = useMezzanineDraft((s) => (s.host ? s.shape : null))
|
|
309
|
+
const openingShape = useOpeningDraft((s) => (s.host ? s.shape : null))
|
|
310
|
+
const terraceShape = useTerraceDraft((s) => (s.host ? s.shape : null))
|
|
137
311
|
const movingNode = useMovingNode()
|
|
312
|
+
// A tool whose kind gives placement notices: its transient draft is the node in hand.
|
|
313
|
+
const toolNotices = !!(tool && nodeRegistry.get(tool)?.placementNotice)
|
|
314
|
+
const toolDraftId = useScene((s) => (toolNotices ? transientDraftId(s.nodes) : null))
|
|
315
|
+
const placementNotice = usePlacementNotice(movingNode?.id ?? toolDraftId)
|
|
316
|
+
const reshapingNode = useReshapingNode()
|
|
138
317
|
const activeHandleDrag = useActiveHandleDrag()
|
|
318
|
+
const mezzanineHandle = useScene((s) => mezzanineGesture(activeHandleDrag, s.nodes))
|
|
139
319
|
const selectedIds = useViewer((s) => s.selection.selectedIds)
|
|
140
320
|
const isMobile = useIsMobile()
|
|
141
321
|
const modifiers = useActiveModifierKeys()
|
|
@@ -175,11 +355,14 @@ export function HelperManager() {
|
|
|
175
355
|
return node ? nodeRegistry.get(node.type)?.snapProfile : undefined
|
|
176
356
|
},
|
|
177
357
|
draftDirectionalOf: (typeOrTool) => nodeRegistry.get(typeOrTool)?.snapDraftDirectional ?? true,
|
|
358
|
+
paintRegion: mode === 'material-paint' && paintRegionDrawing,
|
|
178
359
|
}),
|
|
179
|
-
[scope, mode, tool],
|
|
360
|
+
[scope, mode, tool, paintRegionDrawing],
|
|
180
361
|
)
|
|
362
|
+
// Contexts whose mode is picked in the Build panel (the wall's Rooms variant)
|
|
363
|
+
// get no HUD chip: the header names the variant instead.
|
|
181
364
|
const continuationContext = useMemo(
|
|
182
|
-
() => getActiveContinuationContext(),
|
|
365
|
+
() => keyCyclableContinuationContext(getActiveContinuationContext()),
|
|
183
366
|
[scope, mode, tool],
|
|
184
367
|
)
|
|
185
368
|
const selectModeHints = useMemo(() => {
|
|
@@ -190,10 +373,12 @@ export function HelperManager() {
|
|
|
190
373
|
: single?.type === 'duct-fitting' || single?.type === 'pipe-fitting'
|
|
191
374
|
? 'fitting'
|
|
192
375
|
: null
|
|
376
|
+
const hasOpeningRadiusSelection = single?.type === 'door' || single?.type === 'window'
|
|
193
377
|
return resolveSelectModeHelpHints({
|
|
194
378
|
selectedCount: selectedNodes.length,
|
|
195
379
|
hasMovableSelection: selectedNodes.some((node) => canDirectMoveNode(node)),
|
|
196
380
|
hasRotatableSelection: selectedNodes.some((node) => canDirectRotateNode(node)),
|
|
381
|
+
hasOpeningRadiusSelection,
|
|
197
382
|
commandPressed: modifiers.command,
|
|
198
383
|
shiftPressed: modifiers.shift,
|
|
199
384
|
mepSelection,
|
|
@@ -211,15 +396,17 @@ export function HelperManager() {
|
|
|
211
396
|
// tools — editor shortcut hints would only mislead there.
|
|
212
397
|
if (workspaceMode === 'studio') return null
|
|
213
398
|
|
|
214
|
-
// Rotating a node (or a multi-selection group) via its in-world gizmo:
|
|
215
|
-
// advertise Shift = free rotation, the same angle-step bypass wall drafting
|
|
216
|
-
// exposes. Takes priority over the idle select-mode hints since a handle
|
|
217
|
-
// drag is the active interaction.
|
|
218
399
|
if (
|
|
219
400
|
activeHandleDrag?.label === ROTATE_HANDLE_DRAG_LABEL ||
|
|
220
401
|
activeHandleDrag?.label === GROUP_ROTATE_DRAG_LABEL
|
|
221
402
|
) {
|
|
222
|
-
return
|
|
403
|
+
return (
|
|
404
|
+
<ContextualHelperPanel
|
|
405
|
+
hints={resolveRotateHandleHelpHints(modifiers.alt)}
|
|
406
|
+
snapContext={snapContext}
|
|
407
|
+
title={ROTATE_HUD_TITLE}
|
|
408
|
+
/>
|
|
409
|
+
)
|
|
223
410
|
}
|
|
224
411
|
|
|
225
412
|
// Group-move drag / pick-up: the drag resolves to the 'item' snap context
|
|
@@ -230,6 +417,79 @@ export function HelperManager() {
|
|
|
230
417
|
<ContextualHelperPanel
|
|
231
418
|
hints={[{ keys: ['R / T'], label: 'Rotate the selection ±45°' }]}
|
|
232
419
|
snapContext={snapContext}
|
|
420
|
+
title={MOVE_SELECTION_HUD_TITLE}
|
|
421
|
+
/>
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (mezzanineHandle) {
|
|
426
|
+
return (
|
|
427
|
+
<ContextualHelperPanel
|
|
428
|
+
hints={MEZZANINE_GESTURE_HINTS[mezzanineHandle]}
|
|
429
|
+
snapContext={snapContext}
|
|
430
|
+
title={MEZZANINE_HUD_TITLE}
|
|
431
|
+
/>
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// A picked-up room (Move / Duplicate / Rotate from its pill): the same keys
|
|
436
|
+
// as a group pick-up.
|
|
437
|
+
if (activeHandleDrag?.label === ROOM_MOVE_DRAG_LABEL) {
|
|
438
|
+
return (
|
|
439
|
+
<ContextualHelperPanel
|
|
440
|
+
hints={[
|
|
441
|
+
{ keys: ['Left click'], label: 'Place' },
|
|
442
|
+
{ keys: [['R', 'T']], label: 'Rotate ±45°' },
|
|
443
|
+
{ keys: ['Alt'], label: 'Free move · slide doors and windows' },
|
|
444
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
445
|
+
]}
|
|
446
|
+
snapContext={snapContext}
|
|
447
|
+
title={MOVE_ROOM_HUD_TITLE}
|
|
448
|
+
/>
|
|
449
|
+
)
|
|
450
|
+
}
|
|
451
|
+
// A paint region gesture holds a handle-drag scope while it draws; its HUD
|
|
452
|
+
// stays the region one (Backspace / close hints for a polygon mid-draft).
|
|
453
|
+
if (mode === 'material-paint' && paintRegionDrawing) {
|
|
454
|
+
return (
|
|
455
|
+
<ContextualHelperPanel
|
|
456
|
+
hints={paintRegionHints(paintRegionMode, paintRegionNotice, paintRegionHovering)}
|
|
457
|
+
snapContext={snapContext}
|
|
458
|
+
title={paintRegionHudTitle(paintRegionMode)}
|
|
459
|
+
/>
|
|
460
|
+
)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// The terrace tool draws like "Add mezzanine": same outline gestures and chip.
|
|
464
|
+
if (terraceShape) {
|
|
465
|
+
return (
|
|
466
|
+
<ContextualHelperPanel
|
|
467
|
+
chipHints={TERRACE_CHIP_HINTS}
|
|
468
|
+
hints={terraceHints(terraceShape)}
|
|
469
|
+
snapContext={snapContext}
|
|
470
|
+
title={TERRACE_HUD_TITLE}
|
|
471
|
+
/>
|
|
472
|
+
)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (openingShape) {
|
|
476
|
+
return (
|
|
477
|
+
<ContextualHelperPanel
|
|
478
|
+
hints={openingHints(openingShape)}
|
|
479
|
+
snapContext={snapContext}
|
|
480
|
+
title={OPENING_HUD_TITLE}
|
|
481
|
+
/>
|
|
482
|
+
)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// "Add mezzanine" holds a handle-drag scope on its host room for the whole
|
|
486
|
+
// tool, so it must win over the generic resize HUD below.
|
|
487
|
+
if (mezzanineShape) {
|
|
488
|
+
return (
|
|
489
|
+
<ContextualHelperPanel
|
|
490
|
+
hints={mezzanineHints(mezzanineShape)}
|
|
491
|
+
snapContext={snapContext}
|
|
492
|
+
title={MEZZANINE_HUD_TITLE}
|
|
233
493
|
/>
|
|
234
494
|
)
|
|
235
495
|
}
|
|
@@ -246,6 +506,7 @@ export function HelperManager() {
|
|
|
246
506
|
{ keys: ['Esc'], label: 'Cancel' },
|
|
247
507
|
]}
|
|
248
508
|
snapContext={snapContext}
|
|
509
|
+
title={RESIZE_HUD_TITLE}
|
|
249
510
|
/>
|
|
250
511
|
)
|
|
251
512
|
}
|
|
@@ -254,25 +515,54 @@ export function HelperManager() {
|
|
|
254
515
|
// before the select branch so the idle "drag selected / add objects" hints
|
|
255
516
|
// never leak over an in-progress reshape — and it gets its own snapping chip.
|
|
256
517
|
if (scope.kind === 'reshaping') {
|
|
257
|
-
|
|
518
|
+
// A kind's own reshape brings its hints (`def.affordanceHints[reshape]`).
|
|
519
|
+
const hints = reshapingNode
|
|
520
|
+
? nodeRegistry.get(reshapingNode.type)?.affordanceHints?.[scope.reshape]
|
|
521
|
+
: undefined
|
|
522
|
+
const title = reshapingNode
|
|
523
|
+
? nodeKindHudTitle(reshapingNode.type, scope.reshape === 'split' ? 'Split' : 'Edit')
|
|
524
|
+
: null
|
|
525
|
+
if (hints) {
|
|
526
|
+
return (
|
|
527
|
+
<RegisteredToolHelper
|
|
528
|
+
hints={hints}
|
|
529
|
+
shiftPressed={modifiers.shift}
|
|
530
|
+
snapContext={snapContext}
|
|
531
|
+
title={title}
|
|
532
|
+
/>
|
|
533
|
+
)
|
|
534
|
+
}
|
|
535
|
+
return (
|
|
536
|
+
<ContextualHelperPanel
|
|
537
|
+
hints={reshapingHints(scope.reshape)}
|
|
538
|
+
snapContext={snapContext}
|
|
539
|
+
title={title}
|
|
540
|
+
/>
|
|
541
|
+
)
|
|
258
542
|
}
|
|
259
543
|
|
|
260
544
|
if (movingNode) {
|
|
261
|
-
if (movingNode.type === 'building') return <BuildingHelper showRotate />
|
|
262
545
|
// A fresh placement (e.g. a positioned preset like a shelf) advertises its
|
|
263
546
|
// once/repeat continuation, exactly like the GLB item tool — but an existing
|
|
264
547
|
// node being *moved* is not a placement, so it gets no continuation chip.
|
|
265
|
-
const
|
|
266
|
-
|
|
548
|
+
const isFreshPlacement = isFreshPlacementMetadata(movingNode.metadata)
|
|
549
|
+
const movingTitle = nodeKindHudTitle(movingNode.type, isFreshPlacement ? undefined : 'Move')
|
|
550
|
+
if (movingNode.type === 'building') return <BuildingHelper showRotate title={movingTitle} />
|
|
551
|
+
const movingContinuationContext = isFreshPlacement
|
|
552
|
+
? keyCyclableContinuationContext(continuationContextOf(movingNode.type))
|
|
267
553
|
: null
|
|
268
|
-
const collisionValidatesDrop =
|
|
269
|
-
nodeRegistry.get(movingNode.type)?.capabilities.floorPlaced
|
|
554
|
+
const collisionValidatesDrop = floorPlacedCollides(
|
|
555
|
+
nodeRegistry.get(movingNode.type)?.capabilities.floorPlaced,
|
|
556
|
+
movingNode,
|
|
557
|
+
)
|
|
270
558
|
return (
|
|
271
559
|
<ItemHelper
|
|
272
560
|
continuationContext={movingContinuationContext}
|
|
561
|
+
notice={placementNotice?.line}
|
|
273
562
|
showEsc
|
|
274
563
|
showForce={collisionValidatesDrop}
|
|
275
564
|
snapContext={snapContext}
|
|
565
|
+
title={movingTitle}
|
|
276
566
|
/>
|
|
277
567
|
)
|
|
278
568
|
}
|
|
@@ -281,7 +571,19 @@ export function HelperManager() {
|
|
|
281
571
|
// only contextual control here. The chip hides itself for targets that only
|
|
282
572
|
// paint one surface, so this renders nothing until a scoped target is active.
|
|
283
573
|
if (mode === 'material-paint') {
|
|
284
|
-
return
|
|
574
|
+
return (
|
|
575
|
+
<ContextualHelperPanel
|
|
576
|
+
hints={paintHints(paintRegionMode)}
|
|
577
|
+
showPaintScope
|
|
578
|
+
title={
|
|
579
|
+
paintRegionMode === 'pick'
|
|
580
|
+
? PICK_MATERIAL_HUD_TITLE
|
|
581
|
+
: paintRegionMode === 'erase'
|
|
582
|
+
? ERASE_HUD_TITLE
|
|
583
|
+
: PAINT_HUD_TITLE
|
|
584
|
+
}
|
|
585
|
+
/>
|
|
586
|
+
)
|
|
285
587
|
}
|
|
286
588
|
|
|
287
589
|
// Sculpt mode. The HUD is what makes a sustained brush mode legible: it names
|
|
@@ -290,17 +592,41 @@ export function HelperManager() {
|
|
|
290
592
|
// mode-specific — bracket resize, and an Esc that abandons the stroke rather
|
|
291
593
|
// than exiting the mode.
|
|
292
594
|
if (mode === 'terrain-sculpt') {
|
|
293
|
-
return
|
|
595
|
+
return (
|
|
596
|
+
<ContextualHelperPanel
|
|
597
|
+
hints={terrainSculptHints(terrainVerb, terrainSampling)}
|
|
598
|
+
title={terrainHudTitle(terrainVerb)}
|
|
599
|
+
/>
|
|
600
|
+
)
|
|
294
601
|
}
|
|
295
602
|
|
|
296
603
|
if (scope.kind === 'mesh-editing') {
|
|
297
|
-
return
|
|
604
|
+
return (
|
|
605
|
+
<ContextualHelperPanel
|
|
606
|
+
hints={contextualEditHints}
|
|
607
|
+
snapContext={snapContext}
|
|
608
|
+
title={contextualHelpNode ? nodeKindHudTitle(contextualHelpNode.type, 'Edit') : null}
|
|
609
|
+
/>
|
|
610
|
+
)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
// Divide draws a separator path across the selected room — it reads as a
|
|
615
|
+
// wall draft: the wall's snapping chips (Shift / Ctrl) plus the path keys.
|
|
616
|
+
if (scope.kind === 'room-divide') {
|
|
617
|
+
return (
|
|
618
|
+
<ContextualHelperPanel
|
|
619
|
+
hints={ROOM_DIVIDE_HINTS}
|
|
620
|
+
snapContext={snapContext}
|
|
621
|
+
title={DIVIDE_ROOM_HUD_TITLE}
|
|
622
|
+
/>
|
|
623
|
+
)
|
|
298
624
|
}
|
|
299
625
|
|
|
300
626
|
// Idle select only — an active scope (handle-drag, box-select, …) must not show
|
|
301
627
|
// the idle selection hints.
|
|
302
628
|
if (mode === 'select' && scope.kind === 'idle') {
|
|
303
|
-
return <ContextualHelperPanel hints={selectModeHints} />
|
|
629
|
+
return <ContextualHelperPanel hints={selectModeHints} title={SELECT_HUD_TITLE} />
|
|
304
630
|
}
|
|
305
631
|
|
|
306
632
|
if (tool === 'measurement' && measurementToolKind === 'smart') {
|
|
@@ -311,6 +637,7 @@ export function HelperManager() {
|
|
|
311
637
|
{ keys: ['Click'], label: 'Pin measurement lens' },
|
|
312
638
|
{ keys: ['Esc'], label: 'Exit smart measure' },
|
|
313
639
|
]}
|
|
640
|
+
title={SMART_MEASURE_HUD_TITLE}
|
|
314
641
|
/>
|
|
315
642
|
)
|
|
316
643
|
}
|
|
@@ -328,8 +655,10 @@ export function HelperManager() {
|
|
|
328
655
|
<RegisteredToolHelper
|
|
329
656
|
continuationContext={continuationContext}
|
|
330
657
|
hints={hints}
|
|
658
|
+
notice={placementNotice?.line ?? null}
|
|
331
659
|
shiftPressed={modifiers.shift}
|
|
332
660
|
snapContext={snapContext}
|
|
661
|
+
title={toolHudTitle(tool, wallMode)}
|
|
333
662
|
/>
|
|
334
663
|
)
|
|
335
664
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContinuationContext } from '../../../lib/continuation'
|
|
2
|
+
import type { HudTitle } from '../../../lib/hud-title'
|
|
2
3
|
import type { SnapContext } from '../../../lib/snapping-mode'
|
|
3
4
|
import { ContextualHelperPanel } from './contextual-helper-panel'
|
|
4
5
|
|
|
@@ -11,6 +12,9 @@ interface ItemHelperProps {
|
|
|
11
12
|
// Set for a fresh point-kind placement (e.g. a positioned preset) so the
|
|
12
13
|
// once/repeat continuation chip shows; null for an existing-node move.
|
|
13
14
|
continuationContext?: ContinuationContext | null
|
|
15
|
+
title?: HudTitle | null
|
|
16
|
+
// Why the item would not fit where it is (a door's way, a room too small): a warning, not a block.
|
|
17
|
+
notice?: string | null
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
// Snapping mode is the chip on the right (Shift cycles it), so it's not repeated
|
|
@@ -20,10 +24,13 @@ export function ItemHelper({
|
|
|
20
24
|
snapContext,
|
|
21
25
|
showForce,
|
|
22
26
|
continuationContext = null,
|
|
27
|
+
title = null,
|
|
28
|
+
notice = null,
|
|
23
29
|
}: ItemHelperProps) {
|
|
24
30
|
return (
|
|
25
31
|
<ContextualHelperPanel
|
|
26
32
|
continuationContext={continuationContext}
|
|
33
|
+
notice={notice}
|
|
27
34
|
hints={[
|
|
28
35
|
{ keys: ['Left click'], label: 'Place' },
|
|
29
36
|
{ keys: ['R', 'T'], label: 'Rotate' },
|
|
@@ -31,6 +38,7 @@ export function ItemHelper({
|
|
|
31
38
|
{ keys: [showEsc ? 'Esc' : 'Right click'], label: 'Cancel' },
|
|
32
39
|
]}
|
|
33
40
|
snapContext={snapContext}
|
|
41
|
+
title={title}
|
|
34
42
|
/>
|
|
35
43
|
)
|
|
36
44
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ToolHint } from '@pascal-app/core'
|
|
2
|
-
import { useMemo, useSyncExternalStore } from 'react'
|
|
3
2
|
import type { ContinuationContext } from '../../../lib/continuation'
|
|
3
|
+
import type { HudTitle } from '../../../lib/hud-title'
|
|
4
4
|
import type { SnapContext } from '../../../lib/snapping-mode'
|
|
5
|
-
import
|
|
5
|
+
import { useVisibleToolHints } from '../../../lib/panel-tool-options'
|
|
6
6
|
import { ContextualHelperPanel } from './contextual-helper-panel'
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -19,43 +19,18 @@ export function RegisteredToolHelper({
|
|
|
19
19
|
shiftPressed = false,
|
|
20
20
|
snapContext = null,
|
|
21
21
|
continuationContext = null,
|
|
22
|
+
title = null,
|
|
23
|
+
notice = null,
|
|
22
24
|
}: {
|
|
23
25
|
hints: ToolHint[]
|
|
24
26
|
shiftPressed?: boolean
|
|
25
27
|
snapContext?: SnapContext | null
|
|
26
28
|
continuationContext?: ContinuationContext | null
|
|
29
|
+
title?: HudTitle | null
|
|
30
|
+
notice?: string | null
|
|
27
31
|
}) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const draftVertexCount = useEditor((s) => s.draftVertexCount)
|
|
31
|
-
const visibilityStore = useMemo(
|
|
32
|
-
() => ({
|
|
33
|
-
subscribe: (onChange: () => void) => {
|
|
34
|
-
const unsubscribers = hints.flatMap((hint) =>
|
|
35
|
-
hint.visible ? [hint.visible.subscribe(onChange)] : [],
|
|
36
|
-
)
|
|
37
|
-
return () => {
|
|
38
|
-
for (const unsubscribe of unsubscribers) unsubscribe()
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
getSnapshot: () => hints.map((hint) => (hint.visible?.value() === false ? '0' : '1')).join(''),
|
|
42
|
-
}),
|
|
43
|
-
[hints],
|
|
44
|
-
)
|
|
45
|
-
useSyncExternalStore(
|
|
46
|
-
visibilityStore.subscribe,
|
|
47
|
-
visibilityStore.getSnapshot,
|
|
48
|
-
visibilityStore.getSnapshot,
|
|
49
|
-
)
|
|
50
|
-
// Some hints are replaced by live contextual chips, so keep the generic
|
|
51
|
-
// registry renderer from duplicating stale/static versions.
|
|
52
|
-
const visible = hints.filter(
|
|
53
|
-
(hint) =>
|
|
54
|
-
!(hint.key === 'Shift' && hint.label === 'Cycle snapping mode') &&
|
|
55
|
-
hint.visible?.value() !== false &&
|
|
56
|
-
(hint.minDraftVertices == null || draftVertexCount >= hint.minDraftVertices),
|
|
57
|
-
)
|
|
58
|
-
if (visible.length === 0 && !snapContext && !continuationContext) return null
|
|
32
|
+
const visible = useVisibleToolHints(hints)
|
|
33
|
+
if (visible.length === 0 && !snapContext && !continuationContext && !notice) return null
|
|
59
34
|
// Hints carrying a live-state `chip` render as mode chips next to the
|
|
60
35
|
// snapping / continuation rows; the rest stay static key rows.
|
|
61
36
|
const chipHints = visible.filter((hint) => hint.chip)
|
|
@@ -74,7 +49,9 @@ export function RegisteredToolHelper({
|
|
|
74
49
|
}
|
|
75
50
|
})}
|
|
76
51
|
continuationContext={continuationContext}
|
|
52
|
+
notice={notice}
|
|
77
53
|
snapContext={snapContext}
|
|
54
|
+
title={title}
|
|
78
55
|
/>
|
|
79
56
|
)
|
|
80
57
|
}
|