@ifc-lite/viewer 1.17.2 → 1.17.4

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 (520) hide show
  1. package/.turbo/turbo-build.log +34 -24
  2. package/.turbo/turbo-typecheck.log +1 -42
  3. package/CHANGELOG.md +24 -0
  4. package/DESKTOP_CONTRACT_VERSION +1 -0
  5. package/dist/assets/arrow-CZ5kQ26f.js +20 -0
  6. package/dist/assets/basketViewActivator-BmnNtVfZ.js +1 -0
  7. package/dist/assets/bcf-DOG9_WPX.js +281 -0
  8. package/dist/assets/{browser-BDShTXzi.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-gWfpdfYe.js +257 -0
  12. package/dist/assets/epsg-index.generated-BjJrt_0S.js +1 -0
  13. package/dist/assets/exporters-ChAtBmlj.js +80367 -0
  14. package/dist/assets/geometry.worker-BQ0rzNo-.js +1 -0
  15. package/dist/assets/ids-B4jTqB1O.js +1 -0
  16. package/dist/assets/ifc-lite_bg-BX4E7TX8.wasm +0 -0
  17. package/dist/assets/index-Co8E2-FE.js +106013 -0
  18. package/dist/assets/index-DckuDqlv.css +1 -0
  19. package/dist/assets/lens-CSASnhAL.js +1 -0
  20. package/dist/assets/maplibre-gl-CGLcoNXc.js +811 -0
  21. package/dist/assets/native-bridge-BRvbckFQ.js +429 -0
  22. package/dist/assets/{arrow2-bb-jcVEo.js → parquet-CEXmQNRO.js} +2 -2
  23. package/dist/assets/sandbox-DZiNLNMk.js +5933 -0
  24. package/dist/assets/server-client-BV8zHZ7Y.js +626 -0
  25. package/dist/assets/tauri-core-stub-D8Fa-u43.js +1 -0
  26. package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +1 -0
  27. package/dist/assets/tauri-fs-stub-BdeRC7aK.js +1 -0
  28. package/dist/assets/wasm-bridge-g01g7T9b.js +1 -0
  29. package/dist/assets/zip-DBEtpeu6.js +12 -0
  30. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json +1 -0
  31. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json +1 -0
  32. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json +1 -0
  33. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json +1 -0
  34. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json +1 -0
  35. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json +1 -0
  36. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json +1 -0
  37. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json +1 -0
  38. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json +1 -0
  39. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json +1 -0
  40. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json +1 -0
  41. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json +1 -0
  42. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json +1 -0
  43. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json +1 -0
  44. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json +1 -0
  45. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json +1 -0
  46. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json +1 -0
  47. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json +1 -0
  48. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json +1 -0
  49. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json +1 -0
  50. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json +1 -0
  51. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json +1 -0
  52. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json +1 -0
  53. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json +1 -0
  54. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json +1 -0
  55. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json +1 -0
  56. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json +1 -0
  57. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json +1 -0
  58. package/dist/cesium/Assets/Images/bing_maps_credit.png +0 -0
  59. package/dist/cesium/Assets/Images/cesium_credit.png +0 -0
  60. package/dist/cesium/Assets/Images/google_earth_credit.png +0 -0
  61. package/dist/cesium/Assets/Images/ion-credit.png +0 -0
  62. package/dist/cesium/Assets/Textures/LensFlare/DirtMask.jpg +0 -0
  63. package/dist/cesium/Assets/Textures/LensFlare/StarBurst.jpg +0 -0
  64. package/dist/cesium/Assets/Textures/NaturalEarthII/0/0/0.jpg +0 -0
  65. package/dist/cesium/Assets/Textures/NaturalEarthII/0/1/0.jpg +0 -0
  66. package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/0.jpg +0 -0
  67. package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/1.jpg +0 -0
  68. package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/0.jpg +0 -0
  69. package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/1.jpg +0 -0
  70. package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/0.jpg +0 -0
  71. package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/1.jpg +0 -0
  72. package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/0.jpg +0 -0
  73. package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/1.jpg +0 -0
  74. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/0.jpg +0 -0
  75. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/1.jpg +0 -0
  76. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/2.jpg +0 -0
  77. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/3.jpg +0 -0
  78. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/0.jpg +0 -0
  79. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/1.jpg +0 -0
  80. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/2.jpg +0 -0
  81. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/3.jpg +0 -0
  82. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/0.jpg +0 -0
  83. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/1.jpg +0 -0
  84. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/2.jpg +0 -0
  85. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/3.jpg +0 -0
  86. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/0.jpg +0 -0
  87. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/1.jpg +0 -0
  88. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/2.jpg +0 -0
  89. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/3.jpg +0 -0
  90. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/0.jpg +0 -0
  91. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/1.jpg +0 -0
  92. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/2.jpg +0 -0
  93. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/3.jpg +0 -0
  94. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/0.jpg +0 -0
  95. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/1.jpg +0 -0
  96. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/2.jpg +0 -0
  97. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/3.jpg +0 -0
  98. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/0.jpg +0 -0
  99. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/1.jpg +0 -0
  100. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/2.jpg +0 -0
  101. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/3.jpg +0 -0
  102. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/0.jpg +0 -0
  103. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/1.jpg +0 -0
  104. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/2.jpg +0 -0
  105. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/3.jpg +0 -0
  106. package/dist/cesium/Assets/Textures/NaturalEarthII/tilemapresource.xml +14 -0
  107. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mx.jpg +0 -0
  108. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_my.jpg +0 -0
  109. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mz.jpg +0 -0
  110. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_px.jpg +0 -0
  111. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_py.jpg +0 -0
  112. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_pz.jpg +0 -0
  113. package/dist/cesium/Assets/Textures/maki/airfield.png +0 -0
  114. package/dist/cesium/Assets/Textures/maki/airport.png +0 -0
  115. package/dist/cesium/Assets/Textures/maki/alcohol-shop.png +0 -0
  116. package/dist/cesium/Assets/Textures/maki/america-football.png +0 -0
  117. package/dist/cesium/Assets/Textures/maki/art-gallery.png +0 -0
  118. package/dist/cesium/Assets/Textures/maki/bakery.png +0 -0
  119. package/dist/cesium/Assets/Textures/maki/bank.png +0 -0
  120. package/dist/cesium/Assets/Textures/maki/bar.png +0 -0
  121. package/dist/cesium/Assets/Textures/maki/baseball.png +0 -0
  122. package/dist/cesium/Assets/Textures/maki/basketball.png +0 -0
  123. package/dist/cesium/Assets/Textures/maki/beer.png +0 -0
  124. package/dist/cesium/Assets/Textures/maki/bicycle.png +0 -0
  125. package/dist/cesium/Assets/Textures/maki/building.png +0 -0
  126. package/dist/cesium/Assets/Textures/maki/bus.png +0 -0
  127. package/dist/cesium/Assets/Textures/maki/cafe.png +0 -0
  128. package/dist/cesium/Assets/Textures/maki/camera.png +0 -0
  129. package/dist/cesium/Assets/Textures/maki/campsite.png +0 -0
  130. package/dist/cesium/Assets/Textures/maki/car.png +0 -0
  131. package/dist/cesium/Assets/Textures/maki/cemetery.png +0 -0
  132. package/dist/cesium/Assets/Textures/maki/cesium.png +0 -0
  133. package/dist/cesium/Assets/Textures/maki/chemist.png +0 -0
  134. package/dist/cesium/Assets/Textures/maki/cinema.png +0 -0
  135. package/dist/cesium/Assets/Textures/maki/circle-stroked.png +0 -0
  136. package/dist/cesium/Assets/Textures/maki/circle.png +0 -0
  137. package/dist/cesium/Assets/Textures/maki/city.png +0 -0
  138. package/dist/cesium/Assets/Textures/maki/clothing-store.png +0 -0
  139. package/dist/cesium/Assets/Textures/maki/college.png +0 -0
  140. package/dist/cesium/Assets/Textures/maki/commercial.png +0 -0
  141. package/dist/cesium/Assets/Textures/maki/cricket.png +0 -0
  142. package/dist/cesium/Assets/Textures/maki/cross.png +0 -0
  143. package/dist/cesium/Assets/Textures/maki/dam.png +0 -0
  144. package/dist/cesium/Assets/Textures/maki/danger.png +0 -0
  145. package/dist/cesium/Assets/Textures/maki/disability.png +0 -0
  146. package/dist/cesium/Assets/Textures/maki/dog-park.png +0 -0
  147. package/dist/cesium/Assets/Textures/maki/embassy.png +0 -0
  148. package/dist/cesium/Assets/Textures/maki/emergency-telephone.png +0 -0
  149. package/dist/cesium/Assets/Textures/maki/entrance.png +0 -0
  150. package/dist/cesium/Assets/Textures/maki/farm.png +0 -0
  151. package/dist/cesium/Assets/Textures/maki/fast-food.png +0 -0
  152. package/dist/cesium/Assets/Textures/maki/ferry.png +0 -0
  153. package/dist/cesium/Assets/Textures/maki/fire-station.png +0 -0
  154. package/dist/cesium/Assets/Textures/maki/fuel.png +0 -0
  155. package/dist/cesium/Assets/Textures/maki/garden.png +0 -0
  156. package/dist/cesium/Assets/Textures/maki/gift.png +0 -0
  157. package/dist/cesium/Assets/Textures/maki/golf.png +0 -0
  158. package/dist/cesium/Assets/Textures/maki/grocery.png +0 -0
  159. package/dist/cesium/Assets/Textures/maki/hairdresser.png +0 -0
  160. package/dist/cesium/Assets/Textures/maki/harbor.png +0 -0
  161. package/dist/cesium/Assets/Textures/maki/heart.png +0 -0
  162. package/dist/cesium/Assets/Textures/maki/heliport.png +0 -0
  163. package/dist/cesium/Assets/Textures/maki/hospital.png +0 -0
  164. package/dist/cesium/Assets/Textures/maki/ice-cream.png +0 -0
  165. package/dist/cesium/Assets/Textures/maki/industrial.png +0 -0
  166. package/dist/cesium/Assets/Textures/maki/land-use.png +0 -0
  167. package/dist/cesium/Assets/Textures/maki/laundry.png +0 -0
  168. package/dist/cesium/Assets/Textures/maki/library.png +0 -0
  169. package/dist/cesium/Assets/Textures/maki/lighthouse.png +0 -0
  170. package/dist/cesium/Assets/Textures/maki/lodging.png +0 -0
  171. package/dist/cesium/Assets/Textures/maki/logging.png +0 -0
  172. package/dist/cesium/Assets/Textures/maki/london-underground.png +0 -0
  173. package/dist/cesium/Assets/Textures/maki/marker-stroked.png +0 -0
  174. package/dist/cesium/Assets/Textures/maki/marker.png +0 -0
  175. package/dist/cesium/Assets/Textures/maki/minefield.png +0 -0
  176. package/dist/cesium/Assets/Textures/maki/mobilephone.png +0 -0
  177. package/dist/cesium/Assets/Textures/maki/monument.png +0 -0
  178. package/dist/cesium/Assets/Textures/maki/museum.png +0 -0
  179. package/dist/cesium/Assets/Textures/maki/music.png +0 -0
  180. package/dist/cesium/Assets/Textures/maki/oil-well.png +0 -0
  181. package/dist/cesium/Assets/Textures/maki/park.png +0 -0
  182. package/dist/cesium/Assets/Textures/maki/park2.png +0 -0
  183. package/dist/cesium/Assets/Textures/maki/parking-garage.png +0 -0
  184. package/dist/cesium/Assets/Textures/maki/parking.png +0 -0
  185. package/dist/cesium/Assets/Textures/maki/pharmacy.png +0 -0
  186. package/dist/cesium/Assets/Textures/maki/pitch.png +0 -0
  187. package/dist/cesium/Assets/Textures/maki/place-of-worship.png +0 -0
  188. package/dist/cesium/Assets/Textures/maki/playground.png +0 -0
  189. package/dist/cesium/Assets/Textures/maki/police.png +0 -0
  190. package/dist/cesium/Assets/Textures/maki/polling-place.png +0 -0
  191. package/dist/cesium/Assets/Textures/maki/post.png +0 -0
  192. package/dist/cesium/Assets/Textures/maki/prison.png +0 -0
  193. package/dist/cesium/Assets/Textures/maki/rail-above.png +0 -0
  194. package/dist/cesium/Assets/Textures/maki/rail-light.png +0 -0
  195. package/dist/cesium/Assets/Textures/maki/rail-metro.png +0 -0
  196. package/dist/cesium/Assets/Textures/maki/rail-underground.png +0 -0
  197. package/dist/cesium/Assets/Textures/maki/rail.png +0 -0
  198. package/dist/cesium/Assets/Textures/maki/religious-christian.png +0 -0
  199. package/dist/cesium/Assets/Textures/maki/religious-jewish.png +0 -0
  200. package/dist/cesium/Assets/Textures/maki/religious-muslim.png +0 -0
  201. package/dist/cesium/Assets/Textures/maki/restaurant.png +0 -0
  202. package/dist/cesium/Assets/Textures/maki/roadblock.png +0 -0
  203. package/dist/cesium/Assets/Textures/maki/rocket.png +0 -0
  204. package/dist/cesium/Assets/Textures/maki/school.png +0 -0
  205. package/dist/cesium/Assets/Textures/maki/scooter.png +0 -0
  206. package/dist/cesium/Assets/Textures/maki/shop.png +0 -0
  207. package/dist/cesium/Assets/Textures/maki/skiing.png +0 -0
  208. package/dist/cesium/Assets/Textures/maki/slaughterhouse.png +0 -0
  209. package/dist/cesium/Assets/Textures/maki/soccer.png +0 -0
  210. package/dist/cesium/Assets/Textures/maki/square-stroked.png +0 -0
  211. package/dist/cesium/Assets/Textures/maki/square.png +0 -0
  212. package/dist/cesium/Assets/Textures/maki/star-stroked.png +0 -0
  213. package/dist/cesium/Assets/Textures/maki/star.png +0 -0
  214. package/dist/cesium/Assets/Textures/maki/suitcase.png +0 -0
  215. package/dist/cesium/Assets/Textures/maki/swimming.png +0 -0
  216. package/dist/cesium/Assets/Textures/maki/telephone.png +0 -0
  217. package/dist/cesium/Assets/Textures/maki/tennis.png +0 -0
  218. package/dist/cesium/Assets/Textures/maki/theatre.png +0 -0
  219. package/dist/cesium/Assets/Textures/maki/toilets.png +0 -0
  220. package/dist/cesium/Assets/Textures/maki/town-hall.png +0 -0
  221. package/dist/cesium/Assets/Textures/maki/town.png +0 -0
  222. package/dist/cesium/Assets/Textures/maki/triangle-stroked.png +0 -0
  223. package/dist/cesium/Assets/Textures/maki/triangle.png +0 -0
  224. package/dist/cesium/Assets/Textures/maki/village.png +0 -0
  225. package/dist/cesium/Assets/Textures/maki/warehouse.png +0 -0
  226. package/dist/cesium/Assets/Textures/maki/waste-basket.png +0 -0
  227. package/dist/cesium/Assets/Textures/maki/water.png +0 -0
  228. package/dist/cesium/Assets/Textures/maki/wetland.png +0 -0
  229. package/dist/cesium/Assets/Textures/maki/zoo.png +0 -0
  230. package/dist/cesium/Assets/Textures/moonSmall.jpg +0 -0
  231. package/dist/cesium/Assets/Textures/pin.svg +1 -0
  232. package/dist/cesium/Assets/Textures/waterNormals.jpg +0 -0
  233. package/dist/cesium/Assets/Textures/waterNormalsSmall.jpg +0 -0
  234. package/dist/cesium/Assets/approximateTerrainHeights.json +1 -0
  235. package/dist/cesium/ThirdParty/Workers/package.json +1 -0
  236. package/dist/cesium/ThirdParty/Workers/zip-web-worker.js +1 -0
  237. package/dist/cesium/ThirdParty/basis_transcoder.wasm +0 -0
  238. package/dist/cesium/ThirdParty/draco_decoder.wasm +0 -0
  239. package/dist/cesium/ThirdParty/google-earth-dbroot-parser.js +1 -0
  240. package/dist/cesium/ThirdParty/wasm_splats_bg.wasm +0 -0
  241. package/dist/cesium/ThirdParty/zip-module.wasm +0 -0
  242. package/dist/cesium/Widgets/Animation/Animation.css +127 -0
  243. package/dist/cesium/Widgets/Animation/lighter.css +70 -0
  244. package/dist/cesium/Widgets/BaseLayerPicker/BaseLayerPicker.css +108 -0
  245. package/dist/cesium/Widgets/BaseLayerPicker/lighter.css +22 -0
  246. package/dist/cesium/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css +102 -0
  247. package/dist/cesium/Widgets/CesiumInspector/CesiumInspector.css +113 -0
  248. package/dist/cesium/Widgets/CesiumWidget/CesiumWidget.css +119 -0
  249. package/dist/cesium/Widgets/CesiumWidget/lighter.css +14 -0
  250. package/dist/cesium/Widgets/FullscreenButton/FullscreenButton.css +8 -0
  251. package/dist/cesium/Widgets/Geocoder/Geocoder.css +70 -0
  252. package/dist/cesium/Widgets/Geocoder/lighter.css +17 -0
  253. package/dist/cesium/Widgets/I3SBuildingSceneLayerExplorer/I3SBuildingSceneLayerExplorer.css +27 -0
  254. package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldHillshade.png +0 -0
  255. package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldImagery.png +0 -0
  256. package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldOcean.png +0 -0
  257. package/dist/cesium/Widgets/Images/ImageryProviders/azureAerial.png +0 -0
  258. package/dist/cesium/Widgets/Images/ImageryProviders/azureRoads.png +0 -0
  259. package/dist/cesium/Widgets/Images/ImageryProviders/bingAerial.png +0 -0
  260. package/dist/cesium/Widgets/Images/ImageryProviders/bingAerialLabels.png +0 -0
  261. package/dist/cesium/Widgets/Images/ImageryProviders/bingRoads.png +0 -0
  262. package/dist/cesium/Widgets/Images/ImageryProviders/blueMarble.png +0 -0
  263. package/dist/cesium/Widgets/Images/ImageryProviders/earthAtNight.png +0 -0
  264. package/dist/cesium/Widgets/Images/ImageryProviders/googleContour.png +0 -0
  265. package/dist/cesium/Widgets/Images/ImageryProviders/googleRoadmap.png +0 -0
  266. package/dist/cesium/Widgets/Images/ImageryProviders/googleSatellite.png +0 -0
  267. package/dist/cesium/Widgets/Images/ImageryProviders/googleSatelliteLabels.png +0 -0
  268. package/dist/cesium/Widgets/Images/ImageryProviders/mapQuestOpenStreetMap.png +0 -0
  269. package/dist/cesium/Widgets/Images/ImageryProviders/mapboxSatellite.png +0 -0
  270. package/dist/cesium/Widgets/Images/ImageryProviders/mapboxStreets.png +0 -0
  271. package/dist/cesium/Widgets/Images/ImageryProviders/mapboxTerrain.png +0 -0
  272. package/dist/cesium/Widgets/Images/ImageryProviders/naturalEarthII.png +0 -0
  273. package/dist/cesium/Widgets/Images/ImageryProviders/openStreetMap.png +0 -0
  274. package/dist/cesium/Widgets/Images/ImageryProviders/sentinel-2.png +0 -0
  275. package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmooth.png +0 -0
  276. package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmoothDark.png +0 -0
  277. package/dist/cesium/Widgets/Images/ImageryProviders/stamenToner.png +0 -0
  278. package/dist/cesium/Widgets/Images/ImageryProviders/stamenWatercolor.png +0 -0
  279. package/dist/cesium/Widgets/Images/NavigationHelp/Mouse.svg +84 -0
  280. package/dist/cesium/Widgets/Images/NavigationHelp/MouseLeft.svg +76 -0
  281. package/dist/cesium/Widgets/Images/NavigationHelp/MouseMiddle.svg +76 -0
  282. package/dist/cesium/Widgets/Images/NavigationHelp/MouseRight.svg +76 -0
  283. package/dist/cesium/Widgets/Images/NavigationHelp/Touch.svg +120 -0
  284. package/dist/cesium/Widgets/Images/NavigationHelp/TouchDrag.svg +129 -0
  285. package/dist/cesium/Widgets/Images/NavigationHelp/TouchRotate.svg +76 -0
  286. package/dist/cesium/Widgets/Images/NavigationHelp/TouchTilt.svg +135 -0
  287. package/dist/cesium/Widgets/Images/NavigationHelp/TouchZoom.svg +74 -0
  288. package/dist/cesium/Widgets/Images/TerrainProviders/CesiumWorldTerrain.png +0 -0
  289. package/dist/cesium/Widgets/Images/TerrainProviders/Ellipsoid.png +0 -0
  290. package/dist/cesium/Widgets/Images/TimelineIcons.png +0 -0
  291. package/dist/cesium/Widgets/Images/info-loading.gif +0 -0
  292. package/dist/cesium/Widgets/InfoBox/InfoBox.css +92 -0
  293. package/dist/cesium/Widgets/InfoBox/InfoBoxDescription.css +178 -0
  294. package/dist/cesium/Widgets/NavigationHelpButton/NavigationHelpButton.css +93 -0
  295. package/dist/cesium/Widgets/NavigationHelpButton/lighter.css +38 -0
  296. package/dist/cesium/Widgets/PerformanceWatchdog/PerformanceWatchdog.css +15 -0
  297. package/dist/cesium/Widgets/ProjectionPicker/ProjectionPicker.css +38 -0
  298. package/dist/cesium/Widgets/SceneModePicker/SceneModePicker.css +56 -0
  299. package/dist/cesium/Widgets/SelectionIndicator/SelectionIndicator.css +20 -0
  300. package/dist/cesium/Widgets/Timeline/Timeline.css +103 -0
  301. package/dist/cesium/Widgets/Timeline/lighter.css +23 -0
  302. package/dist/cesium/Widgets/VRButton/VRButton.css +8 -0
  303. package/dist/cesium/Widgets/Viewer/Viewer.css +107 -0
  304. package/dist/cesium/Widgets/VoxelInspector/VoxelInspector.css +16 -0
  305. package/dist/cesium/Widgets/lighter.css +237 -0
  306. package/dist/cesium/Widgets/lighterShared.css +46 -0
  307. package/dist/cesium/Widgets/shared.css +103 -0
  308. package/dist/cesium/Widgets/widgets.css +1342 -0
  309. package/dist/cesium/Workers/chunk-23ZQ2IVV.js +29 -0
  310. package/dist/cesium/Workers/chunk-2EQO3Q56.js +26 -0
  311. package/dist/cesium/Workers/chunk-2MJIIVP4.js +26 -0
  312. package/dist/cesium/Workers/chunk-2TE5NTVD.js +26 -0
  313. package/dist/cesium/Workers/chunk-2ZBHLJST.js +26 -0
  314. package/dist/cesium/Workers/chunk-5TJMAQVL.js +26 -0
  315. package/dist/cesium/Workers/chunk-6BD4U3VO.js +26 -0
  316. package/dist/cesium/Workers/chunk-7TVGLKQF.js +26 -0
  317. package/dist/cesium/Workers/chunk-BTSYJ5XU.js +26 -0
  318. package/dist/cesium/Workers/chunk-BXMEEOCS.js +63 -0
  319. package/dist/cesium/Workers/chunk-BYLCY7GP.js +29 -0
  320. package/dist/cesium/Workers/chunk-CTHM3W6I.js +26 -0
  321. package/dist/cesium/Workers/chunk-CUUSNIVQ.js +26 -0
  322. package/dist/cesium/Workers/chunk-E3JOOS3S.js +26 -0
  323. package/dist/cesium/Workers/chunk-E7KYDCM5.js +26 -0
  324. package/dist/cesium/Workers/chunk-EDVBB7SS.js +27 -0
  325. package/dist/cesium/Workers/chunk-EFBN7QNX.js +26 -0
  326. package/dist/cesium/Workers/chunk-EQ4YRVWL.js +26 -0
  327. package/dist/cesium/Workers/chunk-F6PRE7D6.js +26 -0
  328. package/dist/cesium/Workers/chunk-FC4ZZ65J.js +26 -0
  329. package/dist/cesium/Workers/chunk-FFBVWF2L.js +26 -0
  330. package/dist/cesium/Workers/chunk-GBAA6GVX.js +26 -0
  331. package/dist/cesium/Workers/chunk-ICALLYLG.js +26 -0
  332. package/dist/cesium/Workers/chunk-ILRYTWTP.js +26 -0
  333. package/dist/cesium/Workers/chunk-IRNLBSEJ.js +26 -0
  334. package/dist/cesium/Workers/chunk-IX4VMHEV.js +26 -0
  335. package/dist/cesium/Workers/chunk-L6QHHACZ.js +26 -0
  336. package/dist/cesium/Workers/chunk-LI2ZSORM.js +26 -0
  337. package/dist/cesium/Workers/chunk-LSLE2RL4.js +26 -0
  338. package/dist/cesium/Workers/chunk-M4HLDBCG.js +26 -0
  339. package/dist/cesium/Workers/chunk-MJHHSGEH.js +26 -0
  340. package/dist/cesium/Workers/chunk-NMVKML6W.js +26 -0
  341. package/dist/cesium/Workers/chunk-OCWJRAXS.js +26 -0
  342. package/dist/cesium/Workers/chunk-OIRKANTH.js +26 -0
  343. package/dist/cesium/Workers/chunk-OIT7J4IC.js +26 -0
  344. package/dist/cesium/Workers/chunk-OLZ3FYUM.js +26 -0
  345. package/dist/cesium/Workers/chunk-Q5BPHJQF.js +26 -0
  346. package/dist/cesium/Workers/chunk-QFM5DCMQ.js +26 -0
  347. package/dist/cesium/Workers/chunk-QKUIYMGC.js +28 -0
  348. package/dist/cesium/Workers/chunk-S44JILQT.js +26 -0
  349. package/dist/cesium/Workers/chunk-SLT4J352.js +26 -0
  350. package/dist/cesium/Workers/chunk-SQMIIXB7.js +26 -0
  351. package/dist/cesium/Workers/chunk-TJ4XLGBQ.js +26 -0
  352. package/dist/cesium/Workers/chunk-TNSUQXWK.js +27 -0
  353. package/dist/cesium/Workers/chunk-UBOGZS7F.js +26 -0
  354. package/dist/cesium/Workers/chunk-V3OSTMM6.js +26 -0
  355. package/dist/cesium/Workers/chunk-V7QEYVP3.js +26 -0
  356. package/dist/cesium/Workers/chunk-VUKYSU4H.js +26 -0
  357. package/dist/cesium/Workers/chunk-W37FE5GR.js +26 -0
  358. package/dist/cesium/Workers/chunk-WBOV35NL.js +26 -0
  359. package/dist/cesium/Workers/chunk-WPMZLB3Y.js +26 -0
  360. package/dist/cesium/Workers/chunk-WWWZVEEH.js +26 -0
  361. package/dist/cesium/Workers/chunk-XFIQ5DEQ.js +28 -0
  362. package/dist/cesium/Workers/chunk-XQHLGIO7.js +26 -0
  363. package/dist/cesium/Workers/chunk-XUSCFAVF.js +26 -0
  364. package/dist/cesium/Workers/chunk-YP7I5QBZ.js +26 -0
  365. package/dist/cesium/Workers/chunk-Z3QF2EHT.js +26 -0
  366. package/dist/cesium/Workers/combineGeometry.js +26 -0
  367. package/dist/cesium/Workers/createBoxGeometry.js +26 -0
  368. package/dist/cesium/Workers/createBoxOutlineGeometry.js +26 -0
  369. package/dist/cesium/Workers/createCircleGeometry.js +26 -0
  370. package/dist/cesium/Workers/createCircleOutlineGeometry.js +26 -0
  371. package/dist/cesium/Workers/createCoplanarPolygonGeometry.js +26 -0
  372. package/dist/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
  373. package/dist/cesium/Workers/createCorridorGeometry.js +26 -0
  374. package/dist/cesium/Workers/createCorridorOutlineGeometry.js +26 -0
  375. package/dist/cesium/Workers/createCylinderGeometry.js +26 -0
  376. package/dist/cesium/Workers/createCylinderOutlineGeometry.js +26 -0
  377. package/dist/cesium/Workers/createEllipseGeometry.js +26 -0
  378. package/dist/cesium/Workers/createEllipseOutlineGeometry.js +26 -0
  379. package/dist/cesium/Workers/createEllipsoidGeometry.js +26 -0
  380. package/dist/cesium/Workers/createEllipsoidOutlineGeometry.js +26 -0
  381. package/dist/cesium/Workers/createFrustumGeometry.js +26 -0
  382. package/dist/cesium/Workers/createFrustumOutlineGeometry.js +26 -0
  383. package/dist/cesium/Workers/createGeometry.js +26 -0
  384. package/dist/cesium/Workers/createGroundPolylineGeometry.js +26 -0
  385. package/dist/cesium/Workers/createPlaneGeometry.js +26 -0
  386. package/dist/cesium/Workers/createPlaneOutlineGeometry.js +26 -0
  387. package/dist/cesium/Workers/createPolygonGeometry.js +26 -0
  388. package/dist/cesium/Workers/createPolygonOutlineGeometry.js +26 -0
  389. package/dist/cesium/Workers/createPolylineGeometry.js +26 -0
  390. package/dist/cesium/Workers/createPolylineVolumeGeometry.js +26 -0
  391. package/dist/cesium/Workers/createPolylineVolumeOutlineGeometry.js +26 -0
  392. package/dist/cesium/Workers/createRectangleGeometry.js +26 -0
  393. package/dist/cesium/Workers/createRectangleOutlineGeometry.js +26 -0
  394. package/dist/cesium/Workers/createSimplePolylineGeometry.js +26 -0
  395. package/dist/cesium/Workers/createSphereGeometry.js +26 -0
  396. package/dist/cesium/Workers/createSphereOutlineGeometry.js +26 -0
  397. package/dist/cesium/Workers/createTaskProcessorWorker.js +26 -0
  398. package/dist/cesium/Workers/createVectorTileClampedPolylines.js +26 -0
  399. package/dist/cesium/Workers/createVectorTileGeometries.js +26 -0
  400. package/dist/cesium/Workers/createVectorTilePoints.js +26 -0
  401. package/dist/cesium/Workers/createVectorTilePolygons.js +26 -0
  402. package/dist/cesium/Workers/createVectorTilePolylines.js +26 -0
  403. package/dist/cesium/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -0
  404. package/dist/cesium/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +26 -0
  405. package/dist/cesium/Workers/createVerticesFromHeightmap.js +26 -0
  406. package/dist/cesium/Workers/createVerticesFromQuantizedTerrainMesh.js +26 -0
  407. package/dist/cesium/Workers/createWallGeometry.js +26 -0
  408. package/dist/cesium/Workers/createWallOutlineGeometry.js +26 -0
  409. package/dist/cesium/Workers/decodeDraco.js +26 -0
  410. package/dist/cesium/Workers/decodeGoogleEarthEnterprisePacket.js +26 -0
  411. package/dist/cesium/Workers/decodeI3S.js +26 -0
  412. package/dist/cesium/Workers/gaussianSplatSorter.js +26 -0
  413. package/dist/cesium/Workers/gaussianSplatTextureGenerator.js +26 -0
  414. package/dist/cesium/Workers/incrementallyBuildTerrainPicker.js +26 -0
  415. package/dist/cesium/Workers/transcodeKTX2.js +56 -0
  416. package/dist/cesium/Workers/transferTypedArrayTest.js +26 -0
  417. package/dist/cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
  418. package/dist/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -0
  419. package/dist/index.html +13 -2
  420. package/package.json +27 -19
  421. package/src/App.tsx +1 -17
  422. package/src/components/viewer/BCFPanel.tsx +46 -4
  423. package/src/components/viewer/CesiumOverlay.tsx +672 -0
  424. package/src/components/viewer/CesiumSettingsDialog.tsx +100 -0
  425. package/src/components/viewer/ChatPanel.tsx +54 -16
  426. package/src/components/viewer/CommandPalette.tsx +6 -1
  427. package/src/components/viewer/DesktopEntitlementBanner.tsx +74 -0
  428. package/src/components/viewer/ExportChangesButton.tsx +24 -4
  429. package/src/components/viewer/ExportDialog.tsx +38 -9
  430. package/src/components/viewer/HierarchyPanel.tsx +202 -6
  431. package/src/components/viewer/IDSPanel.tsx +52 -3
  432. package/src/components/viewer/KeyboardShortcutsDialog.tsx +1 -1
  433. package/src/components/viewer/MainToolbar.tsx +353 -27
  434. package/src/components/viewer/PropertiesPanel.tsx +306 -131
  435. package/src/components/viewer/ScriptPanel.tsx +34 -8
  436. package/src/components/viewer/Section2DPanel.tsx +3 -2
  437. package/src/components/viewer/SettingsPage.tsx +430 -0
  438. package/src/components/viewer/StatusBar.tsx +17 -1
  439. package/src/components/viewer/UpgradePage.tsx +6 -4
  440. package/src/components/viewer/ViewerLayout.tsx +47 -6
  441. package/src/components/viewer/Viewport.tsx +49 -8
  442. package/src/components/viewer/ViewportContainer.tsx +285 -32
  443. package/src/components/viewer/ViewportOverlays.tsx +129 -27
  444. package/src/components/viewer/hierarchy/treeDataBuilder.ts +2 -1
  445. package/src/components/viewer/properties/EpsgLookupDialog.tsx +418 -0
  446. package/src/components/viewer/properties/GeoreferencingPanel.tsx +703 -0
  447. package/src/components/viewer/properties/LocationMap.tsx +730 -0
  448. package/src/components/viewer/properties/ModelMetadataPanel.tsx +3 -70
  449. package/src/components/viewer/selectionHandlers.ts +4 -3
  450. package/src/components/viewer/useAnimationLoop.ts +4 -0
  451. package/src/components/viewer/useGeometryStreaming.ts +127 -40
  452. package/src/components/viewer/useMouseControls.ts +4 -1
  453. package/src/hooks/bcfIdLookup.ts +13 -11
  454. package/src/hooks/ids/idsColorSystem.ts +3 -8
  455. package/src/hooks/ingest/viewerModelIngest.ts +275 -0
  456. package/src/hooks/useIDS.ts +32 -17
  457. package/src/hooks/useIfc.ts +7 -1
  458. package/src/hooks/useIfcCache.ts +28 -15
  459. package/src/hooks/useIfcFederation.ts +59 -227
  460. package/src/hooks/useIfcLoader.ts +1656 -130
  461. package/src/hooks/useIfcServer.ts +0 -69
  462. package/src/lib/desktop/ClerkDesktopEntitlementSync.tsx +175 -0
  463. package/src/lib/desktop/desktopEntitlementEvents.ts +39 -0
  464. package/src/lib/desktop-entitlement.ts +45 -0
  465. package/src/lib/desktop-product.ts +124 -0
  466. package/src/lib/geo/cesium-bridge.ts +310 -0
  467. package/src/lib/geo/kmz-exporter.ts +112 -0
  468. package/src/lib/geo/reproject.ts +370 -0
  469. package/src/lib/lens/adapter.ts +3 -1
  470. package/src/lib/recent-files.ts +2 -1
  471. package/src/main.tsx +1 -0
  472. package/src/sdk/adapters/export-adapter.ts +14 -1
  473. package/src/sdk/adapters/viewer-adapter.ts +5 -9
  474. package/src/sdk/adapters/visibility-adapter.ts +6 -9
  475. package/src/services/analysis-extensions.ts +125 -0
  476. package/src/services/app-navigation.ts +13 -0
  477. package/src/services/bsdd.ts +53 -4
  478. package/src/services/cacheService.ts +1 -1
  479. package/src/services/desktop-cache.ts +43 -0
  480. package/src/services/desktop-export.ts +77 -0
  481. package/src/services/desktop-harness.ts +196 -0
  482. package/src/services/desktop-logger.ts +20 -0
  483. package/src/services/desktop-native-metadata.ts +207 -0
  484. package/src/services/desktop-panel-actions.ts +43 -0
  485. package/src/services/desktop-preferences.ts +44 -0
  486. package/src/services/file-dialog.ts +147 -0
  487. package/src/services/tauri-core-stub.ts +7 -0
  488. package/src/services/tauri-dialog-stub.ts +7 -0
  489. package/src/services/tauri-fs-stub.ts +7 -0
  490. package/src/store/basketVisibleSet.ts +3 -4
  491. package/src/store/globalId.ts +79 -0
  492. package/src/store/index.ts +41 -2
  493. package/src/store/slices/cesiumSlice.ts +122 -0
  494. package/src/store/slices/chatSlice.ts +5 -1
  495. package/src/store/slices/dataSlice.ts +139 -28
  496. package/src/store/slices/desktopEntitlementSlice.ts +86 -0
  497. package/src/store/slices/loadingSlice.ts +14 -2
  498. package/src/store/slices/modelSlice.ts +58 -3
  499. package/src/store/slices/mutationSlice.ts +178 -0
  500. package/src/store/slices/pinboardSlice.ts +4 -8
  501. package/src/store/types.ts +96 -2
  502. package/src/store.ts +1 -1
  503. package/src/utils/desktopModelSnapshot.ts +358 -0
  504. package/src/utils/ifcConfig.ts +6 -1
  505. package/src/utils/nativeSpatialDataStore.ts +250 -0
  506. package/src/utils/serverDataModel.ts +4 -0
  507. package/src/utils/spatialHierarchy.ts +10 -11
  508. package/vite.config.ts +41 -0
  509. package/dist/assets/Arrow.dom-BhOg9lpn.js +0 -20
  510. package/dist/assets/arrow2_bg-BlXl-cSQ.js +0 -1
  511. package/dist/assets/basketViewActivator-BRG5DBmM.js +0 -1
  512. package/dist/assets/desktop-cache-oPzaWXYE.js +0 -1
  513. package/dist/assets/geometry.worker-kgiT_Qhh.js +0 -1
  514. package/dist/assets/ifc-lite_bg-FNRmpSvM.wasm +0 -0
  515. package/dist/assets/index-B1Ecw4AU.js +0 -189756
  516. package/dist/assets/index-Ba4eoTe7.css +0 -1
  517. package/dist/assets/index-CrgYBjTn.js +0 -229
  518. package/dist/assets/module-6F3E5H7Y-tx0BadV3.js +0 -6
  519. package/dist/assets/native-bridge-Crsb7TKz.js +0 -111
  520. package/dist/assets/wasm-bridge-mJUhb7uk.js +0 -1
@@ -2,19 +2,26 @@
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';
14
+ import { toGlobalIdFromModels } from '@/store/globalId';
13
15
  import { collectIfcBuildingStoreyElementsWithIfcSpace } from '@/store/basketVisibleSet';
14
16
  import { useIfc } from '@/hooks/useIfc';
15
17
  import { useWebGPU } from '@/hooks/useWebGPU';
16
- 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 '@/utils/ifcConfig';
22
+ import { Upload, MousePointer, Layers, Info, Command, AlertTriangle, ChevronDown, ExternalLink, Plus, Clock3 } from 'lucide-react';
17
23
  import type { MeshData, CoordinateInfo, GeometryResult } from '@ifc-lite/geometry';
24
+ import { extractGeoreferencingOnDemand, type IfcDataStore, type MapConversion, type ProjectedCRS } from '@ifc-lite/parser';
18
25
 
19
26
  const ZERO_VEC3 = { x: 0, y: 0, z: 0 };
20
27
  const DEFAULT_COORDINATE_INFO: CoordinateInfo = {
@@ -25,20 +32,41 @@ const DEFAULT_COORDINATE_INFO: CoordinateInfo = {
25
32
  };
26
33
 
27
34
  export function ViewportContainer() {
28
- const { geometryResult, ifcDataStore, loadFile, loading, models, clearAllModels, loadFilesSequentially } = useIfc();
35
+ const { loadFile, loading, clearAllModels, loadFilesSequentially } = useIfc();
36
+ const releaseGeometryMemory = useViewerStore((s) => s.releaseGeometryMemory);
29
37
  const selectedStoreys = useViewerStore((s) => s.selectedStoreys);
30
38
  const typeVisibility = useViewerStore((s) => s.typeVisibility);
31
39
  const isolatedEntities = useViewerStore((s) => s.isolatedEntities);
32
40
  const classFilter = useViewerStore((s) => s.classFilter);
33
- // Multi-model support: get all loaded models from store (for merged geometry)
34
- const storeModels = useViewerStore((s) => s.models);
35
41
  const resetViewerState = useViewerStore((s) => s.resetViewerState);
36
42
  const bcfOverlayVisible = useViewerStore((s) => s.bcfOverlayVisible);
43
+ const cesiumEnabled = useViewerStore((s) => s.cesiumEnabled);
44
+ const georefMutations = useViewerStore((s) => s.georefMutations);
45
+ const setCesiumSourceModelId = useViewerStore((s) => s.setCesiumSourceModelId);
46
+ // Subscribe to mutationVersion so Cesium reacts to georef edits
47
+ const mutationVersion = useViewerStore((s) => s.mutationVersion);
37
48
  const fileInputRef = useRef<HTMLInputElement>(null);
38
49
  const [isDragging, setIsDragging] = useState(false);
39
50
  const [showTroubleshooting, setShowTroubleshooting] = useState(false);
51
+ const [recentFiles, setRecentFiles] = useState<RecentFileEntry[]>([]);
40
52
  const webgpu = useWebGPU();
41
53
 
54
+ const viewerStoreApi = getViewerStoreApi();
55
+ const viewportStoreState = useSyncExternalStore(
56
+ viewerStoreApi.subscribe,
57
+ viewerStoreApi.getState,
58
+ viewerStoreApi.getState,
59
+ );
60
+
61
+ const {
62
+ geometryResult,
63
+ ifcDataStore,
64
+ models,
65
+ boundedGeometryMode,
66
+ geometryUpdateTick,
67
+ } = viewportStoreState;
68
+ const storeModels = models;
69
+
42
70
  // Check if we have models loaded (for determining add vs replace behavior)
43
71
  const hasModelsLoaded = models.size > 0 || (geometryResult?.meshes && geometryResult.meshes.length > 0);
44
72
 
@@ -52,39 +80,87 @@ export function ViewportContainer() {
52
80
  return map;
53
81
  }, [storeModels]);
54
82
 
83
+ const mergedCacheRef = useRef<MeshData[]>([]);
84
+ const mergedLengthsRef = useRef<Map<string, number>>(new Map());
85
+ const mergedVisibilityRef = useRef<Map<string, boolean>>(new Map());
86
+
55
87
  // Multi-model: merge geometries from all visible models
56
88
  const mergedGeometryResult = useMemo(() => {
57
- // If we have federated models, merge their visible geometries
58
- if (storeModels.size > 0) {
59
- const allMeshes: MeshData[] = [];
89
+ if (storeModels.size === 1) {
90
+ const firstModel = storeModels.values().next().value;
91
+ if (!firstModel?.visible) {
92
+ return {
93
+ meshes: [],
94
+ totalVertices: 0,
95
+ totalTriangles: 0,
96
+ coordinateInfo: DEFAULT_COORDINATE_INFO,
97
+ } satisfies GeometryResult;
98
+ }
99
+ return firstModel.geometryResult ?? geometryResult;
100
+ }
101
+
102
+ if (storeModels.size > 1) {
60
103
  let totalVertices = 0;
61
104
  let totalTriangles = 0;
62
105
  let mergedCoordinateInfo: CoordinateInfo | undefined;
106
+ let shouldRebuild = false;
63
107
 
64
- for (const [modelId, model] of storeModels) {
65
- // Skip hidden models - this is how model visibility works
66
- if (!model.visible) continue;
108
+ if (mergedLengthsRef.current.size !== storeModels.size) {
109
+ shouldRebuild = true;
110
+ }
67
111
 
112
+ for (const [modelId, model] of storeModels) {
68
113
  const modelGeometry = model.geometryResult;
69
- const modelIndex = modelIdToIndex.get(modelId) ?? 0;
70
- if (modelGeometry?.meshes) {
71
- // Tag each mesh with its modelIndex for selection/highlighting
114
+ const meshCount = model.visible ? (modelGeometry?.meshes.length ?? 0) : 0;
115
+ totalVertices += model.visible ? (modelGeometry?.totalVertices ?? 0) : 0;
116
+ totalTriangles += model.visible ? (modelGeometry?.totalTriangles ?? 0) : 0;
117
+ if (!mergedCoordinateInfo && model.visible && modelGeometry?.coordinateInfo) {
118
+ mergedCoordinateInfo = modelGeometry.coordinateInfo;
119
+ }
120
+
121
+ if (
122
+ mergedVisibilityRef.current.get(modelId) !== model.visible ||
123
+ (mergedLengthsRef.current.get(modelId) ?? 0) > meshCount
124
+ ) {
125
+ shouldRebuild = true;
126
+ }
127
+ }
128
+
129
+ if (shouldRebuild) {
130
+ const rebuilt: MeshData[] = [];
131
+ mergedLengthsRef.current = new Map();
132
+ mergedVisibilityRef.current = new Map();
133
+ for (const [modelId, model] of storeModels) {
134
+ const modelGeometry = model.geometryResult;
135
+ mergedVisibilityRef.current.set(modelId, model.visible);
136
+ const modelIndex = modelIdToIndex.get(modelId) ?? 0;
137
+ if (!model.visible || !modelGeometry?.meshes) {
138
+ mergedLengthsRef.current.set(modelId, 0);
139
+ continue;
140
+ }
72
141
  for (const mesh of modelGeometry.meshes) {
73
- allMeshes.push({ ...mesh, modelIndex });
142
+ rebuilt.push({ ...mesh, modelIndex });
74
143
  }
75
- totalVertices += modelGeometry.totalVertices || 0;
76
- totalTriangles += modelGeometry.totalTriangles || 0;
77
-
78
- // Use first model's coordinate info as base (could be improved to compute union)
79
- if (!mergedCoordinateInfo && modelGeometry.coordinateInfo) {
80
- mergedCoordinateInfo = modelGeometry.coordinateInfo;
144
+ mergedLengthsRef.current.set(modelId, modelGeometry.meshes.length);
145
+ }
146
+ mergedCacheRef.current = rebuilt;
147
+ } else {
148
+ for (const [modelId, model] of storeModels) {
149
+ const modelGeometry = model.geometryResult;
150
+ const modelIndex = modelIdToIndex.get(modelId) ?? 0;
151
+ const previousLength = mergedLengthsRef.current.get(modelId) ?? 0;
152
+ const nextMeshes = model.visible ? (modelGeometry?.meshes ?? []) : [];
153
+ for (let i = previousLength; i < nextMeshes.length; i++) {
154
+ const mesh = nextMeshes[i];
155
+ mergedCacheRef.current.push({ ...mesh, modelIndex });
81
156
  }
157
+ mergedLengthsRef.current.set(modelId, nextMeshes.length);
158
+ mergedVisibilityRef.current.set(modelId, model.visible);
82
159
  }
83
160
  }
84
161
 
85
- // Return merged result (may be empty if all models hidden)
86
162
  return {
87
- meshes: allMeshes,
163
+ meshes: mergedCacheRef.current,
88
164
  totalVertices,
89
165
  totalTriangles,
90
166
  coordinateInfo: mergedCoordinateInfo ?? DEFAULT_COORDINATE_INFO,
@@ -95,6 +171,106 @@ export function ViewportContainer() {
95
171
  return geometryResult;
96
172
  }, [storeModels, geometryResult, modelIdToIndex]);
97
173
 
174
+ // Extract georeferencing info merged with any live mutations (for Cesium overlay).
175
+ // Reacts to: model load, Cesium toggle, and every georef field edit.
176
+ const georef = useMemo(() => {
177
+ if (!cesiumEnabled) return null;
178
+
179
+ // Helper: merge original georef with mutations for a model
180
+ function mergeGeoref(
181
+ originalCRS: ProjectedCRS | undefined,
182
+ originalConv: MapConversion | undefined,
183
+ modelId: string,
184
+ ): { mapConversion: MapConversion; projectedCRS: ProjectedCRS } | null {
185
+ const muts = georefMutations.get(modelId);
186
+ const mutCRS = muts?.projectedCRS;
187
+ const mutConv = muts?.mapConversion;
188
+
189
+ // Build merged ProjectedCRS — mutation fields override originals
190
+ const hasCRS = originalCRS || mutCRS;
191
+ if (!hasCRS) return null;
192
+ const projectedCRS: ProjectedCRS = {
193
+ id: originalCRS?.id ?? 0,
194
+ name: (mutCRS?.name ?? originalCRS?.name ?? '') as string,
195
+ description: mutCRS?.description ?? originalCRS?.description,
196
+ geodeticDatum: mutCRS?.geodeticDatum ?? originalCRS?.geodeticDatum,
197
+ verticalDatum: mutCRS?.verticalDatum ?? originalCRS?.verticalDatum,
198
+ mapProjection: mutCRS?.mapProjection ?? originalCRS?.mapProjection,
199
+ mapZone: mutCRS?.mapZone ?? originalCRS?.mapZone,
200
+ mapUnit: mutCRS?.mapUnit ?? originalCRS?.mapUnit,
201
+ };
202
+
203
+ // Need at least an EPSG name to resolve projection
204
+ if (!projectedCRS.name) return null;
205
+
206
+ // Build merged MapConversion
207
+ const mapConversion: MapConversion = {
208
+ id: originalConv?.id ?? 0,
209
+ sourceCRS: originalConv?.sourceCRS ?? 0,
210
+ targetCRS: originalConv?.targetCRS ?? 0,
211
+ eastings: (mutConv?.eastings ?? originalConv?.eastings ?? 0) as number,
212
+ northings: (mutConv?.northings ?? originalConv?.northings ?? 0) as number,
213
+ orthogonalHeight: (mutConv?.orthogonalHeight ?? originalConv?.orthogonalHeight ?? 0) as number,
214
+ xAxisAbscissa: mutConv?.xAxisAbscissa ?? originalConv?.xAxisAbscissa,
215
+ xAxisOrdinate: mutConv?.xAxisOrdinate ?? originalConv?.xAxisOrdinate,
216
+ scale: mutConv?.scale ?? originalConv?.scale,
217
+ };
218
+
219
+ return { mapConversion, projectedCRS };
220
+ }
221
+
222
+ // Check federated models first
223
+ for (const [modelId, model] of storeModels) {
224
+ const ds = model.ifcDataStore;
225
+ if (!ds) continue;
226
+ const original = extractGeoreferencingOnDemand(ds as IfcDataStore);
227
+ const merged = mergeGeoref(
228
+ original?.projectedCRS,
229
+ original?.mapConversion,
230
+ modelId,
231
+ );
232
+ if (merged) {
233
+ // Return coordinateInfo from the SAME model to avoid mismatched transforms
234
+ const coordInfo = model.geometryResult?.coordinateInfo;
235
+ return { hasGeoreference: true, ...merged, sourceModelId: modelId, coordinateInfo: coordInfo };
236
+ }
237
+ }
238
+
239
+ // Fallback to legacy single-model
240
+ if (ifcDataStore) {
241
+ const original = extractGeoreferencingOnDemand(ifcDataStore as IfcDataStore);
242
+ const merged = mergeGeoref(
243
+ original?.projectedCRS,
244
+ original?.mapConversion,
245
+ '__legacy__',
246
+ );
247
+ if (merged) {
248
+ return { hasGeoreference: true, ...merged, sourceModelId: '__legacy__', coordinateInfo: mergedGeometryResult?.coordinateInfo };
249
+ }
250
+ }
251
+
252
+ return null;
253
+ }, [cesiumEnabled, storeModels, ifcDataStore, georefMutations, mutationVersion, mergedGeometryResult]);
254
+
255
+ // Sync the active Cesium source model ID so terrain actions are scoped correctly
256
+ useEffect(() => {
257
+ setCesiumSourceModelId(georef?.sourceModelId ?? null);
258
+ }, [georef?.sourceModelId, setCesiumSourceModelId]);
259
+
260
+ useEffect(() => {
261
+ // Recent files are a desktop-only feature — the web viewer should not
262
+ // show previously opened files in the landing page empty state.
263
+ if (!isTauri()) return;
264
+
265
+ const refreshRecentFiles = () => {
266
+ setRecentFiles(getRecentFiles().slice(0, 3));
267
+ };
268
+
269
+ refreshRecentFiles();
270
+ window.addEventListener('focus', refreshRecentFiles);
271
+ return () => window.removeEventListener('focus', refreshRecentFiles);
272
+ }, []);
273
+
98
274
  const handleDragOver = useCallback((e: React.DragEvent) => {
99
275
  e.preventDefault();
100
276
  e.stopPropagation();
@@ -127,6 +303,10 @@ export function ViewportContainer() {
127
303
 
128
304
  if (supportedFiles.length === 0) return;
129
305
 
306
+ recordRecentFiles(supportedFiles.map((file) => ({ name: file.name, size: file.size })));
307
+ void cacheFileBlobs(supportedFiles);
308
+ setRecentFiles(getRecentFiles().slice(0, 3));
309
+
130
310
  if (hasModelsLoaded) {
131
311
  // Models already loaded - add new files sequentially
132
312
  loadFilesSequentially(supportedFiles);
@@ -157,6 +337,10 @@ export function ViewportContainer() {
157
337
 
158
338
  if (supportedFiles.length === 0) return;
159
339
 
340
+ recordRecentFiles(supportedFiles.map((file) => ({ name: file.name, size: file.size })));
341
+ void cacheFileBlobs(supportedFiles);
342
+ setRecentFiles(getRecentFiles().slice(0, 3));
343
+
160
344
  if (supportedFiles.length === 1) {
161
345
  // Single file - use loadFile (simpler single-model path)
162
346
  loadFile(supportedFiles[0]);
@@ -183,6 +367,7 @@ export function ViewportContainer() {
183
367
  // A version counter triggers downstream re-renders via the Viewport prop.
184
368
  const filteredCacheRef = useRef<MeshData[]>([]);
185
369
  const filteredSourceLenRef = useRef(0);
370
+ const filteredSourceRef = useRef<MeshData[] | null>(null);
186
371
  const filteredTypeVisRef = useRef(typeVisibility);
187
372
  const filteredVersionRef = useRef(0);
188
373
 
@@ -190,6 +375,7 @@ export function ViewportContainer() {
190
375
  if (!mergedGeometryResult?.meshes) {
191
376
  filteredCacheRef.current = [];
192
377
  filteredSourceLenRef.current = 0;
378
+ filteredSourceRef.current = null;
193
379
  filteredVersionRef.current = 0;
194
380
  return null;
195
381
  }
@@ -203,9 +389,11 @@ export function ViewportContainer() {
203
389
  prevVis.spaces !== typeVisibility.spaces ||
204
390
  prevVis.openings !== typeVisibility.openings ||
205
391
  prevVis.site !== typeVisibility.site;
206
- if (typeVisChanged || allMeshes.length < filteredSourceLenRef.current) {
392
+ const sourceChanged = filteredSourceRef.current !== allMeshes;
393
+ if (typeVisChanged || sourceChanged || allMeshes.length < filteredSourceLenRef.current) {
207
394
  cache.length = 0;
208
395
  filteredSourceLenRef.current = 0;
396
+ filteredSourceRef.current = allMeshes;
209
397
  filteredTypeVisRef.current = typeVisibility;
210
398
  }
211
399
 
@@ -237,7 +425,7 @@ export function ViewportContainer() {
237
425
 
238
426
  // Only bump version when cache content actually changed — avoids
239
427
  // unnecessary downstream re-renders when memo runs with same data.
240
- if (cache.length !== prevCacheLen || typeVisChanged) {
428
+ if (cache.length !== prevCacheLen || typeVisChanged || sourceChanged) {
241
429
  filteredVersionRef.current++;
242
430
  }
243
431
 
@@ -265,14 +453,14 @@ export function ViewportContainer() {
265
453
  const hierarchy = model.ifcDataStore?.spatialHierarchy;
266
454
  if (!hierarchy) continue;
267
455
 
268
- const offset = model.idOffset ?? 0;
269
-
270
456
  for (const storeyId of selectedStoreys) {
271
- const localStoreyId = hierarchy.byStorey.has(storeyId) ? storeyId : storeyId - offset;
457
+ const localStoreyId = hierarchy.byStorey.has(storeyId)
458
+ ? storeyId
459
+ : storeyId - (model.idOffset ?? 0);
272
460
  const storeyElementIds = collectIfcBuildingStoreyElementsWithIfcSpace(hierarchy, localStoreyId);
273
461
  if (storeyElementIds) {
274
462
  for (const originalExpressId of storeyElementIds) {
275
- combinedGlobalIds.add(originalExpressId + offset);
463
+ combinedGlobalIds.add(toGlobalIdFromModels(storeModels, model.id, originalExpressId));
276
464
  }
277
465
  }
278
466
  }
@@ -530,7 +718,29 @@ export function ViewportContainer() {
530
718
 
531
719
  {/* Action */}
532
720
  <button
533
- onClick={() => webgpu.supported && fileInputRef.current?.click()}
721
+ onClick={async () => {
722
+ if (!webgpu.supported) {
723
+ return;
724
+ }
725
+
726
+ void logToDesktopTerminal('info', '[ViewportContainer] Empty-state open button clicked');
727
+ const file = await openIfcFileDialog();
728
+ if (file) {
729
+ void logToDesktopTerminal('info', `[ViewportContainer] Native dialog selected ${file.path}`);
730
+ recordRecentFiles([{
731
+ name: file.name,
732
+ size: file.size,
733
+ path: file.path,
734
+ modifiedMs: file.modifiedMs ?? null,
735
+ }]);
736
+ setRecentFiles(getRecentFiles().slice(0, 3));
737
+ loadFile(file);
738
+ return;
739
+ }
740
+
741
+ void logToDesktopTerminal('info', '[ViewportContainer] Falling back to browser file input');
742
+ fileInputRef.current?.click();
743
+ }}
534
744
  disabled={!webgpu.supported || webgpu.checking}
535
745
  className={`group w-full flex items-center justify-center gap-3 px-6 py-3 font-mono text-sm border transition-all ${
536
746
  !webgpu.supported || webgpu.checking
@@ -545,6 +755,37 @@ export function ViewportContainer() {
545
755
  <p className="mt-3 text-xs font-mono text-zinc-400 dark:text-[#565f89]">
546
756
  {webgpu.supported ? 'or drag & drop anywhere' : 'file upload disabled'}
547
757
  </p>
758
+
759
+ {recentFiles.length > 0 && (
760
+ <div className="mt-6 w-full border-t border-zinc-200 dark:border-[#3b4261] pt-4">
761
+ <div className="mb-3 flex items-center gap-2 text-xs font-mono uppercase tracking-[0.2em] text-zinc-400 dark:text-[#565f89]">
762
+ <Clock3 className="h-3.5 w-3.5" />
763
+ <span>Recent Files</span>
764
+ </div>
765
+ <div className="flex flex-col gap-2">
766
+ {recentFiles.map((file) => (
767
+ <button
768
+ key={`${file.name}-${file.timestamp}`}
769
+ type="button"
770
+ onClick={async () => {
771
+ const cached = await getCachedFile(file);
772
+ if (cached) {
773
+ await loadFile(cached);
774
+ return;
775
+ }
776
+ fileInputRef.current?.click();
777
+ }}
778
+ 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"
779
+ >
780
+ <span className="min-w-0 truncate font-mono text-xs">{file.name}</span>
781
+ <span className="shrink-0 font-mono text-[10px] uppercase tracking-wide text-zinc-400 dark:text-[#565f89]">
782
+ {formatFileSize(file.size)}
783
+ </span>
784
+ </button>
785
+ ))}
786
+ </div>
787
+ </div>
788
+ )}
548
789
  </div>
549
790
 
550
791
  {/* Feature Grid */}
@@ -606,18 +847,30 @@ export function ViewportContainer() {
606
847
  </div>
607
848
  )}
608
849
 
850
+ {/* Cesium 3D world context overlay — rendered behind the WebGPU canvas */}
851
+ {cesiumEnabled && georef && (
852
+ <CesiumOverlay
853
+ mapConversion={georef.mapConversion}
854
+ projectedCRS={georef.projectedCRS}
855
+ coordinateInfo={georef.coordinateInfo}
856
+ geometryResult={mergedGeometryResult}
857
+ />
858
+ )}
609
859
  <Viewport
610
860
  geometry={filteredGeometry}
611
861
  geometryVersion={geometryVersion}
612
862
  coordinateInfo={mergedGeometryResult?.coordinateInfo}
613
863
  computedIsolatedIds={computedIsolatedIds}
614
864
  modelIdToIndex={modelIdToIndex}
865
+ cesiumActive={cesiumEnabled && georef !== null}
866
+ releaseGeometryAfterStream={false}
867
+ onGeometryReleased={releaseGeometryMemory}
615
868
  />
616
869
  {bcfOverlayVisible && <BCFOverlay />}
617
870
  <ViewportOverlays />
618
871
  <ToolOverlays />
619
872
  <BasketPresentationDock />
620
- <Section2DPanel
873
+ <Section2DPanel
621
874
  mergedGeometry={mergedGeometryResult}
622
875
  computedIsolatedIds={computedIsolatedIds}
623
876
  modelIdToIndex={modelIdToIndex}
@@ -8,10 +8,16 @@ 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';
@@ -28,6 +34,14 @@ export function ViewportOverlays({ hideViewCube = false }: { hideViewCube?: bool
28
34
  const setOnScaleChange = useViewerStore((s) => s.setOnScaleChange);
29
35
  const { ifcDataStore, geometryResult } = useIfc();
30
36
 
37
+ // Cesium state
38
+ const cesiumEnabled = useViewerStore((s) => s.cesiumEnabled);
39
+ const cesiumDataSource = useViewerStore((s) => s.cesiumDataSource);
40
+ const setCesiumDataSource = useViewerStore((s) => s.setCesiumDataSource);
41
+ const cesiumTerrainEnabled = useViewerStore((s) => s.cesiumTerrainEnabled);
42
+ const setCesiumTerrainEnabled = useViewerStore((s) => s.setCesiumTerrainEnabled);
43
+ const toggleCesium = useViewerStore((s) => s.toggleCesium);
44
+
31
45
  // Use refs for rotation to avoid re-renders - ViewCube updates itself directly
32
46
  const cameraRotationRef = useRef({ azimuth: 45, elevation: 25 });
33
47
  const viewCubeRef = useRef<ViewCubeRef | null>(null);
@@ -132,35 +146,45 @@ export function ViewportOverlays({ hideViewCube = false }: { hideViewCube?: bool
132
146
 
133
147
  return (
134
148
  <>
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>
149
+ {/* Bottom-right: Cesium settings overlay OR Navigation controls */}
150
+ {cesiumEnabled ? (
151
+ <CesiumSettingsOverlay
152
+ dataSource={cesiumDataSource}
153
+ onDataSourceChange={setCesiumDataSource}
154
+ terrainEnabled={cesiumTerrainEnabled}
155
+ onTerrainChange={setCesiumTerrainEnabled}
156
+ onClose={toggleCesium}
157
+ />
158
+ ) : (
159
+ <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">
160
+ <Tooltip>
161
+ <TooltipTrigger asChild>
162
+ <Button variant="ghost" size="icon-sm" onClick={handleHome}>
163
+ <Home className="h-4 w-4" />
164
+ </Button>
165
+ </TooltipTrigger>
166
+ <TooltipContent side="left">Home (H)</TooltipContent>
167
+ </Tooltip>
145
168
 
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>
169
+ <Tooltip>
170
+ <TooltipTrigger asChild>
171
+ <Button variant="ghost" size="icon-sm" onClick={handleZoomIn}>
172
+ <ZoomIn className="h-4 w-4" />
173
+ </Button>
174
+ </TooltipTrigger>
175
+ <TooltipContent side="left">Zoom In (+)</TooltipContent>
176
+ </Tooltip>
154
177
 
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>
178
+ <Tooltip>
179
+ <TooltipTrigger asChild>
180
+ <Button variant="ghost" size="icon-sm" onClick={handleZoomOut}>
181
+ <ZoomOut className="h-4 w-4" />
182
+ </Button>
183
+ </TooltipTrigger>
184
+ <TooltipContent side="left">Zoom Out (-)</TooltipContent>
185
+ </Tooltip>
186
+ </div>
187
+ )}
164
188
 
165
189
  {/* Context Info (bottom-center) - Storey names */}
166
190
  {storeyNames && storeyNames.length > 0 && (
@@ -209,3 +233,81 @@ export function ViewportOverlays({ hideViewCube = false }: { hideViewCube?: bool
209
233
  </>
210
234
  );
211
235
  }
236
+
237
+ /* ------------------------------------------------------------------ */
238
+ /* Cesium Settings Overlay — replaces nav controls when Cesium is on */
239
+ /* ------------------------------------------------------------------ */
240
+
241
+ const DATA_SOURCES: { value: CesiumDataSource; label: string; icon: typeof Globe2 }[] = [
242
+ { value: 'google-photorealistic', label: 'Google 3D', icon: Globe2 },
243
+ { value: 'osm-buildings', label: 'OSM', icon: Building2 },
244
+ { value: 'bing-aerial', label: 'Aerial', icon: Satellite },
245
+ ];
246
+
247
+ function CesiumSettingsOverlay({
248
+ dataSource,
249
+ onDataSourceChange,
250
+ terrainEnabled,
251
+ onTerrainChange,
252
+ onClose,
253
+ }: {
254
+ dataSource: CesiumDataSource;
255
+ onDataSourceChange: (ds: CesiumDataSource) => void;
256
+ terrainEnabled: boolean;
257
+ onTerrainChange: (enabled: boolean) => void;
258
+ onClose: () => void;
259
+ }) {
260
+ return (
261
+ <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]">
262
+ {/* Header */}
263
+ <div className="flex items-center justify-between gap-2">
264
+ <span className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
265
+ 3D World
266
+ </span>
267
+ <Tooltip>
268
+ <TooltipTrigger asChild>
269
+ <Button variant="ghost" size="icon-sm" className="h-5 w-5" onClick={onClose}>
270
+ <X className="h-3 w-3" />
271
+ </Button>
272
+ </TooltipTrigger>
273
+ <TooltipContent side="left">Disable Cesium overlay</TooltipContent>
274
+ </Tooltip>
275
+ </div>
276
+
277
+ {/* Data Source Buttons */}
278
+ <div className="flex flex-col gap-0.5">
279
+ {DATA_SOURCES.map((ds) => {
280
+ const Icon = ds.icon;
281
+ const active = dataSource === ds.value;
282
+ return (
283
+ <button
284
+ key={ds.value}
285
+ onClick={() => onDataSourceChange(ds.value)}
286
+ className={cn(
287
+ 'flex items-center gap-2 px-2 py-1.5 rounded text-xs transition-colors text-left',
288
+ active
289
+ ? 'bg-teal-600 text-white'
290
+ : 'hover:bg-muted text-foreground/80',
291
+ )}
292
+ >
293
+ <Icon className="h-3.5 w-3.5 shrink-0" />
294
+ {ds.label}
295
+ </button>
296
+ );
297
+ })}
298
+ </div>
299
+
300
+ {/* Terrain Toggle */}
301
+ <label className="flex items-center gap-2 px-2 py-1 cursor-pointer border-t border-border pt-2">
302
+ <Mountain className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
303
+ <span className="text-xs text-foreground/80">Terrain</span>
304
+ <input
305
+ type="checkbox"
306
+ checked={terrainEnabled}
307
+ onChange={(e) => onTerrainChange(e.target.checked)}
308
+ className="ml-auto accent-teal-500"
309
+ />
310
+ </label>
311
+ </div>
312
+ );
313
+ }
@@ -13,6 +13,7 @@ import {
13
13
  } from '@ifc-lite/data';
14
14
  import type { IfcDataStore } from '@ifc-lite/parser';
15
15
  import { useViewerStore, type FederatedModel } from '@/store';
16
+ import { toGlobalIdFromModels } from '@/store/globalId';
16
17
  import type { TreeNode, NodeType, StoreyData, UnifiedStorey } from './types';
17
18
 
18
19
  /** Helper to create elevation key (with 0.5m tolerance for matching) */
@@ -300,7 +301,7 @@ export function buildTreeData(
300
301
  nodes.push({
301
302
  id: storeyNodeId,
302
303
  expressIds: allStoreyIds,
303
- globalIds: unified.storeys.map((s) => s.storeyId + (models.get(s.modelId)?.idOffset ?? 0)),
304
+ globalIds: unified.storeys.map((s) => toGlobalIdFromModels(models, s.modelId, s.storeyId)),
304
305
  modelIds: unified.storeys.map(s => s.modelId),
305
306
  name: unified.name,
306
307
  type: 'unified-storey',