@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
@@ -1,42 +0,0 @@
1
- 'use client'
2
-
3
- import * as PopoverPrimitive from '@radix-ui/react-popover'
4
- import type * as React from 'react'
5
-
6
- import { cn } from '../../../lib/utils'
7
-
8
- function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
9
- return <PopoverPrimitive.Root data-slot="popover" {...props} />
10
- }
11
-
12
- function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
13
- return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
14
- }
15
-
16
- function PopoverContent({
17
- className,
18
- align = 'center',
19
- sideOffset = 4,
20
- ...props
21
- }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
22
- return (
23
- <PopoverPrimitive.Portal>
24
- <PopoverPrimitive.Content
25
- align={align}
26
- className={cn(
27
- 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[state=closed]:animate-out data-[state=open]:animate-in',
28
- className,
29
- )}
30
- data-slot="popover-content"
31
- sideOffset={sideOffset}
32
- {...props}
33
- />
34
- </PopoverPrimitive.Portal>
35
- )
36
- }
37
-
38
- function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
39
- return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
40
- }
41
-
42
- export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
@@ -1,29 +0,0 @@
1
- 'use client'
2
-
3
- import * as SwitchPrimitives from '@radix-ui/react-switch'
4
- import * as React from 'react'
5
-
6
- import { cn } from './../../../lib/utils'
7
-
8
- const Switch = React.forwardRef<
9
- React.ElementRef<typeof SwitchPrimitives.Root>,
10
- React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
11
- >(({ className, ...props }, ref) => (
12
- <SwitchPrimitives.Root
13
- className={cn(
14
- 'peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-xs transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
15
- className,
16
- )}
17
- {...props}
18
- ref={ref}
19
- >
20
- <SwitchPrimitives.Thumb
21
- className={cn(
22
- 'pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0',
23
- )}
24
- />
25
- </SwitchPrimitives.Root>
26
- ))
27
- Switch.displayName = SwitchPrimitives.Root.displayName
28
-
29
- export { Switch }
@@ -1,57 +0,0 @@
1
- 'use client'
2
-
3
- import * as TooltipPrimitive from '@radix-ui/react-tooltip'
4
- import type * as React from 'react'
5
-
6
- import { cn } from '../../../lib/utils'
7
-
8
- function TooltipProvider({
9
- delayDuration = 0,
10
- ...props
11
- }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12
- return (
13
- <TooltipPrimitive.Provider
14
- data-slot="tooltip-provider"
15
- delayDuration={delayDuration}
16
- {...props}
17
- />
18
- )
19
- }
20
-
21
- function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
22
- return (
23
- <TooltipProvider>
24
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
25
- </TooltipProvider>
26
- )
27
- }
28
-
29
- function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
30
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
31
- }
32
-
33
- function TooltipContent({
34
- className,
35
- sideOffset = 0,
36
- children,
37
- ...props
38
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
39
- return (
40
- <TooltipPrimitive.Portal>
41
- <TooltipPrimitive.Content
42
- className={cn(
43
- 'fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in text-balance rounded-md bg-foreground px-3 py-1.5 font-barlow text-background text-xs data-[state=closed]:animate-out',
44
- className,
45
- )}
46
- data-slot="tooltip-content"
47
- sideOffset={sideOffset}
48
- {...props}
49
- >
50
- {children}
51
- <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
52
- </TooltipPrimitive.Content>
53
- </TooltipPrimitive.Portal>
54
- )
55
- }
56
-
57
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -1,73 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useState } from 'react'
4
- import { cn } from '../../lib/utils'
5
- import { Button } from './primitives/button'
6
-
7
- const LOADERS = [
8
- 'pascal-loader-1',
9
- 'pascal-loader-2',
10
- 'pascal-loader-3',
11
- 'pascal-loader-4',
12
- 'pascal-loader-5',
13
- ]
14
-
15
- interface SceneLoaderProps {
16
- className?: string
17
- fullScreen?: boolean
18
- }
19
-
20
- export function SceneLoader({ className, fullScreen = false }: SceneLoaderProps) {
21
- const [loaderClass, setLoaderClass] = useState(LOADERS[0]!)
22
-
23
- useEffect(() => {
24
- // Pick a random loader on mount
25
- setLoaderClass(LOADERS[Math.floor(Math.random() * LOADERS.length)] ?? LOADERS[0]!)
26
- }, [])
27
-
28
- return (
29
- <div
30
- className={cn(
31
- 'z-100 flex items-center justify-center bg-background/80 backdrop-blur-md transition-opacity duration-300',
32
- fullScreen ? 'fixed inset-0' : 'absolute inset-0',
33
- className,
34
- )}
35
- >
36
- <div className={cn(loaderClass, 'text-foreground opacity-80')} />
37
- </div>
38
- )
39
- }
40
-
41
- interface SceneLoadFailedProps {
42
- className?: string
43
- onRetry: () => void
44
- }
45
-
46
- /**
47
- * Replaces the loader when the host could not deliver the scene. Rendered
48
- * INSTEAD of falling back to an empty default scene: a session that shows
49
- * scaffold nodes after a failed load autosaves that scaffold over the real
50
- * project (prod scene-wipe class, 2026-09-02).
51
- */
52
- export function SceneLoadFailed({ className, onRetry }: SceneLoadFailedProps) {
53
- return (
54
- <div
55
- className={cn(
56
- 'z-100 flex flex-col items-center justify-center gap-4 bg-background/90 px-6 text-center backdrop-blur-md',
57
- 'absolute inset-0',
58
- className,
59
- )}
60
- role="alert"
61
- >
62
- <div className="flex flex-col gap-1">
63
- <p className="font-medium text-foreground text-sm">This project couldn't be loaded</p>
64
- <p className="text-muted-foreground text-sm">
65
- Nothing was changed. Check your connection and try again.
66
- </p>
67
- </div>
68
- <Button className="rounded-full" onClick={onRetry} size="sm" type="button">
69
- Try again
70
- </Button>
71
- </div>
72
- )
73
- }
@@ -1,67 +0,0 @@
1
- 'use client'
2
-
3
- import { usePascalI18n } from '@pascal-app/core'
4
- import type React from 'react'
5
- import { cn } from '../../lib/utils'
6
- import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/primitives/tooltip'
7
- import { SKY_COMPASS_BOTTOM, useSkyPanel } from './sky-control'
8
-
9
- export type FloorplanCompassButtonProps = {
10
- northRotationDeg: number
11
- onAlignNorth: () => void
12
- needleRef?: React.RefObject<SVGSVGElement | null>
13
- /** Horizontal anchor; defaults to the bottom-left corner. */
14
- style?: React.CSSProperties
15
- }
16
-
17
- export function FloorplanCompassButton({
18
- northRotationDeg,
19
- onAlignNorth,
20
- needleRef,
21
- style,
22
- }: FloorplanCompassButtonProps) {
23
- const i18n = usePascalI18n()
24
- // Beside the sky chip (not when moved to the plan's right corner), centre on the docks' row.
25
- const besideSky = useSkyPanel((s) => s.hosts > 0) && style?.right === undefined
26
- const label = i18n._({
27
- id: 'viewer.floorplan.alignNorth',
28
- message: 'Align view to north',
29
- })
30
- return (
31
- <Tooltip>
32
- <TooltipTrigger asChild>
33
- <button
34
- aria-label={label}
35
- className={cn(
36
- 'group pointer-events-auto absolute bottom-3 z-30 flex h-8 w-8 items-center justify-center rounded-full border border-black/10 bg-white/85 shadow-sm backdrop-blur-md transition hover:bg-white hover:shadow-md dark:border-white/10 dark:bg-neutral-900/85 dark:hover:bg-neutral-900',
37
- !style && 'left-3',
38
- )}
39
- style={besideSky ? { bottom: SKY_COMPASS_BOTTOM, ...style } : style}
40
- onClick={(event) => {
41
- event.preventDefault()
42
- event.stopPropagation()
43
- onAlignNorth()
44
- }}
45
- onPointerDown={(event) => {
46
- event.stopPropagation()
47
- }}
48
- type="button"
49
- >
50
- <span className="relative flex h-6 w-6 items-center justify-center rounded-full bg-[#b8b8b8] shadow-inner dark:bg-neutral-700">
51
- <svg
52
- aria-hidden="true"
53
- className="h-6 w-6"
54
- ref={needleRef}
55
- style={{ transform: `rotate(${northRotationDeg}deg)` }}
56
- viewBox="0 0 48 48"
57
- >
58
- <path d="M24 4.5 31.5 25 24 21.5 16.5 25Z" fill="#f15b5b" />
59
- <path d="M24 43.5 16.5 23 24 26.5 31.5 23Z" fill="#ffffff" />
60
- </svg>
61
- </span>
62
- </button>
63
- </TooltipTrigger>
64
- <TooltipContent side="right">{label}</TooltipContent>
65
- </Tooltip>
66
- )
67
- }
@@ -1,107 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
- import type { FloorplanGeometry } from '@pascal-app/core'
3
- import {
4
- clientToFloorplanPoint,
5
- getFloorplanBounds,
6
- padFloorplanBounds,
7
- panFloorplanViewBox,
8
- scaleFloorplanViewBox,
9
- scaleFloorplanViewBoxBetweenClients,
10
- } from './floorplan-preview-geometry'
11
-
12
- describe('floorplan preview bounds', () => {
13
- test('composes nested translation and rotation', () => {
14
- const geometry: FloorplanGeometry = {
15
- kind: 'group',
16
- transform: { translate: [10, 5], rotate: Math.PI / 2 },
17
- children: [
18
- {
19
- kind: 'rect',
20
- x: 0,
21
- y: 0,
22
- width: 4,
23
- height: 2,
24
- },
25
- ],
26
- }
27
-
28
- expect(getFloorplanBounds([geometry])).toEqual({
29
- minX: 8,
30
- minY: 5,
31
- maxX: 10,
32
- maxY: 9,
33
- })
34
- })
35
-
36
- test('includes image rotation and dimension baselines', () => {
37
- const geometries: FloorplanGeometry[] = [
38
- {
39
- kind: 'image',
40
- url: '/symbol.png',
41
- center: [2, 3],
42
- width: 4,
43
- height: 2,
44
- rotation: Math.PI / 2,
45
- },
46
- {
47
- kind: 'dimension',
48
- start: [-3, -2],
49
- end: [3, -2],
50
- dimensionStart: [-4, -1],
51
- dimensionEnd: [4, -1],
52
- offsetNormal: [0, -1],
53
- offsetDistance: 1,
54
- extensionOvershoot: 0.2,
55
- text: '8 m',
56
- },
57
- ]
58
-
59
- expect(getFloorplanBounds(geometries)).toEqual({
60
- minX: -4,
61
- minY: -2,
62
- maxX: 4,
63
- maxY: 5,
64
- })
65
- })
66
-
67
- test('pads narrow plans by a usable minimum', () => {
68
- expect(padFloorplanBounds({ minX: 0, minY: 0, maxX: 0, maxY: 0 })).toEqual({
69
- minX: -0.75,
70
- minY: -0.75,
71
- maxX: 0.75,
72
- maxY: 0.75,
73
- })
74
- })
75
-
76
- test('maps clients through letterboxed SVG content', () => {
77
- const viewBox = { x: 0, y: 0, width: 10, height: 10 }
78
- const viewport = { left: 0, top: 0, width: 1000, height: 500 }
79
-
80
- expect(clientToFloorplanPoint(viewBox, viewport, 250, 0)).toEqual([0, 0])
81
- expect(clientToFloorplanPoint(viewBox, viewport, 750, 500)).toEqual([10, 10])
82
- expect(panFloorplanViewBox(viewBox, viewport, [500, 250], [550, 250])).toEqual({
83
- x: -1,
84
- y: 0,
85
- width: 10,
86
- height: 10,
87
- })
88
- })
89
-
90
- test('keeps the plan point beneath a moving pinch midpoint', () => {
91
- const viewBox = { x: 0, y: 0, width: 10, height: 10 }
92
- const viewport = { left: 0, top: 0, width: 1000, height: 500 }
93
- const next = scaleFloorplanViewBoxBetweenClients(viewBox, 0.5, viewport, [500, 250], [600, 250])
94
-
95
- expect(next).toEqual({ x: 1.5, y: 2.5, width: 5, height: 5 })
96
- expect(clientToFloorplanPoint(next, viewport, 600, 250)).toEqual([5, 5])
97
- })
98
-
99
- test('clamps zoom without changing the view box aspect ratio', () => {
100
- expect(scaleFloorplanViewBox({ x: 0, y: 0, width: 100, height: 1 }, 0.01)).toEqual({
101
- x: 37.5,
102
- y: 0.375,
103
- width: 25,
104
- height: 0.25,
105
- })
106
- })
107
- })
@@ -1,281 +0,0 @@
1
- import type { FloorplanGeometry, FloorplanPoint } from '@pascal-app/core'
2
-
3
- export type FloorplanBounds = {
4
- minX: number
5
- minY: number
6
- maxX: number
7
- maxY: number
8
- }
9
-
10
- export type FloorplanViewBox = { x: number; y: number; width: number; height: number }
11
-
12
- export type FloorplanViewport = {
13
- left: number
14
- top: number
15
- width: number
16
- height: number
17
- }
18
-
19
- type PlanTransform = { tx: number; ty: number; rotation: number }
20
-
21
- const IDENTITY: PlanTransform = { tx: 0, ty: 0, rotation: 0 }
22
- const MIN_VIEWBOX_SIZE = 0.25
23
- const MAX_VIEWBOX_SIZE = 500
24
-
25
- function clientToViewBoxFraction(
26
- viewBox: FloorplanViewBox,
27
- viewport: FloorplanViewport,
28
- clientX: number,
29
- clientY: number,
30
- ): FloorplanPoint {
31
- const viewportWidth = Math.max(viewport.width, 1)
32
- const viewportHeight = Math.max(viewport.height, 1)
33
- const scale = Math.min(viewportWidth / viewBox.width, viewportHeight / viewBox.height)
34
- const renderedWidth = viewBox.width * scale
35
- const renderedHeight = viewBox.height * scale
36
- const renderedLeft = viewport.left + (viewportWidth - renderedWidth) / 2
37
- const renderedTop = viewport.top + (viewportHeight - renderedHeight) / 2
38
- return [(clientX - renderedLeft) / renderedWidth, (clientY - renderedTop) / renderedHeight]
39
- }
40
-
41
- export function clientToFloorplanPoint(
42
- viewBox: FloorplanViewBox,
43
- viewport: FloorplanViewport,
44
- clientX: number,
45
- clientY: number,
46
- ): FloorplanPoint {
47
- const [fractionX, fractionY] = clientToViewBoxFraction(viewBox, viewport, clientX, clientY)
48
- return [viewBox.x + fractionX * viewBox.width, viewBox.y + fractionY * viewBox.height]
49
- }
50
-
51
- export function scaleFloorplanViewBox(
52
- viewBox: FloorplanViewBox,
53
- factor: number,
54
- anchorX = 0.5,
55
- anchorY = 0.5,
56
- ): FloorplanViewBox {
57
- const minFactor = Math.max(MIN_VIEWBOX_SIZE / viewBox.width, MIN_VIEWBOX_SIZE / viewBox.height)
58
- const maxFactor = Math.min(MAX_VIEWBOX_SIZE / viewBox.width, MAX_VIEWBOX_SIZE / viewBox.height)
59
- const clampedFactor = Math.min(Math.max(factor, minFactor), maxFactor)
60
- const width = viewBox.width * clampedFactor
61
- const height = viewBox.height * clampedFactor
62
- return {
63
- x: viewBox.x + (viewBox.width - width) * anchorX,
64
- y: viewBox.y + (viewBox.height - height) * anchorY,
65
- width,
66
- height,
67
- }
68
- }
69
-
70
- export function scaleFloorplanViewBoxBetweenClients(
71
- viewBox: FloorplanViewBox,
72
- factor: number,
73
- viewport: FloorplanViewport,
74
- sourceClient: FloorplanPoint,
75
- targetClient: FloorplanPoint,
76
- ): FloorplanViewBox {
77
- const sourcePoint = clientToFloorplanPoint(viewBox, viewport, sourceClient[0], sourceClient[1])
78
- const [targetX, targetY] = clientToViewBoxFraction(
79
- viewBox,
80
- viewport,
81
- targetClient[0],
82
- targetClient[1],
83
- )
84
- const scaled = scaleFloorplanViewBox(viewBox, factor, 0, 0)
85
- return {
86
- ...scaled,
87
- x: sourcePoint[0] - targetX * scaled.width,
88
- y: sourcePoint[1] - targetY * scaled.height,
89
- }
90
- }
91
-
92
- export function panFloorplanViewBox(
93
- viewBox: FloorplanViewBox,
94
- viewport: FloorplanViewport,
95
- sourceClient: FloorplanPoint,
96
- targetClient: FloorplanPoint,
97
- ): FloorplanViewBox {
98
- const sourcePoint = clientToFloorplanPoint(viewBox, viewport, sourceClient[0], sourceClient[1])
99
- const targetPoint = clientToFloorplanPoint(viewBox, viewport, targetClient[0], targetClient[1])
100
- return {
101
- ...viewBox,
102
- x: viewBox.x + sourcePoint[0] - targetPoint[0],
103
- y: viewBox.y + sourcePoint[1] - targetPoint[1],
104
- }
105
- }
106
-
107
- function applyTransform(point: FloorplanPoint, transform: PlanTransform): FloorplanPoint {
108
- const cos = Math.cos(transform.rotation)
109
- const sin = Math.sin(transform.rotation)
110
- return [
111
- point[0] * cos - point[1] * sin + transform.tx,
112
- point[0] * sin + point[1] * cos + transform.ty,
113
- ]
114
- }
115
-
116
- function composeTransform(
117
- parent: PlanTransform,
118
- child: NonNullable<Extract<FloorplanGeometry, { kind: 'group' }>['transform']>,
119
- ): PlanTransform {
120
- const translated = applyTransform(child.translate ?? [0, 0], parent)
121
- return {
122
- tx: translated[0],
123
- ty: translated[1],
124
- rotation: parent.rotation + (child.rotate ?? 0),
125
- }
126
- }
127
-
128
- function includePoint(bounds: FloorplanBounds | null, point: FloorplanPoint): FloorplanBounds {
129
- if (!bounds) return { minX: point[0], minY: point[1], maxX: point[0], maxY: point[1] }
130
- return {
131
- minX: Math.min(bounds.minX, point[0]),
132
- minY: Math.min(bounds.minY, point[1]),
133
- maxX: Math.max(bounds.maxX, point[0]),
134
- maxY: Math.max(bounds.maxY, point[1]),
135
- }
136
- }
137
-
138
- function includePoints(
139
- bounds: FloorplanBounds | null,
140
- points: readonly FloorplanPoint[],
141
- transform: PlanTransform,
142
- ): FloorplanBounds | null {
143
- let next = bounds
144
- for (const point of points) next = includePoint(next, applyTransform(point, transform))
145
- return next
146
- }
147
-
148
- function geometryBounds(
149
- geometry: FloorplanGeometry,
150
- transform: PlanTransform,
151
- bounds: FloorplanBounds | null,
152
- ): FloorplanBounds | null {
153
- switch (geometry.kind) {
154
- case 'group': {
155
- const nextTransform = geometry.transform
156
- ? composeTransform(transform, geometry.transform)
157
- : transform
158
- return geometry.children.reduce(
159
- (next, child) => geometryBounds(child, nextTransform, next),
160
- bounds,
161
- )
162
- }
163
- case 'polygon':
164
- case 'polyline':
165
- case 'hatch':
166
- return includePoints(bounds, geometry.points, transform)
167
- case 'rect':
168
- return includePoints(
169
- bounds,
170
- [
171
- [geometry.x, geometry.y],
172
- [geometry.x + geometry.width, geometry.y],
173
- [geometry.x + geometry.width, geometry.y + geometry.height],
174
- [geometry.x, geometry.y + geometry.height],
175
- ],
176
- transform,
177
- )
178
- case 'circle': {
179
- const center = applyTransform([geometry.cx, geometry.cy], transform)
180
- return includePoints(
181
- bounds,
182
- [
183
- [center[0] - geometry.r, center[1] - geometry.r],
184
- [center[0] + geometry.r, center[1] + geometry.r],
185
- ],
186
- IDENTITY,
187
- )
188
- }
189
- case 'line':
190
- case 'hit-line':
191
- case 'edge-handle':
192
- return includePoints(
193
- bounds,
194
- [
195
- [geometry.x1, geometry.y1],
196
- [geometry.x2, geometry.y2],
197
- ],
198
- transform,
199
- )
200
- case 'text':
201
- return includePoint(bounds, applyTransform([geometry.x, geometry.y], transform))
202
- case 'image': {
203
- const halfWidth = geometry.width / 2
204
- const halfHeight = geometry.height / 2
205
- const imageTransform = composeTransform(transform, {
206
- translate: geometry.center,
207
- rotate: geometry.rotation,
208
- })
209
- return includePoints(
210
- bounds,
211
- [
212
- [-halfWidth, -halfHeight],
213
- [halfWidth, -halfHeight],
214
- [halfWidth, halfHeight],
215
- [-halfWidth, halfHeight],
216
- ],
217
- imageTransform,
218
- )
219
- }
220
- case 'endpoint-handle':
221
- case 'midpoint-handle':
222
- case 'move-handle':
223
- case 'move-arrow':
224
- case 'rotate-arrow':
225
- case 'equal-spacing-badge':
226
- return includePoint(bounds, applyTransform(geometry.point, transform))
227
- case 'dimension-label':
228
- return includePoint(bounds, applyTransform([geometry.cx, geometry.cy], transform))
229
- case 'dimension':
230
- return includePoints(
231
- bounds,
232
- [
233
- geometry.start,
234
- geometry.end,
235
- geometry.dimensionStart ?? geometry.start,
236
- geometry.dimensionEnd ?? geometry.end,
237
- ],
238
- transform,
239
- )
240
- case 'dimension-string':
241
- return geometry.segments.reduce(
242
- (next, segment) =>
243
- includePoints(
244
- next,
245
- [
246
- segment.start,
247
- segment.end,
248
- segment.dimensionStart ?? segment.start,
249
- segment.dimensionEnd ?? segment.end,
250
- ],
251
- transform,
252
- ),
253
- bounds,
254
- )
255
- case 'path':
256
- return bounds
257
- default:
258
- return bounds
259
- }
260
- }
261
-
262
- export function getFloorplanBounds(
263
- geometries: readonly FloorplanGeometry[],
264
- ): FloorplanBounds | null {
265
- return geometries.reduce(
266
- (bounds, geometry) => geometryBounds(geometry, IDENTITY, bounds),
267
- null as FloorplanBounds | null,
268
- )
269
- }
270
-
271
- export function padFloorplanBounds(bounds: FloorplanBounds, ratio = 0.12): FloorplanBounds {
272
- const width = Math.max(bounds.maxX - bounds.minX, 1)
273
- const height = Math.max(bounds.maxY - bounds.minY, 1)
274
- const padding = Math.max(Math.max(width, height) * ratio, 0.75)
275
- return {
276
- minX: bounds.minX - padding,
277
- minY: bounds.minY - padding,
278
- maxX: bounds.maxX + padding,
279
- maxY: bounds.maxY + padding,
280
- }
281
- }
@@ -1,43 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
- import {
3
- cameraAzimuthFromFloorplanRotation,
4
- floorplanRotationFromCameraAzimuth,
5
- floorplanViewBoxFromNavigationPose,
6
- nearestEquivalentDegrees,
7
- visibleFloorplanViewWidth,
8
- } from './floorplan-preview-navigation'
9
-
10
- describe('floorplan preview navigation', () => {
11
- test('keeps continuous compass rotations across the angle seam', () => {
12
- expect(nearestEquivalentDegrees(-179, 179)).toBe(181)
13
- expect(floorplanRotationFromCameraAzimuth((-179 * Math.PI) / 180, 179)).toBeCloseTo(181)
14
- })
15
-
16
- test('uses the same north-up azimuth convention as the editor', () => {
17
- expect(cameraAzimuthFromFloorplanRotation(0)).toBe(0)
18
- expect(cameraAzimuthFromFloorplanRotation(90)).toBeCloseTo(Math.PI / 2)
19
- })
20
-
21
- test('maps a camera pose to an aspect-correct centered view box', () => {
22
- expect(
23
- floorplanViewBoxFromNavigationPose(
24
- {
25
- source: '3d',
26
- revision: 1,
27
- target: [0, 0, 0],
28
- azimuth: 0,
29
- viewWidth: 20,
30
- },
31
- { x: 4, y: 3 },
32
- 0,
33
- { width: 1000, height: 500 },
34
- ),
35
- ).toEqual({ x: -6, y: -2, width: 20, height: 10 })
36
- })
37
-
38
- test('reports the actual horizontal span for meet-preserved SVG view boxes', () => {
39
- expect(
40
- visibleFloorplanViewWidth({ x: 0, y: 0, width: 10, height: 10 }, { width: 200, height: 100 }),
41
- ).toBe(20)
42
- })
43
- })