@pascal-app/editor 1.0.7 → 1.1.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 (336) hide show
  1. package/package.json +9 -15
  2. package/src/components/editor/action-menu-button.tsx +1 -1
  3. package/src/components/editor/alignment-3d-guide-layer.tsx +1 -1
  4. package/src/components/editor/custom-camera-controls.tsx +101 -45
  5. package/src/components/editor/duplicate-node.ts +2 -1
  6. package/src/components/editor/editor-layout-v2.tsx +1 -1
  7. package/src/components/editor/elevation-3d-guide-layer.tsx +1 -1
  8. package/src/components/editor/first-person-controls.tsx +20 -1983
  9. package/src/components/editor/fit-scene-framing.test.ts +318 -0
  10. package/src/components/editor/fit-scene-framing.ts +142 -0
  11. package/src/components/editor/floating-action-menu.tsx +5 -3
  12. package/src/components/editor/floating-building-action-menu.tsx +1 -1
  13. package/src/components/editor/floor-openings-overlay.tsx +1 -1
  14. package/src/components/editor/floor-region-controls.tsx +8 -4
  15. package/src/components/editor/floorplan-navigation-presentation.test.ts +0 -40
  16. package/src/components/editor/floorplan-navigation-presentation.ts +0 -66
  17. package/src/components/editor/floorplan-panel.tsx +199 -30
  18. package/src/components/editor/floorplan-wall-typing-keys.test.tsx +152 -0
  19. package/src/components/editor/floorplan-wall-typing-keys.ts +79 -0
  20. package/src/components/editor/follow-pascal.test.tsx +1 -2
  21. package/src/components/editor/follow-pascal.tsx +1 -1
  22. package/src/components/editor/footprint-height-handle.tsx +2 -2
  23. package/src/components/editor/group-actions.ts +1 -2
  24. package/src/components/editor/group-floating-action-menu.tsx +1 -1
  25. package/src/components/editor/group-move-3d.ts +1 -2
  26. package/src/components/editor/group-rotate-handle.tsx +1 -2
  27. package/src/components/editor/group-transform-shared.ts +3 -1
  28. package/src/components/editor/handles/use-handle-drag.ts +1 -2
  29. package/src/components/editor/index.tsx +43 -13
  30. package/src/components/editor/inspector-extension-actions.tsx +1 -1
  31. package/src/components/editor/measurement-pill.tsx +1 -1
  32. package/src/components/editor/node-arrow-handles.tsx +2 -2
  33. package/src/components/editor/open-wall-ends-3d-layer.tsx +2 -3
  34. package/src/components/editor/open-wall-ends-hint.tsx +1 -1
  35. package/src/components/editor/opening-guides-3d-layer.tsx +1 -1
  36. package/src/components/editor/quick-measurement-card.tsx +1 -1
  37. package/src/components/editor/room-controls.tsx +10 -6
  38. package/src/components/editor/room-handles-2d.tsx +3 -3
  39. package/src/components/editor/room-handles.tsx +4 -5
  40. package/src/components/editor/room-highlight.tsx +6 -105
  41. package/src/components/editor/room-labels.test.ts +8 -822
  42. package/src/components/editor/room-labels.tsx +41 -1670
  43. package/src/components/editor/room-webgpu-materials.test.ts +1 -1
  44. package/src/components/editor/selection-manager.tsx +5 -3
  45. package/src/components/editor/site-edge-labels.tsx +7 -3
  46. package/src/components/editor/snapshot-capture-overlay.tsx +2 -5
  47. package/src/components/editor/use-floorplan-background-placement.ts +2 -1
  48. package/src/components/editor/views/plugins-view.tsx +1 -1
  49. package/src/components/editor/views/view-bar.tsx +9 -9
  50. package/src/components/editor/wall-measurement-label.tsx +31 -15
  51. package/src/components/editor/wall-move-side-handles.tsx +11 -7
  52. package/src/components/editor/wall-pull-grip.tsx +2 -2
  53. package/src/components/editor/wall-region-handles.tsx +2 -2
  54. package/src/components/editor/wall-snap-beacon-layer.tsx +53 -32
  55. package/src/components/editor-2d/floorplan-group-move.tsx +3 -4
  56. package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +13 -13
  57. package/src/components/editor-2d/floorplan-open-wall-ends-layer.tsx +2 -3
  58. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +2 -1
  59. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +23 -6
  60. package/src/components/editor-2d/renderers/floorplan-annotation-layout.test.ts +0 -37
  61. package/src/components/editor-2d/renderers/floorplan-annotation-layout.ts +0 -30
  62. package/src/components/editor-2d/renderers/floorplan-dimension-edit-overlay.tsx +13 -3
  63. package/src/components/editor-2d/renderers/floorplan-draft-measurement.tsx +7 -1
  64. package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +14 -3
  65. package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.test.tsx +35 -0
  66. package/src/components/editor-2d/renderers/floorplan-placement-typing-hud.tsx +65 -0
  67. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +30 -10
  68. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +39 -35
  69. package/src/components/systems/roof/roof-edit-system.test.tsx +15 -1
  70. package/src/components/systems/roof/roof-edit-system.tsx +33 -21
  71. package/src/components/tools/distribution/distribution-run-tool.test.tsx +371 -3
  72. package/src/components/tools/distribution/distribution-run-tool.tsx +151 -33
  73. package/src/components/tools/item/move-tool.tsx +1 -6
  74. package/src/components/tools/item/placement-history.test.tsx +1 -1
  75. package/src/components/tools/item/use-placement-coordinator.tsx +3 -4
  76. package/src/components/tools/paint-region/wall-paint-region-tool.tsx +7 -3
  77. package/src/components/tools/registry/move-registry-node-tool.test.ts +42 -1
  78. package/src/components/tools/registry/move-registry-node-tool.tsx +39 -16
  79. package/src/components/tools/shared/cursor-sphere.tsx +1 -1
  80. package/src/components/tools/shared/draft-measurement-label.tsx +1 -1
  81. package/src/components/tools/shared/placement-box.tsx +2 -6
  82. package/src/components/tools/shared/placement-coordinate-input.tsx +94 -0
  83. package/src/components/tools/shared/placement-dimension-guides.tsx +1 -1
  84. package/src/components/tools/shared/segment-angle.ts +0 -4
  85. package/src/components/tools/site/terrain-brush-cursor.tsx +1 -1
  86. package/src/components/tools/tool-manager.tsx +1 -24
  87. package/src/components/tools/wall/wall-typed-entry.test.ts +99 -0
  88. package/src/components/tools/wall/wall-typed-entry.ts +70 -0
  89. package/src/components/ui/action-menu/camera-actions.tsx +2 -2
  90. package/src/components/ui/action-menu/control-modes.tsx +1 -1
  91. package/src/components/ui/action-menu/follow-pascal-pill.tsx +1 -1
  92. package/src/components/ui/action-menu/index.tsx +3 -3
  93. package/src/components/ui/action-menu/measurement-control.tsx +3 -3
  94. package/src/components/ui/action-menu/placement.ts +0 -18
  95. package/src/components/ui/action-menu/point-ask-button.tsx +1 -1
  96. package/src/components/ui/action-menu/view-toggles.tsx +5 -5
  97. package/src/components/ui/controls/finish-swatch.tsx +1 -1
  98. package/src/components/ui/controls/material-paint-panel.tsx +2 -2
  99. package/src/components/ui/controls/scene-material-list.tsx +2 -2
  100. package/src/components/ui/controls/select-control.tsx +1 -1
  101. package/src/components/ui/controls/slider-control.tsx +18 -6
  102. package/src/components/ui/controls/terrain-sculpt-panel.tsx +1 -1
  103. package/src/components/ui/controls/tool-options-panel.tsx +74 -44
  104. package/src/components/ui/floating-level-selector.tsx +3 -3
  105. package/src/components/ui/helpers/contextual-helper-panel.tsx +1 -1
  106. package/src/components/ui/helpers/helper-manager.test.tsx +54 -1
  107. package/src/components/ui/helpers/helper-manager.tsx +55 -1
  108. package/src/components/ui/level-duplicate-dialog.tsx +1 -1
  109. package/src/components/ui/panels/collections/collections-popover.tsx +2 -2
  110. package/src/components/ui/panels/floor-foundation-panel.tsx +3 -3
  111. package/src/components/ui/panels/floor-opening-panel.tsx +5 -5
  112. package/src/components/ui/panels/mobile-selection-bar.tsx +28 -24
  113. package/src/components/ui/panels/multi-height-mode.tsx +3 -3
  114. package/src/components/ui/panels/multi-selection-panel.tsx +1 -1
  115. package/src/components/ui/panels/node-display.ts +1 -1
  116. package/src/components/ui/panels/panel-manager.tsx +5 -3
  117. package/src/components/ui/panels/panel-wrapper.tsx +42 -19
  118. package/src/components/ui/panels/parametric-inspector.tsx +1 -1
  119. package/src/components/ui/panels/room-construction-rows.tsx +7 -7
  120. package/src/components/ui/panels/room-name-options.ts +1 -5
  121. package/src/components/ui/panels/room-openings-field.tsx +1 -1
  122. package/src/components/ui/panels/room-panel.tsx +7 -9
  123. package/src/components/ui/primitives/color-dot.tsx +2 -2
  124. package/src/components/ui/primitives/opacity-control.tsx +2 -2
  125. package/src/components/ui/primitives/sidebar.tsx +3 -3
  126. package/src/components/ui/right-stack.test.ts +13 -6
  127. package/src/components/ui/right-stack.tsx +22 -11
  128. package/src/components/ui/sidebar/icon-rail.tsx +1 -1
  129. package/src/components/ui/sidebar/panels/items-panel/function-tree-panel.tsx +1 -1
  130. package/src/components/ui/sidebar/panels/settings-panel/audio-settings-dialog.tsx +2 -2
  131. package/src/components/ui/sidebar/panels/settings-panel/construction-animation-setting.tsx +1 -1
  132. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +2 -2
  133. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +1 -1
  134. package/src/components/ui/sidebar/panels/settings-panel/load-build-dialog.tsx +2 -6
  135. package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +1 -1
  136. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +1 -1
  137. package/src/components/ui/sidebar/panels/site-panel/ceiling-tree-node.tsx +1 -1
  138. package/src/components/ui/sidebar/panels/site-panel/index.tsx +3 -10
  139. package/src/components/ui/sidebar/panels/site-panel/inline-rename-input.tsx +1 -1
  140. package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +1 -1
  141. package/src/components/ui/sidebar/panels/site-panel/roof-tree-node.tsx +46 -7
  142. package/src/components/ui/sidebar/panels/site-panel/slab-tree-node.tsx +1 -1
  143. package/src/components/ui/sidebar/panels/site-panel/tree-node-actions.tsx +1 -1
  144. package/src/components/ui/sidebar/panels/site-panel/tree-node-drag.tsx +14 -5
  145. package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +2 -2
  146. package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +1 -1
  147. package/src/components/ui/sidebar/panels/zone-panel/index.tsx +3 -3
  148. package/src/components/ui/sidebar/tab-bar.tsx +1 -1
  149. package/src/components/ui/wall-type/wall-type-editor-dialog.tsx +2 -2
  150. package/src/components/ui/wall-type/wall-type-menu.tsx +1 -1
  151. package/src/components/viewer-overlay.tsx +1 -2
  152. package/src/hooks/use-auto-frame.ts +1 -2
  153. package/src/hooks/use-keyboard.ts +1 -2
  154. package/src/hooks/use-selected-room.ts +5 -7
  155. package/src/i18n/floor-footprint-name.ts +1 -1
  156. package/src/i18n/locales/en/messages.po +1036 -306
  157. package/src/i18n/locales/en/messages.ts +1 -1
  158. package/src/i18n/locales/en-XA/messages.ts +1 -1
  159. package/src/i18n/locales/es/messages.po +2267 -439
  160. package/src/i18n/locales/fr/messages.po +2267 -439
  161. package/src/i18n/locales/ja/messages.po +2267 -439
  162. package/src/i18n/locales/zh-CN/messages.po +2267 -439
  163. package/src/i18n/locales.test.ts +1 -1
  164. package/src/index.tsx +183 -178
  165. package/src/lib/below-grade.test.tsx +4 -4
  166. package/src/lib/camera-facing.test.ts +1 -1
  167. package/src/lib/camera-facing.ts +2 -3
  168. package/src/lib/contextual-help.ts +6 -0
  169. package/src/lib/editor-level-display.ts +1 -1
  170. package/src/lib/elevation-guides.ts +21 -6
  171. package/src/lib/elevator-support.ts +1 -72
  172. package/src/lib/floor-footprints.test.ts +3 -3
  173. package/src/lib/floor-footprints.ts +1 -9
  174. package/src/lib/floor-foundation-support.test.ts +8 -3
  175. package/src/lib/floor-opening-draft.ts +2 -2
  176. package/src/lib/floor-region-session.ts +1 -1
  177. package/src/lib/floorplan/annotation-visibility.test.ts +1 -1
  178. package/src/lib/floorplan/annotation-visibility.ts +1 -1
  179. package/src/lib/floorplan/floorplan-export.test.ts +1 -1
  180. package/src/lib/floorplan/floorplan-export.tsx +8 -11
  181. package/src/lib/floorplan/floorplan-extension.ts +4 -157
  182. package/src/lib/floorplan/floorplan-mode.ts +2 -1
  183. package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +1 -1
  184. package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +8 -6
  185. package/src/lib/floorplan/floorplan-plugin-install.test.tsx +3 -4
  186. package/src/lib/floorplan/geometry.ts +2 -112
  187. package/src/lib/floorplan/index.ts +0 -9
  188. package/src/lib/floorplan/items.ts +2 -1
  189. package/src/lib/floorplan/open-wall-ends.ts +2 -1
  190. package/src/lib/floorplan/schedules.ts +6 -3
  191. package/src/lib/floorplan/sheet-export.ts +7 -3
  192. package/src/lib/floorplan/site-plan/site-parts.ts +7 -1
  193. package/src/lib/floorplan/site-plan/site-plan-layer.tsx +1 -1
  194. package/src/lib/follow-pascal-presentation.ts +1 -1
  195. package/src/lib/gesture-lifecycle.ts +3 -1
  196. package/src/lib/glb-export.ts +1 -1
  197. package/src/lib/interaction/scope.ts +1 -0
  198. package/src/lib/layer-thickness.ts +1 -1
  199. package/src/lib/level-selection.test.ts +18 -2
  200. package/src/lib/level-selection.ts +31 -8
  201. package/src/lib/linear-display.ts +1 -1
  202. package/src/lib/live-gesture-round-trip.test.ts +1 -1
  203. package/src/lib/material-paint.ts +1 -1
  204. package/src/lib/measurement-label.test.ts +1 -1
  205. package/src/lib/measurements.test.ts +7 -5
  206. package/src/lib/mezzanine-selection.test.ts +1 -1
  207. package/src/lib/node-action-movement.test.tsx +115 -0
  208. package/src/lib/node-action-movement.ts +2 -0
  209. package/src/lib/paint-region-mode.ts +1 -1
  210. package/src/lib/placement-metadata.ts +4 -0
  211. package/src/lib/point-ask/describe-target.ts +6 -8
  212. package/src/lib/point-ask/region.ts +1 -1
  213. package/src/lib/point-ask/session.ts +1 -2
  214. package/src/lib/print-shell-compiler.ts +12 -1
  215. package/src/lib/print-wall-solids.test.ts +11 -0
  216. package/src/lib/print-wall-solids.ts +9 -0
  217. package/src/lib/roof-duplication.test.ts +100 -0
  218. package/src/lib/roof-duplication.ts +30 -5
  219. package/src/lib/room-built-on.test.ts +2 -5
  220. package/src/lib/room-built-on.ts +1 -15
  221. package/src/lib/room-construction-commands.ts +3 -2
  222. package/src/lib/room-controls-phase6.test.ts +1 -1
  223. package/src/lib/room-handle-drag.ts +8 -37
  224. package/src/lib/room-name-catalog.test.ts +2 -1
  225. package/src/lib/room-name-messages.test.ts +2 -1
  226. package/src/lib/room-name-messages.ts +1 -118
  227. package/src/lib/room-name-storage.test.ts +2 -1
  228. package/src/lib/room-push-dimensions.test.ts +1 -1
  229. package/src/lib/room-selection-commands.ts +2 -2
  230. package/src/lib/room-selection.test.ts +6 -6
  231. package/src/lib/room-transform-session.ts +1 -2
  232. package/src/lib/room-zone-routing.ts +2 -2
  233. package/src/lib/scene-clipboard.ts +2 -1
  234. package/src/lib/selection-routing.test.ts +0 -28
  235. package/src/lib/selection-routing.ts +1 -40
  236. package/src/lib/sfx-bus.ts +8 -32
  237. package/src/lib/sfx-player.test.ts +72 -1
  238. package/src/lib/sfx-player.ts +49 -337
  239. package/src/lib/snapping-mode.test.ts +23 -1
  240. package/src/lib/viewer-selection.test.ts +38 -10
  241. package/src/lib/wall-pull.test.ts +1 -1
  242. package/src/lib/wall-pull.ts +1 -27
  243. package/src/lib/wall-region-list.ts +3 -1
  244. package/src/lib/window-interaction.test.ts +4 -2
  245. package/src/store/use-editor.tsx +22 -26
  246. package/src/store/use-floorplan-annotation-visibility.ts +5 -5
  247. package/src/store/use-placement-typing.test.ts +165 -0
  248. package/src/store/use-placement-typing.ts +186 -0
  249. package/src/store/use-wall-region-handles-selection.ts +1 -2
  250. package/src/components/editor/__bench__/room-label-placement.bench.ts +0 -108
  251. package/src/components/editor/first-person/build-collider-world.test.ts +0 -596
  252. package/src/components/editor/first-person/build-collider-world.ts +0 -549
  253. package/src/components/editor/first-person/ground-holes.test.ts +0 -128
  254. package/src/components/editor/first-person/ground-holes.ts +0 -130
  255. package/src/components/editor/first-person/mechanism-targets.test.ts +0 -162
  256. package/src/components/editor/first-person/mechanism-targets.ts +0 -88
  257. package/src/components/editor/first-person/terrain-collider.test.ts +0 -132
  258. package/src/components/editor/first-person/terrain-collider.ts +0 -212
  259. package/src/components/editor/floorplan-camera-sync.test.ts +0 -249
  260. package/src/components/editor/floorplan-camera-sync.ts +0 -213
  261. package/src/components/editor/localized-html.tsx +0 -22
  262. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.test.tsx +0 -236
  263. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +0 -708
  264. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +0 -385
  265. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +0 -670
  266. package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +0 -89
  267. package/src/components/editor-2d/renderers/floorplan-label-angle.ts +0 -106
  268. package/src/components/editor-2d/svg-paths.ts +0 -124
  269. package/src/components/tools/elevator/elevator-defaults.ts +0 -8
  270. package/src/components/tools/elevator/elevator-tool.tsx +0 -372
  271. package/src/components/tools/elevator/move-elevator-tool.tsx +0 -262
  272. package/src/components/ui/action-menu/action-button.tsx +0 -89
  273. package/src/components/ui/action-menu/follow-pascal-toggle.tsx +0 -42
  274. package/src/components/ui/primitives/button.tsx +0 -69
  275. package/src/components/ui/primitives/dropdown-menu.tsx +0 -232
  276. package/src/components/ui/primitives/popover.tsx +0 -42
  277. package/src/components/ui/primitives/switch.tsx +0 -29
  278. package/src/components/ui/primitives/tooltip.tsx +0 -57
  279. package/src/components/ui/scene-loader.tsx +0 -73
  280. package/src/components/viewer/floorplan-compass-button.tsx +0 -67
  281. package/src/components/viewer/floorplan-preview-geometry.test.ts +0 -107
  282. package/src/components/viewer/floorplan-preview-geometry.ts +0 -281
  283. package/src/components/viewer/floorplan-preview-navigation.test.ts +0 -43
  284. package/src/components/viewer/floorplan-preview-navigation.ts +0 -62
  285. package/src/components/viewer/floorplan-preview-visibility.test.ts +0 -35
  286. package/src/components/viewer/floorplan-preview-visibility.ts +0 -19
  287. package/src/components/viewer/floorplan-preview.tsx +0 -1065
  288. package/src/components/viewer/render-settings-menu.tsx +0 -294
  289. package/src/components/viewer/sky-control.tsx +0 -710
  290. package/src/components/viewer/use-viewer-camera-navigation-sync.ts +0 -140
  291. package/src/components/viewer/use-viewer-ui-sounds.test.ts +0 -15
  292. package/src/components/viewer/use-viewer-ui-sounds.ts +0 -46
  293. package/src/components/viewer/viewer-controls-bar.tsx +0 -496
  294. package/src/components/viewer/viewer-hover-label.tsx +0 -42
  295. package/src/components/viewer/viewer-rooms.tsx +0 -248
  296. package/src/components/viewer/viewer-scene-header.tsx +0 -286
  297. package/src/components/viewer/viewer-selection-manager.tsx +0 -124
  298. package/src/components/viewer/viewer-stage-modes.test.ts +0 -38
  299. package/src/components/viewer/viewer-stage-modes.ts +0 -49
  300. package/src/components/viewer/viewer-stage-switcher.tsx +0 -97
  301. package/src/components/viewer/viewer-stage.test.tsx +0 -47
  302. package/src/components/viewer/viewer-stage.tsx +0 -221
  303. package/src/components/walkthrough-hud.tsx +0 -193
  304. package/src/hooks/use-mobile.ts +0 -19
  305. package/src/i18n/level-name.ts +0 -39
  306. package/src/i18n/scene-theme-name.ts +0 -16
  307. package/src/lib/below-grade.ts +0 -133
  308. package/src/lib/camera-pose.test.ts +0 -237
  309. package/src/lib/camera-pose.ts +0 -172
  310. package/src/lib/door-interaction.ts +0 -102
  311. package/src/lib/floorplan/floorplan-extension.test.ts +0 -42
  312. package/src/lib/floorplan/floorplan-readonly.test.ts +0 -47
  313. package/src/lib/floorplan/floorplan-readonly.ts +0 -95
  314. package/src/lib/floorplan/marks.test.ts +0 -213
  315. package/src/lib/floorplan/marks.ts +0 -238
  316. package/src/lib/measurement-label.ts +0 -68
  317. package/src/lib/measurements.ts +0 -225
  318. package/src/lib/node-display-label.ts +0 -37
  319. package/src/lib/room-assembly-overlay.test.ts +0 -275
  320. package/src/lib/room-assembly-overlay.ts +0 -265
  321. package/src/lib/room-selection.ts +0 -417
  322. package/src/lib/scene-bounds.test.ts +0 -183
  323. package/src/lib/scene-bounds.ts +0 -169
  324. package/src/lib/typing-target.ts +0 -9
  325. package/src/lib/viewer-selection.ts +0 -595
  326. package/src/lib/window-interaction.ts +0 -142
  327. package/src/lib/zone-content.ts +0 -133
  328. package/src/store/camera-pose-store.test.ts +0 -29
  329. package/src/store/camera-pose-store.ts +0 -27
  330. package/src/store/navigation-sync-pose-store.test.ts +0 -34
  331. package/src/store/navigation-sync-pose-store.ts +0 -27
  332. package/src/store/use-audio.test.ts +0 -146
  333. package/src/store/use-audio.tsx +0 -169
  334. package/src/store/use-first-person-hud.ts +0 -40
  335. package/src/store/use-follow-pascal.test.ts +0 -62
  336. package/src/store/use-follow-pascal.ts +0 -53
@@ -0,0 +1,318 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { CameraControlsImpl } from '@react-three/drei'
3
+ import * as THREE from 'three'
4
+ import {
5
+ computeFitSceneLookAt,
6
+ type PendingFitScene,
7
+ planFitSceneOnEvent,
8
+ planFitSceneOnOrbitResume,
9
+ planLevelFollow,
10
+ } from './fit-scene-framing'
11
+
12
+ const sampleBounds = {
13
+ center: [10, 20] as [number, number],
14
+ size: [30, 40] as [number, number],
15
+ }
16
+
17
+ describe('fit-scene framing', () => {
18
+ test('computes default look-at when bounds are null', () => {
19
+ expect(computeFitSceneLookAt(null)).toEqual({
20
+ eyeX: 20,
21
+ eyeY: 20,
22
+ eyeZ: 20,
23
+ targetX: 0,
24
+ targetY: 0,
25
+ targetZ: 0,
26
+ eyeHeight: 20,
27
+ fitSphere: { center: [0, 0, 0], radius: 15 },
28
+ })
29
+ })
30
+
31
+ test('computes orbit look-at from scene bounds', () => {
32
+ const lookAt = computeFitSceneLookAt(sampleBounds)
33
+ // Longer extent is depth=40 → distance=56, height=32
34
+ expect(lookAt).toEqual({
35
+ eyeX: 10 + 56 * 0.7,
36
+ eyeY: 32,
37
+ eyeZ: 20 + 56 * 0.7,
38
+ targetX: 10,
39
+ targetY: 0,
40
+ targetZ: 20,
41
+ eyeHeight: 32,
42
+ fitSphere: { center: [10, 0, 20], radius: 25 * 1.3 },
43
+ })
44
+ })
45
+
46
+ test('rests the pivot and lifts the eye by the level ground height', () => {
47
+ const lookAt = computeFitSceneLookAt(sampleBounds, 3)
48
+ expect(lookAt.targetY).toBe(3)
49
+ expect(lookAt.eyeY).toBe(35)
50
+ expect(lookAt.eyeHeight).toBe(32)
51
+ expect(computeFitSceneLookAt(null, 3)).toMatchObject({ eyeY: 23, targetY: 3 })
52
+ })
53
+
54
+ test('fits the orthographic sphere around the footprint at the pivot', () => {
55
+ const lookAt = computeFitSceneLookAt(sampleBounds, 3)
56
+ expect(lookAt.fitSphere.center).toEqual([lookAt.targetX, lookAt.targetY, lookAt.targetZ])
57
+ const [cx, cz] = sampleBounds.center
58
+ const [w, d] = sampleBounds.size
59
+ for (const [x, z] of [
60
+ [cx - w / 2, cz - d / 2],
61
+ [cx + w / 2, cz + d / 2],
62
+ [cx - w / 2, cz + d / 2],
63
+ ] as const) {
64
+ expect(Math.hypot(x - cx, z - cz)).toBeLessThan(lookAt.fitSphere.radius)
65
+ }
66
+ const lone = computeFitSceneLookAt({ center: [0, 0], size: [1, 0.2] })
67
+ expect(lone.fitSphere.radius).toBe(5)
68
+ expect(computeFitSceneLookAt(null, 3).fitSphere).toEqual({ center: [0, 3, 0], radius: 15 })
69
+ })
70
+
71
+ test('an orthographic camera fitted to the sphere shows the whole footprint whatever its zoom was', () => {
72
+ const previous = globalThis.DOMRect
73
+ globalThis.DOMRect ??= class {} as typeof DOMRect
74
+ CameraControlsImpl.install({ THREE })
75
+ const camera = new THREE.OrthographicCamera(-8, 8, 5, -5, 0.1, 1000)
76
+ const control = new CameraControlsImpl(camera)
77
+ const bounds = { center: [60, -20] as [number, number], size: [80, 30] as [number, number] }
78
+ try {
79
+ // The previous project left the camera zoomed in on a small room.
80
+ control.zoomTo(12, false)
81
+ const lookAt = computeFitSceneLookAt(bounds)
82
+ control.setLookAt(
83
+ lookAt.eyeX,
84
+ lookAt.eyeY,
85
+ lookAt.eyeZ,
86
+ lookAt.targetX,
87
+ lookAt.targetY,
88
+ lookAt.targetZ,
89
+ false,
90
+ )
91
+ const sphere = new THREE.Sphere(
92
+ new THREE.Vector3(...lookAt.fitSphere.center),
93
+ lookAt.fitSphere.radius,
94
+ )
95
+ control.fitToSphere(sphere, false)
96
+ control.update(0)
97
+ camera.updateMatrixWorld(true)
98
+
99
+ const target = control.getTarget(new THREE.Vector3())
100
+ expect(target.toArray()).toEqual([lookAt.targetX, lookAt.targetY, lookAt.targetZ])
101
+ const [cx, cz] = bounds.center
102
+ const [w, d] = bounds.size
103
+ for (const x of [cx - w / 2, cx + w / 2])
104
+ for (const z of [cz - d / 2, cz + d / 2]) {
105
+ const projected = new THREE.Vector3(x, 0, z).project(camera)
106
+ expect(Math.abs(projected.x)).toBeLessThanOrEqual(1)
107
+ expect(Math.abs(projected.y)).toBeLessThanOrEqual(1)
108
+ }
109
+ } finally {
110
+ control.dispose()
111
+ globalThis.DOMRect = previous
112
+ }
113
+ })
114
+
115
+ test('applies immediately when orbit controls are live', () => {
116
+ const plan = planFitSceneOnEvent({
117
+ isPreviewMode: false,
118
+ isFirstPersonMode: false,
119
+ hasControls: true,
120
+ bounds: sampleBounds,
121
+ })
122
+
123
+ expect(plan).toEqual({
124
+ action: 'apply',
125
+ lookAt: computeFitSceneLookAt(sampleBounds),
126
+ })
127
+ })
128
+
129
+ test('ignores fit-scene while preview mode is active', () => {
130
+ const plan = planFitSceneOnEvent({
131
+ isPreviewMode: true,
132
+ isFirstPersonMode: false,
133
+ hasControls: true,
134
+ bounds: sampleBounds,
135
+ })
136
+ expect(plan).toEqual({ action: 'ignore' })
137
+ })
138
+
139
+ test('queues fit-scene while first-person is active (scene-ready during FP)', () => {
140
+ const plan = planFitSceneOnEvent({
141
+ isPreviewMode: false,
142
+ isFirstPersonMode: true,
143
+ hasControls: false,
144
+ bounds: sampleBounds,
145
+ })
146
+
147
+ expect(plan).toEqual({
148
+ action: 'queue',
149
+ pending: { bounds: sampleBounds },
150
+ })
151
+ })
152
+
153
+ test('queues fit-scene when orbit controls are not mounted yet', () => {
154
+ const plan = planFitSceneOnEvent({
155
+ isPreviewMode: false,
156
+ isFirstPersonMode: false,
157
+ hasControls: false,
158
+ bounds: sampleBounds,
159
+ })
160
+
161
+ expect(plan).toEqual({
162
+ action: 'queue',
163
+ pending: { bounds: sampleBounds },
164
+ })
165
+ })
166
+
167
+ test('scene-ready during first-person then return to orbit applies the pending frame', () => {
168
+ // Mirrors the blocking lifecycle: fit arrives while FP is active, then
169
+ // orbit remounts and the queued frame must still apply.
170
+ let pending: PendingFitScene | null = null
171
+
172
+ const duringFirstPerson = planFitSceneOnEvent({
173
+ isPreviewMode: false,
174
+ isFirstPersonMode: true,
175
+ hasControls: false,
176
+ bounds: sampleBounds,
177
+ })
178
+ expect(duringFirstPerson.action).toBe('queue')
179
+ if (duringFirstPerson.action === 'queue') {
180
+ pending = duringFirstPerson.pending
181
+ }
182
+
183
+ // Still in first-person: controls are not ready, keep the pending frame.
184
+ expect(
185
+ planFitSceneOnOrbitResume({
186
+ isPreviewMode: false,
187
+ isFirstPersonMode: true,
188
+ hasControls: false,
189
+ pending,
190
+ }),
191
+ ).toEqual({ action: 'noop' })
192
+
193
+ // Leave first-person; orbit controls remount and can apply the frame.
194
+ const afterOrbitResume = planFitSceneOnOrbitResume({
195
+ isPreviewMode: false,
196
+ isFirstPersonMode: false,
197
+ hasControls: true,
198
+ pending,
199
+ })
200
+
201
+ expect(afterOrbitResume).toEqual({
202
+ action: 'apply',
203
+ lookAt: computeFitSceneLookAt(sampleBounds),
204
+ })
205
+ })
206
+
207
+ test('latest fit-scene while first-person wins when orbit resumes', () => {
208
+ let pending: PendingFitScene | null = null
209
+ const first = planFitSceneOnEvent({
210
+ isPreviewMode: false,
211
+ isFirstPersonMode: true,
212
+ hasControls: false,
213
+ bounds: null,
214
+ })
215
+ if (first.action === 'queue') pending = first.pending
216
+
217
+ const secondBounds = {
218
+ center: [0, 0] as [number, number],
219
+ size: [10, 10] as [number, number],
220
+ }
221
+ const second = planFitSceneOnEvent({
222
+ isPreviewMode: false,
223
+ isFirstPersonMode: true,
224
+ hasControls: false,
225
+ bounds: secondBounds,
226
+ })
227
+ if (second.action === 'queue') pending = second.pending
228
+
229
+ expect(
230
+ planFitSceneOnOrbitResume({
231
+ isPreviewMode: false,
232
+ isFirstPersonMode: false,
233
+ hasControls: true,
234
+ pending,
235
+ }),
236
+ ).toEqual({
237
+ action: 'apply',
238
+ lookAt: computeFitSceneLookAt(secondBounds),
239
+ })
240
+ })
241
+
242
+ test('does not flush a pending frame into preview mode', () => {
243
+ expect(
244
+ planFitSceneOnOrbitResume({
245
+ isPreviewMode: true,
246
+ isFirstPersonMode: false,
247
+ hasControls: true,
248
+ pending: { bounds: sampleBounds },
249
+ }),
250
+ ).toEqual({ action: 'noop' })
251
+ })
252
+ })
253
+
254
+ describe('level follow', () => {
255
+ type Inputs = { levelId: string | null; levelMode: string; camera: object; sceneEmpty: boolean }
256
+
257
+ // The effect's bookkeeping: one instance per mounted CustomCameraControls.
258
+ function mountLevelFollow(initial: Inputs) {
259
+ let firstLoad = true
260
+ let previous = { ...initial }
261
+ return (next: Inputs) => {
262
+ const plan = planLevelFollow({
263
+ firstLoad,
264
+ sceneEmpty: next.sceneEmpty,
265
+ levelChanged: previous.levelId !== next.levelId,
266
+ modeChanged: previous.levelMode !== next.levelMode,
267
+ cameraChanged: previous.camera !== next.camera,
268
+ })
269
+ firstLoad = false
270
+ previous = { ...next }
271
+ return plan
272
+ }
273
+ }
274
+
275
+ const perspective = {}
276
+ const loaded: Inputs = {
277
+ levelId: 'level_1',
278
+ levelMode: 'stacked',
279
+ camera: perspective,
280
+ sceneEmpty: false,
281
+ }
282
+
283
+ test('an empty first load takes the default pose', () => {
284
+ const run = mountLevelFollow({ ...loaded, levelId: null, sceneEmpty: true })
285
+ expect(run({ ...loaded, levelId: null, sceneEmpty: true })).toBe('default-pose')
286
+ })
287
+
288
+ test('a remount over a loaded scene rests the fresh pivot on the level', () => {
289
+ const run = mountLevelFollow(loaded)
290
+ expect(run(loaded)).toBe('rest-pivot')
291
+ expect(run(loaded)).toBe('none')
292
+ })
293
+
294
+ test('a level-mode change pans the pivot', () => {
295
+ const run = mountLevelFollow(loaded)
296
+ run(loaded)
297
+ expect(run({ ...loaded, levelMode: 'exploded' })).toBe('rest-pivot')
298
+ })
299
+
300
+ test('the first pick after a site-phase load pans to the level', () => {
301
+ const site = { ...loaded, levelId: null }
302
+ const run = mountLevelFollow(site)
303
+ run(site)
304
+ expect(run(loaded)).toBe('rest-pivot')
305
+ })
306
+
307
+ test('re-picking the same level does not move the pivot', () => {
308
+ const run = mountLevelFollow(loaded)
309
+ run(loaded)
310
+ expect(run({ ...loaded })).toBe('none')
311
+ })
312
+
313
+ test('a camera swap re-rests the new controls pivot', () => {
314
+ const run = mountLevelFollow(loaded)
315
+ run(loaded)
316
+ expect(run({ ...loaded, camera: {} })).toBe('rest-pivot')
317
+ })
318
+ })
@@ -0,0 +1,142 @@
1
+ export type FitSceneBounds = {
2
+ center: [number, number]
3
+ size: [number, number]
4
+ } | null
5
+
6
+ export type FitSceneLookAt = {
7
+ eyeX: number
8
+ eyeY: number
9
+ eyeZ: number
10
+ targetX: number
11
+ targetY: number
12
+ targetZ: number
13
+ /** The eye's height above `targetY`, before any keep-over-ground lift. */
14
+ eyeHeight: number
15
+ /**
16
+ * What an orthographic camera fits its zoom to: `setLookAt` leaves the
17
+ * previous project's zoom in place. Centred on the target, so fitting it
18
+ * does not move the pivot.
19
+ */
20
+ fitSphere: { center: [number, number, number]; radius: number }
21
+ }
22
+
23
+ // The empty-editor pose shows the default ±15 m site.
24
+ const DEFAULT_FIT_RADIUS = 15
25
+ // Margin around the footprint, as the frame-node path uses for orthographic fits.
26
+ const FIT_SPHERE_MARGIN = 1.3
27
+ // Keeps a lone wall from filling the viewport, like the perspective 15 m floor.
28
+ const MIN_FIT_RADIUS = 5
29
+
30
+ export type PendingFitScene = {
31
+ bounds: FitSceneBounds
32
+ }
33
+
34
+ export type FitSceneEventPlan =
35
+ | { action: 'ignore' }
36
+ | { action: 'apply'; lookAt: FitSceneLookAt }
37
+ | { action: 'queue'; pending: PendingFitScene }
38
+
39
+ export type FitSceneResumePlan = { action: 'noop' } | { action: 'apply'; lookAt: FitSceneLookAt }
40
+
41
+ /**
42
+ * Compute the orbit look-at used by `camera-controls:fit-scene`, resting the
43
+ * pivot at `groundY` (the selected level's orbit height).
44
+ * Null bounds restore the empty-editor default pose.
45
+ */
46
+ export function computeFitSceneLookAt(bounds: FitSceneBounds, groundY = 0): FitSceneLookAt {
47
+ if (!bounds) {
48
+ return {
49
+ eyeX: 20,
50
+ eyeY: 20 + groundY,
51
+ eyeZ: 20,
52
+ targetX: 0,
53
+ targetY: groundY,
54
+ targetZ: 0,
55
+ eyeHeight: 20,
56
+ fitSphere: { center: [0, groundY, 0], radius: DEFAULT_FIT_RADIUS },
57
+ }
58
+ }
59
+
60
+ const [cx, cz] = bounds.center
61
+ const [w, d] = bounds.size
62
+ // Use the longer horizontal extent to size the orbit radius so the whole
63
+ // footprint sits in view regardless of aspect ratio.
64
+ const maxExtent = Math.max(w, d)
65
+ const distance = Math.max(maxExtent * 1.4, 15)
66
+ const height = Math.max(maxExtent * 0.8, 10)
67
+ return {
68
+ eyeX: cx + distance * 0.7,
69
+ eyeY: height + groundY,
70
+ eyeZ: cz + distance * 0.7,
71
+ targetX: cx,
72
+ targetY: groundY,
73
+ targetZ: cz,
74
+ eyeHeight: height,
75
+ fitSphere: {
76
+ center: [cx, groundY, cz],
77
+ radius: Math.max((Math.hypot(w, d) / 2) * FIT_SPHERE_MARGIN, MIN_FIT_RADIUS),
78
+ },
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Decide what to do when a fit-scene event arrives.
84
+ * First-person (and briefly-unmounted orbit controls) must queue the frame
85
+ * so it can apply once orbit is back.
86
+ */
87
+ export function planFitSceneOnEvent(input: {
88
+ isPreviewMode: boolean
89
+ isFirstPersonMode: boolean
90
+ hasControls: boolean
91
+ bounds: FitSceneBounds
92
+ groundY?: number
93
+ }): FitSceneEventPlan {
94
+ if (input.isPreviewMode) return { action: 'ignore' }
95
+
96
+ const pending: PendingFitScene = { bounds: input.bounds }
97
+ if (input.isFirstPersonMode || !input.hasControls) {
98
+ return { action: 'queue', pending }
99
+ }
100
+
101
+ return { action: 'apply', lookAt: computeFitSceneLookAt(input.bounds, input.groundY) }
102
+ }
103
+
104
+ /**
105
+ * Flush a queued load frame once orbit controls can apply it. The ground is
106
+ * read at flush time: the selected level may have changed while it waited.
107
+ */
108
+ export function planFitSceneOnOrbitResume(input: {
109
+ isPreviewMode: boolean
110
+ isFirstPersonMode: boolean
111
+ hasControls: boolean
112
+ pending: PendingFitScene | null
113
+ groundY?: number
114
+ }): FitSceneResumePlan {
115
+ if (!input.pending || input.isPreviewMode || input.isFirstPersonMode || !input.hasControls) {
116
+ return { action: 'noop' }
117
+ }
118
+
119
+ return { action: 'apply', lookAt: computeFitSceneLookAt(input.pending.bounds, input.groundY) }
120
+ }
121
+
122
+ export type LevelFollowPlan = 'none' | 'default-pose' | 'rest-pivot'
123
+
124
+ /**
125
+ * What the level-follow effect does with the orbit pivot.
126
+ *
127
+ * Only a scene-less first run takes the default pose. A first run over a
128
+ * loaded scene is a fresh controls instance (mount, leaving XR, error-boundary
129
+ * recovery) whose pivot sits at the origin, so it is rested on the level like
130
+ * a camera change; on a real load the later scene-ready fit still wins.
131
+ * Afterwards the pivot follows level, level-mode and camera changes only.
132
+ */
133
+ export function planLevelFollow(input: {
134
+ firstLoad: boolean
135
+ sceneEmpty: boolean
136
+ levelChanged: boolean
137
+ modeChanged: boolean
138
+ cameraChanged: boolean
139
+ }): LevelFollowPlan {
140
+ if (input.firstLoad) return input.sceneEmpty ? 'default-pose' : 'rest-pivot'
141
+ return input.levelChanged || input.modeChanged || input.cameraChanged ? 'rest-pivot' : 'none'
142
+ }
@@ -14,6 +14,7 @@ import {
14
14
  getWallThickness,
15
15
  isCurvedWall,
16
16
  isDerivedNode,
17
+ isNodeDuplicable,
17
18
  isRegistryMovable,
18
19
  isRegistrySelectable,
19
20
  isSplineFence,
@@ -29,6 +30,7 @@ import {
29
30
  type WallNode,
30
31
  } from '@pascal-app/core'
31
32
  import { useViewer } from '@pascal-app/viewer'
33
+ import { LocalizedHtml as Html } from '@pascal-app/viewer/ui'
32
34
  import { useFrame } from '@react-three/fiber'
33
35
  import { useCallback, useMemo, useRef } from 'react'
34
36
  import * as THREE from 'three'
@@ -53,7 +55,6 @@ import useInteractionScope, {
53
55
  } from '../../store/use-interaction-scope'
54
56
  import { IconRefGlyph } from '../ui/icon-ref'
55
57
  import { duplicateNodeAndPickUp } from './duplicate-node'
56
- import { Html } from './localized-html'
57
58
  import { formatMeasurement, MeasurementPill } from './measurement-pill'
58
59
  import { NodeActionMenu } from './node-action-menu'
59
60
  import { startZoneRoomTransform } from './room-controls'
@@ -284,7 +285,7 @@ function getHeightPillDimensions(node: WallNode | FenceNode): {
284
285
  } {
285
286
  if (node.type === 'wall') {
286
287
  return {
287
- height: getWallEffectiveHeightForNodes(node, useScene.getState().nodes),
288
+ height: getWallEffectiveHeightForNodes(node, useScene.getState().nodes, 0),
288
289
  length: getWallCurveLength(node),
289
290
  thickness: getWallThickness(node),
290
291
  }
@@ -423,7 +424,7 @@ export function FloatingActionMenu() {
423
424
  | undefined
424
425
  const fallbackHeight =
425
426
  node.type === 'wall'
426
- ? getWallEffectiveHeightForNodes(node, useScene.getState().nodes)
427
+ ? getWallEffectiveHeightForNodes(node, useScene.getState().nodes, 0)
427
428
  : FENCE_DEFAULT_HEIGHT
428
429
  const liveHeight = override?.height ?? node.height ?? fallbackHeight
429
430
  pillHeightRef.current.textContent = `H ${formatMeasurement(liveHeight, unit, metricNotation)}`
@@ -681,6 +682,7 @@ export function FloatingActionMenu() {
681
682
  onDelete={handleDelete}
682
683
  onDuplicate={
683
684
  node &&
685
+ isNodeDuplicable(node as AnyNode) &&
684
686
  node.type !== 'spawn' &&
685
687
  !DELETE_ONLY_TYPES.includes(node.type) &&
686
688
  !HOLE_TYPES.includes(node.type)
@@ -2,12 +2,12 @@
2
2
 
3
3
  import { type BuildingNode, sceneRegistry, useScene } from '@pascal-app/core'
4
4
  import { useViewer } from '@pascal-app/viewer'
5
+ import { LocalizedHtml as Html } from '@pascal-app/viewer/ui'
5
6
  import { useFrame } from '@react-three/fiber'
6
7
  import { useCallback, useRef } from 'react'
7
8
  import * as THREE from 'three'
8
9
  import { sfxEmitter } from '../../lib/sfx-bus'
9
10
  import useEditor from '../../store/use-editor'
10
- import { Html } from './localized-html'
11
11
  import { NodeActionMenu } from './node-action-menu'
12
12
 
13
13
  export function FloatingBuildingActionMenu() {
@@ -5,6 +5,7 @@ import {
5
5
  type AnyNodeId,
6
6
  type FloorOpeningNode,
7
7
  resolveCeilingHeight,
8
+ roomFloorElevation,
8
9
  sceneRegistry,
9
10
  useScene,
10
11
  } from '@pascal-app/core'
@@ -24,7 +25,6 @@ import { useCeilingEditSession } from '../../lib/ceiling-edit-session'
24
25
  import { EDITOR_LAYER } from '../../lib/constants'
25
26
  import { openingRoom, roomSurfaceOpenings, useFloorEditSession } from '../../lib/floor-edit-session'
26
27
  import { useOpeningDraft } from '../../lib/floor-opening-draft'
27
- import { roomFloorElevation } from '../../lib/room-handle-drag'
28
28
  import { PolygonEditor } from '../tools/shared/polygon-editor'
29
29
 
30
30
  // The openings of the room being edited ("Edit floor", "Edit ceiling", or a
@@ -3,12 +3,19 @@
3
3
  import {
4
4
  containsPoint,
5
5
  emitter,
6
+ formatAreaLabel,
6
7
  type MultiPolygon,
7
8
  resolveCeilingHeight,
9
+ roomFloorElevation,
8
10
  sceneRegistry,
9
11
  useScene,
10
12
  } from '@pascal-app/core'
11
- import { getSceneTheme, setSurfaceRaycastLayers, useViewer } from '@pascal-app/viewer'
13
+ import {
14
+ getSceneTheme,
15
+ isTypingTarget,
16
+ setSurfaceRaycastLayers,
17
+ useViewer,
18
+ } from '@pascal-app/viewer'
12
19
  import { useFrame, useThree } from '@react-three/fiber'
13
20
  import { Pentagon, Square } from 'lucide-react'
14
21
  import { useEffect, useMemo, useRef } from 'react'
@@ -60,21 +67,18 @@ import {
60
67
  } from '../../lib/floor-region-session'
61
68
  import { type FloorRegionPoint, snapFloorRegionPoint } from '../../lib/floor-region-snap'
62
69
  import { clientToPlan } from '../../lib/floorplan/plan-coords'
63
- import { formatAreaLabel } from '../../lib/measurements'
64
70
  import {
65
71
  cancelMezzanineDraft,
66
72
  mezzanineDraftActive,
67
73
  useMezzanineDraft,
68
74
  } from '../../lib/mezzanine-draft'
69
75
  import { usePaintRegionMode } from '../../lib/paint-region-mode'
70
- import { roomFloorElevation } from '../../lib/room-handle-drag'
71
76
  import {
72
77
  cancelTerraceDraft,
73
78
  terraceDraftActive,
74
79
  toggleTerraceShape,
75
80
  useTerraceDraft,
76
81
  } from '../../lib/terrace-draft'
77
- import { isTypingTarget } from '../../lib/typing-target'
78
82
  import useEditor from '../../store/use-editor'
79
83
  import { useFloorplanRender } from '../editor-2d/floorplan-render-context'
80
84
  import { usePaintTint } from '../tools/paint-region/use-paint-tint'
@@ -2,13 +2,11 @@ import { describe, expect, test } from 'bun:test'
2
2
  import {
3
3
  canApplyFloorplanNavigationSync,
4
4
  canZoomFloorplanDuringNavigation,
5
- createFloorplanNavigationSyncScheduler,
6
5
  finalizeFloorplanNavigation,
7
6
  flushFloorplanRotationPresentationRestore,
8
7
  getFloorplanRotationOverscanViewBox,
9
8
  queueFloorplanRotationPresentationRestore,
10
9
  resolveFloorplanPresentationViewBox,
11
- setFloorplanCompassRotation,
12
10
  } from './floorplan-navigation-presentation'
13
11
 
14
12
  describe('floorplan navigation presentation', () => {
@@ -60,16 +58,6 @@ describe('floorplan navigation presentation', () => {
60
58
  expect(canApplyFloorplanNavigationSync(false)).toBe(true)
61
59
  })
62
60
 
63
- test('updates the compass presentation on every live rotation frame', () => {
64
- const compass = { style: { transform: '' } }
65
-
66
- setFloorplanCompassRotation(compass, 0.25)
67
- expect(compass.style.transform).toBe('rotate(0.25deg)')
68
-
69
- setFloorplanCompassRotation(compass, 0.5)
70
- expect(compass.style.transform).toBe('rotate(0.5deg)')
71
- })
72
-
73
61
  test('keeps the rotation preview in place until the committed state is ready to paint', () => {
74
62
  const pending = { current: null }
75
63
  const svg = {
@@ -111,32 +99,4 @@ describe('floorplan navigation presentation', () => {
111
99
 
112
100
  expect(calls).toEqual(['zoom', 'pan', 'rotation:42'])
113
101
  })
114
-
115
- test('coalesces a camera pose stream into one settled React commit', () => {
116
- const presented: number[] = []
117
- const committed: number[] = []
118
- let scheduled: (() => void) | null = null
119
- const scheduler = createFloorplanNavigationSyncScheduler<number>({
120
- applyPresentation: (pose) => presented.push(pose),
121
- commit: (pose) => committed.push(pose),
122
- schedule: (callback) => {
123
- scheduled = callback
124
- return 1 as unknown as ReturnType<typeof globalThis.setTimeout>
125
- },
126
- cancel: () => {
127
- scheduled = null
128
- },
129
- })
130
-
131
- for (let pose = 1; pose <= 30; pose += 1) {
132
- scheduler.update(pose)
133
- }
134
-
135
- expect(presented).toHaveLength(30)
136
- expect(committed).toEqual([])
137
-
138
- const settle = scheduled as (() => void) | null
139
- settle?.()
140
- expect(committed).toEqual([30])
141
- })
142
102
  })