@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.
Files changed (624) hide show
  1. package/README.md +106 -0
  2. package/package.json +42 -38
  3. package/src/components/editor/__bench__/room-label-placement.bench.ts +108 -0
  4. package/src/components/editor/action-menu-button.tsx +83 -0
  5. package/src/components/editor/bake-thumbnail.tsx +307 -5
  6. package/src/components/editor/capture-level-picker.tsx +72 -0
  7. package/src/components/editor/construction-reveal-audio.test.tsx +150 -0
  8. package/src/components/editor/construction-reveal-audio.tsx +40 -0
  9. package/src/components/editor/custom-camera-controls.tsx +275 -57
  10. package/src/components/editor/delete-confirmation-dialog.tsx +297 -7
  11. package/src/components/editor/duplicate-node.ts +244 -0
  12. package/src/components/editor/editor-layout-mobile.tsx +37 -26
  13. package/src/components/editor/editor-layout-v2.tsx +132 -59
  14. package/src/components/editor/export-manager.tsx +47 -26
  15. package/src/components/editor/first-person/build-collider-world.test.ts +320 -0
  16. package/src/components/editor/first-person/build-collider-world.ts +28 -7
  17. package/src/components/editor/first-person/ground-holes.test.ts +128 -0
  18. package/src/components/editor/first-person/ground-holes.ts +130 -0
  19. package/src/components/editor/first-person/mechanism-targets.test.ts +162 -0
  20. package/src/components/editor/first-person/mechanism-targets.ts +88 -0
  21. package/src/components/editor/first-person-controls.tsx +220 -9
  22. package/src/components/editor/floating-action-menu.tsx +63 -207
  23. package/src/components/editor/floor-aware-camera-controls.test.ts +466 -0
  24. package/src/components/editor/floor-aware-camera-controls.ts +324 -0
  25. package/src/components/editor/floor-openings-overlay.tsx +243 -0
  26. package/src/components/editor/floor-region-controls.tsx +846 -0
  27. package/src/components/editor/floor-region-webgpu-materials.test.ts +12 -0
  28. package/src/components/editor/floor-surface.test.ts +165 -0
  29. package/src/components/editor/floor-surface.ts +182 -0
  30. package/src/components/editor/floorplan-background-selection.test.ts +11 -2
  31. package/src/components/editor/floorplan-background-selection.ts +1 -3
  32. package/src/components/editor/floorplan-mode-coordinator.tsx +2 -2
  33. package/src/components/editor/floorplan-panel.tsx +414 -363
  34. package/src/components/editor/follow-pascal.test.tsx +912 -0
  35. package/src/components/editor/follow-pascal.tsx +354 -0
  36. package/src/components/editor/footprint-height-handle.tsx +204 -0
  37. package/src/components/editor/group-actions.ts +56 -2
  38. package/src/components/editor/group-move-3d.ts +2 -0
  39. package/src/components/editor/group-rotate-handle.tsx +2 -0
  40. package/src/components/editor/group-transform-shared.test.ts +460 -0
  41. package/src/components/editor/handles/handle-hit-priority.test.tsx +63 -0
  42. package/src/components/editor/handles/handle-hit-priority.tsx +37 -0
  43. package/src/components/editor/handles/swallow-next-click.test.ts +28 -0
  44. package/src/components/editor/handles/use-handle-drag.ts +269 -197
  45. package/src/components/editor/index.tsx +409 -197
  46. package/src/components/editor/join-walls-pill.tsx +44 -0
  47. package/src/components/editor/node-action-menu.tsx +68 -70
  48. package/src/components/editor/node-arrow-handles.tsx +25 -6
  49. package/src/components/editor/open-wall-ends-3d-layer.tsx +273 -0
  50. package/src/components/editor/open-wall-ends-hint.tsx +89 -0
  51. package/src/components/editor/quick-measurement-hud.tsx +3 -2
  52. package/src/components/editor/registry-action-contributions.test.tsx +63 -0
  53. package/src/components/editor/registry-action-contributions.tsx +80 -1
  54. package/src/components/editor/riser-diagram-panel.tsx +14 -7
  55. package/src/components/editor/room-controls.tsx +847 -0
  56. package/src/components/editor/room-floor-highlight.tsx +128 -0
  57. package/src/components/editor/room-handles-2d.tsx +354 -0
  58. package/src/components/editor/room-handles.tsx +675 -0
  59. package/src/components/editor/room-highlight.tsx +233 -0
  60. package/src/components/editor/room-labels.test.ts +850 -0
  61. package/src/components/editor/room-labels.tsx +1890 -0
  62. package/src/components/editor/room-transform-ghost.tsx +256 -0
  63. package/src/components/editor/room-webgpu-materials.test.ts +27 -0
  64. package/src/components/editor/selection-manager.tsx +474 -419
  65. package/src/components/editor/slab-hole-highlights.tsx +5 -2
  66. package/src/components/editor/snapshot-capture-overlay.tsx +7 -2
  67. package/src/components/editor/snapshot-capture.test.ts +119 -60
  68. package/src/components/editor/snapshot-capture.ts +36 -5
  69. package/src/components/editor/thumbnail-generator.test.ts +654 -0
  70. package/src/components/editor/thumbnail-generator.tsx +1103 -223
  71. package/src/components/editor/use-floorplan-background-placement.ts +34 -12
  72. package/src/components/editor/vector-edge-extractor.tsx +121 -0
  73. package/src/components/editor/views/host-view.tsx +61 -0
  74. package/src/components/editor/views/plugins-view.tsx +663 -0
  75. package/src/components/editor/views/scene-region.tsx +59 -0
  76. package/src/components/editor/views/use-editor-views.ts +57 -0
  77. package/src/components/editor/views/view-bar.test.tsx +54 -0
  78. package/src/components/editor/views/view-bar.tsx +343 -0
  79. package/src/components/editor/wall-justification.test.ts +140 -0
  80. package/src/components/editor/wall-move-side-handles.tsx +43 -142
  81. package/src/components/editor/wall-push-arrow.tsx +92 -0
  82. package/src/components/editor/wall-region-handles.tsx +509 -0
  83. package/src/components/editor/wall-snap-beacon-layer.tsx +8 -4
  84. package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
  85. package/src/components/editor-2d/floorplan-group-move.tsx +3 -0
  86. package/src/components/editor-2d/floorplan-open-wall-ends-layer.test.tsx +86 -0
  87. package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +290 -0
  88. package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +2 -2
  89. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
  90. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +116 -106
  91. package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.test.ts +156 -0
  92. package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +278 -0
  93. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +77 -0
  94. package/src/components/editor-2d/renderers/{floorplan-draft-wall-measurement.tsx → floorplan-draft-measurement.tsx} +8 -8
  95. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
  96. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +38 -8
  97. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +319 -233
  98. package/src/components/editor-2d/renderers/floorplan-reveal.test.tsx +254 -0
  99. package/src/components/editor-2d/renderers/floorplan-reveal.ts +141 -0
  100. package/src/components/editor-2d/svg-paths.ts +12 -15
  101. package/src/components/point-ask/bubble.tsx +538 -0
  102. package/src/components/point-ask/hover-chip.tsx +91 -0
  103. package/src/components/point-ask/mode-pill.tsx +92 -0
  104. package/src/components/point-ask/optional-motion.ts +22 -0
  105. package/src/components/point-ask/outline-corners.tsx +93 -0
  106. package/src/components/point-ask/pins.tsx +711 -0
  107. package/src/components/point-ask/point-ask-layer.tsx +83 -0
  108. package/src/components/point-ask/point-ask-styles.ts +105 -0
  109. package/src/components/point-ask/region-marquee-styles.ts +15 -0
  110. package/src/components/point-ask/region-marquee.tsx +64 -0
  111. package/src/components/procedural-items/inspector.tsx +40 -15
  112. package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +12 -2
  113. package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +97 -14
  114. package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +18 -18
  115. package/src/components/systems/roof/roof-edit-system.tsx +1 -0
  116. package/src/components/systems/zone/zone-system.test.ts +47 -0
  117. package/src/components/systems/zone/zone-system.tsx +29 -68
  118. package/src/components/tools/distribution/connection-compatibility.test.ts +34 -0
  119. package/src/components/tools/distribution/connection-compatibility.ts +43 -0
  120. package/src/components/tools/distribution/distribution-public.ts +9 -0
  121. package/src/components/tools/distribution/distribution-run-contract.test.ts +26 -0
  122. package/src/components/tools/distribution/distribution-run-contract.ts +94 -0
  123. package/src/components/tools/distribution/distribution-run-tool.test.ts +241 -0
  124. package/src/components/tools/distribution/distribution-run-tool.test.tsx +125 -0
  125. package/src/components/tools/distribution/distribution-run-tool.tsx +1323 -0
  126. package/src/components/tools/distribution/draw-alignment.ts +35 -0
  127. package/src/components/tools/distribution/ports.test.ts +56 -0
  128. package/src/components/tools/distribution/ports.ts +378 -0
  129. package/src/components/tools/distribution/run-cursor.test.ts +135 -0
  130. package/src/components/tools/distribution/run-cursor.ts +36 -0
  131. package/src/components/tools/distribution/run-direction-feedback.test.ts +60 -0
  132. package/src/components/tools/distribution/run-direction-feedback.tsx +251 -0
  133. package/src/components/tools/distribution/run-port-snap.test.ts +79 -0
  134. package/src/components/tools/distribution/run-port-snap.ts +25 -0
  135. package/src/components/tools/distribution/run-screen-direction.test.ts +23 -0
  136. package/src/components/tools/distribution/run-screen-direction.ts +30 -0
  137. package/src/components/tools/elevator/elevator-tool.tsx +3 -6
  138. package/src/components/tools/elevator/move-elevator-tool.tsx +2 -3
  139. package/src/components/tools/item/item-surface-strategy-resolver.test.ts +67 -68
  140. package/src/components/tools/item/placement-history.test.tsx +512 -0
  141. package/src/components/tools/item/placement-strategies.test.ts +46 -1
  142. package/src/components/tools/item/placement-strategies.ts +11 -10
  143. package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +56 -56
  144. package/src/components/tools/item/use-draft-node.test.tsx +432 -1
  145. package/src/components/tools/item/use-draft-node.ts +166 -95
  146. package/src/components/tools/item/use-placement-coordinator.tsx +148 -95
  147. package/src/components/tools/paint-region/use-paint-tint.ts +37 -0
  148. package/src/components/tools/paint-region/wall-paint-region-tool.tsx +466 -0
  149. package/src/components/tools/registry/item-surface-move.test.ts +62 -63
  150. package/src/components/tools/registry/item-surface-move.ts +2 -0
  151. package/src/components/tools/registry/item-surface-resolver.test.ts +26 -26
  152. package/src/components/tools/registry/move-registry-node-tool.test.ts +268 -1
  153. package/src/components/tools/registry/move-registry-node-tool.tsx +66 -30
  154. package/src/components/tools/select/box-select-tool.tsx +2 -12
  155. package/src/components/tools/select/plane-box-select-tool.test.tsx +79 -0
  156. package/src/components/tools/select/plane-box-select-tool.tsx +35 -22
  157. package/src/components/tools/select/select-candidates.test.ts +37 -4
  158. package/src/components/tools/select/select-candidates.ts +15 -11
  159. package/src/components/tools/shared/cursor-sphere.tsx +36 -12
  160. package/src/components/tools/site/site-boundary-editor.tsx +1 -2
  161. package/src/components/tools/tool-manager.tsx +59 -24
  162. package/src/components/tools/wall/wall-drafting.test.ts +175 -47
  163. package/src/components/tools/zone/zone-tool.tsx +4 -12
  164. package/src/components/ui/action-menu/action-button.tsx +9 -1
  165. package/src/components/ui/action-menu/camera-actions.tsx +9 -4
  166. package/src/components/ui/action-menu/control-modes.tsx +15 -42
  167. package/src/components/ui/action-menu/follow-pascal-pill.tsx +32 -0
  168. package/src/components/ui/action-menu/follow-pascal-toggle.tsx +36 -0
  169. package/src/components/ui/action-menu/index.tsx +56 -33
  170. package/src/components/ui/action-menu/measurement-control.tsx +14 -19
  171. package/src/components/ui/action-menu/placement.ts +20 -0
  172. package/src/components/ui/action-menu/point-ask-button.tsx +52 -0
  173. package/src/components/ui/action-menu/view-toggles.tsx +47 -42
  174. package/src/components/ui/build-panel/build-panel.tsx +242 -0
  175. package/src/components/ui/command-palette/editor-commands.tsx +7 -42
  176. package/src/components/ui/command-palette/index.tsx +50 -39
  177. package/src/components/ui/command-palette/ranked-groups.test.ts +31 -0
  178. package/src/components/ui/controls/action-button.tsx +5 -2
  179. package/src/components/ui/controls/finish-swatch.tsx +110 -0
  180. package/src/components/ui/controls/material-paint-panel.tsx +97 -26
  181. package/src/components/ui/controls/material-picker.tsx +4 -1
  182. package/src/components/ui/controls/metric-control.tsx +1 -1
  183. package/src/components/ui/controls/scene-material-list.tsx +4 -32
  184. package/src/components/ui/controls/segmented-control.tsx +21 -8
  185. package/src/components/ui/controls/select-control.tsx +65 -0
  186. package/src/components/ui/controls/slider-control.tsx +1 -1
  187. package/src/components/ui/controls/toggle-control.tsx +3 -3
  188. package/src/components/ui/controls/tool-options-panel.tsx +1 -1
  189. package/src/components/ui/floating-level-selector.tsx +159 -59
  190. package/src/components/ui/helpers/building-helper.tsx +4 -1
  191. package/src/components/ui/helpers/contextual-helper-panel.tsx +181 -72
  192. package/src/components/ui/helpers/helper-manager.test.tsx +121 -0
  193. package/src/components/ui/helpers/helper-manager.tsx +331 -14
  194. package/src/components/ui/helpers/item-helper.tsx +17 -1
  195. package/src/components/ui/helpers/registered-tool-helper.tsx +8 -1
  196. package/src/components/ui/icon-ref.tsx +45 -1
  197. package/src/components/ui/level-duplicate-dialog.tsx +1 -1
  198. package/src/components/ui/node-icon-image.tsx +16 -0
  199. package/src/components/ui/panels/collections/collections-popover.tsx +28 -22
  200. package/src/components/ui/panels/floor-foundation-panel.tsx +551 -0
  201. package/src/components/ui/panels/floor-opening-panel.tsx +143 -0
  202. package/src/components/ui/panels/mobile-panel-sheet.tsx +4 -4
  203. package/src/components/ui/panels/mobile-selection-bar.tsx +8 -5
  204. package/src/components/ui/panels/multi-field-value.test.ts +26 -3
  205. package/src/components/ui/panels/multi-field-value.ts +6 -3
  206. package/src/components/ui/panels/multi-parametric-inspector.tsx +52 -2
  207. package/src/components/ui/panels/multi-selection-panel.tsx +161 -5
  208. package/src/components/ui/panels/node-display.ts +8 -3
  209. package/src/components/ui/panels/panel-manager.tsx +103 -68
  210. package/src/components/ui/panels/panel-wrapper.tsx +130 -62
  211. package/src/components/ui/panels/parametric-field-control.tsx +3 -3
  212. package/src/components/ui/panels/parametric-inspector.tsx +7 -7
  213. package/src/components/ui/panels/reference-panel.tsx +2 -3
  214. package/src/components/ui/panels/room-construction-rows.tsx +774 -0
  215. package/src/components/ui/panels/room-name-options.test.ts +55 -0
  216. package/src/components/ui/panels/room-name-options.ts +47 -0
  217. package/src/components/ui/panels/room-openings-field.tsx +110 -0
  218. package/src/components/ui/panels/room-panel.tsx +790 -0
  219. package/src/components/ui/panels/shape-choice.tsx +58 -0
  220. package/src/components/ui/panels/wall-region-list.tsx +109 -0
  221. package/src/components/ui/primitives/badge.tsx +8 -0
  222. package/src/components/ui/primitives/color-dot.tsx +36 -24
  223. package/src/components/ui/primitives/sidebar.tsx +3 -1
  224. package/src/components/ui/right-stack.test.ts +29 -0
  225. package/src/components/ui/right-stack.tsx +164 -0
  226. package/src/components/ui/sidebar/app-sidebar.tsx +1 -1
  227. package/src/components/ui/sidebar/icon-rail.tsx +8 -3
  228. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +1 -1
  229. package/src/components/ui/sidebar/panels/items-panel/index.tsx +6 -6
  230. package/src/components/ui/sidebar/panels/settings-panel/animation-speed-setting.tsx +40 -0
  231. package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +7 -8
  232. package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.test.tsx +170 -0
  233. package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.tsx +92 -0
  234. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +138 -27
  235. package/src/components/ui/sidebar/panels/settings-panel/interface-theme.test.tsx +20 -0
  236. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +5 -5
  237. package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +4 -10
  238. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -11
  239. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -4
  240. package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +1 -5
  241. package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +197 -0
  242. package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -5
  243. package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +1 -5
  244. package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +1 -5
  245. package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +1 -5
  246. package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +24 -11
  247. package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +1 -5
  248. package/src/components/ui/sidebar/panels/site-panel/index.tsx +181 -223
  249. package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +30 -8
  250. package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +1 -3
  251. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +4 -1
  252. package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +1 -3
  253. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +1 -4
  254. package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +1 -3
  255. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +21 -13
  256. package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +1 -5
  257. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -5
  258. package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +1 -5
  259. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +13 -37
  260. package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -3
  261. package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -5
  262. package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +1 -5
  263. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +9 -4
  264. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +2 -4
  265. package/src/components/ui/sidebar/tab-bar.tsx +14 -4
  266. package/src/components/ui/sidebar/use-plugin-panels.tsx +15 -5
  267. package/src/components/viewer/floorplan-compass-button.tsx +9 -1
  268. package/src/components/viewer/floorplan-preview.tsx +5 -6
  269. package/src/components/viewer/viewer-controls-bar.tsx +18 -6
  270. package/src/components/viewer/viewer-hover-label.tsx +34 -0
  271. package/src/components/viewer/viewer-rooms.tsx +191 -0
  272. package/src/components/viewer/viewer-scene-header.tsx +181 -145
  273. package/src/components/viewer/viewer-selection-manager.tsx +124 -0
  274. package/src/components/viewer/viewer-stage.tsx +2 -2
  275. package/src/components/viewer-zone-system.tsx +3 -8
  276. package/src/hooks/use-auto-save.integration.test.tsx +215 -43
  277. package/src/hooks/use-auto-save.ts +60 -9
  278. package/src/hooks/use-ceiling-events.test.ts +123 -118
  279. package/src/hooks/use-grid-events.test.ts +105 -105
  280. package/src/hooks/use-keyboard.test.ts +243 -19
  281. package/src/hooks/use-keyboard.ts +118 -83
  282. package/src/hooks/use-open-wall-ends.test.ts +99 -0
  283. package/src/hooks/use-open-wall-ends.ts +238 -0
  284. package/src/hooks/use-placement-notice.test.ts +71 -0
  285. package/src/hooks/use-placement-notice.ts +73 -0
  286. package/src/hooks/use-selected-room.ts +143 -0
  287. package/src/index.tsx +312 -58
  288. package/src/lib/__fixtures__/owner-victor-seattle-parking.json +1 -0
  289. package/src/lib/__fixtures__/owner-villa-aurelia-basement.json +1 -0
  290. package/src/lib/add-storey.test.ts +177 -0
  291. package/src/lib/add-storey.ts +59 -0
  292. package/src/lib/below-grade.test.tsx +171 -0
  293. package/src/lib/below-grade.ts +133 -0
  294. package/src/lib/camera-facing.test.ts +24 -0
  295. package/src/lib/camera-facing.ts +24 -0
  296. package/src/lib/ceiling-edit-session.test.ts +321 -0
  297. package/src/lib/ceiling-edit-session.ts +206 -0
  298. package/src/lib/ceiling-region-session.test.ts +179 -0
  299. package/src/lib/continuation.test.ts +19 -1
  300. package/src/lib/continuation.ts +25 -4
  301. package/src/lib/direct-manipulation.ts +2 -0
  302. package/src/lib/door-interaction.ts +14 -0
  303. package/src/lib/download-project-file.ts +17 -0
  304. package/src/lib/editor-level-display.test.ts +96 -0
  305. package/src/lib/editor-level-display.ts +55 -0
  306. package/src/lib/editor-views.ts +178 -0
  307. package/src/lib/export-geometry-wait.test.ts +90 -0
  308. package/src/lib/export-texture-utils.ts +2 -4
  309. package/src/lib/floor-edit-session.ts +153 -0
  310. package/src/lib/floor-footprints.test.ts +563 -0
  311. package/src/lib/floor-footprints.ts +395 -0
  312. package/src/lib/floor-foundation-support.test.ts +284 -0
  313. package/src/lib/floor-opening-draft.ts +218 -0
  314. package/src/lib/floor-openings.test.ts +216 -0
  315. package/src/lib/floor-region-geometry.ts +191 -0
  316. package/src/lib/floor-region-session.test.ts +485 -0
  317. package/src/lib/floor-region-session.ts +589 -0
  318. package/src/lib/floor-region-snap.ts +101 -0
  319. package/src/lib/floor-step-hover.test.ts +32 -0
  320. package/src/lib/floorplan/annotation-visibility.ts +17 -0
  321. package/src/lib/floorplan/dimension-drive.test.ts +360 -0
  322. package/src/lib/floorplan/dimension-drive.ts +479 -0
  323. package/src/lib/floorplan/floorplan-export.test.ts +12 -8
  324. package/src/lib/floorplan/floorplan-export.tsx +32 -8
  325. package/src/lib/floorplan/floorplan-extension.ts +32 -0
  326. package/src/lib/floorplan/floorplan-mode.test.ts +4 -0
  327. package/src/lib/floorplan/floorplan-mode.ts +2 -0
  328. package/src/lib/floorplan/floorplan-pdfkit-document.ts +363 -27
  329. package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +177 -8
  330. package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +60 -25
  331. package/src/lib/floorplan/floorplan-plugin-install.test.tsx +138 -0
  332. package/src/lib/floorplan/floorplan-readonly.ts +6 -0
  333. package/src/lib/floorplan/geometry.ts +13 -6
  334. package/src/lib/floorplan/index.ts +0 -9
  335. package/src/lib/floorplan/items.ts +5 -2
  336. package/src/lib/floorplan/marks.test.ts +213 -0
  337. package/src/lib/floorplan/marks.ts +238 -0
  338. package/src/lib/floorplan/open-wall-ends.test.ts +268 -0
  339. package/src/lib/floorplan/open-wall-ends.ts +162 -0
  340. package/src/lib/floorplan/pdf-fonts/GeistMono-Bold.ttf +0 -0
  341. package/src/lib/floorplan/pdf-fonts/GeistMono-Regular.ttf +0 -0
  342. package/src/lib/floorplan/pdf-fonts/LICENSE-GeistMono.txt +92 -0
  343. package/src/lib/floorplan/pdf-fonts/LICENSE-LiberationSans.txt +102 -0
  344. package/src/lib/floorplan/pdf-fonts/LiberationSans-Bold.ttf +0 -0
  345. package/src/lib/floorplan/pdf-fonts/LiberationSans-Regular.ttf +0 -0
  346. package/src/lib/floorplan/pdfkit-standard-fonts.d.ts +5 -0
  347. package/src/lib/floorplan/schedules.test.ts +189 -0
  348. package/src/lib/floorplan/schedules.ts +339 -0
  349. package/src/lib/floorplan/sheet-export.test.ts +124 -0
  350. package/src/lib/floorplan/sheet-export.ts +318 -0
  351. package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +768 -0
  352. package/src/lib/floorplan/site-plan/contours.test.ts +43 -0
  353. package/src/lib/floorplan/site-plan/contributors.ts +97 -0
  354. package/src/lib/floorplan/site-plan/coverage.ts +230 -0
  355. package/src/lib/floorplan/site-plan/drawing-type-switch.test.ts +15 -0
  356. package/src/lib/floorplan/site-plan/drawing-type-switch.tsx +86 -0
  357. package/src/lib/floorplan/site-plan/front-edge.test.ts +103 -0
  358. package/src/lib/floorplan/site-plan/front-edge.ts +157 -0
  359. package/src/lib/floorplan/site-plan/geometry.test.ts +269 -0
  360. package/src/lib/floorplan/site-plan/geometry.ts +142 -0
  361. package/src/lib/floorplan/site-plan/index.ts +55 -0
  362. package/src/lib/floorplan/site-plan/site-annotations.test.ts +483 -0
  363. package/src/lib/floorplan/site-plan/site-annotations.ts +1084 -0
  364. package/src/lib/floorplan/site-plan/site-parts.ts +287 -0
  365. package/src/lib/floorplan/site-plan/site-plan-layer.tsx +138 -0
  366. package/src/lib/floorplan/site-plan/street-name.test.ts +69 -0
  367. package/src/lib/floorplan/site-plan/street-name.ts +302 -0
  368. package/src/lib/floorplan/site-plan/yards.test.ts +70 -0
  369. package/src/lib/floorplan/types.ts +1 -27
  370. package/src/lib/follow-pascal-exterior.test-fixture.ts +17 -0
  371. package/src/lib/follow-pascal-exterior.test.ts +171 -0
  372. package/src/lib/follow-pascal-exterior.ts +39 -0
  373. package/src/lib/follow-pascal-presentation.test.ts +203 -0
  374. package/src/lib/follow-pascal-presentation.ts +129 -0
  375. package/src/lib/follow-pascal-sight.ts +84 -0
  376. package/src/lib/follow-pascal-targets.test.ts +406 -0
  377. package/src/lib/follow-pascal-targets.ts +198 -0
  378. package/src/lib/follow-pascal.test.ts +350 -0
  379. package/src/lib/follow-pascal.ts +284 -0
  380. package/src/lib/fresh-planar-placement.test.ts +148 -0
  381. package/src/lib/fresh-planar-placement.ts +114 -37
  382. package/src/lib/geometry-script/author.ts +157 -0
  383. package/src/lib/geometry-script/client.ts +159 -0
  384. package/src/lib/geometry-script/finish-scripts.ts +127 -0
  385. package/src/lib/geometry-script/protocol.ts +13 -0
  386. package/src/lib/gesture-lifecycle.test.ts +595 -0
  387. package/src/lib/gesture-lifecycle.ts +290 -0
  388. package/src/lib/glb-export-zone-holes.test.ts +41 -0
  389. package/src/lib/glb-export.test.ts +636 -126
  390. package/src/lib/glb-export.ts +241 -120
  391. package/src/lib/history.test.ts +18 -7
  392. package/src/lib/history.ts +7 -0
  393. package/src/lib/hud-title.ts +109 -0
  394. package/src/lib/hydration-gated-storage.ts +49 -0
  395. package/src/lib/ifc-export.test.ts +174 -0
  396. package/src/lib/ifc-export.ts +201 -0
  397. package/src/lib/ifc-parts.ts +15 -0
  398. package/src/lib/inspector-expanded.test.ts +44 -0
  399. package/src/lib/inspector-expanded.ts +36 -0
  400. package/src/lib/interaction/scope.ts +22 -0
  401. package/src/lib/keyboard-pan.test.ts +129 -7
  402. package/src/lib/keyboard-pan.ts +161 -12
  403. package/src/lib/level-selection.test.ts +57 -0
  404. package/src/lib/level-selection.ts +16 -1
  405. package/src/lib/live-gesture-round-trip.test.ts +360 -0
  406. package/src/lib/lot/__fixtures__/map-dossier-miami-shores.json +1 -0
  407. package/src/lib/lot/__fixtures__/map-dossier-stpete.json +1 -0
  408. package/src/lib/lot/clean-ring.test.ts +187 -0
  409. package/src/lib/lot/clean-ring.ts +217 -0
  410. package/src/lib/lot/dossier.test.ts +202 -0
  411. package/src/lib/lot/dossier.ts +498 -0
  412. package/src/lib/lot/drop-in.test.ts +326 -0
  413. package/src/lib/lot/drop-in.ts +392 -0
  414. package/src/lib/lot/index.ts +43 -0
  415. package/src/lib/lot/lot-patch.test.ts +340 -0
  416. package/src/lib/lot/lot-patch.ts +394 -0
  417. package/src/lib/lot/parcel-provider.ts +43 -0
  418. package/src/lib/lot/terrain.test.ts +161 -0
  419. package/src/lib/lot/terrain.ts +266 -0
  420. package/src/lib/material-catalog-model.ts +3 -1
  421. package/src/lib/material-paint-panel-model.ts +9 -5
  422. package/src/lib/material-paint.ts +4 -0
  423. package/src/lib/measurement-kind.test.ts +6 -8
  424. package/src/lib/measurements.test.ts +23 -0
  425. package/src/lib/mezzanine-actions.test.ts +342 -0
  426. package/src/lib/mezzanine-draft.test.ts +501 -0
  427. package/src/lib/mezzanine-draft.ts +278 -0
  428. package/src/lib/mezzanine-messages.ts +26 -0
  429. package/src/lib/mezzanine-selection.test.ts +207 -0
  430. package/src/lib/mezzanine-stairs.ts +46 -0
  431. package/src/lib/model-export.ts +3 -1
  432. package/src/lib/node-action-movement.ts +17 -6
  433. package/src/lib/node-display-label.ts +37 -0
  434. package/src/lib/orbit-floor-clearance.test.ts +201 -0
  435. package/src/lib/orbit-floor-clearance.ts +154 -0
  436. package/src/lib/paint-commit-route.test.ts +37 -0
  437. package/src/lib/paint-commit-route.ts +17 -0
  438. package/src/lib/paint-eyedropper.test.tsx +165 -0
  439. package/src/lib/paint-eyedropper.ts +99 -0
  440. package/src/lib/paint-pass-through.test.ts +122 -0
  441. package/src/lib/paint-pass-through.ts +78 -0
  442. package/src/lib/paint-region-hover.ts +25 -0
  443. package/src/lib/paint-region-mode.ts +154 -0
  444. package/src/lib/paint-regions.test.ts +166 -0
  445. package/src/lib/paint-regions.ts +192 -0
  446. package/src/lib/paint-scope.test.ts +317 -35
  447. package/src/lib/paint-scope.ts +267 -25
  448. package/src/lib/panel-rows.ts +21 -0
  449. package/src/lib/parametric-node-update.ts +16 -7
  450. package/src/lib/plate-paint-affected.test.ts +300 -0
  451. package/src/lib/plate-paint-affected.ts +177 -0
  452. package/src/lib/plugin-chat.ts +65 -0
  453. package/src/lib/plugin-panels.test.ts +25 -0
  454. package/src/lib/plugin-panels.ts +94 -0
  455. package/src/lib/point-ask/__fixtures__/consumer-browser.tsx +76 -0
  456. package/src/lib/point-ask/__fixtures__/house.ts +81 -0
  457. package/src/lib/point-ask/ask-created.test.ts +195 -0
  458. package/src/lib/point-ask/ask-flow.test.ts +380 -0
  459. package/src/lib/point-ask/build-context.test.ts +173 -0
  460. package/src/lib/point-ask/build-context.ts +94 -0
  461. package/src/lib/point-ask/capture-crop.ts +129 -0
  462. package/src/lib/point-ask/choreography.ts +69 -0
  463. package/src/lib/point-ask/compare.test.ts +145 -0
  464. package/src/lib/point-ask/compare.ts +73 -0
  465. package/src/lib/point-ask/describe-target.test.ts +165 -0
  466. package/src/lib/point-ask/describe-target.ts +234 -0
  467. package/src/lib/point-ask/dictation.ts +79 -0
  468. package/src/lib/point-ask/edge-marker.test.ts +61 -0
  469. package/src/lib/point-ask/edge-marker.ts +27 -0
  470. package/src/lib/point-ask/frame-loop.ts +25 -0
  471. package/src/lib/point-ask/history-marks.test.ts +94 -0
  472. package/src/lib/point-ask/history-marks.ts +39 -0
  473. package/src/lib/point-ask/host-api.ts +32 -0
  474. package/src/lib/point-ask/outline-box.test.ts +30 -0
  475. package/src/lib/point-ask/outline-box.ts +11 -0
  476. package/src/lib/point-ask/overlay-metrics.ts +6 -0
  477. package/src/lib/point-ask/pick.test.ts +220 -0
  478. package/src/lib/point-ask/pick.ts +121 -0
  479. package/src/lib/point-ask/pin-machine.test.ts +494 -0
  480. package/src/lib/point-ask/pin-machine.ts +217 -0
  481. package/src/lib/point-ask/placement.test.ts +203 -0
  482. package/src/lib/point-ask/placement.ts +141 -0
  483. package/src/lib/point-ask/pointer-tracker.ts +34 -0
  484. package/src/lib/point-ask/projector.ts +83 -0
  485. package/src/lib/point-ask/region.test.ts +157 -0
  486. package/src/lib/point-ask/region.ts +136 -0
  487. package/src/lib/point-ask/retry.test.ts +217 -0
  488. package/src/lib/point-ask/retry.ts +37 -0
  489. package/src/lib/point-ask/scene-commit.test.ts +312 -0
  490. package/src/lib/point-ask/scene-commit.ts +32 -0
  491. package/src/lib/point-ask/session.ts +1391 -0
  492. package/src/lib/point-ask/spring-keyframes.test.ts +45 -0
  493. package/src/lib/point-ask/spring-keyframes.ts +38 -0
  494. package/src/lib/point-ask/spring.test.ts +63 -0
  495. package/src/lib/point-ask/spring.ts +107 -0
  496. package/src/lib/point-ask/suggestions.test.ts +80 -0
  497. package/src/lib/point-ask/suggestions.ts +125 -0
  498. package/src/lib/point-ask/target-scope.test.ts +622 -0
  499. package/src/lib/point-ask/target-scope.ts +338 -0
  500. package/src/lib/point-ask/types.ts +48 -0
  501. package/src/lib/portable-export.test.ts +34 -1
  502. package/src/lib/portable-export.ts +119 -53
  503. package/src/lib/print-feature-thickness.ts +10 -1
  504. package/src/lib/print-roof-solids.test.ts +39 -1
  505. package/src/lib/print-roof-solids.ts +43 -10
  506. package/src/lib/print-shell-compiler-manifold-core.ts +1 -1
  507. package/src/lib/print-shell-compiler.ts +1 -0
  508. package/src/lib/registered-draft-snap.ts +12 -0
  509. package/src/lib/roof-duplication.ts +10 -2
  510. package/src/lib/room-assembly-overlay.test.ts +275 -0
  511. package/src/lib/room-assembly-overlay.ts +265 -0
  512. package/src/lib/room-built-on.test.ts +379 -0
  513. package/src/lib/room-built-on.ts +138 -0
  514. package/src/lib/room-construction-commands.ts +712 -0
  515. package/src/lib/room-construction.test.ts +746 -0
  516. package/src/lib/room-construction.ts +385 -0
  517. package/src/lib/room-controls-phase6.test.ts +630 -0
  518. package/src/lib/room-divide-session.ts +523 -0
  519. package/src/lib/room-handle-drag.ts +1068 -0
  520. package/src/lib/room-name-catalog.test.ts +90 -0
  521. package/src/lib/room-name-catalog.ts +168 -0
  522. package/src/lib/room-push-dimensions.test.ts +311 -0
  523. package/src/lib/room-push-dimensions.ts +251 -0
  524. package/src/lib/room-selection-commands.ts +62 -0
  525. package/src/lib/room-selection.test.ts +1017 -0
  526. package/src/lib/room-selection.ts +417 -0
  527. package/src/lib/room-structure-commands.ts +94 -0
  528. package/src/lib/room-structure.test.ts +921 -0
  529. package/src/lib/room-transform-session.ts +570 -0
  530. package/src/lib/room-zone-routing.test.ts +294 -0
  531. package/src/lib/room-zone-routing.ts +197 -0
  532. package/src/lib/scene-clipboard.test.ts +610 -1
  533. package/src/lib/scene-clipboard.ts +269 -58
  534. package/src/lib/scene-material-refs.test.ts +79 -0
  535. package/src/lib/scene-material-refs.ts +79 -0
  536. package/src/lib/scene.test.ts +24 -36
  537. package/src/lib/scene.ts +33 -57
  538. package/src/lib/selection-routing.test.ts +129 -45
  539. package/src/lib/selection-routing.ts +54 -75
  540. package/src/lib/session-writes.test.ts +120 -0
  541. package/src/lib/session-writes.ts +205 -0
  542. package/src/lib/sfx-bus.ts +13 -0
  543. package/src/lib/sfx-player.test.ts +70 -9
  544. package/src/lib/sfx-player.ts +6 -0
  545. package/src/lib/sidebar-panel.ts +62 -0
  546. package/src/lib/site-boundary.test.ts +4 -6
  547. package/src/lib/site-boundary.ts +1 -1
  548. package/src/lib/snapping-mode.test.ts +4 -0
  549. package/src/lib/snapping-mode.ts +8 -1
  550. package/src/lib/stair-levels.test.ts +25 -0
  551. package/src/lib/stair-levels.ts +22 -3
  552. package/src/lib/terrace-draft.test.ts +145 -0
  553. package/src/lib/terrace-draft.ts +214 -0
  554. package/src/lib/tool-cursor-icon.test.ts +16 -0
  555. package/src/lib/tool-cursor-icon.ts +11 -0
  556. package/src/lib/typing-target.ts +9 -0
  557. package/src/lib/units.ts +77 -39
  558. package/src/lib/vector-edges.test.ts +58 -0
  559. package/src/lib/vector-edges.ts +400 -0
  560. package/src/lib/viewer-selection.test.ts +577 -0
  561. package/src/lib/viewer-selection.ts +580 -0
  562. package/src/lib/wall-draw-variant.test.ts +71 -0
  563. package/src/lib/wall-draw-variant.ts +91 -0
  564. package/src/lib/wall-paint-region-session.ts +246 -0
  565. package/src/lib/wall-paint-region.test.ts +373 -0
  566. package/src/lib/wall-placement-pointer.test.ts +42 -0
  567. package/src/lib/wall-placement-pointer.ts +57 -0
  568. package/src/lib/wall-polygon-draft.test.ts +262 -0
  569. package/src/lib/wall-polygon-draft.ts +286 -0
  570. package/src/lib/wall-push.test.ts +453 -0
  571. package/src/lib/wall-region-face.ts +352 -0
  572. package/src/lib/wall-region-handles.test.ts +304 -0
  573. package/src/lib/wall-region-handles.ts +368 -0
  574. package/src/lib/wall-region-list.test.ts +119 -0
  575. package/src/lib/wall-region-list.ts +136 -0
  576. package/src/lib/wall-region-snap.ts +110 -0
  577. package/src/lib/window-changes.ts +35 -0
  578. package/src/lib/window-interaction.ts +14 -0
  579. package/src/lib/zone-content.ts +4 -1
  580. package/src/lib/zone-name.test.ts +17 -0
  581. package/src/lib/zone-name.ts +17 -0
  582. package/src/store/opens-in-select-mode.test.ts +223 -0
  583. package/src/store/paint-mode.test.ts +188 -0
  584. package/src/store/terrain-sculpt-mode.test.ts +19 -21
  585. package/src/store/tool-mode.test.ts +137 -6
  586. package/src/store/use-audio.test.ts +146 -0
  587. package/src/store/use-audio.tsx +127 -3
  588. package/src/store/use-construction-reveal.test.ts +173 -0
  589. package/src/store/use-construction-reveal.ts +145 -0
  590. package/src/store/use-corner-snap-hint.test.ts +24 -0
  591. package/src/store/use-corner-snap-hint.ts +47 -0
  592. package/src/store/use-delete-confirmation.ts +28 -1
  593. package/src/store/use-drawing-view.test.ts +24 -3
  594. package/src/store/use-drawing-view.ts +29 -1
  595. package/src/store/use-editor.tsx +432 -321
  596. package/src/store/use-floorplan-draft-preview.ts +13 -0
  597. package/src/store/use-floorplan-marquee.ts +12 -2
  598. package/src/store/use-follow-pascal.test.ts +62 -0
  599. package/src/store/use-follow-pascal.ts +53 -0
  600. package/src/store/use-point-ask.ts +247 -0
  601. package/src/store/use-quick-measurement-hud.ts +3 -3
  602. package/src/store/use-stair-build-preview.ts +33 -5
  603. package/src/store/use-wall-move-ghosts.ts +4 -2
  604. package/src/store/use-wall-region-handles-selection.test.ts +222 -0
  605. package/src/store/use-wall-region-handles-selection.ts +139 -0
  606. package/src/store/view-layout.test.ts +201 -0
  607. package/src/store/view-layout.ts +199 -0
  608. package/src/test-utils/immediate-animation-frames.ts +16 -0
  609. package/src/test-utils/live-gesture-round-trip.ts +224 -0
  610. package/src/test-utils/render-current-markup.tsx +16 -0
  611. package/src/test-utils/selection-harness.ts +82 -0
  612. package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +0 -499
  613. package/src/components/systems/zone/zone-label-editor-system.tsx +0 -333
  614. package/src/components/tools/fence/fence-drafting.test.ts +0 -100
  615. package/src/components/tools/fence/fence-drafting.ts +0 -266
  616. package/src/components/tools/stair/stair-click-guard.test.ts +0 -58
  617. package/src/components/tools/stair/stair-click-guard.ts +0 -72
  618. package/src/components/tools/stair/stair-defaults.ts +0 -18
  619. package/src/components/tools/stair/stair-tool.tsx +0 -634
  620. package/src/components/ui/sidebar/panels/plugins-panel.tsx +0 -218
  621. package/src/components/viewer/viewer-units-panel.tsx +0 -116
  622. package/src/lib/floorplan/stairs.ts +0 -489
  623. package/src/lib/level-duplication.test.ts +0 -144
  624. package/src/lib/level-duplication.ts +0 -195
@@ -2,10 +2,9 @@ import {
2
2
  type AnyNode,
3
3
  type AnyNodeId,
4
4
  type BuildingNode,
5
- DEFAULT_LEVEL_HEIGHT,
6
5
  emitter,
7
6
  type GuideNode,
8
- LevelNode,
7
+ type LevelNode,
9
8
  type ScanNode,
10
9
  type SiteNode,
11
10
  useScene,
@@ -19,7 +18,9 @@ import {
19
18
  Copy,
20
19
  Eye,
21
20
  EyeOff,
21
+ Boxes,
22
22
  Group,
23
+ LayoutGrid,
23
24
  Loader2,
24
25
  MoreHorizontal,
25
26
  Pencil,
@@ -29,7 +30,18 @@ import {
29
30
  X,
30
31
  } from 'lucide-react'
31
32
  import { AnimatePresence, LayoutGroup, motion } from 'motion/react'
32
- import { memo, useEffect, useRef, useState, useSyncExternalStore } from 'react'
33
+ import { type ComponentType, lazy, memo, Suspense, useEffect, useRef, useState, useSyncExternalStore } from 'react'
34
+ import { nodeRegistry } from '@pascal-app/core'
35
+
36
+ /** The site kind's inspector (`parametrics.customPanel`), mounted under the Site header. */
37
+ const SiteKindPanel = lazy(async () => {
38
+ const def = nodeRegistry.get('site') as
39
+ | { parametrics?: { customPanel?: () => Promise<{ default: ComponentType }> } }
40
+ | undefined
41
+ const loader = def?.parametrics?.customPanel
42
+ if (!loader) return { default: () => null }
43
+ return loader()
44
+ })
33
45
  import { useShallow } from 'zustand/react/shallow'
34
46
  import { ColorDot } from './../../../../../components/ui/primitives/color-dot'
35
47
  import {
@@ -40,8 +52,9 @@ import {
40
52
  import {
41
53
  buildLevelDuplicateCreateOps,
42
54
  type LevelDuplicatePreset,
43
- } from './../../../../../lib/level-duplication'
44
- import { getDefaultLevelName } from '@pascal-app/core'
55
+ } from '@pascal-app/core/building'
56
+ import { getDefaultLevelName, getLevelDisplayName } from '@pascal-app/core'
57
+ import { addStoreyOnTop } from './../../../../../lib/add-storey'
45
58
  import { deleteLevelWithFallbackSelection } from './../../../../../lib/level-selection'
46
59
  import {
47
60
  formatAreaLabel,
@@ -53,12 +66,19 @@ import {
53
66
  } from './../../../../../lib/measurements'
54
67
  import { createLocalGuideImage, createLocalScan } from './../../../../../lib/local-guide-image'
55
68
  import { editorHostTreeChildrenRegistry } from './../../../../../lib/host-tree-children'
69
+ import { requestRoomDeletion } from './../../../../../lib/room-structure-commands'
70
+ import {
71
+ selectZoneOrRoom,
72
+ useZoneSelected,
73
+ zoneKindLabel,
74
+ } from './../../../../../lib/room-zone-routing'
56
75
  import { createUnitInBuilding, toggleZoneMembership } from './../../../../../lib/units'
57
76
  import { cn } from './../../../../../lib/utils'
58
77
  import useEditor from './../../../../../store/use-editor'
59
78
  import { useUploadStore } from '../../../../../store/use-upload'
60
79
  import { MetricControl } from '../../../controls/metric-control'
61
80
  import { LevelDuplicateDialog } from '../../../level-duplicate-dialog'
81
+ import { CollectionsSection } from './collection-tree-node'
62
82
  import { InlineRenameInput } from './inline-rename-input'
63
83
  import { ZoneMembershipCheckbox } from './zone-membership-checkbox'
64
84
  import { focusTreeNode, TreeNode, TreeNodeWrapper } from './tree-node'
@@ -180,7 +200,7 @@ const PropertyLineSection = memo(function PropertyLineSection() {
180
200
  className={cn(
181
201
  'flex h-6 w-6 cursor-pointer items-center justify-center rounded transition-colors',
182
202
  isEditing
183
- ? 'bg-orange-500/20 text-orange-400'
203
+ ? 'bg-orange-500/20 text-orange-700 dark:text-orange-400'
184
204
  : 'text-muted-foreground hover:bg-accent',
185
205
  )}
186
206
  onClick={handleToggleEdit}
@@ -236,7 +256,7 @@ const PropertyLineSection = memo(function PropertyLineSection() {
236
256
  className={cn(
237
257
  'flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded',
238
258
  points.length > 3
239
- ? 'text-muted-foreground hover:bg-red-500/20 hover:text-red-400'
259
+ ? 'text-muted-foreground hover:bg-red-500/20 hover:text-red-600 dark:hover:text-red-400'
240
260
  : 'cursor-not-allowed text-muted-foreground/30',
241
261
  )}
242
262
  disabled={points.length <= 3}
@@ -719,8 +739,7 @@ const LevelItem = memo(function LevelItem({
719
739
  const [cameraPopoverOpen, setCameraPopoverOpen] = useState(false)
720
740
  const [duplicateDialogOpen, setDuplicateDialogOpen] = useState(false)
721
741
  const [isEditing, setIsEditing] = useState(false)
722
- const createNodes = useScene((s) => s.createNodes)
723
- const updateNodes = useScene((s) => s.updateNodes)
742
+ const applyNodeChanges = useScene((s) => s.applyNodeChanges)
724
743
  const itemRef = useRef<HTMLDivElement>(null)
725
744
  const isSelected = selectedLevelId === level.id
726
745
  const canDeleteLevel = level.level !== 0
@@ -754,22 +773,14 @@ const LevelItem = memo(function LevelItem({
754
773
  }
755
774
 
756
775
  const handleDuplicateLevel = (preset: LevelDuplicatePreset = 'everything') => {
757
- const { createOps, newLevelId, shiftedLevels } = buildLevelDuplicateCreateOps({
776
+ const { createOps, newLevelId, updateOps } = buildLevelDuplicateCreateOps({
758
777
  nodes: useScene.getState().nodes,
759
778
  level,
760
779
  levels,
761
780
  preset,
762
781
  })
763
782
 
764
- if (shiftedLevels.length > 0) {
765
- updateNodes(
766
- shiftedLevels.map((shiftedLevel) => ({
767
- id: shiftedLevel.id as AnyNodeId,
768
- data: { level: shiftedLevel.level } as Partial<AnyNode>,
769
- })),
770
- )
771
- }
772
- createNodes(createOps)
783
+ applyNodeChanges({ create: createOps, update: updateOps })
773
784
  selectLevel(newLevelId as LevelNode['id'], false)
774
785
  setDuplicateDialogOpen(false)
775
786
  }
@@ -838,6 +849,7 @@ const LevelItem = memo(function LevelItem({
838
849
  />
839
850
  <InlineRenameInput
840
851
  defaultName={getDefaultLevelName(level.level)}
852
+ displayName={getLevelDisplayName(level)}
841
853
  isEditing={isEditing}
842
854
  nodeId={level.id}
843
855
  onStartEditing={() => setIsEditing(true)}
@@ -1002,7 +1014,6 @@ const LevelsSection = memo(function LevelsSection({
1002
1014
  onUploadAsset?: (projectId: string, levelId: string, file: File, type: 'scan' | 'guide') => void
1003
1015
  onDeleteAsset?: (projectId: string, url: string) => void
1004
1016
  } = {}) {
1005
- const createNode = useScene((state) => state.createNode)
1006
1017
  const updateNode = useScene((state) => state.updateNode)
1007
1018
  const selectedBuildingId = useViewer((state) => state.selection.buildingId)
1008
1019
  const selectedLevelId = useViewer((state) => state.selection.levelId)
@@ -1025,13 +1036,7 @@ const LevelsSection = memo(function LevelsSection({
1025
1036
  if (!building) return null
1026
1037
 
1027
1038
  const handleAddLevel = () => {
1028
- const newLevel = LevelNode.parse({
1029
- level: levels.length,
1030
- height: DEFAULT_LEVEL_HEIGHT,
1031
- children: [],
1032
- parentId: building.id,
1033
- })
1034
- createNode(newLevel, building.id)
1039
+ const newLevel = addStoreyOnTop(building.id)
1035
1040
  setSelection({ buildingId: building.id, levelId: newLevel.id })
1036
1041
  }
1037
1042
 
@@ -1149,137 +1154,15 @@ const UnitsSection = memo(function UnitsSection({
1149
1154
  )
1150
1155
  })
1151
1156
 
1152
- const LayerToggle = memo(function LayerToggle() {
1153
- const structureLayer = useEditor((state) => state.structureLayer)
1154
- const setStructureLayer = useEditor((state) => state.setStructureLayer)
1155
- const phase = useEditor((state) => state.phase)
1156
- const setPhase = useEditor((state) => state.setPhase)
1157
-
1158
- const activeTab =
1159
- phase === 'structure' && structureLayer === 'elements'
1160
- ? 'structure'
1161
- : phase === 'furnish'
1162
- ? 'furnish'
1163
- : phase === 'structure' && structureLayer === 'zones'
1164
- ? 'zones'
1165
- : 'none'
1166
-
1167
- return (
1168
- <div className="relative flex items-center gap-1 border-border/50 border-b bg-[#2C2C2E] p-1">
1169
- <button
1170
- className={cn(
1171
- 'relative flex flex-1 cursor-pointer flex-col items-center justify-center rounded-md py-2 font-medium text-[10px] transition-all duration-200',
1172
- activeTab === 'structure'
1173
- ? 'text-foreground'
1174
- : 'text-muted-foreground hover:bg-white/5 hover:text-foreground',
1175
- )}
1176
- onClick={() => {
1177
- setPhase('structure')
1178
- setStructureLayer('elements')
1179
- }}
1180
- >
1181
- {activeTab === 'structure' && (
1182
- <motion.div
1183
- className="absolute inset-0 rounded-md bg-[#3e3e3e] shadow-sm ring-1 ring-border/50"
1184
- layoutId="layerToggleActiveBg"
1185
- transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }}
1186
- />
1187
- )}
1188
- <div className="relative z-10 flex flex-col items-center">
1189
- <img
1190
- alt="Structure"
1191
- className={cn(
1192
- 'mb-1 h-6 w-6 transition-all',
1193
- activeTab !== 'structure' && 'opacity-50 grayscale',
1194
- )}
1195
- src="/icons/room.webp"
1196
- />
1197
- Structure
1198
- </div>
1199
- <div className="absolute right-1.5 bottom-1 z-10 rounded border border-border/40 bg-background/40 px-1 py-[2px] backdrop-blur-md">
1200
- <span className="block font-medium font-mono text-[9px] text-muted-foreground/70 leading-none">
1201
- B
1202
- </span>
1203
- </div>
1204
- </button>
1205
-
1206
- <button
1207
- className={cn(
1208
- 'relative flex flex-1 cursor-pointer flex-col items-center justify-center rounded-md py-2 font-medium text-[10px] transition-all duration-200',
1209
- activeTab === 'furnish'
1210
- ? 'text-foreground'
1211
- : 'text-muted-foreground hover:bg-white/5 hover:text-foreground',
1212
- )}
1213
- onClick={() => {
1214
- setPhase('furnish')
1215
- }}
1216
- >
1217
- {activeTab === 'furnish' && (
1218
- <motion.div
1219
- className="absolute inset-0 rounded-md bg-[#3e3e3e] shadow-sm ring-1 ring-border/50"
1220
- layoutId="layerToggleActiveBg"
1221
- transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }}
1222
- />
1223
- )}
1224
- <div className="relative z-10 flex flex-col items-center">
1225
- <img
1226
- alt="Furnish"
1227
- className={cn(
1228
- 'mb-1 h-6 w-6 transition-all',
1229
- activeTab !== 'furnish' && 'opacity-50 grayscale',
1230
- )}
1231
- src="/icons/couch.webp"
1232
- />
1233
- Furnish
1234
- </div>
1235
- <div className="absolute right-1.5 bottom-1 z-10 rounded border border-border/40 bg-background/40 px-1 py-[2px] backdrop-blur-md">
1236
- <span className="block font-medium font-mono text-[9px] text-muted-foreground/70 leading-none">
1237
- F
1238
- </span>
1239
- </div>
1240
- </button>
1241
-
1242
- <button
1243
- className={cn(
1244
- 'relative flex flex-1 cursor-pointer flex-col items-center justify-center rounded-md py-2 font-medium text-[10px] transition-all duration-200',
1245
- activeTab === 'zones'
1246
- ? 'text-foreground'
1247
- : 'text-muted-foreground hover:bg-white/5 hover:text-foreground',
1248
- )}
1249
- onClick={() => {
1250
- setPhase('structure')
1251
- setStructureLayer('zones')
1252
- }}
1253
- >
1254
- {activeTab === 'zones' && (
1255
- <motion.div
1256
- className="absolute inset-0 rounded-md bg-[#3e3e3e] shadow-sm ring-1 ring-border/50"
1257
- layoutId="layerToggleActiveBg"
1258
- transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }}
1259
- />
1260
- )}
1261
- <div className="relative z-10 flex flex-col items-center">
1262
- <img
1263
- alt="Zones"
1264
- className={cn(
1265
- 'mb-1 h-6 w-6 transition-all',
1266
- activeTab !== 'zones' && 'opacity-50 grayscale',
1267
- )}
1268
- src="/icons/kitchen.webp"
1269
- />
1270
- Zones
1271
- </div>
1272
- <div className="absolute right-1.5 bottom-1 z-10 rounded border border-border/40 bg-background/40 px-1 py-[2px] backdrop-blur-md">
1273
- <span className="block font-medium font-mono text-[9px] text-muted-foreground/70 leading-none">
1274
- Z
1275
- </span>
1276
- </div>
1277
- </button>
1278
- </div>
1279
- )
1280
- })
1281
-
1282
- const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLast?: boolean }) {
1157
+ const ZoneItem = memo(function ZoneItem({
1158
+ zone,
1159
+ depth,
1160
+ isLast,
1161
+ }: {
1162
+ zone: ZoneNode
1163
+ depth: number
1164
+ isLast?: boolean
1165
+ }) {
1283
1166
  const [isEditing, setIsEditing] = useState(false)
1284
1167
  const [cameraPopoverOpen, setCameraPopoverOpen] = useState(false)
1285
1168
  const deleteNode = useScene((state) => state.deleteNode)
@@ -1289,15 +1172,14 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
1289
1172
  const unit = focusedUnitId ? s.nodes[focusedUnitId] : undefined
1290
1173
  return unit?.type === 'unit' ? unit : null
1291
1174
  })
1292
- const selectedZoneId = useViewer((state) => state.selection.zoneId)
1293
1175
  const hoveredId = useViewer((state) => state.hoveredId)
1294
1176
  const setSelection = useViewer((state) => state.setSelection)
1295
1177
  const setHoveredId = useViewer((state) => state.setHoveredId)
1296
- const setPhase = useEditor((state) => state.setPhase)
1297
1178
  const setMode = useEditor((state) => state.setMode)
1298
1179
  const unit = useViewer((state) => state.unit)
1299
1180
 
1300
- const isSelected = selectedZoneId === zone.id
1181
+ const isSelected = useZoneSelected(zone.id)
1182
+ const isRoom = zone.spaceRole === 'room'
1301
1183
  const isHovered = hoveredId === zone.id
1302
1184
 
1303
1185
  const itemRef = useRef<HTMLDivElement>(null)
@@ -1308,12 +1190,11 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
1308
1190
  }
1309
1191
  }, [isSelected])
1310
1192
 
1311
- const defaultName = `Zone (${formatAreaLabel(calculatePolygonArea(zone.polygon), unit)})`
1193
+ const defaultName = `${zoneKindLabel(zone)} (${formatAreaLabel(calculatePolygonArea(zone.polygon), unit)})`
1312
1194
 
1313
1195
  const handleClick = () => {
1314
- setSelection({ zoneId: zone.id })
1315
- setPhase('structure')
1316
1196
  setMode('select')
1197
+ selectZoneOrRoom(zone.id)
1317
1198
  }
1318
1199
 
1319
1200
  const handleDoubleClick = () => {
@@ -1322,6 +1203,8 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
1322
1203
 
1323
1204
  const handleDelete = (e: React.MouseEvent) => {
1324
1205
  e.stopPropagation()
1206
+ // A room goes the way the room panel deletes it (its walls, or merged back).
1207
+ if (isRoom) return requestRoomDeletion(zone.id)
1325
1208
  deleteNode(zone.id)
1326
1209
  if (isSelected) {
1327
1210
  setSelection({ zoneId: null })
@@ -1335,7 +1218,7 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
1335
1218
  return (
1336
1219
  <div
1337
1220
  className={cn(
1338
- 'group/row relative flex h-8 cursor-pointer select-none items-center border-border/50 border-b px-3 text-sm transition-all duration-200',
1221
+ 'group/row relative flex h-8 cursor-pointer select-none items-center border-border/50 border-b text-sm transition-all duration-200',
1339
1222
  isSelected
1340
1223
  ? 'bg-accent/50 text-foreground'
1341
1224
  : isHovered
@@ -1347,6 +1230,7 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
1347
1230
  onMouseEnter={() => setHoveredId(zone.id)}
1348
1231
  onMouseLeave={() => setHoveredId(null)}
1349
1232
  ref={itemRef}
1233
+ style={{ paddingLeft: depth * 12 + 28, paddingRight: 12 }}
1350
1234
  >
1351
1235
  {/* Vertical tree line */}
1352
1236
  <div
@@ -1354,12 +1238,12 @@ const ZoneItem = memo(function ZoneItem({ zone, isLast }: { zone: ZoneNode; isLa
1354
1238
  'pointer-events-none absolute w-px bg-border/50',
1355
1239
  isLast ? 'top-0 bottom-1/2' : 'top-0 bottom-0',
1356
1240
  )}
1357
- style={{ left: 8 }}
1241
+ style={{ left: (depth - 1) * 12 + 20 }}
1358
1242
  />
1359
1243
  {/* Horizontal branch line */}
1360
1244
  <div
1361
1245
  className="pointer-events-none absolute top-1/2 h-px bg-border/50"
1362
- style={{ left: 8, width: 4 }}
1246
+ style={{ left: (depth - 1) * 12 + 20, width: 4 }}
1363
1247
  />
1364
1248
 
1365
1249
  {focusedUnit && (
@@ -1476,17 +1360,9 @@ const MultiSelectionBadge = memo(function MultiSelectionBadge() {
1476
1360
  )
1477
1361
  })
1478
1362
 
1479
- const ContentSection = memo(function ContentSection() {
1363
+ const RoomsSection = memo(function RoomsSection() {
1480
1364
  const selectedLevelId = useViewer((state) => state.selection.levelId)
1481
- const structureLayer = useEditor((state) => state.structureLayer)
1482
- const phase = useEditor((state) => state.phase)
1483
- const setPhase = useEditor((state) => state.setPhase)
1484
- const setMode = useEditor((state) => state.setMode)
1485
- const setTool = useEditor((state) => state.setTool)
1486
-
1487
- const level = useScene((s) =>
1488
- selectedLevelId ? ((s.nodes[selectedLevelId] as LevelNode | undefined) ?? null) : null,
1489
- )
1365
+ const armToolMode = useEditor((state) => state.armToolMode)
1490
1366
  const levelZones = useScene(
1491
1367
  useShallow((s) => {
1492
1368
  if (!selectedLevelId) return []
@@ -1495,6 +1371,82 @@ const ContentSection = memo(function ContentSection() {
1495
1371
  )
1496
1372
  }),
1497
1373
  )
1374
+ const [expanded, setExpanded] = useState(true)
1375
+
1376
+ if (!selectedLevelId) return null
1377
+
1378
+ const handleDrawZone = (event: React.MouseEvent) => {
1379
+ event.stopPropagation()
1380
+ armToolMode({ mode: 'build', tool: 'zone' })
1381
+ }
1382
+
1383
+ const rooms = levelZones.filter((zone) => zone.spaceRole === 'room')
1384
+ const others = levelZones.filter((zone) => zone.spaceRole !== 'room')
1385
+
1386
+ return (
1387
+ <div className="subtle-scrollbar max-h-72 shrink-0 overflow-y-auto overflow-x-hidden">
1388
+ <TreeNodeWrapper
1389
+ actions={
1390
+ <button
1391
+ className="flex h-6 w-6 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"
1392
+ onClick={handleDrawZone}
1393
+ title="Draw a zone"
1394
+ type="button"
1395
+ >
1396
+ <Plus className="h-3 w-3" />
1397
+ </button>
1398
+ }
1399
+ depth={1}
1400
+ expanded={expanded}
1401
+ hasChildren
1402
+ icon={<LayoutGrid className="h-3.5 w-3.5" />}
1403
+ label={
1404
+ <span className="flex items-center gap-1.5">
1405
+ Rooms
1406
+ {rooms.length > 0 && (
1407
+ <span className="text-muted-foreground text-xs">{rooms.length}</span>
1408
+ )}
1409
+ </span>
1410
+ }
1411
+ onClick={() => setExpanded((value) => !value)}
1412
+ onToggle={() => setExpanded((value) => !value)}
1413
+ >
1414
+ {levelZones.length === 0 && (
1415
+ <div className="border-border/50 border-b py-2 pr-3 pl-[52px] text-muted-foreground text-xs">
1416
+ Close walls around a space to make a room.
1417
+ </div>
1418
+ )}
1419
+ {rooms.map((zone, index) => (
1420
+ <ZoneItem
1421
+ depth={2}
1422
+ isLast={others.length === 0 && index === rooms.length - 1}
1423
+ key={zone.id}
1424
+ zone={zone}
1425
+ />
1426
+ ))}
1427
+ {others.length > 0 && (
1428
+ <>
1429
+ <div
1430
+ className="pt-3 pb-1 pl-[52px] font-medium text-[11px] text-muted-foreground"
1431
+ data-other-zones
1432
+ >
1433
+ Other zones
1434
+ </div>
1435
+ {others.map((zone, index) => (
1436
+ <ZoneItem depth={2} isLast={index === others.length - 1} key={zone.id} zone={zone} />
1437
+ ))}
1438
+ </>
1439
+ )}
1440
+ </TreeNodeWrapper>
1441
+ </div>
1442
+ )
1443
+ })
1444
+
1445
+ const ContentSection = memo(function ContentSection() {
1446
+ const selectedLevelId = useViewer((state) => state.selection.levelId)
1447
+ const level = useScene((s) =>
1448
+ selectedLevelId ? ((s.nodes[selectedLevelId] as LevelNode | undefined) ?? null) : null,
1449
+ )
1498
1450
  const elementChildren = useScene(
1499
1451
  useShallow((s) => {
1500
1452
  if (!selectedLevelId) return []
@@ -1506,6 +1458,7 @@ const ContentSection = memo(function ContentSection() {
1506
1458
  })
1507
1459
  }),
1508
1460
  )
1461
+ const [expanded, setExpanded] = useState(true)
1509
1462
 
1510
1463
  if (!level) {
1511
1464
  return (
@@ -1513,49 +1466,40 @@ const ContentSection = memo(function ContentSection() {
1513
1466
  )
1514
1467
  }
1515
1468
 
1516
- if (structureLayer === 'zones') {
1517
- const handleAddZone = () => {
1518
- setPhase('structure')
1519
- setMode('build')
1520
- setTool('zone')
1521
- }
1522
-
1523
- if (levelZones.length === 0) {
1524
- return (
1525
- <div className="px-3 py-4 text-muted-foreground text-sm">
1526
- No zones on this level.{' '}
1527
- <button className="cursor-pointer text-primary hover:underline" onClick={handleAddZone}>
1528
- Add one
1529
- </button>
1530
- </div>
1531
- )
1532
- }
1533
-
1534
- return (
1535
- <div className="flex flex-col">
1536
- {levelZones.map((zone, index) => (
1537
- <ZoneItem isLast={index === levelZones.length - 1} key={zone.id} zone={zone} />
1538
- ))}
1539
- </div>
1540
- )
1541
- }
1542
-
1543
- if (elementChildren.length === 0) {
1544
- return <div className="px-3 py-4 text-muted-foreground text-sm">No elements on this level</div>
1545
- }
1546
1469
  return (
1547
- <TreeNodeDragProvider>
1548
- <div className="flex flex-col">
1549
- {elementChildren.map((childId, index) => (
1550
- <TreeNode
1551
- depth={0}
1552
- isLast={index === elementChildren.length - 1}
1553
- key={childId}
1554
- nodeId={childId}
1555
- />
1556
- ))}
1557
- </div>
1558
- </TreeNodeDragProvider>
1470
+ <TreeNodeWrapper
1471
+ depth={1}
1472
+ expanded={expanded}
1473
+ hasChildren
1474
+ icon={<Boxes className="h-3.5 w-3.5" />}
1475
+ label={
1476
+ <span className="flex items-center gap-1.5">
1477
+ Elements
1478
+ {elementChildren.length > 0 && (
1479
+ <span className="text-muted-foreground text-xs">{elementChildren.length}</span>
1480
+ )}
1481
+ </span>
1482
+ }
1483
+ onClick={() => setExpanded((value) => !value)}
1484
+ onToggle={() => setExpanded((value) => !value)}
1485
+ >
1486
+ {elementChildren.length === 0 ? (
1487
+ <div className="border-border/50 border-b py-2 pr-3 pl-[52px] text-muted-foreground text-xs">
1488
+ No elements on this level
1489
+ </div>
1490
+ ) : (
1491
+ <TreeNodeDragProvider>
1492
+ {elementChildren.map((childId, index) => (
1493
+ <TreeNode
1494
+ depth={2}
1495
+ isLast={index === elementChildren.length - 1}
1496
+ key={childId}
1497
+ nodeId={childId}
1498
+ />
1499
+ ))}
1500
+ </TreeNodeDragProvider>
1501
+ )}
1502
+ </TreeNodeWrapper>
1559
1503
  )
1560
1504
  })
1561
1505
 
@@ -1591,7 +1535,7 @@ const BuildingItem = memo(function BuildingItem({
1591
1535
  const handleSelect = () => {
1592
1536
  setSelection({ buildingId: building.id })
1593
1537
  if (phase === 'site') {
1594
- setPhase('structure')
1538
+ setPhase('building')
1595
1539
  }
1596
1540
  }
1597
1541
 
@@ -1713,7 +1657,8 @@ const BuildingItem = memo(function BuildingItem({
1713
1657
  projectId={projectId}
1714
1658
  />
1715
1659
  <UnitsSection buildingId={building.id} />
1716
- <LayerToggle />
1660
+ <RoomsSection />
1661
+ <CollectionsSection />
1717
1662
  </div>
1718
1663
  <div className="subtle-scrollbar relative min-h-0 flex-1 overflow-y-auto overflow-x-hidden">
1719
1664
  <MultiSelectionBadge />
@@ -1769,7 +1714,14 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel
1769
1714
  : 'text-muted-foreground hover:bg-accent/30 hover:text-foreground',
1770
1715
  )}
1771
1716
  layout="position"
1772
- onClick={() => setPhase('site')}
1717
+ onClick={() => {
1718
+ setPhase('site')
1719
+ // Also SELECT the site node so the registry inspector mounts the
1720
+ // site kind's panel (address / parcel / setbacks / front edge).
1721
+ // The site kind opts out of 3D click selection, so this header is
1722
+ // its only selection entry point. (WS1)
1723
+ setSelection({ selectedIds: [siteNode.id as AnyNodeId], zoneId: null })
1724
+ }}
1773
1725
  >
1774
1726
  <div className="flex items-center gap-2">
1775
1727
  <img
@@ -1811,6 +1763,12 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel
1811
1763
  transition={{ type: 'spring', bounce: 0, duration: 0.4 }}
1812
1764
  >
1813
1765
  <PropertyLineSection />
1766
+ {/* Address → parcel, setbacks, front edge: the site kind's own
1767
+ panel (packages/nodes/src/site/panel.tsx), loaded through
1768
+ the registry so the editor does not import the nodes package. */}
1769
+ <Suspense fallback={null}>
1770
+ <SiteKindPanel />
1771
+ </Suspense>
1814
1772
  </motion.div>
1815
1773
  )}
1816
1774
  </AnimatePresence>
@@ -1824,7 +1782,7 @@ export function SitePanel({ projectId, onUploadAsset, onDeleteAsset }: SitePanel
1824
1782
  <div className="flex min-h-0 flex-1 flex-col">
1825
1783
  {buildings.map((building) => {
1826
1784
  const isBuildingActive =
1827
- (phase === 'structure' || phase === 'furnish') &&
1785
+ phase === 'building' &&
1828
1786
  selectedBuildingId === building.id
1829
1787
 
1830
1788
  return (