@pascal-app/editor 1.0.3 → 1.0.5
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/README.md +106 -0
- package/package.json +42 -38
- package/src/components/editor/__bench__/room-label-placement.bench.ts +108 -0
- package/src/components/editor/action-menu-button.tsx +83 -0
- package/src/components/editor/bake-thumbnail.tsx +307 -5
- package/src/components/editor/capture-level-picker.tsx +72 -0
- package/src/components/editor/construction-reveal-audio.test.tsx +150 -0
- package/src/components/editor/construction-reveal-audio.tsx +40 -0
- package/src/components/editor/custom-camera-controls.tsx +275 -57
- 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 -26
- package/src/components/editor/editor-layout-v2.tsx +132 -59
- package/src/components/editor/export-manager.tsx +47 -26
- package/src/components/editor/first-person/build-collider-world.test.ts +320 -0
- package/src/components/editor/first-person/build-collider-world.ts +28 -7
- package/src/components/editor/first-person/ground-holes.test.ts +128 -0
- package/src/components/editor/first-person/ground-holes.ts +130 -0
- package/src/components/editor/first-person/mechanism-targets.test.ts +162 -0
- package/src/components/editor/first-person/mechanism-targets.ts +88 -0
- package/src/components/editor/first-person-controls.tsx +220 -9
- package/src/components/editor/floating-action-menu.tsx +63 -207
- package/src/components/editor/floor-aware-camera-controls.test.ts +466 -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-mode-coordinator.tsx +2 -2
- package/src/components/editor/floorplan-panel.tsx +414 -363
- package/src/components/editor/follow-pascal.test.tsx +912 -0
- package/src/components/editor/follow-pascal.tsx +354 -0
- package/src/components/editor/footprint-height-handle.tsx +204 -0
- package/src/components/editor/group-actions.ts +56 -2
- package/src/components/editor/group-move-3d.ts +2 -0
- package/src/components/editor/group-rotate-handle.tsx +2 -0
- package/src/components/editor/group-transform-shared.test.ts +460 -0
- 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 +269 -197
- package/src/components/editor/index.tsx +409 -197
- package/src/components/editor/join-walls-pill.tsx +44 -0
- package/src/components/editor/node-action-menu.tsx +68 -70
- package/src/components/editor/node-arrow-handles.tsx +25 -6
- 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 +80 -1
- package/src/components/editor/riser-diagram-panel.tsx +14 -7
- 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 +233 -0
- package/src/components/editor/room-labels.test.ts +850 -0
- package/src/components/editor/room-labels.tsx +1890 -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 +474 -419
- package/src/components/editor/slab-hole-highlights.tsx +5 -2
- package/src/components/editor/snapshot-capture-overlay.tsx +7 -2
- package/src/components/editor/snapshot-capture.test.ts +119 -60
- package/src/components/editor/snapshot-capture.ts +36 -5
- package/src/components/editor/thumbnail-generator.test.ts +654 -0
- package/src/components/editor/thumbnail-generator.tsx +1103 -223
- package/src/components/editor/use-floorplan-background-placement.ts +34 -12
- 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 +663 -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.test.tsx +54 -0
- package/src/components/editor/views/view-bar.tsx +343 -0
- package/src/components/editor/wall-justification.test.ts +140 -0
- package/src/components/editor/wall-move-side-handles.tsx +43 -142
- 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 +8 -4
- package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
- package/src/components/editor-2d/floorplan-group-move.tsx +3 -0
- 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 +2 -2
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +116 -106
- 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-wall-measurement.tsx → floorplan-draft-measurement.tsx} +8 -8
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +38 -8
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +319 -233
- package/src/components/editor-2d/renderers/floorplan-reveal.test.tsx +254 -0
- package/src/components/editor-2d/renderers/floorplan-reveal.ts +141 -0
- package/src/components/editor-2d/svg-paths.ts +12 -15
- package/src/components/point-ask/bubble.tsx +538 -0
- package/src/components/point-ask/hover-chip.tsx +91 -0
- package/src/components/point-ask/mode-pill.tsx +92 -0
- package/src/components/point-ask/optional-motion.ts +22 -0
- package/src/components/point-ask/outline-corners.tsx +93 -0
- package/src/components/point-ask/pins.tsx +711 -0
- package/src/components/point-ask/point-ask-layer.tsx +83 -0
- package/src/components/point-ask/point-ask-styles.ts +105 -0
- package/src/components/point-ask/region-marquee-styles.ts +15 -0
- package/src/components/point-ask/region-marquee.tsx +64 -0
- package/src/components/procedural-items/inspector.tsx +40 -15
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +12 -2
- 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/roof/roof-edit-system.tsx +1 -0
- package/src/components/systems/zone/zone-system.test.ts +47 -0
- package/src/components/systems/zone/zone-system.tsx +29 -68
- package/src/components/tools/distribution/connection-compatibility.test.ts +34 -0
- package/src/components/tools/distribution/connection-compatibility.ts +43 -0
- package/src/components/tools/distribution/distribution-public.ts +9 -0
- package/src/components/tools/distribution/distribution-run-contract.test.ts +26 -0
- package/src/components/tools/distribution/distribution-run-contract.ts +94 -0
- package/src/components/tools/distribution/distribution-run-tool.test.ts +241 -0
- package/src/components/tools/distribution/distribution-run-tool.test.tsx +125 -0
- package/src/components/tools/distribution/distribution-run-tool.tsx +1323 -0
- package/src/components/tools/distribution/draw-alignment.ts +35 -0
- package/src/components/tools/distribution/ports.test.ts +56 -0
- package/src/components/tools/distribution/ports.ts +378 -0
- package/src/components/tools/distribution/run-cursor.test.ts +135 -0
- package/src/components/tools/distribution/run-cursor.ts +36 -0
- package/src/components/tools/distribution/run-direction-feedback.test.ts +60 -0
- package/src/components/tools/distribution/run-direction-feedback.tsx +251 -0
- package/src/components/tools/distribution/run-port-snap.test.ts +79 -0
- package/src/components/tools/distribution/run-port-snap.ts +25 -0
- package/src/components/tools/distribution/run-screen-direction.test.ts +23 -0
- package/src/components/tools/distribution/run-screen-direction.ts +30 -0
- package/src/components/tools/elevator/elevator-tool.tsx +3 -6
- package/src/components/tools/elevator/move-elevator-tool.tsx +2 -3
- package/src/components/tools/item/item-surface-strategy-resolver.test.ts +67 -68
- package/src/components/tools/item/placement-history.test.tsx +512 -0
- package/src/components/tools/item/placement-strategies.test.ts +46 -1
- package/src/components/tools/item/placement-strategies.ts +11 -10
- package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +56 -56
- package/src/components/tools/item/use-draft-node.test.tsx +432 -1
- package/src/components/tools/item/use-draft-node.ts +166 -95
- package/src/components/tools/item/use-placement-coordinator.tsx +148 -95
- 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 +62 -63
- package/src/components/tools/registry/item-surface-move.ts +2 -0
- package/src/components/tools/registry/item-surface-resolver.test.ts +26 -26
- package/src/components/tools/registry/move-registry-node-tool.test.ts +268 -1
- package/src/components/tools/registry/move-registry-node-tool.tsx +66 -30
- 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/cursor-sphere.tsx +36 -12
- package/src/components/tools/site/site-boundary-editor.tsx +1 -2
- package/src/components/tools/tool-manager.tsx +59 -24
- package/src/components/tools/wall/wall-drafting.test.ts +175 -47
- package/src/components/tools/zone/zone-tool.tsx +4 -12
- package/src/components/ui/action-menu/action-button.tsx +9 -1
- package/src/components/ui/action-menu/camera-actions.tsx +9 -4
- package/src/components/ui/action-menu/control-modes.tsx +15 -42
- package/src/components/ui/action-menu/follow-pascal-pill.tsx +32 -0
- package/src/components/ui/action-menu/follow-pascal-toggle.tsx +36 -0
- package/src/components/ui/action-menu/index.tsx +56 -33
- package/src/components/ui/action-menu/measurement-control.tsx +14 -19
- package/src/components/ui/action-menu/placement.ts +20 -0
- package/src/components/ui/action-menu/point-ask-button.tsx +52 -0
- package/src/components/ui/action-menu/view-toggles.tsx +47 -42
- package/src/components/ui/build-panel/build-panel.tsx +242 -0
- package/src/components/ui/command-palette/editor-commands.tsx +7 -42
- package/src/components/ui/command-palette/index.tsx +50 -39
- package/src/components/ui/command-palette/ranked-groups.test.ts +31 -0
- package/src/components/ui/controls/action-button.tsx +5 -2
- package/src/components/ui/controls/finish-swatch.tsx +110 -0
- package/src/components/ui/controls/material-paint-panel.tsx +97 -26
- package/src/components/ui/controls/material-picker.tsx +4 -1
- package/src/components/ui/controls/metric-control.tsx +1 -1
- package/src/components/ui/controls/scene-material-list.tsx +4 -32
- package/src/components/ui/controls/segmented-control.tsx +21 -8
- package/src/components/ui/controls/select-control.tsx +65 -0
- package/src/components/ui/controls/slider-control.tsx +1 -1
- package/src/components/ui/controls/toggle-control.tsx +3 -3
- package/src/components/ui/controls/tool-options-panel.tsx +1 -1
- package/src/components/ui/floating-level-selector.tsx +159 -59
- package/src/components/ui/helpers/building-helper.tsx +4 -1
- package/src/components/ui/helpers/contextual-helper-panel.tsx +181 -72
- package/src/components/ui/helpers/helper-manager.test.tsx +121 -0
- package/src/components/ui/helpers/helper-manager.tsx +331 -14
- package/src/components/ui/helpers/item-helper.tsx +17 -1
- package/src/components/ui/helpers/registered-tool-helper.tsx +8 -1
- package/src/components/ui/icon-ref.tsx +45 -1
- 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 +28 -22
- 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 +4 -4
- package/src/components/ui/panels/mobile-selection-bar.tsx +8 -5
- 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-parametric-inspector.tsx +52 -2
- package/src/components/ui/panels/multi-selection-panel.tsx +161 -5
- 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 +130 -62
- package/src/components/ui/panels/parametric-field-control.tsx +3 -3
- package/src/components/ui/panels/parametric-inspector.tsx +7 -7
- 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 +790 -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/badge.tsx +8 -0
- package/src/components/ui/primitives/color-dot.tsx +36 -24
- package/src/components/ui/primitives/sidebar.tsx +3 -1
- package/src/components/ui/right-stack.test.ts +29 -0
- package/src/components/ui/right-stack.tsx +164 -0
- package/src/components/ui/sidebar/app-sidebar.tsx +1 -1
- package/src/components/ui/sidebar/icon-rail.tsx +8 -3
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +1 -1
- package/src/components/ui/sidebar/panels/items-panel/index.tsx +6 -6
- package/src/components/ui/sidebar/panels/settings-panel/animation-speed-setting.tsx +40 -0
- package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +7 -8
- package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.test.tsx +170 -0
- package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.tsx +92 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +138 -27
- package/src/components/ui/sidebar/panels/settings-panel/interface-theme.test.tsx +20 -0
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +5 -5
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +4 -10
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -11
- 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 +181 -223
- 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 +13 -37
- 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 +2 -4
- package/src/components/ui/sidebar/tab-bar.tsx +14 -4
- 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.tsx +5 -6
- package/src/components/viewer/viewer-controls-bar.tsx +18 -6
- package/src/components/viewer/viewer-hover-label.tsx +34 -0
- package/src/components/viewer/viewer-rooms.tsx +191 -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 +3 -8
- package/src/hooks/use-auto-save.integration.test.tsx +215 -43
- package/src/hooks/use-auto-save.ts +60 -9
- package/src/hooks/use-ceiling-events.test.ts +123 -118
- package/src/hooks/use-grid-events.test.ts +105 -105
- package/src/hooks/use-keyboard.test.ts +243 -19
- package/src/hooks/use-keyboard.ts +118 -83
- 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 +71 -0
- package/src/hooks/use-placement-notice.ts +73 -0
- package/src/hooks/use-selected-room.ts +143 -0
- package/src/index.tsx +312 -58
- package/src/lib/__fixtures__/owner-victor-seattle-parking.json +1 -0
- package/src/lib/__fixtures__/owner-villa-aurelia-basement.json +1 -0
- package/src/lib/add-storey.test.ts +177 -0
- package/src/lib/add-storey.ts +59 -0
- package/src/lib/below-grade.test.tsx +171 -0
- package/src/lib/below-grade.ts +133 -0
- package/src/lib/camera-facing.test.ts +24 -0
- package/src/lib/camera-facing.ts +24 -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/continuation.test.ts +19 -1
- package/src/lib/continuation.ts +25 -4
- package/src/lib/direct-manipulation.ts +2 -0
- package/src/lib/door-interaction.ts +14 -0
- package/src/lib/download-project-file.ts +17 -0
- package/src/lib/editor-level-display.test.ts +96 -0
- package/src/lib/editor-level-display.ts +55 -0
- package/src/lib/editor-views.ts +178 -0
- package/src/lib/export-geometry-wait.test.ts +90 -0
- package/src/lib/export-texture-utils.ts +2 -4
- 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 +284 -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 +12 -8
- package/src/lib/floorplan/floorplan-export.tsx +32 -8
- package/src/lib/floorplan/floorplan-extension.ts +32 -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 +363 -27
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +177 -8
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +60 -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/pdfkit-standard-fonts.d.ts +5 -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 +124 -0
- package/src/lib/floorplan/sheet-export.ts +318 -0
- package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +768 -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 +230 -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 +157 -0
- package/src/lib/floorplan/site-plan/geometry.test.ts +269 -0
- package/src/lib/floorplan/site-plan/geometry.ts +142 -0
- package/src/lib/floorplan/site-plan/index.ts +55 -0
- package/src/lib/floorplan/site-plan/site-annotations.test.ts +483 -0
- package/src/lib/floorplan/site-plan/site-annotations.ts +1084 -0
- package/src/lib/floorplan/site-plan/site-parts.ts +287 -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 +70 -0
- package/src/lib/floorplan/types.ts +1 -27
- package/src/lib/follow-pascal-exterior.test-fixture.ts +17 -0
- package/src/lib/follow-pascal-exterior.test.ts +171 -0
- package/src/lib/follow-pascal-exterior.ts +39 -0
- package/src/lib/follow-pascal-presentation.test.ts +203 -0
- package/src/lib/follow-pascal-presentation.ts +129 -0
- package/src/lib/follow-pascal-sight.ts +84 -0
- package/src/lib/follow-pascal-targets.test.ts +406 -0
- package/src/lib/follow-pascal-targets.ts +198 -0
- package/src/lib/follow-pascal.test.ts +350 -0
- package/src/lib/follow-pascal.ts +284 -0
- package/src/lib/fresh-planar-placement.test.ts +148 -0
- package/src/lib/fresh-planar-placement.ts +114 -37
- package/src/lib/geometry-script/author.ts +157 -0
- package/src/lib/geometry-script/client.ts +159 -0
- package/src/lib/geometry-script/finish-scripts.ts +127 -0
- package/src/lib/geometry-script/protocol.ts +13 -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 +636 -126
- package/src/lib/glb-export.ts +241 -120
- package/src/lib/history.test.ts +18 -7
- 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 +22 -0
- package/src/lib/keyboard-pan.test.ts +129 -7
- package/src/lib/keyboard-pan.ts +161 -12
- package/src/lib/level-selection.test.ts +57 -0
- package/src/lib/level-selection.ts +16 -1
- 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 +187 -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 +392 -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 +3 -1
- package/src/lib/material-paint-panel-model.ts +9 -5
- package/src/lib/material-paint.ts +4 -0
- package/src/lib/measurement-kind.test.ts +6 -8
- package/src/lib/measurements.test.ts +23 -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 +201 -0
- package/src/lib/orbit-floor-clearance.ts +154 -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 +122 -0
- package/src/lib/paint-pass-through.ts +78 -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 +267 -25
- package/src/lib/panel-rows.ts +21 -0
- package/src/lib/parametric-node-update.ts +16 -7
- 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 +65 -0
- package/src/lib/plugin-panels.test.ts +25 -0
- package/src/lib/plugin-panels.ts +94 -0
- package/src/lib/point-ask/__fixtures__/consumer-browser.tsx +76 -0
- package/src/lib/point-ask/__fixtures__/house.ts +81 -0
- package/src/lib/point-ask/ask-created.test.ts +195 -0
- package/src/lib/point-ask/ask-flow.test.ts +380 -0
- package/src/lib/point-ask/build-context.test.ts +173 -0
- package/src/lib/point-ask/build-context.ts +94 -0
- package/src/lib/point-ask/capture-crop.ts +129 -0
- package/src/lib/point-ask/choreography.ts +69 -0
- package/src/lib/point-ask/compare.test.ts +145 -0
- package/src/lib/point-ask/compare.ts +73 -0
- package/src/lib/point-ask/describe-target.test.ts +165 -0
- package/src/lib/point-ask/describe-target.ts +234 -0
- package/src/lib/point-ask/dictation.ts +79 -0
- package/src/lib/point-ask/edge-marker.test.ts +61 -0
- package/src/lib/point-ask/edge-marker.ts +27 -0
- package/src/lib/point-ask/frame-loop.ts +25 -0
- package/src/lib/point-ask/history-marks.test.ts +94 -0
- package/src/lib/point-ask/history-marks.ts +39 -0
- package/src/lib/point-ask/host-api.ts +32 -0
- package/src/lib/point-ask/outline-box.test.ts +30 -0
- package/src/lib/point-ask/outline-box.ts +11 -0
- package/src/lib/point-ask/overlay-metrics.ts +6 -0
- package/src/lib/point-ask/pick.test.ts +220 -0
- package/src/lib/point-ask/pick.ts +121 -0
- package/src/lib/point-ask/pin-machine.test.ts +494 -0
- package/src/lib/point-ask/pin-machine.ts +217 -0
- package/src/lib/point-ask/placement.test.ts +203 -0
- package/src/lib/point-ask/placement.ts +141 -0
- package/src/lib/point-ask/pointer-tracker.ts +34 -0
- package/src/lib/point-ask/projector.ts +83 -0
- package/src/lib/point-ask/region.test.ts +157 -0
- package/src/lib/point-ask/region.ts +136 -0
- package/src/lib/point-ask/retry.test.ts +217 -0
- package/src/lib/point-ask/retry.ts +37 -0
- package/src/lib/point-ask/scene-commit.test.ts +312 -0
- package/src/lib/point-ask/scene-commit.ts +32 -0
- package/src/lib/point-ask/session.ts +1391 -0
- package/src/lib/point-ask/spring-keyframes.test.ts +45 -0
- package/src/lib/point-ask/spring-keyframes.ts +38 -0
- package/src/lib/point-ask/spring.test.ts +63 -0
- package/src/lib/point-ask/spring.ts +107 -0
- package/src/lib/point-ask/suggestions.test.ts +80 -0
- package/src/lib/point-ask/suggestions.ts +125 -0
- package/src/lib/point-ask/target-scope.test.ts +622 -0
- package/src/lib/point-ask/target-scope.ts +338 -0
- package/src/lib/point-ask/types.ts +48 -0
- package/src/lib/portable-export.test.ts +34 -1
- package/src/lib/portable-export.ts +119 -53
- package/src/lib/print-feature-thickness.ts +10 -1
- package/src/lib/print-roof-solids.test.ts +39 -1
- package/src/lib/print-roof-solids.ts +43 -10
- package/src/lib/print-shell-compiler-manifold-core.ts +1 -1
- 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 +265 -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 +1017 -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 +610 -1
- package/src/lib/scene-clipboard.ts +269 -58
- 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 +24 -36
- package/src/lib/scene.ts +33 -57
- package/src/lib/selection-routing.test.ts +129 -45
- package/src/lib/selection-routing.ts +54 -75
- package/src/lib/session-writes.test.ts +120 -0
- package/src/lib/session-writes.ts +205 -0
- package/src/lib/sfx-bus.ts +13 -0
- package/src/lib/sfx-player.test.ts +70 -9
- package/src/lib/sfx-player.ts +6 -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 +4 -0
- package/src/lib/snapping-mode.ts +8 -1
- package/src/lib/stair-levels.test.ts +25 -0
- package/src/lib/stair-levels.ts +22 -3
- package/src/lib/terrace-draft.test.ts +145 -0
- package/src/lib/terrace-draft.ts +214 -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 +580 -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-placement-pointer.test.ts +42 -0
- package/src/lib/wall-placement-pointer.ts +57 -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-changes.ts +35 -0
- package/src/lib/window-interaction.ts +14 -0
- 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/terrain-sculpt-mode.test.ts +19 -21
- package/src/store/tool-mode.test.ts +137 -6
- package/src/store/use-audio.test.ts +146 -0
- package/src/store/use-audio.tsx +127 -3
- package/src/store/use-construction-reveal.test.ts +173 -0
- package/src/store/use-construction-reveal.ts +145 -0
- package/src/store/use-corner-snap-hint.test.ts +24 -0
- package/src/store/use-corner-snap-hint.ts +47 -0
- 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 +432 -321
- package/src/store/use-floorplan-draft-preview.ts +13 -0
- package/src/store/use-floorplan-marquee.ts +12 -2
- package/src/store/use-follow-pascal.test.ts +62 -0
- package/src/store/use-follow-pascal.ts +53 -0
- package/src/store/use-point-ask.ts +247 -0
- package/src/store/use-quick-measurement-hud.ts +3 -3
- package/src/store/use-stair-build-preview.ts +33 -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 +201 -0
- package/src/store/view-layout.ts +199 -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/render-current-markup.tsx +16 -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/tools/stair/stair-click-guard.test.ts +0 -58
- package/src/components/tools/stair/stair-click-guard.ts +0 -72
- package/src/components/tools/stair/stair-defaults.ts +0 -18
- package/src/components/tools/stair/stair-tool.tsx +0 -634
- 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,11 +21,52 @@ 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'
|
|
69
|
+
import { cornerHintRows, useCornerSnapHint } from '../../../store/use-corner-snap-hint'
|
|
28
70
|
import useEditor, { getActiveContinuationContext } from '../../../store/use-editor'
|
|
29
71
|
import useInteractionScope, {
|
|
30
72
|
useActiveHandleDrag,
|
|
@@ -33,6 +75,7 @@ import useInteractionScope, {
|
|
|
33
75
|
} from '../../../store/use-interaction-scope'
|
|
34
76
|
import { BuildingHelper } from './building-helper'
|
|
35
77
|
import { ContextualHelperPanel } from './contextual-helper-panel'
|
|
78
|
+
import { transientDraftId, usePlacementNotice } from '../../../hooks/use-placement-notice'
|
|
36
79
|
import { ItemHelper } from './item-helper'
|
|
37
80
|
import { RegisteredToolHelper } from './registered-tool-helper'
|
|
38
81
|
|
|
@@ -91,6 +134,89 @@ type ActiveModifierKeys = {
|
|
|
91
134
|
}
|
|
92
135
|
|
|
93
136
|
const EMPTY_CONTEXTUAL_HINTS: ContextualShortcutHint[] = []
|
|
137
|
+
|
|
138
|
+
// The paint tool's region sub-modes: what one gesture draws, then Esc. A
|
|
139
|
+
// refused gesture (the per-face cap) shows as the first row.
|
|
140
|
+
/** What a click does in each whole-surface sub-mode; only painting teaches the held eyedropper. */
|
|
141
|
+
export function paintHints(mode: PaintMode): ContextualShortcutHint[] {
|
|
142
|
+
if (mode === 'pick')
|
|
143
|
+
return [
|
|
144
|
+
{ keys: ['Left click'], label: 'Paint with this material' },
|
|
145
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
146
|
+
]
|
|
147
|
+
if (mode === 'erase')
|
|
148
|
+
return [{ keys: ['Left click'], label: 'Remove a painted part, or reset a surface' }]
|
|
149
|
+
if (mode === 'surface') return [{ keys: [['Alt', 'Option']], label: 'Hold to pick a material' }]
|
|
150
|
+
return []
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function paintRegionHints(
|
|
154
|
+
mode: 'rectangle' | 'polygon',
|
|
155
|
+
notice: string | null,
|
|
156
|
+
hovering = true,
|
|
157
|
+
): ContextualShortcutHint[] {
|
|
158
|
+
const gesture: Record<typeof mode, ContextualShortcutHint> = {
|
|
159
|
+
rectangle: { keys: ['Drag'], label: 'Press anywhere, drag to the opposite corner' },
|
|
160
|
+
polygon: { keys: ['Left click'], label: 'Add point · click the first to close' },
|
|
161
|
+
}
|
|
162
|
+
// Off anything the sub-mode draws on, the HUD says where to go instead.
|
|
163
|
+
if (!hovering)
|
|
164
|
+
return [
|
|
165
|
+
...(notice ? [{ keys: ['!'], label: notice, active: true }] : []),
|
|
166
|
+
{ keys: ['Hover'], label: paintRegionHoverHint(mode), active: true },
|
|
167
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
168
|
+
]
|
|
169
|
+
return [
|
|
170
|
+
...(notice ? [{ keys: ['!'], label: notice, active: true }] : []),
|
|
171
|
+
gesture[mode],
|
|
172
|
+
...(mode === 'polygon' ? [{ keys: ['Backspace'], label: 'Remove last point' }] : []),
|
|
173
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
// "Add mezzanine": the outline gesture for the shape picked in the room panel,
|
|
177
|
+
// then Esc; the snapping chip sits below.
|
|
178
|
+
export function mezzanineHints(shape: MezzanineShape): ContextualShortcutHint[] {
|
|
179
|
+
return shape === 'rectangle'
|
|
180
|
+
? [
|
|
181
|
+
{ keys: ['Left click'], label: 'Set one corner, then the opposite' },
|
|
182
|
+
{ keys: ['Drag'], label: 'Draw the mezzanine' },
|
|
183
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
184
|
+
]
|
|
185
|
+
: [
|
|
186
|
+
{ keys: ['Left click'], label: 'Add point · click the first to close' },
|
|
187
|
+
{ keys: ['Backspace'], label: 'Remove last point' },
|
|
188
|
+
{ keys: ['Enter'], label: 'Finish' },
|
|
189
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
/** "Cut opening": the outline gesture for the chosen shape, then Esc. */
|
|
193
|
+
export function openingHints(shape: OpeningShape): ContextualShortcutHint[] {
|
|
194
|
+
return mezzanineHints(shape).map((hint) =>
|
|
195
|
+
hint.label === 'Draw the mezzanine' ? { ...hint, label: 'Draw the opening' } : hint,
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
const TERRACE_CHIP_HINTS = [TERRACE_SHAPE_HINT]
|
|
199
|
+
// The terrace tool: the outline gesture for the chosen shape, then Esc.
|
|
200
|
+
export function terraceHints(shape: TerraceShape): ContextualShortcutHint[] {
|
|
201
|
+
return shape === 'rectangle'
|
|
202
|
+
? [
|
|
203
|
+
{ keys: ['Left click'], label: 'Set one corner, then the opposite' },
|
|
204
|
+
{ keys: ['Drag'], label: 'Draw the terrace' },
|
|
205
|
+
{ keys: ['Esc'], label: 'Done' },
|
|
206
|
+
]
|
|
207
|
+
: [
|
|
208
|
+
{ keys: ['Left click'], label: 'Add point · click the first to close' },
|
|
209
|
+
{ keys: ['Backspace'], label: 'Remove last point' },
|
|
210
|
+
{ keys: ['Enter'], label: 'Finish' },
|
|
211
|
+
{ keys: ['Esc'], label: 'Done' },
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
export const ROOM_DIVIDE_HINTS: ContextualShortcutHint[] = [
|
|
215
|
+
{ keys: ['Left click'], label: 'Add point' },
|
|
216
|
+
{ keys: ['Backspace'], label: 'Remove last point' },
|
|
217
|
+
{ keys: ['Enter'], label: 'Finish at the edge' },
|
|
218
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
219
|
+
]
|
|
94
220
|
const NO_CONTEXTUAL_HELP_SUBSCRIPTION = () => () => {}
|
|
95
221
|
|
|
96
222
|
function useActiveModifierKeys(): ActiveModifierKeys {
|
|
@@ -129,6 +255,43 @@ function useActiveModifierKeys(): ActiveModifierKeys {
|
|
|
129
255
|
return modifiers
|
|
130
256
|
}
|
|
131
257
|
|
|
258
|
+
export type MezzanineGesture = 'raise' | 'resize' | 'move'
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* A handle drag on a mezzanine — its floor handle, an edge arrow, or its pick-up
|
|
262
|
+
* (Move / Duplicate) — or null. Its HUD names the mezzanine, not "Resize" or
|
|
263
|
+
* "Move room".
|
|
264
|
+
*/
|
|
265
|
+
export function mezzanineGesture(
|
|
266
|
+
drag: { nodeId: string; label: string } | null,
|
|
267
|
+
nodes: Readonly<Record<string, AnyNode>>,
|
|
268
|
+
): MezzanineGesture | null {
|
|
269
|
+
if (!drag) return null
|
|
270
|
+
const zone = nodes[drag.nodeId]
|
|
271
|
+
if (zone?.type !== 'zone' || zone.floor?.support !== 'open') return null
|
|
272
|
+
if (drag.label === ROOM_ELEVATION_DRAG_LABEL) return 'raise'
|
|
273
|
+
if (drag.label === MEZZANINE_EDGE_DRAG_LABEL) return 'resize'
|
|
274
|
+
if (drag.label === ROOM_MOVE_DRAG_LABEL) return 'move'
|
|
275
|
+
return null
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export const MEZZANINE_GESTURE_HINTS: Record<MezzanineGesture, ContextualShortcutHint[]> = {
|
|
279
|
+
raise: [
|
|
280
|
+
{ keys: ['Drag'], label: 'Raise or lower' },
|
|
281
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
282
|
+
],
|
|
283
|
+
resize: [
|
|
284
|
+
{ keys: ['Drag'], label: 'Resize' },
|
|
285
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
286
|
+
],
|
|
287
|
+
move: [
|
|
288
|
+
{ keys: ['Left click'], label: 'Place inside the room' },
|
|
289
|
+
{ keys: [['R', 'T']], label: 'Rotate ±45°' },
|
|
290
|
+
{ keys: ['Alt'], label: 'Free move' },
|
|
291
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
292
|
+
],
|
|
293
|
+
}
|
|
294
|
+
|
|
132
295
|
export function HelperManager() {
|
|
133
296
|
const mode = useEditor((s) => s.mode)
|
|
134
297
|
const tool = useEditor((s) => s.tool)
|
|
@@ -136,11 +299,25 @@ export function HelperManager() {
|
|
|
136
299
|
const terrainSampling = useEditor((s) => s.terrainSampling)
|
|
137
300
|
const isFirstPersonMode = useEditor((s) => s.isFirstPersonMode)
|
|
138
301
|
const measurementToolKind = useEditor((s) => s.toolDefaults.measurement?.kind)
|
|
302
|
+
const wallMode = useEditor((s) => s.continuationByContext.wall)
|
|
139
303
|
const workspaceMode = useEditor((s) => s.workspaceMode)
|
|
140
304
|
const scope = useInteractionScope((s) => s.scope)
|
|
305
|
+
const paintRegionMode = usePaintRegionMode((s) => s.mode)
|
|
306
|
+
const paintRegionDrawing = paintRegionMode === 'rectangle' || paintRegionMode === 'polygon'
|
|
307
|
+
const paintRegionNotice = usePaintRegionMode((s) => s.notice)
|
|
308
|
+
const paintRegionHovering = usePaintRegionHovering()
|
|
309
|
+
const mezzanineShape = useMezzanineDraft((s) => (s.host ? s.shape : null))
|
|
310
|
+
const openingShape = useOpeningDraft((s) => (s.host ? s.shape : null))
|
|
311
|
+
const terraceShape = useTerraceDraft((s) => (s.host ? s.shape : null))
|
|
141
312
|
const movingNode = useMovingNode()
|
|
313
|
+
const cornerHint = useCornerSnapHint((s) => s.hint)
|
|
314
|
+
// A tool whose kind gives placement notices: its transient draft is the node in hand.
|
|
315
|
+
const toolNotices = !!(tool && nodeRegistry.get(tool)?.placementNotice)
|
|
316
|
+
const toolDraftId = useScene((s) => (toolNotices ? transientDraftId(s.nodes) : null))
|
|
317
|
+
const placementNotice = usePlacementNotice(movingNode?.id ?? toolDraftId, toolNotices)
|
|
142
318
|
const reshapingNode = useReshapingNode()
|
|
143
319
|
const activeHandleDrag = useActiveHandleDrag()
|
|
320
|
+
const mezzanineHandle = useScene((s) => mezzanineGesture(activeHandleDrag, s.nodes))
|
|
144
321
|
const selectedIds = useViewer((s) => s.selection.selectedIds)
|
|
145
322
|
const isMobile = useIsMobile()
|
|
146
323
|
const modifiers = useActiveModifierKeys()
|
|
@@ -180,11 +357,14 @@ export function HelperManager() {
|
|
|
180
357
|
return node ? nodeRegistry.get(node.type)?.snapProfile : undefined
|
|
181
358
|
},
|
|
182
359
|
draftDirectionalOf: (typeOrTool) => nodeRegistry.get(typeOrTool)?.snapDraftDirectional ?? true,
|
|
360
|
+
paintRegion: mode === 'material-paint' && paintRegionDrawing,
|
|
183
361
|
}),
|
|
184
|
-
[scope, mode, tool],
|
|
362
|
+
[scope, mode, tool, paintRegionDrawing],
|
|
185
363
|
)
|
|
364
|
+
// Contexts whose mode is picked in the Build panel (the wall's Rooms variant)
|
|
365
|
+
// get no HUD chip: the header names the variant instead.
|
|
186
366
|
const continuationContext = useMemo(
|
|
187
|
-
() => getActiveContinuationContext(),
|
|
367
|
+
() => keyCyclableContinuationContext(getActiveContinuationContext()),
|
|
188
368
|
[scope, mode, tool],
|
|
189
369
|
)
|
|
190
370
|
const selectModeHints = useMemo(() => {
|
|
@@ -226,6 +406,7 @@ export function HelperManager() {
|
|
|
226
406
|
<ContextualHelperPanel
|
|
227
407
|
hints={resolveRotateHandleHelpHints(modifiers.alt)}
|
|
228
408
|
snapContext={snapContext}
|
|
409
|
+
title={ROTATE_HUD_TITLE}
|
|
229
410
|
/>
|
|
230
411
|
)
|
|
231
412
|
}
|
|
@@ -238,6 +419,79 @@ export function HelperManager() {
|
|
|
238
419
|
<ContextualHelperPanel
|
|
239
420
|
hints={[{ keys: ['R / T'], label: 'Rotate the selection ±45°' }]}
|
|
240
421
|
snapContext={snapContext}
|
|
422
|
+
title={MOVE_SELECTION_HUD_TITLE}
|
|
423
|
+
/>
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (mezzanineHandle) {
|
|
428
|
+
return (
|
|
429
|
+
<ContextualHelperPanel
|
|
430
|
+
hints={MEZZANINE_GESTURE_HINTS[mezzanineHandle]}
|
|
431
|
+
snapContext={snapContext}
|
|
432
|
+
title={MEZZANINE_HUD_TITLE}
|
|
433
|
+
/>
|
|
434
|
+
)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// A picked-up room (Move / Duplicate / Rotate from its pill): the same keys
|
|
438
|
+
// as a group pick-up.
|
|
439
|
+
if (activeHandleDrag?.label === ROOM_MOVE_DRAG_LABEL) {
|
|
440
|
+
return (
|
|
441
|
+
<ContextualHelperPanel
|
|
442
|
+
hints={[
|
|
443
|
+
{ keys: ['Left click'], label: 'Place' },
|
|
444
|
+
{ keys: [['R', 'T']], label: 'Rotate ±45°' },
|
|
445
|
+
{ keys: ['Alt'], label: 'Free move · slide doors and windows' },
|
|
446
|
+
{ keys: ['Esc'], label: 'Cancel' },
|
|
447
|
+
]}
|
|
448
|
+
snapContext={snapContext}
|
|
449
|
+
title={MOVE_ROOM_HUD_TITLE}
|
|
450
|
+
/>
|
|
451
|
+
)
|
|
452
|
+
}
|
|
453
|
+
// A paint region gesture holds a handle-drag scope while it draws; its HUD
|
|
454
|
+
// stays the region one (Backspace / close hints for a polygon mid-draft).
|
|
455
|
+
if (mode === 'material-paint' && paintRegionDrawing) {
|
|
456
|
+
return (
|
|
457
|
+
<ContextualHelperPanel
|
|
458
|
+
hints={paintRegionHints(paintRegionMode, paintRegionNotice, paintRegionHovering)}
|
|
459
|
+
snapContext={snapContext}
|
|
460
|
+
title={paintRegionHudTitle(paintRegionMode)}
|
|
461
|
+
/>
|
|
462
|
+
)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// The terrace tool draws like "Add mezzanine": same outline gestures and chip.
|
|
466
|
+
if (terraceShape) {
|
|
467
|
+
return (
|
|
468
|
+
<ContextualHelperPanel
|
|
469
|
+
chipHints={TERRACE_CHIP_HINTS}
|
|
470
|
+
hints={terraceHints(terraceShape)}
|
|
471
|
+
snapContext={snapContext}
|
|
472
|
+
title={TERRACE_HUD_TITLE}
|
|
473
|
+
/>
|
|
474
|
+
)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (openingShape) {
|
|
478
|
+
return (
|
|
479
|
+
<ContextualHelperPanel
|
|
480
|
+
hints={openingHints(openingShape)}
|
|
481
|
+
snapContext={snapContext}
|
|
482
|
+
title={OPENING_HUD_TITLE}
|
|
483
|
+
/>
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// "Add mezzanine" holds a handle-drag scope on its host room for the whole
|
|
488
|
+
// tool, so it must win over the generic resize HUD below.
|
|
489
|
+
if (mezzanineShape) {
|
|
490
|
+
return (
|
|
491
|
+
<ContextualHelperPanel
|
|
492
|
+
hints={mezzanineHints(mezzanineShape)}
|
|
493
|
+
snapContext={snapContext}
|
|
494
|
+
title={MEZZANINE_HUD_TITLE}
|
|
241
495
|
/>
|
|
242
496
|
)
|
|
243
497
|
}
|
|
@@ -254,6 +508,7 @@ export function HelperManager() {
|
|
|
254
508
|
{ keys: ['Esc'], label: 'Cancel' },
|
|
255
509
|
]}
|
|
256
510
|
snapContext={snapContext}
|
|
511
|
+
title={RESIZE_HUD_TITLE}
|
|
257
512
|
/>
|
|
258
513
|
)
|
|
259
514
|
}
|
|
@@ -266,30 +521,54 @@ export function HelperManager() {
|
|
|
266
521
|
const hints = reshapingNode
|
|
267
522
|
? nodeRegistry.get(reshapingNode.type)?.affordanceHints?.[scope.reshape]
|
|
268
523
|
: undefined
|
|
524
|
+
const title = reshapingNode
|
|
525
|
+
? nodeKindHudTitle(reshapingNode.type, scope.reshape === 'split' ? 'Split' : 'Edit')
|
|
526
|
+
: null
|
|
269
527
|
if (hints) {
|
|
270
528
|
return (
|
|
271
|
-
<RegisteredToolHelper
|
|
529
|
+
<RegisteredToolHelper
|
|
530
|
+
hints={hints}
|
|
531
|
+
shiftPressed={modifiers.shift}
|
|
532
|
+
snapContext={snapContext}
|
|
533
|
+
title={title}
|
|
534
|
+
/>
|
|
272
535
|
)
|
|
273
536
|
}
|
|
274
|
-
return
|
|
537
|
+
return (
|
|
538
|
+
<ContextualHelperPanel
|
|
539
|
+
hints={reshapingHints(scope.reshape)}
|
|
540
|
+
snapContext={snapContext}
|
|
541
|
+
title={title}
|
|
542
|
+
/>
|
|
543
|
+
)
|
|
275
544
|
}
|
|
276
545
|
|
|
277
546
|
if (movingNode) {
|
|
278
|
-
if (movingNode.type === 'building') return <BuildingHelper showRotate />
|
|
279
547
|
// A fresh placement (e.g. a positioned preset like a shelf) advertises its
|
|
280
548
|
// once/repeat continuation, exactly like the GLB item tool — but an existing
|
|
281
549
|
// node being *moved* is not a placement, so it gets no continuation chip.
|
|
282
|
-
const
|
|
283
|
-
|
|
550
|
+
const isFreshPlacement = isFreshPlacementMetadata(movingNode.metadata)
|
|
551
|
+
const movingTitle = nodeKindHudTitle(movingNode.type, isFreshPlacement ? undefined : 'Move')
|
|
552
|
+
if (movingNode.type === 'building') return <BuildingHelper showRotate title={movingTitle} />
|
|
553
|
+
const movingContinuationContext = isFreshPlacement
|
|
554
|
+
? keyCyclableContinuationContext(continuationContextOf(movingNode.type))
|
|
284
555
|
: null
|
|
285
|
-
const collisionValidatesDrop =
|
|
286
|
-
nodeRegistry.get(movingNode.type)?.capabilities.floorPlaced
|
|
556
|
+
const collisionValidatesDrop = floorPlacedCollides(
|
|
557
|
+
nodeRegistry.get(movingNode.type)?.capabilities.floorPlaced,
|
|
558
|
+
movingNode,
|
|
559
|
+
)
|
|
287
560
|
return (
|
|
288
561
|
<ItemHelper
|
|
289
562
|
continuationContext={movingContinuationContext}
|
|
563
|
+
notice={placementNotice?.line}
|
|
290
564
|
showEsc
|
|
565
|
+
extraHints={movingNode.type === 'window' ? cornerHintRows(cornerHint) : []}
|
|
566
|
+
replacesPlace={
|
|
567
|
+
movingNode.type === 'window' && (cornerHint === 'join' || cornerHint === 'new')
|
|
568
|
+
}
|
|
291
569
|
showForce={collisionValidatesDrop}
|
|
292
570
|
snapContext={snapContext}
|
|
571
|
+
title={movingTitle}
|
|
293
572
|
/>
|
|
294
573
|
)
|
|
295
574
|
}
|
|
@@ -298,7 +577,19 @@ export function HelperManager() {
|
|
|
298
577
|
// only contextual control here. The chip hides itself for targets that only
|
|
299
578
|
// paint one surface, so this renders nothing until a scoped target is active.
|
|
300
579
|
if (mode === 'material-paint') {
|
|
301
|
-
return
|
|
580
|
+
return (
|
|
581
|
+
<ContextualHelperPanel
|
|
582
|
+
hints={paintHints(paintRegionMode)}
|
|
583
|
+
showPaintScope
|
|
584
|
+
title={
|
|
585
|
+
paintRegionMode === 'pick'
|
|
586
|
+
? PICK_MATERIAL_HUD_TITLE
|
|
587
|
+
: paintRegionMode === 'erase'
|
|
588
|
+
? ERASE_HUD_TITLE
|
|
589
|
+
: PAINT_HUD_TITLE
|
|
590
|
+
}
|
|
591
|
+
/>
|
|
592
|
+
)
|
|
302
593
|
}
|
|
303
594
|
|
|
304
595
|
// Sculpt mode. The HUD is what makes a sustained brush mode legible: it names
|
|
@@ -307,18 +598,41 @@ export function HelperManager() {
|
|
|
307
598
|
// mode-specific — bracket resize, and an Esc that abandons the stroke rather
|
|
308
599
|
// than exiting the mode.
|
|
309
600
|
if (mode === 'terrain-sculpt') {
|
|
310
|
-
return
|
|
601
|
+
return (
|
|
602
|
+
<ContextualHelperPanel
|
|
603
|
+
hints={terrainSculptHints(terrainVerb, terrainSampling)}
|
|
604
|
+
title={terrainHudTitle(terrainVerb)}
|
|
605
|
+
/>
|
|
606
|
+
)
|
|
311
607
|
}
|
|
312
608
|
|
|
313
609
|
if (scope.kind === 'mesh-editing') {
|
|
314
|
-
return
|
|
610
|
+
return (
|
|
611
|
+
<ContextualHelperPanel
|
|
612
|
+
hints={contextualEditHints}
|
|
613
|
+
snapContext={snapContext}
|
|
614
|
+
title={contextualHelpNode ? nodeKindHudTitle(contextualHelpNode.type, 'Edit') : null}
|
|
615
|
+
/>
|
|
616
|
+
)
|
|
315
617
|
}
|
|
316
618
|
|
|
317
619
|
|
|
620
|
+
// Divide draws a separator path across the selected room — it reads as a
|
|
621
|
+
// wall draft: the wall's snapping chips (Shift / Ctrl) plus the path keys.
|
|
622
|
+
if (scope.kind === 'room-divide') {
|
|
623
|
+
return (
|
|
624
|
+
<ContextualHelperPanel
|
|
625
|
+
hints={ROOM_DIVIDE_HINTS}
|
|
626
|
+
snapContext={snapContext}
|
|
627
|
+
title={DIVIDE_ROOM_HUD_TITLE}
|
|
628
|
+
/>
|
|
629
|
+
)
|
|
630
|
+
}
|
|
631
|
+
|
|
318
632
|
// Idle select only — an active scope (handle-drag, box-select, …) must not show
|
|
319
633
|
// the idle selection hints.
|
|
320
634
|
if (mode === 'select' && scope.kind === 'idle') {
|
|
321
|
-
return <ContextualHelperPanel hints={selectModeHints} />
|
|
635
|
+
return <ContextualHelperPanel hints={selectModeHints} title={SELECT_HUD_TITLE} />
|
|
322
636
|
}
|
|
323
637
|
|
|
324
638
|
if (tool === 'measurement' && measurementToolKind === 'smart') {
|
|
@@ -329,6 +643,7 @@ export function HelperManager() {
|
|
|
329
643
|
{ keys: ['Click'], label: 'Pin measurement lens' },
|
|
330
644
|
{ keys: ['Esc'], label: 'Exit smart measure' },
|
|
331
645
|
]}
|
|
646
|
+
title={SMART_MEASURE_HUD_TITLE}
|
|
332
647
|
/>
|
|
333
648
|
)
|
|
334
649
|
}
|
|
@@ -346,8 +661,10 @@ export function HelperManager() {
|
|
|
346
661
|
<RegisteredToolHelper
|
|
347
662
|
continuationContext={continuationContext}
|
|
348
663
|
hints={hints}
|
|
664
|
+
notice={placementNotice?.line ?? null}
|
|
349
665
|
shiftPressed={modifiers.shift}
|
|
350
666
|
snapContext={snapContext}
|
|
667
|
+
title={toolHudTitle(tool, wallMode)}
|
|
351
668
|
/>
|
|
352
669
|
)
|
|
353
670
|
}
|
|
@@ -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,13 @@ 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
|
|
18
|
+
// Rows a kind adds while it is in hand (a window near a corner says what dropping it does).
|
|
19
|
+
extraHints?: { keys: string[]; label: string }[]
|
|
20
|
+
// The extra rows say what a click does, so the plain Place row is left out.
|
|
21
|
+
replacesPlace?: boolean
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
// Snapping mode is the chip on the right (Shift cycles it), so it's not repeated
|
|
@@ -20,17 +28,25 @@ export function ItemHelper({
|
|
|
20
28
|
snapContext,
|
|
21
29
|
showForce,
|
|
22
30
|
continuationContext = null,
|
|
31
|
+
title = null,
|
|
32
|
+
notice = null,
|
|
33
|
+
extraHints = [],
|
|
34
|
+
replacesPlace = false,
|
|
23
35
|
}: ItemHelperProps) {
|
|
24
36
|
return (
|
|
25
37
|
<ContextualHelperPanel
|
|
26
38
|
continuationContext={continuationContext}
|
|
39
|
+
notice={notice}
|
|
27
40
|
hints={[
|
|
28
|
-
|
|
41
|
+
// Where a click does something else (joining a corner), that row says so instead.
|
|
42
|
+
...(replacesPlace ? [] : [{ keys: ['Left click'], label: 'Place' }]),
|
|
43
|
+
...extraHints,
|
|
29
44
|
{ keys: ['R', 'T'], label: 'Rotate' },
|
|
30
45
|
...(showForce ? [{ keys: ['Alt'], label: 'Force place' }] : []),
|
|
31
46
|
{ keys: [showEsc ? 'Esc' : 'Right click'], label: 'Cancel' },
|
|
32
47
|
]}
|
|
33
48
|
snapContext={snapContext}
|
|
49
|
+
title={title}
|
|
34
50
|
/>
|
|
35
51
|
)
|
|
36
52
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ToolHint } from '@pascal-app/core'
|
|
2
2
|
import type { ContinuationContext } from '../../../lib/continuation'
|
|
3
|
+
import type { HudTitle } from '../../../lib/hud-title'
|
|
3
4
|
import type { SnapContext } from '../../../lib/snapping-mode'
|
|
4
5
|
import { useVisibleToolHints } from '../../../lib/panel-tool-options'
|
|
5
6
|
import { ContextualHelperPanel } from './contextual-helper-panel'
|
|
@@ -18,14 +19,18 @@ export function RegisteredToolHelper({
|
|
|
18
19
|
shiftPressed = false,
|
|
19
20
|
snapContext = null,
|
|
20
21
|
continuationContext = null,
|
|
22
|
+
title = null,
|
|
23
|
+
notice = null,
|
|
21
24
|
}: {
|
|
22
25
|
hints: ToolHint[]
|
|
23
26
|
shiftPressed?: boolean
|
|
24
27
|
snapContext?: SnapContext | null
|
|
25
28
|
continuationContext?: ContinuationContext | null
|
|
29
|
+
title?: HudTitle | null
|
|
30
|
+
notice?: string | null
|
|
26
31
|
}) {
|
|
27
32
|
const visible = useVisibleToolHints(hints)
|
|
28
|
-
if (visible.length === 0 && !snapContext && !continuationContext) return null
|
|
33
|
+
if (visible.length === 0 && !snapContext && !continuationContext && !notice) return null
|
|
29
34
|
// Hints carrying a live-state `chip` render as mode chips next to the
|
|
30
35
|
// snapping / continuation rows; the rest stay static key rows.
|
|
31
36
|
const chipHints = visible.filter((hint) => hint.chip)
|
|
@@ -44,7 +49,9 @@ export function RegisteredToolHelper({
|
|
|
44
49
|
}
|
|
45
50
|
})}
|
|
46
51
|
continuationContext={continuationContext}
|
|
52
|
+
notice={notice}
|
|
47
53
|
snapContext={snapContext}
|
|
54
|
+
title={title}
|
|
48
55
|
/>
|
|
49
56
|
)
|
|
50
57
|
}
|
|
@@ -3,6 +3,50 @@
|
|
|
3
3
|
import { Icon } from '@iconify/react'
|
|
4
4
|
import type { IconRef } from '@pascal-app/core'
|
|
5
5
|
import { type ComponentType, lazy, Suspense } from 'react'
|
|
6
|
+
import { cn } from '../../lib/utils'
|
|
7
|
+
|
|
8
|
+
const SVG_DATA_URI = /^data:image\/svg\+xml[^,]*,/
|
|
9
|
+
|
|
10
|
+
/** Whether a `data:image/svg+xml` URI's markup paints with `currentColor`. */
|
|
11
|
+
function isCurrentColorSvg(src: string): boolean {
|
|
12
|
+
const head = SVG_DATA_URI.exec(src)
|
|
13
|
+
if (!head) return false
|
|
14
|
+
const body = src.slice(head[0].length)
|
|
15
|
+
try {
|
|
16
|
+
const markup = head[0].includes(';base64') ? atob(body) : decodeURIComponent(body)
|
|
17
|
+
return markup.includes('currentColor')
|
|
18
|
+
} catch {
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A `url`-kind icon. An SVG behind an `<img>` renders in its own document, so
|
|
25
|
+
* `currentColor` in the markup resolves to black rather than the surrounding
|
|
26
|
+
* text colour — a monochrome plugin glyph then disappears against the dark
|
|
27
|
+
* sidebar. Such an SVG becomes a CSS mask over the text colour instead. Plugin
|
|
28
|
+
* manifests are third-party, so the markup is never inlined into the page.
|
|
29
|
+
*/
|
|
30
|
+
export function IconRefImage({
|
|
31
|
+
className,
|
|
32
|
+
size,
|
|
33
|
+
src,
|
|
34
|
+
}: {
|
|
35
|
+
className?: string
|
|
36
|
+
size?: number
|
|
37
|
+
src: string
|
|
38
|
+
}) {
|
|
39
|
+
const style = size === undefined ? undefined : { height: size, width: size }
|
|
40
|
+
if (!isCurrentColorSvg(src)) return <img alt="" className={className} src={src} style={style} />
|
|
41
|
+
const mask = `url("${src.replace(/["\\\n\r]/g, encodeURIComponent)}") center / contain no-repeat`
|
|
42
|
+
return (
|
|
43
|
+
<span
|
|
44
|
+
aria-hidden
|
|
45
|
+
className={cn('inline-block bg-current', className)}
|
|
46
|
+
style={{ ...style, mask, WebkitMask: mask }}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
6
50
|
|
|
7
51
|
// `React.lazy` must be called once per loader so the resolved component keeps
|
|
8
52
|
// a stable identity across renders (otherwise every parent re-render remounts
|
|
@@ -26,7 +70,7 @@ function resolveLazyIcon(module: () => Promise<{ default: ComponentType }>): Com
|
|
|
26
70
|
*/
|
|
27
71
|
export function IconRefGlyph({ icon, size = 16 }: { icon: IconRef; size?: number }) {
|
|
28
72
|
if (icon.kind === 'url') {
|
|
29
|
-
return <
|
|
73
|
+
return <IconRefImage className="shrink-0" size={size} src={icon.src} />
|
|
30
74
|
}
|
|
31
75
|
if (icon.kind === 'iconify') {
|
|
32
76
|
return <Icon height={size} icon={icon.name} width={size} />
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { LevelNode } from '@pascal-app/core'
|
|
4
4
|
import { useEffect, useState } from 'react'
|
|
5
|
-
import type { LevelDuplicatePreset } from '
|
|
5
|
+
import type { LevelDuplicatePreset } from '@pascal-app/core/building'
|
|
6
6
|
import { getLevelDisplayName } from '@pascal-app/core'
|
|
7
7
|
import { cn } from '../../lib/utils'
|
|
8
8
|
import {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ARTIFACT_URL_PREFIX, resolveArtifactUrl } from '@pascal-app/core'
|
|
4
|
+
import Image, { type ImageProps } from 'next/image'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A node's icon or thumbnail. A scripted object's thumbnail is an
|
|
8
|
+
* `artifact://` image: it resolves through the project's artifact store and
|
|
9
|
+
* skips image optimization, whose server-side fetch has no session to read a
|
|
10
|
+
* private project's artifact with.
|
|
11
|
+
*/
|
|
12
|
+
export function NodeIconImage({ src, ...props }: Omit<ImageProps, 'src'> & { src: string }) {
|
|
13
|
+
if (!src.startsWith(ARTIFACT_URL_PREFIX)) return <Image src={src} {...props} />
|
|
14
|
+
const url = resolveArtifactUrl(src)
|
|
15
|
+
return url ? <Image src={url} unoptimized {...props} /> : null
|
|
16
|
+
}
|