@pascal-app/editor 0.4.0 → 0.6.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/package.json +8 -7
- package/src/components/editor/editor-layout-v2.tsx +9 -0
- package/src/components/editor/floating-action-menu.tsx +341 -48
- package/src/components/editor/floating-building-action-menu.tsx +70 -0
- package/src/components/editor/floorplan-panel.tsx +1350 -722
- package/src/components/editor/index.tsx +221 -167
- package/src/components/editor/node-action-menu.tsx +40 -11
- package/src/components/editor/selection-manager.tsx +238 -10
- package/src/components/editor/site-edge-labels.tsx +9 -3
- package/src/components/editor/thumbnail-generator.tsx +422 -79
- package/src/components/editor/wall-measurement-label.tsx +120 -32
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +272 -0
- package/src/components/systems/roof/roof-edit-system.tsx +5 -5
- package/src/components/systems/stair/stair-edit-system.tsx +27 -5
- package/src/components/tools/building/move-building-tool.tsx +157 -0
- package/src/components/tools/ceiling/ceiling-hole-editor.tsx +1 -0
- package/src/components/tools/ceiling/move-ceiling-tool.tsx +257 -0
- package/src/components/tools/door/door-math.ts +1 -1
- package/src/components/tools/door/door-tool.tsx +31 -7
- package/src/components/tools/door/move-door-tool.tsx +27 -8
- package/src/components/tools/fence/curve-fence-tool.tsx +179 -0
- package/src/components/tools/fence/fence-drafting.ts +137 -0
- package/src/components/tools/fence/fence-tool.tsx +190 -0
- package/src/components/tools/fence/move-fence-endpoint-tool.tsx +327 -0
- package/src/components/tools/fence/move-fence-tool.tsx +231 -0
- package/src/components/tools/item/item-tool.tsx +3 -3
- package/src/components/tools/item/move-tool.tsx +16 -0
- package/src/components/tools/item/placement-math.ts +14 -6
- package/src/components/tools/item/placement-strategies.ts +17 -9
- package/src/components/tools/item/use-placement-coordinator.tsx +123 -16
- package/src/components/tools/roof/move-roof-tool.tsx +90 -26
- package/src/components/tools/roof/roof-tool.tsx +6 -6
- package/src/components/tools/select/box-select-tool.tsx +2 -2
- package/src/components/tools/shared/polygon-editor.tsx +98 -8
- package/src/components/tools/slab/move-slab-tool.tsx +182 -0
- package/src/components/tools/slab/slab-hole-editor.tsx +1 -0
- package/src/components/tools/slab/slab-tool.tsx +4 -4
- package/src/components/tools/stair/stair-defaults.ts +10 -0
- package/src/components/tools/stair/stair-tool.tsx +39 -8
- package/src/components/tools/tool-manager.tsx +54 -14
- package/src/components/tools/wall/curve-wall-tool.tsx +176 -0
- package/src/components/tools/wall/move-wall-endpoint-tool.tsx +322 -0
- package/src/components/tools/wall/move-wall-tool.tsx +356 -0
- package/src/components/tools/wall/wall-drafting.ts +331 -9
- package/src/components/tools/wall/wall-tool.tsx +19 -29
- package/src/components/tools/window/move-window-tool.tsx +27 -8
- package/src/components/tools/window/window-math.ts +1 -1
- package/src/components/tools/window/window-tool.tsx +31 -7
- package/src/components/tools/zone/zone-tool.tsx +7 -7
- package/src/components/ui/action-menu/control-modes.tsx +9 -4
- package/src/components/ui/action-menu/structure-tools.tsx +1 -0
- package/src/components/ui/command-palette/editor-commands.tsx +9 -4
- package/src/components/ui/command-palette/index.tsx +0 -1
- package/src/components/ui/controls/material-picker.tsx +127 -94
- package/src/components/ui/controls/slider-control.tsx +28 -14
- package/src/components/ui/helpers/building-helper.tsx +32 -0
- package/src/components/ui/helpers/helper-manager.tsx +2 -0
- package/src/components/ui/item-catalog/catalog-items.tsx +5 -0
- package/src/components/ui/panels/ceiling-panel.tsx +61 -17
- package/src/components/ui/panels/door-panel.tsx +5 -5
- package/src/components/ui/panels/fence-panel.tsx +269 -0
- package/src/components/ui/panels/item-panel.tsx +5 -5
- package/src/components/ui/panels/panel-manager.tsx +32 -27
- package/src/components/ui/panels/reference-panel.tsx +5 -4
- package/src/components/ui/panels/roof-panel.tsx +91 -22
- package/src/components/ui/panels/roof-segment-panel.tsx +23 -13
- package/src/components/ui/panels/slab-panel.tsx +63 -15
- package/src/components/ui/panels/stair-panel.tsx +377 -50
- package/src/components/ui/panels/stair-segment-panel.tsx +28 -17
- package/src/components/ui/panels/wall-panel.tsx +159 -11
- package/src/components/ui/panels/window-panel.tsx +5 -7
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +28 -17
- package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +65 -53
- package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +40 -25
- package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +69 -0
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +88 -72
- package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +14 -13
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +64 -53
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +32 -23
- package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +72 -51
- package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +40 -37
- package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +72 -51
- package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +13 -13
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +20 -17
- package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +62 -54
- package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +40 -25
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +27 -28
- package/src/components/ui/viewer-toolbar.tsx +55 -2
- package/src/components/viewer-overlay.tsx +26 -19
- package/src/hooks/use-auto-save.ts +3 -6
- package/src/hooks/use-contextual-tools.ts +25 -16
- package/src/hooks/use-grid-events.ts +13 -1
- package/src/hooks/use-keyboard.ts +7 -2
- package/src/index.tsx +2 -1
- package/src/lib/history.ts +20 -0
- package/src/lib/sfx-player.ts +96 -13
- package/src/store/use-editor.tsx +125 -10
package/src/store/use-editor.tsx
CHANGED
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import type { AssetInput } from '@pascal-app/core'
|
|
4
3
|
import {
|
|
4
|
+
type AnyNodeId,
|
|
5
|
+
type AssetInput,
|
|
5
6
|
type BuildingNode,
|
|
7
|
+
type CeilingNode,
|
|
6
8
|
type DoorNode,
|
|
9
|
+
type FenceNode,
|
|
7
10
|
type ItemNode,
|
|
8
11
|
type LevelNode,
|
|
12
|
+
type RoofSurfaceMaterialRole,
|
|
9
13
|
type RoofNode,
|
|
10
14
|
type RoofSegmentNode,
|
|
15
|
+
type SlabNode,
|
|
11
16
|
type Space,
|
|
17
|
+
type StairSurfaceMaterialRole,
|
|
12
18
|
type StairNode,
|
|
13
19
|
type StairSegmentNode,
|
|
14
20
|
useScene,
|
|
21
|
+
type WallNode,
|
|
22
|
+
type WallSurfaceSide,
|
|
15
23
|
type WindowNode,
|
|
16
24
|
} from '@pascal-app/core'
|
|
17
25
|
import { useViewer } from '@pascal-app/viewer'
|
|
18
26
|
import { create } from 'zustand'
|
|
19
27
|
import { persist } from 'zustand/middleware'
|
|
28
|
+
import { getDefaultCatalogItem } from '../components/ui/item-catalog/catalog-items'
|
|
20
29
|
|
|
21
30
|
const DEFAULT_ACTIVE_SIDEBAR_PANEL = 'site'
|
|
22
31
|
const DEFAULT_FLOORPLAN_PANE_RATIO = 0.5
|
|
@@ -33,6 +42,7 @@ export type Mode = 'select' | 'edit' | 'delete' | 'build'
|
|
|
33
42
|
// Structure mode tools (building elements)
|
|
34
43
|
export type StructureTool =
|
|
35
44
|
| 'wall'
|
|
45
|
+
| 'fence'
|
|
36
46
|
| 'room'
|
|
37
47
|
| 'custom-room'
|
|
38
48
|
| 'slab'
|
|
@@ -64,10 +74,28 @@ export type CatalogCategory =
|
|
|
64
74
|
export type StructureLayer = 'zones' | 'elements'
|
|
65
75
|
|
|
66
76
|
export type FloorplanSelectionTool = 'click' | 'marquee'
|
|
77
|
+
export type GridSnapStep = 0.5 | 0.25 | 0.1 | 0.05
|
|
67
78
|
|
|
68
79
|
// Combined tool type
|
|
69
80
|
export type Tool = SiteTool | StructureTool | FurnishTool
|
|
70
81
|
|
|
82
|
+
export type MovingWallEndpoint = {
|
|
83
|
+
wall: WallNode
|
|
84
|
+
endpoint: 'start' | 'end'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type MovingFenceEndpoint = {
|
|
88
|
+
fence: FenceNode
|
|
89
|
+
endpoint: 'start' | 'end'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type MaterialTargetRole = WallSurfaceSide | StairSurfaceMaterialRole | RoofSurfaceMaterialRole
|
|
93
|
+
|
|
94
|
+
export type SelectedMaterialTarget = {
|
|
95
|
+
nodeId: AnyNodeId
|
|
96
|
+
role: MaterialTargetRole
|
|
97
|
+
}
|
|
98
|
+
|
|
71
99
|
type EditorState = {
|
|
72
100
|
phase: Phase
|
|
73
101
|
setPhase: (phase: Phase) => void
|
|
@@ -85,22 +113,42 @@ type EditorState = {
|
|
|
85
113
|
| ItemNode
|
|
86
114
|
| WindowNode
|
|
87
115
|
| DoorNode
|
|
116
|
+
| FenceNode
|
|
117
|
+
| CeilingNode
|
|
118
|
+
| SlabNode
|
|
119
|
+
| WallNode
|
|
88
120
|
| RoofNode
|
|
89
121
|
| RoofSegmentNode
|
|
90
122
|
| StairNode
|
|
91
123
|
| StairSegmentNode
|
|
124
|
+
| BuildingNode
|
|
92
125
|
| null
|
|
93
126
|
setMovingNode: (
|
|
94
127
|
node:
|
|
95
128
|
| ItemNode
|
|
96
129
|
| WindowNode
|
|
97
130
|
| DoorNode
|
|
131
|
+
| FenceNode
|
|
132
|
+
| CeilingNode
|
|
133
|
+
| SlabNode
|
|
134
|
+
| WallNode
|
|
98
135
|
| RoofNode
|
|
99
136
|
| RoofSegmentNode
|
|
100
137
|
| StairNode
|
|
101
138
|
| StairSegmentNode
|
|
139
|
+
| BuildingNode
|
|
102
140
|
| null,
|
|
103
141
|
) => void
|
|
142
|
+
movingWallEndpoint: MovingWallEndpoint | null
|
|
143
|
+
setMovingWallEndpoint: (value: MovingWallEndpoint | null) => void
|
|
144
|
+
movingFenceEndpoint: MovingFenceEndpoint | null
|
|
145
|
+
setMovingFenceEndpoint: (value: MovingFenceEndpoint | null) => void
|
|
146
|
+
curvingWall: WallNode | null
|
|
147
|
+
setCurvingWall: (wall: WallNode | null) => void
|
|
148
|
+
curvingFence: FenceNode | null
|
|
149
|
+
setCurvingFence: (fence: FenceNode | null) => void
|
|
150
|
+
selectedMaterialTarget: SelectedMaterialTarget | null
|
|
151
|
+
setSelectedMaterialTarget: (target: SelectedMaterialTarget | null) => void
|
|
104
152
|
selectedReferenceId: string | null
|
|
105
153
|
setSelectedReferenceId: (id: string | null) => void
|
|
106
154
|
// Space detection for cutaway mode
|
|
@@ -125,6 +173,8 @@ type EditorState = {
|
|
|
125
173
|
setFloorplanHovered: (hovered: boolean) => void
|
|
126
174
|
floorplanSelectionTool: FloorplanSelectionTool
|
|
127
175
|
setFloorplanSelectionTool: (tool: FloorplanSelectionTool) => void
|
|
176
|
+
gridSnapStep: GridSnapStep
|
|
177
|
+
setGridSnapStep: (step: GridSnapStep) => void
|
|
128
178
|
// First-person walkthrough mode (street view)
|
|
129
179
|
isFirstPersonMode: boolean
|
|
130
180
|
_viewModeBeforeFirstPerson: ViewMode | null
|
|
@@ -145,7 +195,11 @@ export type PersistedEditorUiState = Pick<
|
|
|
145
195
|
|
|
146
196
|
type PersistedEditorLayoutState = Pick<
|
|
147
197
|
EditorState,
|
|
148
|
-
|
|
198
|
+
| 'activeSidebarPanel'
|
|
199
|
+
| 'floorplanPaneRatio'
|
|
200
|
+
| 'splitOrientation'
|
|
201
|
+
| 'floorplanSelectionTool'
|
|
202
|
+
| 'gridSnapStep'
|
|
149
203
|
>
|
|
150
204
|
type PersistedEditorState = PersistedEditorUiState & PersistedEditorLayoutState
|
|
151
205
|
|
|
@@ -164,8 +218,11 @@ export const DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE: PersistedEditorLayoutState =
|
|
|
164
218
|
floorplanPaneRatio: DEFAULT_FLOORPLAN_PANE_RATIO,
|
|
165
219
|
splitOrientation: 'horizontal',
|
|
166
220
|
floorplanSelectionTool: 'click',
|
|
221
|
+
gridSnapStep: 0.5,
|
|
167
222
|
}
|
|
168
223
|
|
|
224
|
+
const GRID_SNAP_STEPS: GridSnapStep[] = [0.5, 0.25, 0.1, 0.05]
|
|
225
|
+
|
|
169
226
|
function normalizeModeForPhase(phase: Phase, mode: Mode | undefined): Mode {
|
|
170
227
|
if (phase === 'site') {
|
|
171
228
|
return 'select'
|
|
@@ -268,6 +325,9 @@ function normalizePersistedEditorLayoutState(
|
|
|
268
325
|
floorplanPaneRatio: normalizeFloorplanPaneRatio(state?.floorplanPaneRatio),
|
|
269
326
|
splitOrientation: state?.splitOrientation === 'vertical' ? 'vertical' : 'horizontal',
|
|
270
327
|
floorplanSelectionTool: state?.floorplanSelectionTool === 'marquee' ? 'marquee' : 'click',
|
|
328
|
+
gridSnapStep: GRID_SNAP_STEPS.includes(state?.gridSnapStep as GridSnapStep)
|
|
329
|
+
? (state?.gridSnapStep as GridSnapStep)
|
|
330
|
+
: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.gridSnapStep,
|
|
271
331
|
}
|
|
272
332
|
}
|
|
273
333
|
|
|
@@ -327,6 +387,10 @@ export function selectDefaultBuildingAndLevel() {
|
|
|
327
387
|
}
|
|
328
388
|
}
|
|
329
389
|
|
|
390
|
+
function getDefaultSelectedItemForCategory(category: CatalogCategory | null): AssetInput | null {
|
|
391
|
+
return getDefaultCatalogItem(category)
|
|
392
|
+
}
|
|
393
|
+
|
|
330
394
|
const useEditor = create<EditorState>()(
|
|
331
395
|
persist(
|
|
332
396
|
(set, get) => ({
|
|
@@ -348,7 +412,11 @@ const useEditor = create<EditorState>()(
|
|
|
348
412
|
} else if (phase === 'structure') {
|
|
349
413
|
set({ tool: 'wall', catalogCategory: null })
|
|
350
414
|
} else if (phase === 'furnish') {
|
|
351
|
-
set({
|
|
415
|
+
set({
|
|
416
|
+
tool: 'item',
|
|
417
|
+
catalogCategory: 'furniture',
|
|
418
|
+
selectedItem: getDefaultSelectedItemForCategory('furniture'),
|
|
419
|
+
})
|
|
352
420
|
}
|
|
353
421
|
} else {
|
|
354
422
|
// Reset to select mode and clear tool/catalog when switching phases
|
|
@@ -388,8 +456,15 @@ const useEditor = create<EditorState>()(
|
|
|
388
456
|
} else if (phase === 'structure' && structureLayer === 'elements') {
|
|
389
457
|
set({ tool: 'wall' })
|
|
390
458
|
} else if (phase === 'furnish') {
|
|
391
|
-
set({
|
|
459
|
+
set({
|
|
460
|
+
tool: 'item',
|
|
461
|
+
catalogCategory: 'furniture',
|
|
462
|
+
selectedItem: getDefaultSelectedItemForCategory('furniture'),
|
|
463
|
+
})
|
|
392
464
|
}
|
|
465
|
+
} else if (phase === 'furnish' && tool === 'item' && !get().selectedItem) {
|
|
466
|
+
const category = get().catalogCategory ?? 'furniture'
|
|
467
|
+
set({ selectedItem: getDefaultSelectedItemForCategory(category) })
|
|
393
468
|
}
|
|
394
469
|
}
|
|
395
470
|
// When leaving build mode, clear tool
|
|
@@ -417,19 +492,44 @@ const useEditor = create<EditorState>()(
|
|
|
417
492
|
})
|
|
418
493
|
},
|
|
419
494
|
catalogCategory: DEFAULT_PERSISTED_EDITOR_UI_STATE.catalogCategory,
|
|
420
|
-
setCatalogCategory: (category) =>
|
|
495
|
+
setCatalogCategory: (category) =>
|
|
496
|
+
set((state) => ({
|
|
497
|
+
catalogCategory: category,
|
|
498
|
+
selectedItem:
|
|
499
|
+
category !== null &&
|
|
500
|
+
state.phase === 'furnish' &&
|
|
501
|
+
state.mode === 'build' &&
|
|
502
|
+
state.tool === 'item'
|
|
503
|
+
? getDefaultSelectedItemForCategory(category)
|
|
504
|
+
: state.selectedItem,
|
|
505
|
+
})),
|
|
421
506
|
selectedItem: null,
|
|
422
507
|
setSelectedItem: (item) => set({ selectedItem: item }),
|
|
423
508
|
movingNode: null as
|
|
424
509
|
| ItemNode
|
|
425
510
|
| WindowNode
|
|
426
511
|
| DoorNode
|
|
512
|
+
| FenceNode
|
|
513
|
+
| CeilingNode
|
|
514
|
+
| SlabNode
|
|
515
|
+
| WallNode
|
|
427
516
|
| RoofNode
|
|
428
517
|
| RoofSegmentNode
|
|
429
518
|
| StairNode
|
|
430
519
|
| StairSegmentNode
|
|
520
|
+
| BuildingNode
|
|
431
521
|
| null,
|
|
432
522
|
setMovingNode: (node) => set({ movingNode: node }),
|
|
523
|
+
movingWallEndpoint: null,
|
|
524
|
+
setMovingWallEndpoint: (value) => set({ movingWallEndpoint: value }),
|
|
525
|
+
movingFenceEndpoint: null,
|
|
526
|
+
setMovingFenceEndpoint: (value) => set({ movingFenceEndpoint: value }),
|
|
527
|
+
curvingWall: null,
|
|
528
|
+
setCurvingWall: (wall) => set({ curvingWall: wall }),
|
|
529
|
+
curvingFence: null,
|
|
530
|
+
setCurvingFence: (fence) => set({ curvingFence: fence }),
|
|
531
|
+
selectedMaterialTarget: null,
|
|
532
|
+
setSelectedMaterialTarget: (target) => set({ selectedMaterialTarget: target }),
|
|
433
533
|
selectedReferenceId: null,
|
|
434
534
|
setSelectedReferenceId: (id) => set({ selectedReferenceId: id }),
|
|
435
535
|
spaces: {},
|
|
@@ -461,6 +561,8 @@ const useEditor = create<EditorState>()(
|
|
|
461
561
|
setFloorplanHovered: (hovered) => set({ isFloorplanHovered: hovered }),
|
|
462
562
|
floorplanSelectionTool: 'click' as FloorplanSelectionTool,
|
|
463
563
|
setFloorplanSelectionTool: (tool) => set({ floorplanSelectionTool: tool }),
|
|
564
|
+
gridSnapStep: DEFAULT_PERSISTED_EDITOR_LAYOUT_STATE.gridSnapStep,
|
|
565
|
+
setGridSnapStep: (step) => set({ gridSnapStep: step }),
|
|
464
566
|
allowUndergroundCamera: false,
|
|
465
567
|
setAllowUndergroundCamera: (enabled) => set({ allowUndergroundCamera: enabled }),
|
|
466
568
|
isFirstPersonMode: false,
|
|
@@ -497,11 +599,23 @@ const useEditor = create<EditorState>()(
|
|
|
497
599
|
}),
|
|
498
600
|
{
|
|
499
601
|
name: 'pascal-editor-ui-preferences',
|
|
500
|
-
merge: (persistedState, currentState) =>
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
602
|
+
merge: (persistedState, currentState) => {
|
|
603
|
+
const mergedState = {
|
|
604
|
+
...currentState,
|
|
605
|
+
...normalizePersistedEditorUiState(persistedState as Partial<PersistedEditorState>),
|
|
606
|
+
...normalizePersistedEditorLayoutState(persistedState as Partial<PersistedEditorState>),
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return {
|
|
610
|
+
...mergedState,
|
|
611
|
+
selectedItem:
|
|
612
|
+
mergedState.phase === 'furnish' &&
|
|
613
|
+
mergedState.mode === 'build' &&
|
|
614
|
+
mergedState.tool === 'item'
|
|
615
|
+
? getDefaultSelectedItemForCategory(mergedState.catalogCategory ?? 'furniture')
|
|
616
|
+
: currentState.selectedItem,
|
|
617
|
+
}
|
|
618
|
+
},
|
|
505
619
|
partialize: (state) => ({
|
|
506
620
|
phase: state.phase,
|
|
507
621
|
mode: state.mode,
|
|
@@ -514,6 +628,7 @@ const useEditor = create<EditorState>()(
|
|
|
514
628
|
floorplanPaneRatio: state.floorplanPaneRatio,
|
|
515
629
|
splitOrientation: state.splitOrientation,
|
|
516
630
|
floorplanSelectionTool: state.floorplanSelectionTool,
|
|
631
|
+
gridSnapStep: state.gridSnapStep,
|
|
517
632
|
}),
|
|
518
633
|
},
|
|
519
634
|
),
|