@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,152 @@
1
+ import { beforeEach, expect, test } from 'bun:test'
2
+ import { emitter } from '@pascal-app/core'
3
+ import { cancelActiveTool, useKeyboard } from '../../hooks/use-keyboard'
4
+ import useEditor from '../../store/use-editor'
5
+ import {
6
+ PLACEMENT_TYPING_LENGTH_ANGLE,
7
+ placementTypingLengthOptions,
8
+ usePlacementTyping,
9
+ } from '../../store/use-placement-typing'
10
+ import { sceneLayout } from '../../store/view-layout'
11
+ import { withSelectionHarness } from '../../test-utils/selection-harness'
12
+ import type { WallPlanPoint } from '../tools/wall/wall-drafting'
13
+ import {
14
+ endFloorplanWallTypingOnCancel,
15
+ type FloorplanWallTypingDraft,
16
+ installFloorplanWallTypingKeys,
17
+ } from './floorplan-wall-typing-keys'
18
+
19
+ function GlobalShortcuts() {
20
+ useKeyboard()
21
+ return null
22
+ }
23
+
24
+ const press = (key: string) => {
25
+ const event = Object.assign(new Event('keydown', { cancelable: true }), {
26
+ key,
27
+ code: '',
28
+ metaKey: false,
29
+ ctrlKey: false,
30
+ altKey: false,
31
+ shiftKey: false,
32
+ repeat: false,
33
+ })
34
+ window.dispatchEvent(event)
35
+ }
36
+
37
+ beforeEach(() => {
38
+ usePlacementTyping.getState().clear()
39
+ })
40
+
41
+ test('2D-only typed wall keys run ahead of the global shortcuts', async () => {
42
+ await withSelectionHarness(async ({ render }) => {
43
+ useEditor.getState().setViewLayout(sceneLayout('2d'))
44
+ useEditor.getState().setPhase('building')
45
+ useEditor.getState().armToolMode({ mode: 'build', tool: 'wall' })
46
+ // The global handler is registered first, as the app mounts it.
47
+ await render(<GlobalShortcuts />)
48
+
49
+ let draft: FloorplanWallTypingDraft | null = {
50
+ start: [0, 0],
51
+ pointer: [2, 0],
52
+ lengthOptions: placementTypingLengthOptions('metric', 'meters'),
53
+ }
54
+ const committed: WallPlanPoint[] = []
55
+ const dropDraft = () => {
56
+ draft = null
57
+ }
58
+ emitter.on('tool:cancel', dropDraft)
59
+ const uninstall = installFloorplanWallTypingKeys({
60
+ getDraft: () => draft,
61
+ commit: (pointer) => committed.push(pointer),
62
+ })
63
+ try {
64
+ press('1')
65
+ press('m')
66
+ expect(usePlacementTyping.getState().fields[0]).toBe('1m')
67
+ expect(useEditor.getState().phase).toBe('building')
68
+ expect(useEditor.getState().mode).toBe('build')
69
+ expect(useEditor.getState().tool).toBe('wall')
70
+
71
+ press('Escape')
72
+ expect(usePlacementTyping.getState().isActive).toBe(false)
73
+ expect(draft).not.toBeNull()
74
+ expect(useEditor.getState().tool).toBe('wall')
75
+
76
+ press('Escape')
77
+ expect(draft).toBeNull()
78
+ expect(committed).toEqual([])
79
+ } finally {
80
+ uninstall()
81
+ emitter.off('tool:cancel', dropDraft)
82
+ usePlacementTyping.getState().clear()
83
+ }
84
+ })
85
+ })
86
+
87
+ test('Enter commits the pointer the typed fields re-project, only in 2D-only view', async () => {
88
+ await withSelectionHarness(async () => {
89
+ const committed: WallPlanPoint[] = []
90
+ const uninstall = installFloorplanWallTypingKeys({
91
+ getDraft: () => ({
92
+ start: [0, 0],
93
+ pointer: [2, 0],
94
+ lengthOptions: placementTypingLengthOptions('metric', 'meters'),
95
+ }),
96
+ commit: (pointer) => committed.push(pointer),
97
+ })
98
+ try {
99
+ useEditor.getState().setViewLayout(sceneLayout('split'))
100
+ press('3')
101
+ expect(usePlacementTyping.getState().isActive).toBe(false)
102
+
103
+ useEditor.getState().setViewLayout(sceneLayout('2d'))
104
+ press('3')
105
+ press('Enter')
106
+ expect(committed).toEqual([[2, 0]])
107
+ } finally {
108
+ uninstall()
109
+ usePlacementTyping.getState().clear()
110
+ }
111
+ })
112
+ })
113
+
114
+ test('in 2D-only view a cancel from any source first ends the typed session, then the draft', async () => {
115
+ await withSelectionHarness(async () => {
116
+ useEditor.getState().setViewLayout(sceneLayout('2d'))
117
+ useEditor.getState().armToolMode({ mode: 'build', tool: 'wall' })
118
+ let draftKept = true
119
+ // The plan's own `tool:cancel` handler.
120
+ const onCancel = () => {
121
+ if (endFloorplanWallTypingOnCancel()) return
122
+ draftKept = false
123
+ }
124
+ emitter.on('tool:cancel', onCancel)
125
+ try {
126
+ usePlacementTyping.getState().begin({ labels: PLACEMENT_TYPING_LENGTH_ANGLE })
127
+ usePlacementTyping.getState().append('3')
128
+
129
+ // The path ⌘Z takes mid-draft, not a keydown the plan sees itself.
130
+ expect(cancelActiveTool()).toBe(true)
131
+ expect(usePlacementTyping.getState().isActive).toBe(false)
132
+ expect(draftKept).toBe(true)
133
+ expect(useEditor.getState().mode).toBe('build')
134
+ expect(useEditor.getState().tool).toBe('wall')
135
+
136
+ cancelActiveTool()
137
+ expect(draftKept).toBe(false)
138
+ } finally {
139
+ emitter.off('tool:cancel', onCancel)
140
+ }
141
+ })
142
+ })
143
+
144
+ test('in split view the typed session and its cancel stay with the 3D wall tool', async () => {
145
+ await withSelectionHarness(async () => {
146
+ useEditor.getState().setViewLayout(sceneLayout('split'))
147
+ useEditor.getState().armToolMode({ mode: 'build', tool: 'wall' })
148
+ usePlacementTyping.getState().begin({ labels: PLACEMENT_TYPING_LENGTH_ANGLE })
149
+ expect(endFloorplanWallTypingOnCancel()).toBe(true)
150
+ expect(usePlacementTyping.getState().isActive).toBe(true)
151
+ })
152
+ })
@@ -0,0 +1,79 @@
1
+ import { isTypingTarget } from '@pascal-app/viewer'
2
+ import { markToolCancelConsumed } from '../../hooks/use-keyboard'
3
+ import type { ParseMeasurementOptions } from '../../lib/measurement-parser'
4
+ import useEditor from '../../store/use-editor'
5
+ import {
6
+ PLACEMENT_TYPING_LENGTH_ANGLE,
7
+ routePlacementTypingKey,
8
+ usePlacementTyping,
9
+ } from '../../store/use-placement-typing'
10
+ import { visibleScene } from '../../store/view-layout'
11
+ import { getSegmentGridStep, type WallPlanPoint } from '../tools/wall/wall-drafting'
12
+ import { resolveTypedWallEnd } from '../tools/wall/wall-typed-entry'
13
+
14
+ export type FloorplanWallTypingDraft = {
15
+ start: WallPlanPoint
16
+ /** The plan-snapped pointer the typed fields re-project. */
17
+ pointer: WallPlanPoint
18
+ lengthOptions: ParseMeasurementOptions
19
+ }
20
+
21
+ /**
22
+ * Typed entry for the plan's own wall draft (2D-only view; in split view the
23
+ * 3D wall tool drafts and owns these keys). A window capture listener, so it
24
+ * runs before the global shortcuts and a typed `1`, `m` or the session's
25
+ * Escape never reaches them.
26
+ */
27
+ export function installFloorplanWallTypingKeys({
28
+ getDraft,
29
+ commit,
30
+ }: {
31
+ getDraft: () => FloorplanWallTypingDraft | null
32
+ commit: (pointer: WallPlanPoint) => void
33
+ }): () => void {
34
+ const onKeyDown = (event: KeyboardEvent) => {
35
+ if (isTypingTarget(event.target)) return
36
+ if (visibleScene(useEditor.getState()) !== '2d') return
37
+ const draft = getDraft()
38
+ if (!draft) return
39
+ const outcome = routePlacementTypingKey(event, {
40
+ begin: (activeField) => {
41
+ usePlacementTyping.getState().begin({ labels: PLACEMENT_TYPING_LENGTH_ANGLE, activeField })
42
+ return true
43
+ },
44
+ tabBegins: true,
45
+ })
46
+ if (!outcome) return
47
+ event.preventDefault()
48
+ event.stopPropagation()
49
+ if (outcome !== 'commit') return
50
+ const typedEnd = resolveTypedWallEnd(
51
+ draft.start,
52
+ draft.pointer,
53
+ usePlacementTyping.getState().fields,
54
+ draft.lengthOptions,
55
+ getSegmentGridStep(),
56
+ )
57
+ if (typedEnd) commit(draft.pointer)
58
+ }
59
+ window.addEventListener('keydown', onKeyDown, true)
60
+ return () => window.removeEventListener('keydown', onKeyDown, true)
61
+ }
62
+
63
+ /**
64
+ * The plan's `tool:cancel` first stage. A cancel from any source (Escape, ⌘Z,
65
+ * a toolbar button) while a typed wall session is open only ends that
66
+ * session: in 2D-only view the plan clears it and consumes the cancel. In
67
+ * split view the 3D wall tool owns the session and its cancel. Returns true
68
+ * when the plan's wall draft must be kept.
69
+ */
70
+ export function endFloorplanWallTypingOnCancel(): boolean {
71
+ const editor = useEditor.getState()
72
+ if (!(editor.mode === 'build' && editor.tool === 'wall')) return false
73
+ if (!usePlacementTyping.getState().isActive) return false
74
+ if (visibleScene(editor) === '2d') {
75
+ markToolCancelConsumed()
76
+ usePlacementTyping.getState().clear()
77
+ }
78
+ return true
79
+ }
@@ -18,12 +18,11 @@ import {
18
18
  WindowNode,
19
19
  } from '@pascal-app/core'
20
20
  import { isNodeRevealing, startConstructionReveal, useViewer } from '@pascal-app/viewer'
21
+ import { followBelowGrade, useFollowPascal } from '@pascal-app/viewer/ui'
21
22
  import { act, create } from '@react-three/test-renderer'
22
23
  import { BoxGeometry, DoubleSide, Group, Mesh, MeshBasicMaterial, Raycaster, Vector3 } from 'three'
23
- import { followBelowGrade } from '../../lib/below-grade'
24
24
  import { openingExterior, wallExterior } from '../../lib/follow-pascal-exterior.test-fixture'
25
25
  import useEditor from '../../store/use-editor'
26
- import useFollowPascal from '../../store/use-follow-pascal'
27
26
  import { usePointAsk } from '../../store/use-point-ask'
28
27
  import { FollowPascal } from './follow-pascal'
29
28
 
@@ -18,6 +18,7 @@ import {
18
18
  subscribeRevealEvents,
19
19
  useViewer,
20
20
  } from '@pascal-app/viewer'
21
+ import { useFollowPascal } from '@pascal-app/viewer/ui'
21
22
  import { useFrame, useThree } from '@react-three/fiber'
22
23
  import { useEffect, useMemo, useRef } from 'react'
23
24
  import { Vector3 } from 'three'
@@ -34,7 +35,6 @@ import { FollowPresentation, type PresentationPort } from '../../lib/follow-pasc
34
35
  import { FollowSight } from '../../lib/follow-pascal-sight'
35
36
  import { stayOnLevel, wantFromEvent } from '../../lib/follow-pascal-targets'
36
37
  import useEditor from '../../store/use-editor'
37
- import useFollowPascal from '../../store/use-follow-pascal'
38
38
  import { usePointAsk } from '../../store/use-point-ask'
39
39
 
40
40
  /**
@@ -2,6 +2,7 @@
2
2
 
3
3
  import {
4
4
  type AnyNodeId,
5
+ formatLinearMeasurement,
5
6
  getStoredLevelHeight,
6
7
  type Point,
7
8
  type SlabNode,
@@ -10,6 +11,7 @@ import {
10
11
  useScene,
11
12
  } from '@pascal-app/core'
12
13
  import { useViewer } from '@pascal-app/viewer'
14
+ import { LocalizedHtml as Html } from '@pascal-app/viewer/ui'
13
15
  import { type ThreeEvent, useFrame, useThree } from '@react-three/fiber'
14
16
  import { useRef, useState } from 'react'
15
17
  import { type Group, Plane, Vector2, Vector3 } from 'three'
@@ -20,12 +22,10 @@ import {
20
22
  THICK_FLOOR_HINT,
21
23
  thickFloorAdvice,
22
24
  } from '../../lib/floor-footprints'
23
- import { formatLinearMeasurement } from '../../lib/measurements'
24
25
  import { runRoomHandleDrag } from '../../lib/room-handle-drag'
25
26
  import useEditor, { isGridSnapActive } from '../../store/use-editor'
26
27
  import useInteractionScope from '../../store/use-interaction-scope'
27
28
  import { suppressBoxSelectForPointer } from '../tools/select/box-select-state'
28
- import { Html } from './localized-html'
29
29
  import { ElevationArrows, outlineCentroid, useHandleScale } from './room-handles'
30
30
 
31
31
  export const FOOTPRINT_HEIGHT_DRAG_LABEL = 'footprint-height'
@@ -14,7 +14,7 @@ import {
14
14
  useLiveTransforms,
15
15
  useScene,
16
16
  } from '@pascal-app/core'
17
- import { markPerfAction, useViewer } from '@pascal-app/viewer'
17
+ import { isTypingTarget, markPerfAction, useViewer } from '@pascal-app/viewer'
18
18
  import { Plane, Vector2, Vector3 } from 'three'
19
19
  import { GROUP_MOVE_DRAG_LABEL } from '../../lib/contextual-help'
20
20
  import { clientToPlan } from '../../lib/floorplan/plan-coords'
@@ -28,7 +28,6 @@ import {
28
28
  pasteSystemEditorClipboardToLevel,
29
29
  } from '../../lib/scene-clipboard'
30
30
  import { emitDeleteSFX, sfxEmitter } from '../../lib/sfx-bus'
31
- import { isTypingTarget } from '../../lib/typing-target'
32
31
  import useAlignmentGuides from '../../store/use-alignment-guides'
33
32
  import useDeleteConfirmation from '../../store/use-delete-confirmation'
34
33
  import useEditor, {
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { type AnyNodeId, 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, useMemo, useRef } from 'react'
7
8
  import * as THREE from 'three'
@@ -20,7 +21,6 @@ import {
20
21
  computeGroupPlanBox,
21
22
  levelFrame,
22
23
  } from './group-transform-shared'
23
- import { Html } from './localized-html'
24
24
  import { NodeActionMenu } from './node-action-menu'
25
25
  import { useMeshSettleEpoch } from './use-mesh-settle-epoch'
26
26
 
@@ -12,13 +12,12 @@ import {
12
12
  useLiveTransforms,
13
13
  useScene,
14
14
  } from '@pascal-app/core'
15
- import { useViewer } from '@pascal-app/viewer'
15
+ import { isTypingTarget, useViewer } from '@pascal-app/viewer'
16
16
  import { type Camera, Plane, type Raycaster, Vector2, Vector3 } from 'three'
17
17
  import { GROUP_MOVE_DRAG_LABEL } from '../../lib/contextual-help'
18
18
  import { isHistoryShortcut } from '../../lib/history'
19
19
  import type { WallGroupPush } from '../../lib/room-handle-drag'
20
20
  import { sfxEmitter } from '../../lib/sfx-bus'
21
- import { isTypingTarget } from '../../lib/typing-target'
22
21
  import useAlignmentGuides from '../../store/use-alignment-guides'
23
22
  import useEditor, {
24
23
  isAlignmentGuideActive,
@@ -10,7 +10,7 @@ import {
10
10
  useLiveTransforms,
11
11
  useScene,
12
12
  } from '@pascal-app/core'
13
- import { useViewer } from '@pascal-app/viewer'
13
+ import { isTypingTarget, useViewer } from '@pascal-app/viewer'
14
14
  import { createPortal, type ThreeEvent, useThree } from '@react-three/fiber'
15
15
  import { useEffect, useMemo, useRef, useState } from 'react'
16
16
  import { OrthographicCamera, Plane, type Ray, Vector2, Vector3 } from 'three'
@@ -19,7 +19,6 @@ import { isHistoryShortcut } from '../../lib/history'
19
19
  import { sfxEmitter } from '../../lib/sfx-bus'
20
20
  import { intersectSpatialDragPlane } from '../../lib/spatial-drag-plane'
21
21
  import { getSpatialPointerId, spatialPointerInput } from '../../lib/spatial-pointer-input'
22
- import { isTypingTarget } from '../../lib/typing-target'
23
22
  import useEditor, { isAngleSnapActive } from '../../store/use-editor'
24
23
  import useInteractionScope, {
25
24
  useActiveHandleDrag,
@@ -3,6 +3,7 @@ import {
3
3
  type AnyNodeId,
4
4
  calculateLevelMiters,
5
5
  getWallPlanFootprint,
6
+ isMoveLocked,
6
7
  nodeRegistry,
7
8
  resolveBuildingForLevel,
8
9
  sceneRegistry,
@@ -99,7 +100,8 @@ export function classifyParticipant(
99
100
  levelId: string | null,
100
101
  sceneNodes: Record<string, AnyNode | undefined>,
101
102
  ): ParticipantKind | null {
102
- if (!node || !isInGroupTransformScope(node, levelId, sceneNodes)) return null
103
+ if (!node || !isInGroupTransformScope(node, levelId, sceneNodes) || isMoveLocked(node))
104
+ return null
103
105
  const shape = classifyPlacementShape(node)
104
106
  if (shape) return shape
105
107
  const start = (node as { start?: unknown }).start
@@ -11,7 +11,7 @@ import {
11
11
  useLiveNodeOverrides,
12
12
  useScene,
13
13
  } from '@pascal-app/core'
14
- import { useViewer } from '@pascal-app/viewer'
14
+ import { isTypingTarget, useViewer } from '@pascal-app/viewer'
15
15
  import { type ThreeEvent, useThree } from '@react-three/fiber'
16
16
  import { useEffect, useRef } from 'react'
17
17
  import {
@@ -28,7 +28,6 @@ import { isHistoryShortcut } from '../../../lib/history'
28
28
  import { sfxEmitter } from '../../../lib/sfx-bus'
29
29
  import { intersectSpatialDragPlane } from '../../../lib/spatial-drag-plane'
30
30
  import { getSpatialPointerId, spatialPointerInput } from '../../../lib/spatial-pointer-input'
31
- import { isTypingTarget } from '../../../lib/typing-target'
32
31
  import useInteractionScope from '../../../store/use-interaction-scope'
33
32
  import { suppressBoxSelectForPointer } from '../../tools/select/box-select-state'
34
33
  import { commitHandleDragPatch } from './handle-drag-history'
@@ -3,7 +3,9 @@
3
3
  import { Icon } from '@iconify/react'
4
4
  import {
5
5
  acquireSceneReadOnlyLease,
6
+ computeSceneBoundsXZ,
6
7
  createI18n,
8
+ emitter,
7
9
  getCatalogMaterialById,
8
10
  getLibraryMaterialIdFromRef,
9
11
  getMaterialSwatch,
@@ -28,6 +30,24 @@ import {
28
30
  type ViewerImmersiveSession,
29
31
  ViewerPresentations,
30
32
  } from '@pascal-app/viewer'
33
+ import { useViewerUiSounds } from '@pascal-app/viewer/sfx'
34
+ import {
35
+ type ActionMenuPlacement,
36
+ ParametricViewerRooms,
37
+ SceneLoader,
38
+ SceneLoadFailed,
39
+ SKY_DOCK_HEIGHT,
40
+ SkyControl,
41
+ Tooltip,
42
+ TooltipContent,
43
+ TooltipTrigger,
44
+ useIsMobile,
45
+ useViewerFloorDisplay,
46
+ ViewerHoverLabel,
47
+ ViewerSelectionManager,
48
+ ViewerStage,
49
+ type ViewerStageMode,
50
+ } from '@pascal-app/viewer/ui'
31
51
  import {
32
52
  type CSSProperties,
33
53
  memo,
@@ -43,9 +63,9 @@ import {
43
63
  } from 'react'
44
64
  import { ViewerOverlay } from '../../components/viewer-overlay'
45
65
  import { ViewerZoneSystem } from '../../components/viewer-zone-system'
66
+ import { useAutoFrame } from '../../hooks/use-auto-frame'
46
67
  import { type SaveStatus, useAutoSave } from '../../hooks/use-auto-save'
47
68
  import { useKeyboard } from '../../hooks/use-keyboard'
48
- import { useIsMobile } from '../../hooks/use-mobile'
49
69
  import { useSaveShortcut } from '../../hooks/use-save-shortcut'
50
70
  import { type HINT_KEY_NAMES, toolHintKey, toolHintKeyDescriptor } from '../../i18n/node-messages'
51
71
  import { useCeilingEditSessionOwner } from '../../lib/ceiling-edit-session'
@@ -70,12 +90,12 @@ import {
70
90
  import { disposeSFXBus, initSFXBus } from '../../lib/sfx-bus'
71
91
  import { useUnitFocusRules } from '../../lib/units'
72
92
  import { cn } from '../../lib/utils'
73
- import { useViewerFloorDisplay } from '../../lib/viewer-selection'
74
93
  import { type CameraHintAction, useCameraHintFocus } from '../../store/use-camera-hint-focus'
75
94
  import useEditor from '../../store/use-editor'
76
95
  import useFloorplanMode from '../../store/use-floorplan-mode'
77
96
  import useSessionGroups from '../../store/use-session-groups'
78
97
  import { paneOfView, VIEW_2D, VIEW_3D, type ViewPaneIndex } from '../../store/view-layout'
98
+ import { FloorplanDimensionHitsGate } from '../editor-2d/renderers/floorplan-dimension-edit-overlay'
79
99
  import { PointAskLayer } from '../point-ask/point-ask-layer'
80
100
  import { CeilingSelectionAffordanceSystem } from '../systems/ceiling/ceiling-selection-affordance-system'
81
101
  import { CeilingSystem, CeilingTopsSync } from '../systems/ceiling/ceiling-system'
@@ -86,7 +106,7 @@ import { ZoneSystem } from '../systems/zone/zone-system'
86
106
  import { BoxSelectTool } from '../tools/select/box-select-tool'
87
107
  import { ToolManager } from '../tools/tool-manager'
88
108
  import { ActionMenu } from '../ui/action-menu'
89
- import { type ActionMenuPlacement, TOP_MENU_CLEARANCE } from '../ui/action-menu/placement'
109
+ import { TOP_MENU_CLEARANCE } from '../ui/action-menu/placement'
90
110
  import { CommandPalette, type CommandPaletteEmptyAction } from '../ui/command-palette'
91
111
  import { EditorCommands } from '../ui/command-palette/editor-commands'
92
112
  import { FloatingLevelSelector } from '../ui/floating-level-selector'
@@ -94,22 +114,13 @@ import { HelperManager } from '../ui/helpers/helper-manager'
94
114
  import { PanelManager } from '../ui/panels/panel-manager'
95
115
  import { ErrorBoundary } from '../ui/primitives/error-boundary'
96
116
  import { useSidebarStore } from '../ui/primitives/sidebar'
97
- import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/primitives/tooltip'
98
117
  import { RightStack } from '../ui/right-stack'
99
- import { SceneLoader, SceneLoadFailed } from '../ui/scene-loader'
100
118
  import { AppSidebar } from '../ui/sidebar/app-sidebar'
101
119
  import type { ExtraPanel } from '../ui/sidebar/icon-rail'
102
120
  import { SettingsPanel, type SettingsPanelProps } from '../ui/sidebar/panels/settings-panel'
103
121
  import { SitePanel, type SitePanelProps } from '../ui/sidebar/panels/site-panel'
104
122
  import type { SidebarTab } from '../ui/sidebar/tab-bar'
105
123
  import { useHostPanels } from '../ui/sidebar/use-plugin-panels'
106
- import { SKY_DOCK_HEIGHT, SkyControl } from '../viewer/sky-control'
107
- import { useViewerUiSounds } from '../viewer/use-viewer-ui-sounds'
108
- import { ViewerHoverLabel } from '../viewer/viewer-hover-label'
109
- import { ParametricViewerRooms } from '../viewer/viewer-rooms'
110
- import { ViewerSelectionManager } from '../viewer/viewer-selection-manager'
111
- import { ViewerStage } from '../viewer/viewer-stage'
112
- import type { ViewerStageMode } from '../viewer/viewer-stage-modes'
113
124
  import { CaptureCameraRig } from './capture-camera-rig'
114
125
  import { CustomCameraControls } from './custom-camera-controls'
115
126
  import { DeleteConfirmationDialog } from './delete-confirmation-dialog'
@@ -1570,6 +1581,8 @@ function EditorContent({
1570
1581
  useKeyboard({ isVersionPreviewMode, disabled: isFirstPersonMode || isStudioMode })
1571
1582
  useGestureLifecycleOwner()
1572
1583
 
1584
+ useAutoFrame()
1585
+
1573
1586
  const { beginSceneLoad, completeSceneLoad, saveNow } = useAutoSave({
1574
1587
  guardAgainstSceneWipe,
1575
1588
  onSave,
@@ -1741,6 +1754,21 @@ function EditorContent({
1741
1754
  return () => window.clearTimeout(timer)
1742
1755
  }, [hasLoadedInitialScene, isLoading, isSceneLoading, isViewerSceneReady, sceneReadyKey])
1743
1756
 
1757
+ // The useAutoFrame emit can be clobbered by the level-follow effect's
1758
+ // first-run pivot rest on fast (client-side navigation) loads. Re-emitting
1759
+ // here is the last word after every load-driven camera effect has run.
1760
+ // Once per load: a remounted viewer reports ready again, but only a new
1761
+ // load reframes; remounted controls rest their own pivot on the level.
1762
+ const framedSceneReadyKeyRef = useRef<number | null>(null)
1763
+ useEffect(() => {
1764
+ if (!isViewerSceneReady || framedSceneReadyKeyRef.current === sceneReadyKey) return
1765
+ framedSceneReadyKeyRef.current = sceneReadyKey
1766
+ const nodes = useScene.getState().nodes
1767
+ if (Object.keys(nodes).length === 0) return
1768
+ const bounds = computeSceneBoundsXZ(nodes)
1769
+ emitter.emit('camera-controls:fit-scene', bounds ? { bounds } : {})
1770
+ }, [isViewerSceneReady, sceneReadyKey])
1771
+
1744
1772
  const isSceneDataLoading = isLoading || isSceneLoading || !hasLoadedInitialScene
1745
1773
  const showLoader = isSceneDataLoading || !isViewerSceneReady
1746
1774
  const visibleLoader =
@@ -2055,7 +2083,9 @@ function EditorContent({
2055
2083
  function EditorFrame(props: EditorProps) {
2056
2084
  return (
2057
2085
  <Profiler id="editor" onRender={recordEditorRender}>
2058
- <EditorContent {...props} />
2086
+ <FloorplanDimensionHitsGate>
2087
+ <EditorContent {...props} />
2088
+ </FloorplanDimensionHitsGate>
2059
2089
  </Profiler>
2060
2090
  )
2061
2091
  }
@@ -1,6 +1,6 @@
1
1
  'use client'
2
2
 
3
- import { useIsMobile } from '../../hooks/use-mobile'
3
+ import { useIsMobile } from '@pascal-app/viewer/ui'
4
4
  import { openExtension } from '../../lib/inspector-card-mode'
5
5
  import { useInspectorExpanded } from '../../lib/inspector-expanded'
6
6
  import { renderExtensionIcon, useInspectorExtensions } from '../ui/panels/inspector-extensions'
@@ -1,8 +1,8 @@
1
1
  'use client'
2
2
 
3
+ import { formatLinearMeasurement, type MetricNotation } from '@pascal-app/core'
3
4
  import { useViewer } from '@pascal-app/viewer'
4
5
  import { type ForwardedRef, Fragment, forwardRef } from 'react'
5
- import { formatLinearMeasurement, type MetricNotation } from '../../lib/measurements'
6
6
 
7
7
  // Canonical in-world dimension formatter — metric metres or imperial
8
8
  // feet/inches. Shared by every measurement readout so they read the same.
@@ -9,6 +9,7 @@ import {
9
9
  createAngleAccumulator,
10
10
  createSceneApi,
11
11
  DEFAULT_ANGLE_STEP,
12
+ formatAngleRadians,
12
13
  type HandleDescriptor,
13
14
  type HandlePortal,
14
15
  type LatchHandle,
@@ -21,6 +22,7 @@ import {
21
22
  useScene,
22
23
  } from '@pascal-app/core'
23
24
  import { useViewer } from '@pascal-app/viewer'
25
+ import { LocalizedHtml as Html } from '@pascal-app/viewer/ui'
24
26
  import { createPortal, type ThreeEvent, useFrame, useThree } from '@react-three/fiber'
25
27
  import { useEffect, useMemo, useRef, useState } from 'react'
26
28
  import {
@@ -59,7 +61,6 @@ import useInteractionScope, {
59
61
  useMovingNode,
60
62
  } from '../../store/use-interaction-scope'
61
63
  import useOpeningGuides from '../../store/use-opening-guides'
62
- import { formatAngleRadians } from '../tools/shared/segment-angle'
63
64
  import {
64
65
  ARROW_COLOR,
65
66
  ARROW_HOVER_COLOR,
@@ -76,7 +77,6 @@ import {
76
77
  import { createLinearResizeDragBinding, linearResizeFactor } from './handles/linear-resize-drag'
77
78
  import { resolveResizeSnapValue } from './handles/resize-snap'
78
79
  import { type HandleDragControls, useHandleDrag } from './handles/use-handle-drag'
79
- import { Html } from './localized-html'
80
80
 
81
81
  // Pooled scratch for the handle rig's world-relative pose mapping.
82
82
  const _rigRelative = new Matrix4()
@@ -1,7 +1,8 @@
1
1
  'use client'
2
2
 
3
3
  import { type OpenWallEnd, sceneRegistry } from '@pascal-app/core'
4
- import { useViewer } from '@pascal-app/viewer'
4
+ import { MEASUREMENT_DANGLING_COLOR, 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 { memo, useEffect, useMemo, useRef } from 'react'
7
8
  import { BoxGeometry, CircleGeometry, type Group, RingGeometry } from 'three'
@@ -13,12 +14,10 @@ import {
13
14
  } from '../../hooks/use-open-wall-ends'
14
15
  import { EDITOR_LAYER } from '../../lib/constants'
15
16
  import { openWallEndKey, openWallEndLabel } from '../../lib/floorplan/open-wall-ends'
16
- import { MEASUREMENT_DANGLING_COLOR } from '../../lib/measurements'
17
17
  import { cn } from '../../lib/utils'
18
18
  import useEditor from '../../store/use-editor'
19
19
  import { isViewVisible, VIEW_3D, visibleScene } from '../../store/view-layout'
20
20
  import { JoinWallsPill } from './join-walls-pill'
21
- import { Html } from './localized-html'
22
21
  import { OpenWallEndsHint } from './open-wall-ends-hint'
23
22
 
24
23
  /**
@@ -1,10 +1,10 @@
1
1
  'use client'
2
2
 
3
3
  import { type AnyNodeId, emitter, type OpenWallEnd } from '@pascal-app/core'
4
+ import { MEASUREMENT_DANGLING_COLOR } from '@pascal-app/viewer'
4
5
  import { useEffect, useState } from 'react'
5
6
  import { createPortal } from 'react-dom'
6
7
  import { openWallEndsSummary } from '../../lib/floorplan/open-wall-ends'
7
- import { MEASUREMENT_DANGLING_COLOR } from '../../lib/measurements'
8
8
 
9
9
  // The viewer area holds the floor plan and the 3D view side by side, so its
10
10
  // top centre clears both views' corner controls in 2D, 3D and split alike.
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import { useViewer } from '@pascal-app/viewer'
4
+ import { LocalizedHtml as Html } from '@pascal-app/viewer/ui'
4
5
  import { memo, useEffect, useLayoutEffect, useMemo } from 'react'
5
6
  import { BufferGeometry, Float32BufferAttribute, Line as ThreeLine } from 'three'
6
7
  import { LineBasicNodeMaterial } from 'three/webgpu'
@@ -9,7 +10,6 @@ import useOpeningGuides, {
9
10
  type OpeningGuide3D,
10
11
  type OpeningGuideVec3,
11
12
  } from '../../store/use-opening-guides'
12
- import { Html } from './localized-html'
13
13
  import { formatMeasurement } from './measurement-pill'
14
14
 
15
15
  const DIMENSION_COLOR = 0x81_8c_f8 // indigo — a neutral measurement
@@ -1,9 +1,9 @@
1
1
  'use client'
2
2
 
3
3
  import type { QuickMeasurementMetric, QuickMeasurementReport } from '@pascal-app/core'
4
+ import { formatAreaLabel, formatLinearMeasurement, formatVolumeLabel } from '@pascal-app/core'
4
5
  import { Crosshair, MapPin } from 'lucide-react'
5
6
  import { memo } from 'react'
6
- import { formatAreaLabel, formatLinearMeasurement, formatVolumeLabel } from '../../lib/measurements'
7
7
 
8
8
  function formatMetric(
9
9
  metric: QuickMeasurementMetric,