@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,169 +0,0 @@
1
- 'use client'
2
-
3
- import { create } from 'zustand'
4
- import { createJSONStorage, persist } from 'zustand/middleware'
5
-
6
- export const AUDIO_SETTINGS_STORAGE_KEY = 'pascal-audio-settings'
7
- /**
8
- * Mute is each tab's own, so someone working in two projects can silence one and hear the other.
9
- * Under this key `sessionStorage` holds this tab's mute (kept across its reloads) and
10
- * `localStorage` the last choice made in any tab, which a new tab starts from.
11
- */
12
- export const AUDIO_MUTED_STORAGE_KEY = 'pascal-audio-muted'
13
-
14
- interface AudioState {
15
- masterVolume: number
16
- sfxVolume: number
17
- radioVolume: number
18
- isRadioPlaying: boolean
19
- /** This tab's mute; the volumes are every tab's. */
20
- muted: boolean
21
- autoplay: boolean
22
- setMasterVolume: (v: number) => void
23
- setSfxVolume: (v: number) => void
24
- setRadioVolume: (v: number) => void
25
- setRadioPlaying: (v: boolean) => void
26
- toggleRadioPlaying: () => void
27
- setMuted: (muted: boolean) => void
28
- toggleMute: () => void
29
- setAutoplay: (v: boolean) => void
30
- }
31
-
32
- type StoredSettings = Pick<
33
- AudioState,
34
- 'masterVolume' | 'sfxVolume' | 'radioVolume' | 'isRadioPlaying' | 'autoplay'
35
- >
36
-
37
- type BrowserStorage = 'localStorage' | 'sessionStorage'
38
-
39
- // Storage can be missing (a test, a server render) or refuse access (site data blocked): the page
40
- // then keeps its choices in memory only.
41
- function readStored(storage: BrowserStorage, key: string) {
42
- try {
43
- return globalThis[storage]?.getItem(key) ?? null
44
- } catch {
45
- return null
46
- }
47
- }
48
-
49
- function writeStored(storage: BrowserStorage, key: string, value: string) {
50
- try {
51
- globalThis[storage]?.setItem(key, value)
52
- } catch {}
53
- }
54
-
55
- /** This tab's mute. A tab without one takes the last choice made in any tab, and keeps it. */
56
- function tabMuted(): boolean {
57
- const own = readStored('sessionStorage', AUDIO_MUTED_STORAGE_KEY)
58
- if (own !== null) return own === 'true'
59
- const muted = readStored('localStorage', AUDIO_MUTED_STORAGE_KEY) === 'true'
60
- writeStored('sessionStorage', AUDIO_MUTED_STORAGE_KEY, String(muted))
61
- return muted
62
- }
63
-
64
- /** A mute chosen in this tab: this tab's, and where the next new tab starts. Open tabs keep theirs. */
65
- function chooseMuted(muted: boolean) {
66
- writeStored('sessionStorage', AUDIO_MUTED_STORAGE_KEY, String(muted))
67
- writeStored('localStorage', AUDIO_MUTED_STORAGE_KEY, String(muted))
68
- return { muted }
69
- }
70
-
71
- // The settings as this tab last read, wrote or took from another tab. A save carries every
72
- // setting, so a save that changes none (a mute, which is this tab's own) is skipped.
73
- let heldSettings: string | null = null
74
- // Set while this tab takes another tab's volumes: that is no choice of its own to save back.
75
- let adopting = false
76
-
77
- const settingsStorage = createJSONStorage<StoredSettings>(() => ({
78
- getItem: (key) => {
79
- heldSettings = readStored('localStorage', key)
80
- return heldSettings
81
- },
82
- setItem: (key, value) => {
83
- if (value === heldSettings) return
84
- heldSettings = value
85
- if (!adopting) writeStored('localStorage', key, value)
86
- },
87
- removeItem: (key) => {
88
- try {
89
- globalThis.localStorage?.removeItem(key)
90
- } catch {}
91
- },
92
- }))
93
-
94
- const useAudio = create<AudioState>()(
95
- persist(
96
- (set) => ({
97
- masterVolume: 70,
98
- sfxVolume: 50,
99
- radioVolume: 25,
100
- isRadioPlaying: false,
101
- muted: false,
102
- autoplay: true,
103
- setMasterVolume: (v) => set({ masterVolume: v }),
104
- setSfxVolume: (v) => set({ sfxVolume: v }),
105
- setRadioVolume: (v) => set({ radioVolume: v }),
106
- setRadioPlaying: (v) => set({ isRadioPlaying: v }),
107
- toggleRadioPlaying: () => set((state) => ({ isRadioPlaying: !state.isRadioPlaying })),
108
- setMuted: (muted) => set(chooseMuted(muted)),
109
- toggleMute: () => set((state) => chooseMuted(!state.muted)),
110
- setAutoplay: (v) => set({ autoplay: v }),
111
- }),
112
- {
113
- name: AUDIO_SETTINGS_STORAGE_KEY,
114
- storage: settingsStorage,
115
- partialize: ({ masterVolume, sfxVolume, radioVolume, isRadioPlaying, autoplay }) => ({
116
- masterVolume,
117
- sfxVolume,
118
- radioVolume,
119
- isRadioPlaying,
120
- autoplay,
121
- }),
122
- // The settings once held one mute for every tab: whoever had muted stays muted, unless a tab
123
- // has chosen since (a tab still on that build can write it again). The format is unchanged,
124
- // so that build reads these settings back with only the mute gone.
125
- merge: (persisted, current) => {
126
- const { muted, ...settings } = (persisted ?? {}) as Partial<StoredSettings> & {
127
- muted?: unknown
128
- }
129
- if (
130
- typeof muted === 'boolean' &&
131
- readStored('localStorage', AUDIO_MUTED_STORAGE_KEY) === null
132
- ) {
133
- writeStored('localStorage', AUDIO_MUTED_STORAGE_KEY, String(muted))
134
- }
135
- return { ...current, ...settings, muted: tabMuted() }
136
- },
137
- },
138
- ),
139
- )
140
-
141
- /**
142
- * Another tab saved the settings: the volumes are every tab's, so this tab takes them at once, and
143
- * a later save of its own carries them. Its mute and whether its radio plays stay its own.
144
- */
145
- function adoptOtherTabSettings(event: StorageEvent) {
146
- if (event.key !== AUDIO_SETTINGS_STORAGE_KEY || event.newValue === null) return
147
- let stored: Partial<Record<keyof StoredSettings, unknown>> | undefined
148
- try {
149
- stored = JSON.parse(event.newValue)?.state
150
- } catch {
151
- return
152
- }
153
- const shared: Partial<StoredSettings> = {}
154
- for (const key of ['masterVolume', 'sfxVolume', 'radioVolume'] as const) {
155
- const value = stored?.[key]
156
- if (typeof value === 'number') shared[key] = value
157
- }
158
- if (typeof stored?.autoplay === 'boolean') shared.autoplay = stored.autoplay
159
- adopting = true
160
- try {
161
- useAudio.setState(shared)
162
- } finally {
163
- adopting = false
164
- }
165
- }
166
-
167
- globalThis.addEventListener?.('storage', adoptOtherTabSettings)
168
-
169
- export default useAudio
@@ -1,40 +0,0 @@
1
- import { create } from 'zustand'
2
-
3
- export type WalkthroughInteract = { label: string; verb: string } | null
4
-
5
- export type FirstPersonHudState = {
6
- floorLabel: string | null
7
- zoneLabel: string | null
8
- interact: WalkthroughInteract
9
- setHud: (hud: Partial<Pick<FirstPersonHudState, 'floorLabel' | 'zoneLabel' | 'interact'>>) => void
10
- reset: () => void
11
- }
12
-
13
- export const useFirstPersonHud = create<FirstPersonHudState>((set) => ({
14
- floorLabel: null,
15
- zoneLabel: null,
16
- interact: null,
17
- setHud: (hud) =>
18
- set((state) => {
19
- const floorLabel = hud.floorLabel === undefined ? state.floorLabel : hud.floorLabel
20
- const zoneLabel = hud.zoneLabel === undefined ? state.zoneLabel : hud.zoneLabel
21
- const interact = hud.interact === undefined ? state.interact : hud.interact
22
-
23
- if (
24
- floorLabel === state.floorLabel &&
25
- zoneLabel === state.zoneLabel &&
26
- interact?.label === state.interact?.label &&
27
- interact?.verb === state.interact?.verb
28
- ) {
29
- return state
30
- }
31
-
32
- return { floorLabel, zoneLabel, interact }
33
- }),
34
- reset: () =>
35
- set((state) =>
36
- state.floorLabel === null && state.zoneLabel === null && state.interact === null
37
- ? state
38
- : { floorLabel: null, zoneLabel: null, interact: null },
39
- ),
40
- }))
@@ -1,62 +0,0 @@
1
- import { beforeEach, describe, expect, test } from 'bun:test'
2
- import { FOLLOW_PASCAL_STORAGE_KEY, useFollowPascal } from './use-follow-pascal'
3
-
4
- // Follow Pascal's switch and its pause: touching the camera pauses it and offers "Resume following";
5
- // a new build starts it again; the switch itself is the person's, kept across sessions.
6
- beforeEach(() => {
7
- useFollowPascal.setState({ on: true, paused: false, building: false })
8
- })
9
-
10
- describe('Follow Pascal', () => {
11
- test('is on until the person turns it off, and turning it off clears a pause', () => {
12
- expect(useFollowPascal.getState().on).toBe(true)
13
- useFollowPascal.getState().pause()
14
- expect(useFollowPascal.getState().paused).toBe(true)
15
- useFollowPascal.getState().setOn(false)
16
- expect(useFollowPascal.getState()).toMatchObject({ on: false, paused: false })
17
- })
18
-
19
- test('a pause waits for the person, and only matters while it is on', () => {
20
- useFollowPascal.setState({ on: false })
21
- useFollowPascal.getState().pause()
22
- expect(useFollowPascal.getState().paused).toBe(false)
23
- useFollowPascal.setState({ on: true })
24
- useFollowPascal.getState().pause()
25
- expect(useFollowPascal.getState().paused).toBe(true)
26
- useFollowPascal.getState().resume()
27
- expect(useFollowPascal.getState().paused).toBe(false)
28
- })
29
-
30
- test('a build that begins takes the camera again, even after a pause', () => {
31
- useFollowPascal.getState().pause()
32
- useFollowPascal.getState().beginBuild()
33
- expect(useFollowPascal.getState()).toMatchObject({ paused: false, building: true })
34
- useFollowPascal.getState().endBuild()
35
- expect(useFollowPascal.getState().building).toBe(false)
36
- })
37
-
38
- test('only the switch is kept across sessions', () => {
39
- const persisted = (
40
- useFollowPascal as unknown as {
41
- persist: { getOptions(): { partialize?: (state: unknown) => unknown; name?: string } }
42
- }
43
- ).persist.getOptions()
44
- expect(persisted.name).toBe(FOLLOW_PASCAL_STORAGE_KEY)
45
- expect(persisted.partialize?.({ on: false, paused: true, building: true })).toEqual({
46
- on: false,
47
- })
48
- })
49
-
50
- test('the pill offers itself only to a paused follower during a build', () => {
51
- const shows = () => {
52
- const { on, paused, building } = useFollowPascal.getState()
53
- return on && paused && building
54
- }
55
- useFollowPascal.getState().beginBuild()
56
- expect(shows()).toBe(false)
57
- useFollowPascal.getState().pause()
58
- expect(shows()).toBe(true)
59
- useFollowPascal.getState().endBuild()
60
- expect(shows()).toBe(false)
61
- })
62
- })
@@ -1,53 +0,0 @@
1
- 'use client'
2
-
3
- import { create } from 'zustand'
4
- import { createJSONStorage, persist } from 'zustand/middleware'
5
-
6
- export const FOLLOW_PASCAL_STORAGE_KEY = 'pascal-follow-pascal'
7
-
8
- /** Reads `localStorage` on each call, so a page without it (a test, a sandbox) still has the store's persist API. */
9
- const browserStorage = createJSONStorage(() => ({
10
- getItem: (key: string) => globalThis.localStorage?.getItem(key) ?? null,
11
- setItem: (key: string, value: string) => globalThis.localStorage?.setItem(key, value),
12
- removeItem: (key: string) => globalThis.localStorage?.removeItem(key),
13
- }))
14
-
15
- type FollowPascalState = {
16
- /** The person's switch: the camera follows what a build works on. Kept across sessions. */
17
- on: boolean
18
- /** The person took the camera: it stays theirs until they resume or the next build begins. */
19
- paused: boolean
20
- /** A build is playing (set by the follower from the reveal's own words). */
21
- building: boolean
22
- setOn: (on: boolean) => void
23
- pause: () => void
24
- resume: () => void
25
- beginBuild: () => void
26
- endBuild: () => void
27
- }
28
-
29
- /** Follow Pascal's switch, its pause and whether a build is being followed. */
30
- const useFollowPascal = create<FollowPascalState>()(
31
- persist(
32
- (set, get) => ({
33
- on: true,
34
- paused: false,
35
- building: false,
36
- setOn: (on) => set({ on, paused: false }),
37
- pause: () => {
38
- if (get().on) set({ paused: true })
39
- },
40
- resume: () => set({ paused: false }),
41
- beginBuild: () => set({ building: true, paused: false }),
42
- endBuild: () => set({ building: false }),
43
- }),
44
- {
45
- name: FOLLOW_PASCAL_STORAGE_KEY,
46
- storage: browserStorage,
47
- partialize: (state) => ({ on: state.on }),
48
- },
49
- ),
50
- )
51
-
52
- export default useFollowPascal
53
- export { useFollowPascal }