@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,58 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Pentagon, Square } from 'lucide-react'
|
|
4
|
+
import { cn } from '../../../lib/utils'
|
|
5
|
+
|
|
6
|
+
export type OutlineShape = 'rectangle' | 'polygon'
|
|
7
|
+
|
|
8
|
+
const SHAPES = [
|
|
9
|
+
{ id: 'rectangle', label: 'Rectangle', Icon: Square },
|
|
10
|
+
{ id: 'polygon', label: 'Polygon', Icon: Pentagon },
|
|
11
|
+
] as const
|
|
12
|
+
|
|
13
|
+
const pill =
|
|
14
|
+
'inline-flex items-center gap-1 rounded-full border border-border/50 px-2 py-0.5 text-xs text-foreground transition-colors hover:bg-accent disabled:pointer-events-none disabled:opacity-50'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The outline shape, picked before drawing starts — the same two choices the
|
|
18
|
+
* Build panel's room tiles offer, so a cut or a mezzanine is started the way a
|
|
19
|
+
* room is.
|
|
20
|
+
*/
|
|
21
|
+
export function ShapeChoice({
|
|
22
|
+
label,
|
|
23
|
+
onPick,
|
|
24
|
+
disabled,
|
|
25
|
+
className,
|
|
26
|
+
data,
|
|
27
|
+
}: {
|
|
28
|
+
label: string
|
|
29
|
+
onPick: (shape: OutlineShape) => void
|
|
30
|
+
disabled?: boolean
|
|
31
|
+
className?: string
|
|
32
|
+
/** `data-shape-choice` hook for tests and proofs. */
|
|
33
|
+
data?: string
|
|
34
|
+
}) {
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
className={cn('flex min-h-7 items-center justify-between gap-2', className)}
|
|
38
|
+
data-shape-choice={data}
|
|
39
|
+
>
|
|
40
|
+
<span className="whitespace-nowrap">{label}</span>
|
|
41
|
+
<div className="flex items-center gap-1.5">
|
|
42
|
+
{SHAPES.map(({ id, label: shapeLabel, Icon }) => (
|
|
43
|
+
<button
|
|
44
|
+
className={pill}
|
|
45
|
+
data-shape={id}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
key={id}
|
|
48
|
+
onClick={() => onPick(id)}
|
|
49
|
+
type="button"
|
|
50
|
+
>
|
|
51
|
+
<Icon aria-hidden className="h-3 w-3" />
|
|
52
|
+
{shapeLabel}
|
|
53
|
+
</button>
|
|
54
|
+
))}
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type AnyNodeId,
|
|
5
|
+
useLiveNodeOverrides,
|
|
6
|
+
useScene,
|
|
7
|
+
type WallFaceRegion,
|
|
8
|
+
} from '@pascal-app/core'
|
|
9
|
+
import { useViewer } from '@pascal-app/viewer'
|
|
10
|
+
import { Trash2 } from 'lucide-react'
|
|
11
|
+
import { useMemo } from 'react'
|
|
12
|
+
import { removeWallRegion } from '../../../lib/paint-regions'
|
|
13
|
+
import { sfxEmitter } from '../../../lib/sfx-bus'
|
|
14
|
+
import { cn } from '../../../lib/utils'
|
|
15
|
+
import {
|
|
16
|
+
wallRegionCapNotice,
|
|
17
|
+
wallRegionFaceLabels,
|
|
18
|
+
wallRegionRows,
|
|
19
|
+
} from '../../../lib/wall-region-list'
|
|
20
|
+
import { useWallRegionSelection } from '../../../store/use-wall-region-handles-selection'
|
|
21
|
+
import { PanelSection } from '../controls/panel-section'
|
|
22
|
+
|
|
23
|
+
const ROW_ACTION_CLASS =
|
|
24
|
+
'flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-black/5 hover:text-foreground dark:hover:bg-white/10'
|
|
25
|
+
|
|
26
|
+
/** The wall panel's "Paint regions" section: one row per region, select or delete. */
|
|
27
|
+
export function WallPaintRegionList({ wallId }: { wallId: string }) {
|
|
28
|
+
const nodes = useScene((state) => state.nodes)
|
|
29
|
+
const materials = useScene((state) => state.materials)
|
|
30
|
+
const unit = useViewer((state) => state.unit)
|
|
31
|
+
const active = useWallRegionSelection((state) => state.active)
|
|
32
|
+
const setActive = useWallRegionSelection((state) => state.setActive)
|
|
33
|
+
// A handle drag previews through a live override; list the live bounds.
|
|
34
|
+
const liveRegions = useLiveNodeOverrides(
|
|
35
|
+
(state) =>
|
|
36
|
+
state.overrides.get(wallId as AnyNodeId)?.faceRegions as WallFaceRegion[] | undefined,
|
|
37
|
+
)
|
|
38
|
+
const sceneWall = nodes[wallId as AnyNodeId]
|
|
39
|
+
const wall = sceneWall?.type === 'wall' ? sceneWall : null
|
|
40
|
+
const faceLabels = useMemo(() => wallRegionFaceLabels(nodes, wallId), [nodes, wallId])
|
|
41
|
+
|
|
42
|
+
if (!wall) return null
|
|
43
|
+
const faceRegions = liveRegions ?? wall.faceRegions
|
|
44
|
+
if (!faceRegions?.length) return null
|
|
45
|
+
const rows = wallRegionRows({ faceRegions }, faceLabels, materials, unit)
|
|
46
|
+
const capNotice = wallRegionCapNotice({ faceRegions }, faceLabels)
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
// Focus inside this list still lets Delete target the active region.
|
|
50
|
+
<div className="contents" data-wall-region-list={wallId}>
|
|
51
|
+
<PanelSection title="Paint regions">
|
|
52
|
+
<div className="flex flex-col gap-1">
|
|
53
|
+
{rows.map((row) => {
|
|
54
|
+
const isActive = active?.wallId === wallId && active.regionId === row.id
|
|
55
|
+
return (
|
|
56
|
+
<div
|
|
57
|
+
className={cn(
|
|
58
|
+
'flex h-9 items-center gap-2 rounded-md px-1 transition-colors',
|
|
59
|
+
isActive ? 'bg-accent' : 'hover:bg-accent/50',
|
|
60
|
+
)}
|
|
61
|
+
data-wall-region-row={row.id}
|
|
62
|
+
key={row.id}
|
|
63
|
+
>
|
|
64
|
+
<button
|
|
65
|
+
aria-pressed={isActive}
|
|
66
|
+
className="flex min-w-0 flex-1 cursor-pointer items-center gap-2 text-left"
|
|
67
|
+
onClick={() => setActive({ wallId, regionId: row.id })}
|
|
68
|
+
type="button"
|
|
69
|
+
>
|
|
70
|
+
<span
|
|
71
|
+
aria-hidden="true"
|
|
72
|
+
className="h-4 w-4 shrink-0 rounded-sm border border-border/50 bg-center bg-cover"
|
|
73
|
+
style={{
|
|
74
|
+
backgroundColor: row.finish.color,
|
|
75
|
+
backgroundImage: row.finish.imageUrl
|
|
76
|
+
? `url(${row.finish.imageUrl})`
|
|
77
|
+
: undefined,
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
<span className="flex min-w-0 flex-col leading-tight">
|
|
81
|
+
<span className="truncate text-sm">{row.finish.name}</span>
|
|
82
|
+
<span className="truncate text-[11px] text-muted-foreground">
|
|
83
|
+
{row.faceLabel} · {row.boundsText}
|
|
84
|
+
</span>
|
|
85
|
+
</span>
|
|
86
|
+
</button>
|
|
87
|
+
<button
|
|
88
|
+
aria-label="Delete region"
|
|
89
|
+
className={ROW_ACTION_CLASS}
|
|
90
|
+
onClick={() => {
|
|
91
|
+
if (isActive) setActive(null)
|
|
92
|
+
if (removeWallRegion(wallId, row.id)) sfxEmitter.emit('sfx:structure-delete')
|
|
93
|
+
}}
|
|
94
|
+
title="Delete region"
|
|
95
|
+
type="button"
|
|
96
|
+
>
|
|
97
|
+
<Trash2 className="h-3 w-3" />
|
|
98
|
+
</button>
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
101
|
+
})}
|
|
102
|
+
{capNotice && (
|
|
103
|
+
<div className="px-1 text-[11px] text-muted-foreground">{capNotice}</div>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
</PanelSection>
|
|
107
|
+
</div>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** A small mark beside a name: the plan a plugin needs ("Pro"), as the plugin manager shows it. */
|
|
2
|
+
export function Badge({ label }: { label: string }) {
|
|
3
|
+
return (
|
|
4
|
+
<span className="rounded-[5px] border border-border px-1.5 py-px font-mono font-medium text-[10px] text-muted-foreground uppercase tracking-wide">
|
|
5
|
+
{label}
|
|
6
|
+
</span>
|
|
7
|
+
)
|
|
8
|
+
}
|
|
@@ -1,43 +1,55 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { ZONE_COLOR_PALETTE } from '@pascal-app/core'
|
|
3
4
|
import { useState } from 'react'
|
|
4
5
|
import { cn } from '../../../lib/utils'
|
|
5
6
|
import { Popover, PopoverContent, PopoverTrigger } from './popover'
|
|
7
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip'
|
|
6
8
|
|
|
7
|
-
export const PALETTE_COLORS =
|
|
8
|
-
'#ef4444', // Red 0°
|
|
9
|
-
'#f97316', // Orange 30°
|
|
10
|
-
'#f59e0b', // Amber 45°
|
|
11
|
-
'#84cc16', // Lime 85°
|
|
12
|
-
'#22c55e', // Green 142°
|
|
13
|
-
'#10b981', // Emerald 160°
|
|
14
|
-
'#06b6d4', // Cyan 190°
|
|
15
|
-
'#3b82f6', // Blue 217°
|
|
16
|
-
'#6366f1', // Indigo 239°
|
|
17
|
-
'#a855f7', // Violet 270°
|
|
18
|
-
'#64748b', // Dark gray
|
|
19
|
-
'#cccccc', // Light gray
|
|
20
|
-
]
|
|
9
|
+
export const PALETTE_COLORS = ZONE_COLOR_PALETTE
|
|
21
10
|
|
|
22
11
|
interface ColorDotProps {
|
|
23
12
|
color: string
|
|
24
13
|
onChange: (color: string) => void
|
|
14
|
+
/** Says what the colour is for, on hover and to assistive tech. */
|
|
15
|
+
label?: string
|
|
16
|
+
side?: 'left' | 'bottom'
|
|
25
17
|
}
|
|
26
18
|
|
|
27
|
-
export function ColorDot({ color, onChange }: ColorDotProps) {
|
|
19
|
+
export function ColorDot({ color, onChange, label, side = 'left' }: ColorDotProps) {
|
|
28
20
|
const [open, setOpen] = useState(false)
|
|
21
|
+
const trigger = (
|
|
22
|
+
<PopoverTrigger asChild>
|
|
23
|
+
<button
|
|
24
|
+
aria-label={label}
|
|
25
|
+
className="relative h-3 w-3 shrink-0 cursor-pointer rounded-sm border border-border/50 transition-all hover:ring-1 hover:ring-ring/50"
|
|
26
|
+
data-color-dot
|
|
27
|
+
onClick={(e) => e.stopPropagation()}
|
|
28
|
+
style={{ backgroundColor: color }}
|
|
29
|
+
type="button"
|
|
30
|
+
/>
|
|
31
|
+
</PopoverTrigger>
|
|
32
|
+
)
|
|
29
33
|
|
|
30
34
|
return (
|
|
31
35
|
<Popover onOpenChange={setOpen} open={open}>
|
|
32
|
-
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<PopoverContent
|
|
36
|
+
{label ? (
|
|
37
|
+
<Tooltip>
|
|
38
|
+
<TooltipTrigger asChild>{trigger}</TooltipTrigger>
|
|
39
|
+
<TooltipContent side="bottom">{label}</TooltipContent>
|
|
40
|
+
</Tooltip>
|
|
41
|
+
) : (
|
|
42
|
+
trigger
|
|
43
|
+
)}
|
|
44
|
+
<PopoverContent
|
|
45
|
+
align={side === 'bottom' ? 'start' : 'center'}
|
|
46
|
+
className="w-auto p-1.5"
|
|
47
|
+
// Keep the pick inside the palette: in a panel header, a pointerdown
|
|
48
|
+
// bubbling out of the portal would start dragging the panel.
|
|
49
|
+
onPointerDown={(event) => event.stopPropagation()}
|
|
50
|
+
side={side}
|
|
51
|
+
sideOffset={6}
|
|
52
|
+
>
|
|
41
53
|
<div className="grid grid-cols-4 gap-1">
|
|
42
54
|
{PALETTE_COLORS.map((c) => (
|
|
43
55
|
<button
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
TooltipTrigger,
|
|
25
25
|
} from './../../../components/ui/primitives/tooltip'
|
|
26
26
|
import { useIsMobile } from './../../../hooks/use-mobile'
|
|
27
|
+
import { hydrationGatedStorage } from './../../../lib/hydration-gated-storage'
|
|
27
28
|
import { cn } from './../../../lib/utils'
|
|
28
29
|
|
|
29
30
|
const SIDEBAR_COOKIE_NAME = 'sidebar_state'
|
|
@@ -47,7 +48,7 @@ type SidebarStore = {
|
|
|
47
48
|
export const useSidebarStore = create<SidebarStore>()(
|
|
48
49
|
persist(
|
|
49
50
|
(set) => ({
|
|
50
|
-
width:
|
|
51
|
+
width: 360,
|
|
51
52
|
setWidth: (width) => {
|
|
52
53
|
if (width < SIDEBAR_COLLAPSE_THRESHOLD) {
|
|
53
54
|
set({ isCollapsed: true })
|
|
@@ -62,6 +63,7 @@ export const useSidebarStore = create<SidebarStore>()(
|
|
|
62
63
|
}),
|
|
63
64
|
{
|
|
64
65
|
name: 'sidebar-preferences',
|
|
66
|
+
storage: hydrationGatedStorage((): boolean => useSidebarStore.persist.hasHydrated()),
|
|
65
67
|
partialize: (state) => ({ width: state.width, isCollapsed: state.isCollapsed }),
|
|
66
68
|
skipHydration: true,
|
|
67
69
|
},
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
import { rightStackFloors } from './right-stack'
|
|
3
|
+
|
|
4
|
+
const tall = { headerHeight: 52, cardHeight: 240 }
|
|
5
|
+
|
|
6
|
+
test('the inspector keeps 180 px, or its own height if shorter, above the full card', () => {
|
|
7
|
+
expect(rightStackFloors({ ...tall, naturalHeight: 500, stackHeight: 700 })).toEqual({
|
|
8
|
+
inspector: 180,
|
|
9
|
+
card: 240,
|
|
10
|
+
})
|
|
11
|
+
expect(rightStackFloors({ ...tall, naturalHeight: 120, stackHeight: 700 }).inspector).toBe(120)
|
|
12
|
+
expect(
|
|
13
|
+
rightStackFloors({ naturalHeight: 0, headerHeight: 0, cardHeight: 240, stackHeight: 700 }),
|
|
14
|
+
).toEqual({ inspector: 0, card: 240 })
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('on a short window the inspector gives way first, down to its header', () => {
|
|
18
|
+
expect(rightStackFloors({ ...tall, naturalHeight: 500, stackHeight: 400 })).toEqual({
|
|
19
|
+
inspector: 152,
|
|
20
|
+
card: 240,
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('on a very short window the inspector keeps its header and the card scrolls', () => {
|
|
25
|
+
expect(rightStackFloors({ ...tall, naturalHeight: 500, stackHeight: 258 })).toEqual({
|
|
26
|
+
inspector: 52,
|
|
27
|
+
card: 198,
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext, type ReactNode, useContext, useEffect, useRef } from 'react'
|
|
4
|
+
import { useIsMobile } from '../../hooks/use-mobile'
|
|
5
|
+
import { cn } from '../../lib/utils'
|
|
6
|
+
import { TOP_MENU_CLEARANCE } from './action-menu/placement'
|
|
7
|
+
|
|
8
|
+
const RightStackContext = createContext(false)
|
|
9
|
+
|
|
10
|
+
/** Whether a panel renders inside the shared right column (in flow) rather than on its own. */
|
|
11
|
+
export function useInRightStack() {
|
|
12
|
+
return useContext(RightStackContext)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** The inspector never shrinks below this (or its own height, if shorter) for the card, px. */
|
|
16
|
+
const INSPECTOR_MIN_HEIGHT = 180
|
|
17
|
+
|
|
18
|
+
/** Gap between the inspector and the card, px (`gap-2`). */
|
|
19
|
+
const GAP = 8
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The heights neither panel shrinks below. The inspector keeps 180 px (or its
|
|
23
|
+
* own height, if shorter) while the full card fits under it, and never less
|
|
24
|
+
* than its header row. The card shows in full whenever the column allows; on a
|
|
25
|
+
* very short window it gives way and scrolls on itself — both stay usable.
|
|
26
|
+
*/
|
|
27
|
+
export function rightStackFloors(args: {
|
|
28
|
+
naturalHeight: number
|
|
29
|
+
headerHeight: number
|
|
30
|
+
cardHeight: number
|
|
31
|
+
stackHeight: number
|
|
32
|
+
}): { inspector: number; card: number } {
|
|
33
|
+
const { naturalHeight, headerHeight, cardHeight, stackHeight } = args
|
|
34
|
+
const gap = naturalHeight > 0 && cardHeight > 0 ? GAP : 0
|
|
35
|
+
const header = Math.min(headerHeight, naturalHeight)
|
|
36
|
+
const inspector = Math.max(
|
|
37
|
+
header,
|
|
38
|
+
Math.min(INSPECTOR_MIN_HEIGHT, naturalHeight, stackHeight - cardHeight - gap),
|
|
39
|
+
)
|
|
40
|
+
const card = Math.max(0, Math.min(cardHeight, stackHeight - gap - inspector))
|
|
41
|
+
return { inspector, card }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The inspector's full height: its header plus everything its scroller holds. */
|
|
45
|
+
function inspectorHeights(slot: HTMLElement): { natural: number; header: number } {
|
|
46
|
+
const panel = slot.querySelector<HTMLElement>('[data-panel-wrapper]')
|
|
47
|
+
if (!panel) return { natural: 0, header: 0 }
|
|
48
|
+
const header = panel.querySelector<HTMLElement>('[data-panel-header]')?.offsetHeight ?? 0
|
|
49
|
+
const scroller = panel.querySelector<HTMLElement>('[data-panel-scroll]')
|
|
50
|
+
if (!scroller) return { natural: panel.offsetHeight, header }
|
|
51
|
+
return { natural: panel.offsetHeight - scroller.clientHeight + scroller.scrollHeight, header }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The right-hand column: the shortcuts card shows in full at the bottom, and
|
|
56
|
+
* the node inspector takes the space above it, growing down from the top and
|
|
57
|
+
* scrolling on itself. On a very short window the inspector keeps its header
|
|
58
|
+
* and the card scrolls instead. Neither hides the other. On a phone the two
|
|
59
|
+
* keep their own layouts (sheet / no card).
|
|
60
|
+
*/
|
|
61
|
+
export function RightStack({
|
|
62
|
+
inspector,
|
|
63
|
+
helper,
|
|
64
|
+
reserveBottomMenu = true,
|
|
65
|
+
belowTopMenu = false,
|
|
66
|
+
}: {
|
|
67
|
+
inspector: ReactNode
|
|
68
|
+
helper: ReactNode
|
|
69
|
+
// Keep clear of an action menu docked at the bottom; off when it sits at the top.
|
|
70
|
+
reserveBottomMenu?: boolean
|
|
71
|
+
// Start below an action menu docked at the top of the stage.
|
|
72
|
+
belowTopMenu?: boolean
|
|
73
|
+
}) {
|
|
74
|
+
const isMobile = useIsMobile()
|
|
75
|
+
const stackRef = useRef<HTMLDivElement>(null)
|
|
76
|
+
const inspectorRef = useRef<HTMLDivElement>(null)
|
|
77
|
+
const cardRef = useRef<HTMLDivElement>(null)
|
|
78
|
+
|
|
79
|
+
// `min(180px, its own height)` has no CSS form for a flex item that scrolls,
|
|
80
|
+
// so both floors are measured (`rightStackFloors`).
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (isMobile) return
|
|
83
|
+
const stack = stackRef.current
|
|
84
|
+
const slot = inspectorRef.current
|
|
85
|
+
const card = cardRef.current
|
|
86
|
+
if (!(stack && slot && card)) return
|
|
87
|
+
let frame = 0
|
|
88
|
+
const fit = () => {
|
|
89
|
+
frame = 0
|
|
90
|
+
const { natural, header } = inspectorHeights(slot)
|
|
91
|
+
const floors = rightStackFloors({
|
|
92
|
+
naturalHeight: natural,
|
|
93
|
+
headerHeight: header,
|
|
94
|
+
cardHeight: card.scrollHeight,
|
|
95
|
+
stackHeight: stack.clientHeight,
|
|
96
|
+
})
|
|
97
|
+
const inspectorMin = `${floors.inspector}px`
|
|
98
|
+
const cardMin = `${floors.card}px`
|
|
99
|
+
if (slot.style.minHeight !== inspectorMin) slot.style.minHeight = inspectorMin
|
|
100
|
+
if (card.style.minHeight !== cardMin) card.style.minHeight = cardMin
|
|
101
|
+
// The card is click-through; it takes the pointer only when it has to scroll.
|
|
102
|
+
const pointer = floors.card < card.scrollHeight ? 'auto' : ''
|
|
103
|
+
if (card.style.pointerEvents !== pointer) card.style.pointerEvents = pointer
|
|
104
|
+
}
|
|
105
|
+
const schedule = () => {
|
|
106
|
+
if (!frame) frame = requestAnimationFrame(fit)
|
|
107
|
+
}
|
|
108
|
+
const resize = new ResizeObserver(schedule)
|
|
109
|
+
resize.observe(stack)
|
|
110
|
+
resize.observe(card)
|
|
111
|
+
// Content growing inside the scroller changes no outer size, hence the mutation watch.
|
|
112
|
+
// The same for the card: a new tool swaps its rows without resizing the slot.
|
|
113
|
+
const mutation = new MutationObserver(schedule)
|
|
114
|
+
for (const target of [slot, card])
|
|
115
|
+
mutation.observe(target, {
|
|
116
|
+
subtree: true,
|
|
117
|
+
childList: true,
|
|
118
|
+
attributes: true,
|
|
119
|
+
attributeFilter: ['class', 'hidden', 'aria-expanded'],
|
|
120
|
+
characterData: true,
|
|
121
|
+
})
|
|
122
|
+
fit()
|
|
123
|
+
return () => {
|
|
124
|
+
if (frame) cancelAnimationFrame(frame)
|
|
125
|
+
resize.disconnect()
|
|
126
|
+
mutation.disconnect()
|
|
127
|
+
}
|
|
128
|
+
}, [isMobile])
|
|
129
|
+
|
|
130
|
+
if (isMobile) {
|
|
131
|
+
return (
|
|
132
|
+
<>
|
|
133
|
+
<div className="pointer-events-auto">{inspector}</div>
|
|
134
|
+
<div className="pointer-events-auto">{helper}</div>
|
|
135
|
+
</>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<RightStackContext.Provider value={true}>
|
|
141
|
+
<div
|
|
142
|
+
className={cn(
|
|
143
|
+
'pointer-events-none fixed right-4 z-40 flex flex-col items-end gap-2',
|
|
144
|
+
reserveBottomMenu ? 'bottom-[74px]' : 'bottom-4',
|
|
145
|
+
!belowTopMenu && 'top-20',
|
|
146
|
+
)}
|
|
147
|
+
style={belowTopMenu ? { top: TOP_MENU_CLEARANCE } : undefined}
|
|
148
|
+
data-right-stack
|
|
149
|
+
ref={stackRef}
|
|
150
|
+
>
|
|
151
|
+
<div className="flex min-h-0 flex-[0_1_auto] flex-col items-end" ref={inspectorRef}>
|
|
152
|
+
{inspector}
|
|
153
|
+
</div>
|
|
154
|
+
<div
|
|
155
|
+
className="no-scrollbar mt-auto flex min-h-0 flex-[0_1_auto] flex-col overflow-y-auto rounded-xl"
|
|
156
|
+
data-right-stack-card
|
|
157
|
+
ref={cardRef}
|
|
158
|
+
>
|
|
159
|
+
{helper}
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</RightStackContext.Provider>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
@@ -78,7 +78,7 @@ export function AppSidebar({
|
|
|
78
78
|
|
|
79
79
|
return (
|
|
80
80
|
<>
|
|
81
|
-
<div className=
|
|
81
|
+
<div className="flex h-full w-full bg-sidebar text-sidebar-foreground">
|
|
82
82
|
{/* Icon Rail */}
|
|
83
83
|
<IconRail
|
|
84
84
|
activePanel={activePanel}
|
|
@@ -17,6 +17,11 @@ export type ExtraPanel = {
|
|
|
17
17
|
label: string
|
|
18
18
|
component: ComponentType
|
|
19
19
|
pluginId?: string
|
|
20
|
+
/** Rail entry that drives the stage (see `SidebarTab.noPanel`); `component` serves the v1 sidebar. */
|
|
21
|
+
noPanel?: boolean
|
|
22
|
+
onSelect?: () => void
|
|
23
|
+
stageActive?: boolean
|
|
24
|
+
onDeselect?: () => void
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
interface IconRailProps {
|
|
@@ -60,7 +65,7 @@ export function IconRail({
|
|
|
60
65
|
<button
|
|
61
66
|
className={cn(
|
|
62
67
|
'flex h-9 w-9 items-center justify-center rounded-lg transition-all',
|
|
63
|
-
isActive ? 'bg-accent' : 'hover:bg-accent',
|
|
68
|
+
isActive ? 'bg-sidebar-accent' : 'hover:bg-sidebar-accent',
|
|
64
69
|
)}
|
|
65
70
|
onClick={() => onPanelChange(panel.id)}
|
|
66
71
|
type="button"
|
|
@@ -102,7 +107,7 @@ export function IconRail({
|
|
|
102
107
|
<button
|
|
103
108
|
className={cn(
|
|
104
109
|
'flex h-9 w-9 items-center justify-center rounded-lg transition-all',
|
|
105
|
-
isActive ? 'bg-accent' : 'hover:bg-accent',
|
|
110
|
+
isActive ? 'bg-sidebar-accent' : 'hover:bg-sidebar-accent',
|
|
106
111
|
)}
|
|
107
112
|
onClick={() => onPanelChange(panel.id)}
|
|
108
113
|
type="button"
|
|
@@ -133,7 +138,7 @@ export function IconRail({
|
|
|
133
138
|
<button
|
|
134
139
|
className={cn(
|
|
135
140
|
'flex h-9 w-9 items-center justify-center rounded-lg transition-all',
|
|
136
|
-
isActive ? 'bg-accent' : 'hover:bg-accent',
|
|
141
|
+
isActive ? 'bg-sidebar-accent' : 'hover:bg-sidebar-accent',
|
|
137
142
|
)}
|
|
138
143
|
onClick={() => onPanelChange(panel.id)}
|
|
139
144
|
type="button"
|
|
@@ -136,7 +136,7 @@ export function FunctionTreePanel({
|
|
|
136
136
|
'relative flex aspect-square items-center justify-center rounded-xl transition-all duration-200',
|
|
137
137
|
isActive
|
|
138
138
|
? 'bg-primary/10 ring-1 ring-primary/50'
|
|
139
|
-
: 'bg-
|
|
139
|
+
: 'bg-black/4 opacity-70 grayscale hover:bg-black/8 hover:opacity-100 hover:grayscale-0 dark:bg-muted/40 dark:hover:bg-muted',
|
|
140
140
|
)}
|
|
141
141
|
onClick={() => {
|
|
142
142
|
triggerSFX('sfx:menu-click')
|
|
@@ -279,7 +279,7 @@ function LegacyItemsPanel({
|
|
|
279
279
|
'cursor-pointer rounded-md px-2 py-0.5 font-medium text-xs transition-colors',
|
|
280
280
|
activePlacementTag === null
|
|
281
281
|
? 'bg-blue-500 text-white'
|
|
282
|
-
: 'bg-blue-950/50 text-blue-300 hover:bg-blue-900/60 hover:text-blue-200',
|
|
282
|
+
: 'bg-blue-50 dark:bg-blue-950/50 text-blue-700 dark:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-900/60 hover:text-blue-800 dark:hover:text-blue-200',
|
|
283
283
|
)}
|
|
284
284
|
onClick={() => setActivePlacementTag(null)}
|
|
285
285
|
type="button"
|
|
@@ -297,8 +297,8 @@ function LegacyItemsPanel({
|
|
|
297
297
|
isActive
|
|
298
298
|
? 'bg-blue-500 text-white'
|
|
299
299
|
: isEmpty
|
|
300
|
-
? 'cursor-not-allowed bg-zinc-800 text-zinc-500'
|
|
301
|
-
: 'bg-blue-950/50 text-blue-300 hover:bg-blue-900/60 hover:text-blue-200',
|
|
300
|
+
? 'cursor-not-allowed bg-zinc-100 dark:bg-zinc-800 text-zinc-400 dark:text-zinc-500'
|
|
301
|
+
: 'bg-blue-50 dark:bg-blue-950/50 text-blue-700 dark:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-900/60 hover:text-blue-800 dark:hover:text-blue-200',
|
|
302
302
|
)}
|
|
303
303
|
disabled={isEmpty}
|
|
304
304
|
key={tag}
|
|
@@ -312,7 +312,7 @@ function LegacyItemsPanel({
|
|
|
312
312
|
isActive
|
|
313
313
|
? 'text-blue-200'
|
|
314
314
|
: isEmpty
|
|
315
|
-
? 'text-zinc-600'
|
|
315
|
+
? 'text-zinc-400 dark:text-zinc-600'
|
|
316
316
|
: 'text-blue-500/70',
|
|
317
317
|
)}
|
|
318
318
|
>
|
|
@@ -337,7 +337,7 @@ function LegacyItemsPanel({
|
|
|
337
337
|
isActive
|
|
338
338
|
? 'bg-violet-500 text-white'
|
|
339
339
|
: isEmpty
|
|
340
|
-
? 'cursor-not-allowed bg-zinc-800 text-zinc-500'
|
|
340
|
+
? 'cursor-not-allowed bg-zinc-100 dark:bg-zinc-800 text-zinc-400 dark:text-zinc-500'
|
|
341
341
|
: 'bg-muted text-muted-foreground hover:bg-muted/80 hover:text-foreground',
|
|
342
342
|
)}
|
|
343
343
|
disabled={isEmpty}
|
|
@@ -352,7 +352,7 @@ function LegacyItemsPanel({
|
|
|
352
352
|
isActive
|
|
353
353
|
? 'text-violet-200'
|
|
354
354
|
: isEmpty
|
|
355
|
-
? 'text-zinc-600'
|
|
355
|
+
? 'text-zinc-400 dark:text-zinc-600'
|
|
356
356
|
: 'text-zinc-500/70',
|
|
357
357
|
)}
|
|
358
358
|
>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ANIMATION_SPEEDS, type AnimationSpeed, normalizeAnimationSpeed } from '@pascal-app/viewer'
|
|
4
|
+
import useConstructionReveal from '../../../../../store/use-construction-reveal'
|
|
5
|
+
import { SegmentedControl } from '../../../controls/segmented-control'
|
|
6
|
+
|
|
7
|
+
const label = (speed: AnimationSpeed) => `×${speed}`
|
|
8
|
+
|
|
9
|
+
// The control's values are strings; a step is its number as text.
|
|
10
|
+
const OPTIONS = ANIMATION_SPEEDS.map((speed) => ({
|
|
11
|
+
label: label(speed),
|
|
12
|
+
value: String(speed),
|
|
13
|
+
title: speed === 1 ? 'The pace builds were made at.' : `Builds play ×${speed} as fast.`,
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
/** How fast what the AI builds plays: ×0.5 to ×2, for the Full and Framing animations and their undo. */
|
|
17
|
+
export function AnimationSpeedSetting() {
|
|
18
|
+
const level = useConstructionReveal((state) => state.level)
|
|
19
|
+
const speed = useConstructionReveal((state) => state.speed)
|
|
20
|
+
const setSpeed = useConstructionReveal((state) => state.setSpeed)
|
|
21
|
+
const off = level === 'off'
|
|
22
|
+
return (
|
|
23
|
+
<div className="space-y-2 py-1">
|
|
24
|
+
<div className="space-y-0.5">
|
|
25
|
+
<p className="font-medium text-sm">Animation speed</p>
|
|
26
|
+
<p className="text-muted-foreground text-xs">
|
|
27
|
+
{off
|
|
28
|
+
? 'Turn the construction animation on to use this.'
|
|
29
|
+
: 'Higher plays a build faster; it applies to the next build.'}
|
|
30
|
+
</p>
|
|
31
|
+
</div>
|
|
32
|
+
<SegmentedControl
|
|
33
|
+
aria-label="Animation speed"
|
|
34
|
+
onChange={(value) => setSpeed(normalizeAnimationSpeed(Number(value)))}
|
|
35
|
+
options={OPTIONS}
|
|
36
|
+
value={String(speed)}
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|