@pascal-app/editor 1.0.3 → 1.0.4

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 (440) hide show
  1. package/package.json +11 -9
  2. package/src/components/editor/action-menu-button.tsx +83 -0
  3. package/src/components/editor/bake-thumbnail.tsx +315 -4
  4. package/src/components/editor/capture-level-picker.tsx +72 -0
  5. package/src/components/editor/custom-camera-controls.tsx +202 -57
  6. package/src/components/editor/delete-confirmation-dialog.tsx +297 -7
  7. package/src/components/editor/duplicate-node.ts +244 -0
  8. package/src/components/editor/editor-layout-mobile.tsx +36 -25
  9. package/src/components/editor/editor-layout-v2.tsx +131 -58
  10. package/src/components/editor/export-manager.tsx +34 -8
  11. package/src/components/editor/first-person/build-collider-world.test.ts +51 -0
  12. package/src/components/editor/first-person/build-collider-world.ts +6 -2
  13. package/src/components/editor/first-person/mechanism-targets.test.ts +75 -0
  14. package/src/components/editor/first-person/mechanism-targets.ts +36 -0
  15. package/src/components/editor/first-person-controls.tsx +205 -2
  16. package/src/components/editor/floating-action-menu.tsx +63 -207
  17. package/src/components/editor/floor-aware-camera-controls.test.ts +442 -0
  18. package/src/components/editor/floor-aware-camera-controls.ts +324 -0
  19. package/src/components/editor/floor-openings-overlay.tsx +243 -0
  20. package/src/components/editor/floor-region-controls.tsx +846 -0
  21. package/src/components/editor/floor-region-webgpu-materials.test.ts +12 -0
  22. package/src/components/editor/floor-surface.test.ts +165 -0
  23. package/src/components/editor/floor-surface.ts +182 -0
  24. package/src/components/editor/floorplan-background-selection.test.ts +11 -2
  25. package/src/components/editor/floorplan-background-selection.ts +1 -3
  26. package/src/components/editor/floorplan-panel.tsx +344 -342
  27. package/src/components/editor/footprint-height-handle.tsx +204 -0
  28. package/src/components/editor/group-actions.ts +54 -2
  29. package/src/components/editor/handles/handle-hit-priority.test.tsx +63 -0
  30. package/src/components/editor/handles/handle-hit-priority.tsx +37 -0
  31. package/src/components/editor/handles/swallow-next-click.test.ts +28 -0
  32. package/src/components/editor/handles/use-handle-drag.ts +269 -197
  33. package/src/components/editor/index.tsx +294 -169
  34. package/src/components/editor/join-walls-pill.tsx +44 -0
  35. package/src/components/editor/node-action-menu.tsx +68 -70
  36. package/src/components/editor/node-arrow-handles.tsx +25 -6
  37. package/src/components/editor/open-wall-ends-3d-layer.tsx +273 -0
  38. package/src/components/editor/open-wall-ends-hint.tsx +89 -0
  39. package/src/components/editor/quick-measurement-hud.tsx +3 -2
  40. package/src/components/editor/registry-action-contributions.test.tsx +63 -0
  41. package/src/components/editor/registry-action-contributions.tsx +80 -1
  42. package/src/components/editor/room-controls.tsx +847 -0
  43. package/src/components/editor/room-floor-highlight.tsx +128 -0
  44. package/src/components/editor/room-handles-2d.tsx +354 -0
  45. package/src/components/editor/room-handles.tsx +675 -0
  46. package/src/components/editor/room-highlight.tsx +225 -0
  47. package/src/components/editor/room-labels.test.ts +357 -0
  48. package/src/components/editor/room-labels.tsx +990 -0
  49. package/src/components/editor/room-transform-ghost.tsx +256 -0
  50. package/src/components/editor/room-webgpu-materials.test.ts +27 -0
  51. package/src/components/editor/selection-manager.tsx +451 -317
  52. package/src/components/editor/slab-hole-highlights.tsx +3 -0
  53. package/src/components/editor/snapshot-capture-overlay.tsx +4 -0
  54. package/src/components/editor/snapshot-capture.test.ts +52 -4
  55. package/src/components/editor/snapshot-capture.ts +30 -5
  56. package/src/components/editor/thumbnail-generator.test.ts +308 -0
  57. package/src/components/editor/thumbnail-generator.tsx +1035 -229
  58. package/src/components/editor/use-floorplan-background-placement.ts +31 -11
  59. package/src/components/editor/vector-edge-extractor.tsx +121 -0
  60. package/src/components/editor/views/host-view.tsx +61 -0
  61. package/src/components/editor/views/plugins-view.tsx +670 -0
  62. package/src/components/editor/views/scene-region.tsx +59 -0
  63. package/src/components/editor/views/use-editor-views.ts +57 -0
  64. package/src/components/editor/views/view-bar.tsx +220 -0
  65. package/src/components/editor/wall-justification.test.ts +140 -0
  66. package/src/components/editor/wall-move-side-handles.tsx +43 -142
  67. package/src/components/editor/wall-push-arrow.tsx +92 -0
  68. package/src/components/editor/wall-region-handles.tsx +509 -0
  69. package/src/components/editor/wall-snap-beacon-layer.tsx +3 -0
  70. package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +8 -4
  71. package/src/components/editor-2d/floorplan-open-wall-ends-layer.test.tsx +86 -0
  72. package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +290 -0
  73. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +11 -1
  74. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +113 -102
  75. package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.test.ts +156 -0
  76. package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +278 -0
  77. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +77 -0
  78. package/src/components/editor-2d/renderers/{floorplan-draft-wall-measurement.tsx → floorplan-draft-measurement.tsx} +8 -8
  79. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +4 -1
  80. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +38 -8
  81. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +291 -227
  82. package/src/components/editor-2d/svg-paths.ts +12 -15
  83. package/src/components/procedural-items/inspector.tsx +40 -15
  84. package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +97 -14
  85. package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +18 -18
  86. package/src/components/systems/zone/zone-system.test.ts +47 -0
  87. package/src/components/systems/zone/zone-system.tsx +29 -68
  88. package/src/components/tools/item/placement-history.test.tsx +474 -0
  89. package/src/components/tools/item/placement-strategies.ts +8 -4
  90. package/src/components/tools/item/use-draft-node.test.tsx +432 -1
  91. package/src/components/tools/item/use-draft-node.ts +166 -95
  92. package/src/components/tools/item/use-placement-coordinator.tsx +93 -46
  93. package/src/components/tools/paint-region/use-paint-tint.ts +37 -0
  94. package/src/components/tools/paint-region/wall-paint-region-tool.tsx +466 -0
  95. package/src/components/tools/registry/move-registry-node-tool.tsx +3 -1
  96. package/src/components/tools/select/box-select-tool.tsx +2 -12
  97. package/src/components/tools/select/plane-box-select-tool.test.tsx +79 -0
  98. package/src/components/tools/select/plane-box-select-tool.tsx +35 -22
  99. package/src/components/tools/select/select-candidates.test.ts +37 -4
  100. package/src/components/tools/select/select-candidates.ts +15 -11
  101. package/src/components/tools/shared/cursor-sphere.tsx +36 -12
  102. package/src/components/tools/site/site-boundary-editor.tsx +1 -2
  103. package/src/components/tools/stair/stair-defaults.ts +0 -3
  104. package/src/components/tools/stair/stair-tool.tsx +90 -42
  105. package/src/components/tools/tool-manager.tsx +46 -20
  106. package/src/components/tools/wall/wall-drafting.test.ts +19 -4
  107. package/src/components/tools/zone/zone-tool.tsx +4 -12
  108. package/src/components/ui/action-menu/action-button.tsx +3 -0
  109. package/src/components/ui/action-menu/camera-actions.tsx +2 -1
  110. package/src/components/ui/action-menu/control-modes.tsx +2 -35
  111. package/src/components/ui/action-menu/measurement-control.tsx +3 -10
  112. package/src/components/ui/build-panel/build-panel.tsx +235 -0
  113. package/src/components/ui/command-palette/editor-commands.tsx +4 -30
  114. package/src/components/ui/command-palette/index.tsx +5 -24
  115. package/src/components/ui/controls/finish-swatch.tsx +110 -0
  116. package/src/components/ui/controls/material-paint-panel.tsx +97 -26
  117. package/src/components/ui/controls/material-picker.tsx +4 -1
  118. package/src/components/ui/controls/scene-material-list.tsx +4 -32
  119. package/src/components/ui/controls/select-control.tsx +65 -0
  120. package/src/components/ui/floating-level-selector.tsx +9 -14
  121. package/src/components/ui/helpers/building-helper.tsx +4 -1
  122. package/src/components/ui/helpers/contextual-helper-panel.tsx +154 -70
  123. package/src/components/ui/helpers/helper-manager.test.tsx +69 -0
  124. package/src/components/ui/helpers/helper-manager.tsx +325 -14
  125. package/src/components/ui/helpers/item-helper.tsx +8 -0
  126. package/src/components/ui/helpers/registered-tool-helper.tsx +8 -1
  127. package/src/components/ui/icon-ref.tsx +45 -1
  128. package/src/components/ui/level-duplicate-dialog.tsx +1 -1
  129. package/src/components/ui/node-icon-image.tsx +16 -0
  130. package/src/components/ui/panels/collections/collections-popover.tsx +20 -14
  131. package/src/components/ui/panels/floor-foundation-panel.tsx +551 -0
  132. package/src/components/ui/panels/floor-opening-panel.tsx +143 -0
  133. package/src/components/ui/panels/mobile-panel-sheet.tsx +2 -2
  134. package/src/components/ui/panels/mobile-selection-bar.tsx +2 -2
  135. package/src/components/ui/panels/multi-field-value.test.ts +26 -3
  136. package/src/components/ui/panels/multi-field-value.ts +6 -3
  137. package/src/components/ui/panels/multi-parametric-inspector.tsx +52 -2
  138. package/src/components/ui/panels/multi-selection-panel.tsx +43 -4
  139. package/src/components/ui/panels/node-display.ts +8 -3
  140. package/src/components/ui/panels/panel-manager.tsx +103 -68
  141. package/src/components/ui/panels/panel-wrapper.tsx +78 -36
  142. package/src/components/ui/panels/parametric-field-control.tsx +1 -1
  143. package/src/components/ui/panels/parametric-inspector.tsx +4 -4
  144. package/src/components/ui/panels/reference-panel.tsx +2 -3
  145. package/src/components/ui/panels/room-construction-rows.tsx +774 -0
  146. package/src/components/ui/panels/room-name-options.test.ts +55 -0
  147. package/src/components/ui/panels/room-name-options.ts +47 -0
  148. package/src/components/ui/panels/room-openings-field.tsx +110 -0
  149. package/src/components/ui/panels/room-panel.tsx +725 -0
  150. package/src/components/ui/panels/shape-choice.tsx +58 -0
  151. package/src/components/ui/panels/wall-region-list.tsx +109 -0
  152. package/src/components/ui/primitives/color-dot.tsx +36 -24
  153. package/src/components/ui/primitives/sidebar.tsx +2 -0
  154. package/src/components/ui/right-stack.test.ts +29 -0
  155. package/src/components/ui/right-stack.tsx +145 -0
  156. package/src/components/ui/sidebar/icon-rail.tsx +5 -0
  157. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +31 -4
  158. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +5 -5
  159. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +4 -4
  160. package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +1 -5
  161. package/src/components/ui/sidebar/panels/site-panel/collection-tree-node.tsx +197 -0
  162. package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -5
  163. package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +1 -5
  164. package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +1 -5
  165. package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +1 -5
  166. package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +24 -11
  167. package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +1 -5
  168. package/src/components/ui/sidebar/panels/site-panel/index.tsx +176 -211
  169. package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +30 -8
  170. package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +1 -3
  171. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +4 -1
  172. package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +1 -3
  173. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +1 -4
  174. package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +1 -3
  175. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +21 -13
  176. package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +1 -5
  177. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +1 -5
  178. package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +1 -5
  179. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +0 -27
  180. package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -3
  181. package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -5
  182. package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +1 -5
  183. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +9 -4
  184. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +0 -2
  185. package/src/components/ui/sidebar/tab-bar.tsx +10 -0
  186. package/src/components/ui/sidebar/use-plugin-panels.tsx +15 -5
  187. package/src/components/viewer/floorplan-compass-button.tsx +9 -1
  188. package/src/components/viewer/floorplan-preview.tsx +5 -6
  189. package/src/components/viewer/viewer-hover-label.tsx +34 -0
  190. package/src/components/viewer/viewer-rooms.tsx +175 -0
  191. package/src/components/viewer/viewer-scene-header.tsx +181 -145
  192. package/src/components/viewer/viewer-selection-manager.tsx +124 -0
  193. package/src/components/viewer/viewer-stage.tsx +2 -2
  194. package/src/components/viewer-zone-system.tsx +3 -8
  195. package/src/hooks/use-auto-save.integration.test.tsx +123 -9
  196. package/src/hooks/use-auto-save.ts +34 -8
  197. package/src/hooks/use-keyboard.test.ts +178 -3
  198. package/src/hooks/use-keyboard.ts +89 -71
  199. package/src/hooks/use-open-wall-ends.test.ts +99 -0
  200. package/src/hooks/use-open-wall-ends.ts +238 -0
  201. package/src/hooks/use-placement-notice.test.ts +48 -0
  202. package/src/hooks/use-placement-notice.ts +42 -0
  203. package/src/hooks/use-selected-room.ts +143 -0
  204. package/src/index.tsx +246 -39
  205. package/src/lib/ceiling-edit-session.test.ts +321 -0
  206. package/src/lib/ceiling-edit-session.ts +206 -0
  207. package/src/lib/ceiling-region-session.test.ts +179 -0
  208. package/src/lib/continuation.test.ts +19 -1
  209. package/src/lib/continuation.ts +25 -4
  210. package/src/lib/door-interaction.ts +14 -0
  211. package/src/lib/editor-level-display.test.ts +96 -0
  212. package/src/lib/editor-level-display.ts +48 -0
  213. package/src/lib/editor-views.ts +144 -0
  214. package/src/lib/export-geometry-wait.test.ts +90 -0
  215. package/src/lib/floor-edit-session.ts +153 -0
  216. package/src/lib/floor-footprints.test.ts +563 -0
  217. package/src/lib/floor-footprints.ts +395 -0
  218. package/src/lib/floor-foundation-support.test.ts +283 -0
  219. package/src/lib/floor-opening-draft.ts +218 -0
  220. package/src/lib/floor-openings.test.ts +216 -0
  221. package/src/lib/floor-region-geometry.ts +191 -0
  222. package/src/lib/floor-region-session.test.ts +485 -0
  223. package/src/lib/floor-region-session.ts +589 -0
  224. package/src/lib/floor-region-snap.ts +101 -0
  225. package/src/lib/floor-step-hover.test.ts +32 -0
  226. package/src/lib/floorplan/annotation-visibility.ts +17 -0
  227. package/src/lib/floorplan/dimension-drive.test.ts +360 -0
  228. package/src/lib/floorplan/dimension-drive.ts +479 -0
  229. package/src/lib/floorplan/floorplan-export.test.ts +12 -8
  230. package/src/lib/floorplan/floorplan-export.tsx +32 -8
  231. package/src/lib/floorplan/floorplan-extension.ts +32 -0
  232. package/src/lib/floorplan/floorplan-mode.test.ts +4 -0
  233. package/src/lib/floorplan/floorplan-mode.ts +2 -0
  234. package/src/lib/floorplan/floorplan-pdfkit-document.ts +339 -18
  235. package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +138 -8
  236. package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +56 -25
  237. package/src/lib/floorplan/floorplan-plugin-install.test.tsx +138 -0
  238. package/src/lib/floorplan/floorplan-readonly.ts +6 -0
  239. package/src/lib/floorplan/geometry.ts +13 -6
  240. package/src/lib/floorplan/index.ts +0 -9
  241. package/src/lib/floorplan/items.ts +5 -2
  242. package/src/lib/floorplan/marks.test.ts +213 -0
  243. package/src/lib/floorplan/marks.ts +238 -0
  244. package/src/lib/floorplan/open-wall-ends.test.ts +268 -0
  245. package/src/lib/floorplan/open-wall-ends.ts +162 -0
  246. package/src/lib/floorplan/pdf-fonts/GeistMono-Bold.ttf +0 -0
  247. package/src/lib/floorplan/pdf-fonts/GeistMono-Regular.ttf +0 -0
  248. package/src/lib/floorplan/pdf-fonts/LICENSE-GeistMono.txt +92 -0
  249. package/src/lib/floorplan/pdf-fonts/LICENSE-LiberationSans.txt +102 -0
  250. package/src/lib/floorplan/pdf-fonts/LiberationSans-Bold.ttf +0 -0
  251. package/src/lib/floorplan/pdf-fonts/LiberationSans-Regular.ttf +0 -0
  252. package/src/lib/floorplan/schedules.test.ts +189 -0
  253. package/src/lib/floorplan/schedules.ts +339 -0
  254. package/src/lib/floorplan/sheet-export.test.ts +100 -0
  255. package/src/lib/floorplan/sheet-export.ts +318 -0
  256. package/src/lib/floorplan/site-plan/build-site-plan-drawing.ts +779 -0
  257. package/src/lib/floorplan/site-plan/contours.test.ts +43 -0
  258. package/src/lib/floorplan/site-plan/contributors.ts +97 -0
  259. package/src/lib/floorplan/site-plan/coverage.ts +232 -0
  260. package/src/lib/floorplan/site-plan/drawing-type-switch.test.ts +15 -0
  261. package/src/lib/floorplan/site-plan/drawing-type-switch.tsx +86 -0
  262. package/src/lib/floorplan/site-plan/front-edge.test.ts +103 -0
  263. package/src/lib/floorplan/site-plan/front-edge.ts +156 -0
  264. package/src/lib/floorplan/site-plan/geometry.test.ts +275 -0
  265. package/src/lib/floorplan/site-plan/geometry.ts +557 -0
  266. package/src/lib/floorplan/site-plan/index.ts +71 -0
  267. package/src/lib/floorplan/site-plan/site-annotations.test.ts +366 -0
  268. package/src/lib/floorplan/site-plan/site-annotations.ts +1083 -0
  269. package/src/lib/floorplan/site-plan/site-parts.ts +379 -0
  270. package/src/lib/floorplan/site-plan/site-plan-layer.tsx +138 -0
  271. package/src/lib/floorplan/site-plan/street-name.test.ts +69 -0
  272. package/src/lib/floorplan/site-plan/street-name.ts +302 -0
  273. package/src/lib/floorplan/site-plan/yards.test.ts +69 -0
  274. package/src/lib/floorplan/types.ts +1 -27
  275. package/src/lib/fresh-planar-placement.test.ts +36 -0
  276. package/src/lib/fresh-planar-placement.ts +59 -36
  277. package/src/lib/geometry-script/author.ts +152 -0
  278. package/src/lib/geometry-script/client.ts +152 -0
  279. package/src/lib/geometry-script/protocol.ts +12 -0
  280. package/src/lib/gesture-lifecycle.test.ts +595 -0
  281. package/src/lib/gesture-lifecycle.ts +290 -0
  282. package/src/lib/glb-export-zone-holes.test.ts +41 -0
  283. package/src/lib/glb-export.test.ts +485 -0
  284. package/src/lib/glb-export.ts +237 -119
  285. package/src/lib/history.test.ts +6 -0
  286. package/src/lib/history.ts +7 -0
  287. package/src/lib/hud-title.ts +109 -0
  288. package/src/lib/hydration-gated-storage.ts +49 -0
  289. package/src/lib/ifc-export.test.ts +174 -0
  290. package/src/lib/ifc-export.ts +201 -0
  291. package/src/lib/ifc-parts.ts +15 -0
  292. package/src/lib/inspector-expanded.test.ts +44 -0
  293. package/src/lib/inspector-expanded.ts +36 -0
  294. package/src/lib/interaction/scope.ts +18 -0
  295. package/src/lib/keyboard-pan.test.ts +129 -7
  296. package/src/lib/keyboard-pan.ts +161 -12
  297. package/src/lib/live-gesture-round-trip.test.ts +360 -0
  298. package/src/lib/lot/__fixtures__/map-dossier-miami-shores.json +1 -0
  299. package/src/lib/lot/__fixtures__/map-dossier-stpete.json +1 -0
  300. package/src/lib/lot/clean-ring.test.ts +186 -0
  301. package/src/lib/lot/clean-ring.ts +217 -0
  302. package/src/lib/lot/dossier.test.ts +202 -0
  303. package/src/lib/lot/dossier.ts +498 -0
  304. package/src/lib/lot/drop-in.test.ts +326 -0
  305. package/src/lib/lot/drop-in.ts +407 -0
  306. package/src/lib/lot/index.ts +43 -0
  307. package/src/lib/lot/lot-patch.test.ts +340 -0
  308. package/src/lib/lot/lot-patch.ts +394 -0
  309. package/src/lib/lot/parcel-provider.ts +43 -0
  310. package/src/lib/lot/terrain.test.ts +161 -0
  311. package/src/lib/lot/terrain.ts +266 -0
  312. package/src/lib/material-catalog-model.ts +3 -1
  313. package/src/lib/material-paint-panel-model.ts +9 -5
  314. package/src/lib/material-paint.ts +4 -0
  315. package/src/lib/mezzanine-actions.test.ts +342 -0
  316. package/src/lib/mezzanine-draft.test.ts +501 -0
  317. package/src/lib/mezzanine-draft.ts +278 -0
  318. package/src/lib/mezzanine-messages.ts +26 -0
  319. package/src/lib/mezzanine-selection.test.ts +207 -0
  320. package/src/lib/mezzanine-stairs.ts +46 -0
  321. package/src/lib/model-export.ts +3 -1
  322. package/src/lib/node-action-movement.ts +17 -6
  323. package/src/lib/node-display-label.ts +37 -0
  324. package/src/lib/orbit-floor-clearance.test.ts +165 -0
  325. package/src/lib/orbit-floor-clearance.ts +106 -0
  326. package/src/lib/paint-commit-route.test.ts +37 -0
  327. package/src/lib/paint-commit-route.ts +17 -0
  328. package/src/lib/paint-eyedropper.test.tsx +165 -0
  329. package/src/lib/paint-eyedropper.ts +99 -0
  330. package/src/lib/paint-pass-through.test.ts +111 -0
  331. package/src/lib/paint-pass-through.ts +76 -0
  332. package/src/lib/paint-region-hover.ts +25 -0
  333. package/src/lib/paint-region-mode.ts +154 -0
  334. package/src/lib/paint-regions.test.ts +166 -0
  335. package/src/lib/paint-regions.ts +192 -0
  336. package/src/lib/paint-scope.test.ts +317 -35
  337. package/src/lib/paint-scope.ts +266 -25
  338. package/src/lib/panel-rows.ts +21 -0
  339. package/src/lib/parametric-node-update.ts +1 -1
  340. package/src/lib/plate-paint-affected.test.ts +300 -0
  341. package/src/lib/plate-paint-affected.ts +177 -0
  342. package/src/lib/plugin-chat.ts +50 -0
  343. package/src/lib/plugin-panels.test.ts +25 -0
  344. package/src/lib/plugin-panels.ts +94 -0
  345. package/src/lib/print-shell-compiler.ts +1 -0
  346. package/src/lib/registered-draft-snap.ts +12 -0
  347. package/src/lib/roof-duplication.ts +10 -2
  348. package/src/lib/room-assembly-overlay.test.ts +275 -0
  349. package/src/lib/room-assembly-overlay.ts +266 -0
  350. package/src/lib/room-built-on.test.ts +379 -0
  351. package/src/lib/room-built-on.ts +138 -0
  352. package/src/lib/room-construction-commands.ts +712 -0
  353. package/src/lib/room-construction.test.ts +746 -0
  354. package/src/lib/room-construction.ts +385 -0
  355. package/src/lib/room-controls-phase6.test.ts +630 -0
  356. package/src/lib/room-divide-session.ts +523 -0
  357. package/src/lib/room-handle-drag.ts +1068 -0
  358. package/src/lib/room-name-catalog.test.ts +90 -0
  359. package/src/lib/room-name-catalog.ts +168 -0
  360. package/src/lib/room-push-dimensions.test.ts +311 -0
  361. package/src/lib/room-push-dimensions.ts +251 -0
  362. package/src/lib/room-selection-commands.ts +62 -0
  363. package/src/lib/room-selection.test.ts +973 -0
  364. package/src/lib/room-selection.ts +417 -0
  365. package/src/lib/room-structure-commands.ts +94 -0
  366. package/src/lib/room-structure.test.ts +921 -0
  367. package/src/lib/room-transform-session.ts +570 -0
  368. package/src/lib/room-zone-routing.test.ts +294 -0
  369. package/src/lib/room-zone-routing.ts +197 -0
  370. package/src/lib/scene-clipboard.test.ts +364 -0
  371. package/src/lib/scene-clipboard.ts +229 -48
  372. package/src/lib/scene-material-refs.test.ts +79 -0
  373. package/src/lib/scene-material-refs.ts +79 -0
  374. package/src/lib/scene.test.ts +24 -36
  375. package/src/lib/scene.ts +33 -57
  376. package/src/lib/selection-routing.test.ts +129 -45
  377. package/src/lib/selection-routing.ts +53 -74
  378. package/src/lib/session-writes.test.ts +120 -0
  379. package/src/lib/session-writes.ts +205 -0
  380. package/src/lib/sidebar-panel.ts +62 -0
  381. package/src/lib/site-boundary.test.ts +4 -6
  382. package/src/lib/site-boundary.ts +1 -1
  383. package/src/lib/snapping-mode.test.ts +4 -0
  384. package/src/lib/snapping-mode.ts +8 -1
  385. package/src/lib/terrace-draft.test.ts +145 -0
  386. package/src/lib/terrace-draft.ts +213 -0
  387. package/src/lib/tool-cursor-icon.test.ts +16 -0
  388. package/src/lib/tool-cursor-icon.ts +11 -0
  389. package/src/lib/typing-target.ts +9 -0
  390. package/src/lib/units.ts +77 -39
  391. package/src/lib/vector-edges.test.ts +58 -0
  392. package/src/lib/vector-edges.ts +400 -0
  393. package/src/lib/viewer-selection.test.ts +577 -0
  394. package/src/lib/viewer-selection.ts +569 -0
  395. package/src/lib/wall-draw-variant.test.ts +71 -0
  396. package/src/lib/wall-draw-variant.ts +91 -0
  397. package/src/lib/wall-paint-region-session.ts +246 -0
  398. package/src/lib/wall-paint-region.test.ts +373 -0
  399. package/src/lib/wall-polygon-draft.test.ts +262 -0
  400. package/src/lib/wall-polygon-draft.ts +286 -0
  401. package/src/lib/wall-push.test.ts +453 -0
  402. package/src/lib/wall-region-face.ts +352 -0
  403. package/src/lib/wall-region-handles.test.ts +304 -0
  404. package/src/lib/wall-region-handles.ts +368 -0
  405. package/src/lib/wall-region-list.test.ts +119 -0
  406. package/src/lib/wall-region-list.ts +136 -0
  407. package/src/lib/wall-region-snap.ts +110 -0
  408. package/src/lib/window-interaction.ts +14 -0
  409. package/src/lib/zone-content.ts +4 -1
  410. package/src/lib/zone-name.test.ts +17 -0
  411. package/src/lib/zone-name.ts +17 -0
  412. package/src/store/opens-in-select-mode.test.ts +223 -0
  413. package/src/store/paint-mode.test.ts +188 -0
  414. package/src/store/terrain-sculpt-mode.test.ts +19 -21
  415. package/src/store/tool-mode.test.ts +137 -6
  416. package/src/store/use-delete-confirmation.ts +28 -1
  417. package/src/store/use-drawing-view.test.ts +24 -3
  418. package/src/store/use-drawing-view.ts +29 -1
  419. package/src/store/use-editor.tsx +432 -321
  420. package/src/store/use-floorplan-draft-preview.ts +13 -0
  421. package/src/store/use-floorplan-marquee.ts +12 -2
  422. package/src/store/use-quick-measurement-hud.ts +3 -3
  423. package/src/store/use-stair-build-preview.ts +13 -5
  424. package/src/store/use-wall-move-ghosts.ts +4 -2
  425. package/src/store/use-wall-region-handles-selection.test.ts +222 -0
  426. package/src/store/use-wall-region-handles-selection.ts +139 -0
  427. package/src/store/view-layout.test.ts +175 -0
  428. package/src/store/view-layout.ts +196 -0
  429. package/src/test-utils/immediate-animation-frames.ts +16 -0
  430. package/src/test-utils/live-gesture-round-trip.ts +224 -0
  431. package/src/test-utils/selection-harness.ts +82 -0
  432. package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +0 -499
  433. package/src/components/systems/zone/zone-label-editor-system.tsx +0 -333
  434. package/src/components/tools/fence/fence-drafting.test.ts +0 -100
  435. package/src/components/tools/fence/fence-drafting.ts +0 -266
  436. package/src/components/ui/sidebar/panels/plugins-panel.tsx +0 -218
  437. package/src/components/viewer/viewer-units-panel.tsx +0 -116
  438. package/src/lib/floorplan/stairs.ts +0 -489
  439. package/src/lib/level-duplication.test.ts +0 -144
  440. package/src/lib/level-duplication.ts +0 -195
@@ -0,0 +1,91 @@
1
+ import { emitter } from '@pascal-app/core'
2
+ import useEditor from '../store/use-editor'
3
+ import type { ContinuationMode } from './continuation'
4
+
5
+ /**
6
+ * How the wall tool draws, picked from the Build panel's Rooms group before
7
+ * drawing starts. Stored as the wall's continuation mode (persisted with the
8
+ * editor preferences), so the choice survives tool switches and reloads.
9
+ */
10
+ export type WallDrawVariant = 'rectangle' | 'polygon' | 'walls'
11
+
12
+ export type WallDrawVariantInfo = {
13
+ id: WallDrawVariant
14
+ /** The wall continuation mode the tool reads. */
15
+ mode: ContinuationMode
16
+ /** Tile label. */
17
+ label: string
18
+ /** The name the HUD shows while the variant is armed. */
19
+ title: string
20
+ iconSrc: string
21
+ /** Caption under the Rooms tiles: a bold lead, then the rest. */
22
+ caption: { lead: string; rest: string }
23
+ }
24
+
25
+ export const WALL_DRAW_VARIANTS: readonly WallDrawVariantInfo[] = [
26
+ {
27
+ id: 'rectangle',
28
+ mode: 'rectangle',
29
+ label: 'Rectangle',
30
+ title: 'Rectangle room',
31
+ iconSrc: '/icons/room.webp',
32
+ caption: {
33
+ lead: 'Click two corners',
34
+ rest: 'on the grid. Walls, floor and ceiling are created together.',
35
+ },
36
+ },
37
+ {
38
+ id: 'polygon',
39
+ mode: 'room',
40
+ label: 'Polygon',
41
+ title: 'Polygon room',
42
+ iconSrc: '/icons/polygon-room.webp',
43
+ caption: {
44
+ lead: 'Click each corner,',
45
+ rest: 'then the first one again to close. For L-shapes and angled rooms.',
46
+ },
47
+ },
48
+ {
49
+ id: 'walls',
50
+ mode: 'single',
51
+ label: 'Walls',
52
+ title: 'Walls',
53
+ iconSrc: '/icons/wall.webp',
54
+ caption: {
55
+ lead: 'Draw one wall at a time.',
56
+ rest: 'Close a loop and it becomes a room; leave it open for partitions.',
57
+ },
58
+ },
59
+ ]
60
+
61
+ export const WALL_DRAW_IDLE_CAPTION =
62
+ 'Pick a shape to start. Every room brings its own walls, floor and ceiling.'
63
+
64
+ export function getWallDrawVariantInfo(variant: WallDrawVariant): WallDrawVariantInfo {
65
+ return WALL_DRAW_VARIANTS.find((info) => info.id === variant) ?? WALL_DRAW_VARIANTS[1]!
66
+ }
67
+
68
+ export function wallDrawVariantOf(mode: ContinuationMode): WallDrawVariant {
69
+ return WALL_DRAW_VARIANTS.find((info) => info.mode === mode)?.id ?? 'polygon'
70
+ }
71
+
72
+ export function getWallDrawVariant(): WallDrawVariant {
73
+ return wallDrawVariantOf(useEditor.getState().getContinuation('wall'))
74
+ }
75
+
76
+ export function useWallDrawVariant(): WallDrawVariant {
77
+ return useEditor((state) => wallDrawVariantOf(state.getContinuation('wall')))
78
+ }
79
+
80
+ /**
81
+ * Pick how the wall tool draws. Callers arm the wall tool themselves; switching
82
+ * while it is armed drops the in-flight draft so a half-drawn chain never
83
+ * finishes as a rectangle.
84
+ */
85
+ export function selectWallDrawVariant(variant: WallDrawVariant): void {
86
+ const editor = useEditor.getState()
87
+ const { mode } = getWallDrawVariantInfo(variant)
88
+ if (editor.getContinuation('wall') === mode) return
89
+ if (editor.mode === 'build' && editor.tool === 'wall') emitter.emit('tool:cancel')
90
+ editor.setContinuation('wall', mode)
91
+ }
@@ -0,0 +1,246 @@
1
+ import { type AnyNodeId, useScene, type WallFace } from '@pascal-app/core'
2
+ import { create } from 'zustand'
3
+ import useEditor from '../store/use-editor'
4
+ import { beginGesture, type GestureHandle } from './gesture-lifecycle'
5
+ import { hasActivePaintMaterial } from './material-paint'
6
+ import { isPaintErasing, usePaintRegionMode } from './paint-region-mode'
7
+ import {
8
+ addWallRegion,
9
+ type RegionWriteResult,
10
+ WALL_REGION_CAP_MESSAGE,
11
+ type WallRegionBounds,
12
+ wallFaceHasRoom,
13
+ } from './paint-regions'
14
+ import type { WallFaceExtent } from './wall-region-face'
15
+ import {
16
+ DEFAULT_WAINSCOT_HEIGHT,
17
+ faceBaseAt,
18
+ MIN_WALL_REGION_SIZE,
19
+ snapWallRegionValue,
20
+ type WallFaceBaseRuns,
21
+ type WallRegionSnap,
22
+ type WallRegionSnapTargets,
23
+ wallRegionSnapTargets,
24
+ } from './wall-region-snap'
25
+
26
+ // The wall half of "Paint part of a surface": hover → press anywhere on a face
27
+ // → drag to the opposite corner → release paints one rectangle, as one undo
28
+ // step. Pure over face coordinates — the 3D tool resolves the pointer to a
29
+ // `WallRegionHit` and renders `preview`.
30
+
31
+ export const WALL_PAINT_REGION_HANDLE = 'paint-region'
32
+ export const PICK_MATERIAL_MESSAGE = 'Pick a material first'
33
+
34
+ /** The pointer on one wall face. `u` / `v` are raw (unsnapped) face coordinates. */
35
+ export type WallRegionHit = {
36
+ wallId: string
37
+ face: WallFace
38
+ u: number
39
+ v: number
40
+ /** Reference chord length. */
41
+ length: number
42
+ extent: WallFaceExtent
43
+ runs: WallFaceBaseRuns
44
+ }
45
+
46
+ export type WallRegionPreview = {
47
+ wallId: string
48
+ face: WallFace
49
+ /** What a release now would paint (absent bounds run to the edge); null = nothing. */
50
+ bounds: WallRegionBounds | null
51
+ /** The pressed corner, or where a press would put it. */
52
+ corner: [number, number]
53
+ /** The readout: width × height. */
54
+ measure: { values: number[]; u: number; v: number } | null
55
+ pressed: boolean
56
+ }
57
+
58
+ type Gesture = {
59
+ owner: GestureHandle
60
+ hit: WallRegionHit
61
+ targets: WallRegionSnapTargets
62
+ corner: [number, number]
63
+ }
64
+
65
+ export const useWallPaintRegionSession = create<{ preview: WallRegionPreview | null }>(() => ({
66
+ preview: null,
67
+ }))
68
+
69
+ let gesture: Gesture | null = null
70
+
71
+ export function isWallRegionGestureActive() {
72
+ return gesture !== null
73
+ }
74
+
75
+ function clamp(value: number, low: number, high: number) {
76
+ return Math.min(Math.max(value, low), Math.max(low, high))
77
+ }
78
+
79
+ /** Room the face has above its base at `u`. */
80
+ function faceHeightAt(hit: WallRegionHit, u: number) {
81
+ return hit.extent.top - faceBaseAt(hit.runs, u)
82
+ }
83
+
84
+ function targetsFor(hit: WallRegionHit): WallRegionSnapTargets {
85
+ const nodes = useScene.getState().nodes
86
+ const wall = nodes[hit.wallId as AnyNodeId]
87
+ if (wall?.type !== 'wall') return { u: [0, hit.length], v: [0, DEFAULT_WAINSCOT_HEIGHT] }
88
+ const targets = wallRegionSnapTargets(wall, hit.face, nodes, hit.runs)
89
+ return { ...targets, v: [...targets.v, faceHeightAt(hit, hit.u)] }
90
+ }
91
+
92
+ /** The pointer on the face, snapped and held inside it. */
93
+ function snapped(hit: WallRegionHit, targets: WallRegionSnapTargets, snap: WallRegionSnap) {
94
+ const u = clamp(snapWallRegionValue(hit.u, targets.u, snap).value, 0, hit.length)
95
+ const v = clamp(snapWallRegionValue(hit.v, targets.v, snap).value, 0, faceHeightAt(hit, u))
96
+ return { ...hit, u, v }
97
+ }
98
+
99
+ /** The box between the pressed corner and the pointer, or null when it would paint a sliver. */
100
+ export function wallRegionBoundsFor(state: {
101
+ hit: WallRegionHit
102
+ corner: [number, number]
103
+ }): WallRegionBounds | null {
104
+ const { hit } = state
105
+ const [au, av] = state.corner
106
+ const bu = clamp(hit.u, 0, hit.length)
107
+ const bv = clamp(hit.v, 0, faceHeightAt(hit, bu))
108
+ const u0 = Math.min(au, bu)
109
+ const u1 = Math.max(au, bu)
110
+ const v0 = Math.min(av, bv)
111
+ const v1 = Math.max(av, bv)
112
+ if (u1 - u0 < MIN_WALL_REGION_SIZE || v1 - v0 < MIN_WALL_REGION_SIZE) return null
113
+ const bounds: WallRegionBounds = {}
114
+ // A bound on the face's edge is left open, so the region follows the edge.
115
+ if (u0 > 1e-6) bounds.u0 = u0
116
+ if (u1 < hit.length - 1e-6) bounds.u1 = u1
117
+ if (v0 > 1e-6) bounds.v0 = v0
118
+ if (v1 < faceHeightAt(hit, (u0 + u1) / 2) - 1e-6) bounds.v1 = v1
119
+ return bounds
120
+ }
121
+
122
+ function previewOf(state: Omit<Gesture, 'owner'>, pressed: boolean): WallRegionPreview {
123
+ const { hit, corner } = state
124
+ const base = { wallId: hit.wallId, face: hit.face, corner, pressed }
125
+ if (!pressed) return { ...base, bounds: null, measure: null }
126
+ const [au, av] = corner
127
+ return {
128
+ ...base,
129
+ bounds: wallRegionBoundsFor(state),
130
+ measure: {
131
+ values: [Math.abs(hit.u - au), Math.abs(hit.v - av)],
132
+ u: (au + hit.u) / 2,
133
+ v: (av + hit.v) / 2,
134
+ },
135
+ }
136
+ }
137
+
138
+ function track(hit: WallRegionHit, snap: WallRegionSnap): Omit<Gesture, 'owner'> {
139
+ const targets = targetsFor(hit)
140
+ const at = snapped(hit, targets, snap)
141
+ return { hit: at, targets, corner: [at.u, at.v] }
142
+ }
143
+
144
+ /** Pointer over the canvas with no gesture live: preview where a press would start the box. */
145
+ export function hoverWallRegion(hit: WallRegionHit | null, snap: WallRegionSnap) {
146
+ if (gesture) return
147
+ useWallPaintRegionSession.setState({
148
+ preview: hit ? previewOf(track(hit, snap), false) : null,
149
+ })
150
+ }
151
+
152
+ /**
153
+ * Starts a gesture at any point of a face, or refuses it with a notice.
154
+ * Returns whether it started. The gesture lives under the lifecycle owner: a
155
+ * sub-mode, mode, level or selection change, its scope replaced, its wall
156
+ * deleted or any history command cancels it; `onHostCancel` lets the pointer
157
+ * surface drop its press.
158
+ */
159
+ export function pressWallRegion(
160
+ hit: WallRegionHit,
161
+ snap: WallRegionSnap,
162
+ onHostCancel?: () => void,
163
+ ): boolean {
164
+ const regionMode = usePaintRegionMode.getState()
165
+ regionMode.setNotice(null)
166
+ const editor = useEditor.getState()
167
+ if (isPaintErasing() || !hasActivePaintMaterial(editor.activePaintMaterial)) {
168
+ regionMode.setNotice(PICK_MATERIAL_MESSAGE)
169
+ return false
170
+ }
171
+ const wall = useScene.getState().nodes[hit.wallId as AnyNodeId]
172
+ if (wall?.type !== 'wall' || useScene.getState().readOnly) return false
173
+ if (!wallFaceHasRoom(wall, hit.face)) {
174
+ regionMode.setNotice(WALL_REGION_CAP_MESSAGE)
175
+ return false
176
+ }
177
+ gesture?.owner.cancel()
178
+ const tracked = track(hit, snap)
179
+ const owner = beginGesture({
180
+ kind: 'wall-paint-region',
181
+ scope: { kind: 'handle-drag', nodeId: hit.wallId, handle: WALL_PAINT_REGION_HANDLE },
182
+ paintSubMode: true,
183
+ stale: () => {
184
+ const state = useScene.getState()
185
+ return state.readOnly || state.nodes[hit.wallId as AnyNodeId]?.type !== 'wall'
186
+ },
187
+ onCancel: () => {
188
+ if (gesture?.owner === owner) gesture = null
189
+ useWallPaintRegionSession.setState({ preview: null })
190
+ onHostCancel?.()
191
+ },
192
+ })
193
+ gesture = { ...tracked, owner }
194
+ useWallPaintRegionSession.setState({ preview: previewOf(gesture, true) })
195
+ return true
196
+ }
197
+
198
+ /** The live gesture's lifecycle handle (the surface captures its pointer through it). */
199
+ export function activeWallRegionGesture(): GestureHandle | null {
200
+ return gesture?.owner ?? null
201
+ }
202
+
203
+ /** The pointer moved with the button held; `hit` is on the pressed face. */
204
+ export function dragWallRegion(hit: WallRegionHit, snap: WallRegionSnap) {
205
+ if (!gesture || hit.wallId !== gesture.hit.wallId || hit.face !== gesture.hit.face) return
206
+ gesture.hit = snapped(hit, gesture.targets, snap)
207
+ useWallPaintRegionSession.setState({ preview: previewOf(gesture, true) })
208
+ }
209
+
210
+ /** Ends the gesture and paints its region. Null when nothing was committed. */
211
+ export function releaseWallRegion(): RegionWriteResult | null {
212
+ const current = gesture
213
+ if (!current) return null
214
+ gesture = null
215
+ useWallPaintRegionSession.setState({ preview: null })
216
+ const bounds = wallRegionBoundsFor(current)
217
+ if (!bounds) {
218
+ current.owner.end()
219
+ return null
220
+ }
221
+ const paint = useEditor.getState().activePaintMaterial
222
+ const result =
223
+ current.owner.finish(() =>
224
+ addWallRegion(current.hit.wallId, current.hit.face, bounds, {
225
+ material: paint?.material,
226
+ materialPreset: paint?.materialPreset,
227
+ }),
228
+ ) ?? null
229
+ if (result && !result.ok) usePaintRegionMode.getState().setNotice(result.message)
230
+ return result
231
+ }
232
+
233
+ /** Drops the gesture without writing anything. Returns whether one was live. */
234
+ export function cancelWallRegion(): boolean {
235
+ const current = gesture
236
+ if (!current) return false
237
+ current.owner.cancel()
238
+ gesture = null
239
+ useWallPaintRegionSession.setState({ preview: null })
240
+ return true
241
+ }
242
+
243
+ /** The wall the live gesture draws on, if any. */
244
+ export function activeWallRegionTarget(): { wallId: string; face: WallFace } | null {
245
+ return gesture ? { wallId: gesture.hit.wallId, face: gesture.hit.face } : null
246
+ }