@pascal-app/viewer 1.1.0 → 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 (224) hide show
  1. package/README.md +157 -0
  2. package/dist/components/viewer/drone-controller.d.ts.map +1 -1
  3. package/dist/components/viewer/drone-controller.js +1 -5
  4. package/dist/components/viewer/first-person/build-collider-world.d.ts +14 -0
  5. package/dist/components/viewer/first-person/build-collider-world.d.ts.map +1 -0
  6. package/dist/components/viewer/first-person/build-collider-world.js +426 -0
  7. package/dist/components/viewer/first-person/ground-holes.d.ts +22 -0
  8. package/dist/components/viewer/first-person/ground-holes.d.ts.map +1 -0
  9. package/dist/components/viewer/first-person/ground-holes.js +116 -0
  10. package/dist/components/viewer/first-person/mechanism-targets.d.ts +27 -0
  11. package/dist/components/viewer/first-person/mechanism-targets.d.ts.map +1 -0
  12. package/dist/components/viewer/first-person/mechanism-targets.js +79 -0
  13. package/dist/components/viewer/first-person/terrain-collider.d.ts +32 -0
  14. package/dist/components/viewer/first-person/terrain-collider.d.ts.map +1 -0
  15. package/dist/components/viewer/first-person/terrain-collider.js +175 -0
  16. package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
  17. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  18. package/dist/components/viewer/glb-walkthrough-controller.js +3 -4
  19. package/dist/components/viewer/live-walkthrough-controller.d.ts +17 -0
  20. package/dist/components/viewer/live-walkthrough-controller.d.ts.map +1 -0
  21. package/dist/components/viewer/live-walkthrough-controller.js +1449 -0
  22. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  23. package/dist/components/viewer/post-processing.js +22 -1
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +3 -0
  27. package/dist/lib/daylight/room-map.d.ts +29 -4
  28. package/dist/lib/daylight/room-map.d.ts.map +1 -1
  29. package/dist/lib/daylight/room-map.js +219 -36
  30. package/dist/lib/daylight/room-scene.d.ts.map +1 -1
  31. package/dist/lib/daylight/room-scene.js +9 -3
  32. package/dist/lib/daylight/rooms.d.ts +15 -6
  33. package/dist/lib/daylight/rooms.d.ts.map +1 -1
  34. package/dist/lib/daylight/rooms.js +49 -9
  35. package/dist/lib/measurement-presentation.d.ts +2 -0
  36. package/dist/lib/measurement-presentation.d.ts.map +1 -1
  37. package/dist/lib/measurement-presentation.js +6 -0
  38. package/dist/lib/typing-target.d.ts +2 -0
  39. package/dist/lib/typing-target.d.ts.map +1 -0
  40. package/dist/lib/typing-target.js +7 -0
  41. package/dist/lib/viewer-config.d.ts +6 -0
  42. package/dist/lib/viewer-config.d.ts.map +1 -1
  43. package/dist/lib/viewer-config.js +2 -0
  44. package/dist/sfx/audio-settings.d.ts +81 -0
  45. package/dist/sfx/audio-settings.d.ts.map +1 -0
  46. package/dist/sfx/audio-settings.js +166 -0
  47. package/dist/sfx/index.d.ts +6 -0
  48. package/dist/sfx/index.d.ts.map +1 -0
  49. package/dist/sfx/index.js +5 -0
  50. package/dist/sfx/interaction-sounds.d.ts +7 -0
  51. package/dist/sfx/interaction-sounds.d.ts.map +1 -0
  52. package/dist/sfx/interaction-sounds.js +39 -0
  53. package/dist/sfx/player.d.ts +31 -0
  54. package/dist/sfx/player.d.ts.map +1 -0
  55. package/dist/sfx/player.js +255 -0
  56. package/dist/sfx/sounds.d.ts +60 -0
  57. package/dist/sfx/sounds.d.ts.map +1 -0
  58. package/dist/sfx/sounds.js +25 -0
  59. package/dist/sfx/use-viewer-ui-sounds.d.ts +8 -0
  60. package/dist/sfx/use-viewer-ui-sounds.d.ts.map +1 -0
  61. package/dist/sfx/use-viewer-ui-sounds.js +42 -0
  62. package/dist/store/use-first-person-hud.d.ts +13 -0
  63. package/dist/store/use-first-person-hud.d.ts.map +1 -0
  64. package/dist/store/use-first-person-hud.js +21 -0
  65. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  66. package/dist/systems/roof/roof-system.js +27 -13
  67. package/dist/ui/components/action-button.d.ts +14 -0
  68. package/dist/ui/components/action-button.d.ts.map +1 -0
  69. package/dist/ui/components/action-button.js +21 -0
  70. package/dist/ui/components/action-menu-placement.d.ts +7 -0
  71. package/dist/ui/components/action-menu-placement.d.ts.map +1 -0
  72. package/dist/ui/components/action-menu-placement.js +10 -0
  73. package/dist/ui/components/floorplan-compass-button.d.ts +10 -0
  74. package/dist/ui/components/floorplan-compass-button.d.ts.map +1 -0
  75. package/dist/ui/components/floorplan-compass-button.js +22 -0
  76. package/dist/ui/components/follow-pascal-toggle.d.ts +6 -0
  77. package/dist/ui/components/follow-pascal-toggle.d.ts.map +1 -0
  78. package/dist/ui/components/follow-pascal-toggle.js +21 -0
  79. package/dist/ui/components/localized-html.d.ts +9 -0
  80. package/dist/ui/components/localized-html.d.ts.map +1 -0
  81. package/dist/ui/components/localized-html.js +16 -0
  82. package/dist/ui/components/render-settings-menu.d.ts +9 -0
  83. package/dist/ui/components/render-settings-menu.d.ts.map +1 -0
  84. package/dist/ui/components/render-settings-menu.js +145 -0
  85. package/dist/ui/components/rooms/room-highlight.d.ts +10 -0
  86. package/dist/ui/components/rooms/room-highlight.d.ts.map +1 -0
  87. package/dist/ui/components/rooms/room-highlight.js +56 -0
  88. package/dist/ui/components/rooms/room-labels.d.ts +278 -0
  89. package/dist/ui/components/rooms/room-labels.d.ts.map +1 -0
  90. package/dist/ui/components/rooms/room-labels.js +1295 -0
  91. package/dist/ui/components/rooms/viewer-rooms.d.ts +14 -0
  92. package/dist/ui/components/rooms/viewer-rooms.d.ts.map +1 -0
  93. package/dist/ui/components/rooms/viewer-rooms.js +123 -0
  94. package/dist/ui/components/scene-loader.d.ts +18 -0
  95. package/dist/ui/components/scene-loader.d.ts.map +1 -0
  96. package/dist/ui/components/scene-loader.js +29 -0
  97. package/dist/ui/components/sky-control.d.ts +37 -0
  98. package/dist/ui/components/sky-control.d.ts.map +1 -0
  99. package/dist/ui/components/sky-control.js +382 -0
  100. package/dist/ui/components/use-viewer-camera-navigation-sync.d.ts +4 -0
  101. package/dist/ui/components/use-viewer-camera-navigation-sync.d.ts.map +1 -0
  102. package/dist/ui/components/use-viewer-camera-navigation-sync.js +117 -0
  103. package/dist/ui/components/viewer-controls-bar.d.ts +20 -0
  104. package/dist/ui/components/viewer-controls-bar.d.ts.map +1 -0
  105. package/dist/ui/components/viewer-controls-bar.js +130 -0
  106. package/dist/ui/components/viewer-hover-label.d.ts +14 -0
  107. package/dist/ui/components/viewer-hover-label.d.ts.map +1 -0
  108. package/dist/ui/components/viewer-hover-label.js +28 -0
  109. package/dist/ui/components/viewer-scene-header.d.ts +34 -0
  110. package/dist/ui/components/viewer-scene-header.d.ts.map +1 -0
  111. package/dist/ui/components/viewer-scene-header.js +92 -0
  112. package/dist/ui/components/viewer-selection-manager.d.ts +7 -0
  113. package/dist/ui/components/viewer-selection-manager.d.ts.map +1 -0
  114. package/dist/ui/components/viewer-selection-manager.js +116 -0
  115. package/dist/ui/components/viewer-stage-modes.d.ts +7 -0
  116. package/dist/ui/components/viewer-stage-modes.d.ts.map +1 -0
  117. package/dist/ui/components/viewer-stage-modes.js +42 -0
  118. package/dist/ui/components/viewer-stage-switcher.d.ts +10 -0
  119. package/dist/ui/components/viewer-stage-switcher.d.ts.map +1 -0
  120. package/dist/ui/components/viewer-stage-switcher.js +16 -0
  121. package/dist/ui/components/viewer-stage.d.ts +25 -0
  122. package/dist/ui/components/viewer-stage.d.ts.map +1 -0
  123. package/dist/ui/components/viewer-stage.js +107 -0
  124. package/dist/ui/components/walkthrough-hud.d.ts +29 -0
  125. package/dist/ui/components/walkthrough-hud.d.ts.map +1 -0
  126. package/dist/ui/components/walkthrough-hud.js +55 -0
  127. package/dist/ui/floorplan/floorplan-camera-sync.d.ts +22 -0
  128. package/dist/ui/floorplan/floorplan-camera-sync.d.ts.map +1 -0
  129. package/dist/ui/floorplan/floorplan-camera-sync.js +157 -0
  130. package/dist/ui/floorplan/floorplan-dimension-renderer.d.ts +61 -0
  131. package/dist/ui/floorplan/floorplan-dimension-renderer.d.ts.map +1 -0
  132. package/dist/ui/floorplan/floorplan-dimension-renderer.js +280 -0
  133. package/dist/ui/floorplan/floorplan-geometry-renderer.d.ts +54 -0
  134. package/dist/ui/floorplan/floorplan-geometry-renderer.d.ts.map +1 -0
  135. package/dist/ui/floorplan/floorplan-geometry-renderer.js +317 -0
  136. package/dist/ui/floorplan/floorplan-label-angle.d.ts +24 -0
  137. package/dist/ui/floorplan/floorplan-label-angle.d.ts.map +1 -0
  138. package/dist/ui/floorplan/floorplan-label-angle.js +60 -0
  139. package/dist/ui/floorplan/floorplan-navigation-sync.d.ts +18 -0
  140. package/dist/ui/floorplan/floorplan-navigation-sync.d.ts.map +1 -0
  141. package/dist/ui/floorplan/floorplan-navigation-sync.js +41 -0
  142. package/dist/ui/floorplan/floorplan-preview-geometry.d.ts +26 -0
  143. package/dist/ui/floorplan/floorplan-preview-geometry.d.ts.map +1 -0
  144. package/dist/ui/floorplan/floorplan-preview-geometry.js +173 -0
  145. package/dist/ui/floorplan/floorplan-preview-navigation.d.ts +22 -0
  146. package/dist/ui/floorplan/floorplan-preview-navigation.d.ts.map +1 -0
  147. package/dist/ui/floorplan/floorplan-preview-navigation.js +41 -0
  148. package/dist/ui/floorplan/floorplan-preview-visibility.d.ts +8 -0
  149. package/dist/ui/floorplan/floorplan-preview-visibility.d.ts.map +1 -0
  150. package/dist/ui/floorplan/floorplan-preview-visibility.js +21 -0
  151. package/dist/ui/floorplan/floorplan-preview.d.ts +19 -0
  152. package/dist/ui/floorplan/floorplan-preview.d.ts.map +1 -0
  153. package/dist/ui/floorplan/floorplan-preview.js +700 -0
  154. package/dist/ui/hooks/use-mobile.d.ts +2 -0
  155. package/dist/ui/hooks/use-mobile.d.ts.map +1 -0
  156. package/dist/ui/hooks/use-mobile.js +14 -0
  157. package/dist/ui/index.d.ts +49 -0
  158. package/dist/ui/index.d.ts.map +1 -0
  159. package/dist/ui/index.js +48 -0
  160. package/dist/ui/lib/below-grade.d.ts +38 -0
  161. package/dist/ui/lib/below-grade.d.ts.map +1 -0
  162. package/dist/ui/lib/below-grade.js +102 -0
  163. package/dist/ui/lib/camera-pose.d.ts +19 -0
  164. package/dist/ui/lib/camera-pose.d.ts.map +1 -0
  165. package/dist/ui/lib/camera-pose.js +113 -0
  166. package/dist/ui/lib/cn.d.ts +3 -0
  167. package/dist/ui/lib/cn.d.ts.map +1 -0
  168. package/dist/ui/lib/cn.js +5 -0
  169. package/dist/ui/lib/level-name.d.ts +10 -0
  170. package/dist/ui/lib/level-name.d.ts.map +1 -0
  171. package/dist/ui/lib/level-name.js +33 -0
  172. package/dist/ui/lib/node-display-label.d.ts +8 -0
  173. package/dist/ui/lib/node-display-label.d.ts.map +1 -0
  174. package/dist/ui/lib/node-display-label.js +36 -0
  175. package/dist/ui/lib/room-area.d.ts +3 -0
  176. package/dist/ui/lib/room-area.d.ts.map +1 -0
  177. package/dist/ui/lib/room-area.js +8 -0
  178. package/dist/ui/lib/room-assembly-overlay.d.ts +46 -0
  179. package/dist/ui/lib/room-assembly-overlay.d.ts.map +1 -0
  180. package/dist/ui/lib/room-assembly-overlay.js +222 -0
  181. package/dist/ui/lib/room-name-messages.d.ts +28 -0
  182. package/dist/ui/lib/room-name-messages.d.ts.map +1 -0
  183. package/dist/ui/lib/room-name-messages.js +112 -0
  184. package/dist/ui/lib/room-selection.d.ts +58 -0
  185. package/dist/ui/lib/room-selection.d.ts.map +1 -0
  186. package/dist/ui/lib/room-selection.js +297 -0
  187. package/dist/ui/lib/scene-theme-name.d.ts +4 -0
  188. package/dist/ui/lib/scene-theme-name.d.ts.map +1 -0
  189. package/dist/ui/lib/scene-theme-name.js +22 -0
  190. package/dist/ui/lib/selection-modifiers.d.ts +20 -0
  191. package/dist/ui/lib/selection-modifiers.d.ts.map +1 -0
  192. package/dist/ui/lib/selection-modifiers.js +15 -0
  193. package/dist/ui/lib/viewer-selection.d.ts +132 -0
  194. package/dist/ui/lib/viewer-selection.d.ts.map +1 -0
  195. package/dist/ui/lib/viewer-selection.js +462 -0
  196. package/dist/ui/lib/zone-content.d.ts +6 -0
  197. package/dist/ui/lib/zone-content.d.ts.map +1 -0
  198. package/dist/ui/lib/zone-content.js +96 -0
  199. package/dist/ui/primitives/button.d.ts +11 -0
  200. package/dist/ui/primitives/button.d.ts.map +1 -0
  201. package/dist/ui/primitives/button.js +35 -0
  202. package/dist/ui/primitives/dropdown-menu.d.ts +26 -0
  203. package/dist/ui/primitives/dropdown-menu.d.ts.map +1 -0
  204. package/dist/ui/primitives/dropdown-menu.js +53 -0
  205. package/dist/ui/primitives/popover.d.ts +8 -0
  206. package/dist/ui/primitives/popover.d.ts.map +1 -0
  207. package/dist/ui/primitives/popover.js +17 -0
  208. package/dist/ui/primitives/switch.d.ts +5 -0
  209. package/dist/ui/primitives/switch.d.ts.map +1 -0
  210. package/dist/ui/primitives/switch.js +8 -0
  211. package/dist/ui/primitives/tooltip.d.ts +8 -0
  212. package/dist/ui/primitives/tooltip.d.ts.map +1 -0
  213. package/dist/ui/primitives/tooltip.js +17 -0
  214. package/dist/ui/store/camera-pose-store.d.ts +17 -0
  215. package/dist/ui/store/camera-pose-store.d.ts.map +1 -0
  216. package/dist/ui/store/camera-pose-store.js +15 -0
  217. package/dist/ui/store/navigation-sync-pose-store.d.ts +26 -0
  218. package/dist/ui/store/navigation-sync-pose-store.d.ts.map +1 -0
  219. package/dist/ui/store/navigation-sync-pose-store.js +16 -0
  220. package/dist/ui/store/use-follow-pascal.d.ts +30 -0
  221. package/dist/ui/store/use-follow-pascal.d.ts.map +1 -0
  222. package/dist/ui/store/use-follow-pascal.js +28 -0
  223. package/dist/ui/theme.css +339 -0
  224. package/package.json +34 -7
@@ -1 +1 @@
1
- {"version":3,"file":"room-scene.d.ts","sourceRoot":"","sources":["../../../src/lib/daylight/room-scene.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoD,KAAK,QAAQ,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAMhG,OAAO,KAAK,EAAE,QAAQ,EAAmB,cAAc,EAAY,MAAM,SAAS,CAAA;AAGlF;;;GAGG;AAEH,mGAAmG;AACnG,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gBAAgB,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;IAC3C,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,yFAAyF;IACzF,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3F,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CAC3C,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAA;IACrC,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,CAAA;CAC5C,CAAA;AAED,8FAA8F;AAC9F,eAAO,MAAM,SAAS,eAAsB,CAAA;AAU5C,oEAAoE;AACpE,wBAAgB,SAAS,IAAI,QAAQ,EAAE,CAuBtC;AAeD,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAK3D;AAkCD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,cAAc,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAuE5E;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,EAAE,CAE3D;AAqBD,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,QAAQ,EAAE,CAEnE;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAanD;AASD,0FAA0F;AAC1F,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CA2E7F;AAiED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CA2CvC;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,IAAI,IAAI,CAM1C"}
1
+ {"version":3,"file":"room-scene.d.ts","sourceRoot":"","sources":["../../../src/lib/daylight/room-scene.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoD,KAAK,QAAQ,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAMhG,OAAO,KAAK,EAAE,QAAQ,EAAmB,cAAc,EAAY,MAAM,SAAS,CAAA;AAGlF;;;GAGG;AAEH,mGAAmG;AACnG,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gBAAgB,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;IAC3C,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,yFAAyF;IACzF,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3F,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CAC3C,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,MAAM,EAAE,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAA;IACrC,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,CAAA;CAC5C,CAAA;AAED,8FAA8F;AAC9F,eAAO,MAAM,SAAS,eAAsB,CAAA;AAU5C,oEAAoE;AACpE,wBAAgB,SAAS,IAAI,QAAQ,EAAE,CAuBtC;AAeD,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAK3D;AAkCD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,cAAc,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CA0E5E;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,EAAE,CAE3D;AAqBD,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,QAAQ,EAAE,CAEnE;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAanD;AASD,0FAA0F;AAC1F,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CA8E7F;AAiED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CA2CvC;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,IAAI,IAAI,CAM1C"}
@@ -138,9 +138,12 @@ export function liveRoomScene(virtualLampsOn) {
138
138
  const walls = [];
139
139
  for (const id of sceneRegistry.byType.wall ?? []) {
140
140
  const object = sceneRegistry.nodes.get(id);
141
+ if (!object)
142
+ continue;
143
+ const wallLevel = levelIndex.get(nodes[id]?.parentId ?? '') ?? 0;
141
144
  // Doors, windows and items hang under their wall: a closed leaf mustn't close its doorway.
142
- if (object)
143
- walls.push(...meshesOf(object, (child) => registered.has(child)));
145
+ for (const mesh of meshesOf(object, (child) => registered.has(child)))
146
+ walls.push({ object: mesh, levelIndex: wallLevel });
144
147
  }
145
148
  const openings = [];
146
149
  for (const kind of OPENING_TYPES) {
@@ -266,7 +269,10 @@ export function bakedRoomScene(root, virtualLampsOn) {
266
269
  const kind = object.userData.kind;
267
270
  return kind === 'door' || kind === 'window' || kind === 'item';
268
271
  };
269
- const walls = wallRoots.flatMap((object) => meshesOf(object, isIdentity));
272
+ const walls = wallRoots.flatMap((object) => {
273
+ const levelIndex = levelOf(object).index;
274
+ return meshesOf(object, isIdentity).map((mesh) => ({ object: mesh, levelIndex }));
275
+ });
270
276
  const bounds = new Box3();
271
277
  for (const zone of zones)
272
278
  for (const [x, z] of zone.polygon)
@@ -4,8 +4,8 @@ import { type MapLevel, RoomMap } from './room-map';
4
4
  * "Room-aware" interior light, the game-engine way (ported from the enhanced viewer):
5
5
  *
6
6
  * - Zone polygons are rasterized per level into a top-down room-id map (10 cm cells). A fragment
7
- * finds its room by looking up `p + n·0.18` — a wall face looks *into* the room it faces, so
8
- * light never leaks through walls and there are no shadow maps to pay for.
7
+ * finds its room a step off itself along its normal — a wall face looks *into* the room it
8
+ * faces, so light never leaks through walls and there are no shadow maps to pay for.
9
9
  * - Each room owns its lamps and window "portals": glass rectangles that emit sky (+ ground
10
10
  * bounce) radiance inwards with an analytic rectangle form factor, plus the internally
11
11
  * reflected component (split-flux) and the sky patch on the floor. Enclosed rooms see only
@@ -65,8 +65,14 @@ export type RoomSceneInput = {
65
65
  object: Object3D;
66
66
  kind: 'ceiling' | 'roof' | 'slab';
67
67
  }[];
68
- /** Wall meshes only (openings and their parts excluded), for walkable-opening rays. */
69
- walls: Object3D[];
68
+ /**
69
+ * Wall meshes only (openings and their parts excluded), each with its level: walkable-opening
70
+ * rays test them, and their footprints keep rooms from reaching past a wall's far face.
71
+ */
72
+ walls: {
73
+ object: Object3D;
74
+ levelIndex: number;
75
+ }[];
70
76
  openings: RoomOpeningRoot[];
71
77
  lamps: RoomLamp[];
72
78
  /** Whether rooms without lamps of their own get their virtual ceiling lamp lit. */
@@ -235,8 +241,11 @@ export declare class RoomLighting {
235
241
  /** Room index (−1 outside) at a world-space position (live heights). */
236
242
  roomAtNode: import("three/tsl").FnNode<[any], import("three/webgpu").VarNode<"int", import("three/webgpu").ConstNode<"int", number>>>;
237
243
  /**
238
- * The room a surface faces (probed 18 cm off it along its normal). One node per material: its
239
- * lighting and its exterior mask share the lookup.
244
+ * The room a surface faces, probed off it along its normal: 30 cm sideways, 18 cm up or down.
245
+ * Sideways it must clear a wall from inside it: a shutter, a door leaf or a pane set half-way
246
+ * into a 30 cm wall looks out past the wall's face, not into the cells along it, where a wall
247
+ * off the grid axes reads as room and outside in turn. One node per material: its lighting and
248
+ * its exterior mask share the lookup.
240
249
  */
241
250
  probeRoomNode(): N;
242
251
  /** E' = Emax·(1 − e^(−E/Emax)), on luminance, hue kept — near-field hot spots compress, never blow out. */
@@ -1 +1 @@
1
- {"version":3,"file":"rooms.d.ts","sourceRoot":"","sources":["../../../src/lib/daylight/rooms.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EAEJ,KAAK,MAAM,EACX,KAAK,EASL,KAAK,QAAQ,EAGb,SAAS,EAGT,OAAO,EACP,OAAO,EACR,MAAM,OAAO,CAAA;AAsBd,OAAO,EAAQ,KAAK,QAAQ,EAAE,OAAO,EAAwB,MAAM,YAAY,CAAA;AAE/E;;;;;;;;;;;;;;;;;;GAkBG;AAEH,KAAK,CAAC,GAAG,GAAG,CAAA;AAWZ,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,KAAK,CAAA;IACZ,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,6CAA6C;IAC7C,EAAE,EAAE,OAAO,CAAA;IACX,8EAA8E;IAC9E,IAAI,CAAC,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IACjE;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,gBAAgB;IAChB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;IAC3B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvF,mFAAmF;AACnF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,IAAI,CAAA;IACZ,2FAA2F;IAC3F,MAAM,EAAE,QAAQ,EAAE,CAAA;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,kEAAkE;IAClE,MAAM,EAAE;QAAE,MAAM,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAA;KAAE,EAAE,CAAA;IACjE,uFAAuF;IACvF,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,mFAAmF;IACnF,cAAc,EAAE,OAAO,CAAA;IACvB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAA;CACpD,CAAA;AAED,KAAK,MAAM,GAAG;IACZ,MAAM,EAAE,OAAO,CAAA;IACf,4BAA4B;IAC5B,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,kFAAkF;IAClF,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,KAAK,OAAO,GAAG;IACb,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,MAAM,EAAE,OAAO,CAAA;IACf,yCAAyC;IACzC,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAWD,KAAK,IAAI,GAAG;IACV,IAAI,EAAE,QAAQ,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,OAAO,CAAA;IACpB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAA;IAChB,2FAA2F;IAC3F,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,KAAK,CAAA;AAOpD,qBAAa,YAAY;IACvB,KAAK,EAAE,IAAI,EAAE,CAAK;IAClB,gGAAgG;IAChG,aAAa,EAAE,QAAQ,EAAE,CAAK;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAK;IACtB,QAAQ,EAAE,OAAO,EAAE,CAAK;IACxB,MAAM,EAAE,QAAQ,EAAE,CAAK;IACvB,qEAAqE;IACrE,QAAQ,CAAC,GAAG,UAAgB;IAC5B,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,OAAO,CAAqD;IAGpE,QAAQ,CAAC,UAAU,sDAAe;IAClC,QAAQ,CAAC,UAAU,sDAAe;IAClC,QAAQ,CAAC,QAAQ,sDAAa;IAC9B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,sDAAa;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,sDAAe;IACjC,qCAAqC;IACrC,QAAQ,CAAC,SAAS,sDAAa;IAC/B,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,sDAAe;IAChC,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,sDAAa;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,SAAS,sDAAa;IAC/B,6BAA6B;IAC7B,QAAQ,CAAC,OAAO,sDAAa;IAG7B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAUjB;IAED,0EAA0E;IAC1E,KAAK,CAAC,KAAK,EAAE,cAAc,QAwM1B;IAED,oGAAoG;IACpG,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAEzB;IAED;;;;OAIG;IACH,UAAU,EAAE,OAAO,EAAE,CAAK;IAC1B,0FAA0F;IAC1F,YAAY,EAAE,OAAO,EAAE,CAAK;IAC5B,oFAAoF;IACpF,OAAO,CAAC,aAAa,CAAgB;IAErC;;;;;;;;OAQG;IACH,WAAW,CACT,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,KAAK,EACf,MAAM,SAAI,GACT;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,KAAK,GAAG,IAAI,CAAA;KAAE,CAmD9D;IAED,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAK1D;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAkB5E;IAED,oFAAoF;IACpF,OAAO,SAEN;IAED,yFAAyF;IACzF,aAAa,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,GAAG,OAAO,CAexD;IAED,8FAA8F;IAC9F,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,QAgB/C;IAED,yDAAyD;IACzD,YAAY,CAAC,IAAI,EAAE,MAAM,UAUxB;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAS/B;IAED,0FAA0F;IAC1F,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAczE;IAED;;;;OAIG;IACH,SAAS,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,QAwDzF;IAOD,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,UAAU,CAAI;IAEtB,OAAO,CAAC,YAAY;IA6EpB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,SAAS;IAOjB,qGAAqG;IACrG,OAAO,CAAC,oBAAoB;IAkB5B,OAAO,CAAC,GAAG;IAIX;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IA6GrB,2FAA2F;IAC3F,OAAO,CAAC,cAAc;IA2DtB,wEAAwE;IACxE,UAAU,4HAA2C;IAErD;;;OAGG;IACH,aAAa,IAAI,CAAC,CAEjB;IAED,2GAA2G;IAC3G,OAAO,CAAC,cAAc;IAMtB;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,KAAK;IAKb;;;;OAIG;IACH,YAAY,6EAsIV;IAEF;;;;;;;;OAQG;IACH,iBAAiB,8EAsDf;IAEF;;;;OAIG;IACH,iBAAiB,8CA6Bf;IAEF,uFAAuF;IACvF,gBAAgB,gIAWd;IAEF,2FAA2F;IAC3F,YAAY,yEASV;CACH;AA8ED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,WAQ5E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,UAKxD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,UAKnD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,WAYvD;AAED,iDAAiD;AACjD,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,GAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAO,WAuBvF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,SAgB/B;AAED,qEAAqE;AACrE,eAAO,MAAM,aAAa,cAAqB,CAAA"}
1
+ {"version":3,"file":"rooms.d.ts","sourceRoot":"","sources":["../../../src/lib/daylight/rooms.ts"],"names":[],"mappings":"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"}
@@ -131,7 +131,7 @@ export class RoomLighting {
131
131
  level: room.levelIndex,
132
132
  polygon: room.zone.polygon,
133
133
  holes: room.zone.holes,
134
- })), openings.walk);
134
+ })), openings.walk, wallFootprints(input.walls));
135
135
  this.openings = openings.finish();
136
136
  // ---- lamps ---------------------------------------------------------------------------
137
137
  for (const lamp of input.lamps) {
@@ -676,12 +676,14 @@ export class RoomLighting {
676
676
  .map((o) => ({ id: o.id, box: new Box3().setFromObject(o.object) }));
677
677
  const doorHoles = doors.map((door) => door.box.clone().expandByScalar(0.05));
678
678
  ray.far = 0.9;
679
- const wallGrid = new MeshGrid(input.walls);
679
+ const wallGrid = new MeshGrid(input.walls.map((wall) => wall.object));
680
680
  const samples = new Map();
681
681
  const v = new Vector3();
682
682
  const dirV = new Vector3();
683
683
  const walk = (tile) => {
684
684
  const { cells, stride, i0, j0, x0, z0, ci0, cj0, level: l } = tile;
685
+ // The tile lies in its building's turned frame: points and directions go back to the world.
686
+ const [cos, sin] = tile.turn;
685
687
  const floorY = this.levels[l]?.y ?? 0;
686
688
  const visit = (i, j, axis) => {
687
689
  const di = axis === 'x' ? 1 : 0;
@@ -699,9 +701,11 @@ export class RoomLighting {
699
701
  return;
700
702
  if (b < 0 && !this.rooms[a].enclosed)
701
703
  return;
702
- const x = x0 + (i + 0.5 + di * 0.5) * CELL;
703
- const z = z0 + (j + 0.5 + dj * 0.5) * CELL;
704
- dirV.set(di, 0, dj);
704
+ const mx = x0 + (i + 0.5 + di * 0.5) * CELL;
705
+ const mz = z0 + (j + 0.5 + dj * 0.5) * CELL;
706
+ const x = mx * cos - mz * sin;
707
+ const z = mx * sin + mz * cos;
708
+ dirV.set(di * cos - dj * sin, 0, di * sin + dj * cos);
705
709
  for (const h of [0.35, 1.6]) {
706
710
  v.set(x, floorY + h, z).addScaledVector(dirV, -0.45);
707
711
  ray.set(v, dirV);
@@ -722,7 +726,7 @@ export class RoomLighting {
722
726
  samples.set(key, s);
723
727
  }
724
728
  s.pts.push(new Vector3(x, floorY, z));
725
- s.facing.push(new Vector3(di * dir, 0, dj * dir));
729
+ s.facing.push(dirV.clone().multiplyScalar(dir));
726
730
  };
727
731
  // Each edge belongs to the tile of the cell before it. A uniform tile has none inside: only
728
732
  // its borders with the next tiles can hold one.
@@ -836,11 +840,14 @@ export class RoomLighting {
836
840
  /** Room index (−1 outside) at a world-space position (live heights). */
837
841
  roomAtNode = Fn(([p]) => this.map.roomAtNode(p));
838
842
  /**
839
- * The room a surface faces (probed 18 cm off it along its normal). One node per material: its
840
- * lighting and its exterior mask share the lookup.
843
+ * The room a surface faces, probed off it along its normal: 30 cm sideways, 18 cm up or down.
844
+ * Sideways it must clear a wall from inside it: a shutter, a door leaf or a pane set half-way
845
+ * into a 30 cm wall looks out past the wall's face, not into the cells along it, where a wall
846
+ * off the grid axes reads as room and outside in turn. One node per material: its lighting and
847
+ * its exterior mask share the lookup.
841
848
  */
842
849
  probeRoomNode() {
843
- return this.roomAtNode(positionWorld.add(normalWorld.mul(0.18)));
850
+ return this.roomAtNode(positionWorld.add(normalWorld.mul(vec3(0.3, 0.18, 0.3))));
844
851
  }
845
852
  /** E' = Emax·(1 − e^(−E/Emax)), on luminance, hue kept — near-field hot spots compress, never blow out. */
846
853
  softClampLamps(e) {
@@ -1107,6 +1114,39 @@ export class RoomLighting {
1107
1114
  });
1108
1115
  }
1109
1116
  // ---- helpers -------------------------------------------------------------------------------
1117
+ /**
1118
+ * The walls' footprints per level: every triangle that has an area seen from above (tops,
1119
+ * bottoms, lintels, sills), in world XZ. Read from the vertices as they are now, like `MeshGrid`.
1120
+ */
1121
+ function wallFootprints(walls) {
1122
+ const byLevel = new Map();
1123
+ const a = new Vector3();
1124
+ const b = new Vector3();
1125
+ const c = new Vector3();
1126
+ for (const { object, levelIndex } of walls) {
1127
+ const mesh = object;
1128
+ const position = mesh.isMesh ? mesh.geometry?.getAttribute('position') : undefined;
1129
+ if (!position || position.count === 0)
1130
+ continue;
1131
+ let triangles = byLevel.get(levelIndex);
1132
+ if (!triangles) {
1133
+ triangles = [];
1134
+ byLevel.set(levelIndex, triangles);
1135
+ }
1136
+ const index = mesh.geometry.getIndex();
1137
+ const count = index ? index.count : position.count;
1138
+ for (let k = 0; k + 2 < count; k += 3) {
1139
+ a.fromBufferAttribute(position, index ? index.getX(k) : k).applyMatrix4(mesh.matrixWorld);
1140
+ b.fromBufferAttribute(position, index ? index.getX(k + 1) : k + 1).applyMatrix4(mesh.matrixWorld);
1141
+ c.fromBufferAttribute(position, index ? index.getX(k + 2) : k + 2).applyMatrix4(mesh.matrixWorld);
1142
+ // Faces seen edge-on from above (the wall's sides) add nothing to its footprint.
1143
+ if (Math.abs((b.x - a.x) * (c.z - a.z) - (c.x - a.x) * (b.z - a.z)) < 1e-6)
1144
+ continue;
1145
+ triangles.push(a.x, a.z, b.x, b.z, c.x, c.z);
1146
+ }
1147
+ }
1148
+ return [...byLevel].map(([level, triangles]) => ({ level, triangles }));
1149
+ }
1110
1150
  /**
1111
1151
  * Ray hits against the meshes' geometry itself, nearest first. Picking overrides (walls hidden
1112
1152
  * by the cutaway, ceiling caps, batched originals) mustn't open rooms to the sky or invent
@@ -2,4 +2,6 @@ export declare const MEASUREMENT_ACTIVE_COLOR = "#6366f1";
2
2
  export declare const MEASUREMENT_DANGLING_COLOR = "#dc2626";
3
3
  export declare const MEASUREMENT_PERSISTENT_COLOR = "#111827";
4
4
  export declare function measurementPresentationColor(dangling: boolean, active: boolean): string;
5
+ export declare const MEASUREMENT_FLOORPLAN_COLOR = "#4f46e5";
6
+ export declare function measurementFloorplanPresentationColor(dangling: boolean, active: boolean): string;
5
7
  //# sourceMappingURL=measurement-presentation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"measurement-presentation.d.ts","sourceRoot":"","sources":["../../src/lib/measurement-presentation.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,YAAY,CAAA;AACjD,eAAO,MAAM,0BAA0B,YAAY,CAAA;AAEnD,eAAO,MAAM,4BAA4B,YAAY,CAAA;AAErD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAGvF"}
1
+ {"version":3,"file":"measurement-presentation.d.ts","sourceRoot":"","sources":["../../src/lib/measurement-presentation.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,YAAY,CAAA;AACjD,eAAO,MAAM,0BAA0B,YAAY,CAAA;AAEnD,eAAO,MAAM,4BAA4B,YAAY,CAAA;AAErD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAGvF;AAED,eAAO,MAAM,2BAA2B,YAAY,CAAA;AAEpD,wBAAgB,qCAAqC,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAGhG"}
@@ -6,3 +6,9 @@ export function measurementPresentationColor(dangling, active) {
6
6
  return MEASUREMENT_DANGLING_COLOR;
7
7
  return active ? MEASUREMENT_ACTIVE_COLOR : MEASUREMENT_PERSISTENT_COLOR;
8
8
  }
9
+ export const MEASUREMENT_FLOORPLAN_COLOR = '#4f46e5';
10
+ export function measurementFloorplanPresentationColor(dangling, active) {
11
+ if (dangling)
12
+ return MEASUREMENT_DANGLING_COLOR;
13
+ return active ? MEASUREMENT_ACTIVE_COLOR : MEASUREMENT_FLOORPLAN_COLOR;
14
+ }
@@ -0,0 +1,2 @@
1
+ export declare function isTypingTarget(target: EventTarget | null): boolean;
2
+ //# sourceMappingURL=typing-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typing-target.d.ts","sourceRoot":"","sources":["../../src/lib/typing-target.ts"],"names":[],"mappings":"AAKA,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,WAGxD"}
@@ -0,0 +1,7 @@
1
+ // Keys typed into a field (the room-name combobox, any input, an open dialog)
2
+ // belong to that field, never to a canvas gesture.
3
+ const TYPING_TARGET = 'input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="combobox"], [role="textbox"], [role="dialog"], [role="alertdialog"]';
4
+ export function isTypingTarget(target) {
5
+ const element = target;
6
+ return !!element && (element.isContentEditable === true || !!element.closest?.(TYPING_TARGET));
7
+ }
@@ -1,6 +1,10 @@
1
1
  export type ViewerConfig = {
2
2
  assetsBaseUrl?: string;
3
3
  storageUrl?: string;
4
+ /** Base URL for interaction and UI sound files, resolved when played or preloaded. */
5
+ sfxBaseUrl?: string;
6
+ /** Base URL for the viewer UI's icons (`viewer/ui`), resolved per render. */
7
+ iconsBaseUrl?: string;
4
8
  decoders?: {
5
9
  dracoPath?: string;
6
10
  basisPath?: string;
@@ -13,6 +17,8 @@ export declare function configureViewer(options: ViewerConfig): void;
13
17
  export declare function getViewerConfig(): {
14
18
  assetsBaseUrl: string;
15
19
  storageUrl: string | undefined;
20
+ sfxBaseUrl: string;
21
+ iconsBaseUrl: string;
16
22
  decoders: {
17
23
  dracoPath: string;
18
24
  basisPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"viewer-config.d.ts","sourceRoot":"","sources":["../../src/lib/viewer-config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE;QACT,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF,CAAA;AAED,eAAO,MAAM,cAAc,8BAA8B,CAAA;AAIzD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAE3D;AAED,qFAAqF;AACrF,wBAAgB,eAAe;IAE3B,aAAa;IAIb,UAAU;IAGV,QAAQ;QACN,SAAS;QAET,SAAS;;EAKd"}
1
+ {"version":3,"file":"viewer-config.d.ts","sourceRoot":"","sources":["../../src/lib/viewer-config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE;QACT,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF,CAAA;AAED,eAAO,MAAM,cAAc,8BAA8B,CAAA;AAIzD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAE3D;AAED,qFAAqF;AACrF,wBAAgB,eAAe;IAE3B,aAAa;IAIb,UAAU;IAGV,UAAU;IACV,YAAY;IACZ,QAAQ;QACN,SAAS;QAET,SAAS;;EAKd"}
@@ -12,6 +12,8 @@ export function getViewerConfig() {
12
12
  ASSETS_CDN_URL),
13
13
  storageUrl: config.storageUrl ??
14
14
  (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_SUPABASE_URL : undefined),
15
+ sfxBaseUrl: config.sfxBaseUrl ?? `${ASSETS_CDN_URL}/audios/sfx`,
16
+ iconsBaseUrl: config.iconsBaseUrl ?? `${ASSETS_CDN_URL}/icons`,
15
17
  decoders: {
16
18
  dracoPath: config.decoders?.dracoPath ?? 'https://www.gstatic.com/draco/versioned/decoders/1.5.5/',
17
19
  basisPath: config.decoders?.basisPath ??
@@ -0,0 +1,81 @@
1
+ /** Persisted volume and playback preferences shared across tabs. */
2
+ export declare const AUDIO_SETTINGS_STORAGE_KEY = "pascal-audio-settings";
3
+ /** Per-tab session mute and the local preference inherited by new tabs. */
4
+ export declare const AUDIO_MUTED_STORAGE_KEY = "pascal-audio-muted";
5
+ interface AudioState {
6
+ masterVolume: number;
7
+ sfxVolume: number;
8
+ radioVolume: number;
9
+ isRadioPlaying: boolean;
10
+ /** This tab's mute; the volumes are every tab's. */
11
+ muted: boolean;
12
+ autoplay: boolean;
13
+ setMasterVolume: (v: number) => void;
14
+ setSfxVolume: (v: number) => void;
15
+ setRadioVolume: (v: number) => void;
16
+ setRadioPlaying: (v: boolean) => void;
17
+ toggleRadioPlaying: () => void;
18
+ setMuted: (muted: boolean) => void;
19
+ toggleMute: () => void;
20
+ setAutoplay: (v: boolean) => void;
21
+ }
22
+ declare const audioStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<AudioState>, "persist" | "setState"> & {
23
+ setState(partial: AudioState | Partial<AudioState> | ((state: AudioState) => AudioState | Partial<AudioState>), replace?: false | undefined): unknown;
24
+ setState(state: AudioState | ((state: AudioState) => AudioState), replace: true): unknown;
25
+ persist: {
26
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<AudioState, {
27
+ masterVolume: number;
28
+ sfxVolume: number;
29
+ radioVolume: number;
30
+ isRadioPlaying: boolean;
31
+ autoplay: boolean;
32
+ }, unknown>>) => void;
33
+ clearStorage: () => void;
34
+ rehydrate: () => Promise<void> | void;
35
+ hasHydrated: () => boolean;
36
+ onHydrate: (fn: (state: AudioState) => void) => () => void;
37
+ onFinishHydration: (fn: (state: AudioState) => void) => () => void;
38
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<AudioState, {
39
+ masterVolume: number;
40
+ sfxVolume: number;
41
+ radioVolume: number;
42
+ isRadioPlaying: boolean;
43
+ autoplay: boolean;
44
+ }, unknown>>;
45
+ };
46
+ }>;
47
+ /** Shared audio preferences; storage is read lazily and denied storage stays in memory. */
48
+ export declare const useAudioSettings: (<T = AudioState>(selector?: (state: AudioState) => T) => T) & {
49
+ (): AudioState;
50
+ <U>(selector: (state: AudioState) => U): U;
51
+ } & Omit<import("zustand").StoreApi<AudioState>, "persist" | "setState"> & {
52
+ setState(partial: AudioState | Partial<AudioState> | ((state: AudioState) => AudioState | Partial<AudioState>), replace?: false | undefined): unknown;
53
+ setState(state: AudioState | ((state: AudioState) => AudioState), replace: true): unknown;
54
+ persist: {
55
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<AudioState, {
56
+ masterVolume: number;
57
+ sfxVolume: number;
58
+ radioVolume: number;
59
+ isRadioPlaying: boolean;
60
+ autoplay: boolean;
61
+ }, unknown>>) => void;
62
+ clearStorage: () => void;
63
+ rehydrate: () => Promise<void> | void;
64
+ hasHydrated: () => boolean;
65
+ onHydrate: (fn: (state: AudioState) => void) => () => void;
66
+ onFinishHydration: (fn: (state: AudioState) => void) => () => void;
67
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<AudioState, {
68
+ masterVolume: number;
69
+ sfxVolume: number;
70
+ radioVolume: number;
71
+ isRadioPlaying: boolean;
72
+ autoplay: boolean;
73
+ }, unknown>>;
74
+ };
75
+ } & {
76
+ getState: () => AudioState;
77
+ setState: typeof audioStore.setState;
78
+ subscribe: (...args: Parameters<typeof audioStore.subscribe>) => () => void;
79
+ };
80
+ export {};
81
+ //# sourceMappingURL=audio-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-settings.d.ts","sourceRoot":"","sources":["../../src/sfx/audio-settings.ts"],"names":[],"mappings":"AAKA,oEAAoE;AACpE,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AACjE,2EAA2E;AAC3E,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAE3D,UAAU,UAAU;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,OAAO,CAAA;IACvB,oDAAoD;IACpD,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;IACjB,eAAe,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,cAAc,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,eAAe,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,kBAAkB,EAAE,MAAM,IAAI,CAAA;IAC9B,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAClC;AAgED,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;EAiDf,CAAA;AA2CD,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,IAC1B,CAAC,0BAAyB,CAAC,KAAK,EAAE,UAAU,KAAK,CAAC,KAAsC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAalF,OAAO,UAAU,CAAC,QAAQ;yBACX,UAAU,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC;CAK/D,CAAA"}
@@ -0,0 +1,166 @@
1
+ 'use client';
2
+ import { create } from 'zustand';
3
+ import { createJSONStorage, persist } from 'zustand/middleware';
4
+ /** Persisted volume and playback preferences shared across tabs. */
5
+ export const AUDIO_SETTINGS_STORAGE_KEY = 'pascal-audio-settings';
6
+ /** Per-tab session mute and the local preference inherited by new tabs. */
7
+ export const AUDIO_MUTED_STORAGE_KEY = 'pascal-audio-muted';
8
+ // Storage can be missing (a test, a server render) or refuse access (site data blocked): the page
9
+ // then keeps its choices in memory only.
10
+ function readStored(storage, key) {
11
+ try {
12
+ return globalThis[storage]?.getItem(key) ?? null;
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ function writeStored(storage, key, value) {
19
+ try {
20
+ globalThis[storage]?.setItem(key, value);
21
+ }
22
+ catch { }
23
+ }
24
+ /** This tab's mute. A tab without one takes the last choice made in any tab, and keeps it. */
25
+ function tabMuted() {
26
+ const own = readStored('sessionStorage', AUDIO_MUTED_STORAGE_KEY);
27
+ if (own !== null)
28
+ return own === 'true';
29
+ const muted = readStored('localStorage', AUDIO_MUTED_STORAGE_KEY) === 'true';
30
+ writeStored('sessionStorage', AUDIO_MUTED_STORAGE_KEY, String(muted));
31
+ return muted;
32
+ }
33
+ /** A mute chosen in this tab: this tab's, and where the next new tab starts. Open tabs keep theirs. */
34
+ function chooseMuted(muted) {
35
+ writeStored('sessionStorage', AUDIO_MUTED_STORAGE_KEY, String(muted));
36
+ writeStored('localStorage', AUDIO_MUTED_STORAGE_KEY, String(muted));
37
+ return { muted };
38
+ }
39
+ // The settings as this tab last read, wrote or took from another tab. A save carries every
40
+ // setting, so a save that changes none (a mute, which is this tab's own) is skipped.
41
+ let heldSettings = null;
42
+ // Set while this tab takes another tab's volumes: that is no choice of its own to save back.
43
+ let adopting = false;
44
+ const settingsStorage = createJSONStorage(() => ({
45
+ getItem: (key) => {
46
+ heldSettings = readStored('localStorage', key);
47
+ return heldSettings;
48
+ },
49
+ setItem: (key, value) => {
50
+ if (value === heldSettings)
51
+ return;
52
+ heldSettings = value;
53
+ if (!adopting)
54
+ writeStored('localStorage', key, value);
55
+ },
56
+ removeItem: (key) => {
57
+ try {
58
+ globalThis.localStorage?.removeItem(key);
59
+ }
60
+ catch { }
61
+ },
62
+ }));
63
+ const audioStore = create()(persist((set) => ({
64
+ masterVolume: 70,
65
+ sfxVolume: 50,
66
+ radioVolume: 25,
67
+ isRadioPlaying: false,
68
+ muted: false,
69
+ autoplay: true,
70
+ setMasterVolume: (v) => set({ masterVolume: v }),
71
+ setSfxVolume: (v) => set({ sfxVolume: v }),
72
+ setRadioVolume: (v) => set({ radioVolume: v }),
73
+ setRadioPlaying: (v) => set({ isRadioPlaying: v }),
74
+ toggleRadioPlaying: () => set((state) => ({ isRadioPlaying: !state.isRadioPlaying })),
75
+ setMuted: (muted) => set(chooseMuted(muted)),
76
+ toggleMute: () => set((state) => chooseMuted(!state.muted)),
77
+ setAutoplay: (v) => set({ autoplay: v }),
78
+ }), {
79
+ name: AUDIO_SETTINGS_STORAGE_KEY,
80
+ skipHydration: true,
81
+ onRehydrateStorage: () => {
82
+ attachStorageListener();
83
+ },
84
+ storage: settingsStorage,
85
+ partialize: ({ masterVolume, sfxVolume, radioVolume, isRadioPlaying, autoplay }) => ({
86
+ masterVolume,
87
+ sfxVolume,
88
+ radioVolume,
89
+ isRadioPlaying,
90
+ autoplay,
91
+ }),
92
+ // The settings once held one mute for every tab: whoever had muted stays muted, unless a tab
93
+ // has chosen since (a tab still on that build can write it again). The format is unchanged,
94
+ // so that build reads these settings back with only the mute gone.
95
+ merge: (persisted, current) => {
96
+ const { muted, ...settings } = (persisted ?? {});
97
+ if (typeof muted === 'boolean' &&
98
+ readStored('localStorage', AUDIO_MUTED_STORAGE_KEY) === null) {
99
+ writeStored('localStorage', AUDIO_MUTED_STORAGE_KEY, String(muted));
100
+ }
101
+ return { ...current, ...settings, muted: tabMuted() };
102
+ },
103
+ }));
104
+ /**
105
+ * Another tab saved the settings: the volumes are every tab's, so this tab takes them at once, and
106
+ * a later save of its own carries them. Its mute and whether its radio plays stay its own.
107
+ */
108
+ function adoptOtherTabSettings(event) {
109
+ if (event.key !== AUDIO_SETTINGS_STORAGE_KEY || event.newValue === null)
110
+ return;
111
+ let stored;
112
+ try {
113
+ stored = JSON.parse(event.newValue)?.state;
114
+ }
115
+ catch {
116
+ return;
117
+ }
118
+ const shared = {};
119
+ for (const key of ['masterVolume', 'sfxVolume', 'radioVolume']) {
120
+ const value = stored?.[key];
121
+ if (typeof value === 'number')
122
+ shared[key] = value;
123
+ }
124
+ if (typeof stored?.autoplay === 'boolean')
125
+ shared.autoplay = stored.autoplay;
126
+ adopting = true;
127
+ try {
128
+ audioStore.setState(shared);
129
+ }
130
+ finally {
131
+ adopting = false;
132
+ }
133
+ }
134
+ let initialized = false;
135
+ let listening = false;
136
+ function attachStorageListener() {
137
+ initialized = true;
138
+ if (listening || !globalThis.addEventListener)
139
+ return;
140
+ globalThis.addEventListener('storage', adoptOtherTabSettings);
141
+ listening = true;
142
+ }
143
+ function initializeSettings() {
144
+ if (initialized)
145
+ return;
146
+ initialized = true;
147
+ void audioStore.persist.rehydrate();
148
+ }
149
+ /** Shared audio preferences; storage is read lazily and denied storage stays in memory. */
150
+ export const useAudioSettings = Object.assign((selector = (state) => state) => {
151
+ initializeSettings();
152
+ return audioStore(selector);
153
+ }, audioStore, {
154
+ getState: () => {
155
+ initializeSettings();
156
+ return audioStore.getState();
157
+ },
158
+ setState: ((...args) => {
159
+ initializeSettings();
160
+ return audioStore.setState(...args);
161
+ }),
162
+ subscribe: (...args) => {
163
+ initializeSettings();
164
+ return audioStore.subscribe(...args);
165
+ },
166
+ });
@@ -0,0 +1,6 @@
1
+ export { AUDIO_MUTED_STORAGE_KEY, AUDIO_SETTINGS_STORAGE_KEY, useAudioSettings, } from './audio-settings';
2
+ export { INTERACTION_SOUNDS, initInteractionSounds } from './interaction-sounds';
3
+ export { disposeSounds, playSound, preloadSounds, type SoundDefinition, type SoundPlaybackOptions, startSoundLoop, stopSoundLoop, updateSoundVolumes, } from './player';
4
+ export { SOUNDS, type SoundName } from './sounds';
5
+ export { useViewerUiSounds } from './use-viewer-ui-sounds';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sfx/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAChF,OAAO,EACL,aAAa,EACb,SAAS,EACT,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,cAAc,EACd,aAAa,EACb,kBAAkB,GACnB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA"}
@@ -0,0 +1,5 @@
1
+ export { AUDIO_MUTED_STORAGE_KEY, AUDIO_SETTINGS_STORAGE_KEY, useAudioSettings, } from './audio-settings';
2
+ export { INTERACTION_SOUNDS, initInteractionSounds } from './interaction-sounds';
3
+ export { disposeSounds, playSound, preloadSounds, startSoundLoop, stopSoundLoop, updateSoundVolumes, } from './player';
4
+ export { SOUNDS } from './sounds';
5
+ export { useViewerUiSounds } from './use-viewer-ui-sounds';
@@ -0,0 +1,7 @@
1
+ import { type InteractionSound } from '@pascal-app/core';
2
+ import type { SoundName } from './sounds';
3
+ /** Core interaction cues and their playback gain; windows use softer door recordings. */
4
+ export declare const INTERACTION_SOUNDS: Record<InteractionSound, [SoundName, number]>;
5
+ /** Connect core interaction cues to playback; call the returned cleanup on unmount. */
6
+ export declare function initInteractionSounds(): () => void;
7
+ //# sourceMappingURL=interaction-sounds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction-sounds.d.ts","sourceRoot":"","sources":["../../src/sfx/interaction-sounds.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAEjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC,yFAAyF;AACzF,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,CAgB5E,CAAA;AAOD,uFAAuF;AACvF,wBAAgB,qBAAqB,IAAI,MAAM,IAAI,CASlD"}
@@ -0,0 +1,39 @@
1
+ import { emitter } from '@pascal-app/core';
2
+ import { playSound, preloadSounds } from './player';
3
+ /** Core interaction cues and their playback gain; windows use softer door recordings. */
4
+ export const INTERACTION_SOUNDS = {
5
+ 'door-open': ['doorOpen', 1],
6
+ 'door-swing': ['doorSwing', 1],
7
+ 'door-close': ['doorClose', 1],
8
+ 'window-open': ['doorOpen', 0.6],
9
+ 'window-swing': ['doorSwing', 0.6],
10
+ 'window-close': ['doorClose', 0.6],
11
+ garage: ['garageDoor', 1],
12
+ 'shutters-open': ['shuttersOpen', 1],
13
+ 'shutters-close': ['shuttersClose', 1],
14
+ // Cabinet doors and drawers: the panel recordings, as the marketing house plays its cabinets.
15
+ 'panel-open': ['shuttersOpen', 0.75],
16
+ 'panel-swing': ['doorSwing', 0.45],
17
+ 'panel-close': ['shuttersClose', 0.75],
18
+ 'switch-on': ['switchOn', 1],
19
+ 'switch-off': ['switchOff', 1],
20
+ };
21
+ let subscribers = 0;
22
+ const handleInteractionSound = (sound) => {
23
+ const [name, volumeMultiplier] = INTERACTION_SOUNDS[sound];
24
+ playSound(name, { volumeMultiplier });
25
+ };
26
+ /** Connect core interaction cues to playback; call the returned cleanup on unmount. */
27
+ export function initInteractionSounds() {
28
+ preloadSounds();
29
+ if (subscribers++ === 0)
30
+ emitter.on('interaction:sound', handleInteractionSound);
31
+ let active = true;
32
+ return () => {
33
+ if (!active)
34
+ return;
35
+ active = false;
36
+ if (--subscribers === 0)
37
+ emitter.off('interaction:sound', handleInteractionSound);
38
+ };
39
+ }