@pascal-app/editor 1.0.0-beta.4 → 1.0.0
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/LICENSE +21 -0
- package/bunfig.toml +4 -0
- package/package.json +15 -8
- package/scripts/generate-print-golden-house.ts +89 -0
- package/src/components/editor/alignment-3d-guide-layer.tsx +12 -3
- package/src/components/editor/bake-exporter.tsx +1 -0
- package/src/components/editor/camera-dragging-lifecycle.test.ts +41 -0
- package/src/components/editor/camera-dragging-lifecycle.ts +10 -2
- package/src/components/editor/capture-camera-rig.tsx +59 -0
- package/src/components/editor/custom-camera-controls.tsx +20 -11
- package/src/components/editor/editor-layout-mobile.tsx +6 -6
- package/src/components/editor/editor-layout-v2.tsx +17 -5
- package/src/components/editor/elevation-3d-guide-layer.tsx +3 -2
- package/src/components/editor/export-manager.tsx +175 -33
- package/src/components/editor/first-person/build-collider-world.test.ts +30 -0
- package/src/components/editor/first-person-controls.tsx +182 -57
- package/src/components/editor/floating-action-menu.tsx +55 -55
- package/src/components/editor/floorplan-background-selection.test.ts +31 -4
- package/src/components/editor/floorplan-background-selection.ts +27 -7
- package/src/components/editor/floorplan-panel.tsx +81 -57
- package/src/components/editor/grid.tsx +12 -7
- package/src/components/editor/group-actions.ts +34 -50
- package/src/components/editor/group-floating-action-menu.tsx +30 -15
- package/src/components/editor/group-move-3d.ts +51 -21
- package/src/components/editor/group-transform-shared.test.ts +78 -0
- package/src/components/editor/group-transform-shared.ts +74 -7
- package/src/components/editor/handles/handle-arrow-raycast.test.ts +86 -0
- package/src/components/editor/handles/handle-arrow.tsx +109 -48
- package/src/components/editor/handles/linear-resize-drag.ts +67 -0
- package/src/components/editor/handles/resize-snap.test.ts +46 -0
- package/src/components/editor/handles/resize-snap.ts +10 -1
- package/src/components/editor/handles/use-handle-drag.ts +18 -1
- package/src/components/editor/index.tsx +252 -46
- package/src/components/editor/measurement-pill.tsx +12 -12
- package/src/components/editor/node-action-menu.tsx +42 -1
- package/src/components/editor/node-arrow-handles.tsx +47 -58
- package/src/components/editor/opening-guides-3d-layer.tsx +13 -4
- package/src/components/editor/selection-manager.tsx +132 -47
- package/src/components/editor/site-edge-labels.tsx +2 -1
- package/src/components/editor/snapshot-capture-overlay.tsx +309 -41
- package/src/components/editor/snapshot-capture.test.ts +422 -0
- package/src/components/editor/snapshot-capture.ts +145 -0
- package/src/components/editor/thumbnail-generator.tsx +295 -232
- package/src/components/editor/use-floorplan-background-placement.ts +53 -5
- package/src/components/editor/wall-measurement-label.tsx +3 -2
- package/src/components/editor/wall-move-side-handles.tsx +299 -7
- package/src/components/editor-2d/floorplan-group-action-menu.tsx +23 -16
- package/src/components/editor-2d/floorplan-group-move.tsx +67 -33
- package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +3 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +129 -38
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +14 -1
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +16 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +2 -0
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +52 -3
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +196 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +204 -114
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +402 -0
- package/src/components/systems/ceiling/ceiling-bracket-batch.ts +371 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +347 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +281 -305
- package/src/components/systems/roof/roof-edit-system.test.tsx +120 -0
- package/src/components/systems/roof/roof-edit-system.tsx +10 -19
- package/src/components/systems/selection-affordance-manager.tsx +59 -9
- package/src/components/systems/selection-affordance-services.ts +19 -0
- package/src/components/systems/stair/stair-edit-system.tsx +5 -2
- package/src/components/tools/fence/fence-drafting.test.ts +100 -0
- package/src/components/tools/fence/fence-drafting.ts +6 -25
- package/src/components/tools/item/draft-creation.test.ts +12 -0
- package/src/components/tools/item/draft-creation.ts +10 -0
- package/src/components/tools/item/face-host-commit.test.ts +65 -0
- package/src/components/tools/item/face-host-commit.ts +56 -0
- package/src/components/tools/item/face-host-preview.test.ts +77 -0
- package/src/components/tools/item/face-host-preview.ts +82 -0
- package/src/components/tools/item/move-tool.tsx +4 -3
- package/src/components/tools/item/placement-math.test.ts +11 -1
- package/src/components/tools/item/placement-math.ts +0 -20
- package/src/components/tools/item/placement-strategies.test.ts +494 -0
- package/src/components/tools/item/placement-strategies.ts +192 -24
- package/src/components/tools/item/placement-surface.test.ts +61 -0
- package/src/components/tools/item/placement-surface.ts +24 -0
- package/src/components/tools/item/placement-types.ts +6 -0
- package/src/components/tools/item/test-face-host.ts +121 -0
- package/src/components/tools/item/use-draft-node.test.tsx +336 -0
- package/src/components/tools/item/use-draft-node.ts +26 -4
- package/src/components/tools/item/use-placement-coordinator.tsx +447 -180
- package/src/components/tools/registered-tool-install-lifecycle.test.tsx +256 -0
- package/src/components/tools/registry/move-registry-node-tool.test.ts +10 -0
- package/src/components/tools/registry/move-registry-node-tool.tsx +306 -110
- package/src/components/tools/registry-tool-context.tsx +30 -0
- package/src/components/tools/select/box-select-tool.tsx +3 -1
- package/src/components/tools/select/marquee-footprint.test.ts +53 -0
- package/src/components/tools/select/marquee-footprint.ts +29 -0
- package/src/components/tools/shared/placement-box.tsx +63 -8
- package/src/components/tools/shared/placement-dimension-guides.tsx +121 -0
- package/src/components/tools/shared/pointer-support-cap.test.ts +319 -17
- package/src/components/tools/shared/pointer-support-cap.ts +45 -8
- package/src/components/tools/site/terrain-brush-cursor.tsx +2 -1
- package/src/components/tools/site/terrain-sculpt-grid.tsx +9 -5
- package/src/components/tools/site/terrain-sculpt-tool.tsx +1 -1
- package/src/components/tools/stair/stair-tool.tsx +127 -74
- package/src/components/tools/tool-manager.tsx +25 -8
- package/src/components/tools/wall/wall-drafting.test.ts +527 -4
- package/src/components/tools/wall/wall-drafting.ts +55 -462
- package/src/components/ui/action-menu/control-modes.tsx +9 -6
- package/src/components/ui/action-menu/index.tsx +11 -1
- package/src/components/ui/command-palette/editor-commands.tsx +12 -13
- package/src/components/ui/command-palette/index.tsx +7 -2
- package/src/components/ui/controls/material-paint-panel.tsx +6 -4
- package/src/components/ui/controls/material-picker.tsx +4 -4
- package/src/components/ui/controls/scene-material-list.tsx +6 -5
- package/src/components/ui/controls/segmented-control.tsx +8 -1
- package/src/components/ui/controls/slider-control.tsx +19 -3
- package/src/components/ui/controls/toggle-control.tsx +18 -5
- package/src/components/ui/controls/tool-options-panel.tsx +126 -0
- package/src/components/ui/floating-level-selector.tsx +23 -6
- package/src/components/ui/helpers/contextual-helper-panel.tsx +9 -0
- package/src/components/ui/helpers/helper-manager.tsx +47 -11
- package/src/components/ui/helpers/item-helper.tsx +1 -1
- package/src/components/ui/helpers/registered-tool-helper.tsx +21 -0
- package/src/components/ui/panels/homogeneous-selection.test.ts +80 -0
- package/src/components/ui/panels/homogeneous-selection.ts +47 -0
- package/src/components/ui/panels/mobile-selection-bar.tsx +11 -5
- package/src/components/ui/panels/multi-field-value.test.ts +209 -0
- package/src/components/ui/panels/multi-field-value.ts +163 -0
- package/src/components/ui/panels/multi-height-mode.tsx +185 -0
- package/src/components/ui/panels/multi-parametric-inspector.tsx +263 -0
- package/src/components/ui/panels/multi-selection-panel.tsx +78 -26
- package/src/components/ui/panels/node-display.ts +4 -0
- package/src/components/ui/panels/panel-manager.tsx +77 -3
- package/src/components/ui/panels/panel-wrapper.tsx +228 -12
- package/src/components/ui/panels/parametric-field-control.tsx +173 -0
- package/src/components/ui/panels/parametric-field-utils.ts +18 -0
- package/src/components/ui/panels/parametric-inspector.tsx +29 -162
- package/src/components/ui/panels/reference-panel.tsx +31 -1
- package/src/components/ui/primitives/shortcut-token.tsx +23 -1
- package/src/components/ui/scene-loader.tsx +35 -0
- package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +7 -2
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +390 -36
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +31 -23
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.test.ts +140 -0
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +109 -0
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +19 -5
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +128 -46
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +5 -2
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +28 -11
- package/src/components/ui/sidebar/panels/site-panel/tree-node.test.ts +8 -0
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +22 -7
- package/src/components/ui/sidebar/tab-bar.tsx +9 -1
- package/src/components/ui/sidebar/use-plugin-panels.tsx +17 -2
- package/src/components/viewer/floorplan-compass-button.tsx +50 -0
- package/src/components/viewer/floorplan-preview-geometry.test.ts +107 -0
- package/src/components/viewer/floorplan-preview-geometry.ts +281 -0
- package/src/components/viewer/floorplan-preview-navigation.test.ts +43 -0
- package/src/components/viewer/floorplan-preview-navigation.ts +62 -0
- package/src/components/viewer/floorplan-preview.tsx +1069 -0
- package/src/components/viewer/use-viewer-camera-navigation-sync.ts +140 -0
- package/src/components/viewer/viewer-scene-header.tsx +2 -1
- package/src/components/viewer/viewer-stage-modes.test.ts +38 -0
- package/src/components/viewer/viewer-stage-modes.ts +49 -0
- package/src/components/viewer/viewer-stage-switcher.tsx +95 -0
- package/src/components/viewer/viewer-stage.test.tsx +47 -0
- package/src/components/viewer/viewer-stage.tsx +221 -0
- package/src/components/viewer-overlay.tsx +13 -30
- package/src/components/viewer-zone-system.tsx +6 -2
- package/src/components/walkthrough-hud.tsx +19 -10
- package/src/hooks/use-auto-save.test.ts +122 -1
- package/src/hooks/use-auto-save.ts +119 -13
- package/src/hooks/use-ceiling-events.test.ts +122 -0
- package/src/hooks/use-drag-action.ts +17 -3
- package/src/hooks/use-grid-events.test.ts +240 -0
- package/src/hooks/use-grid-events.ts +190 -11
- package/src/hooks/use-keyboard.test.ts +244 -0
- package/src/hooks/use-keyboard.ts +164 -63
- package/src/hooks/use-registered-tool-enabled.ts +20 -0
- package/src/hooks/use-save-shortcut.ts +33 -0
- package/src/index.tsx +109 -4
- package/src/lib/active-placement-surface.test.ts +16 -0
- package/src/lib/active-placement-surface.ts +14 -0
- package/src/lib/ceiling-surface-raycast.test.ts +64 -0
- package/src/lib/ceiling-surface-raycast.ts +12 -0
- package/src/lib/contextual-help-extension.ts +17 -0
- package/src/lib/contextual-help.test.ts +8 -0
- package/src/lib/contextual-help.ts +8 -0
- package/src/lib/continuation.test.ts +11 -0
- package/src/lib/continuation.ts +8 -1
- package/src/lib/direct-manipulation.test.ts +132 -0
- package/src/lib/direct-manipulation.ts +38 -1
- package/src/lib/editor-api.ts +6 -3
- package/src/lib/elevation-guides.test.ts +28 -1
- package/src/lib/elevation-guides.ts +22 -7
- package/src/lib/floating-menu-scale.test.ts +29 -0
- package/src/lib/floating-menu-scale.ts +14 -0
- package/src/lib/floorplan/apply-alignment.test.ts +47 -2
- package/src/lib/floorplan/apply-alignment.ts +10 -1
- package/src/lib/floorplan/floorplan-export.test.ts +397 -1
- package/src/lib/floorplan/floorplan-export.tsx +111 -16
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +26 -0
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +32 -24
- package/src/lib/floorplan/floorplan-readonly.test.ts +47 -0
- package/src/lib/floorplan/floorplan-readonly.ts +85 -0
- package/src/lib/floorplan-grid-event-point.test.ts +29 -0
- package/src/lib/floorplan-grid-event-point.ts +13 -0
- package/src/lib/glb-export.test.ts +1169 -21
- package/src/lib/glb-export.ts +629 -101
- package/src/lib/grid-event-presentation.ts +16 -0
- package/src/lib/history.test.ts +523 -77
- package/src/lib/history.ts +133 -14
- package/src/lib/host-tree-children.test.ts +27 -0
- package/src/lib/host-tree-children.ts +79 -0
- package/src/lib/inspector-card-mode.test.ts +92 -0
- package/src/lib/inspector-card-mode.ts +60 -0
- package/src/lib/interaction/hot-set.test.ts +10 -1
- package/src/lib/interaction/hot-set.ts +3 -1
- package/src/lib/interaction/overlay-policy.test.ts +33 -1
- package/src/lib/interaction/overlay-policy.ts +16 -0
- package/src/lib/interaction/registered-drafting.ts +36 -0
- package/src/lib/interaction/scope.ts +31 -0
- package/src/lib/level-print-export.test.ts +484 -0
- package/src/lib/level-print-export.ts +526 -0
- package/src/lib/local-project-presentation-persistence.test.ts +302 -0
- package/src/lib/local-project-presentation-persistence.ts +335 -0
- package/src/lib/material-paint.ts +2 -0
- package/src/lib/measurement-parser.ts +3 -0
- package/src/lib/measurements.test.ts +4 -0
- package/src/lib/model-export.ts +29 -0
- package/src/lib/paint-preview-owner.ts +71 -0
- package/src/lib/paint-scope.test.ts +58 -2
- package/src/lib/paint-scope.ts +8 -2
- package/src/lib/planar-cursor-placement.test.ts +178 -1
- package/src/lib/planar-cursor-placement.ts +61 -5
- package/src/lib/plugin-panels.test.ts +92 -1
- package/src/lib/plugin-panels.ts +48 -0
- package/src/lib/portable-export.test.ts +366 -0
- package/src/lib/portable-export.ts +969 -0
- package/src/lib/print-3mf.test.ts +86 -0
- package/src/lib/print-3mf.ts +161 -0
- package/src/lib/print-content-scope.test.ts +107 -0
- package/src/lib/print-content-scope.ts +90 -0
- package/src/lib/print-export.test.ts +294 -0
- package/src/lib/print-export.ts +735 -0
- package/src/lib/print-feature-thickness.test.ts +147 -0
- package/src/lib/print-feature-thickness.ts +180 -0
- package/src/lib/print-golden-house.test-fixture.ts +352 -0
- package/src/lib/print-golden-house.test.ts +249 -0
- package/src/lib/print-roof-solids.test.ts +133 -0
- package/src/lib/print-roof-solids.ts +630 -0
- package/src/lib/print-shell-compiler-baseline.test.ts +419 -0
- package/src/lib/print-shell-compiler-baseline.ts +225 -0
- package/src/lib/print-shell-compiler-manifold-core.ts +330 -0
- package/src/lib/print-shell-compiler-manifold-worker.ts +187 -0
- package/src/lib/print-shell-compiler-manifold.worker.ts +20 -0
- package/src/lib/print-shell-compiler-mesh-data.ts +32 -0
- package/src/lib/print-shell-compiler-protocol.ts +30 -0
- package/src/lib/print-shell-compiler.ts +273 -0
- package/src/lib/print-wall-solids.test.ts +194 -0
- package/src/lib/print-wall-solids.ts +328 -0
- package/src/lib/rigid-plan-svg-transform.test.ts +35 -0
- package/src/lib/rigid-plan-svg-transform.ts +24 -0
- package/src/lib/scene.ts +22 -8
- package/src/lib/selection-routing.test.ts +125 -6
- package/src/lib/selection-routing.ts +34 -1
- package/src/lib/session-groups.test.ts +88 -0
- package/src/lib/session-groups.ts +201 -0
- package/src/lib/sfx-bus.ts +4 -0
- package/src/lib/sfx-player.ts +8 -0
- package/src/lib/snapping-mode.test.ts +32 -4
- package/src/lib/snapping-mode.ts +11 -7
- package/src/lib/structured-clone-fallback.test.ts +45 -0
- package/src/lib/structured-clone-fallback.ts +24 -0
- package/src/lib/usdz-export.ts +38 -0
- package/src/lib/walkthrough-pointer-lock.ts +47 -0
- package/src/store/live-draft-preview-stores.test.ts +3 -0
- package/src/store/terrain-sculpt-mode.test.ts +3 -6
- package/src/store/tool-mode.test.ts +87 -0
- package/src/store/use-camera-hint-focus.ts +48 -0
- package/src/store/use-editor.tsx +299 -188
- package/src/store/use-floorplan-draft-preview.ts +9 -0
- package/src/store/use-interaction-scope.test.ts +23 -1
- package/src/store/use-interaction-scope.ts +63 -2
- package/src/store/use-placement-preview.ts +56 -3
- package/src/store/use-session-groups.ts +103 -0
- package/src/components/tools/roof/roof-tool.tsx +0 -656
- package/src/components/ui/helpers/roof-helper.tsx +0 -14
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { isNodeKindEnabled, useScene } from '@pascal-app/core'
|
|
4
|
+
import { useEffect } from 'react'
|
|
5
|
+
import useEditor, { type Tool } from '../store/use-editor'
|
|
6
|
+
|
|
7
|
+
export function useRegisteredToolEnabled(tool: Tool | null): boolean {
|
|
8
|
+
const installedPlugins = useScene((state) => state.installedPlugins)
|
|
9
|
+
const enabled = tool === null || isNodeKindEnabled(tool, installedPlugins)
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (enabled || tool === null) return
|
|
13
|
+
|
|
14
|
+
// The render gate has already unmounted the tool, so its own cleanup cancels
|
|
15
|
+
// any draft before clearing the stale selection prevents reinstall remounts.
|
|
16
|
+
if (useEditor.getState().tool === tool) useEditor.getState().setTool(null)
|
|
17
|
+
}, [enabled, tool])
|
|
18
|
+
|
|
19
|
+
return enabled
|
|
20
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from 'react'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Claims Cmd/Ctrl+S for the app's save.
|
|
7
|
+
*
|
|
8
|
+
* Capture phase and ungated on purpose: the browser's "Save page" dialog must
|
|
9
|
+
* never appear anywhere in the editor — including first-person, studio mode and
|
|
10
|
+
* while focus sits in an input, where people still expect the chord to save.
|
|
11
|
+
* `e.code` keeps it on the physical S key across keyboard layouts.
|
|
12
|
+
*/
|
|
13
|
+
export function useSaveShortcut(onSave: () => void) {
|
|
14
|
+
const onSaveRef = useRef(onSave)
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
onSaveRef.current = onSave
|
|
18
|
+
}, [onSave])
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
22
|
+
if (!(e.metaKey || e.ctrlKey) || e.altKey || e.shiftKey) return
|
|
23
|
+
if (e.code !== 'KeyS') return
|
|
24
|
+
|
|
25
|
+
e.preventDefault()
|
|
26
|
+
e.stopPropagation()
|
|
27
|
+
onSaveRef.current()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
window.addEventListener('keydown', handleKeyDown, true)
|
|
31
|
+
return () => window.removeEventListener('keydown', handleKeyDown, true)
|
|
32
|
+
}, [])
|
|
33
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -56,6 +56,7 @@ export {
|
|
|
56
56
|
formatMeasurement,
|
|
57
57
|
MeasurementPill,
|
|
58
58
|
} from './components/editor/measurement-pill'
|
|
59
|
+
export { NodeActionMenu } from './components/editor/node-action-menu'
|
|
59
60
|
// In-world arrow handle primitives (chevron geometry, invisible hit area,
|
|
60
61
|
// shared material, palette + scale constants). Re-exported so kind-owned
|
|
61
62
|
// 3D selection affordances in `@pascal-app/nodes` (duct side-move / height /
|
|
@@ -98,6 +99,11 @@ export {
|
|
|
98
99
|
buildSvgArrowHeadPoints,
|
|
99
100
|
getArcPlanPoint,
|
|
100
101
|
} from './components/editor-2d/svg-paths'
|
|
102
|
+
export type {
|
|
103
|
+
SelectionAffordanceHistoryApi,
|
|
104
|
+
SelectionAffordanceInteractionApi,
|
|
105
|
+
SelectionAffordanceProps,
|
|
106
|
+
} from './components/systems/selection-affordance-services'
|
|
101
107
|
// Phase 5 Stage D transitional exports — pure drafting / angle helpers
|
|
102
108
|
// consumed by kind-owned drag actions in @pascal-app/nodes. Stage F
|
|
103
109
|
// cleanup moves these into @pascal-app/nodes (fence/drafting.ts +
|
|
@@ -113,7 +119,6 @@ export { MoveTool } from './components/tools/item/move-tool'
|
|
|
113
119
|
// `@pascal-app/nodes` (wall curve sagitta snap, door / window placement,
|
|
114
120
|
// item drop) so kinds don't reach into editor internals.
|
|
115
121
|
export {
|
|
116
|
-
calculateCursorRotation,
|
|
117
122
|
calculateItemRotation,
|
|
118
123
|
getSideFromNormal,
|
|
119
124
|
isValidWallSideFace,
|
|
@@ -132,6 +137,7 @@ export {
|
|
|
132
137
|
type PlacementCoordinatorConfig,
|
|
133
138
|
usePlacementCoordinator,
|
|
134
139
|
} from './components/tools/item/use-placement-coordinator'
|
|
140
|
+
export { useRegistryToolContext } from './components/tools/registry-tool-context'
|
|
135
141
|
export { CursorSphere } from './components/tools/shared/cursor-sphere'
|
|
136
142
|
export { DragBoundingBox } from './components/tools/shared/drag-bounding-box'
|
|
137
143
|
export { getFloorStackPreviewPosition } from './components/tools/shared/floor-stack-preview'
|
|
@@ -144,6 +150,7 @@ export {
|
|
|
144
150
|
resolveLevelConstructionPlane,
|
|
145
151
|
} from './components/tools/shared/horizontal-construction-plane'
|
|
146
152
|
export { PlacementBox } from './components/tools/shared/placement-box'
|
|
153
|
+
export { PlacementDimensionGuides } from './components/tools/shared/placement-dimension-guides'
|
|
147
154
|
// Pointer-decided support surface (deck top vs floor underneath) — the
|
|
148
155
|
// draw tools (wall / fence) ride their grid plane and commit cap on it.
|
|
149
156
|
export {
|
|
@@ -234,6 +241,7 @@ export { SegmentedControl } from './components/ui/controls/segmented-control'
|
|
|
234
241
|
export { SliderControl } from './components/ui/controls/slider-control'
|
|
235
242
|
export { TerrainSculptPanel } from './components/ui/controls/terrain-sculpt-panel'
|
|
236
243
|
export { ToggleControl } from './components/ui/controls/toggle-control'
|
|
244
|
+
export { ToolOptionsPanel } from './components/ui/controls/tool-options-panel'
|
|
237
245
|
export { FloatingLevelSelector } from './components/ui/floating-level-selector'
|
|
238
246
|
export { CATALOG_ITEMS } from './components/ui/item-catalog/catalog-items'
|
|
239
247
|
// Item collections UI — used by the kind-owned ItemPanel in nodes/.
|
|
@@ -254,6 +262,10 @@ export {
|
|
|
254
262
|
DropdownMenuSubTrigger,
|
|
255
263
|
DropdownMenuTrigger,
|
|
256
264
|
} from './components/ui/primitives/dropdown-menu'
|
|
265
|
+
export {
|
|
266
|
+
ShortcutToken,
|
|
267
|
+
shortcutDisplayValue,
|
|
268
|
+
} from './components/ui/primitives/shortcut-token'
|
|
257
269
|
export { useSidebarStore } from './components/ui/primitives/sidebar'
|
|
258
270
|
export { Slider } from './components/ui/primitives/slider'
|
|
259
271
|
export { SceneLoader } from './components/ui/scene-loader'
|
|
@@ -274,6 +286,16 @@ export {
|
|
|
274
286
|
SnapTargetBadge,
|
|
275
287
|
SnapTargetIcon,
|
|
276
288
|
} from './components/ui/snap-target-badge'
|
|
289
|
+
export {
|
|
290
|
+
FloorplanCompassButton,
|
|
291
|
+
type FloorplanCompassButtonProps,
|
|
292
|
+
} from './components/viewer/floorplan-compass-button'
|
|
293
|
+
export {
|
|
294
|
+
FloorplanPreview,
|
|
295
|
+
type FloorplanPreviewProps,
|
|
296
|
+
type FloorplanPreviewScene,
|
|
297
|
+
} from './components/viewer/floorplan-preview'
|
|
298
|
+
export { useViewerCameraNavigationSync } from './components/viewer/use-viewer-camera-navigation-sync'
|
|
277
299
|
export {
|
|
278
300
|
ViewerControlsBar,
|
|
279
301
|
type ViewerControlsBarProps,
|
|
@@ -282,6 +304,19 @@ export {
|
|
|
282
304
|
ViewerSceneHeader,
|
|
283
305
|
type ViewerSceneHeaderProps,
|
|
284
306
|
} from './components/viewer/viewer-scene-header'
|
|
307
|
+
export { ViewerStage, type ViewerStageProps } from './components/viewer/viewer-stage'
|
|
308
|
+
export {
|
|
309
|
+
normalizeViewerStageModes,
|
|
310
|
+
resolveMobileViewerStageMode,
|
|
311
|
+
resolveViewerStageMode,
|
|
312
|
+
VIEWER_STAGE_MODES,
|
|
313
|
+
viewerStageIncludes3D,
|
|
314
|
+
} from './components/viewer/viewer-stage-modes'
|
|
315
|
+
export {
|
|
316
|
+
type ViewerStageMode,
|
|
317
|
+
ViewerStageSwitcher,
|
|
318
|
+
type ViewerStageSwitcherProps,
|
|
319
|
+
} from './components/viewer/viewer-stage-switcher'
|
|
285
320
|
export {
|
|
286
321
|
WalkthroughHud,
|
|
287
322
|
type WalkthroughHudProps,
|
|
@@ -310,6 +345,12 @@ export {
|
|
|
310
345
|
resolveCeilingPlanPointSnap,
|
|
311
346
|
} from './lib/ceiling-plan-snap'
|
|
312
347
|
export { EDITOR_LAYER } from './lib/constants'
|
|
348
|
+
export type { ContextualShortcutHint } from './lib/contextual-help'
|
|
349
|
+
export {
|
|
350
|
+
CONTEXTUAL_HELP_NODE_EXTENSION_KEY,
|
|
351
|
+
type ContextualHelpNodeExtension,
|
|
352
|
+
getContextualHelpNodeExtension,
|
|
353
|
+
} from './lib/contextual-help-extension'
|
|
313
354
|
// Helper libs used by the kind-owned roof / stair / elevator panels.
|
|
314
355
|
export {
|
|
315
356
|
CONTINUATION_PROFILES,
|
|
@@ -318,6 +359,7 @@ export {
|
|
|
318
359
|
continuationContextOf,
|
|
319
360
|
nextContinuation,
|
|
320
361
|
} from './lib/continuation'
|
|
362
|
+
export { createEditorApi } from './lib/editor-api'
|
|
321
363
|
export {
|
|
322
364
|
clearStructuralElevationGuide,
|
|
323
365
|
collectElevationSnapTargets,
|
|
@@ -325,6 +367,7 @@ export {
|
|
|
325
367
|
type ElevationGuideSource,
|
|
326
368
|
type ElevationSnapMatch,
|
|
327
369
|
type ElevationSnapTarget,
|
|
370
|
+
publishResolvedElevationGuide,
|
|
328
371
|
publishStructuralElevationGuide,
|
|
329
372
|
resolveElevationSnapMatch,
|
|
330
373
|
resolveStructuralElevationSnap,
|
|
@@ -335,6 +378,7 @@ export {
|
|
|
335
378
|
resolveElevatorSupportLevelId,
|
|
336
379
|
resolveElevatorSupportY,
|
|
337
380
|
} from './lib/elevator-support'
|
|
381
|
+
export { getFloatingMenuScale } from './lib/floating-menu-scale'
|
|
338
382
|
// Floor-plan stair helpers — the cumulative-transform walk
|
|
339
383
|
// (`computeFloorplanStairSegmentTransforms`) and the rich segment-entry
|
|
340
384
|
// builder (`buildFloorplanStairEntry`) used by the kind-owned stair
|
|
@@ -359,6 +403,10 @@ export type {
|
|
|
359
403
|
FloorplanAnnotationCategory,
|
|
360
404
|
FloorplanAnnotationVisibility,
|
|
361
405
|
} from './lib/floorplan/annotation-visibility'
|
|
406
|
+
export {
|
|
407
|
+
exportFloorplanPdf,
|
|
408
|
+
type FloorplanExportScope,
|
|
409
|
+
} from './lib/floorplan/floorplan-export'
|
|
362
410
|
export {
|
|
363
411
|
createFloorplanContextExtensions,
|
|
364
412
|
FLOORPLAN_CONTEXT_EXTENSION_KEY,
|
|
@@ -384,23 +432,47 @@ export {
|
|
|
384
432
|
type FloorplanMode,
|
|
385
433
|
isFloorplanToolAvailableInMode,
|
|
386
434
|
} from './lib/floorplan/floorplan-mode'
|
|
387
|
-
export { commitFreshPlacementSubtree } from './lib/fresh-planar-placement'
|
|
388
|
-
export { exportSceneToGlb } from './lib/glb-export'
|
|
389
435
|
export {
|
|
436
|
+
commitFreshPlacementSubtree,
|
|
437
|
+
createFreshPlacementSubtree,
|
|
438
|
+
} from './lib/fresh-planar-placement'
|
|
439
|
+
export { exportSceneToGlb, type GlbExportOptions } from './lib/glb-export'
|
|
440
|
+
export {
|
|
441
|
+
type EditorGridEvent,
|
|
442
|
+
type GridEventScreenProjection,
|
|
443
|
+
getGridEventScreenProjection,
|
|
444
|
+
} from './lib/grid-event-presentation'
|
|
445
|
+
export {
|
|
446
|
+
getHistoryCommandState,
|
|
390
447
|
type HistoryCommandDelegate,
|
|
448
|
+
type HistoryCommandResult,
|
|
449
|
+
type HistoryCommandState,
|
|
391
450
|
installHistoryCommandDelegate,
|
|
392
451
|
runRedo,
|
|
393
452
|
runUndo,
|
|
453
|
+
subscribeHistoryCommandState,
|
|
394
454
|
} from './lib/history'
|
|
455
|
+
export {
|
|
456
|
+
type EditorHostTreeChildren,
|
|
457
|
+
type EditorHostTreeChildrenProps,
|
|
458
|
+
editorHostTreeChildrenRegistry,
|
|
459
|
+
registerEditorHostTreeChildren,
|
|
460
|
+
} from './lib/host-tree-children'
|
|
461
|
+
export {
|
|
462
|
+
DRAFTING_SURFACE_EXTENSION_KEY,
|
|
463
|
+
type DraftingSurfaceExtension,
|
|
464
|
+
} from './lib/interaction/registered-drafting'
|
|
395
465
|
export {
|
|
396
466
|
boundaryReshapeScope,
|
|
397
467
|
curveReshapeScope,
|
|
398
468
|
endpointReshapeScope,
|
|
399
469
|
holeEditScope,
|
|
470
|
+
meshEditScope,
|
|
400
471
|
movingNodeOf,
|
|
401
472
|
scopeNodeId,
|
|
402
473
|
} from './lib/interaction/scope'
|
|
403
474
|
export {
|
|
475
|
+
type ActivePaintMaterial,
|
|
404
476
|
buildResetSurfaceMaterialUpdates,
|
|
405
477
|
buildRoofSurfaceMaterialPatch,
|
|
406
478
|
buildSingleSurfaceMaterialPatch,
|
|
@@ -419,6 +491,14 @@ export {
|
|
|
419
491
|
measurementPolygonLabelAnchor,
|
|
420
492
|
triangulateMeasurementPolygon,
|
|
421
493
|
} from './lib/measurement-label'
|
|
494
|
+
export {
|
|
495
|
+
type LingoUnitSpec,
|
|
496
|
+
lingoUnitSpec,
|
|
497
|
+
type MeasurementHintOptions,
|
|
498
|
+
measurementHint,
|
|
499
|
+
type ParseMeasurementOptions,
|
|
500
|
+
parseMeasurement,
|
|
501
|
+
} from './lib/measurement-parser'
|
|
422
502
|
export {
|
|
423
503
|
buildMeasurementAngleArcPoints,
|
|
424
504
|
cubicMetersToVolumeUnit,
|
|
@@ -440,6 +520,12 @@ export {
|
|
|
440
520
|
metersToLinearUnit,
|
|
441
521
|
squareMetersToAreaUnit,
|
|
442
522
|
} from './lib/measurements'
|
|
523
|
+
export type {
|
|
524
|
+
ModelExport,
|
|
525
|
+
ModelExportArtifact,
|
|
526
|
+
ModelExportFormat,
|
|
527
|
+
ModelExportOptions,
|
|
528
|
+
} from './lib/model-export'
|
|
443
529
|
export { consumePlacementDragRelease } from './lib/placement-drag-release'
|
|
444
530
|
export {
|
|
445
531
|
addFreshPlacementMetadata,
|
|
@@ -483,6 +569,13 @@ export {
|
|
|
483
569
|
type SlabPlanSnapInput,
|
|
484
570
|
type SlabPlanSnapResult,
|
|
485
571
|
} from './lib/slab-plan-snap'
|
|
572
|
+
export {
|
|
573
|
+
getSnappingModeLabel,
|
|
574
|
+
resolveSnapFlags,
|
|
575
|
+
type SnapContext,
|
|
576
|
+
type SnapFlags,
|
|
577
|
+
type SnappingMode,
|
|
578
|
+
} from './lib/snapping-mode'
|
|
486
579
|
export { duplicateStairSubtree } from './lib/stair-duplication'
|
|
487
580
|
export {
|
|
488
581
|
getBuildingLevelsForLevel,
|
|
@@ -506,6 +599,7 @@ export {
|
|
|
506
599
|
resolveFlattenTarget,
|
|
507
600
|
sculptFieldForSite,
|
|
508
601
|
} from './lib/terrain-sculpt'
|
|
602
|
+
export { exportSceneToUsdz, type UsdzExportOptions } from './lib/usdz-export'
|
|
509
603
|
// `cn` (twMerge + clsx) — used by kind-owned panels in `@pascal-app/
|
|
510
604
|
// nodes` so they don't need their own copy / their own tailwind-merge
|
|
511
605
|
// dependency.
|
|
@@ -521,6 +615,7 @@ export {
|
|
|
521
615
|
export { subscribeCameraPose } from './store/camera-pose-store'
|
|
522
616
|
export { default as useAlignmentGuides } from './store/use-alignment-guides'
|
|
523
617
|
export { default as useAudio } from './store/use-audio'
|
|
618
|
+
export { type CameraHintAction, useCameraHintFocus } from './store/use-camera-hint-focus'
|
|
524
619
|
export { type CommandAction, useCommandRegistry } from './store/use-command-registry'
|
|
525
620
|
export {
|
|
526
621
|
DRAWING_TYPE_OPTIONS,
|
|
@@ -529,17 +624,24 @@ export {
|
|
|
529
624
|
export type {
|
|
530
625
|
CaptureMode,
|
|
531
626
|
FloorplanSelectionTool,
|
|
627
|
+
Mode,
|
|
532
628
|
SnapshotCropMode,
|
|
533
629
|
SnapshotStandardAspect,
|
|
534
630
|
SplitOrientation,
|
|
631
|
+
StructureTool,
|
|
535
632
|
Tool,
|
|
536
633
|
ToolDefaults,
|
|
634
|
+
ToolMode,
|
|
537
635
|
ViewMode,
|
|
538
636
|
WorkspaceMode,
|
|
539
637
|
} from './store/use-editor'
|
|
540
638
|
export {
|
|
639
|
+
armMaterialPaint,
|
|
640
|
+
armToolMode,
|
|
541
641
|
default as useEditor,
|
|
542
642
|
getActiveContinuationContext,
|
|
643
|
+
getActiveSnapContext,
|
|
644
|
+
getActiveSnappingMode,
|
|
543
645
|
getContinuation,
|
|
544
646
|
isAlignmentGuideActive,
|
|
545
647
|
isAngleSnapActive,
|
|
@@ -596,7 +698,10 @@ export {
|
|
|
596
698
|
type PathDraftPoint,
|
|
597
699
|
usePathDraftPreview,
|
|
598
700
|
} from './store/use-path-draft-preview'
|
|
599
|
-
export {
|
|
701
|
+
export {
|
|
702
|
+
default as usePlacementPreview,
|
|
703
|
+
type PlacementPreviewDimension,
|
|
704
|
+
} from './store/use-placement-preview'
|
|
600
705
|
export {
|
|
601
706
|
activateQuickMeasurementHudSource,
|
|
602
707
|
clearQuickMeasurementHudSource,
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
clearPlacementSurface,
|
|
5
5
|
getPlacementSurface,
|
|
6
6
|
publishPlacementSurface,
|
|
7
|
+
usesOrientedPlacementPlane,
|
|
7
8
|
} from './active-placement-surface'
|
|
8
9
|
|
|
9
10
|
describe('active placement surface', () => {
|
|
@@ -21,4 +22,19 @@ describe('active placement surface', () => {
|
|
|
21
22
|
|
|
22
23
|
expect(getPlacementSurface()?.projection).toBe('surface')
|
|
23
24
|
})
|
|
25
|
+
|
|
26
|
+
test('copies a stable lattice anchor when one is provided', () => {
|
|
27
|
+
publishPlacementSurface(
|
|
28
|
+
new Vector3(4, 2, 3),
|
|
29
|
+
new Vector3(0, 0, 1),
|
|
30
|
+
'surface',
|
|
31
|
+
new Vector3(1, 2, 3),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
expect(getPlacementSurface()?.anchor?.toArray()).toEqual([1, 2, 3])
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('orients the grid to a sloped placement surface', () => {
|
|
38
|
+
expect(usesOrientedPlacementPlane(new Vector3(0, 0.6, 0.8))).toBe(true)
|
|
39
|
+
})
|
|
24
40
|
})
|
|
@@ -11,12 +11,15 @@ import { Vector3 } from 'three'
|
|
|
11
11
|
// readers must consume them within the same frame.
|
|
12
12
|
export type PlacementSurface = {
|
|
13
13
|
point: Vector3
|
|
14
|
+
/** Optional stable origin for the construction lattice. */
|
|
15
|
+
anchor?: Vector3
|
|
14
16
|
normal: Vector3
|
|
15
17
|
projection: 'surface' | 'fixed-plane'
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
const surface: PlacementSurface = {
|
|
19
21
|
point: new Vector3(),
|
|
22
|
+
anchor: new Vector3(),
|
|
20
23
|
normal: new Vector3(0, 1, 0),
|
|
21
24
|
projection: 'surface',
|
|
22
25
|
}
|
|
@@ -26,8 +29,15 @@ export function publishPlacementSurface(
|
|
|
26
29
|
point: Vector3,
|
|
27
30
|
normal: Vector3,
|
|
28
31
|
projection: PlacementSurface['projection'] = 'surface',
|
|
32
|
+
anchor?: Vector3,
|
|
29
33
|
): void {
|
|
30
34
|
surface.point.copy(point)
|
|
35
|
+
if (anchor) {
|
|
36
|
+
surface.anchor ??= new Vector3()
|
|
37
|
+
surface.anchor.copy(anchor)
|
|
38
|
+
} else {
|
|
39
|
+
surface.anchor = undefined
|
|
40
|
+
}
|
|
31
41
|
surface.normal.copy(normal)
|
|
32
42
|
surface.projection = projection
|
|
33
43
|
active = true
|
|
@@ -40,3 +50,7 @@ export function clearPlacementSurface(): void {
|
|
|
40
50
|
export function getPlacementSurface(): PlacementSurface | null {
|
|
41
51
|
return active ? surface : null
|
|
42
52
|
}
|
|
53
|
+
|
|
54
|
+
export function usesOrientedPlacementPlane(normal: Vector3): boolean {
|
|
55
|
+
return Math.abs(normal.y) < 0.95
|
|
56
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
BackSide,
|
|
4
|
+
Mesh,
|
|
5
|
+
MeshBasicMaterial,
|
|
6
|
+
Path,
|
|
7
|
+
Raycaster,
|
|
8
|
+
Shape,
|
|
9
|
+
ShapeGeometry,
|
|
10
|
+
Vector3,
|
|
11
|
+
} from 'three'
|
|
12
|
+
import { raycastCeilingUnderside } from './ceiling-surface-raycast'
|
|
13
|
+
|
|
14
|
+
function ceilingMesh() {
|
|
15
|
+
const shape = new Shape()
|
|
16
|
+
shape.moveTo(-2, -2)
|
|
17
|
+
shape.lineTo(2, -2)
|
|
18
|
+
shape.lineTo(2, 2)
|
|
19
|
+
shape.lineTo(-2, 2)
|
|
20
|
+
shape.closePath()
|
|
21
|
+
const hole = new Path()
|
|
22
|
+
hole.moveTo(-0.5, -0.5)
|
|
23
|
+
hole.lineTo(-0.5, 0.5)
|
|
24
|
+
hole.lineTo(0.5, 0.5)
|
|
25
|
+
hole.lineTo(0.5, -0.5)
|
|
26
|
+
hole.closePath()
|
|
27
|
+
shape.holes.push(hole)
|
|
28
|
+
const geometry = new ShapeGeometry(shape).rotateX(-Math.PI / 2)
|
|
29
|
+
const mesh = new Mesh(geometry, new MeshBasicMaterial({ side: BackSide }))
|
|
30
|
+
mesh.position.y = 3
|
|
31
|
+
mesh.updateMatrixWorld(true)
|
|
32
|
+
return mesh
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe('ceiling drawing surfaces', () => {
|
|
36
|
+
test('lets drawing pass through the transparent top while picking the underside', () => {
|
|
37
|
+
const ceiling = ceilingMesh()
|
|
38
|
+
for (const side of [-1, 1]) {
|
|
39
|
+
const ray = new Raycaster(new Vector3(1, 3 + side * 2, 0), new Vector3(0, -side, 0))
|
|
40
|
+
const hit = raycastCeilingUnderside(ray, ceiling)[0]
|
|
41
|
+
if (side > 0) {
|
|
42
|
+
expect(hit).toBeUndefined()
|
|
43
|
+
} else {
|
|
44
|
+
expect(hit?.point.y).toBeCloseTo(3)
|
|
45
|
+
expect(hit?.object).toBe(ceiling)
|
|
46
|
+
}
|
|
47
|
+
expect(ceiling.material.side).toBe(BackSide)
|
|
48
|
+
}
|
|
49
|
+
ceiling.geometry.dispose()
|
|
50
|
+
ceiling.material.dispose()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('respects ceiling holes and the polygon boundary from either side', () => {
|
|
54
|
+
const ceiling = ceilingMesh()
|
|
55
|
+
for (const side of [-1, 1]) {
|
|
56
|
+
for (const x of [0, 3]) {
|
|
57
|
+
const ray = new Raycaster(new Vector3(x, 3 + side * 2, 0), new Vector3(0, -side, 0))
|
|
58
|
+
expect(raycastCeilingUnderside(ray, ceiling)).toHaveLength(0)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
ceiling.geometry.dispose()
|
|
62
|
+
ceiling.material.dispose()
|
|
63
|
+
})
|
|
64
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BackSide, Mesh, MeshBasicMaterial, type Object3D, type Raycaster } from 'three'
|
|
2
|
+
|
|
3
|
+
const pickingMaterial = new MeshBasicMaterial({ side: BackSide })
|
|
4
|
+
|
|
5
|
+
export function raycastCeilingUnderside(raycaster: Raycaster, ceiling: Object3D) {
|
|
6
|
+
if (!(ceiling instanceof Mesh)) return []
|
|
7
|
+
// Ignore the transparent top and its grid overlay so drawing can reach surfaces below.
|
|
8
|
+
const proxy = new Mesh(ceiling.geometry, pickingMaterial)
|
|
9
|
+
proxy.matrixWorld.copy(ceiling.matrixWorld)
|
|
10
|
+
proxy.layers.mask = ceiling.layers.mask
|
|
11
|
+
return raycaster.intersectObject(proxy, false).map((hit) => ({ ...hit, object: ceiling }))
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NodeDefinition } from '@pascal-app/core'
|
|
2
|
+
import type { ContextualShortcutHint } from './contextual-help'
|
|
3
|
+
|
|
4
|
+
export const CONTEXTUAL_HELP_NODE_EXTENSION_KEY = 'pascal:editor/contextual-help'
|
|
5
|
+
|
|
6
|
+
export type ContextualHelpNodeExtension = {
|
|
7
|
+
subscribe: (onChange: () => void) => () => void
|
|
8
|
+
getHints: (nodeId: string) => ContextualShortcutHint[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getContextualHelpNodeExtension(
|
|
12
|
+
definition: NodeDefinition<any> | undefined,
|
|
13
|
+
): ContextualHelpNodeExtension | undefined {
|
|
14
|
+
return definition?.extensions?.[CONTEXTUAL_HELP_NODE_EXTENSION_KEY] as
|
|
15
|
+
| ContextualHelpNodeExtension
|
|
16
|
+
| undefined
|
|
17
|
+
}
|
|
@@ -73,6 +73,14 @@ describe('resolveSelectModeHelpHints', () => {
|
|
|
73
73
|
label: 'Click or drag the selection to move it as one',
|
|
74
74
|
},
|
|
75
75
|
{ keys: ['R / T'], label: 'Rotate the selection ±45°' },
|
|
76
|
+
{
|
|
77
|
+
keys: ['Cmd/Ctrl', 'G'],
|
|
78
|
+
label: 'Group selection (session only)',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
keys: ['Cmd/Ctrl', 'Shift', 'G'],
|
|
82
|
+
label: 'Ungroup session selection',
|
|
83
|
+
},
|
|
76
84
|
{
|
|
77
85
|
keys: [['Cmd/Ctrl', 'Shift'], 'Left click'],
|
|
78
86
|
label: 'Add or remove objects from the selection',
|
|
@@ -93,6 +93,14 @@ export function resolveSelectModeHelpHints({
|
|
|
93
93
|
label: 'Click or drag the selection to move it as one',
|
|
94
94
|
})
|
|
95
95
|
hints.push({ keys: [ROTATE_KEYS], label: 'Rotate the selection ±45°' })
|
|
96
|
+
hints.push({
|
|
97
|
+
keys: [COMMAND_KEY, 'G'],
|
|
98
|
+
label: 'Group selection (session only)',
|
|
99
|
+
})
|
|
100
|
+
hints.push({
|
|
101
|
+
keys: [COMMAND_KEY, SHIFT_KEY, 'G'],
|
|
102
|
+
label: 'Ungroup session selection',
|
|
103
|
+
})
|
|
96
104
|
hints.push({
|
|
97
105
|
keys: [[COMMAND_KEY, SHIFT_KEY], LEFT_CLICK],
|
|
98
106
|
label: 'Add or remove objects from the selection',
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { CONTINUATION_PROFILES, continuationContextOf, nextContinuation } from './continuation'
|
|
3
|
+
|
|
4
|
+
describe('canopy continuation', () => {
|
|
5
|
+
test('maps the canopy tool to its own single and continuous profile', () => {
|
|
6
|
+
expect(continuationContextOf('lean-to-extension')).toBe('canopy')
|
|
7
|
+
expect(CONTINUATION_PROFILES.canopy.default).toBe('single')
|
|
8
|
+
expect(nextContinuation('canopy', 'single')).toBe('continuous')
|
|
9
|
+
expect(nextContinuation('canopy', 'continuous')).toBe('single')
|
|
10
|
+
})
|
|
11
|
+
})
|
package/src/lib/continuation.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ContinuationContext = 'wall' | 'fence' | 'point' | 'cabinet'
|
|
1
|
+
export type ContinuationContext = 'wall' | 'fence' | 'point' | 'cabinet' | 'canopy'
|
|
2
2
|
export type ContinuationMode = string
|
|
3
3
|
|
|
4
4
|
export const CONTINUATION_PROFILES: Record<
|
|
@@ -42,6 +42,12 @@ export const CONTINUATION_PROFILES: Record<
|
|
|
42
42
|
labels: { single: 'Single cabinet', continuous: 'Continuous run' },
|
|
43
43
|
icons: { single: 'lucide:minus', continuous: 'lucide:waypoints' },
|
|
44
44
|
},
|
|
45
|
+
canopy: {
|
|
46
|
+
options: ['single', 'continuous'],
|
|
47
|
+
default: 'single',
|
|
48
|
+
labels: { single: 'Single canopy', continuous: 'Continuous canopy' },
|
|
49
|
+
icons: { single: 'lucide:minus', continuous: 'lucide:waypoints' },
|
|
50
|
+
},
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
const POINT_KINDS = new Set(['item', 'door', 'window', 'shelf', 'column'])
|
|
@@ -60,5 +66,6 @@ export function continuationContextOf(kind: string): ContinuationContext | null
|
|
|
60
66
|
if (kind === 'wall') return 'wall'
|
|
61
67
|
if (kind === 'fence') return 'fence'
|
|
62
68
|
if (kind === 'cabinet') return 'cabinet'
|
|
69
|
+
if (kind === 'lean-to-extension') return 'canopy'
|
|
63
70
|
return POINT_KINDS.has(kind) ? 'point' : null
|
|
64
71
|
}
|