@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,7 +2,7 @@
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 React, { useRef, useCallback, useEffect, useMemo } from 'react';
5
+ import React, { useRef, useCallback, useEffect, useMemo, useSyncExternalStore } from 'react';
6
6
  import {
7
7
  FolderOpen,
8
8
  Download,
@@ -37,6 +37,8 @@ import {
37
37
  Layout,
38
38
  LayoutTemplate,
39
39
  FileCode2,
40
+ Globe2,
41
+ Settings,
40
42
  } from 'lucide-react';
41
43
  import { Button } from '@/components/ui/button';
42
44
  import { Separator } from '@/components/ui/separator';
@@ -64,13 +66,30 @@ import { ExportDialog } from './ExportDialog';
64
66
  import { BulkPropertyEditor } from './BulkPropertyEditor';
65
67
  import { DataConnector } from './DataConnector';
66
68
  import { ExportChangesButton } from './ExportChangesButton';
69
+ // CesiumSettingsDialog removed — settings now shown as overlay on Cesium viewer
67
70
  import { useFloorplanView } from '@/hooks/useFloorplanView';
71
+ import { buildDesktopUpgradeUrl, hasDesktopFeatureAccess, type DesktopFeature } from '@/lib/desktop-product';
68
72
  import { recordRecentFiles, cacheFileBlobs } from '@/lib/recent-files';
69
73
  import { ThemeSwitch } from './ThemeSwitch';
70
74
  import { toast } from '@/components/ui/toast';
75
+ import { navigateToPath } from '@/services/app-navigation';
76
+ import { getStartupHarnessRequest, setActiveHarnessRequest, tryClaimStartupHarnessRequest } from '@/services/desktop-harness';
77
+ import { logToDesktopTerminal } from '@/services/desktop-logger';
78
+ import { openIfcFileDialog, type NativeFileHandle } from '@/services/file-dialog';
79
+ import { isTauri } from '@/lib/platform';
80
+ import {
81
+ closeActiveAnalysisExtension,
82
+ getAnalysisExtensionsSnapshot,
83
+ openAnalysisExtension,
84
+ subscribeAnalysisExtensions,
85
+ } from '@/services/analysis-extensions';
71
86
 
72
87
  type Tool = 'select' | 'walk' | 'measure' | 'section';
73
- type WorkspacePanel = 'script' | 'list' | 'bcf' | 'ids' | 'lens';
88
+ type WorkspacePanel = 'script' | 'list' | 'bcf' | 'ids' | 'lens' | string;
89
+
90
+ function isNativeFileHandle(file: File | NativeFileHandle): file is NativeFileHandle {
91
+ return typeof (file as NativeFileHandle).path === 'string';
92
+ }
74
93
 
75
94
  // #region FIX: Move ToolButton OUTSIDE MainToolbar to prevent recreation on every render
76
95
  // This fixes Radix UI Tooltip's asChild prop becoming stale during re-renders
@@ -148,18 +167,109 @@ interface MainToolbarProps {
148
167
  export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainToolbarProps) {
149
168
  const fileInputRef = useRef<HTMLInputElement>(null);
150
169
  const addModelInputRef = useRef<HTMLInputElement>(null);
151
- const { loadFile, loading, progress, geometryResult, ifcDataStore, models, clearAllModels, loadFilesSequentially, loadFederatedIfcx, addIfcxOverlays, addModel } = useIfc();
170
+ const {
171
+ loadFile,
172
+ loading,
173
+ progress,
174
+ geometryProgress,
175
+ metadataProgress,
176
+ geometryResult,
177
+ ifcDataStore,
178
+ models,
179
+ clearAllModels,
180
+ loadFilesSequentially,
181
+ loadFederatedIfcx,
182
+ addIfcxOverlays,
183
+ addModel,
184
+ } = useIfc();
152
185
 
153
186
  // Listen for programmatic file-load requests (from command palette recent files)
154
187
  useEffect(() => {
155
188
  const handler = (e: Event) => {
156
- const file = (e as CustomEvent<File>).detail;
157
- if (file) loadFile(file);
189
+ const file = (e as CustomEvent<File | NativeFileHandle>).detail;
190
+ if (file) {
191
+ recordRecentFiles([isNativeFileHandle(file)
192
+ ? { name: file.name, size: file.size, path: file.path, modifiedMs: file.modifiedMs ?? null }
193
+ : { name: file.name, size: file.size }]);
194
+ void loadFile(file);
195
+ }
158
196
  };
159
197
  window.addEventListener('ifc-lite:load-file', handler);
160
198
  return () => window.removeEventListener('ifc-lite:load-file', handler);
161
199
  }, [loadFile]);
162
200
 
201
+ useEffect(() => {
202
+ let cancelled = false;
203
+ const sleep = (ms: number) => new Promise((resolve) => globalThis.setTimeout(resolve, ms));
204
+
205
+ const waitForViewerToSettle = async (label: string) => {
206
+ const timeoutMs = 120_000;
207
+ const pollMs = 100;
208
+ const start = performance.now();
209
+ while (!cancelled) {
210
+ const state = useViewerStore.getState();
211
+ const meshCount = state.geometryResult?.meshes.length ?? 0;
212
+ if (!state.loading && meshCount > 0) {
213
+ void logToDesktopTerminal(
214
+ 'info',
215
+ `[DesktopHarness] ${label} settled: loading=${state.loading} meshes=${meshCount} progress=${state.progress?.phase ?? 'none'}`
216
+ );
217
+ return;
218
+ }
219
+ if (performance.now() - start >= timeoutMs) {
220
+ throw new Error(`[DesktopHarness] Timed out waiting for ${label} to settle`);
221
+ }
222
+ await sleep(pollMs);
223
+ }
224
+ };
225
+
226
+ void (async () => {
227
+ void logToDesktopTerminal('info', '[DesktopHarness] MainToolbar startup harness effect running');
228
+ const request = await getStartupHarnessRequest();
229
+ if (!request || cancelled) {
230
+ void logToDesktopTerminal(
231
+ 'info',
232
+ `[DesktopHarness] No startup harness request available (cancelled=${cancelled})`
233
+ );
234
+ return;
235
+ }
236
+ if (!tryClaimStartupHarnessRequest(request)) {
237
+ void logToDesktopTerminal('info', `[DesktopHarness] Startup harness request already claimed for ${request.file.path}`);
238
+ return;
239
+ }
240
+ void logToDesktopTerminal('info', `[DesktopHarness] Claimed startup harness request for ${request.file.path}`);
241
+ console.log(`[DesktopHarness] Auto-loading startup file: ${request.file.path}`);
242
+ if (!request.replaceFile) {
243
+ void logToDesktopTerminal('info', `[DesktopHarness] Calling loadFile for ${request.file.path}`);
244
+ await loadFile(request.file);
245
+ return;
246
+ }
247
+
248
+ void logToDesktopTerminal(
249
+ 'info',
250
+ `[DesktopHarness] Running replacement sequence first=${request.file.path} second=${request.replaceFile.path}`
251
+ );
252
+ setActiveHarnessRequest(null);
253
+ await loadFile(request.file);
254
+ await waitForViewerToSettle(`first load ${request.file.name}`);
255
+ if (cancelled) {
256
+ return;
257
+ }
258
+
259
+ setActiveHarnessRequest({
260
+ ...request,
261
+ file: request.replaceFile,
262
+ replaceFile: undefined,
263
+ });
264
+ void logToDesktopTerminal('info', `[DesktopHarness] Calling replacement loadFile for ${request.replaceFile.path}`);
265
+ await loadFile(request.replaceFile);
266
+ })();
267
+
268
+ return () => {
269
+ cancelled = true;
270
+ };
271
+ }, [loadFile]);
272
+
163
273
  // Floorplan view
164
274
  const { availableStoreys, activateFloorplan } = useFloorplanView();
165
275
 
@@ -195,6 +305,30 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
195
305
  const setLensPanelVisible = useViewerStore((state) => state.setLensPanelVisible);
196
306
  const scriptPanelVisible = useViewerStore((state) => state.scriptPanelVisible);
197
307
  const setScriptPanelVisible = useViewerStore((state) => state.setScriptPanelVisible);
308
+ // Cesium 3D overlay state
309
+ const cesiumAvailable = useViewerStore((state) => state.cesiumAvailable);
310
+ const cesiumEnabled = useViewerStore((state) => state.cesiumEnabled);
311
+ const toggleCesium = useViewerStore((state) => state.toggleCesium);
312
+ const storeModels = useViewerStore((state) => state.models);
313
+ const desktopEntitlement = useViewerStore((state) => state.desktopEntitlement);
314
+ const analysisExtensionState = useSyncExternalStore(
315
+ subscribeAnalysisExtensions,
316
+ getAnalysisExtensionsSnapshot,
317
+ getAnalysisExtensionsSnapshot,
318
+ );
319
+ const activeAnalysisExtension = useMemo(
320
+ () => analysisExtensionState.extensions.find((extension) => extension.id === analysisExtensionState.activeId) ?? null,
321
+ [analysisExtensionState.activeId, analysisExtensionState.extensions],
322
+ );
323
+ const rightAnalysisExtensions = useMemo(
324
+ () => analysisExtensionState.extensions.filter((extension) => (extension.placement ?? 'right') === 'right'),
325
+ [analysisExtensionState.extensions],
326
+ );
327
+ const bottomAnalysisExtensions = useMemo(
328
+ () => analysisExtensionState.extensions.filter((extension) => (extension.placement ?? 'right') === 'bottom'),
329
+ [analysisExtensionState.extensions],
330
+ );
331
+ const desktopShell = isTauri();
198
332
 
199
333
  // Check which type geometries exist across ALL loaded models (federation-aware).
200
334
  // PERF: Use meshes.length as dep proxy instead of full geometryResult, and
@@ -361,7 +495,23 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
361
495
  goHomeFromStore();
362
496
  }, []);
363
497
 
498
+ const promptDesktopUpgrade = useCallback((featureLabel: string) => {
499
+ toast.info(`${featureLabel} is available with Desktop Pro`);
500
+ navigateToPath(buildDesktopUpgradeUrl());
501
+ }, []);
502
+
503
+ const requireDesktopFeature = useCallback((feature: DesktopFeature, label: string) => {
504
+ if (hasDesktopFeatureAccess(desktopEntitlement, feature)) {
505
+ return true;
506
+ }
507
+ promptDesktopUpgrade(label);
508
+ return false;
509
+ }, [desktopEntitlement, promptDesktopUpgrade]);
510
+
364
511
  const handleToggleBottomPanel = useCallback((panel: 'script' | 'list') => {
512
+ if (activeAnalysisExtension?.placement === 'bottom') {
513
+ closeActiveAnalysisExtension();
514
+ }
365
515
  const isScriptPanel = panel === 'script';
366
516
  const nextScriptVisible = isScriptPanel ? !scriptPanelVisible : false;
367
517
  const nextListVisible = isScriptPanel ? false : !listPanelVisible;
@@ -372,9 +522,19 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
372
522
  if (nextScriptVisible || nextListVisible) {
373
523
  setRightPanelCollapsed(false);
374
524
  }
375
- }, [listPanelVisible, scriptPanelVisible, setListPanelVisible, setRightPanelCollapsed, setScriptPanelVisible]);
525
+ }, [activeAnalysisExtension?.placement, listPanelVisible, scriptPanelVisible, setListPanelVisible, setRightPanelCollapsed, setScriptPanelVisible]);
376
526
 
377
527
  const handleToggleRightPanel = useCallback((panel: 'bcf' | 'ids' | 'lens') => {
528
+ if (activeAnalysisExtension?.placement !== 'bottom') {
529
+ closeActiveAnalysisExtension();
530
+ }
531
+ if (panel === 'bcf' && !requireDesktopFeature('bcf_issue_management', 'BCF issue management')) {
532
+ return;
533
+ }
534
+ if (panel === 'ids' && !requireDesktopFeature('ids_validation', 'IDS validation')) {
535
+ return;
536
+ }
537
+
378
538
  const nextBcfVisible = panel === 'bcf' ? !bcfPanelVisible : false;
379
539
  const nextIdsVisible = panel === 'ids' ? !idsPanelVisible : false;
380
540
  const nextLensVisible = panel === 'lens' ? !lensPanelVisible : false;
@@ -387,15 +547,55 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
387
547
  setRightPanelCollapsed(false);
388
548
  }
389
549
  }, [
550
+ activeAnalysisExtension?.placement,
390
551
  bcfPanelVisible,
391
552
  idsPanelVisible,
392
553
  lensPanelVisible,
554
+ requireDesktopFeature,
393
555
  setBcfPanelVisible,
394
556
  setIdsPanelVisible,
395
557
  setLensPanelVisible,
396
558
  setRightPanelCollapsed,
397
559
  ]);
398
560
 
561
+ const handleToggleAnalysisExtension = useCallback((id: string) => {
562
+ const extension = analysisExtensionState.extensions.find((candidate) => candidate.id === id);
563
+ if (!extension) {
564
+ return;
565
+ }
566
+
567
+ if (analysisExtensionState.activeId === id) {
568
+ closeActiveAnalysisExtension();
569
+ return;
570
+ }
571
+
572
+ const opened = openAnalysisExtension(id);
573
+ if (!opened) {
574
+ return;
575
+ }
576
+
577
+ if ((extension.placement ?? 'right') === 'bottom') {
578
+ setScriptPanelVisible(false);
579
+ setListPanelVisible(false);
580
+ setRightPanelCollapsed(false);
581
+ return;
582
+ }
583
+
584
+ setBcfPanelVisible(false);
585
+ setIdsPanelVisible(false);
586
+ setLensPanelVisible(false);
587
+ setRightPanelCollapsed(false);
588
+ }, [
589
+ analysisExtensionState.activeId,
590
+ analysisExtensionState.extensions,
591
+ setBcfPanelVisible,
592
+ setIdsPanelVisible,
593
+ setLensPanelVisible,
594
+ setListPanelVisible,
595
+ setRightPanelCollapsed,
596
+ setScriptPanelVisible,
597
+ ]);
598
+
399
599
  const activeWorkspacePanels = useMemo(() => {
400
600
  const panels = new Set<WorkspacePanel>();
401
601
  if (scriptPanelVisible) panels.add('script');
@@ -403,8 +603,16 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
403
603
  if (bcfPanelVisible) panels.add('bcf');
404
604
  if (idsPanelVisible) panels.add('ids');
405
605
  if (lensPanelVisible) panels.add('lens');
606
+ if (analysisExtensionState.activeId) panels.add(analysisExtensionState.activeId);
406
607
  return panels;
407
- }, [bcfPanelVisible, idsPanelVisible, lensPanelVisible, listPanelVisible, scriptPanelVisible]);
608
+ }, [
609
+ analysisExtensionState.activeId,
610
+ bcfPanelVisible,
611
+ idsPanelVisible,
612
+ lensPanelVisible,
613
+ listPanelVisible,
614
+ scriptPanelVisible,
615
+ ]);
408
616
 
409
617
  const workspacePanelLabel = useMemo(() => {
410
618
  if (activeWorkspacePanels.size === 0) return null;
@@ -413,10 +621,12 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
413
621
  if (activeWorkspacePanels.has('list')) return 'Lists';
414
622
  if (activeWorkspacePanels.has('bcf')) return 'BCF Issues';
415
623
  if (activeWorkspacePanels.has('ids')) return 'IDS Validation';
416
- return 'Lens Rules';
417
- }, [activeWorkspacePanels]);
624
+ if (activeWorkspacePanels.has('lens')) return 'Lens Rules';
625
+ return activeAnalysisExtension?.label ?? 'Analysis';
626
+ }, [activeAnalysisExtension?.label, activeWorkspacePanels]);
418
627
 
419
628
  const handleExportGLB = useCallback(() => {
629
+ if (!requireDesktopFeature('exports', 'Exports')) return;
420
630
  if (!geometryResult) return;
421
631
  try {
422
632
  const exporter = new GLTFExporter(geometryResult);
@@ -433,9 +643,10 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
433
643
  console.error('Export failed:', err);
434
644
  toast.error(`GLB export failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
435
645
  }
436
- }, [geometryResult]);
646
+ }, [geometryResult, requireDesktopFeature]);
437
647
 
438
648
  const handleScreenshot = useCallback(() => {
649
+ if (!requireDesktopFeature('exports', 'Exports')) return;
439
650
  const canvas = document.querySelector('canvas');
440
651
  if (!canvas) return;
441
652
  try {
@@ -449,9 +660,10 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
449
660
  console.error('Screenshot failed:', err);
450
661
  toast.error('Screenshot failed');
451
662
  }
452
- }, []);
663
+ }, [requireDesktopFeature]);
453
664
 
454
665
  const handleExportCSV = useCallback((type: 'entities' | 'properties' | 'quantities' | 'spatial') => {
666
+ if (!requireDesktopFeature('exports', 'Exports')) return;
455
667
  if (!ifcDataStore) return;
456
668
  try {
457
669
  const exporter = new CSVExporter(ifcDataStore);
@@ -489,9 +701,10 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
489
701
  console.error('CSV export failed:', err);
490
702
  toast.error(`CSV export failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
491
703
  }
492
- }, [ifcDataStore]);
704
+ }, [ifcDataStore, requireDesktopFeature]);
493
705
 
494
706
  const handleExportJSON = useCallback(() => {
707
+ if (!requireDesktopFeature('exports', 'Exports')) return;
495
708
  if (!ifcDataStore) return;
496
709
  try {
497
710
  const entities: Record<string, unknown>[] = [];
@@ -519,7 +732,7 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
519
732
  console.error('JSON export failed:', err);
520
733
  toast.error(`JSON export failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
521
734
  }
522
- }, [ifcDataStore]);
735
+ }, [ifcDataStore, requireDesktopFeature]);
523
736
 
524
737
  return (
525
738
  <div className="flex items-center gap-1 px-2 h-12 border-b bg-white dark:bg-black border-zinc-200 dark:border-zinc-800 relative z-50">
@@ -547,9 +760,25 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
547
760
  <Button
548
761
  variant="ghost"
549
762
  size="icon-sm"
550
- onClick={(e) => {
763
+ onClick={async (e) => {
551
764
  // Blur button to close tooltip before opening file dialog
552
765
  (e.currentTarget as HTMLButtonElement).blur();
766
+
767
+ void logToDesktopTerminal('info', '[MainToolbar] Open file button clicked');
768
+ const file = await openIfcFileDialog();
769
+ if (file) {
770
+ void logToDesktopTerminal('info', `[MainToolbar] Native dialog selected ${file.path}`);
771
+ recordRecentFiles([{
772
+ name: file.name,
773
+ size: file.size,
774
+ path: file.path,
775
+ modifiedMs: file.modifiedMs ?? null,
776
+ }]);
777
+ void loadFile(file);
778
+ return;
779
+ }
780
+
781
+ void logToDesktopTerminal('info', '[MainToolbar] Falling back to browser file input');
553
782
  fileInputRef.current?.click();
554
783
  }}
555
784
  disabled={loading}
@@ -592,14 +821,21 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
592
821
  </Button>
593
822
  </DropdownMenuTrigger>
594
823
  <DropdownMenuContent>
595
- <ExportDialog
596
- trigger={
597
- <DropdownMenuItem onSelect={(e) => e.preventDefault()}>
598
- <FileText className="h-4 w-4 mr-2" />
599
- Export IFC (with changes)
600
- </DropdownMenuItem>
601
- }
602
- />
824
+ {hasDesktopFeatureAccess(desktopEntitlement, 'exports') ? (
825
+ <ExportDialog
826
+ trigger={
827
+ <DropdownMenuItem onSelect={(e) => e.preventDefault()}>
828
+ <FileText className="h-4 w-4 mr-2" />
829
+ Export IFC (with changes)
830
+ </DropdownMenuItem>
831
+ }
832
+ />
833
+ ) : (
834
+ <DropdownMenuItem onClick={() => promptDesktopUpgrade('Exports')}>
835
+ <FileText className="h-4 w-4 mr-2" />
836
+ Export IFC (with changes)
837
+ </DropdownMenuItem>
838
+ )}
603
839
  <DropdownMenuSeparator />
604
840
  <DropdownMenuItem onClick={handleExportGLB}>
605
841
  <Download className="h-4 w-4 mr-2" />
@@ -676,7 +912,9 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
676
912
  </DropdownMenu>
677
913
 
678
914
  {/* Export Changes Button - shows when there are pending mutations */}
679
- <ExportChangesButton />
915
+ {hasDesktopFeatureAccess(desktopEntitlement, 'exports') ? (
916
+ <ExportChangesButton />
917
+ ) : null}
680
918
 
681
919
  {/* ── Panels ── */}
682
920
  <DropdownMenu>
@@ -732,6 +970,42 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
732
970
  <Palette className="h-4 w-4 mr-2" />
733
971
  Lens Rules
734
972
  </DropdownMenuCheckboxItem>
973
+ {rightAnalysisExtensions.length > 0 && (
974
+ <>
975
+ <DropdownMenuSeparator />
976
+ {rightAnalysisExtensions.map((extension) => {
977
+ const Icon = extension.icon;
978
+ return (
979
+ <DropdownMenuCheckboxItem
980
+ key={extension.id}
981
+ checked={activeWorkspacePanels.has(extension.id)}
982
+ onCheckedChange={() => handleToggleAnalysisExtension(extension.id)}
983
+ >
984
+ <Icon className="h-4 w-4 mr-2" />
985
+ {extension.label}
986
+ </DropdownMenuCheckboxItem>
987
+ );
988
+ })}
989
+ </>
990
+ )}
991
+ {bottomAnalysisExtensions.length > 0 && (
992
+ <>
993
+ <DropdownMenuSeparator />
994
+ {bottomAnalysisExtensions.map((extension) => {
995
+ const Icon = extension.icon;
996
+ return (
997
+ <DropdownMenuCheckboxItem
998
+ key={extension.id}
999
+ checked={activeWorkspacePanels.has(extension.id)}
1000
+ onCheckedChange={() => handleToggleAnalysisExtension(extension.id)}
1001
+ >
1002
+ <Icon className="h-4 w-4 mr-2" />
1003
+ {extension.label}
1004
+ </DropdownMenuCheckboxItem>
1005
+ );
1006
+ })}
1007
+ </>
1008
+ )}
735
1009
  </DropdownMenuContent>
736
1010
  </DropdownMenu>
737
1011
 
@@ -884,6 +1158,32 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
884
1158
  </TooltipContent>
885
1159
  </Tooltip>
886
1160
 
1161
+ {/* Cesium 3D Context toggle + settings — web only, only when model has georeferencing */}
1162
+ {cesiumAvailable && !desktopShell && (
1163
+ <div className="flex items-center">
1164
+ <Tooltip>
1165
+ <TooltipTrigger asChild>
1166
+ <Button
1167
+ variant={cesiumEnabled ? 'default' : 'ghost'}
1168
+ size="icon-sm"
1169
+ aria-label={cesiumEnabled ? 'Hide 3D World Context (Cesium)' : 'Show 3D World Context (Cesium)'}
1170
+ aria-pressed={cesiumEnabled}
1171
+ onClick={(e) => {
1172
+ (e.currentTarget as HTMLButtonElement).blur();
1173
+ toggleCesium();
1174
+ }}
1175
+ className={cn(cesiumEnabled && 'bg-teal-600 text-white hover:bg-teal-700')}
1176
+ >
1177
+ <Globe2 className="h-4 w-4" />
1178
+ </Button>
1179
+ </TooltipTrigger>
1180
+ <TooltipContent>
1181
+ {cesiumEnabled ? 'Hide' : 'Show'} 3D World Context (Cesium)
1182
+ </TooltipContent>
1183
+ </Tooltip>
1184
+ </div>
1185
+ )}
1186
+
887
1187
  {/* Hover Tooltips toggle */}
888
1188
  <Tooltip>
889
1189
  <TooltipTrigger asChild>
@@ -946,11 +1246,22 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
946
1246
  <div className="flex-1" />
947
1247
 
948
1248
  {/* Loading Progress */}
949
- {loading && progress && (
1249
+ {loading && (geometryProgress || metadataProgress || progress) && (
950
1250
  <div className="flex items-center gap-2 mr-4">
951
- <span className="text-xs text-muted-foreground">{progress.phase}</span>
952
- <Progress value={progress.percent} className="w-32 h-2" />
953
- <span className="text-xs text-muted-foreground">{Math.round(progress.percent)}%</span>
1251
+ <span className="text-xs text-muted-foreground">
1252
+ {(geometryProgress ?? metadataProgress ?? progress)?.phase}
1253
+ {geometryProgress && metadataProgress ? ` | ${metadataProgress.phase}` : ''}
1254
+ </span>
1255
+ {(geometryProgress ?? metadataProgress ?? progress)?.indeterminate ? (
1256
+ <Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground" />
1257
+ ) : (
1258
+ <>
1259
+ <Progress value={(geometryProgress ?? metadataProgress ?? progress)?.percent ?? 0} className="w-32 h-2" />
1260
+ <span className="text-xs text-muted-foreground">
1261
+ {Math.round((geometryProgress ?? metadataProgress ?? progress)?.percent ?? 0)}%
1262
+ </span>
1263
+ </>
1264
+ )}
954
1265
  </div>
955
1266
  )}
956
1267
 
@@ -961,13 +1272,29 @@ export function MainToolbar({ onShowShortcuts }: MainToolbarProps = {} as MainTo
961
1272
 
962
1273
  {/* Right Side Actions */}
963
1274
  <div className="flex items-center gap-2 ml-2 pl-2 border-l border-zinc-200 dark:border-zinc-700/60">
1275
+ {desktopShell ? (
1276
+ <Tooltip>
1277
+ <TooltipTrigger asChild>
1278
+ <Button
1279
+ variant="ghost"
1280
+ size="icon"
1281
+ className="rounded-full"
1282
+ onClick={() => navigateToPath('/settings')}
1283
+ >
1284
+ <Settings className="!h-[20px] !w-[20px]" />
1285
+ </Button>
1286
+ </TooltipTrigger>
1287
+ <TooltipContent>Settings</TooltipContent>
1288
+ </Tooltip>
1289
+ ) : null}
1290
+
964
1291
  <Tooltip>
965
1292
  <TooltipTrigger asChild>
966
1293
  <div>
967
1294
  <ThemeSwitch />
968
1295
  </div>
969
1296
  </TooltipTrigger>
970
- <TooltipContent>Toggle theme</TooltipContent>
1297
+ <TooltipContent>Toggle theme (Shift+click for secret mode)</TooltipContent>
971
1298
  </Tooltip>
972
1299
 
973
1300
  <Tooltip>