@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
@@ -2,20 +2,27 @@
2
2
  * License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
4
 
5
- import { useMemo, useRef, useState, useCallback } from 'react';
5
+ import { useMemo, useRef, useState, useCallback, useEffect, useSyncExternalStore } from 'react';
6
6
  import { Viewport } from './Viewport';
7
7
  import { ViewportOverlays } from './ViewportOverlays';
8
8
  import { ToolOverlays } from './ToolOverlays';
9
9
  import { Section2DPanel } from './Section2DPanel';
10
10
  import { BasketPresentationDock } from './BasketPresentationDock';
11
11
  import { BCFOverlay } from './bcf/BCFOverlay';
12
- import { useViewerStore } from '@/store';
12
+ import { CesiumOverlay } from './CesiumOverlay';
13
+ import { getViewerStoreApi, useViewerStore } from '@/store';
13
14
  import { toGlobalIdFromModels } from '@/store/globalId';
14
15
  import { collectIfcBuildingStoreyElementsWithIfcSpace } from '@/store/basketVisibleSet';
15
16
  import { useIfc } from '@/hooks/useIfc';
16
17
  import { useWebGPU } from '@/hooks/useWebGPU';
17
- import { Upload, MousePointer, Layers, Info, Command, AlertTriangle, ChevronDown, ExternalLink, Plus } from 'lucide-react';
18
+ import { openIfcFileDialog } from '@/services/file-dialog';
19
+ import { logToDesktopTerminal } from '@/services/desktop-logger';
20
+ import { cacheFileBlobs, formatFileSize, getCachedFile, getRecentFiles, recordRecentFiles, type RecentFileEntry } from '@/lib/recent-files';
21
+ import { isTauri } from '@/lib/platform';
22
+ import { Upload, MousePointer, Layers, Info, Command, AlertTriangle, ChevronDown, ExternalLink, Plus, Clock3 } from 'lucide-react';
18
23
  import type { MeshData, CoordinateInfo, GeometryResult } from '@ifc-lite/geometry';
24
+ import { type IfcDataStore } from '@ifc-lite/parser';
25
+ import { getEffectiveGeoreference } from '@/lib/geo/effective-georef';
19
26
 
20
27
  const ZERO_VEC3 = { x: 0, y: 0, z: 0 };
21
28
  const DEFAULT_COORDINATE_INFO: CoordinateInfo = {
@@ -26,20 +33,42 @@ const DEFAULT_COORDINATE_INFO: CoordinateInfo = {
26
33
  };
27
34
 
28
35
  export function ViewportContainer() {
29
- const { geometryResult, ifcDataStore, loadFile, loading, models, clearAllModels, loadFilesSequentially } = useIfc();
36
+ const { loadFile, loading, clearAllModels, loadFilesSequentially } = useIfc();
37
+ const releaseGeometryMemory = useViewerStore((s) => s.releaseGeometryMemory);
30
38
  const selectedStoreys = useViewerStore((s) => s.selectedStoreys);
31
39
  const typeVisibility = useViewerStore((s) => s.typeVisibility);
32
40
  const isolatedEntities = useViewerStore((s) => s.isolatedEntities);
33
41
  const classFilter = useViewerStore((s) => s.classFilter);
34
- // Multi-model support: get all loaded models from store (for merged geometry)
35
- const storeModels = useViewerStore((s) => s.models);
36
42
  const resetViewerState = useViewerStore((s) => s.resetViewerState);
37
43
  const bcfOverlayVisible = useViewerStore((s) => s.bcfOverlayVisible);
44
+ const cesiumEnabled = useViewerStore((s) => s.cesiumEnabled);
45
+ const georefMutations = useViewerStore((s) => s.georefMutations);
46
+ const setCesiumSourceModelId = useViewerStore((s) => s.setCesiumSourceModelId);
47
+ const setCesiumAvailable = useViewerStore((s) => s.setCesiumAvailable);
48
+ // Subscribe to mutationVersion so Cesium reacts to georef edits
49
+ const mutationVersion = useViewerStore((s) => s.mutationVersion);
38
50
  const fileInputRef = useRef<HTMLInputElement>(null);
39
51
  const [isDragging, setIsDragging] = useState(false);
40
52
  const [showTroubleshooting, setShowTroubleshooting] = useState(false);
53
+ const [recentFiles, setRecentFiles] = useState<RecentFileEntry[]>([]);
41
54
  const webgpu = useWebGPU();
42
55
 
56
+ const viewerStoreApi = getViewerStoreApi();
57
+ const viewportStoreState = useSyncExternalStore(
58
+ viewerStoreApi.subscribe,
59
+ viewerStoreApi.getState,
60
+ viewerStoreApi.getState,
61
+ );
62
+
63
+ const {
64
+ geometryResult,
65
+ ifcDataStore,
66
+ models,
67
+ boundedGeometryMode,
68
+ geometryUpdateTick,
69
+ } = viewportStoreState;
70
+ const storeModels = models;
71
+
43
72
  // Check if we have models loaded (for determining add vs replace behavior)
44
73
  const hasModelsLoaded = models.size > 0 || (geometryResult?.meshes && geometryResult.meshes.length > 0);
45
74
 
@@ -53,39 +82,87 @@ export function ViewportContainer() {
53
82
  return map;
54
83
  }, [storeModels]);
55
84
 
85
+ const mergedCacheRef = useRef<MeshData[]>([]);
86
+ const mergedLengthsRef = useRef<Map<string, number>>(new Map());
87
+ const mergedVisibilityRef = useRef<Map<string, boolean>>(new Map());
88
+
56
89
  // Multi-model: merge geometries from all visible models
57
90
  const mergedGeometryResult = useMemo(() => {
58
- // If we have federated models, merge their visible geometries
59
- if (storeModels.size > 0) {
60
- const allMeshes: MeshData[] = [];
91
+ if (storeModels.size === 1) {
92
+ const firstModel = storeModels.values().next().value;
93
+ if (!firstModel?.visible) {
94
+ return {
95
+ meshes: [],
96
+ totalVertices: 0,
97
+ totalTriangles: 0,
98
+ coordinateInfo: DEFAULT_COORDINATE_INFO,
99
+ } satisfies GeometryResult;
100
+ }
101
+ return firstModel.geometryResult ?? geometryResult;
102
+ }
103
+
104
+ if (storeModels.size > 1) {
61
105
  let totalVertices = 0;
62
106
  let totalTriangles = 0;
63
107
  let mergedCoordinateInfo: CoordinateInfo | undefined;
108
+ let shouldRebuild = false;
64
109
 
65
- for (const [modelId, model] of storeModels) {
66
- // Skip hidden models - this is how model visibility works
67
- if (!model.visible) continue;
110
+ if (mergedLengthsRef.current.size !== storeModels.size) {
111
+ shouldRebuild = true;
112
+ }
68
113
 
114
+ for (const [modelId, model] of storeModels) {
69
115
  const modelGeometry = model.geometryResult;
70
- const modelIndex = modelIdToIndex.get(modelId) ?? 0;
71
- if (modelGeometry?.meshes) {
72
- // Tag each mesh with its modelIndex for selection/highlighting
116
+ const meshCount = model.visible ? (modelGeometry?.meshes.length ?? 0) : 0;
117
+ totalVertices += model.visible ? (modelGeometry?.totalVertices ?? 0) : 0;
118
+ totalTriangles += model.visible ? (modelGeometry?.totalTriangles ?? 0) : 0;
119
+ if (!mergedCoordinateInfo && model.visible && modelGeometry?.coordinateInfo) {
120
+ mergedCoordinateInfo = modelGeometry.coordinateInfo;
121
+ }
122
+
123
+ if (
124
+ mergedVisibilityRef.current.get(modelId) !== model.visible ||
125
+ (mergedLengthsRef.current.get(modelId) ?? 0) > meshCount
126
+ ) {
127
+ shouldRebuild = true;
128
+ }
129
+ }
130
+
131
+ if (shouldRebuild) {
132
+ const rebuilt: MeshData[] = [];
133
+ mergedLengthsRef.current = new Map();
134
+ mergedVisibilityRef.current = new Map();
135
+ for (const [modelId, model] of storeModels) {
136
+ const modelGeometry = model.geometryResult;
137
+ mergedVisibilityRef.current.set(modelId, model.visible);
138
+ const modelIndex = modelIdToIndex.get(modelId) ?? 0;
139
+ if (!model.visible || !modelGeometry?.meshes) {
140
+ mergedLengthsRef.current.set(modelId, 0);
141
+ continue;
142
+ }
73
143
  for (const mesh of modelGeometry.meshes) {
74
- allMeshes.push({ ...mesh, modelIndex });
144
+ rebuilt.push({ ...mesh, modelIndex });
75
145
  }
76
- totalVertices += modelGeometry.totalVertices || 0;
77
- totalTriangles += modelGeometry.totalTriangles || 0;
78
-
79
- // Use first model's coordinate info as base (could be improved to compute union)
80
- if (!mergedCoordinateInfo && modelGeometry.coordinateInfo) {
81
- mergedCoordinateInfo = modelGeometry.coordinateInfo;
146
+ mergedLengthsRef.current.set(modelId, modelGeometry.meshes.length);
147
+ }
148
+ mergedCacheRef.current = rebuilt;
149
+ } else {
150
+ for (const [modelId, model] of storeModels) {
151
+ const modelGeometry = model.geometryResult;
152
+ const modelIndex = modelIdToIndex.get(modelId) ?? 0;
153
+ const previousLength = mergedLengthsRef.current.get(modelId) ?? 0;
154
+ const nextMeshes = model.visible ? (modelGeometry?.meshes ?? []) : [];
155
+ for (let i = previousLength; i < nextMeshes.length; i++) {
156
+ const mesh = nextMeshes[i];
157
+ mergedCacheRef.current.push({ ...mesh, modelIndex });
82
158
  }
159
+ mergedLengthsRef.current.set(modelId, nextMeshes.length);
160
+ mergedVisibilityRef.current.set(modelId, model.visible);
83
161
  }
84
162
  }
85
163
 
86
- // Return merged result (may be empty if all models hidden)
87
164
  return {
88
- meshes: allMeshes,
165
+ meshes: mergedCacheRef.current,
89
166
  totalVertices,
90
167
  totalTriangles,
91
168
  coordinateInfo: mergedCoordinateInfo ?? DEFAULT_COORDINATE_INFO,
@@ -96,6 +173,88 @@ export function ViewportContainer() {
96
173
  return geometryResult;
97
174
  }, [storeModels, geometryResult, modelIdToIndex]);
98
175
 
176
+ // Extract georeferencing info merged with any live mutations (for Cesium overlay).
177
+ // Reacts to: model load, Cesium toggle, and every georef field edit.
178
+ const georef = useMemo(() => {
179
+ if (!cesiumEnabled) return null;
180
+
181
+ // Check federated models first
182
+ for (const [modelId, model] of storeModels) {
183
+ const ds = model.ifcDataStore;
184
+ if (!ds) continue;
185
+ const effective = getEffectiveGeoreference(
186
+ ds as IfcDataStore,
187
+ model.geometryResult?.coordinateInfo,
188
+ georefMutations.get(modelId),
189
+ );
190
+ if (effective?.projectedCRS?.name && effective.mapConversion) {
191
+ return { ...effective, sourceModelId: modelId };
192
+ }
193
+ }
194
+
195
+ // Fallback to legacy single-model
196
+ if (ifcDataStore) {
197
+ const effective = getEffectiveGeoreference(
198
+ ifcDataStore as IfcDataStore,
199
+ mergedGeometryResult?.coordinateInfo,
200
+ georefMutations.get('__legacy__'),
201
+ );
202
+ if (effective?.projectedCRS?.name && effective.mapConversion) {
203
+ return { ...effective, sourceModelId: '__legacy__' };
204
+ }
205
+ }
206
+
207
+ return null;
208
+ }, [cesiumEnabled, storeModels, ifcDataStore, georefMutations, mutationVersion, mergedGeometryResult]);
209
+
210
+ // Determine whether Cesium button should be visible (model has georef or user added it via mutations).
211
+ // Runs independently of cesiumEnabled so the button appears/disappears reactively.
212
+ useEffect(() => {
213
+ function hasGeoref(): boolean {
214
+ // Check federated models
215
+ for (const [modelId, model] of storeModels) {
216
+ const ds = model.ifcDataStore;
217
+ if (!ds) continue;
218
+ const effective = getEffectiveGeoreference(
219
+ ds as IfcDataStore,
220
+ model.geometryResult?.coordinateInfo,
221
+ georefMutations.get(modelId),
222
+ );
223
+ if (effective?.projectedCRS?.name) return true;
224
+ }
225
+ // Fallback to legacy single-model
226
+ if (ifcDataStore) {
227
+ const effective = getEffectiveGeoreference(
228
+ ifcDataStore as IfcDataStore,
229
+ mergedGeometryResult?.coordinateInfo,
230
+ georefMutations.get('__legacy__'),
231
+ );
232
+ if (effective?.projectedCRS?.name) return true;
233
+ }
234
+ return false;
235
+ }
236
+ setCesiumAvailable(hasGeoref());
237
+ }, [storeModels, ifcDataStore, georefMutations, mutationVersion, setCesiumAvailable, mergedGeometryResult]);
238
+
239
+ // Sync the active Cesium source model ID so terrain actions are scoped correctly
240
+ useEffect(() => {
241
+ setCesiumSourceModelId(georef?.sourceModelId ?? null);
242
+ }, [georef?.sourceModelId, setCesiumSourceModelId]);
243
+
244
+ useEffect(() => {
245
+ // Recent files are a desktop-only feature — the web viewer should not
246
+ // show previously opened files in the landing page empty state.
247
+ if (!isTauri()) return;
248
+
249
+ const refreshRecentFiles = () => {
250
+ setRecentFiles(getRecentFiles().slice(0, 3));
251
+ };
252
+
253
+ refreshRecentFiles();
254
+ window.addEventListener('focus', refreshRecentFiles);
255
+ return () => window.removeEventListener('focus', refreshRecentFiles);
256
+ }, []);
257
+
99
258
  const handleDragOver = useCallback((e: React.DragEvent) => {
100
259
  e.preventDefault();
101
260
  e.stopPropagation();
@@ -128,6 +287,10 @@ export function ViewportContainer() {
128
287
 
129
288
  if (supportedFiles.length === 0) return;
130
289
 
290
+ recordRecentFiles(supportedFiles.map((file) => ({ name: file.name, size: file.size })));
291
+ void cacheFileBlobs(supportedFiles);
292
+ setRecentFiles(getRecentFiles().slice(0, 3));
293
+
131
294
  if (hasModelsLoaded) {
132
295
  // Models already loaded - add new files sequentially
133
296
  loadFilesSequentially(supportedFiles);
@@ -158,6 +321,10 @@ export function ViewportContainer() {
158
321
 
159
322
  if (supportedFiles.length === 0) return;
160
323
 
324
+ recordRecentFiles(supportedFiles.map((file) => ({ name: file.name, size: file.size })));
325
+ void cacheFileBlobs(supportedFiles);
326
+ setRecentFiles(getRecentFiles().slice(0, 3));
327
+
161
328
  if (supportedFiles.length === 1) {
162
329
  // Single file - use loadFile (simpler single-model path)
163
330
  loadFile(supportedFiles[0]);
@@ -184,6 +351,7 @@ export function ViewportContainer() {
184
351
  // A version counter triggers downstream re-renders via the Viewport prop.
185
352
  const filteredCacheRef = useRef<MeshData[]>([]);
186
353
  const filteredSourceLenRef = useRef(0);
354
+ const filteredSourceRef = useRef<MeshData[] | null>(null);
187
355
  const filteredTypeVisRef = useRef(typeVisibility);
188
356
  const filteredVersionRef = useRef(0);
189
357
 
@@ -191,6 +359,7 @@ export function ViewportContainer() {
191
359
  if (!mergedGeometryResult?.meshes) {
192
360
  filteredCacheRef.current = [];
193
361
  filteredSourceLenRef.current = 0;
362
+ filteredSourceRef.current = null;
194
363
  filteredVersionRef.current = 0;
195
364
  return null;
196
365
  }
@@ -204,9 +373,11 @@ export function ViewportContainer() {
204
373
  prevVis.spaces !== typeVisibility.spaces ||
205
374
  prevVis.openings !== typeVisibility.openings ||
206
375
  prevVis.site !== typeVisibility.site;
207
- if (typeVisChanged || allMeshes.length < filteredSourceLenRef.current) {
376
+ const sourceChanged = filteredSourceRef.current !== allMeshes;
377
+ if (typeVisChanged || sourceChanged || allMeshes.length < filteredSourceLenRef.current) {
208
378
  cache.length = 0;
209
379
  filteredSourceLenRef.current = 0;
380
+ filteredSourceRef.current = allMeshes;
210
381
  filteredTypeVisRef.current = typeVisibility;
211
382
  }
212
383
 
@@ -238,7 +409,7 @@ export function ViewportContainer() {
238
409
 
239
410
  // Only bump version when cache content actually changed — avoids
240
411
  // unnecessary downstream re-renders when memo runs with same data.
241
- if (cache.length !== prevCacheLen || typeVisChanged) {
412
+ if (cache.length !== prevCacheLen || typeVisChanged || sourceChanged) {
242
413
  filteredVersionRef.current++;
243
414
  }
244
415
 
@@ -531,7 +702,29 @@ export function ViewportContainer() {
531
702
 
532
703
  {/* Action */}
533
704
  <button
534
- onClick={() => webgpu.supported && fileInputRef.current?.click()}
705
+ onClick={async () => {
706
+ if (!webgpu.supported) {
707
+ return;
708
+ }
709
+
710
+ void logToDesktopTerminal('info', '[ViewportContainer] Empty-state open button clicked');
711
+ const file = await openIfcFileDialog();
712
+ if (file) {
713
+ void logToDesktopTerminal('info', `[ViewportContainer] Native dialog selected ${file.path}`);
714
+ recordRecentFiles([{
715
+ name: file.name,
716
+ size: file.size,
717
+ path: file.path,
718
+ modifiedMs: file.modifiedMs ?? null,
719
+ }]);
720
+ setRecentFiles(getRecentFiles().slice(0, 3));
721
+ loadFile(file);
722
+ return;
723
+ }
724
+
725
+ void logToDesktopTerminal('info', '[ViewportContainer] Falling back to browser file input');
726
+ fileInputRef.current?.click();
727
+ }}
535
728
  disabled={!webgpu.supported || webgpu.checking}
536
729
  className={`group w-full flex items-center justify-center gap-3 px-6 py-3 font-mono text-sm border transition-all ${
537
730
  !webgpu.supported || webgpu.checking
@@ -546,6 +739,37 @@ export function ViewportContainer() {
546
739
  <p className="mt-3 text-xs font-mono text-zinc-400 dark:text-[#565f89]">
547
740
  {webgpu.supported ? 'or drag & drop anywhere' : 'file upload disabled'}
548
741
  </p>
742
+
743
+ {recentFiles.length > 0 && (
744
+ <div className="mt-6 w-full border-t border-zinc-200 dark:border-[#3b4261] pt-4">
745
+ <div className="mb-3 flex items-center gap-2 text-xs font-mono uppercase tracking-[0.2em] text-zinc-400 dark:text-[#565f89]">
746
+ <Clock3 className="h-3.5 w-3.5" />
747
+ <span>Recent Files</span>
748
+ </div>
749
+ <div className="flex flex-col gap-2">
750
+ {recentFiles.map((file) => (
751
+ <button
752
+ key={`${file.name}-${file.timestamp}`}
753
+ type="button"
754
+ onClick={async () => {
755
+ const cached = await getCachedFile(file);
756
+ if (cached) {
757
+ await loadFile(cached);
758
+ return;
759
+ }
760
+ fileInputRef.current?.click();
761
+ }}
762
+ className="flex items-center justify-between gap-3 border border-zinc-200 bg-zinc-50 px-3 py-2 text-left transition-colors hover:border-primary hover:text-primary dark:border-[#3b4261] dark:bg-[#1f2335] dark:hover:border-primary"
763
+ >
764
+ <span className="min-w-0 truncate font-mono text-xs">{file.name}</span>
765
+ <span className="shrink-0 font-mono text-[10px] uppercase tracking-wide text-zinc-400 dark:text-[#565f89]">
766
+ {formatFileSize(file.size)}
767
+ </span>
768
+ </button>
769
+ ))}
770
+ </div>
771
+ </div>
772
+ )}
549
773
  </div>
550
774
 
551
775
  {/* Feature Grid */}
@@ -607,18 +831,31 @@ export function ViewportContainer() {
607
831
  </div>
608
832
  )}
609
833
 
834
+ {/* Cesium 3D world context overlay — rendered behind the WebGPU canvas (web only) */}
835
+ {cesiumEnabled && georef && !isTauri() && (
836
+ <CesiumOverlay
837
+ mapConversion={georef.mapConversion}
838
+ projectedCRS={georef.projectedCRS}
839
+ coordinateInfo={georef.coordinateInfo}
840
+ geometryResult={mergedGeometryResult}
841
+ lengthUnitScale={georef.lengthUnitScale}
842
+ />
843
+ )}
610
844
  <Viewport
611
845
  geometry={filteredGeometry}
612
846
  geometryVersion={geometryVersion}
613
847
  coordinateInfo={mergedGeometryResult?.coordinateInfo}
614
848
  computedIsolatedIds={computedIsolatedIds}
615
849
  modelIdToIndex={modelIdToIndex}
850
+ cesiumActive={cesiumEnabled && georef !== null && !isTauri()}
851
+ releaseGeometryAfterStream={false}
852
+ onGeometryReleased={releaseGeometryMemory}
616
853
  />
617
854
  {bcfOverlayVisible && <BCFOverlay />}
618
855
  <ViewportOverlays />
619
856
  <ToolOverlays />
620
857
  <BasketPresentationDock />
621
- <Section2DPanel
858
+ <Section2DPanel
622
859
  mergedGeometry={mergedGeometryResult}
623
860
  computedIsolatedIds={computedIsolatedIds}
624
861
  modelIdToIndex={modelIdToIndex}
@@ -8,13 +8,22 @@ import {
8
8
  ZoomIn,
9
9
  ZoomOut,
10
10
  Layers,
11
+ Globe2,
12
+ Mountain,
13
+ Building2,
14
+ Satellite,
15
+ X,
11
16
  } from 'lucide-react';
12
17
  import { Button } from '@/components/ui/button';
13
18
  import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
14
19
  import { useViewerStore } from '@/store';
20
+ import type { CesiumDataSource } from '@/store/slices/cesiumSlice';
15
21
  import { goHomeFromStore } from '@/store/homeView';
16
22
  import { useIfc } from '@/hooks/useIfc';
17
23
  import { cn } from '@/lib/utils';
24
+ import { isTauri } from '@/lib/platform';
25
+
26
+ const isDesktop = isTauri();
18
27
  import { ViewCube, type ViewCubeRef } from './ViewCube';
19
28
  import { AxisHelper, type AxisHelperRef } from './AxisHelper';
20
29
 
@@ -28,6 +37,14 @@ export function ViewportOverlays({ hideViewCube = false }: { hideViewCube?: bool
28
37
  const setOnScaleChange = useViewerStore((s) => s.setOnScaleChange);
29
38
  const { ifcDataStore, geometryResult } = useIfc();
30
39
 
40
+ // Cesium state
41
+ const cesiumEnabled = useViewerStore((s) => s.cesiumEnabled);
42
+ const cesiumDataSource = useViewerStore((s) => s.cesiumDataSource);
43
+ const setCesiumDataSource = useViewerStore((s) => s.setCesiumDataSource);
44
+ const cesiumTerrainEnabled = useViewerStore((s) => s.cesiumTerrainEnabled);
45
+ const setCesiumTerrainEnabled = useViewerStore((s) => s.setCesiumTerrainEnabled);
46
+ const toggleCesium = useViewerStore((s) => s.toggleCesium);
47
+
31
48
  // Use refs for rotation to avoid re-renders - ViewCube updates itself directly
32
49
  const cameraRotationRef = useRef({ azimuth: 45, elevation: 25 });
33
50
  const viewCubeRef = useRef<ViewCubeRef | null>(null);
@@ -132,35 +149,45 @@ export function ViewportOverlays({ hideViewCube = false }: { hideViewCube?: bool
132
149
 
133
150
  return (
134
151
  <>
135
- {/* Navigation Controls (bottom-right) */}
136
- <div className="absolute bottom-4 right-4 flex flex-col gap-1 bg-background/80 backdrop-blur-sm rounded-lg border shadow-sm p-1">
137
- <Tooltip>
138
- <TooltipTrigger asChild>
139
- <Button variant="ghost" size="icon-sm" onClick={handleHome}>
140
- <Home className="h-4 w-4" />
141
- </Button>
142
- </TooltipTrigger>
143
- <TooltipContent side="left">Home (H)</TooltipContent>
144
- </Tooltip>
152
+ {/* Bottom-right: Cesium settings overlay OR Navigation controls (Cesium is web-only) */}
153
+ {cesiumEnabled && !isDesktop ? (
154
+ <CesiumSettingsOverlay
155
+ dataSource={cesiumDataSource}
156
+ onDataSourceChange={setCesiumDataSource}
157
+ terrainEnabled={cesiumTerrainEnabled}
158
+ onTerrainChange={setCesiumTerrainEnabled}
159
+ onClose={toggleCesium}
160
+ />
161
+ ) : (
162
+ <div className="absolute bottom-4 right-4 flex flex-col gap-1 bg-background/80 backdrop-blur-sm rounded-lg border shadow-sm p-1">
163
+ <Tooltip>
164
+ <TooltipTrigger asChild>
165
+ <Button variant="ghost" size="icon-sm" onClick={handleHome}>
166
+ <Home className="h-4 w-4" />
167
+ </Button>
168
+ </TooltipTrigger>
169
+ <TooltipContent side="left">Home (H)</TooltipContent>
170
+ </Tooltip>
145
171
 
146
- <Tooltip>
147
- <TooltipTrigger asChild>
148
- <Button variant="ghost" size="icon-sm" onClick={handleZoomIn}>
149
- <ZoomIn className="h-4 w-4" />
150
- </Button>
151
- </TooltipTrigger>
152
- <TooltipContent side="left">Zoom In (+)</TooltipContent>
153
- </Tooltip>
172
+ <Tooltip>
173
+ <TooltipTrigger asChild>
174
+ <Button variant="ghost" size="icon-sm" onClick={handleZoomIn}>
175
+ <ZoomIn className="h-4 w-4" />
176
+ </Button>
177
+ </TooltipTrigger>
178
+ <TooltipContent side="left">Zoom In (+)</TooltipContent>
179
+ </Tooltip>
154
180
 
155
- <Tooltip>
156
- <TooltipTrigger asChild>
157
- <Button variant="ghost" size="icon-sm" onClick={handleZoomOut}>
158
- <ZoomOut className="h-4 w-4" />
159
- </Button>
160
- </TooltipTrigger>
161
- <TooltipContent side="left">Zoom Out (-)</TooltipContent>
162
- </Tooltip>
163
- </div>
181
+ <Tooltip>
182
+ <TooltipTrigger asChild>
183
+ <Button variant="ghost" size="icon-sm" onClick={handleZoomOut}>
184
+ <ZoomOut className="h-4 w-4" />
185
+ </Button>
186
+ </TooltipTrigger>
187
+ <TooltipContent side="left">Zoom Out (-)</TooltipContent>
188
+ </Tooltip>
189
+ </div>
190
+ )}
164
191
 
165
192
  {/* Context Info (bottom-center) - Storey names */}
166
193
  {storeyNames && storeyNames.length > 0 && (
@@ -209,3 +236,81 @@ export function ViewportOverlays({ hideViewCube = false }: { hideViewCube?: bool
209
236
  </>
210
237
  );
211
238
  }
239
+
240
+ /* ------------------------------------------------------------------ */
241
+ /* Cesium Settings Overlay — replaces nav controls when Cesium is on */
242
+ /* ------------------------------------------------------------------ */
243
+
244
+ const DATA_SOURCES: { value: CesiumDataSource; label: string; icon: typeof Globe2 }[] = [
245
+ { value: 'google-photorealistic', label: 'Google 3D', icon: Globe2 },
246
+ { value: 'osm-buildings', label: 'OSM', icon: Building2 },
247
+ { value: 'bing-aerial', label: 'Aerial', icon: Satellite },
248
+ ];
249
+
250
+ function CesiumSettingsOverlay({
251
+ dataSource,
252
+ onDataSourceChange,
253
+ terrainEnabled,
254
+ onTerrainChange,
255
+ onClose,
256
+ }: {
257
+ dataSource: CesiumDataSource;
258
+ onDataSourceChange: (ds: CesiumDataSource) => void;
259
+ terrainEnabled: boolean;
260
+ onTerrainChange: (enabled: boolean) => void;
261
+ onClose: () => void;
262
+ }) {
263
+ return (
264
+ <div className="absolute bottom-4 right-4 z-10 pointer-events-auto bg-background/90 backdrop-blur-sm rounded-lg border shadow-lg p-2 flex flex-col gap-2 min-w-[160px]">
265
+ {/* Header */}
266
+ <div className="flex items-center justify-between gap-2">
267
+ <span className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
268
+ 3D World
269
+ </span>
270
+ <Tooltip>
271
+ <TooltipTrigger asChild>
272
+ <Button variant="ghost" size="icon-sm" className="h-5 w-5" onClick={onClose}>
273
+ <X className="h-3 w-3" />
274
+ </Button>
275
+ </TooltipTrigger>
276
+ <TooltipContent side="left">Disable Cesium overlay</TooltipContent>
277
+ </Tooltip>
278
+ </div>
279
+
280
+ {/* Data Source Buttons */}
281
+ <div className="flex flex-col gap-0.5">
282
+ {DATA_SOURCES.map((ds) => {
283
+ const Icon = ds.icon;
284
+ const active = dataSource === ds.value;
285
+ return (
286
+ <button
287
+ key={ds.value}
288
+ onClick={() => onDataSourceChange(ds.value)}
289
+ className={cn(
290
+ 'flex items-center gap-2 px-2 py-1.5 rounded text-xs transition-colors text-left',
291
+ active
292
+ ? 'bg-teal-600 text-white'
293
+ : 'hover:bg-muted text-foreground/80',
294
+ )}
295
+ >
296
+ <Icon className="h-3.5 w-3.5 shrink-0" />
297
+ {ds.label}
298
+ </button>
299
+ );
300
+ })}
301
+ </div>
302
+
303
+ {/* Terrain Toggle */}
304
+ <label className="flex items-center gap-2 px-2 py-1 cursor-pointer border-t border-border pt-2">
305
+ <Mountain className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
306
+ <span className="text-xs text-foreground/80">Terrain</span>
307
+ <input
308
+ type="checkbox"
309
+ checked={terrainEnabled}
310
+ onChange={(e) => onTerrainChange(e.target.checked)}
311
+ className="ml-auto accent-teal-500"
312
+ />
313
+ </label>
314
+ </div>
315
+ );
316
+ }
@@ -215,12 +215,12 @@ export function BCFTopicDetail({
215
215
  <img
216
216
  src={vp.snapshot}
217
217
  alt="Viewpoint"
218
- className="w-full aspect-video object-cover cursor-pointer hover:opacity-90 transition-opacity"
218
+ className="w-full max-h-48 object-contain bg-muted cursor-pointer hover:opacity-90 transition-opacity"
219
219
  onClick={() => onActivateViewpoint(vp)}
220
220
  />
221
221
  ) : (
222
222
  <div
223
- className="w-full aspect-video bg-muted flex items-center justify-center cursor-pointer hover:bg-muted/80 transition-colors"
223
+ className="w-full aspect-video bg-muted flex items-center justify-center cursor-pointer hover:bg-muted/80 transition-colors min-h-[120px]"
224
224
  onClick={() => onActivateViewpoint(vp)}
225
225
  >
226
226
  <Camera className="h-6 w-6 text-muted-foreground" />
@@ -294,7 +294,7 @@ export function BCFTopicDetail({
294
294
  <img
295
295
  src={associatedViewpoint.snapshot}
296
296
  alt="Associated viewpoint"
297
- className="w-full h-16 object-cover"
297
+ className="w-full max-h-24 object-contain bg-muted"
298
298
  />
299
299
  </div>
300
300
  )}
@@ -327,7 +327,7 @@ export function BCFTopicDetail({
327
327
  <img
328
328
  src={selectedViewpoint.snapshot}
329
329
  alt="Selected viewpoint"
330
- className="w-10 h-10 object-cover rounded"
330
+ className="w-12 h-10 object-contain rounded bg-muted"
331
331
  />
332
332
  )}
333
333
  <div className="flex-1 min-w-0">