@ifc-lite/viewer 1.17.3 → 1.17.6

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 (530) hide show
  1. package/.turbo/turbo-build.log +39 -30
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +132 -0
  4. package/DESKTOP_CONTRACT_VERSION +1 -0
  5. package/dist/assets/arrow-CZ5kQ26f.js +20 -0
  6. package/dist/assets/basketViewActivator-86rgogji.js +1 -0
  7. package/dist/assets/{bcf-D5-QWGO9.js → bcf-DOG9_WPX.js} +1 -1
  8. package/dist/assets/{browser-CKs-FY1P.js → browser-C5TFR7sH.js} +1 -1
  9. package/dist/assets/cesium-ADbP7waU.css +1 -0
  10. package/dist/assets/cesium-DUOzBlqv.js +17817 -0
  11. package/dist/assets/drawing-2d-DoxKMqbO.js +257 -0
  12. package/dist/assets/{exporters-C_6J153K.js → exporters-CcPS9MK5.js} +2898 -2380
  13. package/dist/assets/geometry.worker-BFUYA08u.js +1 -0
  14. package/dist/assets/ids-DQ5jY0E8.js +1 -0
  15. package/dist/assets/ifc-lite_bg-BINvzoCP.wasm +0 -0
  16. package/dist/assets/{index-jhBr1wbn.js → index-Bfms9I4A.js} +41036 -34613
  17. package/dist/assets/index-_bfZsDCC.css +1 -0
  18. package/dist/assets/{maplibre-gl-BpvwNKKy.js → maplibre-gl-CGLcoNXc.js} +1 -1
  19. package/dist/assets/native-bridge-DUyLCMZS.js +429 -0
  20. package/dist/assets/{sandbox-B79eavQ3.js → sandbox-C8575tul.js} +4342 -4324
  21. package/dist/assets/{server-client-D3bUPJJc.js → server-client-BuZK7OST.js} +4 -4
  22. package/dist/assets/tauri-core-stub-D8Fa-u43.js +1 -0
  23. package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +1 -0
  24. package/dist/assets/tauri-fs-stub-BdeRC7aK.js +1 -0
  25. package/dist/assets/wasm-bridge-JsqEGDV8.js +1 -0
  26. package/dist/assets/{zip-B-jFFAGa.js → zip-DBEtpeu6.js} +3 -3
  27. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json +1 -0
  28. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json +1 -0
  29. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json +1 -0
  30. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json +1 -0
  31. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json +1 -0
  32. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json +1 -0
  33. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json +1 -0
  34. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json +1 -0
  35. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json +1 -0
  36. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json +1 -0
  37. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json +1 -0
  38. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json +1 -0
  39. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json +1 -0
  40. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json +1 -0
  41. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json +1 -0
  42. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json +1 -0
  43. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json +1 -0
  44. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json +1 -0
  45. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json +1 -0
  46. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json +1 -0
  47. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json +1 -0
  48. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json +1 -0
  49. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json +1 -0
  50. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json +1 -0
  51. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json +1 -0
  52. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json +1 -0
  53. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json +1 -0
  54. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json +1 -0
  55. package/dist/cesium/Assets/Images/bing_maps_credit.png +0 -0
  56. package/dist/cesium/Assets/Images/cesium_credit.png +0 -0
  57. package/dist/cesium/Assets/Images/google_earth_credit.png +0 -0
  58. package/dist/cesium/Assets/Images/ion-credit.png +0 -0
  59. package/dist/cesium/Assets/Textures/LensFlare/DirtMask.jpg +0 -0
  60. package/dist/cesium/Assets/Textures/LensFlare/StarBurst.jpg +0 -0
  61. package/dist/cesium/Assets/Textures/NaturalEarthII/0/0/0.jpg +0 -0
  62. package/dist/cesium/Assets/Textures/NaturalEarthII/0/1/0.jpg +0 -0
  63. package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/0.jpg +0 -0
  64. package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/1.jpg +0 -0
  65. package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/0.jpg +0 -0
  66. package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/1.jpg +0 -0
  67. package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/0.jpg +0 -0
  68. package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/1.jpg +0 -0
  69. package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/0.jpg +0 -0
  70. package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/1.jpg +0 -0
  71. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/0.jpg +0 -0
  72. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/1.jpg +0 -0
  73. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/2.jpg +0 -0
  74. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/3.jpg +0 -0
  75. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/0.jpg +0 -0
  76. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/1.jpg +0 -0
  77. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/2.jpg +0 -0
  78. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/3.jpg +0 -0
  79. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/0.jpg +0 -0
  80. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/1.jpg +0 -0
  81. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/2.jpg +0 -0
  82. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/3.jpg +0 -0
  83. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/0.jpg +0 -0
  84. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/1.jpg +0 -0
  85. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/2.jpg +0 -0
  86. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/3.jpg +0 -0
  87. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/0.jpg +0 -0
  88. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/1.jpg +0 -0
  89. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/2.jpg +0 -0
  90. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/3.jpg +0 -0
  91. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/0.jpg +0 -0
  92. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/1.jpg +0 -0
  93. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/2.jpg +0 -0
  94. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/3.jpg +0 -0
  95. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/0.jpg +0 -0
  96. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/1.jpg +0 -0
  97. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/2.jpg +0 -0
  98. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/3.jpg +0 -0
  99. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/0.jpg +0 -0
  100. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/1.jpg +0 -0
  101. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/2.jpg +0 -0
  102. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/3.jpg +0 -0
  103. package/dist/cesium/Assets/Textures/NaturalEarthII/tilemapresource.xml +14 -0
  104. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mx.jpg +0 -0
  105. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_my.jpg +0 -0
  106. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mz.jpg +0 -0
  107. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_px.jpg +0 -0
  108. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_py.jpg +0 -0
  109. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_pz.jpg +0 -0
  110. package/dist/cesium/Assets/Textures/maki/airfield.png +0 -0
  111. package/dist/cesium/Assets/Textures/maki/airport.png +0 -0
  112. package/dist/cesium/Assets/Textures/maki/alcohol-shop.png +0 -0
  113. package/dist/cesium/Assets/Textures/maki/america-football.png +0 -0
  114. package/dist/cesium/Assets/Textures/maki/art-gallery.png +0 -0
  115. package/dist/cesium/Assets/Textures/maki/bakery.png +0 -0
  116. package/dist/cesium/Assets/Textures/maki/bank.png +0 -0
  117. package/dist/cesium/Assets/Textures/maki/bar.png +0 -0
  118. package/dist/cesium/Assets/Textures/maki/baseball.png +0 -0
  119. package/dist/cesium/Assets/Textures/maki/basketball.png +0 -0
  120. package/dist/cesium/Assets/Textures/maki/beer.png +0 -0
  121. package/dist/cesium/Assets/Textures/maki/bicycle.png +0 -0
  122. package/dist/cesium/Assets/Textures/maki/building.png +0 -0
  123. package/dist/cesium/Assets/Textures/maki/bus.png +0 -0
  124. package/dist/cesium/Assets/Textures/maki/cafe.png +0 -0
  125. package/dist/cesium/Assets/Textures/maki/camera.png +0 -0
  126. package/dist/cesium/Assets/Textures/maki/campsite.png +0 -0
  127. package/dist/cesium/Assets/Textures/maki/car.png +0 -0
  128. package/dist/cesium/Assets/Textures/maki/cemetery.png +0 -0
  129. package/dist/cesium/Assets/Textures/maki/cesium.png +0 -0
  130. package/dist/cesium/Assets/Textures/maki/chemist.png +0 -0
  131. package/dist/cesium/Assets/Textures/maki/cinema.png +0 -0
  132. package/dist/cesium/Assets/Textures/maki/circle-stroked.png +0 -0
  133. package/dist/cesium/Assets/Textures/maki/circle.png +0 -0
  134. package/dist/cesium/Assets/Textures/maki/city.png +0 -0
  135. package/dist/cesium/Assets/Textures/maki/clothing-store.png +0 -0
  136. package/dist/cesium/Assets/Textures/maki/college.png +0 -0
  137. package/dist/cesium/Assets/Textures/maki/commercial.png +0 -0
  138. package/dist/cesium/Assets/Textures/maki/cricket.png +0 -0
  139. package/dist/cesium/Assets/Textures/maki/cross.png +0 -0
  140. package/dist/cesium/Assets/Textures/maki/dam.png +0 -0
  141. package/dist/cesium/Assets/Textures/maki/danger.png +0 -0
  142. package/dist/cesium/Assets/Textures/maki/disability.png +0 -0
  143. package/dist/cesium/Assets/Textures/maki/dog-park.png +0 -0
  144. package/dist/cesium/Assets/Textures/maki/embassy.png +0 -0
  145. package/dist/cesium/Assets/Textures/maki/emergency-telephone.png +0 -0
  146. package/dist/cesium/Assets/Textures/maki/entrance.png +0 -0
  147. package/dist/cesium/Assets/Textures/maki/farm.png +0 -0
  148. package/dist/cesium/Assets/Textures/maki/fast-food.png +0 -0
  149. package/dist/cesium/Assets/Textures/maki/ferry.png +0 -0
  150. package/dist/cesium/Assets/Textures/maki/fire-station.png +0 -0
  151. package/dist/cesium/Assets/Textures/maki/fuel.png +0 -0
  152. package/dist/cesium/Assets/Textures/maki/garden.png +0 -0
  153. package/dist/cesium/Assets/Textures/maki/gift.png +0 -0
  154. package/dist/cesium/Assets/Textures/maki/golf.png +0 -0
  155. package/dist/cesium/Assets/Textures/maki/grocery.png +0 -0
  156. package/dist/cesium/Assets/Textures/maki/hairdresser.png +0 -0
  157. package/dist/cesium/Assets/Textures/maki/harbor.png +0 -0
  158. package/dist/cesium/Assets/Textures/maki/heart.png +0 -0
  159. package/dist/cesium/Assets/Textures/maki/heliport.png +0 -0
  160. package/dist/cesium/Assets/Textures/maki/hospital.png +0 -0
  161. package/dist/cesium/Assets/Textures/maki/ice-cream.png +0 -0
  162. package/dist/cesium/Assets/Textures/maki/industrial.png +0 -0
  163. package/dist/cesium/Assets/Textures/maki/land-use.png +0 -0
  164. package/dist/cesium/Assets/Textures/maki/laundry.png +0 -0
  165. package/dist/cesium/Assets/Textures/maki/library.png +0 -0
  166. package/dist/cesium/Assets/Textures/maki/lighthouse.png +0 -0
  167. package/dist/cesium/Assets/Textures/maki/lodging.png +0 -0
  168. package/dist/cesium/Assets/Textures/maki/logging.png +0 -0
  169. package/dist/cesium/Assets/Textures/maki/london-underground.png +0 -0
  170. package/dist/cesium/Assets/Textures/maki/marker-stroked.png +0 -0
  171. package/dist/cesium/Assets/Textures/maki/marker.png +0 -0
  172. package/dist/cesium/Assets/Textures/maki/minefield.png +0 -0
  173. package/dist/cesium/Assets/Textures/maki/mobilephone.png +0 -0
  174. package/dist/cesium/Assets/Textures/maki/monument.png +0 -0
  175. package/dist/cesium/Assets/Textures/maki/museum.png +0 -0
  176. package/dist/cesium/Assets/Textures/maki/music.png +0 -0
  177. package/dist/cesium/Assets/Textures/maki/oil-well.png +0 -0
  178. package/dist/cesium/Assets/Textures/maki/park.png +0 -0
  179. package/dist/cesium/Assets/Textures/maki/park2.png +0 -0
  180. package/dist/cesium/Assets/Textures/maki/parking-garage.png +0 -0
  181. package/dist/cesium/Assets/Textures/maki/parking.png +0 -0
  182. package/dist/cesium/Assets/Textures/maki/pharmacy.png +0 -0
  183. package/dist/cesium/Assets/Textures/maki/pitch.png +0 -0
  184. package/dist/cesium/Assets/Textures/maki/place-of-worship.png +0 -0
  185. package/dist/cesium/Assets/Textures/maki/playground.png +0 -0
  186. package/dist/cesium/Assets/Textures/maki/police.png +0 -0
  187. package/dist/cesium/Assets/Textures/maki/polling-place.png +0 -0
  188. package/dist/cesium/Assets/Textures/maki/post.png +0 -0
  189. package/dist/cesium/Assets/Textures/maki/prison.png +0 -0
  190. package/dist/cesium/Assets/Textures/maki/rail-above.png +0 -0
  191. package/dist/cesium/Assets/Textures/maki/rail-light.png +0 -0
  192. package/dist/cesium/Assets/Textures/maki/rail-metro.png +0 -0
  193. package/dist/cesium/Assets/Textures/maki/rail-underground.png +0 -0
  194. package/dist/cesium/Assets/Textures/maki/rail.png +0 -0
  195. package/dist/cesium/Assets/Textures/maki/religious-christian.png +0 -0
  196. package/dist/cesium/Assets/Textures/maki/religious-jewish.png +0 -0
  197. package/dist/cesium/Assets/Textures/maki/religious-muslim.png +0 -0
  198. package/dist/cesium/Assets/Textures/maki/restaurant.png +0 -0
  199. package/dist/cesium/Assets/Textures/maki/roadblock.png +0 -0
  200. package/dist/cesium/Assets/Textures/maki/rocket.png +0 -0
  201. package/dist/cesium/Assets/Textures/maki/school.png +0 -0
  202. package/dist/cesium/Assets/Textures/maki/scooter.png +0 -0
  203. package/dist/cesium/Assets/Textures/maki/shop.png +0 -0
  204. package/dist/cesium/Assets/Textures/maki/skiing.png +0 -0
  205. package/dist/cesium/Assets/Textures/maki/slaughterhouse.png +0 -0
  206. package/dist/cesium/Assets/Textures/maki/soccer.png +0 -0
  207. package/dist/cesium/Assets/Textures/maki/square-stroked.png +0 -0
  208. package/dist/cesium/Assets/Textures/maki/square.png +0 -0
  209. package/dist/cesium/Assets/Textures/maki/star-stroked.png +0 -0
  210. package/dist/cesium/Assets/Textures/maki/star.png +0 -0
  211. package/dist/cesium/Assets/Textures/maki/suitcase.png +0 -0
  212. package/dist/cesium/Assets/Textures/maki/swimming.png +0 -0
  213. package/dist/cesium/Assets/Textures/maki/telephone.png +0 -0
  214. package/dist/cesium/Assets/Textures/maki/tennis.png +0 -0
  215. package/dist/cesium/Assets/Textures/maki/theatre.png +0 -0
  216. package/dist/cesium/Assets/Textures/maki/toilets.png +0 -0
  217. package/dist/cesium/Assets/Textures/maki/town-hall.png +0 -0
  218. package/dist/cesium/Assets/Textures/maki/town.png +0 -0
  219. package/dist/cesium/Assets/Textures/maki/triangle-stroked.png +0 -0
  220. package/dist/cesium/Assets/Textures/maki/triangle.png +0 -0
  221. package/dist/cesium/Assets/Textures/maki/village.png +0 -0
  222. package/dist/cesium/Assets/Textures/maki/warehouse.png +0 -0
  223. package/dist/cesium/Assets/Textures/maki/waste-basket.png +0 -0
  224. package/dist/cesium/Assets/Textures/maki/water.png +0 -0
  225. package/dist/cesium/Assets/Textures/maki/wetland.png +0 -0
  226. package/dist/cesium/Assets/Textures/maki/zoo.png +0 -0
  227. package/dist/cesium/Assets/Textures/moonSmall.jpg +0 -0
  228. package/dist/cesium/Assets/Textures/pin.svg +1 -0
  229. package/dist/cesium/Assets/Textures/waterNormals.jpg +0 -0
  230. package/dist/cesium/Assets/Textures/waterNormalsSmall.jpg +0 -0
  231. package/dist/cesium/Assets/approximateTerrainHeights.json +1 -0
  232. package/dist/cesium/ThirdParty/Workers/package.json +1 -0
  233. package/dist/cesium/ThirdParty/Workers/zip-web-worker.js +1 -0
  234. package/dist/cesium/ThirdParty/basis_transcoder.wasm +0 -0
  235. package/dist/cesium/ThirdParty/draco_decoder.wasm +0 -0
  236. package/dist/cesium/ThirdParty/google-earth-dbroot-parser.js +1 -0
  237. package/dist/cesium/ThirdParty/wasm_splats_bg.wasm +0 -0
  238. package/dist/cesium/ThirdParty/zip-module.wasm +0 -0
  239. package/dist/cesium/Widgets/Animation/Animation.css +127 -0
  240. package/dist/cesium/Widgets/Animation/lighter.css +70 -0
  241. package/dist/cesium/Widgets/BaseLayerPicker/BaseLayerPicker.css +108 -0
  242. package/dist/cesium/Widgets/BaseLayerPicker/lighter.css +22 -0
  243. package/dist/cesium/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css +102 -0
  244. package/dist/cesium/Widgets/CesiumInspector/CesiumInspector.css +113 -0
  245. package/dist/cesium/Widgets/CesiumWidget/CesiumWidget.css +119 -0
  246. package/dist/cesium/Widgets/CesiumWidget/lighter.css +14 -0
  247. package/dist/cesium/Widgets/FullscreenButton/FullscreenButton.css +8 -0
  248. package/dist/cesium/Widgets/Geocoder/Geocoder.css +70 -0
  249. package/dist/cesium/Widgets/Geocoder/lighter.css +17 -0
  250. package/dist/cesium/Widgets/I3SBuildingSceneLayerExplorer/I3SBuildingSceneLayerExplorer.css +27 -0
  251. package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldHillshade.png +0 -0
  252. package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldImagery.png +0 -0
  253. package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldOcean.png +0 -0
  254. package/dist/cesium/Widgets/Images/ImageryProviders/azureAerial.png +0 -0
  255. package/dist/cesium/Widgets/Images/ImageryProviders/azureRoads.png +0 -0
  256. package/dist/cesium/Widgets/Images/ImageryProviders/bingAerial.png +0 -0
  257. package/dist/cesium/Widgets/Images/ImageryProviders/bingAerialLabels.png +0 -0
  258. package/dist/cesium/Widgets/Images/ImageryProviders/bingRoads.png +0 -0
  259. package/dist/cesium/Widgets/Images/ImageryProviders/blueMarble.png +0 -0
  260. package/dist/cesium/Widgets/Images/ImageryProviders/earthAtNight.png +0 -0
  261. package/dist/cesium/Widgets/Images/ImageryProviders/googleContour.png +0 -0
  262. package/dist/cesium/Widgets/Images/ImageryProviders/googleRoadmap.png +0 -0
  263. package/dist/cesium/Widgets/Images/ImageryProviders/googleSatellite.png +0 -0
  264. package/dist/cesium/Widgets/Images/ImageryProviders/googleSatelliteLabels.png +0 -0
  265. package/dist/cesium/Widgets/Images/ImageryProviders/mapQuestOpenStreetMap.png +0 -0
  266. package/dist/cesium/Widgets/Images/ImageryProviders/mapboxSatellite.png +0 -0
  267. package/dist/cesium/Widgets/Images/ImageryProviders/mapboxStreets.png +0 -0
  268. package/dist/cesium/Widgets/Images/ImageryProviders/mapboxTerrain.png +0 -0
  269. package/dist/cesium/Widgets/Images/ImageryProviders/naturalEarthII.png +0 -0
  270. package/dist/cesium/Widgets/Images/ImageryProviders/openStreetMap.png +0 -0
  271. package/dist/cesium/Widgets/Images/ImageryProviders/sentinel-2.png +0 -0
  272. package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmooth.png +0 -0
  273. package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmoothDark.png +0 -0
  274. package/dist/cesium/Widgets/Images/ImageryProviders/stamenToner.png +0 -0
  275. package/dist/cesium/Widgets/Images/ImageryProviders/stamenWatercolor.png +0 -0
  276. package/dist/cesium/Widgets/Images/NavigationHelp/Mouse.svg +84 -0
  277. package/dist/cesium/Widgets/Images/NavigationHelp/MouseLeft.svg +76 -0
  278. package/dist/cesium/Widgets/Images/NavigationHelp/MouseMiddle.svg +76 -0
  279. package/dist/cesium/Widgets/Images/NavigationHelp/MouseRight.svg +76 -0
  280. package/dist/cesium/Widgets/Images/NavigationHelp/Touch.svg +120 -0
  281. package/dist/cesium/Widgets/Images/NavigationHelp/TouchDrag.svg +129 -0
  282. package/dist/cesium/Widgets/Images/NavigationHelp/TouchRotate.svg +76 -0
  283. package/dist/cesium/Widgets/Images/NavigationHelp/TouchTilt.svg +135 -0
  284. package/dist/cesium/Widgets/Images/NavigationHelp/TouchZoom.svg +74 -0
  285. package/dist/cesium/Widgets/Images/TerrainProviders/CesiumWorldTerrain.png +0 -0
  286. package/dist/cesium/Widgets/Images/TerrainProviders/Ellipsoid.png +0 -0
  287. package/dist/cesium/Widgets/Images/TimelineIcons.png +0 -0
  288. package/dist/cesium/Widgets/Images/info-loading.gif +0 -0
  289. package/dist/cesium/Widgets/InfoBox/InfoBox.css +92 -0
  290. package/dist/cesium/Widgets/InfoBox/InfoBoxDescription.css +178 -0
  291. package/dist/cesium/Widgets/NavigationHelpButton/NavigationHelpButton.css +93 -0
  292. package/dist/cesium/Widgets/NavigationHelpButton/lighter.css +38 -0
  293. package/dist/cesium/Widgets/PerformanceWatchdog/PerformanceWatchdog.css +15 -0
  294. package/dist/cesium/Widgets/ProjectionPicker/ProjectionPicker.css +38 -0
  295. package/dist/cesium/Widgets/SceneModePicker/SceneModePicker.css +56 -0
  296. package/dist/cesium/Widgets/SelectionIndicator/SelectionIndicator.css +20 -0
  297. package/dist/cesium/Widgets/Timeline/Timeline.css +103 -0
  298. package/dist/cesium/Widgets/Timeline/lighter.css +23 -0
  299. package/dist/cesium/Widgets/VRButton/VRButton.css +8 -0
  300. package/dist/cesium/Widgets/Viewer/Viewer.css +107 -0
  301. package/dist/cesium/Widgets/VoxelInspector/VoxelInspector.css +16 -0
  302. package/dist/cesium/Widgets/lighter.css +237 -0
  303. package/dist/cesium/Widgets/lighterShared.css +46 -0
  304. package/dist/cesium/Widgets/shared.css +103 -0
  305. package/dist/cesium/Widgets/widgets.css +1342 -0
  306. package/dist/cesium/Workers/chunk-23ZQ2IVV.js +29 -0
  307. package/dist/cesium/Workers/chunk-2EQO3Q56.js +26 -0
  308. package/dist/cesium/Workers/chunk-2MJIIVP4.js +26 -0
  309. package/dist/cesium/Workers/chunk-2TE5NTVD.js +26 -0
  310. package/dist/cesium/Workers/chunk-2ZBHLJST.js +26 -0
  311. package/dist/cesium/Workers/chunk-5TJMAQVL.js +26 -0
  312. package/dist/cesium/Workers/chunk-6BD4U3VO.js +26 -0
  313. package/dist/cesium/Workers/chunk-7TVGLKQF.js +26 -0
  314. package/dist/cesium/Workers/chunk-BTSYJ5XU.js +26 -0
  315. package/dist/cesium/Workers/chunk-BXMEEOCS.js +63 -0
  316. package/dist/cesium/Workers/chunk-BYLCY7GP.js +29 -0
  317. package/dist/cesium/Workers/chunk-CTHM3W6I.js +26 -0
  318. package/dist/cesium/Workers/chunk-CUUSNIVQ.js +26 -0
  319. package/dist/cesium/Workers/chunk-E3JOOS3S.js +26 -0
  320. package/dist/cesium/Workers/chunk-E7KYDCM5.js +26 -0
  321. package/dist/cesium/Workers/chunk-EDVBB7SS.js +27 -0
  322. package/dist/cesium/Workers/chunk-EFBN7QNX.js +26 -0
  323. package/dist/cesium/Workers/chunk-EQ4YRVWL.js +26 -0
  324. package/dist/cesium/Workers/chunk-F6PRE7D6.js +26 -0
  325. package/dist/cesium/Workers/chunk-FC4ZZ65J.js +26 -0
  326. package/dist/cesium/Workers/chunk-FFBVWF2L.js +26 -0
  327. package/dist/cesium/Workers/chunk-GBAA6GVX.js +26 -0
  328. package/dist/cesium/Workers/chunk-ICALLYLG.js +26 -0
  329. package/dist/cesium/Workers/chunk-ILRYTWTP.js +26 -0
  330. package/dist/cesium/Workers/chunk-IRNLBSEJ.js +26 -0
  331. package/dist/cesium/Workers/chunk-IX4VMHEV.js +26 -0
  332. package/dist/cesium/Workers/chunk-L6QHHACZ.js +26 -0
  333. package/dist/cesium/Workers/chunk-LI2ZSORM.js +26 -0
  334. package/dist/cesium/Workers/chunk-LSLE2RL4.js +26 -0
  335. package/dist/cesium/Workers/chunk-M4HLDBCG.js +26 -0
  336. package/dist/cesium/Workers/chunk-MJHHSGEH.js +26 -0
  337. package/dist/cesium/Workers/chunk-NMVKML6W.js +26 -0
  338. package/dist/cesium/Workers/chunk-OCWJRAXS.js +26 -0
  339. package/dist/cesium/Workers/chunk-OIRKANTH.js +26 -0
  340. package/dist/cesium/Workers/chunk-OIT7J4IC.js +26 -0
  341. package/dist/cesium/Workers/chunk-OLZ3FYUM.js +26 -0
  342. package/dist/cesium/Workers/chunk-Q5BPHJQF.js +26 -0
  343. package/dist/cesium/Workers/chunk-QFM5DCMQ.js +26 -0
  344. package/dist/cesium/Workers/chunk-QKUIYMGC.js +28 -0
  345. package/dist/cesium/Workers/chunk-S44JILQT.js +26 -0
  346. package/dist/cesium/Workers/chunk-SLT4J352.js +26 -0
  347. package/dist/cesium/Workers/chunk-SQMIIXB7.js +26 -0
  348. package/dist/cesium/Workers/chunk-TJ4XLGBQ.js +26 -0
  349. package/dist/cesium/Workers/chunk-TNSUQXWK.js +27 -0
  350. package/dist/cesium/Workers/chunk-UBOGZS7F.js +26 -0
  351. package/dist/cesium/Workers/chunk-V3OSTMM6.js +26 -0
  352. package/dist/cesium/Workers/chunk-V7QEYVP3.js +26 -0
  353. package/dist/cesium/Workers/chunk-VUKYSU4H.js +26 -0
  354. package/dist/cesium/Workers/chunk-W37FE5GR.js +26 -0
  355. package/dist/cesium/Workers/chunk-WBOV35NL.js +26 -0
  356. package/dist/cesium/Workers/chunk-WPMZLB3Y.js +26 -0
  357. package/dist/cesium/Workers/chunk-WWWZVEEH.js +26 -0
  358. package/dist/cesium/Workers/chunk-XFIQ5DEQ.js +28 -0
  359. package/dist/cesium/Workers/chunk-XQHLGIO7.js +26 -0
  360. package/dist/cesium/Workers/chunk-XUSCFAVF.js +26 -0
  361. package/dist/cesium/Workers/chunk-YP7I5QBZ.js +26 -0
  362. package/dist/cesium/Workers/chunk-Z3QF2EHT.js +26 -0
  363. package/dist/cesium/Workers/combineGeometry.js +26 -0
  364. package/dist/cesium/Workers/createBoxGeometry.js +26 -0
  365. package/dist/cesium/Workers/createBoxOutlineGeometry.js +26 -0
  366. package/dist/cesium/Workers/createCircleGeometry.js +26 -0
  367. package/dist/cesium/Workers/createCircleOutlineGeometry.js +26 -0
  368. package/dist/cesium/Workers/createCoplanarPolygonGeometry.js +26 -0
  369. package/dist/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
  370. package/dist/cesium/Workers/createCorridorGeometry.js +26 -0
  371. package/dist/cesium/Workers/createCorridorOutlineGeometry.js +26 -0
  372. package/dist/cesium/Workers/createCylinderGeometry.js +26 -0
  373. package/dist/cesium/Workers/createCylinderOutlineGeometry.js +26 -0
  374. package/dist/cesium/Workers/createEllipseGeometry.js +26 -0
  375. package/dist/cesium/Workers/createEllipseOutlineGeometry.js +26 -0
  376. package/dist/cesium/Workers/createEllipsoidGeometry.js +26 -0
  377. package/dist/cesium/Workers/createEllipsoidOutlineGeometry.js +26 -0
  378. package/dist/cesium/Workers/createFrustumGeometry.js +26 -0
  379. package/dist/cesium/Workers/createFrustumOutlineGeometry.js +26 -0
  380. package/dist/cesium/Workers/createGeometry.js +26 -0
  381. package/dist/cesium/Workers/createGroundPolylineGeometry.js +26 -0
  382. package/dist/cesium/Workers/createPlaneGeometry.js +26 -0
  383. package/dist/cesium/Workers/createPlaneOutlineGeometry.js +26 -0
  384. package/dist/cesium/Workers/createPolygonGeometry.js +26 -0
  385. package/dist/cesium/Workers/createPolygonOutlineGeometry.js +26 -0
  386. package/dist/cesium/Workers/createPolylineGeometry.js +26 -0
  387. package/dist/cesium/Workers/createPolylineVolumeGeometry.js +26 -0
  388. package/dist/cesium/Workers/createPolylineVolumeOutlineGeometry.js +26 -0
  389. package/dist/cesium/Workers/createRectangleGeometry.js +26 -0
  390. package/dist/cesium/Workers/createRectangleOutlineGeometry.js +26 -0
  391. package/dist/cesium/Workers/createSimplePolylineGeometry.js +26 -0
  392. package/dist/cesium/Workers/createSphereGeometry.js +26 -0
  393. package/dist/cesium/Workers/createSphereOutlineGeometry.js +26 -0
  394. package/dist/cesium/Workers/createTaskProcessorWorker.js +26 -0
  395. package/dist/cesium/Workers/createVectorTileClampedPolylines.js +26 -0
  396. package/dist/cesium/Workers/createVectorTileGeometries.js +26 -0
  397. package/dist/cesium/Workers/createVectorTilePoints.js +26 -0
  398. package/dist/cesium/Workers/createVectorTilePolygons.js +26 -0
  399. package/dist/cesium/Workers/createVectorTilePolylines.js +26 -0
  400. package/dist/cesium/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -0
  401. package/dist/cesium/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +26 -0
  402. package/dist/cesium/Workers/createVerticesFromHeightmap.js +26 -0
  403. package/dist/cesium/Workers/createVerticesFromQuantizedTerrainMesh.js +26 -0
  404. package/dist/cesium/Workers/createWallGeometry.js +26 -0
  405. package/dist/cesium/Workers/createWallOutlineGeometry.js +26 -0
  406. package/dist/cesium/Workers/decodeDraco.js +26 -0
  407. package/dist/cesium/Workers/decodeGoogleEarthEnterprisePacket.js +26 -0
  408. package/dist/cesium/Workers/decodeI3S.js +26 -0
  409. package/dist/cesium/Workers/gaussianSplatSorter.js +26 -0
  410. package/dist/cesium/Workers/gaussianSplatTextureGenerator.js +26 -0
  411. package/dist/cesium/Workers/incrementallyBuildTerrainPicker.js +26 -0
  412. package/dist/cesium/Workers/transcodeKTX2.js +56 -0
  413. package/dist/cesium/Workers/transferTypedArrayTest.js +26 -0
  414. package/dist/cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
  415. package/dist/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -0
  416. package/dist/index.html +13 -10
  417. package/index.html +1 -0
  418. package/package.json +18 -15
  419. package/src/App.tsx +7 -9
  420. package/src/components/viewer/BCFPanel.tsx +46 -4
  421. package/src/components/viewer/CesiumOverlay.tsx +715 -0
  422. package/src/components/viewer/CesiumSettingsDialog.tsx +100 -0
  423. package/src/components/viewer/ChatPanel.tsx +232 -90
  424. package/src/components/viewer/CommandPalette.tsx +6 -1
  425. package/src/components/viewer/DesktopEntitlementBanner.tsx +74 -0
  426. package/src/components/viewer/ExportChangesButton.tsx +6 -1
  427. package/src/components/viewer/ExportDialog.tsx +22 -6
  428. package/src/components/viewer/HierarchyPanel.tsx +196 -0
  429. package/src/components/viewer/IDSPanel.tsx +52 -3
  430. package/src/components/viewer/KeyboardShortcutsDialog.tsx +1 -1
  431. package/src/components/viewer/MainToolbar.tsx +355 -28
  432. package/src/components/viewer/PropertiesPanel.tsx +234 -81
  433. package/src/components/viewer/ScriptPanel.tsx +34 -8
  434. package/src/components/viewer/SettingsPage.tsx +581 -0
  435. package/src/components/viewer/StatusBar.tsx +17 -1
  436. package/src/components/viewer/ThemeSwitch.tsx +63 -7
  437. package/src/components/viewer/ViewerLayout.tsx +48 -6
  438. package/src/components/viewer/Viewport.tsx +61 -8
  439. package/src/components/viewer/ViewportContainer.tsx +265 -28
  440. package/src/components/viewer/ViewportOverlays.tsx +132 -27
  441. package/src/components/viewer/bcf/BCFTopicDetail.tsx +4 -4
  442. package/src/components/viewer/chat/ModelSelector.tsx +90 -54
  443. package/src/components/viewer/properties/GeoreferencingPanel.tsx +229 -55
  444. package/src/components/viewer/properties/LocationMap.tsx +462 -19
  445. package/src/components/viewer/properties/ModelMetadataPanel.tsx +1 -1
  446. package/src/components/viewer/selectionHandlers.ts +4 -3
  447. package/src/components/viewer/tools/SectionCapControls.tsx +237 -0
  448. package/src/components/viewer/tools/SectionPanel.tsx +39 -18
  449. package/src/components/viewer/useAnimationLoop.ts +13 -1
  450. package/src/components/viewer/useGeometryStreaming.ts +127 -40
  451. package/src/components/viewer/useMouseControls.ts +4 -1
  452. package/src/components/viewer/useRenderUpdates.ts +1 -1
  453. package/src/hooks/ids/idsDataAccessor.ts +60 -24
  454. package/src/hooks/ingest/viewerModelIngest.ts +280 -0
  455. package/src/hooks/useIDS.ts +1 -1
  456. package/src/hooks/useIfc.ts +7 -1
  457. package/src/hooks/useIfcCache.ts +28 -15
  458. package/src/hooks/useIfcFederation.ts +378 -291
  459. package/src/hooks/useIfcLoader.ts +1657 -130
  460. package/src/hooks/useIfcServer.ts +0 -69
  461. package/src/hooks/useViewControls.ts +13 -5
  462. package/src/index.css +484 -10
  463. package/src/lib/desktop/desktopEntitlementEvents.ts +39 -0
  464. package/src/lib/desktop-entitlement.ts +43 -0
  465. package/src/lib/desktop-product.ts +124 -0
  466. package/src/lib/geo/cesium-bridge.ts +318 -0
  467. package/src/lib/geo/effective-georef.test.ts +73 -0
  468. package/src/lib/geo/effective-georef.ts +111 -0
  469. package/src/lib/geo/reproject.ts +249 -37
  470. package/src/lib/llm/byok-guard.test.ts +77 -0
  471. package/src/lib/llm/byok-guard.ts +39 -0
  472. package/src/lib/llm/free-models.test.ts +0 -6
  473. package/src/lib/llm/models.ts +104 -42
  474. package/src/lib/llm/stream-client.ts +74 -110
  475. package/src/lib/llm/stream-direct.test.ts +130 -0
  476. package/src/lib/llm/stream-direct.ts +316 -0
  477. package/src/lib/llm/types.ts +14 -2
  478. package/src/lib/recent-files.ts +2 -1
  479. package/src/main.tsx +1 -10
  480. package/src/services/analysis-extensions.ts +125 -0
  481. package/src/services/api-keys.ts +73 -0
  482. package/src/services/app-navigation.ts +13 -0
  483. package/src/services/bsdd.ts +53 -4
  484. package/src/services/cacheService.ts +1 -1
  485. package/src/services/desktop-cache.ts +43 -0
  486. package/src/services/desktop-export.ts +77 -0
  487. package/src/services/desktop-harness.ts +196 -0
  488. package/src/services/desktop-logger.ts +20 -0
  489. package/src/services/desktop-native-metadata.ts +207 -0
  490. package/src/services/desktop-panel-actions.ts +43 -0
  491. package/src/services/desktop-preferences.ts +44 -0
  492. package/src/services/file-dialog.ts +147 -0
  493. package/src/services/tauri-core-stub.ts +7 -0
  494. package/src/services/tauri-dialog-stub.ts +7 -0
  495. package/src/services/tauri-fs-stub.ts +7 -0
  496. package/src/store/constants.ts +20 -2
  497. package/src/store/index.ts +52 -7
  498. package/src/store/slices/cesiumSlice.ts +127 -0
  499. package/src/store/slices/chatSlice.test.ts +6 -76
  500. package/src/store/slices/chatSlice.ts +21 -58
  501. package/src/store/slices/dataSlice.ts +139 -28
  502. package/src/store/slices/desktopEntitlementSlice.ts +86 -0
  503. package/src/store/slices/loadingSlice.ts +14 -2
  504. package/src/store/slices/modelSlice.ts +58 -3
  505. package/src/store/slices/sectionSlice.test.ts +87 -7
  506. package/src/store/slices/sectionSlice.ts +151 -5
  507. package/src/store/slices/uiSlice.ts +28 -5
  508. package/src/store/types.ts +122 -2
  509. package/src/store.ts +1 -1
  510. package/src/utils/desktopModelSnapshot.ts +358 -0
  511. package/src/utils/ifcConfig.ts +6 -1
  512. package/src/utils/nativeSpatialDataStore.ts +253 -0
  513. package/src/utils/serverDataModel.ts +4 -0
  514. package/src/utils/spatialHierarchy.ts +10 -11
  515. package/src/utils/viewportUtils.ts +7 -2
  516. package/src/vite-env.d.ts +0 -4
  517. package/vite.config.ts +24 -0
  518. package/dist/assets/arrow-DJf2ErbF.js +0 -20
  519. package/dist/assets/basketViewActivator-aojwdomq.js +0 -1
  520. package/dist/assets/desktop-cache-oPzaWXYE.js +0 -1
  521. package/dist/assets/drawing-2d-gWfpdfYe.js +0 -257
  522. package/dist/assets/geometry.worker-Nz9_YIqh.js +0 -1
  523. package/dist/assets/ids-B4jTqB1O.js +0 -1
  524. package/dist/assets/ifc-lite_bg-eSkBTizQ.wasm +0 -0
  525. package/dist/assets/index-pbE7itQS.css +0 -1
  526. package/dist/assets/native-bridge-DSIyEYXG.js +0 -113
  527. package/dist/assets/wasm-bridge-B0J07fZZ.js +0 -1
  528. package/src/components/viewer/UpgradePage.tsx +0 -69
  529. package/src/lib/llm/ClerkChatSync.tsx +0 -74
  530. package/src/lib/llm/clerk-auth.ts +0 -62
@@ -0,0 +1,237 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+
5
+ /**
6
+ * Cap-surface appearance controls shown inside the expanded Section panel.
7
+ *
8
+ * Layout principle (tight compact panel, ≤ 260 px wide):
9
+ * [Display] Surfaces ⬛ Lines ⬛
10
+ * ───────────────────────────────────────────────
11
+ * [Hatch] <pattern select>
12
+ * [Colours] Fill ▣ Hatch ▣
13
+ * [Shape] Spacing __px Angle __° Width __px
14
+ *
15
+ * Surfaces and Lines toggle independently so users can get a clean
16
+ * "architectural drawing" look (outlines only), a pure hatched fill, or
17
+ * the combination. All style inputs are hidden when Surfaces is off.
18
+ */
19
+
20
+ import { useCallback, useId } from 'react';
21
+ import { useViewerStore } from '@/store';
22
+ import type { SectionCapHatchId } from '@/store/types';
23
+
24
+ const PATTERN_LABELS: Record<SectionCapHatchId, string> = {
25
+ solid: 'Solid fill',
26
+ diagonal: 'Diagonal',
27
+ crossHatch: 'Cross-hatch',
28
+ horizontal: 'Horizontal',
29
+ vertical: 'Vertical',
30
+ concrete: 'Concrete',
31
+ brick: 'Brick',
32
+ insulation: 'Insulation',
33
+ };
34
+
35
+ const PATTERN_IDS: SectionCapHatchId[] = [
36
+ 'diagonal', 'crossHatch', 'horizontal', 'vertical',
37
+ 'concrete', 'brick', 'insulation', 'solid',
38
+ ];
39
+
40
+ function rgbaToHex(c: [number, number, number, number]): string {
41
+ const to2 = (v: number) => Math.round(Math.max(0, Math.min(1, v)) * 255).toString(16).padStart(2, '0');
42
+ return `#${to2(c[0])}${to2(c[1])}${to2(c[2])}`;
43
+ }
44
+
45
+ function hexToRgba(hex: string, alpha: number): [number, number, number, number] {
46
+ const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
47
+ if (!m) return [1, 1, 1, alpha];
48
+ return [
49
+ parseInt(m[1], 16) / 255,
50
+ parseInt(m[2], 16) / 255,
51
+ parseInt(m[3], 16) / 255,
52
+ alpha,
53
+ ];
54
+ }
55
+
56
+ interface DisplayToggleProps {
57
+ active: boolean;
58
+ label: string;
59
+ onToggle: () => void;
60
+ }
61
+
62
+ function DisplayToggle({ active, label, onToggle }: DisplayToggleProps): React.JSX.Element {
63
+ return (
64
+ <button
65
+ type="button"
66
+ onClick={onToggle}
67
+ aria-pressed={active}
68
+ className={`flex items-center justify-center gap-1.5 px-2 py-1 text-[10px] font-mono uppercase tracking-wide border rounded transition-colors ${
69
+ active
70
+ ? 'bg-primary text-primary-foreground border-primary'
71
+ : 'bg-muted text-muted-foreground border-muted hover:border-foreground/20'
72
+ }`}
73
+ title={`${active ? 'Hide' : 'Show'} ${label.toLowerCase()}`}
74
+ >
75
+ <span
76
+ aria-hidden
77
+ className={`inline-block h-1.5 w-1.5 rounded-full ${active ? 'bg-primary-foreground' : 'bg-muted-foreground'}`}
78
+ />
79
+ {label}
80
+ </button>
81
+ );
82
+ }
83
+
84
+ export function SectionCapControls(): React.JSX.Element {
85
+ const sectionPlane = useViewerStore((s) => s.sectionPlane);
86
+ const setShowCap = useViewerStore((s) => s.setSectionShowCap);
87
+ const setShowOutlines = useViewerStore((s) => s.setSectionShowOutlines);
88
+ const setCapStyle = useViewerStore((s) => s.setSectionCapStyle);
89
+
90
+ const onPattern = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
91
+ setCapStyle({ pattern: e.target.value as SectionCapHatchId });
92
+ }, [setCapStyle]);
93
+
94
+ const onFillColor = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
95
+ setCapStyle({ fillColor: hexToRgba(e.target.value, sectionPlane.capStyle.fillColor[3]) });
96
+ }, [setCapStyle, sectionPlane.capStyle.fillColor]);
97
+
98
+ const onStrokeColor = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
99
+ setCapStyle({ strokeColor: hexToRgba(e.target.value, sectionPlane.capStyle.strokeColor[3]) });
100
+ }, [setCapStyle, sectionPlane.capStyle.strokeColor]);
101
+
102
+ const onSpacing = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
103
+ const v = Number(e.target.value);
104
+ if (Number.isFinite(v)) setCapStyle({ spacingPx: Math.max(2, v) });
105
+ }, [setCapStyle]);
106
+
107
+ const onAngle = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
108
+ const deg = Number(e.target.value);
109
+ if (Number.isFinite(deg)) setCapStyle({ angleRad: (deg * Math.PI) / 180 });
110
+ }, [setCapStyle]);
111
+
112
+ const onWidth = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
113
+ const v = Number(e.target.value);
114
+ if (Number.isFinite(v)) setCapStyle({ widthPx: Math.max(1, v) });
115
+ }, [setCapStyle]);
116
+
117
+ const onToggleCap = useCallback(() => setShowCap(!sectionPlane.showCap), [setShowCap, sectionPlane.showCap]);
118
+ const onToggleOutlines = useCallback(() => setShowOutlines(!sectionPlane.showOutlines), [setShowOutlines, sectionPlane.showOutlines]);
119
+
120
+ const angleDeg = Math.round((sectionPlane.capStyle.angleRad * 180) / Math.PI);
121
+
122
+ // Stable ids for label/control association. Multiple instances of the
123
+ // panel (rare, but possible during HMR) each get their own id namespace.
124
+ const baseId = useId();
125
+ const patternId = `${baseId}-pattern`;
126
+ const fillId = `${baseId}-fill`;
127
+ const strokeId = `${baseId}-stroke`;
128
+ const spacingId = `${baseId}-spacing`;
129
+ const angleId = `${baseId}-angle`;
130
+ const widthId = `${baseId}-width`;
131
+
132
+ const hatchInputsDisabled = !sectionPlane.showCap;
133
+
134
+ return (
135
+ <div className="mt-3 border-t pt-3 space-y-3">
136
+ {/* Display toggles — surfaces and lines independently. */}
137
+ <div>
138
+ <div className="text-[10px] uppercase tracking-wider text-muted-foreground mb-1.5">Display</div>
139
+ <div className="grid grid-cols-2 gap-2">
140
+ <DisplayToggle active={sectionPlane.showCap} label="Surfaces" onToggle={onToggleCap} />
141
+ <DisplayToggle active={sectionPlane.showOutlines} label="Lines" onToggle={onToggleOutlines} />
142
+ </div>
143
+ </div>
144
+
145
+ {/* Hatch style — disabled visually when surfaces are off. */}
146
+ <fieldset
147
+ disabled={hatchInputsDisabled}
148
+ className={`space-y-2 ${hatchInputsDisabled ? 'opacity-50 pointer-events-none' : ''}`}
149
+ >
150
+ <div>
151
+ <label htmlFor={patternId} className="text-[10px] uppercase tracking-wider text-muted-foreground block mb-1">
152
+ Hatch pattern
153
+ </label>
154
+ <select
155
+ id={patternId}
156
+ value={sectionPlane.capStyle.pattern}
157
+ onChange={onPattern}
158
+ className="w-full text-xs bg-muted px-2 py-1.5 rounded border-none"
159
+ >
160
+ {PATTERN_IDS.map((id) => (
161
+ <option key={id} value={id}>{PATTERN_LABELS[id]}</option>
162
+ ))}
163
+ </select>
164
+ </div>
165
+
166
+ <div className="grid grid-cols-2 gap-2">
167
+ <label htmlFor={fillId} className="flex items-center gap-2 text-[10px] uppercase tracking-wider text-muted-foreground">
168
+ <input
169
+ id={fillId}
170
+ type="color"
171
+ value={rgbaToHex(sectionPlane.capStyle.fillColor)}
172
+ onChange={onFillColor}
173
+ className="h-5 w-5 rounded cursor-pointer border border-muted"
174
+ aria-label="Fill colour"
175
+ />
176
+ Fill
177
+ </label>
178
+ <label htmlFor={strokeId} className="flex items-center gap-2 text-[10px] uppercase tracking-wider text-muted-foreground">
179
+ <input
180
+ id={strokeId}
181
+ type="color"
182
+ value={rgbaToHex(sectionPlane.capStyle.strokeColor)}
183
+ onChange={onStrokeColor}
184
+ className="h-5 w-5 rounded cursor-pointer border border-muted"
185
+ aria-label="Hatch colour"
186
+ />
187
+ Hatch
188
+ </label>
189
+ </div>
190
+
191
+ <div className="grid grid-cols-3 gap-2">
192
+ <div>
193
+ <label htmlFor={spacingId} className="text-[10px] text-muted-foreground block mb-1">Spacing (px)</label>
194
+ <input
195
+ id={spacingId}
196
+ type="number"
197
+ min="2"
198
+ max="64"
199
+ step="1"
200
+ value={sectionPlane.capStyle.spacingPx}
201
+ onChange={onSpacing}
202
+ className="w-full text-xs bg-muted px-1.5 py-0.5 rounded border-none text-right"
203
+ />
204
+ </div>
205
+ <div>
206
+ <label htmlFor={angleId} className="text-[10px] text-muted-foreground block mb-1">Angle (°)</label>
207
+ <input
208
+ id={angleId}
209
+ type="number"
210
+ min="-180"
211
+ max="180"
212
+ step="5"
213
+ value={angleDeg}
214
+ onChange={onAngle}
215
+ className="w-full text-xs bg-muted px-1.5 py-0.5 rounded border-none text-right"
216
+ />
217
+ </div>
218
+ <div>
219
+ <label htmlFor={widthId} className="text-[10px] text-muted-foreground block mb-1">Width (px)</label>
220
+ <input
221
+ id={widthId}
222
+ type="number"
223
+ min="1"
224
+ max="16"
225
+ step="0.5"
226
+ value={sectionPlane.capStyle.widthPx}
227
+ onChange={onWidth}
228
+ className="w-full text-xs bg-muted px-1.5 py-0.5 rounded border-none text-right"
229
+ />
230
+ </div>
231
+ </div>
232
+ </fieldset>
233
+ </div>
234
+ );
235
+ }
236
+
237
+ export default SectionCapControls;
@@ -7,17 +7,19 @@
7
7
  */
8
8
 
9
9
  import React, { useCallback, useState } from 'react';
10
- import { X, Slice, ChevronDown, FileImage } from 'lucide-react';
10
+ import { X, Slice, ChevronDown, FileImage, FlipHorizontal2 } from 'lucide-react';
11
11
  import { Button } from '@/components/ui/button';
12
12
  import { useViewerStore } from '@/store';
13
13
  import { AXIS_INFO } from './sectionConstants';
14
14
  import { SectionPlaneVisualization } from './SectionVisualization';
15
+ import { SectionCapControls } from './SectionCapControls';
15
16
 
16
17
  export function SectionOverlay() {
17
18
  const sectionPlane = useViewerStore((s) => s.sectionPlane);
18
19
  const setSectionPlaneAxis = useViewerStore((s) => s.setSectionPlaneAxis);
19
20
  const setSectionPlanePosition = useViewerStore((s) => s.setSectionPlanePosition);
20
21
  const toggleSectionPlane = useViewerStore((s) => s.toggleSectionPlane);
22
+ const flipSectionPlane = useViewerStore((s) => s.flipSectionPlane);
21
23
  const setActiveTool = useViewerStore((s) => s.setActiveTool);
22
24
  const setDrawingPanelVisible = useViewerStore((s) => s.setDrawing2DPanelVisible);
23
25
  const drawingPanelVisible = useViewerStore((s) => s.drawing2DPanelVisible);
@@ -83,10 +85,10 @@ export function SectionOverlay() {
83
85
 
84
86
  {/* Expandable content */}
85
87
  {!isPanelCollapsed && (
86
- <div className="border-t px-3 pb-3 min-w-64">
88
+ <div className="border-t px-3 pb-3 min-w-72">
87
89
  {/* Direction Selection */}
88
90
  <div className="mt-3">
89
- <label className="text-xs text-muted-foreground mb-2 block">Direction</label>
91
+ <div className="text-[10px] uppercase tracking-wider text-muted-foreground mb-1.5">Direction</div>
90
92
  <div className="flex gap-1">
91
93
  {(['down', 'front', 'side'] as const).map((axis) => (
92
94
  <Button
@@ -105,16 +107,29 @@ export function SectionOverlay() {
105
107
  {/* Position Slider */}
106
108
  <div className="mt-3">
107
109
  <div className="flex items-center justify-between mb-1">
108
- <label className="text-xs text-muted-foreground">Position</label>
109
- <input
110
- type="number"
111
- min="0"
112
- max="100"
113
- step="0.1"
114
- value={sectionPlane.position}
115
- onChange={handlePositionChange}
116
- className="w-16 text-xs font-mono bg-muted px-1.5 py-0.5 rounded border-none text-right [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
117
- />
110
+ <div className="text-[10px] uppercase tracking-wider text-muted-foreground">Position</div>
111
+ <div className="flex items-center gap-1">
112
+ <Button
113
+ variant={sectionPlane.flipped ? 'default' : 'ghost'}
114
+ size="icon-sm"
115
+ onClick={flipSectionPlane}
116
+ aria-pressed={sectionPlane.flipped}
117
+ aria-label={sectionPlane.flipped ? 'Unflip cut direction' : 'Flip cut direction'}
118
+ title={sectionPlane.flipped ? 'Cut direction is flipped' : 'Flip cut direction'}
119
+ >
120
+ <FlipHorizontal2 className="h-3 w-3" />
121
+ </Button>
122
+ <input
123
+ type="number"
124
+ min="0"
125
+ max="100"
126
+ step="0.1"
127
+ value={sectionPlane.position}
128
+ onChange={handlePositionChange}
129
+ aria-label="Section plane position percentage"
130
+ className="w-16 text-xs font-mono bg-muted px-1.5 py-0.5 rounded border-none text-right [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
131
+ />
132
+ </div>
118
133
  </div>
119
134
  <input
120
135
  type="range"
@@ -123,10 +138,14 @@ export function SectionOverlay() {
123
138
  step="0.1"
124
139
  value={sectionPlane.position}
125
140
  onChange={handlePositionChange}
141
+ aria-label="Section plane position slider"
126
142
  className="w-full h-2 bg-muted rounded-lg appearance-none cursor-pointer accent-primary"
127
143
  />
128
144
  </div>
129
145
 
146
+ {/* Cap surface controls (hatch, colour, spacing) */}
147
+ <SectionCapControls />
148
+
130
149
  {/* Show 2D panel button - only when panel is closed */}
131
150
  {!drawingPanelVisible && (
132
151
  <div className="mt-3 pt-3 border-t">
@@ -156,12 +175,14 @@ export function SectionOverlay() {
156
175
  >
157
176
  <span className="font-mono text-xs uppercase tracking-wide">
158
177
  {sectionPlane.enabled
159
- ? `Cutting ${AXIS_INFO[sectionPlane.axis].label.toLowerCase()} at ${sectionPlane.position.toFixed(1)}%`
160
- : 'Preview mode'}
178
+ ? `Cut ${AXIS_INFO[sectionPlane.axis].label.toLowerCase()} at ${sectionPlane.position.toFixed(1)}%${sectionPlane.flipped ? ' (flipped)' : ''}`
179
+ : 'Clip off — drag slider to cut'}
161
180
  </span>
162
181
  </div>
163
182
 
164
- {/* Enable toggle - brutalist style matching Measure tool */}
183
+ {/* Enable toggle when OFF the model is not clipped even though the
184
+ plane visual is shown. Label is explicit so users don't mistake
185
+ "Preview" for "nothing will happen". */}
165
186
  <div className="pointer-events-auto absolute bottom-4 left-1/2 -translate-x-1/2 z-30">
166
187
  <button
167
188
  onClick={toggleSectionPlane}
@@ -170,9 +191,9 @@ export function SectionOverlay() {
170
191
  ? 'bg-primary text-primary-foreground border-primary'
171
192
  : 'bg-zinc-100 dark:bg-zinc-900 text-zinc-500 border-zinc-300 dark:border-zinc-700'
172
193
  }`}
173
- title="Toggle section plane"
194
+ title={sectionPlane.enabled ? 'Click to disable the cut' : 'Click to enable the cut'}
174
195
  >
175
- {sectionPlane.enabled ? 'Cutting' : 'Preview'}
196
+ {sectionPlane.enabled ? 'Clipping' : 'Clip off'}
176
197
  </button>
177
198
  </div>
178
199
 
@@ -29,6 +29,8 @@ export interface UseAnimationLoopParams {
29
29
  lastFrameTimeRef: MutableRefObject<number>;
30
30
  mouseIsDraggingRef: MutableRefObject<boolean>;
31
31
  activeToolRef: MutableRefObject<string>;
32
+ /** When set, clips model below this Y value (terrain clipping for Cesium overlay). */
33
+ terrainClipYRef: MutableRefObject<number | null>;
32
34
  hiddenEntitiesRef: MutableRefObject<Set<number>>;
33
35
  isolatedEntitiesRef: MutableRefObject<Set<number> | null>;
34
36
  selectedEntityIdRef: MutableRefObject<number | null>;
@@ -62,6 +64,7 @@ export function useAnimationLoop(params: UseAnimationLoopParams): void {
62
64
  lastFrameTimeRef,
63
65
  mouseIsDraggingRef,
64
66
  activeToolRef,
67
+ terrainClipYRef,
65
68
  hiddenEntitiesRef,
66
69
  isolatedEntitiesRef,
67
70
  selectedEntityIdRef,
@@ -167,10 +170,19 @@ export function useAnimationLoop(params: UseAnimationLoopParams): void {
167
170
  isInteracting: isInteractingRef.current || isAnimating,
168
171
  buildingRotation: coordinateInfoRef.current?.buildingRotation,
169
172
  sectionPlane: activeToolRef.current === 'section' ? {
170
- ...sectionPlaneRef.current,
173
+ axis: sectionPlaneRef.current.axis,
174
+ position: sectionPlaneRef.current.position,
175
+ enabled: sectionPlaneRef.current.enabled,
176
+ flipped: sectionPlaneRef.current.flipped,
177
+ // Cap rendering settings — the renderer reads these to draw the
178
+ // filled, hatched cut surfaces.
179
+ showCap: sectionPlaneRef.current.showCap,
180
+ showOutlines: sectionPlaneRef.current.showOutlines,
181
+ capStyle: sectionPlaneRef.current.capStyle,
171
182
  min: sectionRangeRef.current?.min,
172
183
  max: sectionRangeRef.current?.max,
173
184
  } : undefined,
185
+ terrainClipY: terrainClipYRef.current ?? undefined,
174
186
  });
175
187
  lastRenderTime = currentTime;
176
188
  }
@@ -21,6 +21,7 @@
21
21
  import { useEffect, useRef, type MutableRefObject } from 'react';
22
22
  import type { Renderer } from '@ifc-lite/renderer';
23
23
  import type { MeshData, CoordinateInfo } from '@ifc-lite/geometry';
24
+ import { logToDesktopTerminal } from '@/services/desktop-logger';
24
25
 
25
26
  export interface UseGeometryStreamingParams {
26
27
  rendererRef: MutableRefObject<Renderer | null>;
@@ -37,6 +38,8 @@ export interface UseGeometryStreamingParams {
37
38
  clearPendingMeshColorUpdates: () => void;
38
39
  clearPendingColorUpdates: () => void;
39
40
  clearColorRef: MutableRefObject<[number, number, number, number]>;
41
+ releaseGeometryAfterFinalize?: boolean;
42
+ onGeometryReleased?: () => void;
40
43
  }
41
44
 
42
45
  // Default bounds used when geometry is cleared
@@ -47,6 +50,11 @@ const DEFAULT_BOUNDS = {
47
50
 
48
51
  const MAX_VALID_COORD = 10000;
49
52
 
53
+ function traceGeometrySync(message: string): void {
54
+ console.log(`[GeomSync] ${message}`);
55
+ void logToDesktopTerminal('info', `[GeomSync] ${message}`);
56
+ }
57
+
50
58
  export function useGeometryStreaming(params: UseGeometryStreamingParams): void {
51
59
  const {
52
60
  rendererRef,
@@ -61,6 +69,8 @@ export function useGeometryStreaming(params: UseGeometryStreamingParams): void {
61
69
  clearPendingMeshColorUpdates,
62
70
  clearPendingColorUpdates,
63
71
  clearColorRef,
72
+ releaseGeometryAfterFinalize = false,
73
+ onGeometryReleased,
64
74
  } = params;
65
75
 
66
76
  // ─── Tracking refs ───────────────────────────────────────────────────
@@ -71,6 +81,33 @@ export function useGeometryStreaming(params: UseGeometryStreamingParams): void {
71
81
  const finalBoundsRefittedRef = useRef(false);
72
82
  const cameraSnapshotRef = useRef<{ px: number; py: number; pz: number; tx: number; ty: number; tz: number } | null>(null);
73
83
  const prevIsStreamingRef = useRef(isStreaming);
84
+ const queuePumpTimerRef = useRef<ReturnType<typeof globalThis.setTimeout> | null>(null);
85
+
86
+ // Only activate the timer-based queue pump when the tab is background-throttled
87
+ // (rAF stops firing). In the foreground, the animation loop already drains the
88
+ // queue every frame — a parallel setTimeout(0) pump doubles the GPU work and
89
+ // hurts Chrome (Dawn) where each buffer op is an IPC round-trip.
90
+ const ensureQueuePump = () => {
91
+ if (queuePumpTimerRef.current !== null) return;
92
+ if (!globalThis.document?.hidden) return; // rAF is active — let the animation loop drain
93
+ queuePumpTimerRef.current = globalThis.setTimeout(() => {
94
+ queuePumpTimerRef.current = null;
95
+ const renderer = rendererRef.current;
96
+ if (!renderer || !isInitialized) return;
97
+ const device = renderer.getGPUDevice();
98
+ const pipeline = renderer.getPipeline();
99
+ const scene = renderer.getScene();
100
+ if (!device || !pipeline || !scene.hasQueuedMeshes()) return;
101
+ const flushed = scene.flushPending(device, pipeline);
102
+ if (flushed) {
103
+ renderer.clearCaches();
104
+ renderer.requestRender();
105
+ }
106
+ if (scene.hasQueuedMeshes()) {
107
+ ensureQueuePump();
108
+ }
109
+ }, 0);
110
+ };
74
111
 
75
112
  // ─── Main geometry effect ────────────────────────────────────────────
76
113
  // Runs on every geometry change (new file, incremental batch, visibility toggle).
@@ -81,6 +118,7 @@ export function useGeometryStreaming(params: UseGeometryStreamingParams): void {
81
118
  // Geometry cleared/null — reset so next load is fresh
82
119
  if (!geometry) {
83
120
  if (lastGeometryLengthRef.current > 0 || lastGeometryRef.current !== null) {
121
+ traceGeometrySync(`geometry cleared lastLength=${lastGeometryLengthRef.current}`);
84
122
  lastGeometryLengthRef.current = 0;
85
123
  lastGeometryRef.current = null;
86
124
  processedMeshIdsRef.current.clear();
@@ -121,7 +159,9 @@ export function useGeometryStreaming(params: UseGeometryStreamingParams): void {
121
159
  }
122
160
 
123
161
  if (isNewFile) {
162
+ traceGeometrySync(`new file currentLength=${currentLength} lastLength=${lastLength} releaseAfterFinalize=${releaseGeometryAfterFinalize}`);
124
163
  scene.clear();
164
+ scene.setEphemeralStreamingMode(releaseGeometryAfterFinalize);
125
165
  processedMeshIdsRef.current.clear();
126
166
  cameraFittedRef.current = false;
127
167
  finalBoundsRefittedRef.current = false;
@@ -132,14 +172,18 @@ export function useGeometryStreaming(params: UseGeometryStreamingParams): void {
132
172
  geometryBoundsRef.current = { ...DEFAULT_BOUNDS };
133
173
  } else if (!isIncremental && currentLength !== lastLength) {
134
174
  if (currentLength < lastLength) {
175
+ traceGeometrySync(`geometry rebuilt after shrink currentLength=${currentLength} lastLength=${lastLength}`);
135
176
  // Length decreased (model hidden) — rebuild scene, keep camera
136
177
  scene.clear();
178
+ scene.setEphemeralStreamingMode(releaseGeometryAfterFinalize);
137
179
  processedMeshIdsRef.current.clear();
138
180
  lastGeometryLengthRef.current = 0;
139
181
  lastGeometryRef.current = geometry;
140
182
  } else {
183
+ traceGeometrySync(`geometry rebuilt after replace currentLength=${currentLength} lastLength=${lastLength} releaseAfterFinalize=${releaseGeometryAfterFinalize}`);
141
184
  // New file while another was open — full reset
142
185
  scene.clear();
186
+ scene.setEphemeralStreamingMode(releaseGeometryAfterFinalize);
143
187
  processedMeshIdsRef.current.clear();
144
188
  cameraFittedRef.current = false;
145
189
  finalBoundsRefittedRef.current = false;
@@ -193,6 +237,10 @@ export function useGeometryStreaming(params: UseGeometryStreamingParams): void {
193
237
  if (isStreaming) {
194
238
  // Queue for the animation loop — zero GPU work here.
195
239
  scene.queueMeshes(newMeshes);
240
+ // Desktop benchmark windows can become background-throttled, which
241
+ // stalls requestAnimationFrame-based draining. Keep a timer-based
242
+ // pump active so large native loads still finish offscreen.
243
+ ensureQueuePump();
196
244
  } else {
197
245
  // Non-streaming: process immediately (visibility toggles, etc.)
198
246
  scene.appendToBatches(newMeshes, device, pipeline, false);
@@ -230,66 +278,105 @@ export function useGeometryStreaming(params: UseGeometryStreamingParams): void {
230
278
  renderer.requestRender();
231
279
  }, [geometry, geometryVersion, coordinateInfo, isInitialized, isStreaming]);
232
280
 
281
+ useEffect(() => {
282
+ return () => {
283
+ if (queuePumpTimerRef.current !== null) {
284
+ globalThis.clearTimeout(queuePumpTimerRef.current);
285
+ queuePumpTimerRef.current = null;
286
+ }
287
+ };
288
+ }, []);
289
+
233
290
  // ─── Streaming complete: finalize + bounds refit ─────────────────────
234
291
  useEffect(() => {
235
292
  const renderer = rendererRef.current;
236
293
  if (!renderer || !isInitialized) return;
237
294
 
238
295
  if (prevIsStreamingRef.current && !isStreaming) {
239
- // Flush any remaining queued meshes synchronously before finalize
240
- const device = renderer.getGPUDevice();
241
- const pipeline = renderer.getPipeline();
242
296
  const scene = renderer.getScene();
243
- if (device && pipeline && scene.hasQueuedMeshes()) {
244
- scene.flushPending(device, pipeline);
245
- }
246
-
297
+ traceGeometrySync(
298
+ `stream transition complete geometryLength=${geometry?.length ?? 0} queued=${scene.hasQueuedMeshes()} batches=${scene.getBatchedMeshes().length}`
299
+ );
247
300
  renderer.requestRender();
248
301
 
249
- // Defer heavy work so the browser processes pending input events first.
250
- // Streaming fragments keep rendering until proper batches replace them.
251
302
  const capturedGeometry = geometry;
252
- const timer = setTimeout(() => {
253
- const r = rendererRef.current;
254
- if (!r) return;
255
-
256
- console.log('[GeomStream] Streaming ended — starting finalize');
257
-
258
- // Compute exact bounds and refit camera (fast ~15ms scan)
259
- if (cameraFittedRef.current && !finalBoundsRefittedRef.current && capturedGeometry && capturedGeometry.length > 0) {
260
- const t0 = performance.now();
261
- const exactBounds = computeBounds(capturedGeometry);
262
- console.log(`[GeomStream] computeBounds: ${(performance.now() - t0).toFixed(0)}ms`);
263
- if (exactBounds) {
264
- if (!userMovedCamera(r, cameraSnapshotRef.current)) {
265
- r.getCamera().fitToBounds(exactBounds.min, exactBounds.max);
303
+ let timeoutId: ReturnType<typeof globalThis.setTimeout> | null = null;
304
+ let rafId: number | null = null;
305
+
306
+ const startFinalize = () => {
307
+ timeoutId = globalThis.setTimeout(() => {
308
+ const r = rendererRef.current;
309
+ if (!r) return;
310
+
311
+ console.log('[GeomStream] Streaming ended — starting finalize');
312
+ traceGeometrySync(
313
+ `finalize start geometryLength=${capturedGeometry?.length ?? 0} releaseAfterFinalize=${releaseGeometryAfterFinalize}`
314
+ );
315
+
316
+ // Compute exact bounds and refit camera (fast ~15ms scan)
317
+ if (cameraFittedRef.current && !finalBoundsRefittedRef.current && capturedGeometry && capturedGeometry.length > 0) {
318
+ const t0 = performance.now();
319
+ const exactBounds = computeBounds(capturedGeometry);
320
+ console.log(`[GeomStream] computeBounds: ${(performance.now() - t0).toFixed(0)}ms`);
321
+ if (exactBounds) {
322
+ if (!userMovedCamera(r, cameraSnapshotRef.current)) {
323
+ r.getCamera().fitToBounds(exactBounds.min, exactBounds.max);
324
+ }
325
+ geometryBoundsRef.current = exactBounds;
326
+ finalBoundsRefittedRef.current = true;
266
327
  }
267
- geometryBoundsRef.current = exactBounds;
268
- finalBoundsRefittedRef.current = true;
269
328
  }
270
- }
271
329
 
272
- // Time-sliced finalize: rebuild proper batches in ~8ms chunks
273
- const dev = r.getGPUDevice();
274
- const pipe = r.getPipeline();
275
- if (dev && pipe) {
276
- const t0 = performance.now();
277
- r.getScene().finalizeStreamingAsync(dev, pipe).then(() => {
278
- const batchCount = r.getScene().getBatchedMeshes().length;
279
- let totalIdx = 0;
280
- for (const b of r.getScene().getBatchedMeshes()) totalIdx += b.indexCount;
281
- console.log(`[GeomStream] finalizeStreamingAsync complete: ${(performance.now() - t0).toFixed(0)}ms → ${batchCount} consolidated batches, ${(totalIdx / 3 / 1e6).toFixed(1)}M triangles`);
330
+ // Time-sliced finalize: rebuild proper batches in ~8ms chunks
331
+ if (releaseGeometryAfterFinalize) {
332
+ r.getScene().finishEphemeralStreaming();
333
+ onGeometryReleased?.();
282
334
  r.clearCaches();
283
335
  r.requestRender();
284
- });
336
+ traceGeometrySync(`ephemeral finalize complete batches=${r.getScene().getBatchedMeshes().length}`);
337
+ return;
338
+ }
339
+
340
+ const dev = r.getGPUDevice();
341
+ const pipe = r.getPipeline();
342
+ if (dev && pipe) {
343
+ const t0 = performance.now();
344
+ r.getScene().finalizeStreamingAsync(dev, pipe).then(() => {
345
+ const batchCount = r.getScene().getBatchedMeshes().length;
346
+ let totalIdx = 0;
347
+ for (const b of r.getScene().getBatchedMeshes()) totalIdx += b.indexCount;
348
+ console.log(`[GeomStream] finalizeStreamingAsync complete: ${(performance.now() - t0).toFixed(0)}ms → ${batchCount} consolidated batches, ${(totalIdx / 3 / 1e6).toFixed(1)}M triangles`);
349
+ traceGeometrySync(
350
+ `finalize complete elapsed=${(performance.now() - t0).toFixed(0)}ms batches=${batchCount} queued=${r.getScene().hasQueuedMeshes()}`
351
+ );
352
+ r.clearCaches();
353
+ r.requestRender();
354
+ });
355
+ }
356
+ }, 0);
357
+ };
358
+
359
+ const waitForQueueDrain = () => {
360
+ const currentRenderer = rendererRef.current;
361
+ if (!currentRenderer) return;
362
+ if (!currentRenderer.getScene().hasQueuedMeshes()) {
363
+ startFinalize();
364
+ return;
285
365
  }
286
- }, 0);
366
+ currentRenderer.requestRender();
367
+ rafId = requestAnimationFrame(waitForQueueDrain);
368
+ };
369
+
370
+ waitForQueueDrain();
287
371
 
288
372
  prevIsStreamingRef.current = isStreaming;
289
- return () => clearTimeout(timer);
373
+ return () => {
374
+ if (timeoutId !== null) globalThis.clearTimeout(timeoutId);
375
+ if (rafId !== null) cancelAnimationFrame(rafId);
376
+ };
290
377
  }
291
378
  prevIsStreamingRef.current = isStreaming;
292
- }, [isStreaming, isInitialized]);
379
+ }, [isStreaming, isInitialized, releaseGeometryAfterFinalize, onGeometryReleased]);
293
380
 
294
381
  // ─── Mesh color updates (style/material deferred colors) ─────────────
295
382
  useEffect(() => {