@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
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ChevronRight } from 'lucide-react'
|
|
4
|
+
import { type ComponentPropsWithoutRef, type ReactNode, useEffect, useState } from 'react'
|
|
5
|
+
import { cn } from '../../../lib/utils'
|
|
6
|
+
import {
|
|
7
|
+
WALL_DRAW_IDLE_CAPTION,
|
|
8
|
+
WALL_DRAW_VARIANTS,
|
|
9
|
+
type WallDrawVariant,
|
|
10
|
+
} from '../../../lib/wall-draw-variant'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The Build panel's grouped layout (Rooms / Add to rooms / Outdoor / Advanced),
|
|
14
|
+
* shared by the community and standalone editor Build tabs so both read the
|
|
15
|
+
* same. Hosts own what each tile arms; these only lay the groups out.
|
|
16
|
+
*/
|
|
17
|
+
export function BuildPanelSection({
|
|
18
|
+
children,
|
|
19
|
+
hint,
|
|
20
|
+
id,
|
|
21
|
+
title,
|
|
22
|
+
}: {
|
|
23
|
+
children: ReactNode
|
|
24
|
+
hint?: string
|
|
25
|
+
id: string
|
|
26
|
+
title: string
|
|
27
|
+
}) {
|
|
28
|
+
return (
|
|
29
|
+
<section className="flex flex-col" data-build-group={id}>
|
|
30
|
+
<div className="mb-2 flex items-baseline justify-between gap-2 px-0.5">
|
|
31
|
+
<h3 className="font-medium text-foreground text-xs">{title}</h3>
|
|
32
|
+
{hint ? <span className="truncate text-[11px] text-muted-foreground">{hint}</span> : null}
|
|
33
|
+
</div>
|
|
34
|
+
{children}
|
|
35
|
+
</section>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ADVANCED_OPEN_KEY = 'pascal-build-advanced-open'
|
|
40
|
+
|
|
41
|
+
function readAdvancedOpen(): boolean {
|
|
42
|
+
try {
|
|
43
|
+
return window.localStorage.getItem(ADVANCED_OPEN_KEY) === '1'
|
|
44
|
+
} catch {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function writeAdvancedOpen(open: boolean) {
|
|
50
|
+
try {
|
|
51
|
+
window.localStorage.setItem(ADVANCED_OPEN_KEY, open ? '1' : '0')
|
|
52
|
+
} catch {
|
|
53
|
+
// Storage blocked (private window, preview): the section just starts closed.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The collapsible Advanced group. Remembers its open state per viewer, and
|
|
59
|
+
* opens itself when one of its tools becomes the armed one (e.g. by shortcut)
|
|
60
|
+
* so the lit tile is never hidden.
|
|
61
|
+
*/
|
|
62
|
+
export function BuildPanelAdvancedSection({
|
|
63
|
+
children,
|
|
64
|
+
containsActiveTool,
|
|
65
|
+
description,
|
|
66
|
+
hint,
|
|
67
|
+
}: {
|
|
68
|
+
children: ReactNode
|
|
69
|
+
containsActiveTool: boolean
|
|
70
|
+
description?: string
|
|
71
|
+
hint?: string
|
|
72
|
+
}) {
|
|
73
|
+
const [open, setOpen] = useState(false)
|
|
74
|
+
useEffect(() => setOpen(readAdvancedOpen()), [])
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (containsActiveTool) setOpen(true)
|
|
77
|
+
}, [containsActiveTool])
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<section className="flex flex-col" data-build-group="advanced">
|
|
81
|
+
<button
|
|
82
|
+
aria-expanded={open}
|
|
83
|
+
className="-mx-1.5 flex items-center gap-2 rounded-lg px-1.5 py-1.5 text-left transition-colors hover:bg-muted/60"
|
|
84
|
+
onClick={() => {
|
|
85
|
+
const next = !open
|
|
86
|
+
setOpen(next)
|
|
87
|
+
writeAdvancedOpen(next)
|
|
88
|
+
}}
|
|
89
|
+
type="button"
|
|
90
|
+
>
|
|
91
|
+
<ChevronRight
|
|
92
|
+
className={cn(
|
|
93
|
+
'size-3 shrink-0 text-muted-foreground transition-transform duration-150',
|
|
94
|
+
open && 'rotate-90',
|
|
95
|
+
)}
|
|
96
|
+
/>
|
|
97
|
+
<h3 className="font-medium text-foreground text-xs">Advanced</h3>
|
|
98
|
+
{hint ? (
|
|
99
|
+
<span className="ml-auto truncate text-[11px] text-muted-foreground">{hint}</span>
|
|
100
|
+
) : null}
|
|
101
|
+
</button>
|
|
102
|
+
{open ? (
|
|
103
|
+
<div className="flex flex-col gap-2.5 pt-1.5">
|
|
104
|
+
{description ? (
|
|
105
|
+
<p className="px-0.5 text-[11px] text-muted-foreground leading-relaxed">
|
|
106
|
+
{description}
|
|
107
|
+
</p>
|
|
108
|
+
) : null}
|
|
109
|
+
{children}
|
|
110
|
+
</div>
|
|
111
|
+
) : null}
|
|
112
|
+
</section>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function BuildToolGrid({ children, columns }: { children: ReactNode; columns: 3 | 4 }) {
|
|
117
|
+
return (
|
|
118
|
+
<div className={cn('grid gap-1.5', columns === 3 ? 'grid-cols-3' : 'grid-cols-4')}>
|
|
119
|
+
{children}
|
|
120
|
+
</div>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
type BuildToolTileProps = Omit<ComponentPropsWithoutRef<'button'>, 'children' | 'type'> & {
|
|
125
|
+
active: boolean
|
|
126
|
+
/** Corner mark, e.g. the snap-target badge of wall-hosted tools. */
|
|
127
|
+
badge?: ReactNode
|
|
128
|
+
iconSrc: string
|
|
129
|
+
label: string
|
|
130
|
+
size?: 'lg' | 'sm'
|
|
131
|
+
tone?: 'primary' | 'neutral'
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* One tool tile: the isometric icon over its name. Inactive icons stay grey and
|
|
136
|
+
* take their colour on hover. Neutral tiles keep that behavior when armed.
|
|
137
|
+
*/
|
|
138
|
+
export function BuildToolTile({
|
|
139
|
+
active,
|
|
140
|
+
badge,
|
|
141
|
+
className,
|
|
142
|
+
iconSrc,
|
|
143
|
+
label,
|
|
144
|
+
size = 'sm',
|
|
145
|
+
tone = 'primary',
|
|
146
|
+
...buttonProps
|
|
147
|
+
}: BuildToolTileProps) {
|
|
148
|
+
return (
|
|
149
|
+
<button
|
|
150
|
+
aria-pressed={active}
|
|
151
|
+
className={cn(
|
|
152
|
+
'group relative flex min-w-0 flex-col items-center justify-end rounded-xl transition-all duration-200',
|
|
153
|
+
// Small tiles grow to two label lines ("Spawn point"); a grid row's
|
|
154
|
+
// tiles stretch together, so a row keeps one height.
|
|
155
|
+
size === 'lg' ? 'h-[104px] gap-1.5 px-1.5 pt-2 pb-2.5' : 'min-h-[74px] gap-1 px-1 pt-1.5 pb-2',
|
|
156
|
+
active
|
|
157
|
+
? tone === 'neutral'
|
|
158
|
+
? 'bg-black/8 ring-1 ring-foreground/30 dark:bg-muted'
|
|
159
|
+
: 'bg-primary/10 ring-1 ring-primary/50'
|
|
160
|
+
: 'bg-black/4 hover:bg-black/8 dark:bg-muted/40 dark:hover:bg-muted',
|
|
161
|
+
className,
|
|
162
|
+
)}
|
|
163
|
+
type="button"
|
|
164
|
+
{...buttonProps}
|
|
165
|
+
>
|
|
166
|
+
<img
|
|
167
|
+
alt=""
|
|
168
|
+
className={cn(
|
|
169
|
+
'min-h-0 object-contain transition duration-200 group-hover:scale-110',
|
|
170
|
+
size === 'lg' ? 'size-14' : 'size-9',
|
|
171
|
+
(!active || tone === 'neutral') &&
|
|
172
|
+
'opacity-70 grayscale group-hover:opacity-100 group-hover:grayscale-0',
|
|
173
|
+
)}
|
|
174
|
+
draggable={false}
|
|
175
|
+
src={iconSrc}
|
|
176
|
+
/>
|
|
177
|
+
<span
|
|
178
|
+
className={cn(
|
|
179
|
+
'line-clamp-2 max-w-full text-center font-medium leading-tight',
|
|
180
|
+
size === 'lg' ? 'text-xs' : 'text-[11px]',
|
|
181
|
+
active ? 'text-foreground' : 'text-muted-foreground group-hover:text-foreground',
|
|
182
|
+
)}
|
|
183
|
+
>
|
|
184
|
+
{label}
|
|
185
|
+
</span>
|
|
186
|
+
{badge ? <span className="absolute top-1 right-1">{badge}</span> : null}
|
|
187
|
+
</button>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** The line under the Rooms tiles: what the armed shape does, lead in bold. */
|
|
192
|
+
export function BuildPanelCaption({ lead, rest }: { lead?: string; rest: string }) {
|
|
193
|
+
return (
|
|
194
|
+
<p className="mt-2 min-h-[2lh] px-0.5 text-[11px] text-muted-foreground leading-relaxed">
|
|
195
|
+
{lead ? <span className="font-medium text-foreground">{lead} </span> : null}
|
|
196
|
+
{rest}
|
|
197
|
+
</p>
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The Rooms group: one large tile per wall drawing variant, and the armed
|
|
203
|
+
* variant's caption. `activeVariant` is null while the wall tool is not armed.
|
|
204
|
+
*/
|
|
205
|
+
export function BuildPanelRoomsSection({
|
|
206
|
+
activeVariant,
|
|
207
|
+
onHover,
|
|
208
|
+
onSelect,
|
|
209
|
+
tileProps,
|
|
210
|
+
}: {
|
|
211
|
+
activeVariant: WallDrawVariant | null
|
|
212
|
+
onHover?: (variant: WallDrawVariant) => void
|
|
213
|
+
onSelect: (variant: WallDrawVariant) => void
|
|
214
|
+
/** Extra attributes per tile (e.g. a host's guide-target hook). */
|
|
215
|
+
tileProps?: (variant: WallDrawVariant) => Record<string, string | undefined>
|
|
216
|
+
}) {
|
|
217
|
+
const active = WALL_DRAW_VARIANTS.find((variant) => variant.id === activeVariant)
|
|
218
|
+
return (
|
|
219
|
+
<BuildPanelSection hint="walls · floor · ceiling" id="rooms" title="Rooms">
|
|
220
|
+
<BuildToolGrid columns={3}>
|
|
221
|
+
{WALL_DRAW_VARIANTS.map((variant) => (
|
|
222
|
+
<BuildToolTile
|
|
223
|
+
active={variant.id === activeVariant}
|
|
224
|
+
data-build-tool={`room-${variant.id}`}
|
|
225
|
+
iconSrc={variant.iconSrc}
|
|
226
|
+
key={variant.id}
|
|
227
|
+
label={variant.label}
|
|
228
|
+
onClick={() => onSelect(variant.id)}
|
|
229
|
+
onMouseEnter={() => onHover?.(variant.id)}
|
|
230
|
+
size="lg"
|
|
231
|
+
title={variant.title}
|
|
232
|
+
{...tileProps?.(variant.id)}
|
|
233
|
+
/>
|
|
234
|
+
))}
|
|
235
|
+
</BuildToolGrid>
|
|
236
|
+
<BuildPanelCaption
|
|
237
|
+
lead={active?.caption.lead}
|
|
238
|
+
rest={active ? active.caption.rest : WALL_DRAW_IDLE_CAPTION}
|
|
239
|
+
/>
|
|
240
|
+
</BuildPanelSection>
|
|
241
|
+
)
|
|
242
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import type
|
|
4
|
-
import { DEFAULT_LEVEL_HEIGHT, LevelNode, type UnitNode, useScene } from '@pascal-app/core'
|
|
3
|
+
import { type AnyNodeId, type LevelNode, type UnitNode, useScene } from '@pascal-app/core'
|
|
5
4
|
import { useViewer } from '@pascal-app/viewer'
|
|
6
5
|
import {
|
|
7
6
|
AppWindow,
|
|
@@ -31,15 +30,16 @@ import {
|
|
|
31
30
|
Redo2,
|
|
32
31
|
Sparkles,
|
|
33
32
|
Square,
|
|
34
|
-
SquareStack,
|
|
35
33
|
Sun,
|
|
36
34
|
Trash2,
|
|
37
35
|
Undo2,
|
|
38
36
|
Video,
|
|
39
37
|
} from 'lucide-react'
|
|
40
38
|
import { useEffect } from 'react'
|
|
39
|
+
import { addStoreyOnTop } from '../../../lib/add-storey'
|
|
41
40
|
import { getHistoryCommandState, runRedo, runUndo } from '../../../lib/history'
|
|
42
41
|
import { deleteLevelWithFallbackSelection } from '../../../lib/level-selection'
|
|
42
|
+
import { getSavedSceneDocument } from '../../../lib/scene'
|
|
43
43
|
import { createUnitInBuilding, enterUnitFocus, leaveUnitFocus } from '../../../lib/units'
|
|
44
44
|
import { useCommandRegistry } from '../../../store/use-command-registry'
|
|
45
45
|
import type { StructureTool } from '../../../store/use-editor'
|
|
@@ -50,10 +50,8 @@ export function EditorCommands() {
|
|
|
50
50
|
const register = useCommandRegistry((s) => s.register)
|
|
51
51
|
const { navigateTo, setInputValue, setOpen } = useCommandPalette()
|
|
52
52
|
|
|
53
|
-
const setPhase = useEditor((s) => s.setPhase)
|
|
54
53
|
const armToolMode = useEditor((s) => s.armToolMode)
|
|
55
54
|
const armMaterialPaint = useEditor((s) => s.armMaterialPaint)
|
|
56
|
-
const setStructureLayer = useEditor((s) => s.setStructureLayer)
|
|
57
55
|
const isPreviewMode = useEditor((s) => s.isPreviewMode)
|
|
58
56
|
const setPreviewMode = useEditor((s) => s.setPreviewMode)
|
|
59
57
|
|
|
@@ -79,12 +77,7 @@ export function EditorCommands() {
|
|
|
79
77
|
)
|
|
80
78
|
|
|
81
79
|
const activateTool = (tool: StructureTool) => {
|
|
82
|
-
run(() => {
|
|
83
|
-
setPhase('structure')
|
|
84
|
-
if (tool === 'zone') setStructureLayer('zones')
|
|
85
|
-
else setStructureLayer('elements')
|
|
86
|
-
armToolMode({ mode: 'build', tool })
|
|
87
|
-
})
|
|
80
|
+
run(() => armToolMode({ mode: 'build', tool }))
|
|
88
81
|
}
|
|
89
82
|
|
|
90
83
|
return register([
|
|
@@ -175,11 +168,7 @@ export function EditorCommands() {
|
|
|
175
168
|
keywords: ['paint', 'material', 'texture', 'bucket', 'surface'],
|
|
176
169
|
shortcut: ['P'],
|
|
177
170
|
execute: () =>
|
|
178
|
-
run(() =>
|
|
179
|
-
setPhase('structure')
|
|
180
|
-
setStructureLayer('elements')
|
|
181
|
-
armMaterialPaint()
|
|
182
|
-
}),
|
|
171
|
+
run(() => armMaterialPaint()),
|
|
183
172
|
},
|
|
184
173
|
{
|
|
185
174
|
id: 'editor.mode.terrain-sculpt',
|
|
@@ -214,16 +203,7 @@ export function EditorCommands() {
|
|
|
214
203
|
const { nodes } = useScene.getState()
|
|
215
204
|
const building = Object.values(nodes).find((n) => n.type === 'building')
|
|
216
205
|
if (!building) return
|
|
217
|
-
const
|
|
218
|
-
(childId) => nodes[childId as keyof typeof nodes]?.type === 'level',
|
|
219
|
-
).length
|
|
220
|
-
const newLevel = LevelNode.parse({
|
|
221
|
-
level: levelCount,
|
|
222
|
-
height: DEFAULT_LEVEL_HEIGHT,
|
|
223
|
-
children: [],
|
|
224
|
-
parentId: building.id,
|
|
225
|
-
})
|
|
226
|
-
useScene.getState().createNode(newLevel, building.id)
|
|
206
|
+
const newLevel = addStoreyOnTop(building.id)
|
|
227
207
|
useViewer.getState().setSelection({ levelId: newLevel.id })
|
|
228
208
|
}),
|
|
229
209
|
},
|
|
@@ -315,19 +295,6 @@ export function EditorCommands() {
|
|
|
315
295
|
navigate: true,
|
|
316
296
|
execute: () => navigateTo('wall-mode'),
|
|
317
297
|
},
|
|
318
|
-
{
|
|
319
|
-
id: 'editor.viewer.level-mode',
|
|
320
|
-
label: 'Level Mode',
|
|
321
|
-
group: 'Viewer Controls',
|
|
322
|
-
icon: <SquareStack className="h-4 w-4" />,
|
|
323
|
-
keywords: ['level', 'floor', 'exploded', 'stacked', 'solo'],
|
|
324
|
-
badge: () => {
|
|
325
|
-
const mode = useViewer.getState().levelMode
|
|
326
|
-
return { manual: 'Manual', stacked: 'Stacked', exploded: 'Exploded', solo: 'Solo' }[mode]
|
|
327
|
-
},
|
|
328
|
-
navigate: true,
|
|
329
|
-
execute: () => navigateTo('level-mode'),
|
|
330
|
-
},
|
|
331
298
|
{
|
|
332
299
|
id: 'editor.viewer.camera-mode',
|
|
333
300
|
label: () => {
|
|
@@ -422,7 +389,7 @@ export function EditorCommands() {
|
|
|
422
389
|
keywords: ['export', 'download', 'json', 'save', 'data'],
|
|
423
390
|
execute: () =>
|
|
424
391
|
run(() => {
|
|
425
|
-
const { nodes, rootNodeIds } =
|
|
392
|
+
const { nodes, rootNodeIds } = getSavedSceneDocument()
|
|
426
393
|
const blob = new Blob([JSON.stringify({ nodes, rootNodeIds }, null, 2)], {
|
|
427
394
|
type: 'application/json',
|
|
428
395
|
})
|
|
@@ -476,10 +443,8 @@ export function EditorCommands() {
|
|
|
476
443
|
navigateTo,
|
|
477
444
|
setInputValue,
|
|
478
445
|
setOpen,
|
|
479
|
-
setPhase,
|
|
480
446
|
armToolMode,
|
|
481
447
|
armMaterialPaint,
|
|
482
|
-
setStructureLayer,
|
|
483
448
|
isPreviewMode,
|
|
484
449
|
setPreviewMode,
|
|
485
450
|
exportScene,
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
import type { AnyNodeId, LevelNode } from '@pascal-app/core'
|
|
4
4
|
import { useScene } from '@pascal-app/core'
|
|
5
5
|
import { markPerfAction, useViewer } from '@pascal-app/viewer'
|
|
6
|
-
import { Command, useCommandState } from 'cmdk'
|
|
6
|
+
import { Command, defaultFilter, useCommandState } from 'cmdk'
|
|
7
7
|
import { ChevronRight, Search } from 'lucide-react'
|
|
8
|
-
import type { ReactNode } from 'react'
|
|
9
|
-
import { useEffect, useState } from 'react'
|
|
8
|
+
import type { ReactNode, RefObject } from 'react'
|
|
9
|
+
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
|
10
10
|
import { create } from 'zustand'
|
|
11
11
|
import { useShallow } from 'zustand/shallow'
|
|
12
12
|
import { Dialog, DialogContent, DialogTitle } from './../../../components/ui/primitives/dialog'
|
|
13
13
|
import { getLevelDisplayName } from '@pascal-app/core'
|
|
14
|
-
import { useCommandRegistry } from '../../../store/use-command-registry'
|
|
14
|
+
import { type CommandAction, useCommandRegistry } from '../../../store/use-command-registry'
|
|
15
15
|
import { usePaletteViewRegistry } from '../../../store/use-palette-view-registry'
|
|
16
16
|
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
@@ -54,6 +54,28 @@ function resolve(value: string | (() => string)): string {
|
|
|
54
54
|
return typeof value === 'function' ? value() : value
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
type ActionGroup = [group: string, actions: CommandAction[]]
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The registered actions by group, in the order they were registered. A search puts the group
|
|
61
|
+
* holding the best match first: cmdk ranks the items inside a group but leaves the groups where
|
|
62
|
+
* they are, so a strong match could sit below a screen of weak ones.
|
|
63
|
+
*/
|
|
64
|
+
export function rankedGroups(actions: CommandAction[], search: string): ActionGroup[] {
|
|
65
|
+
const groups = new Map<string, CommandAction[]>()
|
|
66
|
+
for (const action of actions) {
|
|
67
|
+
groups.set(action.group, [...(groups.get(action.group) ?? []), action])
|
|
68
|
+
}
|
|
69
|
+
const ordered: ActionGroup[] = [...groups]
|
|
70
|
+
if (!search) return ordered
|
|
71
|
+
const bestMatch = ([, list]: ActionGroup) =>
|
|
72
|
+
Math.max(...list.map((action) => defaultFilter(resolve(action.label), search, action.keywords)))
|
|
73
|
+
return ordered
|
|
74
|
+
.map((entry) => ({ entry, score: bestMatch(entry) }))
|
|
75
|
+
.sort((a, b) => b.score - a.score)
|
|
76
|
+
.map(({ entry }) => entry)
|
|
77
|
+
}
|
|
78
|
+
|
|
57
79
|
function Shortcut({ keys }: { keys: string[] }) {
|
|
58
80
|
return (
|
|
59
81
|
<span className="ml-auto flex shrink-0 items-center gap-0.5">
|
|
@@ -149,7 +171,6 @@ function OptionItem({
|
|
|
149
171
|
// ---------------------------------------------------------------------------
|
|
150
172
|
const PAGE_LABEL: Record<string, string> = {
|
|
151
173
|
'wall-mode': 'Wall Mode',
|
|
152
|
-
'level-mode': 'Level Mode',
|
|
153
174
|
'rename-level': 'Rename Level',
|
|
154
175
|
'goto-level': 'Go to Level',
|
|
155
176
|
}
|
|
@@ -163,6 +184,20 @@ export interface CommandPaletteEmptyAction {
|
|
|
163
184
|
onSelect: (query: string) => void
|
|
164
185
|
}
|
|
165
186
|
|
|
187
|
+
/**
|
|
188
|
+
* A search selects the first item; this scrolls the list back to the top to show it. cmdk scrolls
|
|
189
|
+
* before the list re-renders the selection, so once a search has moved the groups it scrolls to
|
|
190
|
+
* wherever the item selected before went. Rendered after the list, whose ref is set by then.
|
|
191
|
+
*/
|
|
192
|
+
function TopOnFirstItem({ list }: { list: RefObject<HTMLDivElement | null> }) {
|
|
193
|
+
const value = useCommandState((s) => s.value)
|
|
194
|
+
useLayoutEffect(() => {
|
|
195
|
+
const first = list.current?.querySelector('[cmdk-item]:not([aria-disabled="true"])')
|
|
196
|
+
if (value && first?.getAttribute('aria-selected') === 'true') list.current?.scrollTo({ top: 0 })
|
|
197
|
+
}, [list, value])
|
|
198
|
+
return null
|
|
199
|
+
}
|
|
200
|
+
|
|
166
201
|
function EmptyActionItem({ action }: { action: CommandPaletteEmptyAction }) {
|
|
167
202
|
const count = useCommandState((s) => s.filtered.count)
|
|
168
203
|
const search = useCommandState((s) => s.search)
|
|
@@ -194,6 +229,7 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
194
229
|
|
|
195
230
|
const [meta, setMeta] = useState('⌘')
|
|
196
231
|
const [isFullscreen, setIsFullscreen] = useState(false)
|
|
232
|
+
const listRef = useRef<HTMLDivElement>(null)
|
|
197
233
|
|
|
198
234
|
const page = pages[pages.length - 1]
|
|
199
235
|
|
|
@@ -204,8 +240,6 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
204
240
|
|
|
205
241
|
const wallMode = useViewer((s) => s.wallMode)
|
|
206
242
|
const setWallMode = useViewer((s) => s.setWallMode)
|
|
207
|
-
const levelMode = useViewer((s) => s.levelMode)
|
|
208
|
-
const setLevelMode = useViewer((s) => s.setLevelMode)
|
|
209
243
|
|
|
210
244
|
const allLevels = useScene(
|
|
211
245
|
useShallow((s) =>
|
|
@@ -250,12 +284,6 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
250
284
|
down: 'Down',
|
|
251
285
|
translucent: 'Translucent',
|
|
252
286
|
}
|
|
253
|
-
const levelModeLabel: Record<'manual' | 'stacked' | 'exploded' | 'solo', string> = {
|
|
254
|
-
manual: 'Manual',
|
|
255
|
-
stacked: 'Stacked',
|
|
256
|
-
exploded: 'Exploded',
|
|
257
|
-
solo: 'Solo',
|
|
258
|
-
}
|
|
259
287
|
|
|
260
288
|
const confirmRename = () => {
|
|
261
289
|
if (!(activeLevelId && inputValue.trim())) return
|
|
@@ -264,15 +292,7 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
264
292
|
})
|
|
265
293
|
}
|
|
266
294
|
|
|
267
|
-
|
|
268
|
-
// Group registered actions by group (preserving insertion order)
|
|
269
|
-
// ---------------------------------------------------------------------------
|
|
270
|
-
const grouped = actions.reduce<Map<string, typeof actions>>((acc, action) => {
|
|
271
|
-
const list = acc.get(action.group) ?? []
|
|
272
|
-
list.push(action)
|
|
273
|
-
acc.set(action.group, list)
|
|
274
|
-
return acc
|
|
275
|
-
}, new Map())
|
|
295
|
+
const grouped = rankedGroups(actions, inputValue)
|
|
276
296
|
|
|
277
297
|
const onClose = () => setOpen(false)
|
|
278
298
|
const onBack = () => {
|
|
@@ -335,7 +355,7 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
335
355
|
/>
|
|
336
356
|
</div>
|
|
337
357
|
|
|
338
|
-
<Command.List className="max-h-100 overflow-y-auto p-1.5">
|
|
358
|
+
<Command.List className="max-h-100 overflow-y-auto p-1.5" ref={listRef}>
|
|
339
359
|
{(!emptyAction || page) && (
|
|
340
360
|
<Command.Empty className="py-8 text-center text-muted-foreground text-sm">
|
|
341
361
|
No commands found.
|
|
@@ -353,7 +373,7 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
353
373
|
|
|
354
374
|
{/* ── Root view: render from registry ───────────────────────── */}
|
|
355
375
|
{!page &&
|
|
356
|
-
|
|
376
|
+
grouped.map(([group, groupActions]) => (
|
|
357
377
|
<Command.Group heading={group} key={group}>
|
|
358
378
|
{groupActions.map((action) => (
|
|
359
379
|
<Item
|
|
@@ -364,7 +384,11 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
364
384
|
keywords={action.keywords}
|
|
365
385
|
label={action.label}
|
|
366
386
|
navigate={action.navigate}
|
|
367
|
-
|
|
387
|
+
// A registered command that does not navigate within the
|
|
388
|
+
// palette closes it, like the built-ins — otherwise the
|
|
389
|
+
// dialog overlay keeps swallowing clicks under a full-screen
|
|
390
|
+
// surface the command opened (Sheets).
|
|
391
|
+
onSelect={() => (action.navigate ? action.execute() : run(() => action.execute()))}
|
|
368
392
|
shortcut={action.shortcut}
|
|
369
393
|
/>
|
|
370
394
|
))}
|
|
@@ -385,20 +409,6 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
385
409
|
</Command.Group>
|
|
386
410
|
)}
|
|
387
411
|
|
|
388
|
-
{/* ── Level Mode sub-page ───────────────────────────────────── */}
|
|
389
|
-
{page === 'level-mode' && (
|
|
390
|
-
<Command.Group heading="Level Mode">
|
|
391
|
-
{(['stacked', 'exploded', 'solo'] as const).map((mode) => (
|
|
392
|
-
<OptionItem
|
|
393
|
-
isActive={levelMode === mode}
|
|
394
|
-
key={mode}
|
|
395
|
-
label={levelModeLabel[mode]}
|
|
396
|
-
onSelect={() => run(() => setLevelMode(mode))}
|
|
397
|
-
/>
|
|
398
|
-
))}
|
|
399
|
-
</Command.Group>
|
|
400
|
-
)}
|
|
401
|
-
|
|
402
412
|
{/* ── Go to Level sub-page ──────────────────────────────────── */}
|
|
403
413
|
{page === 'goto-level' && (
|
|
404
414
|
<Command.Group heading="Go to Level">
|
|
@@ -458,6 +468,7 @@ export function CommandPalette({ emptyAction }: { emptyAction?: CommandPaletteEm
|
|
|
458
468
|
</Command.Group>
|
|
459
469
|
)}
|
|
460
470
|
</Command.List>
|
|
471
|
+
<TopOnFirstItem list={listRef} />
|
|
461
472
|
|
|
462
473
|
{/* Footer hint */}
|
|
463
474
|
<div className="flex items-center justify-between border-border/50 border-t px-3 py-2">
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { rankedGroups } from '.'
|
|
3
|
+
|
|
4
|
+
const action = (group: string, label: string, keywords?: string[]) => ({
|
|
5
|
+
id: label,
|
|
6
|
+
group,
|
|
7
|
+
label,
|
|
8
|
+
keywords,
|
|
9
|
+
execute: () => {},
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
// What a host's palette holds: weak fuzzy matches for "theme" registered before the strong one.
|
|
13
|
+
const actions = [
|
|
14
|
+
action('Generate', 'Generate house from template: The Poppy', ['template', 'adu', 'poppy']),
|
|
15
|
+
action('Viewer Controls', 'Switch to Solid', ['solid', 'shading', 'render', 'mode']),
|
|
16
|
+
action('View', 'Use dark theme', ['theme', 'appearance', 'interface']),
|
|
17
|
+
action('Export & Share', 'Take Screenshot', ['screenshot', 'capture', 'image']),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
const order = (search: string) => rankedGroups(actions, search).map(([group]) => group)
|
|
21
|
+
|
|
22
|
+
describe('command palette groups', () => {
|
|
23
|
+
test('a search lists the group with the best match first', () => {
|
|
24
|
+
expect(order('theme')[0]).toBe('View')
|
|
25
|
+
expect(order('solid')[0]).toBe('Viewer Controls')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('with no search the groups keep the order they were registered in', () => {
|
|
29
|
+
expect(order('')).toEqual(['Generate', 'Viewer Controls', 'View', 'Export & Share'])
|
|
30
|
+
})
|
|
31
|
+
})
|
|
@@ -12,12 +12,15 @@ export function ActionButton({ icon, label, className, ...props }: ActionButtonP
|
|
|
12
12
|
<button
|
|
13
13
|
{...props}
|
|
14
14
|
className={cn(
|
|
15
|
-
|
|
15
|
+
// The fill runs through custom properties so a caller's `bg-*` or
|
|
16
|
+
// `hover:bg-*` replaces it in both themes; tailwind-merge would keep a
|
|
17
|
+
// `dark:bg-*` fill alive under the caller's class.
|
|
18
|
+
'flex min-h-9 min-w-0 flex-1 shrink-0 items-center justify-center gap-1.5 rounded-lg border border-border/50 bg-(--action-fill) px-3 py-2 font-medium text-foreground text-xs leading-4 transition-colors [&>svg]:shrink-0 [--action-fill-hover:rgb(0_0_0/0.08)] [--action-fill:rgb(0_0_0/0.04)] hover:bg-(--action-fill-hover) active:bg-(--action-fill-hover) dark:[--action-fill-hover:#3e3e3e] dark:[--action-fill:#2C2C2E]',
|
|
16
19
|
className,
|
|
17
20
|
)}
|
|
18
21
|
>
|
|
19
22
|
{icon}
|
|
20
|
-
<span>{label}</span>
|
|
23
|
+
<span className="min-w-0 whitespace-normal text-center">{label}</span>
|
|
21
24
|
</button>
|
|
22
25
|
)
|
|
23
26
|
}
|