@pascal-app/editor 0.8.0 → 0.9.2

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 (350) hide show
  1. package/package.json +15 -13
  2. package/src/components/editor/alignment-3d-guide-layer.tsx +168 -0
  3. package/src/components/editor/bake-exporter.tsx +47 -0
  4. package/src/components/editor/custom-camera-controls.tsx +795 -28
  5. package/src/components/editor/editor-layout-mobile.tsx +8 -3
  6. package/src/components/editor/editor-layout-v2.tsx +76 -51
  7. package/src/components/editor/export-manager.tsx +69 -32
  8. package/src/components/editor/fence-tangent-lines-3d.tsx +87 -0
  9. package/src/components/editor/first-person/build-collider-world.test.ts +182 -0
  10. package/src/components/editor/first-person/build-collider-world.ts +228 -83
  11. package/src/components/editor/first-person-controls.tsx +943 -27
  12. package/src/components/editor/floating-action-menu.tsx +676 -208
  13. package/src/components/editor/floating-building-action-menu.tsx +2 -2
  14. package/src/components/editor/floorplan-background-selection.test.ts +58 -0
  15. package/src/components/editor/floorplan-background-selection.ts +3 -2
  16. package/src/components/editor/floorplan-panel.tsx +6916 -12137
  17. package/src/components/editor/grid.tsx +197 -36
  18. package/src/components/editor/group-actions.ts +457 -0
  19. package/src/components/editor/group-floating-action-menu.tsx +130 -0
  20. package/src/components/editor/group-move-3d.ts +397 -0
  21. package/src/components/editor/group-rotate-handle.tsx +420 -0
  22. package/src/components/editor/group-selection-box-3d.tsx +173 -0
  23. package/src/components/editor/group-transform-shared.test.ts +396 -0
  24. package/src/components/editor/group-transform-shared.ts +489 -0
  25. package/src/components/editor/handles/handle-arrow.tsx +581 -0
  26. package/src/components/editor/handles/use-handle-drag.ts +246 -0
  27. package/src/components/editor/index.tsx +412 -162
  28. package/src/components/editor/measurement-pill.tsx +115 -0
  29. package/src/components/editor/node-action-menu.tsx +14 -1
  30. package/src/components/editor/node-arrow-handles.tsx +1646 -0
  31. package/src/components/editor/opening-guides-3d-layer.tsx +144 -0
  32. package/src/components/editor/quick-measurement-card.tsx +70 -0
  33. package/src/components/editor/quick-measurement-hud.tsx +28 -0
  34. package/src/components/editor/riser-diagram-panel.tsx +137 -0
  35. package/src/components/editor/selection-manager.tsx +1106 -483
  36. package/src/components/editor/site-edge-labels.tsx +45 -21
  37. package/src/components/editor/slab-hole-highlights.tsx +474 -0
  38. package/src/components/editor/snapshot-capture-overlay.tsx +369 -155
  39. package/src/components/editor/three-context-bridge.ts +22 -0
  40. package/src/components/editor/thumbnail-generator.tsx +140 -41
  41. package/src/components/editor/use-floorplan-background-placement.ts +190 -35
  42. package/src/components/editor/use-floorplan-hit-testing.ts +24 -0
  43. package/src/components/editor/use-floorplan-scene-data.ts +19 -7
  44. package/src/components/editor/use-mesh-settle-epoch.ts +26 -0
  45. package/src/components/editor/wall-measurement-label.tsx +14 -19
  46. package/src/components/editor/wall-move-side-handles.tsx +870 -0
  47. package/src/components/editor/wall-opening-highlights.tsx +181 -0
  48. package/src/components/editor/wall-snap-beacon-layer.tsx +304 -0
  49. package/src/components/editor-2d/floorplan-action-menu-layer.tsx +15 -6
  50. package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +142 -0
  51. package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +5 -3
  52. package/src/components/editor-2d/floorplan-group-action-menu.tsx +87 -0
  53. package/src/components/editor-2d/floorplan-group-move.tsx +701 -0
  54. package/src/components/editor-2d/floorplan-measurement-tool-layer.test.ts +113 -0
  55. package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +1656 -0
  56. package/src/components/editor-2d/floorplan-quick-measure-layer.tsx +212 -0
  57. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +392 -0
  58. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +857 -0
  59. package/src/components/editor-2d/floorplan-render-context.tsx +62 -0
  60. package/src/components/editor-2d/floorplan-snap-beacon-layer.tsx +77 -0
  61. package/src/components/editor-2d/floorplan-wall-move-ghost-layer.tsx +47 -0
  62. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +266 -0
  63. package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +15 -0
  64. package/src/components/editor-2d/renderers/floorplan-label-angle.ts +14 -0
  65. package/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +108 -66
  66. package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +62 -0
  67. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +308 -0
  68. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +3325 -0
  69. package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +24 -9
  70. package/src/components/editor-2d/renderers/floorplan-voronoi-layer.tsx +128 -0
  71. package/src/components/editor-2d/svg-paths.ts +10 -2
  72. package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +439 -57
  73. package/src/components/systems/ceiling/ceiling-system.tsx +109 -12
  74. package/src/components/systems/roof/roof-edit-system.tsx +1851 -22
  75. package/src/components/systems/selection-affordance-manager.tsx +38 -0
  76. package/src/components/systems/stair/stair-edit-system.tsx +1 -1
  77. package/src/components/systems/zone/zone-label-editor-system.tsx +14 -1
  78. package/src/components/systems/zone/zone-system.tsx +42 -13
  79. package/src/components/tools/elevator/elevator-defaults.ts +8 -0
  80. package/src/components/tools/elevator/elevator-tool.tsx +375 -0
  81. package/src/components/tools/elevator/move-elevator-tool.tsx +263 -0
  82. package/src/components/tools/fence/fence-drafting.ts +141 -27
  83. package/src/components/tools/item/move-tool.tsx +45 -105
  84. package/src/components/tools/item/placement-math.test.ts +22 -0
  85. package/src/components/tools/item/placement-math.ts +59 -7
  86. package/src/components/tools/item/placement-strategies.ts +432 -16
  87. package/src/components/tools/item/placement-types.ts +22 -1
  88. package/src/components/tools/item/use-draft-node.ts +51 -2
  89. package/src/components/tools/item/use-placement-coordinator.tsx +1399 -256
  90. package/src/components/tools/registry/move-registry-node-tool.tsx +1064 -0
  91. package/src/components/tools/roof/roof-tool.tsx +350 -28
  92. package/src/components/tools/select/box-select-state.ts +88 -0
  93. package/src/components/tools/select/box-select-tool.tsx +442 -520
  94. package/src/components/tools/select/marquee-geometry.test.ts +161 -0
  95. package/src/components/tools/select/marquee-geometry.ts +155 -0
  96. package/src/components/tools/select/plane-box-select-tool.tsx +570 -0
  97. package/src/components/tools/select/screen-rectangle-selection.ts +84 -0
  98. package/src/components/tools/select/select-candidates.test.ts +109 -0
  99. package/src/components/tools/select/select-candidates.ts +85 -0
  100. package/src/components/tools/shared/affordance-dispatch.ts +30 -0
  101. package/src/components/tools/shared/cursor-sphere.tsx +62 -26
  102. package/src/components/tools/shared/drag-bounding-box.tsx +195 -0
  103. package/src/components/tools/shared/facing-indicator.tsx +87 -0
  104. package/src/components/tools/shared/facing-pose-indicator.tsx +54 -0
  105. package/src/components/tools/shared/floor-stack-preview.ts +25 -0
  106. package/src/components/tools/shared/fresh-placement-visibility.ts +61 -0
  107. package/src/components/tools/shared/placement-box-geometry.ts +123 -0
  108. package/src/components/tools/shared/placement-box.tsx +305 -0
  109. package/src/components/tools/shared/polygon-editor.tsx +866 -175
  110. package/src/components/tools/shared/segment-angle.ts +56 -0
  111. package/src/components/tools/site/site-boundary-editor.tsx +520 -16
  112. package/src/components/tools/site/site-flag-model.ts +18 -0
  113. package/src/components/tools/stair/stair-defaults.ts +2 -1
  114. package/src/components/tools/stair/stair-tool.tsx +342 -37
  115. package/src/components/tools/tool-manager.tsx +239 -62
  116. package/src/components/tools/wall/wall-drafting.test.ts +330 -0
  117. package/src/components/tools/wall/wall-drafting.ts +248 -171
  118. package/src/components/tools/wall/wall-snap-geometry.test.ts +123 -0
  119. package/src/components/tools/wall/wall-snap-geometry.ts +331 -0
  120. package/src/components/tools/zone/zone-boundary-editor.tsx +5 -1
  121. package/src/components/tools/zone/zone-tool.tsx +86 -82
  122. package/src/components/ui/action-menu/action-button.tsx +21 -1
  123. package/src/components/ui/action-menu/camera-actions.tsx +24 -17
  124. package/src/components/ui/action-menu/control-modes.tsx +40 -170
  125. package/src/components/ui/action-menu/furnish-tools.tsx +5 -5
  126. package/src/components/ui/action-menu/index.tsx +3 -117
  127. package/src/components/ui/action-menu/measurement-control.tsx +188 -0
  128. package/src/components/ui/action-menu/structure-tools.tsx +27 -93
  129. package/src/components/ui/action-menu/view-toggles.tsx +279 -90
  130. package/src/components/ui/command-palette/editor-commands.tsx +20 -15
  131. package/src/components/ui/command-palette/index.tsx +5 -3
  132. package/src/components/ui/controls/material-paint-panel.tsx +152 -0
  133. package/src/components/ui/controls/material-picker.tsx +87 -177
  134. package/src/components/ui/controls/material-properties-editor.tsx +108 -0
  135. package/src/components/ui/controls/metric-control.tsx +142 -46
  136. package/src/components/ui/controls/scene-material-list.tsx +247 -0
  137. package/src/components/ui/controls/slider-control.tsx +88 -38
  138. package/src/components/ui/floating-level-selector.tsx +47 -10
  139. package/src/components/ui/helpers/building-helper.tsx +10 -23
  140. package/src/components/ui/helpers/contextual-helper-panel.tsx +428 -0
  141. package/src/components/ui/helpers/helper-manager.tsx +235 -23
  142. package/src/components/ui/helpers/item-helper.tsx +28 -32
  143. package/src/components/ui/helpers/registered-tool-helper.tsx +59 -0
  144. package/src/components/ui/helpers/roof-helper.tsx +10 -12
  145. package/src/components/ui/icon-ref.tsx +47 -0
  146. package/src/components/ui/item-catalog/catalog-items.tsx +40 -1
  147. package/src/components/ui/item-catalog/item-catalog.tsx +16 -36
  148. package/src/components/ui/level-duplicate-dialog.tsx +2 -1
  149. package/src/components/ui/panels/node-display.ts +17 -16
  150. package/src/components/ui/panels/panel-manager.tsx +55 -58
  151. package/src/components/ui/panels/panel-wrapper.tsx +238 -12
  152. package/src/components/ui/panels/parametric-inspector.tsx +482 -0
  153. package/src/components/ui/panels/reference-panel.tsx +54 -19
  154. package/src/components/ui/primitives/dropdown-menu.tsx +12 -8
  155. package/src/components/ui/primitives/shortcut-token.tsx +39 -3
  156. package/src/components/ui/primitives/sidebar.tsx +1 -0
  157. package/src/components/ui/scene-loader.tsx +1 -3
  158. package/src/components/ui/sidebar/app-sidebar.tsx +5 -1
  159. package/src/components/ui/sidebar/icon-rail.tsx +50 -31
  160. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +263 -0
  161. package/src/components/ui/sidebar/panels/items-panel/index.tsx +82 -8
  162. package/src/components/ui/sidebar/panels/plugins-panel.tsx +218 -0
  163. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +122 -41
  164. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +66 -7
  165. package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +267 -0
  166. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +3 -1
  167. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +9 -5
  168. package/src/components/ui/sidebar/panels/site-panel/chimney-tree-node.tsx +86 -0
  169. package/src/components/ui/sidebar/panels/site-panel/column-tree-node.tsx +1 -1
  170. package/src/components/ui/sidebar/panels/site-panel/door-tree-node.tsx +7 -2
  171. package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +92 -0
  172. package/src/components/ui/sidebar/panels/site-panel/elevator-tree-node.tsx +75 -0
  173. package/src/components/ui/sidebar/panels/site-panel/fence-tree-node.tsx +3 -3
  174. package/src/components/ui/sidebar/panels/site-panel/gutter-tree-node.tsx +86 -0
  175. package/src/components/ui/sidebar/panels/site-panel/index.tsx +42 -21
  176. package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +31 -17
  177. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +2 -1
  178. package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +140 -0
  179. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +48 -8
  180. package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +132 -0
  181. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +9 -5
  182. package/src/components/ui/sidebar/panels/site-panel/solar-panel-tree-node.tsx +86 -0
  183. package/src/components/ui/sidebar/panels/site-panel/spawn-tree-node.tsx +7 -1
  184. package/src/components/ui/sidebar/panels/site-panel/stair-tree-node.tsx +2 -2
  185. package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +112 -39
  186. package/src/components/ui/sidebar/panels/site-panel/tree-structure.ts +36 -0
  187. package/src/components/ui/sidebar/panels/site-panel/wall-tree-node.tsx +1 -1
  188. package/src/components/ui/sidebar/panels/site-panel/window-tree-node.tsx +7 -2
  189. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +3 -3
  190. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +51 -2
  191. package/src/components/ui/sidebar/tab-bar.tsx +75 -1
  192. package/src/components/ui/sidebar/use-plugin-panels.tsx +132 -0
  193. package/src/components/ui/snap-target-badge.test.tsx +40 -0
  194. package/src/components/ui/snap-target-badge.tsx +88 -0
  195. package/src/components/viewer-overlay.tsx +253 -61
  196. package/src/components/viewer-zone-system.tsx +7 -2
  197. package/src/hooks/use-auto-save.test.ts +14 -0
  198. package/src/hooks/use-auto-save.ts +64 -10
  199. package/src/hooks/use-ceiling-events.ts +176 -0
  200. package/src/hooks/use-drag-action.ts +127 -0
  201. package/src/hooks/use-keyboard.ts +493 -95
  202. package/src/hooks/use-selection.ts +64 -0
  203. package/src/index.tsx +451 -6
  204. package/src/lib/active-placement-surface.ts +35 -0
  205. package/src/lib/ceiling-plan-snap.ts +24 -0
  206. package/src/lib/constants.ts +6 -2
  207. package/src/lib/contextual-help.test.ts +112 -0
  208. package/src/lib/contextual-help.ts +144 -0
  209. package/src/lib/continuation.ts +64 -0
  210. package/src/lib/direct-manipulation.test.ts +192 -0
  211. package/src/lib/direct-manipulation.ts +109 -0
  212. package/src/lib/editor-api.ts +50 -0
  213. package/src/lib/elevator-support.ts +96 -0
  214. package/src/lib/floorplan/apply-alignment.test.ts +25 -0
  215. package/src/lib/floorplan/apply-alignment.ts +117 -0
  216. package/src/lib/floorplan/floorplan-export.tsx +364 -0
  217. package/src/lib/floorplan/geometry.ts +53 -0
  218. package/src/lib/floorplan/index.ts +10 -0
  219. package/src/lib/floorplan/items.ts +5 -2
  220. package/src/lib/floorplan/plan-coords.ts +21 -0
  221. package/src/lib/floorplan/selection-tool.ts +40 -0
  222. package/src/lib/floorplan/stairs.ts +12 -1
  223. package/src/lib/fresh-planar-placement.test.ts +339 -0
  224. package/src/lib/fresh-planar-placement.ts +130 -0
  225. package/src/lib/glb-export.test.ts +441 -0
  226. package/src/lib/glb-export.ts +874 -0
  227. package/src/lib/history.ts +11 -1
  228. package/src/lib/interaction/hot-set.test.ts +133 -0
  229. package/src/lib/interaction/hot-set.ts +67 -0
  230. package/src/lib/interaction/overlay-policy.test.ts +51 -0
  231. package/src/lib/interaction/overlay-policy.ts +59 -0
  232. package/src/lib/interaction/scope.ts +211 -0
  233. package/src/lib/level-duplication.ts +54 -42
  234. package/src/lib/level-selection.ts +2 -2
  235. package/src/lib/material-paint.ts +227 -48
  236. package/src/lib/measurement-kind.test.ts +30 -0
  237. package/src/lib/measurement-kind.ts +19 -0
  238. package/src/lib/measurement-label.test.ts +47 -0
  239. package/src/lib/measurement-label.ts +68 -0
  240. package/src/lib/measurement-parser.ts +116 -0
  241. package/src/lib/measurements.test.ts +183 -0
  242. package/src/lib/measurements.ts +199 -0
  243. package/src/lib/paint-scope.test.ts +454 -0
  244. package/src/lib/paint-scope.ts +461 -0
  245. package/src/lib/placement-drag-release.ts +23 -0
  246. package/src/lib/placement-metadata.ts +29 -0
  247. package/src/lib/planar-cursor-placement.test.ts +100 -0
  248. package/src/lib/planar-cursor-placement.ts +42 -0
  249. package/src/lib/plugin-panels.test.ts +19 -0
  250. package/src/lib/plugin-panels.ts +91 -0
  251. package/src/lib/quick-measurement.test.ts +77 -0
  252. package/src/lib/quick-measurement.ts +109 -0
  253. package/src/lib/roof-duplication.ts +7 -7
  254. package/src/lib/roof-hover-outline-proxy.ts +22 -0
  255. package/src/lib/roof-wall-hit.ts +164 -0
  256. package/src/lib/scene-clipboard.test.ts +196 -0
  257. package/src/lib/scene-clipboard.ts +332 -0
  258. package/src/lib/scene.ts +40 -8
  259. package/src/lib/selection-routing.test.ts +298 -0
  260. package/src/lib/selection-routing.ts +175 -0
  261. package/src/lib/sfx/index.ts +1 -0
  262. package/src/lib/sfx/movement-tick.test.ts +65 -0
  263. package/src/lib/sfx/movement-tick.ts +18 -0
  264. package/src/lib/sfx-bus.ts +69 -10
  265. package/src/lib/sfx-player.test.ts +124 -0
  266. package/src/lib/sfx-player.ts +148 -55
  267. package/src/lib/site-boundary.ts +1 -0
  268. package/src/lib/slab-plan-snap.test.ts +93 -0
  269. package/src/lib/slab-plan-snap.ts +108 -0
  270. package/src/lib/snapping-mode.test.ts +138 -0
  271. package/src/lib/snapping-mode.ts +179 -0
  272. package/src/lib/stair-duplication.ts +4 -4
  273. package/src/lib/stair-levels.test.ts +146 -0
  274. package/src/lib/stair-levels.ts +177 -0
  275. package/src/lib/surface-plan-snap.test.ts +71 -0
  276. package/src/lib/surface-plan-snap.ts +256 -0
  277. package/src/lib/use-linear-display.ts +40 -0
  278. package/src/lib/world-grid-snap.ts +231 -0
  279. package/src/lib/zone-content.ts +130 -0
  280. package/src/store/use-alignment-guides.ts +21 -0
  281. package/src/store/use-direct-manipulation-feedback.ts +20 -0
  282. package/src/store/use-editor.tsx +767 -114
  283. package/src/store/use-facing-pose.ts +44 -0
  284. package/src/store/use-fence-curve-draft.ts +21 -0
  285. package/src/store/use-floorplan-draft-preview.ts +101 -0
  286. package/src/store/use-floorplan-marquee.ts +50 -0
  287. package/src/store/use-interaction-scope.test.ts +186 -0
  288. package/src/store/use-interaction-scope.ts +132 -0
  289. package/src/store/use-measurement-draft.test.ts +530 -0
  290. package/src/store/use-measurement-draft.ts +543 -0
  291. package/src/store/use-opening-guides.ts +41 -0
  292. package/src/store/use-placement-preview.ts +39 -0
  293. package/src/store/use-quick-measurement-hud.test.ts +53 -0
  294. package/src/store/use-quick-measurement-hud.ts +77 -0
  295. package/src/store/use-segment-draft-chain.ts +28 -0
  296. package/src/store/use-stair-build-preview.ts +43 -0
  297. package/src/store/use-wall-move-ghosts.ts +36 -0
  298. package/src/store/use-wall-snap-indicator.ts +34 -0
  299. package/src/components/editor/first-person/bvh-ecctrl.tsx +0 -860
  300. package/src/components/editor/preset-thumbnail-generator.tsx +0 -125
  301. package/src/components/editor-2d/renderers/floorplan-roof-layer.tsx +0 -113
  302. package/src/components/tools/building/move-building-tool.tsx +0 -157
  303. package/src/components/tools/ceiling/ceiling-boundary-editor.tsx +0 -43
  304. package/src/components/tools/ceiling/ceiling-hole-editor.tsx +0 -49
  305. package/src/components/tools/ceiling/ceiling-tool.tsx +0 -465
  306. package/src/components/tools/ceiling/move-ceiling-tool.tsx +0 -264
  307. package/src/components/tools/column/column-tool.tsx +0 -97
  308. package/src/components/tools/column/move-column-tool.tsx +0 -105
  309. package/src/components/tools/door/door-math.ts +0 -110
  310. package/src/components/tools/door/door-tool.tsx +0 -324
  311. package/src/components/tools/door/move-door-tool.tsx +0 -412
  312. package/src/components/tools/fence/curve-fence-tool.tsx +0 -178
  313. package/src/components/tools/fence/fence-tool.tsx +0 -346
  314. package/src/components/tools/fence/move-fence-endpoint-tool.tsx +0 -441
  315. package/src/components/tools/fence/move-fence-tool.tsx +0 -302
  316. package/src/components/tools/item/item-tool.tsx +0 -26
  317. package/src/components/tools/roof/move-roof-tool.tsx +0 -364
  318. package/src/components/tools/slab/move-slab-tool.tsx +0 -182
  319. package/src/components/tools/slab/slab-boundary-editor.tsx +0 -43
  320. package/src/components/tools/slab/slab-hole-editor.tsx +0 -49
  321. package/src/components/tools/slab/slab-tool.tsx +0 -322
  322. package/src/components/tools/spawn/move-spawn-tool.tsx +0 -101
  323. package/src/components/tools/spawn/spawn-tool.tsx +0 -130
  324. package/src/components/tools/wall/curve-wall-tool.tsx +0 -178
  325. package/src/components/tools/wall/move-wall-endpoint-tool.tsx +0 -426
  326. package/src/components/tools/wall/move-wall-tool.tsx +0 -358
  327. package/src/components/tools/wall/wall-tool.tsx +0 -332
  328. package/src/components/tools/window/move-window-tool.tsx +0 -447
  329. package/src/components/tools/window/window-math.ts +0 -117
  330. package/src/components/tools/window/window-tool.tsx +0 -332
  331. package/src/components/ui/helpers/ceiling-helper.tsx +0 -20
  332. package/src/components/ui/helpers/slab-helper.tsx +0 -20
  333. package/src/components/ui/helpers/wall-helper.tsx +0 -20
  334. package/src/components/ui/panels/ceiling-panel.tsx +0 -249
  335. package/src/components/ui/panels/column-panel.tsx +0 -759
  336. package/src/components/ui/panels/door-panel.tsx +0 -1299
  337. package/src/components/ui/panels/fence-panel.tsx +0 -222
  338. package/src/components/ui/panels/item-panel.tsx +0 -306
  339. package/src/components/ui/panels/paint-panel.tsx +0 -163
  340. package/src/components/ui/panels/presets/presets-popover.tsx +0 -511
  341. package/src/components/ui/panels/roof-panel.tsx +0 -280
  342. package/src/components/ui/panels/roof-segment-panel.tsx +0 -311
  343. package/src/components/ui/panels/slab-panel.tsx +0 -250
  344. package/src/components/ui/panels/spawn-panel.tsx +0 -161
  345. package/src/components/ui/panels/stair-panel.tsx +0 -577
  346. package/src/components/ui/panels/stair-segment-panel.tsx +0 -325
  347. package/src/components/ui/panels/wall-panel.tsx +0 -273
  348. package/src/components/ui/panels/window-panel.tsx +0 -968
  349. package/src/contexts/presets-context.tsx +0 -121
  350. package/src/hooks/use-contextual-tools.ts +0 -61
@@ -1,7 +1,18 @@
1
1
  'use client'
2
2
 
3
3
  import { emitter, sceneRegistry } from '@pascal-app/core'
4
- import { SSGI_PARAMS, snapLevelsToTruePositions, useViewer } from '@pascal-app/viewer'
4
+ import {
5
+ backdropGradient,
6
+ deepSkyColor,
7
+ GRID_LAYER,
8
+ getSceneTheme,
9
+ horizonHazeColor,
10
+ packNormalToRGB,
11
+ SSGI_PARAMS,
12
+ snapLevelsToTruePositions,
13
+ unpackRGBToNormal,
14
+ useViewer,
15
+ } from '@pascal-app/viewer'
5
16
  import type { CameraControls } from '@react-three/drei'
6
17
  import { useThree } from '@react-three/fiber'
7
18
  import { useCallback, useEffect, useRef } from 'react'
@@ -11,16 +22,18 @@ import { ssgi } from 'three/addons/tsl/display/SSGINode.js'
11
22
  import { denoise } from 'three/examples/jsm/tsl/display/DenoiseNode.js'
12
23
  import { fxaa } from 'three/examples/jsm/tsl/display/FXAANode.js'
13
24
  import {
14
- colorToDirection,
15
25
  convertToTexture,
16
26
  diffuseColor,
17
- directionToColor,
18
27
  float,
28
+ mix,
19
29
  mrt,
20
30
  normalView,
21
31
  output,
22
32
  pass,
23
33
  sample,
34
+ screenUV,
35
+ smoothstep,
36
+ uniform,
24
37
  vec4,
25
38
  } from 'three/tsl'
26
39
  import { RenderPipeline, RenderTarget, type WebGPURenderer } from 'three/webgpu'
@@ -54,6 +67,18 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
54
67
  const pipelineRef = useRef<RenderPipeline | null>(null)
55
68
  const renderTargetRef = useRef<RenderTarget | null>(null)
56
69
 
70
+ // Backdrop compositing for scene snapshots (studio renders, project
71
+ // thumbnails): theme background + sky gradient, same world-ray math as the
72
+ // viewport backdrop in viewer's post-processing. Uniform-driven so the one
73
+ // cached pipeline serves both opaque and transparent (preset/item) captures.
74
+ const bgColorUniform = useRef(uniform(new THREE.Color('#ffffff')))
75
+ const bgSkyUniform = useRef(uniform(new THREE.Color('#ffffff')))
76
+ const bgSkyDeepUniform = useRef(uniform(new THREE.Color('#ffffff')))
77
+ const bgHazeUniform = useRef(uniform(new THREE.Color('#ffffff')))
78
+ const bgProjInvUniform = useRef(uniform(new THREE.Matrix4()))
79
+ const bgCamWorldUniform = useRef(uniform(new THREE.Matrix4()))
80
+ const bgMixUniform = useRef(uniform(1))
81
+
57
82
  useEffect(() => {
58
83
  onThumbnailCaptureRef.current = onThumbnailCapture
59
84
  }, [onThumbnailCapture])
@@ -62,6 +87,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
62
87
  useEffect(() => {
63
88
  const cam = new THREE.PerspectiveCamera(60, THUMBNAIL_WIDTH / THUMBNAIL_HEIGHT, 0.1, 1000)
64
89
  cam.layers.disable(EDITOR_LAYER)
90
+ cam.layers.disable(GRID_LAYER)
65
91
  thumbnailCameraRef.current = cam
66
92
 
67
93
  let mounted = true
@@ -73,13 +99,13 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
73
99
 
74
100
  // pass() handles MRT internally for all material types, including custom
75
101
  // shaders — unlike renderer.setMRT() which crashes on non-NodeMaterials.
76
- // pass() also respects camera.layers, so EDITOR_LAYER objects are filtered.
102
+ // pass() also respects camera.layers, so EDITOR_LAYER + GRID_LAYER objects are filtered.
77
103
  const scenePass = pass(scene, cam)
78
104
  scenePass.setMRT(
79
105
  mrt({
80
106
  output,
81
107
  diffuseColor,
82
- normal: directionToColor(normalView),
108
+ normal: packNormalToRGB(normalView),
83
109
  }),
84
110
  )
85
111
 
@@ -90,7 +116,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
90
116
  scenePass.getTexture('diffuseColor').type = UnsignedByteType
91
117
  scenePass.getTexture('normal').type = UnsignedByteType
92
118
 
93
- const sceneNormal = sample((uv) => colorToDirection(scenePassNormal.sample(uv)))
119
+ const sceneNormal = sample((uv) => unpackRGBToNormal(scenePassNormal.sample(uv)))
94
120
 
95
121
  const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, cam as any)
96
122
  giPass.sliceCount.value = SSGI_PARAMS.sliceCount
@@ -105,14 +131,49 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
105
131
  giPass.useScreenSpaceSampling.value = SSGI_PARAMS.useScreenSpaceSampling
106
132
  giPass.useTemporalFiltering = SSGI_PARAMS.useTemporalFiltering
107
133
 
108
- const giTexture = (giPass as any).getTextureNode()
109
- const aoAsRgb = vec4(giTexture.a, giTexture.a, giTexture.a, float(1))
134
+ // r185: SSGI's AO lives in its own single-channel texture (getAONode)
135
+ // rather than the alpha of one packed rgba texture.
136
+ const aoTexture = (giPass as any).getAONode()
137
+ const aoAsRgb = vec4(aoTexture.r, aoTexture.r, aoTexture.r, float(1))
110
138
  const denoisePass = denoise(aoAsRgb, scenePassDepth, sceneNormal, cam)
111
139
  denoisePass.index.value = 0
112
140
  denoisePass.radius.value = 4
113
141
 
114
- const ao = (denoisePass as any).r
115
- const finalOutput = vec4(scenePassColor.rgb.mul(ao), scenePassColor.a)
142
+ // Same far-field AO fade as the viewport pipeline — without it the
143
+ // horizon picks up a visible AO line in captures.
144
+ const aoFarFade = smoothstep(
145
+ float(0.9994),
146
+ float(0.9998),
147
+ scenePassDepth.sample(screenUV).r,
148
+ )
149
+ const ao = mix((denoisePass as any).r, float(1), aoFarFade)
150
+ const sceneRgb = scenePassColor.rgb.mul(ao)
151
+
152
+ // Per-pixel world ray from the capture camera → sky gradient above the
153
+ // horizon (dir.y = 0), flat background below — mirrors the viewport
154
+ // backdrop. bgMix 0 bypasses it and keeps the capture transparent.
155
+ const ndc = vec4(
156
+ screenUV.x.mul(2).sub(1),
157
+ float(1).sub(screenUV.y).mul(2).sub(1),
158
+ 1,
159
+ 1,
160
+ ) as any
161
+ const viewRay = (bgProjInvUniform.current as any).mul(ndc)
162
+ const worldDir = (bgCamWorldUniform.current as any)
163
+ .mul(vec4(viewRay.xyz, 0))
164
+ .xyz.normalize()
165
+ const bgGradient = backdropGradient({
166
+ dirY: worldDir.y,
167
+ background: bgColorUniform.current,
168
+ haze: bgHazeUniform.current,
169
+ sky: bgSkyUniform.current,
170
+ skyDeep: bgSkyDeepUniform.current,
171
+ })
172
+ const alpha = scenePassColor.a
173
+ const finalOutput = vec4(
174
+ mix(sceneRgb, mix(bgGradient, sceneRgb, alpha), bgMixUniform.current),
175
+ mix(alpha, float(1), bgMixUniform.current),
176
+ )
116
177
 
117
178
  // FXAA requires a texture node as input; convertToTexture renders finalOutput
118
179
  // into an intermediate RT so FXAA can sample it with neighbour UV offsets.
@@ -150,7 +211,11 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
150
211
  snapLevels: boolean,
151
212
  captureMode?: 'standard' | 'viewport' | 'area',
152
213
  cropRegion?: { x: number; y: number; width: number; height: number },
214
+ standardSize?: { w: number; h: number },
215
+ transparent = false,
153
216
  ) => {
217
+ const standardW = standardSize?.w ?? THUMBNAIL_WIDTH
218
+ const standardH = standardSize?.h ?? THUMBNAIL_HEIGHT
154
219
  if (isGenerating.current) return
155
220
  if (!onThumbnailCaptureRef.current) return
156
221
 
@@ -172,6 +237,21 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
172
237
  const { width, height } = gl.domElement
173
238
  thumbnailCamera.aspect = width / height
174
239
  thumbnailCamera.updateProjectionMatrix()
240
+ // The capture camera never joins the scene graph, so its matrixWorld
241
+ // is only refreshed by the render itself — too late for the backdrop
242
+ // uniforms below.
243
+ thumbnailCamera.updateMatrixWorld()
244
+
245
+ const theme = getSceneTheme(useViewer.getState().sceneTheme)
246
+ bgColorUniform.current.value.set(theme.background)
247
+ bgSkyUniform.current.value.set(theme.backgroundSky ?? theme.background)
248
+ bgSkyDeepUniform.current.value.set(deepSkyColor(theme.backgroundSky ?? theme.background))
249
+ bgHazeUniform.current.value.set(
250
+ horizonHazeColor(theme.backgroundSky ?? theme.background, theme.appearance),
251
+ )
252
+ bgProjInvUniform.current.value.copy(thumbnailCamera.projectionMatrixInverse)
253
+ bgCamWorldUniform.current.value.copy(thumbnailCamera.matrixWorld)
254
+ bgMixUniform.current.value = transparent ? 0 : 1
175
255
 
176
256
  // Capture camera data for snapshot storage
177
257
  const pos = mainCamera.position
@@ -201,13 +281,15 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
201
281
  restoreLevels = snapLevelsToTruePositions()
202
282
  }
203
283
 
204
- // Hide scan and guide nodes directly so they are excluded from the
205
- // thumbnail regardless of whether ScanSystem/GuideSystem listeners are
206
- // registered. Returns a function that restores the original visibility.
284
+ // Hide scan, guide, and spawn nodes directly so they are excluded from
285
+ // the thumbnail regardless of whether ScanSystem/GuideSystem listeners
286
+ // are registered. Spawn renders on SCENE_LAYER for occlusion, so the
287
+ // thumbnail camera's layer mask can't filter it either. Returns a
288
+ // function that restores the original visibility.
207
289
  const restoreNodeVisibility = (() => {
208
290
  const saved = new Map<THREE.Object3D, boolean>()
209
- for (const type of ['scan', 'guide'] as const) {
210
- const ids = sceneRegistry.byType[type]
291
+ for (const type of ['scan', 'guide', 'spawn'] as const) {
292
+ const ids = sceneRegistry.byType[type]!
211
293
  ids.forEach((id) => {
212
294
  const node = sceneRegistry.nodes.get(id)
213
295
  if (node) {
@@ -237,18 +319,21 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
237
319
 
238
320
  // Notify other systems (wall cutouts, selection manager) to restore
239
321
  // their overrides before capture and re-apply them after.
240
- emitter.emit('thumbnail:before-capture', undefined)
241
- ;(renderer as any).setClearAlpha(0)
242
- renderer.setRenderTarget(rt)
243
- pipelineRef.current.render()
244
- renderer.setRenderTarget(null)
245
- emitter.emit('thumbnail:after-capture', undefined)
246
-
247
- // Restore level positions, levelMode, and node visibility immediately after the
248
- // render before the async GPU readback.
249
- restoreLevels()
250
- restoreLevelMode?.()
251
- restoreNodeVisibility()
322
+ try {
323
+ emitter.emit('thumbnail:before-capture', undefined)
324
+ ;(renderer as any).setClearAlpha(0)
325
+ renderer.setRenderTarget(rt)
326
+ pipelineRef.current.render()
327
+ } finally {
328
+ // Restore level positions, levelMode, and node visibility immediately
329
+ // after the render before the async GPU readback. Runs in `finally`
330
+ // so a render failure can't leave helpers permanently hidden.
331
+ renderer.setRenderTarget(null)
332
+ emitter.emit('thumbnail:after-capture', undefined)
333
+ restoreLevels()
334
+ restoreLevelMode?.()
335
+ restoreNodeVisibility()
336
+ }
252
337
 
253
338
  // Read pixels from the RT asynchronously.
254
339
  // WebGPU copyTextureToBuffer aligns each row to 256 bytes, so we must
@@ -336,9 +421,9 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
336
421
  offscreen.getContext('2d')!.drawImage(srcCanvas, sx, sy, outW, outH, 0, 0, outW, outH)
337
422
  blob = await offscreen.convertToBlob({ type: 'image/png' })
338
423
  } else {
339
- // Standard: center-crop to 1920×1080 aspect ratio
424
+ // Standard: center-crop to the requested aspect (default 1920×1080)
340
425
  const srcAspect = width / height
341
- const dstAspect = THUMBNAIL_WIDTH / THUMBNAIL_HEIGHT
426
+ const dstAspect = standardW / standardH
342
427
  let sx = 0,
343
428
  sy = 0,
344
429
  sWidth = width,
@@ -350,8 +435,8 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
350
435
  sHeight = Math.round(width / dstAspect)
351
436
  sy = Math.round((height - sHeight) / 2)
352
437
  }
353
- outW = THUMBNAIL_WIDTH
354
- outH = THUMBNAIL_HEIGHT
438
+ outW = standardW
439
+ outH = standardH
355
440
  const offscreen = new OffscreenCanvas(outW, outH)
356
441
  offscreen
357
442
  .getContext('2d')!
@@ -363,12 +448,15 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
363
448
  cameraData.resolution = { w: outW, h: outH }
364
449
  } else {
365
450
  // Fallback: plain render directly to the canvas
366
- emitter.emit('thumbnail:before-capture', undefined)
367
- gl.render(scene, thumbnailCamera)
368
- emitter.emit('thumbnail:after-capture', undefined)
369
- restoreLevels()
370
- restoreLevelMode?.()
371
- restoreNodeVisibility()
451
+ try {
452
+ emitter.emit('thumbnail:before-capture', undefined)
453
+ gl.render(scene, thumbnailCamera)
454
+ } finally {
455
+ emitter.emit('thumbnail:after-capture', undefined)
456
+ restoreLevels()
457
+ restoreLevelMode?.()
458
+ restoreNodeVisibility()
459
+ }
372
460
 
373
461
  let outW: number
374
462
  let outH: number
@@ -405,7 +493,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
405
493
  )
406
494
  } else {
407
495
  const srcAspect = width / height
408
- const dstAspect = THUMBNAIL_WIDTH / THUMBNAIL_HEIGHT
496
+ const dstAspect = standardW / standardH
409
497
  let sx = 0,
410
498
  sy = 0,
411
499
  sWidth = width,
@@ -417,8 +505,8 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
417
505
  sHeight = Math.round(width / dstAspect)
418
506
  sy = Math.round((height - sHeight) / 2)
419
507
  }
420
- outW = THUMBNAIL_WIDTH
421
- outH = THUMBNAIL_HEIGHT
508
+ outW = standardW
509
+ outH = standardH
422
510
  const offscreen = document.createElement('canvas')
423
511
  offscreen.width = outW
424
512
  offscreen.height = outH
@@ -459,9 +547,20 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro
459
547
  const handleGenerateThumbnail = async (event: {
460
548
  captureMode?: 'standard' | 'viewport' | 'area'
461
549
  cropRegion?: { x: number; y: number; width: number; height: number }
550
+ standardSize?: { w: number; h: number }
462
551
  snapLevels?: boolean
552
+ // Preset/item captures keep the alpha channel (their thumbnails compose
553
+ // onto arbitrary palette backgrounds); scene snapshots — studio renders
554
+ // and project thumbnails — composite the theme backdrop + sky.
555
+ transparent?: boolean
463
556
  }) => {
464
- await generate(event.snapLevels === true, event.captureMode, event.cropRegion)
557
+ await generate(
558
+ event.snapLevels === true,
559
+ event.captureMode,
560
+ event.cropRegion,
561
+ event.standardSize,
562
+ event.transparent === true,
563
+ )
465
564
  }
466
565
 
467
566
  emitter.on('camera-controls:generate-thumbnail', handleGenerateThumbnail)