@pascal-app/editor 0.6.0 → 0.7.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.
Files changed (122) hide show
  1. package/package.json +9 -5
  2. package/src/components/editor/bottom-sheet.tsx +149 -0
  3. package/src/components/editor/custom-camera-controls.tsx +75 -7
  4. package/src/components/editor/editor-layout-mobile.tsx +264 -0
  5. package/src/components/editor/editor-layout-v2.tsx +20 -0
  6. package/src/components/editor/first-person/build-collider-world.ts +365 -0
  7. package/src/components/editor/first-person/bvh-ecctrl.tsx +795 -0
  8. package/src/components/editor/first-person-controls.tsx +496 -143
  9. package/src/components/editor/floating-action-menu.tsx +32 -55
  10. package/src/components/editor/floorplan-background-selection.ts +113 -0
  11. package/src/components/editor/floorplan-panel.tsx +9855 -3298
  12. package/src/components/editor/index.tsx +269 -21
  13. package/src/components/editor/selection-manager.tsx +575 -13
  14. package/src/components/editor/thumbnail-generator.tsx +38 -7
  15. package/src/components/editor/use-floorplan-background-placement.ts +257 -0
  16. package/src/components/editor/use-floorplan-hit-testing.ts +171 -0
  17. package/src/components/editor/use-floorplan-scene-data.ts +189 -0
  18. package/src/components/editor/wall-measurement-label.tsx +267 -36
  19. package/src/components/editor-2d/floorplan-action-menu-layer.tsx +95 -0
  20. package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +160 -0
  21. package/src/components/editor-2d/floorplan-hotkey-handlers.tsx +92 -0
  22. package/src/components/editor-2d/renderers/floorplan-draft-layer.tsx +119 -0
  23. package/src/components/editor-2d/renderers/floorplan-marquee-layer.tsx +58 -0
  24. package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +197 -0
  25. package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +113 -0
  26. package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +474 -0
  27. package/src/components/editor-2d/svg-paths.ts +119 -0
  28. package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +1 -0
  29. package/src/components/tools/ceiling/ceiling-hole-editor.tsx +1 -0
  30. package/src/components/tools/ceiling/ceiling-tool.tsx +5 -5
  31. package/src/components/tools/column/column-tool.tsx +97 -0
  32. package/src/components/tools/column/move-column-tool.tsx +105 -0
  33. package/src/components/tools/door/door-tool.tsx +7 -0
  34. package/src/components/tools/door/move-door-tool.tsx +28 -8
  35. package/src/components/tools/fence/fence-drafting.ts +10 -3
  36. package/src/components/tools/fence/fence-tool.tsx +159 -3
  37. package/src/components/tools/fence/move-fence-endpoint-tool.tsx +129 -18
  38. package/src/components/tools/fence/move-fence-tool.tsx +101 -34
  39. package/src/components/tools/item/move-tool.tsx +10 -1
  40. package/src/components/tools/item/placement-math.ts +30 -1
  41. package/src/components/tools/item/placement-strategies.ts +109 -31
  42. package/src/components/tools/item/placement-types.ts +7 -0
  43. package/src/components/tools/item/use-draft-node.ts +2 -0
  44. package/src/components/tools/item/use-placement-coordinator.tsx +660 -52
  45. package/src/components/tools/roof/move-roof-tool.tsx +22 -15
  46. package/src/components/tools/shared/polygon-editor.tsx +153 -28
  47. package/src/components/tools/shared/segment-angle.ts +156 -0
  48. package/src/components/tools/slab/slab-boundary-editor.tsx +1 -0
  49. package/src/components/tools/slab/slab-hole-editor.tsx +1 -0
  50. package/src/components/tools/spawn/move-spawn-tool.tsx +101 -0
  51. package/src/components/tools/spawn/spawn-tool.tsx +130 -0
  52. package/src/components/tools/tool-manager.tsx +18 -3
  53. package/src/components/tools/wall/move-wall-endpoint-tool.tsx +121 -20
  54. package/src/components/tools/wall/wall-drafting.ts +18 -9
  55. package/src/components/tools/wall/wall-tool.tsx +134 -2
  56. package/src/components/tools/window/move-window-tool.tsx +18 -0
  57. package/src/components/tools/window/window-tool.tsx +5 -0
  58. package/src/components/ui/action-menu/camera-actions.tsx +37 -33
  59. package/src/components/ui/action-menu/control-modes.tsx +28 -1
  60. package/src/components/ui/action-menu/index.tsx +91 -1
  61. package/src/components/ui/action-menu/structure-tools.tsx +2 -0
  62. package/src/components/ui/action-menu/view-toggles.tsx +424 -35
  63. package/src/components/ui/command-palette/editor-commands.tsx +18 -1
  64. package/src/components/ui/controls/material-picker.tsx +152 -165
  65. package/src/components/ui/controls/slider-control.tsx +66 -18
  66. package/src/components/ui/floating-level-selector.tsx +286 -55
  67. package/src/components/ui/helpers/helper-manager.tsx +5 -0
  68. package/src/components/ui/item-catalog/catalog-items.tsx +1116 -1219
  69. package/src/components/ui/item-catalog/item-catalog.tsx +42 -175
  70. package/src/components/ui/level-duplicate-dialog.tsx +115 -0
  71. package/src/components/ui/panels/ceiling-panel.tsx +1 -25
  72. package/src/components/ui/panels/column-panel.tsx +715 -0
  73. package/src/components/ui/panels/door-panel.tsx +981 -289
  74. package/src/components/ui/panels/fence-panel.tsx +3 -45
  75. package/src/components/ui/panels/mobile-panel-sheet.tsx +108 -0
  76. package/src/components/ui/panels/mobile-selection-bar.tsx +100 -0
  77. package/src/components/ui/panels/node-display.ts +39 -0
  78. package/src/components/ui/panels/paint-panel.tsx +138 -0
  79. package/src/components/ui/panels/panel-manager.tsx +210 -1
  80. package/src/components/ui/panels/panel-wrapper.tsx +48 -39
  81. package/src/components/ui/panels/reference-panel.tsx +238 -5
  82. package/src/components/ui/panels/roof-panel.tsx +4 -105
  83. package/src/components/ui/panels/roof-segment-panel.tsx +0 -25
  84. package/src/components/ui/panels/slab-panel.tsx +4 -30
  85. package/src/components/ui/panels/spawn-panel.tsx +155 -0
  86. package/src/components/ui/panels/stair-panel.tsx +11 -117
  87. package/src/components/ui/panels/stair-segment-panel.tsx +0 -25
  88. package/src/components/ui/panels/wall-panel.tsx +1 -95
  89. package/src/components/ui/panels/window-panel.tsx +660 -139
  90. package/src/components/ui/sidebar/mobile-tab-bar.tsx +46 -0
  91. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +2 -2
  92. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +2 -2
  93. package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +77 -0
  94. package/src/components/ui/sidebar/panels/site-panel/index.tsx +109 -24
  95. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -2
  96. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +82 -0
  97. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +9 -3
  98. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +8 -5
  99. package/src/components/ui/sidebar/tab-bar.tsx +3 -0
  100. package/src/components/ui/viewer-toolbar.tsx +42 -1
  101. package/src/hooks/use-auto-frame.ts +45 -0
  102. package/src/hooks/use-keyboard.ts +64 -7
  103. package/src/hooks/use-mobile.ts +12 -12
  104. package/src/lib/door-interaction.ts +88 -0
  105. package/src/lib/floorplan/geometry.ts +263 -0
  106. package/src/lib/floorplan/index.ts +38 -0
  107. package/src/lib/floorplan/items.ts +179 -0
  108. package/src/lib/floorplan/selection-tool.ts +231 -0
  109. package/src/lib/floorplan/stairs.ts +478 -0
  110. package/src/lib/floorplan/types.ts +57 -0
  111. package/src/lib/floorplan/walls.ts +23 -0
  112. package/src/lib/guide-events.ts +10 -0
  113. package/src/lib/level-duplication.test.ts +72 -0
  114. package/src/lib/level-duplication.ts +153 -0
  115. package/src/lib/local-guide-image.ts +42 -0
  116. package/src/lib/material-paint.ts +284 -0
  117. package/src/lib/roof-duplication.ts +214 -0
  118. package/src/lib/scene-bounds.test.ts +183 -0
  119. package/src/lib/scene-bounds.ts +169 -0
  120. package/src/lib/stair-duplication.ts +126 -0
  121. package/src/lib/window-interaction.ts +86 -0
  122. package/src/store/use-editor.tsx +164 -8
@@ -3,7 +3,7 @@
3
3
  import type { AssetInput } from '@pascal-app/core'
4
4
  import { resolveCdnUrl } from '@pascal-app/viewer'
5
5
  import Image from 'next/image'
6
- import { useEffect, useState } from 'react'
6
+ import { useEffect } from 'react'
7
7
  import {
8
8
  Tooltip,
9
9
  TooltipContent,
@@ -13,54 +13,19 @@ import { cn } from './../../../lib/utils'
13
13
  import useEditor, { type CatalogCategory } from './../../../store/use-editor'
14
14
  import { CATALOG_ITEMS } from './catalog-items'
15
15
 
16
- const PLACEMENT_TAGS = new Set(['floor', 'wall', 'ceiling', 'countertop'])
17
-
18
16
  export function ItemCatalog({ category }: { category: CatalogCategory }) {
19
17
  const selectedItem = useEditor((state) => state.selectedItem)
20
18
  const setSelectedItem = useEditor((state) => state.setSelectedItem)
21
- const [activePlacementTag, setActivePlacementTag] = useState<string | null>(null)
22
- const [activeFunctionalTag, setActiveFunctionalTag] = useState<string | null>(null)
23
19
 
24
20
  const categoryItems = CATALOG_ITEMS.filter((item) => item.category === category)
25
21
 
26
- // Collect tags available in this category
27
- const allTags = Array.from(new Set(categoryItems.flatMap((item) => item.tags ?? [])))
28
- const placementTags = allTags.filter((t) => PLACEMENT_TAGS.has(t))
29
- const functionalTags = allTags.filter((t) => !PLACEMENT_TAGS.has(t))
30
- const hasFilters = allTags.length > 1
31
-
32
- // Count items for a placement tag given the current functional filter
33
- const placementCount = (tag: string | null) =>
34
- categoryItems.filter((item) => {
35
- const tags = item.tags ?? []
36
- if (tag !== null && !tags.includes(tag)) return false
37
- if (activeFunctionalTag && !tags.includes(activeFunctionalTag)) return false
38
- return true
39
- }).length
40
-
41
- // Count items for a functional tag given the current placement filter
42
- const functionalCount = (tag: string) =>
43
- categoryItems.filter((item) => {
44
- const tags = item.tags ?? []
45
- if (!tags.includes(tag)) return false
46
- if (activePlacementTag && !tags.includes(activePlacementTag)) return false
47
- return true
48
- }).length
49
-
50
- const filteredItems = categoryItems.filter((item) => {
51
- const tags = item.tags ?? []
52
- if (activePlacementTag && !tags.includes(activePlacementTag)) return false
53
- if (activeFunctionalTag && !tags.includes(activeFunctionalTag)) return false
54
- return true
55
- })
56
-
57
- // Auto-select first item if current selection is not in the filtered list
22
+ // Auto-select first item if current selection is not in this category
58
23
  useEffect(() => {
59
- const isCurrentItemInCategory = filteredItems.some((item) => item.src === selectedItem?.src)
60
- if (!isCurrentItemInCategory && filteredItems.length > 0) {
61
- setSelectedItem(filteredItems[0] as AssetInput)
24
+ const isCurrentItemInCategory = categoryItems.some((item) => item.src === selectedItem?.src)
25
+ if (!isCurrentItemInCategory && categoryItems.length > 0) {
26
+ setSelectedItem(categoryItems[0] as AssetInput)
62
27
  }
63
- }, [filteredItems, selectedItem?.src, setSelectedItem])
28
+ }, [categoryItems, selectedItem?.src, setSelectedItem])
64
29
 
65
30
  // Get attachment icon based on attachTo type
66
31
  const getAttachmentIcon = (attachTo: AssetInput['attachTo']) => {
@@ -74,146 +39,48 @@ export function ItemCatalog({ category }: { category: CatalogCategory }) {
74
39
  }
75
40
 
76
41
  return (
77
- <div className="flex flex-col gap-2">
78
- {/* Filter chips */}
79
- {hasFilters && (
80
- <div className="flex flex-col gap-1.5">
81
- {/* Placement row */}
82
- {placementTags.length > 0 && (
83
- <div className="flex flex-wrap gap-1">
42
+ <div className="-mx-2 -my-2 flex max-w-xl gap-2 overflow-x-auto p-2">
43
+ {categoryItems.map((item, index) => {
44
+ const isSelected = selectedItem?.src === item?.src
45
+ const attachmentIcon = getAttachmentIcon(item?.attachTo)
46
+ return (
47
+ <Tooltip key={index}>
48
+ <TooltipTrigger asChild>
84
49
  <button
85
50
  className={cn(
86
- 'cursor-pointer rounded-md px-2 py-0.5 font-medium text-xs transition-colors',
87
- activePlacementTag === null
88
- ? 'bg-blue-500 text-white'
89
- : 'bg-blue-950/50 text-blue-300 hover:bg-blue-900/60 hover:text-blue-200',
51
+ 'relative aspect-square h-14 min-h-14 w-14 min-w-14 shrink-0 flex-col gap-px rounded-lg transition-all duration-200 ease-out hover:scale-105 hover:cursor-pointer',
52
+ isSelected && 'ring-2 ring-primary-foreground',
90
53
  )}
91
- onClick={() => setActivePlacementTag(null)}
54
+ onClick={() => setSelectedItem(item)}
92
55
  type="button"
93
56
  >
94
- All
57
+ <Image
58
+ alt={item.name}
59
+ className="rounded-lg object-cover"
60
+ fill
61
+ loading="eager"
62
+ sizes="56px"
63
+ src={resolveCdnUrl(item.thumbnail) || ''}
64
+ />
65
+ {attachmentIcon && (
66
+ <div className="absolute right-0.5 bottom-0.5 flex h-4 w-4 items-center justify-center rounded bg-black/60">
67
+ <Image
68
+ alt={item.attachTo === 'ceiling' ? 'Ceiling attachment' : 'Wall attachment'}
69
+ className="h-4 w-4"
70
+ height={16}
71
+ src={attachmentIcon}
72
+ width={16}
73
+ />
74
+ </div>
75
+ )}
95
76
  </button>
96
- {placementTags.map((tag) => {
97
- const count = placementCount(tag)
98
- const isActive = activePlacementTag === tag
99
- const isEmpty = count === 0 && !isActive
100
- return (
101
- <button
102
- className={cn(
103
- 'inline-flex cursor-pointer items-center gap-1 rounded-md py-0.5 pr-1.5 pl-2 font-medium text-xs capitalize transition-colors',
104
- isActive
105
- ? 'bg-blue-500 text-white'
106
- : isEmpty
107
- ? 'cursor-not-allowed bg-zinc-800 text-zinc-500'
108
- : 'bg-blue-950/50 text-blue-300 hover:bg-blue-900/60 hover:text-blue-200',
109
- )}
110
- disabled={isEmpty}
111
- key={tag}
112
- onClick={() => setActivePlacementTag(isActive ? null : tag)}
113
- type="button"
114
- >
115
- {tag}
116
- <span
117
- className={cn(
118
- 'text-[10px]',
119
- isActive ? 'text-blue-200' : isEmpty ? 'text-zinc-600' : 'text-blue-500/70',
120
- )}
121
- >
122
- {count}
123
- </span>
124
- </button>
125
- )
126
- })}
127
- </div>
128
- )}
129
-
130
- {/* Functional row */}
131
- {functionalTags.length > 0 && (
132
- <div className="flex flex-wrap gap-1">
133
- {functionalTags.map((tag) => {
134
- const count = functionalCount(tag)
135
- const isActive = activeFunctionalTag === tag
136
- const isEmpty = count === 0 && !isActive
137
- return (
138
- <button
139
- className={cn(
140
- 'inline-flex cursor-pointer items-center gap-1 rounded-md py-0.5 pr-1.5 pl-2 font-medium text-xs capitalize transition-colors',
141
- isActive
142
- ? 'bg-violet-500 text-white'
143
- : isEmpty
144
- ? 'cursor-not-allowed bg-zinc-800 text-zinc-500'
145
- : 'bg-muted text-muted-foreground hover:bg-muted/80 hover:text-foreground',
146
- )}
147
- disabled={isEmpty}
148
- key={tag}
149
- onClick={() => setActiveFunctionalTag(isActive ? null : tag)}
150
- type="button"
151
- >
152
- {tag}
153
- <span
154
- className={cn(
155
- 'text-[10px]',
156
- isActive
157
- ? 'text-violet-200'
158
- : isEmpty
159
- ? 'text-zinc-600'
160
- : 'text-zinc-500/70',
161
- )}
162
- >
163
- {count}
164
- </span>
165
- </button>
166
- )
167
- })}
168
- </div>
169
- )}
170
- </div>
171
- )}
172
-
173
- {/* Items */}
174
- <div className="-mx-2 -my-2 flex max-w-xl gap-2 overflow-x-auto p-2">
175
- {filteredItems.map((item, index) => {
176
- const isSelected = selectedItem?.src === item?.src
177
- const attachmentIcon = getAttachmentIcon(item?.attachTo)
178
- return (
179
- <Tooltip key={index}>
180
- <TooltipTrigger asChild>
181
- <button
182
- className={cn(
183
- 'relative aspect-square h-14 min-h-14 w-14 min-w-14 shrink-0 flex-col gap-px rounded-lg transition-all duration-200 ease-out hover:scale-105 hover:cursor-pointer',
184
- isSelected && 'ring-2 ring-primary-foreground',
185
- )}
186
- onClick={() => setSelectedItem(item)}
187
- type="button"
188
- >
189
- <Image
190
- alt={item.name}
191
- className="rounded-lg object-cover"
192
- fill
193
- loading="eager"
194
- sizes="56px"
195
- src={resolveCdnUrl(item.thumbnail) || ''}
196
- />
197
- {attachmentIcon && (
198
- <div className="absolute right-0.5 bottom-0.5 flex h-4 w-4 items-center justify-center rounded bg-black/60">
199
- <Image
200
- alt={item.attachTo === 'ceiling' ? 'Ceiling attachment' : 'Wall attachment'}
201
- className="h-4 w-4"
202
- height={16}
203
- src={attachmentIcon}
204
- width={16}
205
- />
206
- </div>
207
- )}
208
- </button>
209
- </TooltipTrigger>
210
- <TooltipContent className="text-xs" side="top">
211
- {item.name}
212
- </TooltipContent>
213
- </Tooltip>
214
- )
215
- })}
216
- </div>
77
+ </TooltipTrigger>
78
+ <TooltipContent className="text-xs" side="top">
79
+ {item.name}
80
+ </TooltipContent>
81
+ </Tooltip>
82
+ )
83
+ })}
217
84
  </div>
218
85
  )
219
86
  }
@@ -0,0 +1,115 @@
1
+ 'use client'
2
+
3
+ import type { LevelNode } from '@pascal-app/core'
4
+ import { useEffect, useState } from 'react'
5
+ import { cn } from '../../lib/utils'
6
+ import type { LevelDuplicatePreset } from '../../lib/level-duplication'
7
+ import {
8
+ Dialog,
9
+ DialogContent,
10
+ DialogDescription,
11
+ DialogFooter,
12
+ DialogHeader,
13
+ DialogTitle,
14
+ } from './primitives/dialog'
15
+
16
+ const DUPLICATE_PRESETS: Array<{
17
+ id: LevelDuplicatePreset
18
+ label: string
19
+ description: string
20
+ }> = [
21
+ {
22
+ id: 'everything',
23
+ label: 'Everything',
24
+ description: 'Structure, materials, furniture, and references.',
25
+ },
26
+ {
27
+ id: 'structure',
28
+ label: 'Structure only',
29
+ description: 'Walls, slabs, roofs, stairs, windows, and doors without finishes.',
30
+ },
31
+ {
32
+ id: 'structure-materials',
33
+ label: 'Structure + materials',
34
+ description: 'Structure with the current material and finish assignments.',
35
+ },
36
+ {
37
+ id: 'structure-furniture',
38
+ label: 'Structure + furniture',
39
+ description: 'Structure, finishes, and placed items, without guide references.',
40
+ },
41
+ ]
42
+
43
+ function getLevelLabel(level: LevelNode | null) {
44
+ if (!level) return 'this level'
45
+ return level.name || `Level ${level.level}`
46
+ }
47
+
48
+ export function LevelDuplicateDialog({
49
+ open,
50
+ level,
51
+ onConfirm,
52
+ onOpenChange,
53
+ }: {
54
+ open: boolean
55
+ level: LevelNode | null
56
+ onConfirm: (preset: LevelDuplicatePreset) => void
57
+ onOpenChange: (open: boolean) => void
58
+ }) {
59
+ const [preset, setPreset] = useState<LevelDuplicatePreset>('everything')
60
+
61
+ useEffect(() => {
62
+ if (open) {
63
+ setPreset('everything')
64
+ }
65
+ }, [open])
66
+
67
+ return (
68
+ <Dialog onOpenChange={onOpenChange} open={open}>
69
+ <DialogContent className="sm:max-w-md" showCloseButton={false}>
70
+ <DialogHeader>
71
+ <DialogTitle>Duplicate Level</DialogTitle>
72
+ <DialogDescription>
73
+ Choose what to copy from {getLevelLabel(level)}.
74
+ </DialogDescription>
75
+ </DialogHeader>
76
+
77
+ <div className="grid gap-2">
78
+ {DUPLICATE_PRESETS.map((option) => (
79
+ <button
80
+ className={cn(
81
+ 'cursor-pointer rounded-xl border px-3 py-3 text-left transition-colors',
82
+ preset === option.id
83
+ ? 'border-primary bg-primary/10 text-foreground'
84
+ : 'border-border bg-background hover:bg-accent/40',
85
+ )}
86
+ key={option.id}
87
+ onClick={() => setPreset(option.id)}
88
+ type="button"
89
+ >
90
+ <div className="font-medium text-sm">{option.label}</div>
91
+ <div className="mt-1 text-muted-foreground text-xs">{option.description}</div>
92
+ </button>
93
+ ))}
94
+ </div>
95
+
96
+ <DialogFooter>
97
+ <button
98
+ className="cursor-pointer rounded-md px-4 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent"
99
+ onClick={() => onOpenChange(false)}
100
+ type="button"
101
+ >
102
+ Cancel
103
+ </button>
104
+ <button
105
+ className="cursor-pointer rounded-md bg-primary px-4 py-2 text-primary-foreground text-sm transition-opacity hover:opacity-90"
106
+ onClick={() => onConfirm(preset)}
107
+ type="button"
108
+ >
109
+ Duplicate
110
+ </button>
111
+ </DialogFooter>
112
+ </DialogContent>
113
+ </Dialog>
114
+ )
115
+ }
@@ -1,13 +1,12 @@
1
1
  'use client'
2
2
 
3
- import { type AnyNode, type CeilingNode, type MaterialSchema, useScene } from '@pascal-app/core'
3
+ import { type AnyNode, type CeilingNode, useScene } from '@pascal-app/core'
4
4
  import { useViewer } from '@pascal-app/viewer'
5
5
  import { Edit, Move, Plus, Trash2 } from 'lucide-react'
6
6
  import { useCallback, useEffect } from 'react'
7
7
  import { sfxEmitter } from '../../../lib/sfx-bus'
8
8
  import useEditor from '../../../store/use-editor'
9
9
  import { ActionButton, ActionGroup } from '../controls/action-button'
10
- import { MaterialPicker } from '../controls/material-picker'
11
10
  import { PanelSection } from '../controls/panel-section'
12
11
  import { SliderControl } from '../controls/slider-control'
13
12
  import { PanelWrapper } from './panel-wrapper'
@@ -32,20 +31,6 @@ export function CeilingPanel() {
32
31
  [selectedId, updateNode],
33
32
  )
34
33
 
35
- const handleMaterialChange = useCallback(
36
- (material: MaterialSchema) => {
37
- handleUpdate({ material, materialPreset: undefined })
38
- },
39
- [handleUpdate],
40
- )
41
-
42
- const handleMaterialPresetChange = useCallback(
43
- (materialPreset: string) => {
44
- handleUpdate({ materialPreset, material: undefined })
45
- },
46
- [handleUpdate],
47
- )
48
-
49
34
  const handleClose = useCallback(() => {
50
35
  setSelection({ selectedIds: [] })
51
36
  setEditingHole(null)
@@ -257,15 +242,6 @@ export function CeilingPanel() {
257
242
  </div>
258
243
  </PanelSection>
259
244
 
260
- <PanelSection title="Material">
261
- <MaterialPicker
262
- nodeType="ceiling"
263
- onChange={handleMaterialChange}
264
- onSelectMaterialPreset={handleMaterialPresetChange}
265
- selectedMaterialPreset={node.materialPreset}
266
- value={node.material}
267
- />
268
- </PanelSection>
269
245
  <ActionGroup>
270
246
  <ActionButton icon={<Move className="h-3.5 w-3.5" />} label="Move" onClick={handleMove} />
271
247
  </ActionGroup>