@pascal-app/editor 0.9.1 → 1.0.0-beta.1

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 (366) hide show
  1. package/package.json +14 -9
  2. package/src/components/editor/alignment-3d-guide-layer.tsx +10 -2
  3. package/src/components/editor/bake-exporter.tsx +49 -0
  4. package/src/components/editor/bake-thumbnail.tsx +95 -0
  5. package/src/components/editor/camera-dragging-lifecycle.test.ts +49 -0
  6. package/src/components/editor/camera-dragging-lifecycle.ts +41 -0
  7. package/src/components/editor/custom-camera-controls.tsx +332 -199
  8. package/src/components/editor/delete-confirmation-dialog.tsx +54 -0
  9. package/src/components/editor/editor-layout-mobile.tsx +5 -0
  10. package/src/components/editor/editor-layout-v2.tsx +18 -0
  11. package/src/components/editor/elevation-3d-guide-layer.tsx +120 -0
  12. package/src/components/editor/export-manager.tsx +71 -63
  13. package/src/components/editor/fence-tangent-lines-3d.tsx +87 -0
  14. package/src/components/editor/first-person/build-collider-world.test.ts +102 -5
  15. package/src/components/editor/first-person/build-collider-world.ts +52 -5
  16. package/src/components/editor/first-person/terrain-collider.test.ts +132 -0
  17. package/src/components/editor/first-person/terrain-collider.ts +212 -0
  18. package/src/components/editor/first-person-controls.tsx +410 -136
  19. package/src/components/editor/floating-action-menu.tsx +435 -64
  20. package/src/components/editor/floating-building-action-menu.tsx +1 -1
  21. package/src/components/editor/floorplan-background-selection.test.ts +72 -0
  22. package/src/components/editor/floorplan-background-selection.ts +7 -20
  23. package/src/components/editor/floorplan-camera-sync.test.ts +249 -0
  24. package/src/components/editor/floorplan-camera-sync.ts +213 -0
  25. package/src/components/editor/floorplan-mode-coordinator.tsx +82 -0
  26. package/src/components/editor/floorplan-navigation-presentation.test.ts +142 -0
  27. package/src/components/editor/floorplan-navigation-presentation.ts +156 -0
  28. package/src/components/editor/floorplan-panel.tsx +2517 -1492
  29. package/src/components/editor/grid.tsx +166 -39
  30. package/src/components/editor/group-actions.ts +597 -0
  31. package/src/components/editor/group-floating-action-menu.tsx +130 -0
  32. package/src/components/editor/group-move-3d.ts +397 -0
  33. package/src/components/editor/group-rotate-handle.tsx +102 -64
  34. package/src/components/editor/group-selection-box-3d.tsx +173 -0
  35. package/src/components/editor/group-transform-shared.test.ts +192 -1
  36. package/src/components/editor/group-transform-shared.ts +240 -7
  37. package/src/components/editor/handles/handle-arrow.tsx +81 -23
  38. package/src/components/editor/handles/handle-drag-history.test.ts +96 -0
  39. package/src/components/editor/handles/handle-drag-history.ts +14 -0
  40. package/src/components/editor/handles/preview-overrides.test.ts +26 -0
  41. package/src/components/editor/handles/preview-overrides.ts +13 -0
  42. package/src/components/editor/handles/resize-snap.test.ts +52 -0
  43. package/src/components/editor/handles/resize-snap.ts +23 -0
  44. package/src/components/editor/handles/use-handle-drag.ts +49 -6
  45. package/src/components/editor/index.tsx +289 -35
  46. package/src/components/editor/measurement-pill.tsx +68 -23
  47. package/src/components/editor/node-action-menu.tsx +14 -1
  48. package/src/components/editor/node-arrow-handles.tsx +409 -180
  49. package/src/components/editor/opening-guides-3d-layer.tsx +144 -0
  50. package/src/components/editor/quick-measurement-card.tsx +76 -0
  51. package/src/components/editor/quick-measurement-hud.tsx +34 -0
  52. package/src/components/editor/riser-diagram-panel.tsx +137 -0
  53. package/src/components/editor/selection-manager.tsx +814 -466
  54. package/src/components/editor/site-edge-labels.tsx +28 -6
  55. package/src/components/editor/slab-hole-highlights.tsx +13 -5
  56. package/src/components/editor/snapshot-capture-overlay.tsx +258 -115
  57. package/src/components/editor/three-context-bridge.ts +22 -0
  58. package/src/components/editor/thumbnail-generator.tsx +118 -253
  59. package/src/components/editor/use-floorplan-background-placement.ts +113 -45
  60. package/src/components/editor/use-floorplan-hit-testing.test.ts +82 -0
  61. package/src/components/editor/use-floorplan-hit-testing.ts +144 -161
  62. package/src/components/editor/use-floorplan-scene-data.ts +5 -5
  63. package/src/components/editor/use-mesh-settle-epoch.ts +27 -0
  64. package/src/components/editor/wall-measurement-label.tsx +10 -28
  65. package/src/components/editor/wall-move-side-handles.tsx +229 -39
  66. package/src/components/editor/wall-opening-highlights.tsx +63 -32
  67. package/src/components/editor/wall-snap-beacon-layer.tsx +162 -6
  68. package/src/components/editor-2d/floorplan-action-menu-layer.tsx +9 -5
  69. package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +2 -1
  70. package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +40 -9
  71. package/src/components/editor-2d/floorplan-cursor-indicator-position.test.ts +31 -0
  72. package/src/components/editor-2d/floorplan-cursor-indicator-position.ts +39 -0
  73. package/src/components/editor-2d/floorplan-group-action-menu.tsx +87 -0
  74. package/src/components/editor-2d/floorplan-group-move.tsx +744 -0
  75. package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +113 -0
  76. package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +1672 -0
  77. package/src/components/editor-2d/floorplan-quick-measure-layer.tsx +212 -0
  78. package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +72 -0
  79. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +219 -20
  80. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +327 -59
  81. package/src/components/editor-2d/floorplan-render-context.test.ts +27 -0
  82. package/src/components/editor-2d/floorplan-render-context.tsx +113 -8
  83. package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +5 -3
  84. package/src/components/editor-2d/renderers/floorplan-annotation-layout.test.ts +285 -0
  85. package/src/components/editor-2d/renderers/floorplan-annotation-layout.ts +516 -0
  86. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.test.tsx +236 -0
  87. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +618 -0
  88. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +369 -0
  89. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +431 -32
  90. package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +89 -0
  91. package/src/components/editor-2d/renderers/floorplan-label-angle.ts +106 -0
  92. package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +94 -26
  93. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +587 -0
  94. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +2280 -578
  95. package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +20 -10
  96. package/src/components/editor-2d/renderers/floorplan-voronoi-layer.tsx +128 -0
  97. package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +425 -61
  98. package/src/components/systems/ceiling/ceiling-system.tsx +97 -4
  99. package/src/components/systems/roof/roof-edit-system.tsx +1768 -13
  100. package/src/components/systems/selection-affordance-manager.tsx +38 -0
  101. package/src/components/systems/zone/zone-label-editor-system.tsx +14 -1
  102. package/src/components/systems/zone/zone-system.tsx +42 -13
  103. package/src/components/tools/elevator/elevator-tool.tsx +34 -8
  104. package/src/components/tools/elevator/move-elevator-tool.tsx +13 -2
  105. package/src/components/tools/fence/fence-drafting.ts +107 -8
  106. package/src/components/tools/item/move-tool.tsx +17 -13
  107. package/src/components/tools/item/placement-math.test.ts +13 -1
  108. package/src/components/tools/item/placement-math.ts +28 -2
  109. package/src/components/tools/item/placement-strategies.ts +246 -5
  110. package/src/components/tools/item/placement-types.ts +13 -1
  111. package/src/components/tools/item/use-draft-node.ts +146 -57
  112. package/src/components/tools/item/use-placement-coordinator.tsx +813 -125
  113. package/src/components/tools/registry/move-registry-node-tool.tsx +636 -103
  114. package/src/components/tools/roof/roof-tool.tsx +336 -94
  115. package/src/components/tools/select/box-select-state.ts +18 -7
  116. package/src/components/tools/select/box-select-tool.tsx +176 -40
  117. package/src/components/tools/select/marquee-geometry.test.ts +161 -0
  118. package/src/components/tools/select/marquee-geometry.ts +155 -0
  119. package/src/components/tools/select/plane-box-select-tool.tsx +1 -8
  120. package/src/components/tools/select/select-candidates.ts +1 -1
  121. package/src/components/tools/shared/cursor-sphere.tsx +62 -26
  122. package/src/components/tools/shared/drag-bounding-box.tsx +28 -4
  123. package/src/components/tools/shared/facing-indicator.tsx +87 -0
  124. package/src/components/tools/shared/facing-pose-indicator.tsx +54 -0
  125. package/src/components/tools/shared/floor-stack-preview.ts +4 -0
  126. package/src/components/tools/shared/horizontal-construction-plane.ts +123 -0
  127. package/src/components/tools/shared/placement-box.tsx +183 -1
  128. package/src/components/tools/shared/pointer-support-cap.test.ts +98 -0
  129. package/src/components/tools/shared/pointer-support-cap.ts +273 -0
  130. package/src/components/tools/shared/polygon-editor.tsx +276 -35
  131. package/src/components/tools/site/site-boundary-editor.tsx +101 -42
  132. package/src/components/tools/site/stroke-pointer-ownership.test.ts +91 -0
  133. package/src/components/tools/site/stroke-pointer-ownership.ts +57 -0
  134. package/src/components/tools/site/terrain-brush-cursor.tsx +247 -0
  135. package/src/components/tools/site/terrain-sculpt-grid.tsx +192 -0
  136. package/src/components/tools/site/terrain-sculpt-tool.tsx +356 -0
  137. package/src/components/tools/stair/stair-click-guard.test.ts +58 -0
  138. package/src/components/tools/stair/stair-click-guard.ts +72 -0
  139. package/src/components/tools/stair/stair-defaults.ts +1 -0
  140. package/src/components/tools/stair/stair-tool.tsx +164 -42
  141. package/src/components/tools/tool-manager.tsx +149 -30
  142. package/src/components/tools/wall/wall-drafting.test.ts +447 -0
  143. package/src/components/tools/wall/wall-drafting.ts +304 -108
  144. package/src/components/tools/wall/wall-snap-geometry.test.ts +52 -0
  145. package/src/components/tools/wall/wall-snap-geometry.ts +137 -9
  146. package/src/components/tools/zone/zone-boundary-editor.tsx +5 -1
  147. package/src/components/tools/zone/zone-tool.tsx +98 -88
  148. package/src/components/ui/action-menu/camera-actions.tsx +24 -17
  149. package/src/components/ui/action-menu/control-modes.tsx +44 -94
  150. package/src/components/ui/action-menu/furnish-tools.tsx +5 -5
  151. package/src/components/ui/action-menu/index.tsx +2 -4
  152. package/src/components/ui/action-menu/measurement-control.tsx +267 -0
  153. package/src/components/ui/action-menu/structure-tools.tsx +22 -13
  154. package/src/components/ui/action-menu/view-toggles.tsx +42 -90
  155. package/src/components/ui/command-palette/editor-commands.tsx +16 -3
  156. package/src/components/ui/command-palette/index.tsx +4 -3
  157. package/src/components/ui/controls/material-paint-panel.tsx +93 -18
  158. package/src/components/ui/controls/material-picker.tsx +162 -152
  159. package/src/components/ui/controls/material-properties-editor.tsx +108 -0
  160. package/src/components/ui/controls/metric-control.tsx +118 -44
  161. package/src/components/ui/controls/scene-material-list.tsx +247 -0
  162. package/src/components/ui/controls/slider-control.tsx +88 -38
  163. package/src/components/ui/controls/terrain-sculpt-panel.tsx +193 -0
  164. package/src/components/ui/floating-level-selector.tsx +74 -20
  165. package/src/components/ui/helpers/building-helper.tsx +10 -23
  166. package/src/components/ui/helpers/contextual-helper-panel.tsx +428 -0
  167. package/src/components/ui/helpers/helper-manager.tsx +273 -14
  168. package/src/components/ui/helpers/item-helper.tsx +28 -32
  169. package/src/components/ui/helpers/registered-tool-helper.tsx +45 -11
  170. package/src/components/ui/helpers/roof-helper.tsx +10 -12
  171. package/src/components/ui/icon-ref.tsx +47 -0
  172. package/src/components/ui/item-catalog/catalog-items.tsx +39 -1
  173. package/src/components/ui/item-catalog/item-catalog.tsx +13 -36
  174. package/src/components/ui/level-duplicate-dialog.tsx +1 -1
  175. package/src/components/ui/panels/multi-selection-panel.tsx +57 -0
  176. package/src/components/ui/panels/node-display.ts +17 -17
  177. package/src/components/ui/panels/panel-manager.tsx +36 -14
  178. package/src/components/ui/panels/panel-wrapper.tsx +24 -3
  179. package/src/components/ui/panels/parametric-inspector.tsx +19 -4
  180. package/src/components/ui/panels/reference-panel.tsx +54 -19
  181. package/src/components/ui/panels/selection-breakdown.test.ts +24 -0
  182. package/src/components/ui/panels/selection-breakdown.ts +20 -0
  183. package/src/components/ui/primitives/shortcut-token.tsx +39 -3
  184. package/src/components/ui/primitives/sidebar.tsx +1 -0
  185. package/src/components/ui/sidebar/app-sidebar.tsx +5 -1
  186. package/src/components/ui/sidebar/icon-rail.tsx +50 -31
  187. package/src/components/ui/sidebar/panels/plugins-panel.tsx +218 -0
  188. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +82 -41
  189. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +76 -6
  190. package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +12 -4
  191. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +2 -1
  192. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -3
  193. package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +10 -7
  194. package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -1
  195. package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +7 -2
  196. package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +10 -7
  197. package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +1 -1
  198. package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +1 -1
  199. package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +10 -7
  200. package/src/components/ui/sidebar/panels/site-panel/index.tsx +42 -17
  201. package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +31 -17
  202. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +1 -1
  203. package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +72 -25
  204. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +7 -3
  205. package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +12 -8
  206. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +4 -3
  207. package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +10 -7
  208. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -1
  209. package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +2 -2
  210. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +44 -3
  211. package/src/components/ui/sidebar/panels/site-panel/tree-structure.ts +36 -0
  212. package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -1
  213. package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +7 -2
  214. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +3 -3
  215. package/src/components/ui/sidebar/tab-bar.tsx +42 -28
  216. package/src/components/ui/sidebar/use-plugin-panels.tsx +132 -0
  217. package/src/components/ui/snap-target-badge.test.tsx +40 -0
  218. package/src/components/ui/snap-target-badge.tsx +88 -0
  219. package/src/components/viewer/viewer-controls-bar.tsx +466 -0
  220. package/src/components/viewer/viewer-scene-header.tsx +235 -0
  221. package/src/components/viewer-overlay.tsx +36 -596
  222. package/src/components/viewer-zone-system.tsx +6 -1
  223. package/src/components/walkthrough-hud.tsx +111 -0
  224. package/src/hooks/use-auto-save.test.ts +14 -0
  225. package/src/hooks/use-auto-save.ts +54 -7
  226. package/src/hooks/use-ceiling-events.ts +3 -2
  227. package/src/hooks/use-drag-action.ts +4 -1
  228. package/src/hooks/use-grid-events.ts +24 -0
  229. package/src/hooks/use-keyboard.ts +438 -70
  230. package/src/index.tsx +355 -9
  231. package/src/lib/active-placement-surface.test.ts +24 -0
  232. package/src/lib/active-placement-surface.ts +42 -0
  233. package/src/lib/camera-pose.test.ts +237 -0
  234. package/src/lib/camera-pose.ts +172 -0
  235. package/src/lib/ceiling-plan-snap.ts +24 -0
  236. package/src/lib/contextual-help.test.ts +112 -0
  237. package/src/lib/contextual-help.ts +144 -0
  238. package/src/lib/continuation.ts +64 -0
  239. package/src/lib/direct-manipulation.test.ts +275 -0
  240. package/src/lib/direct-manipulation.ts +118 -0
  241. package/src/lib/door-interaction.ts +1 -1
  242. package/src/lib/editor-api.ts +23 -35
  243. package/src/lib/elevation-guides.test.ts +107 -0
  244. package/src/lib/elevation-guides.ts +236 -0
  245. package/src/lib/floorplan/annotation-visibility.test.ts +327 -0
  246. package/src/lib/floorplan/annotation-visibility.ts +133 -0
  247. package/src/lib/floorplan/apply-alignment.test.ts +25 -0
  248. package/src/lib/floorplan/apply-alignment.ts +18 -12
  249. package/src/lib/floorplan/drawing-coordination.test.ts +46 -0
  250. package/src/lib/floorplan/drawing-coordination.ts +13 -0
  251. package/src/lib/floorplan/floorplan-export.test.ts +319 -0
  252. package/src/lib/floorplan/floorplan-export.tsx +978 -0
  253. package/src/lib/floorplan/floorplan-extension.test.ts +43 -0
  254. package/src/lib/floorplan/floorplan-extension.ts +181 -0
  255. package/src/lib/floorplan/floorplan-mode.test.ts +120 -0
  256. package/src/lib/floorplan/floorplan-mode.ts +73 -0
  257. package/src/lib/floorplan/floorplan-pdfkit-document.ts +194 -0
  258. package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +107 -0
  259. package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +680 -0
  260. package/src/lib/floorplan/geometry.ts +52 -0
  261. package/src/lib/floorplan/index.ts +3 -0
  262. package/src/lib/floorplan/items.ts +5 -2
  263. package/src/lib/floorplan/plan-coords.ts +21 -0
  264. package/src/lib/fresh-planar-placement.test.ts +283 -3
  265. package/src/lib/fresh-planar-placement.ts +85 -1
  266. package/src/lib/glb-export.test.ts +556 -0
  267. package/src/lib/glb-export.ts +1071 -0
  268. package/src/lib/ground-surface.ts +95 -0
  269. package/src/lib/history.test.ts +96 -0
  270. package/src/lib/history.ts +31 -0
  271. package/src/lib/interaction/hot-set.test.ts +133 -0
  272. package/src/lib/interaction/hot-set.ts +67 -0
  273. package/src/lib/interaction/overlay-policy.test.ts +52 -0
  274. package/src/lib/interaction/overlay-policy.ts +59 -0
  275. package/src/lib/interaction/scope.ts +243 -0
  276. package/src/lib/level-duplication.test.ts +2 -1
  277. package/src/lib/level-selection.ts +2 -2
  278. package/src/lib/material-paint.ts +36 -49
  279. package/src/lib/measurement-kind.test.ts +30 -0
  280. package/src/lib/measurement-kind.ts +19 -0
  281. package/src/lib/measurement-label.test.ts +47 -0
  282. package/src/lib/measurement-label.ts +68 -0
  283. package/src/lib/measurement-parser.ts +116 -0
  284. package/src/lib/measurements.test.ts +188 -0
  285. package/src/lib/measurements.ts +210 -0
  286. package/src/lib/paint-scope.test.ts +454 -0
  287. package/src/lib/paint-scope.ts +461 -0
  288. package/src/lib/placement-drag-release.ts +23 -0
  289. package/src/lib/planar-cursor-placement.test.ts +57 -0
  290. package/src/lib/plugin-panels.test.ts +19 -0
  291. package/src/lib/plugin-panels.ts +91 -0
  292. package/src/lib/quick-action-feedback.ts +33 -0
  293. package/src/lib/quick-action-nodes.test.ts +94 -0
  294. package/src/lib/quick-action-nodes.ts +47 -0
  295. package/src/lib/quick-measurement.test.ts +77 -0
  296. package/src/lib/quick-measurement.ts +109 -0
  297. package/src/lib/roof-duplication.ts +6 -6
  298. package/src/lib/roof-hover-outline-proxy.ts +22 -0
  299. package/src/lib/roof-wall-hit.ts +164 -0
  300. package/src/lib/scene-clipboard.test.ts +325 -0
  301. package/src/lib/scene-clipboard.ts +310 -21
  302. package/src/lib/scene.ts +28 -5
  303. package/src/lib/selection-routing.test.ts +313 -0
  304. package/src/lib/selection-routing.ts +180 -0
  305. package/src/lib/sfx/index.ts +1 -0
  306. package/src/lib/sfx/movement-tick.test.ts +65 -0
  307. package/src/lib/sfx/movement-tick.ts +18 -0
  308. package/src/lib/sfx-bus.ts +82 -14
  309. package/src/lib/sfx-player.test.ts +266 -0
  310. package/src/lib/sfx-player.ts +262 -69
  311. package/src/lib/site-boundary.test.ts +53 -0
  312. package/src/lib/site-boundary.ts +27 -0
  313. package/src/lib/slab-plan-snap.test.ts +93 -0
  314. package/src/lib/slab-plan-snap.ts +108 -0
  315. package/src/lib/snapping-mode.test.ts +165 -0
  316. package/src/lib/snapping-mode.ts +191 -0
  317. package/src/lib/stair-duplication.test.ts +103 -0
  318. package/src/lib/stair-duplication.ts +58 -86
  319. package/src/lib/stair-levels.ts +2 -0
  320. package/src/lib/surface-plan-snap.test.ts +71 -0
  321. package/src/lib/surface-plan-snap.ts +256 -0
  322. package/src/lib/terrain-sculpt.test.ts +429 -0
  323. package/src/lib/terrain-sculpt.ts +301 -0
  324. package/src/lib/terrain-verb-color.test.ts +51 -0
  325. package/src/lib/terrain-verb-color.ts +58 -0
  326. package/src/lib/touch-gesture-priority.test.ts +45 -0
  327. package/src/lib/touch-gesture-priority.ts +30 -0
  328. package/src/lib/use-linear-display.ts +40 -0
  329. package/src/lib/window-interaction.ts +1 -1
  330. package/src/lib/world-grid-snap.ts +37 -5
  331. package/src/store/camera-pose-store.test.ts +29 -0
  332. package/src/store/camera-pose-store.ts +27 -0
  333. package/src/store/live-draft-preview-stores.test.ts +136 -0
  334. package/src/store/navigation-sync-pose-store.test.ts +34 -0
  335. package/src/store/navigation-sync-pose-store.ts +27 -0
  336. package/src/store/terrain-sculpt-mode.test.ts +223 -0
  337. package/src/store/use-delete-confirmation.ts +27 -0
  338. package/src/store/use-direct-manipulation-feedback.ts +20 -0
  339. package/src/store/use-drawing-view.test.ts +23 -0
  340. package/src/store/use-drawing-view.ts +84 -0
  341. package/src/store/use-editor.tsx +665 -126
  342. package/src/store/use-elevation-guides.ts +40 -0
  343. package/src/store/use-facing-pose.ts +44 -0
  344. package/src/store/use-fence-curve-draft.ts +51 -0
  345. package/src/store/use-first-person-hud.ts +40 -0
  346. package/src/store/use-floorplan-annotation-visibility.ts +60 -0
  347. package/src/store/use-floorplan-draft-preview.ts +151 -0
  348. package/src/store/use-floorplan-marquee.ts +50 -0
  349. package/src/store/use-floorplan-mode.ts +110 -0
  350. package/src/store/use-interaction-scope.test.ts +206 -0
  351. package/src/store/use-interaction-scope.ts +140 -0
  352. package/src/store/use-measurement-draft.test.ts +559 -0
  353. package/src/store/use-measurement-draft.ts +570 -0
  354. package/src/store/use-opening-guides.ts +41 -0
  355. package/src/store/use-path-draft-preview.ts +90 -0
  356. package/src/store/use-placement-preview.ts +11 -3
  357. package/src/store/use-quick-measurement-hud.test.ts +53 -0
  358. package/src/store/use-quick-measurement-hud.ts +77 -0
  359. package/src/store/use-segment-draft-chain.ts +28 -0
  360. package/src/store/use-stair-build-preview.ts +53 -0
  361. package/src/store/use-wall-snap-indicator.ts +2 -0
  362. package/src/components/editor/first-person/bvh-ecctrl.tsx +0 -860
  363. package/src/components/editor/group-move-handle.tsx +0 -316
  364. package/src/components/ui/panels/paint-panel.tsx +0 -163
  365. package/src/lib/floorplan/selection-tool.ts +0 -271
  366. package/src/lib/level-name.ts +0 -11
@@ -1,21 +1,40 @@
1
1
  import {
2
+ type AnyNodeDefinition,
2
3
  type AnyNodeId,
3
4
  type BuildingNode,
4
5
  type CeilingNode,
6
+ type FenceNode,
5
7
  nodeRegistry,
6
8
  type SlabNode,
7
9
  useScene,
10
+ type WallNode,
8
11
  } from '@pascal-app/core'
9
12
  import { useViewer } from '@pascal-app/viewer'
10
- import { type ComponentType, lazy, Suspense } from 'react'
13
+ import { type ComponentType, lazy, Suspense, useMemo } from 'react'
14
+ import { siteBoundaryHandlesEnabled } from '../../lib/site-boundary'
11
15
  import useEditor, { type Phase, type Tool } from '../../store/use-editor'
16
+ import {
17
+ useControlPointReshape,
18
+ useEditingHole,
19
+ useEndpointReshape,
20
+ useIsCurveReshape,
21
+ useIsFloorplanDrivenReshape,
22
+ useIsToolDrivenReshape,
23
+ useMovingNode,
24
+ useReshapingNode,
25
+ useTangentReshape,
26
+ } from '../../store/use-interaction-scope'
12
27
  import { Alignment3DGuideLayer } from '../editor/alignment-3d-guide-layer'
28
+ import { Elevation3DGuideLayer } from '../editor/elevation-3d-guide-layer'
29
+ import { OpeningGuides3DLayer } from '../editor/opening-guides-3d-layer'
13
30
  import { WallSnapBeaconLayer } from '../editor/wall-snap-beacon-layer'
14
31
  import { ElevatorTool } from './elevator/elevator-tool'
15
32
  import { MoveTool } from './item/move-tool'
16
33
  import { RoofTool } from './roof/roof-tool'
17
34
  import { getRegistryAffordanceTool } from './shared/affordance-dispatch'
35
+ import { FacingPoseIndicator } from './shared/facing-pose-indicator'
18
36
  import { SiteBoundaryEditor } from './site/site-boundary-editor'
37
+ import { TerrainSculptTool } from './site/terrain-sculpt-tool'
19
38
  import { StairTool } from './stair/stair-tool'
20
39
  import { ZoneBoundaryEditor } from './zone/zone-boundary-editor'
21
40
  import { ZoneTool } from './zone/zone-tool'
@@ -23,6 +42,29 @@ import { ZoneTool } from './zone/zone-tool'
23
42
  // Cache lazy tool components keyed by their loader so React.lazy isn't
24
43
  // re-invoked across renders.
25
44
  const lazyToolCache = new WeakMap<() => Promise<unknown>, ComponentType>()
45
+ const registryToolPreloadCache = new WeakMap<AnyNodeDefinition, Promise<void>>()
46
+
47
+ export function preloadRegistryToolModules(tool: string | null): Promise<void> {
48
+ if (!tool) return Promise.resolve()
49
+ const def = nodeRegistry.get(tool)
50
+ if (!def) return Promise.resolve()
51
+ const cached = registryToolPreloadCache.get(def)
52
+ if (cached) return cached
53
+
54
+ const loaders: Array<() => Promise<unknown>> = []
55
+ if (def.tool) loaders.push(def.tool)
56
+ if (def.preview) loaders.push(def.preview)
57
+ if (def.renderer?.kind === 'parametric') loaders.push(def.renderer.module)
58
+ if (def.system) loaders.push(def.system.module)
59
+ if (def.parametrics?.customPanel) loaders.push(def.parametrics.customPanel)
60
+ if (def.parametrics?.trailingSection) loaders.push(def.parametrics.trailingSection)
61
+ const moveTool = def.affordanceTools?.move
62
+ if (moveTool) loaders.push(moveTool)
63
+
64
+ const preload = Promise.allSettled(loaders.map((loader) => loader())).then(() => undefined)
65
+ registryToolPreloadCache.set(def, preload)
66
+ return preload
67
+ }
26
68
 
27
69
  function getRegistryTool(tool: Tool | null): ComponentType | null {
28
70
  if (!tool) return null
@@ -30,7 +72,13 @@ function getRegistryTool(tool: Tool | null): ComponentType | null {
30
72
  if (!def?.tool) return null
31
73
  const cached = lazyToolCache.get(def.tool)
32
74
  if (cached) return cached
33
- const Comp = lazy(def.tool as () => Promise<{ default: ComponentType }>)
75
+ const Comp = lazy(async () => {
76
+ // Placement can only begin once the node's preview, committed renderer,
77
+ // inspector, and move contribution are warm. This keeps the click itself
78
+ // synchronous even under Next.js dev-time on-demand compilation.
79
+ await preloadRegistryToolModules(tool)
80
+ return def.tool!() as Promise<{ default: ComponentType }>
81
+ })
34
82
  lazyToolCache.set(def.tool, Comp)
35
83
  return Comp
36
84
  }
@@ -54,12 +102,39 @@ export const ToolManager: React.FC = () => {
54
102
  const phase = useEditor((state) => state.phase)
55
103
  const mode = useEditor((state) => state.mode)
56
104
  const tool = useEditor((state) => state.tool)
57
- const movingNode = useEditor((state) => state.movingNode)
58
- const movingWallEndpoint = useEditor((state) => state.movingWallEndpoint)
59
- const movingFenceEndpoint = useEditor((state) => state.movingFenceEndpoint)
60
- const curvingWall = useEditor((state) => state.curvingWall)
61
- const curvingFence = useEditor((state) => state.curvingFence)
62
- const editingHole = useEditor((state) => state.editingHole)
105
+ const movingNode = useMovingNode()
106
+ const movingNodeOrigin = useEditor((state) => state.movingNodeOrigin)
107
+ const endpointReshape = useEndpointReshape()
108
+ const controlPointReshape = useControlPointReshape()
109
+ const tangentReshape = useTangentReshape()
110
+ const isCurveReshape = useIsCurveReshape()
111
+ const isToolDrivenReshape = useIsToolDrivenReshape()
112
+ const isFloorplanDrivenReshape = useIsFloorplanDrivenReshape()
113
+ const reshapingNode = useReshapingNode()
114
+ // The endpoint affordance tool's `target` is kind-specific
115
+ // (`{ wall | fence, endpoint }`); rebuild it from the (frozen) reshaped node +
116
+ // the scope's endpoint. Memoised so it stays referentially stable across the
117
+ // scene-write re-renders during the drag — otherwise a fresh object each frame
118
+ // re-fires the tool's setup effect (endpoint drag would loop / freeze).
119
+ const endpointTarget = useMemo(() => {
120
+ if (!(endpointReshape && reshapingNode)) return null
121
+ return reshapingNode.type === 'fence'
122
+ ? { fence: reshapingNode as FenceNode, endpoint: endpointReshape.endpoint }
123
+ : { wall: reshapingNode as WallNode, endpoint: endpointReshape.endpoint }
124
+ }, [endpointReshape, reshapingNode])
125
+ const controlPointTarget = useMemo(() => {
126
+ if (!(controlPointReshape && reshapingNode?.type === 'fence')) return null
127
+ return { fence: reshapingNode as FenceNode, index: controlPointReshape.index }
128
+ }, [controlPointReshape, reshapingNode])
129
+ const tangentTarget = useMemo(() => {
130
+ if (!(tangentReshape && reshapingNode?.type === 'fence')) return null
131
+ return {
132
+ fence: reshapingNode as FenceNode,
133
+ index: tangentReshape.index,
134
+ side: tangentReshape.side,
135
+ }
136
+ }, [reshapingNode, tangentReshape])
137
+ const editingHole = useEditingHole()
63
138
  const selectedZoneId = useViewer((state) => state.selection.zoneId)
64
139
  const selectedIds = useViewer((state) => state.selection.selectedIds)
65
140
  const buildingId = useViewer((state) => state.selection.buildingId)
@@ -90,15 +165,27 @@ export const ToolManager: React.FC = () => {
90
165
  | CeilingNode['id']
91
166
  | undefined
92
167
 
93
- // Keep the site vertex flags available in select mode; the editor component
94
- // switches to full polygon editing only after a flag activates site mode.
95
- const showSiteBoundaryEditor = phase === 'site' || mode === 'select'
168
+ // Site boundary handles normally share one 2D/3D rule. Sculpt is the deliberate
169
+ // 3D exception: the brush only owns this canvas, where PolygonEditor can hand
170
+ // off its pointer before a boundary drag starts.
171
+ const sculpting = mode === 'terrain-sculpt'
172
+ // Sculpt keeps the 3D property controls visible. PolygonEditor marks its
173
+ // pointer before the canvas-level brush listener runs, and activating one
174
+ // exits sculpt mode before starting the boundary drag.
175
+ const showSiteBoundaryEditor = sculpting || siteBoundaryHandlesEnabled({ mode, phase })
176
+
177
+ // A multi-selection is manipulated as one rigid group (drag / R / T), so
178
+ // per-node reshape chrome — the slab / ceiling boundary editors' vertex and
179
+ // edge handles — mounts only for a sole selection.
180
+ const isSoleSelection = selectedIds.length === 1
96
181
 
97
182
  // Show slab boundary editor when in structure/select mode with a slab selected (but not editing a hole)
98
183
  const showSlabBoundaryEditor =
99
184
  phase === 'structure' &&
100
185
  mode === 'select' &&
186
+ isSoleSelection &&
101
187
  selectedSlabId !== undefined &&
188
+ !isFloorplanDrivenReshape &&
102
189
  !editingSlabHoleIsManual
103
190
 
104
191
  // Show slab hole editor when editing a hole on the selected slab
@@ -106,20 +193,24 @@ export const ToolManager: React.FC = () => {
106
193
  selectedSlabId !== undefined &&
107
194
  editingHole !== null &&
108
195
  editingHole.nodeId === selectedSlabId &&
196
+ !isFloorplanDrivenReshape &&
109
197
  editingSlabHoleIsManual
110
198
 
111
199
  // Show ceiling boundary editor when in structure/select mode with a ceiling selected (but not editing a hole)
112
200
  const showCeilingBoundaryEditor =
113
201
  phase === 'structure' &&
114
202
  mode === 'select' &&
203
+ isSoleSelection &&
115
204
  selectedCeilingId !== undefined &&
205
+ !isFloorplanDrivenReshape &&
116
206
  (!editingHole || editingHole.nodeId !== selectedCeilingId)
117
207
 
118
208
  // Show ceiling hole editor when editing a hole on the selected ceiling
119
209
  const showCeilingHoleEditor =
120
210
  selectedCeilingId !== undefined &&
121
211
  editingHole !== null &&
122
- editingHole.nodeId === selectedCeilingId
212
+ editingHole.nodeId === selectedCeilingId &&
213
+ !isFloorplanDrivenReshape
123
214
 
124
215
  // Show zone boundary editor when in structure/select mode with a zone selected
125
216
  // Hide when editing a slab or ceiling to avoid overlapping handles
@@ -127,12 +218,23 @@ export const ToolManager: React.FC = () => {
127
218
  phase === 'structure' &&
128
219
  mode === 'select' &&
129
220
  selectedZoneId !== null &&
221
+ !isFloorplanDrivenReshape &&
130
222
  !showSlabBoundaryEditor &&
131
223
  !showCeilingBoundaryEditor
132
224
 
133
225
  // Show build tools when in build mode
134
226
  const showBuildTool = mode === 'build' && tool !== null
135
227
 
228
+ // A move initiated from the 2D floor-plan (orange move-dot) is owned end-to-
229
+ // end by `FloorplanRegistryMoveOverlay`, which marks the origin `'2d'` at
230
+ // dot-down. Mounting the 3D affordance mover alongside it would adopt the
231
+ // same node and, on its unmount, restore the adopt-time position — snapping
232
+ // the committed 2D move back to its start. Gate the 3D mover off for 2D moves
233
+ // (the scene writes the overlay makes still mirror into the 3D view). A
234
+ // 3D-initiated move leaves the origin null until its own commit, so this only
235
+ // suppresses the 3D tool for genuinely 2D-owned moves.
236
+ const showMover = movingNode != null && movingNodeOrigin !== '2d'
237
+
136
238
  // Registry-first: if the active tool's kind has a NodeDefinition with a
137
239
  // tool contribution, the registry-driven tool takes over.
138
240
  const RegistryToolComponent = showBuildTool ? getRegistryTool(tool) : null
@@ -162,7 +264,11 @@ export const ToolManager: React.FC = () => {
162
264
  <>
163
265
  {/* World-space tools: site boundary and building movement operate in world coordinates */}
164
266
  {showSiteBoundaryEditor && <SiteBoundaryEditor />}
165
- {movingNode?.type === 'building' && (
267
+ {/* Terrain sculpting is a mode rather than a `tools[phase][tool]` entry —
268
+ it places no node — so it gets its own gate here. World-space, because
269
+ the ground is not building-local. */}
270
+ {sculpting && <TerrainSculptTool />}
271
+ {showMover && movingNode?.type === 'building' && (
166
272
  <MoveTool onNodeMoved={handlePlacedNodeSelected} onSpawnMoved={handlePlacedNodeSelected} />
167
273
  )}
168
274
 
@@ -216,49 +322,52 @@ export const ToolManager: React.FC = () => {
216
322
  </Suspense>
217
323
  ) : null
218
324
  })()}
219
- {movingWallEndpoint &&
325
+ {isToolDrivenReshape &&
326
+ endpointTarget &&
327
+ reshapingNode &&
220
328
  (() => {
221
329
  const RegistryAffordance = getRegistryAffordanceTool(
222
- movingWallEndpoint.wall.type,
330
+ reshapingNode.type,
223
331
  'move-endpoint',
224
332
  )
225
333
  return RegistryAffordance ? (
226
334
  <Suspense fallback={null}>
227
- <RegistryAffordance target={movingWallEndpoint} />
335
+ <RegistryAffordance target={endpointTarget} />
228
336
  </Suspense>
229
337
  ) : null
230
338
  })()}
231
- {movingFenceEndpoint &&
339
+ {isToolDrivenReshape &&
340
+ isCurveReshape &&
341
+ reshapingNode &&
232
342
  (() => {
233
- const RegistryAffordance = getRegistryAffordanceTool(
234
- movingFenceEndpoint.fence.type,
235
- 'move-endpoint',
236
- )
343
+ const RegistryAffordance = getRegistryAffordanceTool(reshapingNode.type, 'curve')
237
344
  return RegistryAffordance ? (
238
345
  <Suspense fallback={null}>
239
- <RegistryAffordance target={movingFenceEndpoint} />
346
+ <RegistryAffordance node={reshapingNode} />
240
347
  </Suspense>
241
348
  ) : null
242
349
  })()}
243
- {curvingWall &&
350
+ {isToolDrivenReshape &&
351
+ controlPointTarget &&
244
352
  (() => {
245
- const Registry = getRegistryAffordanceTool(curvingWall.type, 'curve')
246
- return Registry ? (
353
+ const RegistryAffordance = getRegistryAffordanceTool('fence', 'move-control-point')
354
+ return RegistryAffordance ? (
247
355
  <Suspense fallback={null}>
248
- <Registry node={curvingWall} />
356
+ <RegistryAffordance target={controlPointTarget} />
249
357
  </Suspense>
250
358
  ) : null
251
359
  })()}
252
- {curvingFence &&
360
+ {isToolDrivenReshape &&
361
+ tangentTarget &&
253
362
  (() => {
254
- const RegistryAffordance = getRegistryAffordanceTool(curvingFence.type, 'curve')
363
+ const RegistryAffordance = getRegistryAffordanceTool('fence', 'move-tangent')
255
364
  return RegistryAffordance ? (
256
365
  <Suspense fallback={null}>
257
- <RegistryAffordance node={curvingFence} />
366
+ <RegistryAffordance target={tangentTarget} />
258
367
  </Suspense>
259
368
  ) : null
260
369
  })()}
261
- {movingNode && movingNode.type !== 'building' && (
370
+ {showMover && movingNode.type !== 'building' && (
262
371
  <MoveTool
263
372
  onNodeMoved={handlePlacedNodeSelected}
264
373
  onSpawnMoved={handlePlacedNodeSelected}
@@ -283,6 +392,16 @@ export const ToolManager: React.FC = () => {
283
392
  tools above. Lives inside the building-local group so the
284
393
  building-local guide coords render at the right world position. */}
285
394
  <Alignment3DGuideLayer />
395
+ {/* The one forward-facing triangle renderer. Placement/move tools
396
+ publish their ghost pose to `useFacingPose`; this draws it. Mounted
397
+ here so it shares the building-local frame the tools publish in. */}
398
+ <FacingPoseIndicator />
399
+ {/* Wall-plane proximity / sill / equal-spacing guides for openings,
400
+ published by the door/window move tools in the same world frame. */}
401
+ <OpeningGuides3DLayer />
402
+ {/* Structural Y-datum feedback for slab, ceiling, wall, and fence
403
+ elevation handles. Ephemeral editor chrome; never scene data. */}
404
+ <Elevation3DGuideLayer />
286
405
  {/* "Magnetic" beacon at the active wall-draft snap point. */}
287
406
  <WallSnapBeaconLayer />
288
407
  </group>