@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.
- package/.turbo/turbo-build.log +39 -30
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +132 -0
- package/DESKTOP_CONTRACT_VERSION +1 -0
- package/dist/assets/arrow-CZ5kQ26f.js +20 -0
- package/dist/assets/basketViewActivator-86rgogji.js +1 -0
- package/dist/assets/{bcf-D5-QWGO9.js → bcf-DOG9_WPX.js} +1 -1
- package/dist/assets/{browser-CKs-FY1P.js → browser-C5TFR7sH.js} +1 -1
- package/dist/assets/cesium-ADbP7waU.css +1 -0
- package/dist/assets/cesium-DUOzBlqv.js +17817 -0
- package/dist/assets/drawing-2d-DoxKMqbO.js +257 -0
- package/dist/assets/{exporters-C_6J153K.js → exporters-CcPS9MK5.js} +2898 -2380
- package/dist/assets/geometry.worker-BFUYA08u.js +1 -0
- package/dist/assets/ids-DQ5jY0E8.js +1 -0
- package/dist/assets/ifc-lite_bg-BINvzoCP.wasm +0 -0
- package/dist/assets/{index-jhBr1wbn.js → index-Bfms9I4A.js} +41036 -34613
- package/dist/assets/index-_bfZsDCC.css +1 -0
- package/dist/assets/{maplibre-gl-BpvwNKKy.js → maplibre-gl-CGLcoNXc.js} +1 -1
- package/dist/assets/native-bridge-DUyLCMZS.js +429 -0
- package/dist/assets/{sandbox-B79eavQ3.js → sandbox-C8575tul.js} +4342 -4324
- package/dist/assets/{server-client-D3bUPJJc.js → server-client-BuZK7OST.js} +4 -4
- package/dist/assets/tauri-core-stub-D8Fa-u43.js +1 -0
- package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +1 -0
- package/dist/assets/tauri-fs-stub-BdeRC7aK.js +1 -0
- package/dist/assets/wasm-bridge-JsqEGDV8.js +1 -0
- package/dist/assets/{zip-B-jFFAGa.js → zip-DBEtpeu6.js} +3 -3
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json +1 -0
- package/dist/cesium/Assets/Images/bing_maps_credit.png +0 -0
- package/dist/cesium/Assets/Images/cesium_credit.png +0 -0
- package/dist/cesium/Assets/Images/google_earth_credit.png +0 -0
- package/dist/cesium/Assets/Images/ion-credit.png +0 -0
- package/dist/cesium/Assets/Textures/LensFlare/DirtMask.jpg +0 -0
- package/dist/cesium/Assets/Textures/LensFlare/StarBurst.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/0/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/0/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/tilemapresource.xml +14 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mx.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_my.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mz.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_px.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_py.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_pz.jpg +0 -0
- package/dist/cesium/Assets/Textures/maki/airfield.png +0 -0
- package/dist/cesium/Assets/Textures/maki/airport.png +0 -0
- package/dist/cesium/Assets/Textures/maki/alcohol-shop.png +0 -0
- package/dist/cesium/Assets/Textures/maki/america-football.png +0 -0
- package/dist/cesium/Assets/Textures/maki/art-gallery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bakery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bank.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bar.png +0 -0
- package/dist/cesium/Assets/Textures/maki/baseball.png +0 -0
- package/dist/cesium/Assets/Textures/maki/basketball.png +0 -0
- package/dist/cesium/Assets/Textures/maki/beer.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bicycle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/building.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bus.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cafe.png +0 -0
- package/dist/cesium/Assets/Textures/maki/camera.png +0 -0
- package/dist/cesium/Assets/Textures/maki/campsite.png +0 -0
- package/dist/cesium/Assets/Textures/maki/car.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cemetery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cesium.png +0 -0
- package/dist/cesium/Assets/Textures/maki/chemist.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cinema.png +0 -0
- package/dist/cesium/Assets/Textures/maki/circle-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/circle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/city.png +0 -0
- package/dist/cesium/Assets/Textures/maki/clothing-store.png +0 -0
- package/dist/cesium/Assets/Textures/maki/college.png +0 -0
- package/dist/cesium/Assets/Textures/maki/commercial.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cricket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cross.png +0 -0
- package/dist/cesium/Assets/Textures/maki/dam.png +0 -0
- package/dist/cesium/Assets/Textures/maki/danger.png +0 -0
- package/dist/cesium/Assets/Textures/maki/disability.png +0 -0
- package/dist/cesium/Assets/Textures/maki/dog-park.png +0 -0
- package/dist/cesium/Assets/Textures/maki/embassy.png +0 -0
- package/dist/cesium/Assets/Textures/maki/emergency-telephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/entrance.png +0 -0
- package/dist/cesium/Assets/Textures/maki/farm.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fast-food.png +0 -0
- package/dist/cesium/Assets/Textures/maki/ferry.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fire-station.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fuel.png +0 -0
- package/dist/cesium/Assets/Textures/maki/garden.png +0 -0
- package/dist/cesium/Assets/Textures/maki/gift.png +0 -0
- package/dist/cesium/Assets/Textures/maki/golf.png +0 -0
- package/dist/cesium/Assets/Textures/maki/grocery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/hairdresser.png +0 -0
- package/dist/cesium/Assets/Textures/maki/harbor.png +0 -0
- package/dist/cesium/Assets/Textures/maki/heart.png +0 -0
- package/dist/cesium/Assets/Textures/maki/heliport.png +0 -0
- package/dist/cesium/Assets/Textures/maki/hospital.png +0 -0
- package/dist/cesium/Assets/Textures/maki/ice-cream.png +0 -0
- package/dist/cesium/Assets/Textures/maki/industrial.png +0 -0
- package/dist/cesium/Assets/Textures/maki/land-use.png +0 -0
- package/dist/cesium/Assets/Textures/maki/laundry.png +0 -0
- package/dist/cesium/Assets/Textures/maki/library.png +0 -0
- package/dist/cesium/Assets/Textures/maki/lighthouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/lodging.png +0 -0
- package/dist/cesium/Assets/Textures/maki/logging.png +0 -0
- package/dist/cesium/Assets/Textures/maki/london-underground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/marker-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/marker.png +0 -0
- package/dist/cesium/Assets/Textures/maki/minefield.png +0 -0
- package/dist/cesium/Assets/Textures/maki/mobilephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/monument.png +0 -0
- package/dist/cesium/Assets/Textures/maki/museum.png +0 -0
- package/dist/cesium/Assets/Textures/maki/music.png +0 -0
- package/dist/cesium/Assets/Textures/maki/oil-well.png +0 -0
- package/dist/cesium/Assets/Textures/maki/park.png +0 -0
- package/dist/cesium/Assets/Textures/maki/park2.png +0 -0
- package/dist/cesium/Assets/Textures/maki/parking-garage.png +0 -0
- package/dist/cesium/Assets/Textures/maki/parking.png +0 -0
- package/dist/cesium/Assets/Textures/maki/pharmacy.png +0 -0
- package/dist/cesium/Assets/Textures/maki/pitch.png +0 -0
- package/dist/cesium/Assets/Textures/maki/place-of-worship.png +0 -0
- package/dist/cesium/Assets/Textures/maki/playground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/police.png +0 -0
- package/dist/cesium/Assets/Textures/maki/polling-place.png +0 -0
- package/dist/cesium/Assets/Textures/maki/post.png +0 -0
- package/dist/cesium/Assets/Textures/maki/prison.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-above.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-light.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-metro.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-underground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-christian.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-jewish.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-muslim.png +0 -0
- package/dist/cesium/Assets/Textures/maki/restaurant.png +0 -0
- package/dist/cesium/Assets/Textures/maki/roadblock.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rocket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/school.png +0 -0
- package/dist/cesium/Assets/Textures/maki/scooter.png +0 -0
- package/dist/cesium/Assets/Textures/maki/shop.png +0 -0
- package/dist/cesium/Assets/Textures/maki/skiing.png +0 -0
- package/dist/cesium/Assets/Textures/maki/slaughterhouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/soccer.png +0 -0
- package/dist/cesium/Assets/Textures/maki/square-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/square.png +0 -0
- package/dist/cesium/Assets/Textures/maki/star-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/star.png +0 -0
- package/dist/cesium/Assets/Textures/maki/suitcase.png +0 -0
- package/dist/cesium/Assets/Textures/maki/swimming.png +0 -0
- package/dist/cesium/Assets/Textures/maki/telephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/tennis.png +0 -0
- package/dist/cesium/Assets/Textures/maki/theatre.png +0 -0
- package/dist/cesium/Assets/Textures/maki/toilets.png +0 -0
- package/dist/cesium/Assets/Textures/maki/town-hall.png +0 -0
- package/dist/cesium/Assets/Textures/maki/town.png +0 -0
- package/dist/cesium/Assets/Textures/maki/triangle-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/triangle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/village.png +0 -0
- package/dist/cesium/Assets/Textures/maki/warehouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/waste-basket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/water.png +0 -0
- package/dist/cesium/Assets/Textures/maki/wetland.png +0 -0
- package/dist/cesium/Assets/Textures/maki/zoo.png +0 -0
- package/dist/cesium/Assets/Textures/moonSmall.jpg +0 -0
- package/dist/cesium/Assets/Textures/pin.svg +1 -0
- package/dist/cesium/Assets/Textures/waterNormals.jpg +0 -0
- package/dist/cesium/Assets/Textures/waterNormalsSmall.jpg +0 -0
- package/dist/cesium/Assets/approximateTerrainHeights.json +1 -0
- package/dist/cesium/ThirdParty/Workers/package.json +1 -0
- package/dist/cesium/ThirdParty/Workers/zip-web-worker.js +1 -0
- package/dist/cesium/ThirdParty/basis_transcoder.wasm +0 -0
- package/dist/cesium/ThirdParty/draco_decoder.wasm +0 -0
- package/dist/cesium/ThirdParty/google-earth-dbroot-parser.js +1 -0
- package/dist/cesium/ThirdParty/wasm_splats_bg.wasm +0 -0
- package/dist/cesium/ThirdParty/zip-module.wasm +0 -0
- package/dist/cesium/Widgets/Animation/Animation.css +127 -0
- package/dist/cesium/Widgets/Animation/lighter.css +70 -0
- package/dist/cesium/Widgets/BaseLayerPicker/BaseLayerPicker.css +108 -0
- package/dist/cesium/Widgets/BaseLayerPicker/lighter.css +22 -0
- package/dist/cesium/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css +102 -0
- package/dist/cesium/Widgets/CesiumInspector/CesiumInspector.css +113 -0
- package/dist/cesium/Widgets/CesiumWidget/CesiumWidget.css +119 -0
- package/dist/cesium/Widgets/CesiumWidget/lighter.css +14 -0
- package/dist/cesium/Widgets/FullscreenButton/FullscreenButton.css +8 -0
- package/dist/cesium/Widgets/Geocoder/Geocoder.css +70 -0
- package/dist/cesium/Widgets/Geocoder/lighter.css +17 -0
- package/dist/cesium/Widgets/I3SBuildingSceneLayerExplorer/I3SBuildingSceneLayerExplorer.css +27 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldHillshade.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldImagery.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldOcean.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/azureAerial.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/azureRoads.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingAerial.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingAerialLabels.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingRoads.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/blueMarble.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/earthAtNight.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleContour.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleRoadmap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleSatellite.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleSatelliteLabels.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapQuestOpenStreetMap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxSatellite.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxStreets.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxTerrain.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/naturalEarthII.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/openStreetMap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/sentinel-2.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmooth.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmoothDark.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stamenToner.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stamenWatercolor.png +0 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/Mouse.svg +84 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseLeft.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseMiddle.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseRight.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/Touch.svg +120 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchDrag.svg +129 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchRotate.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchTilt.svg +135 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchZoom.svg +74 -0
- package/dist/cesium/Widgets/Images/TerrainProviders/CesiumWorldTerrain.png +0 -0
- package/dist/cesium/Widgets/Images/TerrainProviders/Ellipsoid.png +0 -0
- package/dist/cesium/Widgets/Images/TimelineIcons.png +0 -0
- package/dist/cesium/Widgets/Images/info-loading.gif +0 -0
- package/dist/cesium/Widgets/InfoBox/InfoBox.css +92 -0
- package/dist/cesium/Widgets/InfoBox/InfoBoxDescription.css +178 -0
- package/dist/cesium/Widgets/NavigationHelpButton/NavigationHelpButton.css +93 -0
- package/dist/cesium/Widgets/NavigationHelpButton/lighter.css +38 -0
- package/dist/cesium/Widgets/PerformanceWatchdog/PerformanceWatchdog.css +15 -0
- package/dist/cesium/Widgets/ProjectionPicker/ProjectionPicker.css +38 -0
- package/dist/cesium/Widgets/SceneModePicker/SceneModePicker.css +56 -0
- package/dist/cesium/Widgets/SelectionIndicator/SelectionIndicator.css +20 -0
- package/dist/cesium/Widgets/Timeline/Timeline.css +103 -0
- package/dist/cesium/Widgets/Timeline/lighter.css +23 -0
- package/dist/cesium/Widgets/VRButton/VRButton.css +8 -0
- package/dist/cesium/Widgets/Viewer/Viewer.css +107 -0
- package/dist/cesium/Widgets/VoxelInspector/VoxelInspector.css +16 -0
- package/dist/cesium/Widgets/lighter.css +237 -0
- package/dist/cesium/Widgets/lighterShared.css +46 -0
- package/dist/cesium/Widgets/shared.css +103 -0
- package/dist/cesium/Widgets/widgets.css +1342 -0
- package/dist/cesium/Workers/chunk-23ZQ2IVV.js +29 -0
- package/dist/cesium/Workers/chunk-2EQO3Q56.js +26 -0
- package/dist/cesium/Workers/chunk-2MJIIVP4.js +26 -0
- package/dist/cesium/Workers/chunk-2TE5NTVD.js +26 -0
- package/dist/cesium/Workers/chunk-2ZBHLJST.js +26 -0
- package/dist/cesium/Workers/chunk-5TJMAQVL.js +26 -0
- package/dist/cesium/Workers/chunk-6BD4U3VO.js +26 -0
- package/dist/cesium/Workers/chunk-7TVGLKQF.js +26 -0
- package/dist/cesium/Workers/chunk-BTSYJ5XU.js +26 -0
- package/dist/cesium/Workers/chunk-BXMEEOCS.js +63 -0
- package/dist/cesium/Workers/chunk-BYLCY7GP.js +29 -0
- package/dist/cesium/Workers/chunk-CTHM3W6I.js +26 -0
- package/dist/cesium/Workers/chunk-CUUSNIVQ.js +26 -0
- package/dist/cesium/Workers/chunk-E3JOOS3S.js +26 -0
- package/dist/cesium/Workers/chunk-E7KYDCM5.js +26 -0
- package/dist/cesium/Workers/chunk-EDVBB7SS.js +27 -0
- package/dist/cesium/Workers/chunk-EFBN7QNX.js +26 -0
- package/dist/cesium/Workers/chunk-EQ4YRVWL.js +26 -0
- package/dist/cesium/Workers/chunk-F6PRE7D6.js +26 -0
- package/dist/cesium/Workers/chunk-FC4ZZ65J.js +26 -0
- package/dist/cesium/Workers/chunk-FFBVWF2L.js +26 -0
- package/dist/cesium/Workers/chunk-GBAA6GVX.js +26 -0
- package/dist/cesium/Workers/chunk-ICALLYLG.js +26 -0
- package/dist/cesium/Workers/chunk-ILRYTWTP.js +26 -0
- package/dist/cesium/Workers/chunk-IRNLBSEJ.js +26 -0
- package/dist/cesium/Workers/chunk-IX4VMHEV.js +26 -0
- package/dist/cesium/Workers/chunk-L6QHHACZ.js +26 -0
- package/dist/cesium/Workers/chunk-LI2ZSORM.js +26 -0
- package/dist/cesium/Workers/chunk-LSLE2RL4.js +26 -0
- package/dist/cesium/Workers/chunk-M4HLDBCG.js +26 -0
- package/dist/cesium/Workers/chunk-MJHHSGEH.js +26 -0
- package/dist/cesium/Workers/chunk-NMVKML6W.js +26 -0
- package/dist/cesium/Workers/chunk-OCWJRAXS.js +26 -0
- package/dist/cesium/Workers/chunk-OIRKANTH.js +26 -0
- package/dist/cesium/Workers/chunk-OIT7J4IC.js +26 -0
- package/dist/cesium/Workers/chunk-OLZ3FYUM.js +26 -0
- package/dist/cesium/Workers/chunk-Q5BPHJQF.js +26 -0
- package/dist/cesium/Workers/chunk-QFM5DCMQ.js +26 -0
- package/dist/cesium/Workers/chunk-QKUIYMGC.js +28 -0
- package/dist/cesium/Workers/chunk-S44JILQT.js +26 -0
- package/dist/cesium/Workers/chunk-SLT4J352.js +26 -0
- package/dist/cesium/Workers/chunk-SQMIIXB7.js +26 -0
- package/dist/cesium/Workers/chunk-TJ4XLGBQ.js +26 -0
- package/dist/cesium/Workers/chunk-TNSUQXWK.js +27 -0
- package/dist/cesium/Workers/chunk-UBOGZS7F.js +26 -0
- package/dist/cesium/Workers/chunk-V3OSTMM6.js +26 -0
- package/dist/cesium/Workers/chunk-V7QEYVP3.js +26 -0
- package/dist/cesium/Workers/chunk-VUKYSU4H.js +26 -0
- package/dist/cesium/Workers/chunk-W37FE5GR.js +26 -0
- package/dist/cesium/Workers/chunk-WBOV35NL.js +26 -0
- package/dist/cesium/Workers/chunk-WPMZLB3Y.js +26 -0
- package/dist/cesium/Workers/chunk-WWWZVEEH.js +26 -0
- package/dist/cesium/Workers/chunk-XFIQ5DEQ.js +28 -0
- package/dist/cesium/Workers/chunk-XQHLGIO7.js +26 -0
- package/dist/cesium/Workers/chunk-XUSCFAVF.js +26 -0
- package/dist/cesium/Workers/chunk-YP7I5QBZ.js +26 -0
- package/dist/cesium/Workers/chunk-Z3QF2EHT.js +26 -0
- package/dist/cesium/Workers/combineGeometry.js +26 -0
- package/dist/cesium/Workers/createBoxGeometry.js +26 -0
- package/dist/cesium/Workers/createBoxOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCircleGeometry.js +26 -0
- package/dist/cesium/Workers/createCircleOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCoplanarPolygonGeometry.js +26 -0
- package/dist/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCorridorGeometry.js +26 -0
- package/dist/cesium/Workers/createCorridorOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCylinderGeometry.js +26 -0
- package/dist/cesium/Workers/createCylinderOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipseGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipseOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipsoidGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipsoidOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createFrustumGeometry.js +26 -0
- package/dist/cesium/Workers/createFrustumOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createGeometry.js +26 -0
- package/dist/cesium/Workers/createGroundPolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createPlaneGeometry.js +26 -0
- package/dist/cesium/Workers/createPlaneOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolygonGeometry.js +26 -0
- package/dist/cesium/Workers/createPolygonOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineVolumeGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineVolumeOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createRectangleGeometry.js +26 -0
- package/dist/cesium/Workers/createRectangleOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createSimplePolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createSphereGeometry.js +26 -0
- package/dist/cesium/Workers/createSphereOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createTaskProcessorWorker.js +26 -0
- package/dist/cesium/Workers/createVectorTileClampedPolylines.js +26 -0
- package/dist/cesium/Workers/createVectorTileGeometries.js +26 -0
- package/dist/cesium/Workers/createVectorTilePoints.js +26 -0
- package/dist/cesium/Workers/createVectorTilePolygons.js +26 -0
- package/dist/cesium/Workers/createVectorTilePolylines.js +26 -0
- package/dist/cesium/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -0
- package/dist/cesium/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +26 -0
- package/dist/cesium/Workers/createVerticesFromHeightmap.js +26 -0
- package/dist/cesium/Workers/createVerticesFromQuantizedTerrainMesh.js +26 -0
- package/dist/cesium/Workers/createWallGeometry.js +26 -0
- package/dist/cesium/Workers/createWallOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/decodeDraco.js +26 -0
- package/dist/cesium/Workers/decodeGoogleEarthEnterprisePacket.js +26 -0
- package/dist/cesium/Workers/decodeI3S.js +26 -0
- package/dist/cesium/Workers/gaussianSplatSorter.js +26 -0
- package/dist/cesium/Workers/gaussianSplatTextureGenerator.js +26 -0
- package/dist/cesium/Workers/incrementallyBuildTerrainPicker.js +26 -0
- package/dist/cesium/Workers/transcodeKTX2.js +56 -0
- package/dist/cesium/Workers/transferTypedArrayTest.js +26 -0
- package/dist/cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
- package/dist/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -0
- package/dist/index.html +13 -10
- package/index.html +1 -0
- package/package.json +18 -15
- package/src/App.tsx +7 -9
- package/src/components/viewer/BCFPanel.tsx +46 -4
- package/src/components/viewer/CesiumOverlay.tsx +715 -0
- package/src/components/viewer/CesiumSettingsDialog.tsx +100 -0
- package/src/components/viewer/ChatPanel.tsx +232 -90
- package/src/components/viewer/CommandPalette.tsx +6 -1
- package/src/components/viewer/DesktopEntitlementBanner.tsx +74 -0
- package/src/components/viewer/ExportChangesButton.tsx +6 -1
- package/src/components/viewer/ExportDialog.tsx +22 -6
- package/src/components/viewer/HierarchyPanel.tsx +196 -0
- package/src/components/viewer/IDSPanel.tsx +52 -3
- package/src/components/viewer/KeyboardShortcutsDialog.tsx +1 -1
- package/src/components/viewer/MainToolbar.tsx +355 -28
- package/src/components/viewer/PropertiesPanel.tsx +234 -81
- package/src/components/viewer/ScriptPanel.tsx +34 -8
- package/src/components/viewer/SettingsPage.tsx +581 -0
- package/src/components/viewer/StatusBar.tsx +17 -1
- package/src/components/viewer/ThemeSwitch.tsx +63 -7
- package/src/components/viewer/ViewerLayout.tsx +48 -6
- package/src/components/viewer/Viewport.tsx +61 -8
- package/src/components/viewer/ViewportContainer.tsx +265 -28
- package/src/components/viewer/ViewportOverlays.tsx +132 -27
- package/src/components/viewer/bcf/BCFTopicDetail.tsx +4 -4
- package/src/components/viewer/chat/ModelSelector.tsx +90 -54
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +229 -55
- package/src/components/viewer/properties/LocationMap.tsx +462 -19
- package/src/components/viewer/properties/ModelMetadataPanel.tsx +1 -1
- package/src/components/viewer/selectionHandlers.ts +4 -3
- package/src/components/viewer/tools/SectionCapControls.tsx +237 -0
- package/src/components/viewer/tools/SectionPanel.tsx +39 -18
- package/src/components/viewer/useAnimationLoop.ts +13 -1
- package/src/components/viewer/useGeometryStreaming.ts +127 -40
- package/src/components/viewer/useMouseControls.ts +4 -1
- package/src/components/viewer/useRenderUpdates.ts +1 -1
- package/src/hooks/ids/idsDataAccessor.ts +60 -24
- package/src/hooks/ingest/viewerModelIngest.ts +280 -0
- package/src/hooks/useIDS.ts +1 -1
- package/src/hooks/useIfc.ts +7 -1
- package/src/hooks/useIfcCache.ts +28 -15
- package/src/hooks/useIfcFederation.ts +378 -291
- package/src/hooks/useIfcLoader.ts +1657 -130
- package/src/hooks/useIfcServer.ts +0 -69
- package/src/hooks/useViewControls.ts +13 -5
- package/src/index.css +484 -10
- package/src/lib/desktop/desktopEntitlementEvents.ts +39 -0
- package/src/lib/desktop-entitlement.ts +43 -0
- package/src/lib/desktop-product.ts +124 -0
- package/src/lib/geo/cesium-bridge.ts +318 -0
- package/src/lib/geo/effective-georef.test.ts +73 -0
- package/src/lib/geo/effective-georef.ts +111 -0
- package/src/lib/geo/reproject.ts +249 -37
- package/src/lib/llm/byok-guard.test.ts +77 -0
- package/src/lib/llm/byok-guard.ts +39 -0
- package/src/lib/llm/free-models.test.ts +0 -6
- package/src/lib/llm/models.ts +104 -42
- package/src/lib/llm/stream-client.ts +74 -110
- package/src/lib/llm/stream-direct.test.ts +130 -0
- package/src/lib/llm/stream-direct.ts +316 -0
- package/src/lib/llm/types.ts +14 -2
- package/src/lib/recent-files.ts +2 -1
- package/src/main.tsx +1 -10
- package/src/services/analysis-extensions.ts +125 -0
- package/src/services/api-keys.ts +73 -0
- package/src/services/app-navigation.ts +13 -0
- package/src/services/bsdd.ts +53 -4
- package/src/services/cacheService.ts +1 -1
- package/src/services/desktop-cache.ts +43 -0
- package/src/services/desktop-export.ts +77 -0
- package/src/services/desktop-harness.ts +196 -0
- package/src/services/desktop-logger.ts +20 -0
- package/src/services/desktop-native-metadata.ts +207 -0
- package/src/services/desktop-panel-actions.ts +43 -0
- package/src/services/desktop-preferences.ts +44 -0
- package/src/services/file-dialog.ts +147 -0
- package/src/services/tauri-core-stub.ts +7 -0
- package/src/services/tauri-dialog-stub.ts +7 -0
- package/src/services/tauri-fs-stub.ts +7 -0
- package/src/store/constants.ts +20 -2
- package/src/store/index.ts +52 -7
- package/src/store/slices/cesiumSlice.ts +127 -0
- package/src/store/slices/chatSlice.test.ts +6 -76
- package/src/store/slices/chatSlice.ts +21 -58
- package/src/store/slices/dataSlice.ts +139 -28
- package/src/store/slices/desktopEntitlementSlice.ts +86 -0
- package/src/store/slices/loadingSlice.ts +14 -2
- package/src/store/slices/modelSlice.ts +58 -3
- package/src/store/slices/sectionSlice.test.ts +87 -7
- package/src/store/slices/sectionSlice.ts +151 -5
- package/src/store/slices/uiSlice.ts +28 -5
- package/src/store/types.ts +122 -2
- package/src/store.ts +1 -1
- package/src/utils/desktopModelSnapshot.ts +358 -0
- package/src/utils/ifcConfig.ts +6 -1
- package/src/utils/nativeSpatialDataStore.ts +253 -0
- package/src/utils/serverDataModel.ts +4 -0
- package/src/utils/spatialHierarchy.ts +10 -11
- package/src/utils/viewportUtils.ts +7 -2
- package/src/vite-env.d.ts +0 -4
- package/vite.config.ts +24 -0
- package/dist/assets/arrow-DJf2ErbF.js +0 -20
- package/dist/assets/basketViewActivator-aojwdomq.js +0 -1
- package/dist/assets/desktop-cache-oPzaWXYE.js +0 -1
- package/dist/assets/drawing-2d-gWfpdfYe.js +0 -257
- package/dist/assets/geometry.worker-Nz9_YIqh.js +0 -1
- package/dist/assets/ids-B4jTqB1O.js +0 -1
- package/dist/assets/ifc-lite_bg-eSkBTizQ.wasm +0 -0
- package/dist/assets/index-pbE7itQS.css +0 -1
- package/dist/assets/native-bridge-DSIyEYXG.js +0 -113
- package/dist/assets/wasm-bridge-B0J07fZZ.js +0 -1
- package/src/components/viewer/UpgradePage.tsx +0 -69
- package/src/lib/llm/ClerkChatSync.tsx +0 -74
- package/src/lib/llm/clerk-auth.ts +0 -62
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Direct browser-to-provider streaming for BYOK (Bring Your Own Key) models.
|
|
7
|
+
*
|
|
8
|
+
* Anthropic: Uses the official @anthropic-ai/sdk with `dangerouslyAllowBrowser`.
|
|
9
|
+
* OpenAI: Uses fetch against the OpenAI chat completions API (same SSE format
|
|
10
|
+
* the proxy already returns, so SSE parsing is shared).
|
|
11
|
+
*
|
|
12
|
+
* Keys are stored in localStorage and sent directly to the provider.
|
|
13
|
+
* They never pass through our server.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
17
|
+
import { readSseStream, type StreamMessage, type StreamOptions } from './stream-client.js';
|
|
18
|
+
|
|
19
|
+
const STREAM_REQUEST_TIMEOUT_MS = 45_000;
|
|
20
|
+
|
|
21
|
+
// ── Anthropic ──────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
type AnthropicMediaType = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
|
|
24
|
+
|
|
25
|
+
type AnthropicContentBlock =
|
|
26
|
+
| { type: 'text'; text: string }
|
|
27
|
+
| { type: 'image'; source: { type: 'base64'; media_type: AnthropicMediaType; data: string } };
|
|
28
|
+
|
|
29
|
+
function toAnthropicMessages(
|
|
30
|
+
messages: StreamMessage[],
|
|
31
|
+
): Array<{ role: 'user' | 'assistant'; content: string | AnthropicContentBlock[] }> {
|
|
32
|
+
return messages
|
|
33
|
+
.filter((m) => m.role === 'user' || m.role === 'assistant')
|
|
34
|
+
.map((m) => {
|
|
35
|
+
if (typeof m.content === 'string') {
|
|
36
|
+
return { role: m.role as 'user' | 'assistant', content: m.content };
|
|
37
|
+
}
|
|
38
|
+
// Multimodal content — convert OpenAI-style parts to Anthropic format
|
|
39
|
+
const blocks: AnthropicContentBlock[] = m.content.map((part) => {
|
|
40
|
+
if (part.type === 'text') {
|
|
41
|
+
return { type: 'text' as const, text: part.text };
|
|
42
|
+
}
|
|
43
|
+
// image_url → Anthropic image block
|
|
44
|
+
const dataUrl = part.image_url.url;
|
|
45
|
+
const match = dataUrl.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
46
|
+
if (match) {
|
|
47
|
+
return {
|
|
48
|
+
type: 'image' as const,
|
|
49
|
+
source: {
|
|
50
|
+
type: 'base64' as const,
|
|
51
|
+
media_type: match[1] as AnthropicMediaType,
|
|
52
|
+
data: match[2],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// Fallback: pass URL as text
|
|
57
|
+
return { type: 'text' as const, text: `[Image: ${dataUrl.slice(0, 100)}]` };
|
|
58
|
+
});
|
|
59
|
+
return { role: m.role as 'user' | 'assistant', content: blocks };
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function streamAnthropicChat(
|
|
64
|
+
apiKey: string,
|
|
65
|
+
options: Omit<StreamOptions, 'proxyUrl' | 'authToken' | 'onUsageInfo'>,
|
|
66
|
+
): Promise<void> {
|
|
67
|
+
const { model, messages, system, signal, onChunk, onComplete, onError, onFinishReason } = options;
|
|
68
|
+
|
|
69
|
+
const client = new Anthropic({
|
|
70
|
+
apiKey,
|
|
71
|
+
dangerouslyAllowBrowser: true,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let fullText = '';
|
|
75
|
+
try {
|
|
76
|
+
const stream = client.messages.stream({
|
|
77
|
+
model,
|
|
78
|
+
max_tokens: 8192,
|
|
79
|
+
temperature: 0.3,
|
|
80
|
+
system: system || undefined,
|
|
81
|
+
messages: toAnthropicMessages(messages),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Wire up abort signal
|
|
85
|
+
if (signal) {
|
|
86
|
+
const onAbort = () => stream.abort();
|
|
87
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
88
|
+
stream.on('end', () => signal.removeEventListener('abort', onAbort));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
stream.on('text', (text) => {
|
|
92
|
+
fullText += text;
|
|
93
|
+
onChunk(text);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const finalMessage = await stream.finalMessage();
|
|
97
|
+
|
|
98
|
+
if (signal?.aborted) return;
|
|
99
|
+
|
|
100
|
+
const stopReason = finalMessage.stop_reason;
|
|
101
|
+
onFinishReason?.(stopReason === 'end_turn' ? 'stop' : stopReason);
|
|
102
|
+
onComplete(fullText);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (signal?.aborted) return;
|
|
105
|
+
|
|
106
|
+
if (err instanceof Anthropic.APIError) {
|
|
107
|
+
const msg = err.status === 401
|
|
108
|
+
? 'Invalid Anthropic API key. Check your key in Settings.'
|
|
109
|
+
: err.status === 429
|
|
110
|
+
? 'Anthropic rate limit reached. Please wait and try again.'
|
|
111
|
+
: `Anthropic error (${err.status}): ${err.message}`;
|
|
112
|
+
onError(new Error(msg));
|
|
113
|
+
} else {
|
|
114
|
+
onError(err instanceof Error ? err : new Error(String(err)));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── OpenAI ─────────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
import { getModelById } from './models.js';
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Stream an OpenAI model. Automatically picks the right API:
|
|
125
|
+
* - Chat Completions (`/v1/chat/completions`) for standard chat models
|
|
126
|
+
* - Responses (`/v1/responses`) for Codex-style models
|
|
127
|
+
*/
|
|
128
|
+
export async function streamOpenAiChat(
|
|
129
|
+
apiKey: string,
|
|
130
|
+
options: Omit<StreamOptions, 'proxyUrl' | 'authToken' | 'onUsageInfo'>,
|
|
131
|
+
): Promise<void> {
|
|
132
|
+
const modelDef = getModelById(options.model);
|
|
133
|
+
if (modelDef?.openaiApi === 'responses') {
|
|
134
|
+
return streamOpenAiResponses(apiKey, options);
|
|
135
|
+
}
|
|
136
|
+
return streamOpenAiChatCompletions(apiKey, options);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Standard Chat Completions API (GPT-5.4, GPT-5.4 Mini, etc.) */
|
|
140
|
+
async function streamOpenAiChatCompletions(
|
|
141
|
+
apiKey: string,
|
|
142
|
+
options: Omit<StreamOptions, 'proxyUrl' | 'authToken' | 'onUsageInfo'>,
|
|
143
|
+
): Promise<void> {
|
|
144
|
+
const { model, messages, system, signal, onChunk, onComplete, onError, onFinishReason } = options;
|
|
145
|
+
|
|
146
|
+
const allMessages: StreamMessage[] = system
|
|
147
|
+
? [{ role: 'system', content: system }, ...messages]
|
|
148
|
+
: [...messages];
|
|
149
|
+
|
|
150
|
+
const { response, cleanup } = await openAiFetch(
|
|
151
|
+
'https://api.openai.com/v1/chat/completions',
|
|
152
|
+
{
|
|
153
|
+
model,
|
|
154
|
+
messages: allMessages.map((m) => ({ role: m.role, content: m.content })),
|
|
155
|
+
stream: true,
|
|
156
|
+
temperature: 0.3,
|
|
157
|
+
max_completion_tokens: 8192,
|
|
158
|
+
},
|
|
159
|
+
apiKey,
|
|
160
|
+
signal,
|
|
161
|
+
onError,
|
|
162
|
+
);
|
|
163
|
+
if (!response) return;
|
|
164
|
+
|
|
165
|
+
if (!response.body) { cleanup(); onError(new Error('No response body')); return; }
|
|
166
|
+
|
|
167
|
+
let fullText = '';
|
|
168
|
+
let finishReason: string | null = null;
|
|
169
|
+
|
|
170
|
+
const ok = await readSseStream(response.body, signal, (data) => {
|
|
171
|
+
const parsed = JSON.parse(data) as {
|
|
172
|
+
choices?: Array<{ delta?: { content?: string }; finish_reason?: string | null }>;
|
|
173
|
+
};
|
|
174
|
+
const content = parsed.choices?.[0]?.delta?.content;
|
|
175
|
+
if (content) { fullText += content; onChunk(content); }
|
|
176
|
+
const fr = parsed.choices?.[0]?.finish_reason;
|
|
177
|
+
if (fr) finishReason = fr;
|
|
178
|
+
}, onError);
|
|
179
|
+
|
|
180
|
+
cleanup();
|
|
181
|
+
if (ok) { onFinishReason?.(finishReason); onComplete(fullText); }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Responses API for Codex-style models (GPT-5.3 Codex) */
|
|
185
|
+
async function streamOpenAiResponses(
|
|
186
|
+
apiKey: string,
|
|
187
|
+
options: Omit<StreamOptions, 'proxyUrl' | 'authToken' | 'onUsageInfo'>,
|
|
188
|
+
): Promise<void> {
|
|
189
|
+
const { model, messages, system, signal, onChunk, onComplete, onError, onFinishReason } = options;
|
|
190
|
+
|
|
191
|
+
// Build the input array: system instructions + conversation
|
|
192
|
+
const input: Array<{ role: string; content: string | unknown[] }> = [];
|
|
193
|
+
if (system) {
|
|
194
|
+
input.push({ role: 'developer', content: system });
|
|
195
|
+
}
|
|
196
|
+
for (const m of messages) {
|
|
197
|
+
input.push({ role: m.role, content: m.content });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const { response, cleanup } = await openAiFetch(
|
|
201
|
+
'https://api.openai.com/v1/responses',
|
|
202
|
+
{
|
|
203
|
+
model,
|
|
204
|
+
input,
|
|
205
|
+
stream: true,
|
|
206
|
+
max_output_tokens: 8192,
|
|
207
|
+
},
|
|
208
|
+
apiKey,
|
|
209
|
+
signal,
|
|
210
|
+
onError,
|
|
211
|
+
);
|
|
212
|
+
if (!response) return;
|
|
213
|
+
|
|
214
|
+
if (!response.body) { cleanup(); onError(new Error('No response body')); return; }
|
|
215
|
+
|
|
216
|
+
let fullText = '';
|
|
217
|
+
// Map Responses API terminal events → chat-style finish_reason.
|
|
218
|
+
// `response.incomplete` is any non-completed terminal state: when the
|
|
219
|
+
// reason is `max_output_tokens` — or simply absent — map to 'length' so
|
|
220
|
+
// the ChatPanel "Continue" UX can resume a truncated Codex reply. Other
|
|
221
|
+
// explicit reasons (e.g. `content_filter`) pass through unchanged.
|
|
222
|
+
let finishReason: string | null = 'stop';
|
|
223
|
+
|
|
224
|
+
const ok = await readSseStream(response.body, signal, (data) => {
|
|
225
|
+
const event = JSON.parse(data) as {
|
|
226
|
+
type?: string;
|
|
227
|
+
delta?: string;
|
|
228
|
+
response?: {
|
|
229
|
+
status?: string;
|
|
230
|
+
incomplete_details?: { reason?: string } | null;
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
if (event.type === 'response.output_text.delta' && event.delta) {
|
|
234
|
+
fullText += event.delta;
|
|
235
|
+
onChunk(event.delta);
|
|
236
|
+
} else if (event.type === 'response.incomplete') {
|
|
237
|
+
const reason = event.response?.incomplete_details?.reason;
|
|
238
|
+
finishReason = reason == null || reason === 'max_output_tokens' ? 'length' : reason;
|
|
239
|
+
} else if (event.type === 'response.completed') {
|
|
240
|
+
finishReason = 'stop';
|
|
241
|
+
}
|
|
242
|
+
}, onError);
|
|
243
|
+
|
|
244
|
+
cleanup();
|
|
245
|
+
if (ok) { onFinishReason?.(finishReason); onComplete(fullText); }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ── Shared helpers ─────────────────────────────────────────────────────────
|
|
249
|
+
|
|
250
|
+
async function openAiFetch(
|
|
251
|
+
url: string,
|
|
252
|
+
body: Record<string, unknown>,
|
|
253
|
+
apiKey: string,
|
|
254
|
+
signal: AbortSignal | undefined,
|
|
255
|
+
onError: (err: Error) => void,
|
|
256
|
+
): Promise<{ response: Response | null; cleanup: () => void }> {
|
|
257
|
+
const controller = new AbortController();
|
|
258
|
+
const timeoutId = setTimeout(
|
|
259
|
+
() => controller.abort(new Error('Chat request timed out. Please try again.')),
|
|
260
|
+
STREAM_REQUEST_TIMEOUT_MS,
|
|
261
|
+
);
|
|
262
|
+
const abortFromParent = () => controller.abort(signal?.reason);
|
|
263
|
+
if (signal) {
|
|
264
|
+
if (signal.aborted) { clearTimeout(timeoutId); return { response: null, cleanup: () => {} }; }
|
|
265
|
+
signal.addEventListener('abort', abortFromParent, { once: true });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// cleanup() clears the connect timeout and removes the abort listener.
|
|
269
|
+
// Callers must call it AFTER streaming completes, not before — otherwise
|
|
270
|
+
// user cancellation during SSE consumption won't abort the fetch.
|
|
271
|
+
const cleanup = () => {
|
|
272
|
+
clearTimeout(timeoutId);
|
|
273
|
+
signal?.removeEventListener('abort', abortFromParent);
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
let response: Response;
|
|
277
|
+
try {
|
|
278
|
+
response = await fetch(url, {
|
|
279
|
+
method: 'POST',
|
|
280
|
+
headers: {
|
|
281
|
+
Authorization: `Bearer ${apiKey}`,
|
|
282
|
+
'Content-Type': 'application/json',
|
|
283
|
+
},
|
|
284
|
+
body: JSON.stringify(body),
|
|
285
|
+
signal: controller.signal,
|
|
286
|
+
});
|
|
287
|
+
} catch (err) {
|
|
288
|
+
cleanup();
|
|
289
|
+
if (signal?.aborted) return { response: null, cleanup: () => {} };
|
|
290
|
+
if (controller.signal.aborted && controller.signal.reason instanceof Error) {
|
|
291
|
+
onError(controller.signal.reason);
|
|
292
|
+
} else {
|
|
293
|
+
onError(err instanceof Error ? err : new Error(String(err)));
|
|
294
|
+
}
|
|
295
|
+
return { response: null, cleanup: () => {} };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!response.ok) {
|
|
299
|
+
cleanup();
|
|
300
|
+
let detail = `OpenAI error (${response.status})`;
|
|
301
|
+
try {
|
|
302
|
+
const errBody = (await response.json()) as { error?: { message?: string } };
|
|
303
|
+
if (response.status === 401) {
|
|
304
|
+
detail = 'Invalid OpenAI API key. Check your key in the chat panel.';
|
|
305
|
+
} else if (response.status === 429) {
|
|
306
|
+
detail = 'OpenAI rate limit reached. Please wait and try again.';
|
|
307
|
+
} else if (errBody.error?.message) {
|
|
308
|
+
detail = `OpenAI: ${errBody.error.message}`;
|
|
309
|
+
}
|
|
310
|
+
} catch { /* ignore parse failure */ }
|
|
311
|
+
onError(new Error(detail));
|
|
312
|
+
return { response: null, cleanup: () => {} };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return { response, cleanup };
|
|
316
|
+
}
|
package/src/lib/llm/types.ts
CHANGED
|
@@ -126,7 +126,15 @@ export interface FileAttachment {
|
|
|
126
126
|
isImage?: boolean;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
export type ModelTier = 'free' | '
|
|
129
|
+
export type ModelTier = 'free' | 'byok';
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Where requests for this model are routed.
|
|
133
|
+
* - 'proxy': through the server-side proxy (free models)
|
|
134
|
+
* - 'anthropic': direct browser-to-Anthropic API (user's own key)
|
|
135
|
+
* - 'openai': direct browser-to-OpenAI API (user's own key)
|
|
136
|
+
*/
|
|
137
|
+
export type ModelSource = 'proxy' | 'anthropic' | 'openai';
|
|
130
138
|
|
|
131
139
|
/** Relative cost indicator for paid models */
|
|
132
140
|
export type ModelCost = '$' | '$$' | '$$$';
|
|
@@ -136,6 +144,8 @@ export interface LLMModel {
|
|
|
136
144
|
name: string;
|
|
137
145
|
provider: string;
|
|
138
146
|
tier: ModelTier;
|
|
147
|
+
/** Where requests are routed — proxy (free) or direct to provider (BYOK) */
|
|
148
|
+
source: ModelSource;
|
|
139
149
|
contextWindow: number;
|
|
140
150
|
/** Whether this model accepts image inputs in chat content */
|
|
141
151
|
supportsImages: boolean;
|
|
@@ -143,8 +153,10 @@ export interface LLMModel {
|
|
|
143
153
|
supportsFileAttachments: boolean;
|
|
144
154
|
/** Notes shown in model selector */
|
|
145
155
|
notes?: string;
|
|
146
|
-
/** Relative cost indicator (
|
|
156
|
+
/** Relative cost indicator (BYOK models only) */
|
|
147
157
|
cost?: ModelCost;
|
|
158
|
+
/** OpenAI API variant: 'chat' (default) or 'responses' (Codex-style models) */
|
|
159
|
+
openaiApi?: 'chat' | 'responses';
|
|
148
160
|
}
|
|
149
161
|
|
|
150
162
|
export type ChatStatus = 'idle' | 'sending' | 'streaming' | 'error';
|
package/src/lib/recent-files.ts
CHANGED
|
@@ -103,7 +103,8 @@ export async function cacheFileBlobs(files: File[]): Promise<void> {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
/** Retrieve a cached file blob and reconstruct a File object. */
|
|
106
|
-
export async function getCachedFile(
|
|
106
|
+
export async function getCachedFile(target: string | RecentFileEntry): Promise<File | null> {
|
|
107
|
+
const name = typeof target === 'string' ? target : target.name;
|
|
107
108
|
try {
|
|
108
109
|
const db = await openDB();
|
|
109
110
|
const tx = db.transaction(STORE_NAME, 'readonly');
|
package/src/main.tsx
CHANGED
|
@@ -8,21 +8,12 @@
|
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import ReactDOM from 'react-dom/client';
|
|
11
|
-
import { ClerkProvider } from '@clerk/clerk-react';
|
|
12
11
|
import { App } from './App';
|
|
13
12
|
import './index.css';
|
|
14
13
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
15
14
|
|
|
16
|
-
const clerkPublishableKey = (import.meta.env.VITE_CLERK_PUBLISHABLE_KEY as string | undefined)?.trim();
|
|
17
|
-
|
|
18
15
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
19
16
|
<React.StrictMode>
|
|
20
|
-
|
|
21
|
-
<ClerkProvider publishableKey={clerkPublishableKey}>
|
|
22
|
-
<App />
|
|
23
|
-
</ClerkProvider>
|
|
24
|
-
) : (
|
|
25
|
-
<App />
|
|
26
|
-
)}
|
|
17
|
+
<App />
|
|
27
18
|
</React.StrictMode>
|
|
28
19
|
);
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
export type AnalysisExtensionPlacement = 'right' | 'bottom';
|
|
8
|
+
|
|
9
|
+
export interface AnalysisExtensionRenderProps {
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AnalysisExtensionDefinition {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
placement?: AnalysisExtensionPlacement;
|
|
18
|
+
icon: ComponentType<{ className?: string }>;
|
|
19
|
+
renderPanel: (props: AnalysisExtensionRenderProps) => ReactNode;
|
|
20
|
+
onBeforeOpen?: () => boolean | void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AnalysisExtensionsSnapshot {
|
|
24
|
+
activeId: string | null;
|
|
25
|
+
extensions: AnalysisExtensionDefinition[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const listeners = new Set<() => void>();
|
|
29
|
+
const extensions = new Map<string, AnalysisExtensionDefinition>();
|
|
30
|
+
|
|
31
|
+
let activeId: string | null = null;
|
|
32
|
+
let snapshot: AnalysisExtensionsSnapshot = {
|
|
33
|
+
activeId,
|
|
34
|
+
extensions: [],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function rebuildSnapshot(): void {
|
|
38
|
+
snapshot = {
|
|
39
|
+
activeId,
|
|
40
|
+
extensions: Array.from(extensions.values()),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function emit(): void {
|
|
45
|
+
rebuildSnapshot();
|
|
46
|
+
listeners.forEach((listener) => listener());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function setActiveId(nextActiveId: string | null): void {
|
|
50
|
+
if (activeId === nextActiveId) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
activeId = nextActiveId;
|
|
54
|
+
emit();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function canOpen(definition: AnalysisExtensionDefinition): boolean {
|
|
58
|
+
return definition.onBeforeOpen?.() !== false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function subscribeAnalysisExtensions(listener: () => void): () => void {
|
|
62
|
+
listeners.add(listener);
|
|
63
|
+
return () => {
|
|
64
|
+
listeners.delete(listener);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getAnalysisExtensionsSnapshot(): AnalysisExtensionsSnapshot {
|
|
69
|
+
return snapshot;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getAnalysisExtensionById(id: string | null | undefined): AnalysisExtensionDefinition | null {
|
|
73
|
+
if (!id) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return extensions.get(id) ?? null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function closeActiveAnalysisExtension(): void {
|
|
80
|
+
setActiveId(null);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function openAnalysisExtension(id: string): boolean {
|
|
84
|
+
const definition = extensions.get(id);
|
|
85
|
+
if (!definition) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (!canOpen(definition)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
setActiveId(id);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function toggleAnalysisExtension(id: string): boolean {
|
|
96
|
+
if (activeId === id) {
|
|
97
|
+
closeActiveAnalysisExtension();
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return openAnalysisExtension(id);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function registerAnalysisExtensions(definitions: AnalysisExtensionDefinition[]): () => void {
|
|
104
|
+
const nextEntries = definitions.map((definition) => [definition.id, definition] as const);
|
|
105
|
+
nextEntries.forEach(([id, definition]) => {
|
|
106
|
+
extensions.set(id, definition);
|
|
107
|
+
});
|
|
108
|
+
emit();
|
|
109
|
+
|
|
110
|
+
return () => {
|
|
111
|
+
let shouldEmit = false;
|
|
112
|
+
for (const [id] of nextEntries) {
|
|
113
|
+
if (extensions.delete(id)) {
|
|
114
|
+
shouldEmit = true;
|
|
115
|
+
}
|
|
116
|
+
if (activeId === id) {
|
|
117
|
+
activeId = null;
|
|
118
|
+
shouldEmit = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (shouldEmit) {
|
|
122
|
+
emit();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* BYOK (Bring Your Own Key) API key storage.
|
|
7
|
+
*
|
|
8
|
+
* Stores user-provided API keys for Anthropic and OpenAI in localStorage.
|
|
9
|
+
* Keys are sent directly from the browser to the provider APIs — they never
|
|
10
|
+
* pass through our server proxy.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface ApiKeyConfig {
|
|
14
|
+
anthropicKey: string;
|
|
15
|
+
openaiKey: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const STORAGE_KEY = 'ifc-lite:api-keys:v1';
|
|
19
|
+
const CHANGED_EVENT = 'ifc-lite:api-keys-changed';
|
|
20
|
+
|
|
21
|
+
const EMPTY_CONFIG: ApiKeyConfig = {
|
|
22
|
+
anthropicKey: '',
|
|
23
|
+
openaiKey: '',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function sanitize(value: unknown): ApiKeyConfig {
|
|
27
|
+
const parsed = value && typeof value === 'object' ? (value as Partial<ApiKeyConfig>) : {};
|
|
28
|
+
return {
|
|
29
|
+
anthropicKey: typeof parsed.anthropicKey === 'string' ? parsed.anthropicKey.trim() : '',
|
|
30
|
+
openaiKey: typeof parsed.openaiKey === 'string' ? parsed.openaiKey.trim() : '',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getApiKeys(): ApiKeyConfig {
|
|
35
|
+
try {
|
|
36
|
+
return sanitize(JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '{}'));
|
|
37
|
+
} catch {
|
|
38
|
+
return { ...EMPTY_CONFIG };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function updateApiKeys(updates: Partial<ApiKeyConfig>): ApiKeyConfig {
|
|
43
|
+
const next = { ...getApiKeys(), ...updates };
|
|
44
|
+
// Trim keys before saving
|
|
45
|
+
next.anthropicKey = next.anthropicKey.trim();
|
|
46
|
+
next.openaiKey = next.openaiKey.trim();
|
|
47
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
|
48
|
+
window.dispatchEvent(new Event(CHANGED_EVENT));
|
|
49
|
+
return next;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function clearApiKeys(): ApiKeyConfig {
|
|
53
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
54
|
+
window.dispatchEvent(new Event(CHANGED_EVENT));
|
|
55
|
+
return { ...EMPTY_CONFIG };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function subscribeApiKeys(listener: () => void): () => void {
|
|
59
|
+
window.addEventListener(CHANGED_EVENT, listener);
|
|
60
|
+
return () => window.removeEventListener(CHANGED_EVENT, listener);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function hasAnthropicKey(): boolean {
|
|
64
|
+
return getApiKeys().anthropicKey.length > 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function hasOpenaiKey(): boolean {
|
|
68
|
+
return getApiKeys().openaiKey.length > 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function hasAnyApiKey(): boolean {
|
|
72
|
+
return hasAnthropicKey() || hasOpenaiKey();
|
|
73
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
|
|
5
|
+
export function navigateToPath(path: string, options: { replace?: boolean } = {}): void {
|
|
6
|
+
const { replace = false } = options;
|
|
7
|
+
if (replace) {
|
|
8
|
+
window.history.replaceState({}, '', path);
|
|
9
|
+
} else {
|
|
10
|
+
window.history.pushState({}, '', path);
|
|
11
|
+
}
|
|
12
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
13
|
+
}
|
package/src/services/bsdd.ts
CHANGED
|
@@ -99,6 +99,40 @@ async function fetchJson<T>(url: string): Promise<T> {
|
|
|
99
99
|
return res.json() as Promise<T>;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
interface BsddSearchApiResult {
|
|
103
|
+
uri?: string;
|
|
104
|
+
code?: string;
|
|
105
|
+
referenceCode?: string;
|
|
106
|
+
name?: string;
|
|
107
|
+
definition?: string | null;
|
|
108
|
+
dictionaryUri?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function matchesIfcType(candidate: BsddSearchApiResult, ifcType: string): boolean {
|
|
112
|
+
return candidate.code === ifcType
|
|
113
|
+
|| candidate.referenceCode === ifcType
|
|
114
|
+
|| candidate.name === ifcType;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function resolveFallbackClassUri(ifcType: string): Promise<string | null> {
|
|
118
|
+
try {
|
|
119
|
+
const raw = await fetchJson<{ classes?: BsddSearchApiResult[] }>(
|
|
120
|
+
`${BSDD_API}/api/Class/Search/v1?SearchText=${encodeURIComponent(ifcType)}&RelatedIfcEntities=${encodeURIComponent(ifcType)}`,
|
|
121
|
+
);
|
|
122
|
+
const classes = raw.classes ?? [];
|
|
123
|
+
if (classes.length === 0) return null;
|
|
124
|
+
|
|
125
|
+
const preferred = classes.find((entry) => entry.dictionaryUri === IFC_DICTIONARY_URI && matchesIfcType(entry, ifcType))
|
|
126
|
+
?? classes.find((entry) => matchesIfcType(entry, ifcType))
|
|
127
|
+
?? classes.find((entry) => entry.dictionaryUri === IFC_DICTIONARY_URI)
|
|
128
|
+
?? classes[0];
|
|
129
|
+
|
|
130
|
+
return preferred?.uri ?? null;
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
102
136
|
// ---------------------------------------------------------------------------
|
|
103
137
|
// Public API
|
|
104
138
|
// ---------------------------------------------------------------------------
|
|
@@ -128,15 +162,30 @@ export function ifcClassUri(ifcType: string): string {
|
|
|
128
162
|
export async function fetchClassInfo(
|
|
129
163
|
ifcType: string,
|
|
130
164
|
): Promise<BsddClassInfo | null> {
|
|
131
|
-
const
|
|
165
|
+
const defaultUri = ifcClassUri(ifcType);
|
|
166
|
+
let uri = defaultUri;
|
|
132
167
|
const cached = getCached(uri);
|
|
133
168
|
if (cached) return cached;
|
|
134
169
|
|
|
135
170
|
try {
|
|
136
171
|
// Parameter names must be PascalCase per the bSDD OpenAPI spec
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
172
|
+
let raw: Record<string, unknown>;
|
|
173
|
+
try {
|
|
174
|
+
raw = await fetchJson<Record<string, unknown>>(
|
|
175
|
+
`${BSDD_API}/api/Class/v1?Uri=${encodeURIComponent(uri)}&IncludeClassProperties=true&IncludeClassRelations=true`,
|
|
176
|
+
);
|
|
177
|
+
} catch {
|
|
178
|
+
const fallbackUri = await resolveFallbackClassUri(ifcType);
|
|
179
|
+
if (!fallbackUri || fallbackUri === uri) {
|
|
180
|
+
throw new Error('bSDD class lookup failed');
|
|
181
|
+
}
|
|
182
|
+
uri = fallbackUri;
|
|
183
|
+
const fallbackCached = getCached(uri);
|
|
184
|
+
if (fallbackCached) return fallbackCached;
|
|
185
|
+
raw = await fetchJson<Record<string, unknown>>(
|
|
186
|
+
`${BSDD_API}/api/Class/v1?Uri=${encodeURIComponent(uri)}&IncludeClassProperties=true&IncludeClassRelations=true`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
140
189
|
|
|
141
190
|
let info = mapClassResponse(raw, true);
|
|
142
191
|
|
|
@@ -71,7 +71,7 @@ let cacheService: ICacheService | null = null;
|
|
|
71
71
|
export async function getCacheService(): Promise<ICacheService> {
|
|
72
72
|
if (cacheService) return cacheService;
|
|
73
73
|
|
|
74
|
-
if (isTauri) {
|
|
74
|
+
if (isTauri()) {
|
|
75
75
|
// Desktop: Use Tauri native filesystem
|
|
76
76
|
const mod = await import('./desktop-cache.js');
|
|
77
77
|
cacheService = {
|