@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
|
@@ -48,10 +48,13 @@ import {
|
|
|
48
48
|
DialogDescription,
|
|
49
49
|
} from '@/components/ui/dialog';
|
|
50
50
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
51
|
+
import { toast } from '@/components/ui/toast';
|
|
52
|
+
import { buildDesktopUpgradeUrl, hasDesktopFeatureAccess } from '@/lib/desktop-product';
|
|
51
53
|
import { cn, formatDuration } from '@/lib/utils';
|
|
52
54
|
import { useViewerStore } from '@/store';
|
|
53
55
|
import { useSandbox } from '@/hooks/useSandbox';
|
|
54
56
|
import { SCRIPT_TEMPLATES } from '@/lib/scripts/templates';
|
|
57
|
+
import { navigateToPath } from '@/services/app-navigation';
|
|
55
58
|
import { CodeEditor } from './CodeEditor';
|
|
56
59
|
import { ChatPanel } from './ChatPanel';
|
|
57
60
|
import type { LogEntry } from '@/store/slices/scriptSlice';
|
|
@@ -141,6 +144,8 @@ export function ScriptPanel({ onClose }: ScriptPanelProps) {
|
|
|
141
144
|
const [outputCollapsed, setOutputCollapsed] = useState(false);
|
|
142
145
|
const chatPanelVisible = useViewerStore((s) => s.chatPanelVisible);
|
|
143
146
|
const setChatPanelVisible = useViewerStore((s) => s.setChatPanelVisible);
|
|
147
|
+
const desktopEntitlement = useViewerStore((s) => s.desktopEntitlement);
|
|
148
|
+
const canUseAiAssistant = hasDesktopFeatureAccess(desktopEntitlement, 'ai_assistant');
|
|
144
149
|
|
|
145
150
|
// Chat panel width (px) — resizable via drag handle
|
|
146
151
|
const [chatWidth, setChatWidth] = useState(380);
|
|
@@ -150,14 +155,27 @@ export function ScriptPanel({ onClose }: ScriptPanelProps) {
|
|
|
150
155
|
// Open chat by default when script panel mounts
|
|
151
156
|
useEffect(() => {
|
|
152
157
|
try {
|
|
153
|
-
if (localStorage.getItem('ifc-lite-chat-panel-visible') === null) {
|
|
158
|
+
if (canUseAiAssistant && localStorage.getItem('ifc-lite-chat-panel-visible') === null) {
|
|
154
159
|
setChatPanelVisible(true);
|
|
155
160
|
}
|
|
156
161
|
} catch {
|
|
157
|
-
|
|
162
|
+
if (canUseAiAssistant) {
|
|
163
|
+
setChatPanelVisible(true);
|
|
164
|
+
}
|
|
158
165
|
}
|
|
159
166
|
return () => { cleanupChatDragRef.current?.(); };
|
|
160
|
-
}, [setChatPanelVisible]);
|
|
167
|
+
}, [canUseAiAssistant, setChatPanelVisible]);
|
|
168
|
+
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
if (!canUseAiAssistant && chatPanelVisible) {
|
|
171
|
+
setChatPanelVisible(false);
|
|
172
|
+
}
|
|
173
|
+
}, [canUseAiAssistant, chatPanelVisible, setChatPanelVisible]);
|
|
174
|
+
|
|
175
|
+
const promptAiUpgrade = useCallback(() => {
|
|
176
|
+
toast.info('AI assistant is available with Desktop Pro');
|
|
177
|
+
navigateToPath(buildDesktopUpgradeUrl());
|
|
178
|
+
}, []);
|
|
161
179
|
|
|
162
180
|
const handleChatResizeStart = useCallback((e: React.MouseEvent) => {
|
|
163
181
|
e.preventDefault();
|
|
@@ -223,6 +241,10 @@ export function ScriptPanel({ onClose }: ScriptPanelProps) {
|
|
|
223
241
|
|
|
224
242
|
const handleFixWithLlm = useCallback(() => {
|
|
225
243
|
if (!lastError) return;
|
|
244
|
+
if (!canUseAiAssistant) {
|
|
245
|
+
promptAiUpgrade();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
226
248
|
setChatPanelVisible(true);
|
|
227
249
|
const state = useViewerStore.getState();
|
|
228
250
|
queueChatRepairRequest({
|
|
@@ -230,11 +252,15 @@ export function ScriptPanel({ onClose }: ScriptPanelProps) {
|
|
|
230
252
|
diagnostics: state.scriptLastDiagnostics,
|
|
231
253
|
reason: lastError.startsWith('Preflight validation failed:') ? 'preflight' : 'runtime',
|
|
232
254
|
});
|
|
233
|
-
}, [lastError, queueChatRepairRequest, setChatPanelVisible]);
|
|
255
|
+
}, [canUseAiAssistant, lastError, promptAiUpgrade, queueChatRepairRequest, setChatPanelVisible]);
|
|
234
256
|
|
|
235
257
|
const toggleChat = useCallback(() => {
|
|
258
|
+
if (!canUseAiAssistant) {
|
|
259
|
+
promptAiUpgrade();
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
236
262
|
setChatPanelVisible(!chatPanelVisible);
|
|
237
|
-
}, [chatPanelVisible, setChatPanelVisible]);
|
|
263
|
+
}, [canUseAiAssistant, chatPanelVisible, promptAiUpgrade, setChatPanelVisible]);
|
|
238
264
|
|
|
239
265
|
return (
|
|
240
266
|
<div className="h-full flex bg-background">
|
|
@@ -289,12 +315,12 @@ export function ScriptPanel({ onClose }: ScriptPanelProps) {
|
|
|
289
315
|
variant={chatPanelVisible ? 'default' : 'ghost'}
|
|
290
316
|
size="icon-xs"
|
|
291
317
|
onClick={toggleChat}
|
|
292
|
-
className={cn(chatPanelVisible && 'bg-blue-500 hover:bg-blue-600 text-white')}
|
|
318
|
+
className={cn(canUseAiAssistant && chatPanelVisible && 'bg-blue-500 hover:bg-blue-600 text-white')}
|
|
293
319
|
>
|
|
294
320
|
<Bot className="h-3.5 w-3.5" />
|
|
295
321
|
</Button>
|
|
296
322
|
</TooltipTrigger>
|
|
297
|
-
<TooltipContent>{chatPanelVisible ? 'Hide AI Chat' : 'Show AI Chat'}</TooltipContent>
|
|
323
|
+
<TooltipContent>{canUseAiAssistant ? (chatPanelVisible ? 'Hide AI Chat' : 'Show AI Chat') : 'Desktop Pro required for AI Chat'}</TooltipContent>
|
|
298
324
|
</Tooltip>
|
|
299
325
|
|
|
300
326
|
{onClose && (
|
|
@@ -460,7 +486,7 @@ export function ScriptPanel({ onClose }: ScriptPanelProps) {
|
|
|
460
486
|
className="h-6 px-2 text-xs border-destructive/40 text-destructive bg-transparent hover:bg-destructive/10"
|
|
461
487
|
onClick={handleFixWithLlm}
|
|
462
488
|
>
|
|
463
|
-
Fix with LLM
|
|
489
|
+
{canUseAiAssistant ? 'Fix with LLM' : 'Upgrade for AI Fix'}
|
|
464
490
|
</Button>
|
|
465
491
|
</div>
|
|
466
492
|
</div>
|
|
@@ -0,0 +1,581 @@
|
|
|
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 {
|
|
6
|
+
ArrowLeft,
|
|
7
|
+
Bot,
|
|
8
|
+
Check,
|
|
9
|
+
ChevronDown,
|
|
10
|
+
ChevronUp,
|
|
11
|
+
Clock3,
|
|
12
|
+
Cloud,
|
|
13
|
+
CreditCard,
|
|
14
|
+
ExternalLink,
|
|
15
|
+
Eye,
|
|
16
|
+
EyeOff,
|
|
17
|
+
FolderOpen,
|
|
18
|
+
Key,
|
|
19
|
+
LayoutPanelTop,
|
|
20
|
+
Lock,
|
|
21
|
+
Settings2,
|
|
22
|
+
ShieldCheck,
|
|
23
|
+
Trash2,
|
|
24
|
+
WifiOff,
|
|
25
|
+
} from 'lucide-react';
|
|
26
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
27
|
+
import { Badge } from '@/components/ui/badge';
|
|
28
|
+
import { Button } from '@/components/ui/button';
|
|
29
|
+
import { Switch } from '@/components/ui/switch';
|
|
30
|
+
import { toast } from '@/components/ui/toast';
|
|
31
|
+
import { useViewerStore } from '@/store';
|
|
32
|
+
import {
|
|
33
|
+
buildDesktopUpgradeUrl,
|
|
34
|
+
getDesktopFeatureCatalog,
|
|
35
|
+
getDesktopPlanSummary,
|
|
36
|
+
getDesktopPlanTier,
|
|
37
|
+
hasDesktopPro,
|
|
38
|
+
isDesktopBillingEnforced,
|
|
39
|
+
type DesktopEntitlement,
|
|
40
|
+
} from '@/lib/desktop-product';
|
|
41
|
+
import { navigateToPath } from '@/services/app-navigation';
|
|
42
|
+
import {
|
|
43
|
+
getDesktopPreferences,
|
|
44
|
+
subscribeDesktopPreferences,
|
|
45
|
+
updateDesktopPreferences,
|
|
46
|
+
} from '@/services/desktop-preferences';
|
|
47
|
+
import {
|
|
48
|
+
getApiKeys,
|
|
49
|
+
updateApiKeys,
|
|
50
|
+
clearApiKeys,
|
|
51
|
+
subscribeApiKeys,
|
|
52
|
+
type ApiKeyConfig,
|
|
53
|
+
} from '@/services/api-keys';
|
|
54
|
+
|
|
55
|
+
export function SettingsPage() {
|
|
56
|
+
const desktopEntitlement = useViewerStore((s) => s.desktopEntitlement);
|
|
57
|
+
const chatUsage = useViewerStore((s) => s.chatUsage);
|
|
58
|
+
const [preferences, setPreferences] = useState(() => getDesktopPreferences());
|
|
59
|
+
const [apiKeys, setApiKeys] = useState(() => getApiKeys());
|
|
60
|
+
const returnTo = (() => {
|
|
61
|
+
const params = new URLSearchParams(window.location.search);
|
|
62
|
+
const candidate = params.get('returnTo');
|
|
63
|
+
return candidate && candidate.startsWith('/') ? candidate : '/';
|
|
64
|
+
})();
|
|
65
|
+
useEffect(() => subscribeDesktopPreferences(() => {
|
|
66
|
+
setPreferences(getDesktopPreferences());
|
|
67
|
+
}), []);
|
|
68
|
+
useEffect(() => subscribeApiKeys(() => {
|
|
69
|
+
setApiKeys(getApiKeys());
|
|
70
|
+
}), []);
|
|
71
|
+
|
|
72
|
+
const updatePreference = (updates: Partial<typeof preferences>) => {
|
|
73
|
+
setPreferences(updateDesktopPreferences(updates));
|
|
74
|
+
};
|
|
75
|
+
const planTier = getDesktopPlanTier(desktopEntitlement);
|
|
76
|
+
const planSummary = getDesktopPlanSummary(desktopEntitlement, chatUsage);
|
|
77
|
+
const featureCatalog = getDesktopFeatureCatalog(desktopEntitlement);
|
|
78
|
+
const canRestoreWorkspace = hasDesktopPro(desktopEntitlement);
|
|
79
|
+
const usageSummary = useMemo(() => {
|
|
80
|
+
if (!chatUsage) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const resetLabel = chatUsage.resetAt
|
|
84
|
+
? new Date(chatUsage.resetAt * 1000).toLocaleDateString()
|
|
85
|
+
: 'Unknown';
|
|
86
|
+
const unit = chatUsage.type === 'credits' ? 'credits' : 'requests';
|
|
87
|
+
return `${chatUsage.used}/${chatUsage.limit} ${unit} used. Resets ${resetLabel}.`;
|
|
88
|
+
}, [chatUsage]);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
92
|
+
<div className="mx-auto w-full max-w-4xl px-6 py-8">
|
|
93
|
+
<div className="mb-6 flex items-center justify-between">
|
|
94
|
+
<Button variant="ghost" size="sm" onClick={() => navigateToPath(returnTo, { replace: true })}>
|
|
95
|
+
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
96
|
+
Back to Viewer
|
|
97
|
+
</Button>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div className="space-y-6">
|
|
101
|
+
{/* API Keys Section */}
|
|
102
|
+
<ApiKeysSection apiKeys={apiKeys} />
|
|
103
|
+
|
|
104
|
+
<section className="rounded-lg border bg-card p-6 shadow-sm">
|
|
105
|
+
<div className="mb-5 flex items-center gap-3">
|
|
106
|
+
<ShieldCheck className="h-5 w-5" />
|
|
107
|
+
<div>
|
|
108
|
+
<h1 className="text-2xl font-semibold">Desktop Account</h1>
|
|
109
|
+
<p className="text-sm text-muted-foreground">
|
|
110
|
+
App-wide entitlement, trial state, offline grace, and AI usage limits.
|
|
111
|
+
</p>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div className="mb-5 flex flex-wrap items-center gap-2">
|
|
116
|
+
<StatusBadge entitlement={desktopEntitlement} />
|
|
117
|
+
<Badge variant={hasDesktopPro(desktopEntitlement) ? 'default' : 'secondary'}>
|
|
118
|
+
{planTier === 'pro' ? 'Desktop Pro' : 'Desktop Free'}
|
|
119
|
+
</Badge>
|
|
120
|
+
<Badge variant="outline">
|
|
121
|
+
Source: {desktopEntitlement.source.replace('_', ' ')}
|
|
122
|
+
</Badge>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div className="mb-5 grid gap-3 md:grid-cols-2">
|
|
126
|
+
<InfoCard
|
|
127
|
+
title="Plan Summary"
|
|
128
|
+
body={planSummary}
|
|
129
|
+
icon={<CreditCard className="h-4 w-4" />}
|
|
130
|
+
/>
|
|
131
|
+
<InfoCard
|
|
132
|
+
title="AI Usage (Free Tier)"
|
|
133
|
+
body={usageSummary ?? 'No AI usage data yet. Usage appears after the first chat message through the proxy.'}
|
|
134
|
+
icon={<Bot className="h-4 w-4" />}
|
|
135
|
+
/>
|
|
136
|
+
<InfoCard
|
|
137
|
+
title="Last Validated"
|
|
138
|
+
body={formatTimestamp(desktopEntitlement.validatedAt)}
|
|
139
|
+
icon={<Clock3 className="h-4 w-4" />}
|
|
140
|
+
/>
|
|
141
|
+
<InfoCard
|
|
142
|
+
title="Offline Grace"
|
|
143
|
+
body={formatOfflineGrace(desktopEntitlement)}
|
|
144
|
+
icon={<WifiOff className="h-4 w-4" />}
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
</section>
|
|
148
|
+
|
|
149
|
+
<section className="rounded-lg border bg-card p-6 shadow-sm">
|
|
150
|
+
<div className="mb-5 flex items-center gap-3">
|
|
151
|
+
<Settings2 className="h-5 w-5" />
|
|
152
|
+
<div>
|
|
153
|
+
<h1 className="text-2xl font-semibold">Desktop Settings</h1>
|
|
154
|
+
<p className="text-sm text-muted-foreground">
|
|
155
|
+
Local preferences for startup behavior and account access.
|
|
156
|
+
</p>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div className="space-y-5">
|
|
161
|
+
<div className="flex items-start justify-between gap-4 rounded-md border p-4">
|
|
162
|
+
<div className="space-y-1">
|
|
163
|
+
<div className="flex items-center gap-2 font-medium">
|
|
164
|
+
<FolderOpen className="h-4 w-4" />
|
|
165
|
+
Reopen last model on launch
|
|
166
|
+
</div>
|
|
167
|
+
<p className="text-sm text-muted-foreground">
|
|
168
|
+
Automatically load the most recently used IFC file when the desktop app starts.
|
|
169
|
+
</p>
|
|
170
|
+
</div>
|
|
171
|
+
<Switch
|
|
172
|
+
checked={preferences.reopenLastModelOnLaunch}
|
|
173
|
+
onCheckedChange={(checked) => updatePreference({ reopenLastModelOnLaunch: checked })}
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<div className="flex items-start justify-between gap-4 rounded-md border p-4">
|
|
178
|
+
<div className="space-y-1">
|
|
179
|
+
<div className="flex items-center gap-2 font-medium">
|
|
180
|
+
<LayoutPanelTop className="h-4 w-4" />
|
|
181
|
+
Restore workspace layout
|
|
182
|
+
</div>
|
|
183
|
+
<p className="text-sm text-muted-foreground">
|
|
184
|
+
Restore panel visibility, camera view, sectioning, and other saved workspace state on launch. Desktop Pro feature.
|
|
185
|
+
</p>
|
|
186
|
+
</div>
|
|
187
|
+
<Switch
|
|
188
|
+
checked={preferences.restoreWorkspaceLayoutOnLaunch}
|
|
189
|
+
disabled={!canRestoreWorkspace}
|
|
190
|
+
onCheckedChange={(checked) => updatePreference({ restoreWorkspaceLayoutOnLaunch: checked })}
|
|
191
|
+
/>
|
|
192
|
+
</div>
|
|
193
|
+
{!canRestoreWorkspace && (
|
|
194
|
+
<div className="rounded-md border border-dashed p-4 text-sm text-muted-foreground">
|
|
195
|
+
Workspace restore is included with Desktop Pro. Reopening the last model remains available on Free.
|
|
196
|
+
<div className="mt-3">
|
|
197
|
+
<Button size="sm" onClick={() => navigateToPath(buildDesktopUpgradeUrl('/settings'))}>
|
|
198
|
+
Upgrade to Desktop Pro
|
|
199
|
+
</Button>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
</div>
|
|
204
|
+
</section>
|
|
205
|
+
|
|
206
|
+
<section className="rounded-lg border bg-card p-6 shadow-sm">
|
|
207
|
+
<div className="mb-5 flex items-center gap-3">
|
|
208
|
+
<CreditCard className="h-5 w-5" />
|
|
209
|
+
<div>
|
|
210
|
+
<h2 className="text-xl font-semibold">Billing & Features</h2>
|
|
211
|
+
<p className="text-sm text-muted-foreground">
|
|
212
|
+
Desktop billing is app-wide. The viewer stays available on Free, while Pro unlocks advanced desktop features.
|
|
213
|
+
</p>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<div className="mb-5 rounded-md border p-4">
|
|
218
|
+
<div className="flex items-center justify-between gap-4">
|
|
219
|
+
<div>
|
|
220
|
+
<div className="font-medium capitalize">{planTier} plan</div>
|
|
221
|
+
<p className="text-sm text-muted-foreground">{planSummary}</p>
|
|
222
|
+
</div>
|
|
223
|
+
{isDesktopBillingEnforced() && !hasDesktopPro(desktopEntitlement) && (
|
|
224
|
+
<Button onClick={() => navigateToPath(buildDesktopUpgradeUrl('/settings'))}>
|
|
225
|
+
Upgrade to Pro
|
|
226
|
+
</Button>
|
|
227
|
+
)}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div className="mb-5 grid gap-3">
|
|
232
|
+
{featureCatalog.map((feature) => (
|
|
233
|
+
<div key={feature.key} className="flex items-start justify-between gap-4 rounded-md border p-4">
|
|
234
|
+
<div>
|
|
235
|
+
<div className="font-medium">{feature.label}</div>
|
|
236
|
+
<p className="text-sm text-muted-foreground">{feature.description}</p>
|
|
237
|
+
</div>
|
|
238
|
+
<div className="flex items-center gap-2 text-sm">
|
|
239
|
+
{feature.enabled ? (
|
|
240
|
+
<>
|
|
241
|
+
<Check className="h-4 w-4 text-emerald-500" />
|
|
242
|
+
Included
|
|
243
|
+
</>
|
|
244
|
+
) : (
|
|
245
|
+
<>
|
|
246
|
+
<Lock className="h-4 w-4 text-amber-500" />
|
|
247
|
+
Pro
|
|
248
|
+
</>
|
|
249
|
+
)}
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
))}
|
|
253
|
+
</div>
|
|
254
|
+
</section>
|
|
255
|
+
|
|
256
|
+
<section className="rounded-lg border bg-card p-6 shadow-sm">
|
|
257
|
+
<div className="mb-5 flex items-center gap-3">
|
|
258
|
+
<Cloud className="h-5 w-5" />
|
|
259
|
+
<div>
|
|
260
|
+
<h2 className="text-xl font-semibold">Privacy & Network</h2>
|
|
261
|
+
<p className="text-sm text-muted-foreground">
|
|
262
|
+
Local IFC viewing remains available offline. Connected services degrade individually instead of blocking the desktop viewer.
|
|
263
|
+
</p>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
268
|
+
<InfoCard
|
|
269
|
+
title="Always Local"
|
|
270
|
+
body="Model loading, hierarchy, properties, navigation, measurement, and core viewing stay on your machine."
|
|
271
|
+
icon={<ShieldCheck className="h-4 w-4" />}
|
|
272
|
+
/>
|
|
273
|
+
<InfoCard
|
|
274
|
+
title="Needs Network"
|
|
275
|
+
body="AI assistant (free via proxy, or direct with your API key), live bSDD lookups, and billing sync require network access."
|
|
276
|
+
icon={<Cloud className="h-4 w-4" />}
|
|
277
|
+
/>
|
|
278
|
+
</div>
|
|
279
|
+
</section>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ── API Keys Section ──────────────────────────────────────────────────────
|
|
287
|
+
|
|
288
|
+
function ApiKeyInput({
|
|
289
|
+
id,
|
|
290
|
+
value,
|
|
291
|
+
onChange,
|
|
292
|
+
onSave,
|
|
293
|
+
placeholder,
|
|
294
|
+
isSaved,
|
|
295
|
+
}: {
|
|
296
|
+
id: string;
|
|
297
|
+
value: string;
|
|
298
|
+
onChange: (v: string) => void;
|
|
299
|
+
onSave: () => void;
|
|
300
|
+
placeholder: string;
|
|
301
|
+
isSaved: boolean;
|
|
302
|
+
}) {
|
|
303
|
+
const [show, setShow] = useState(false);
|
|
304
|
+
return (
|
|
305
|
+
<div className="flex gap-2">
|
|
306
|
+
<div className="relative flex-1">
|
|
307
|
+
<input
|
|
308
|
+
id={id}
|
|
309
|
+
type={show ? 'text' : 'password'}
|
|
310
|
+
value={value}
|
|
311
|
+
onChange={(e) => onChange(e.target.value)}
|
|
312
|
+
onKeyDown={(e) => { if (e.key === 'Enter' && !isSaved) onSave(); }}
|
|
313
|
+
placeholder={placeholder}
|
|
314
|
+
autoComplete="off"
|
|
315
|
+
spellCheck={false}
|
|
316
|
+
className="w-full rounded-md border border-input bg-background px-3 py-1.5 text-sm font-mono focus:outline-none focus:ring-1 focus:ring-ring pr-8"
|
|
317
|
+
/>
|
|
318
|
+
<button
|
|
319
|
+
type="button"
|
|
320
|
+
onClick={() => setShow(!show)}
|
|
321
|
+
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
|
322
|
+
aria-label={show ? 'Hide key' : 'Show key'}
|
|
323
|
+
>
|
|
324
|
+
{show ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
|
|
325
|
+
</button>
|
|
326
|
+
</div>
|
|
327
|
+
<Button size="sm" onClick={onSave} disabled={isSaved}>
|
|
328
|
+
Save
|
|
329
|
+
</Button>
|
|
330
|
+
</div>
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function ApiKeysSection({ apiKeys }: { apiKeys: ApiKeyConfig }) {
|
|
335
|
+
const [anthropicKey, setAnthropicKey] = useState(apiKeys.anthropicKey);
|
|
336
|
+
const [openaiKey, setOpenaiKey] = useState(apiKeys.openaiKey);
|
|
337
|
+
const [showHowTo, setShowHowTo] = useState<'anthropic' | 'openai' | null>(null);
|
|
338
|
+
|
|
339
|
+
useEffect(() => {
|
|
340
|
+
setAnthropicKey(apiKeys.anthropicKey);
|
|
341
|
+
setOpenaiKey(apiKeys.openaiKey);
|
|
342
|
+
}, [apiKeys.anthropicKey, apiKeys.openaiKey]);
|
|
343
|
+
|
|
344
|
+
const saveAnthropicKey = useCallback(() => {
|
|
345
|
+
updateApiKeys({ anthropicKey: anthropicKey.trim() });
|
|
346
|
+
toast.success(anthropicKey.trim() ? 'Anthropic API key saved' : 'Anthropic API key removed');
|
|
347
|
+
}, [anthropicKey]);
|
|
348
|
+
|
|
349
|
+
const saveOpenaiKey = useCallback(() => {
|
|
350
|
+
updateApiKeys({ openaiKey: openaiKey.trim() });
|
|
351
|
+
toast.success(openaiKey.trim() ? 'OpenAI API key saved' : 'OpenAI API key removed');
|
|
352
|
+
}, [openaiKey]);
|
|
353
|
+
|
|
354
|
+
const handleClearAll = useCallback(() => {
|
|
355
|
+
clearApiKeys();
|
|
356
|
+
setAnthropicKey('');
|
|
357
|
+
setOpenaiKey('');
|
|
358
|
+
toast.success('All API keys removed');
|
|
359
|
+
}, []);
|
|
360
|
+
|
|
361
|
+
const hasAnyKey = apiKeys.anthropicKey.length > 0 || apiKeys.openaiKey.length > 0;
|
|
362
|
+
|
|
363
|
+
return (
|
|
364
|
+
<section className="rounded-lg border bg-card p-6 shadow-sm">
|
|
365
|
+
<div className="mb-5 flex items-center gap-3">
|
|
366
|
+
<Key className="h-5 w-5" />
|
|
367
|
+
<div>
|
|
368
|
+
<h1 className="text-2xl font-semibold">API Keys</h1>
|
|
369
|
+
<p className="text-sm text-muted-foreground">
|
|
370
|
+
Bring your own Anthropic or OpenAI API key to unlock additional models.
|
|
371
|
+
You can configure one or both providers independently.
|
|
372
|
+
</p>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<div className="space-y-4">
|
|
377
|
+
{/* Anthropic */}
|
|
378
|
+
<div className="rounded-md border p-4">
|
|
379
|
+
<div className="mb-2 flex items-center justify-between">
|
|
380
|
+
<label className="text-sm font-medium" htmlFor="anthropic-key">Anthropic API Key</label>
|
|
381
|
+
{apiKeys.anthropicKey ? (
|
|
382
|
+
<Badge variant="default" className="text-[10px]">
|
|
383
|
+
<Check className="mr-1 h-3 w-3" />
|
|
384
|
+
Configured
|
|
385
|
+
</Badge>
|
|
386
|
+
) : (
|
|
387
|
+
<Badge variant="outline" className="text-[10px]">Not set</Badge>
|
|
388
|
+
)}
|
|
389
|
+
</div>
|
|
390
|
+
<p className="mb-2 text-xs text-muted-foreground">
|
|
391
|
+
Unlocks <strong>Claude Opus 4.6</strong>, <strong>Claude Sonnet 4.6</strong>, and <strong>Claude Haiku 4.5</strong>.
|
|
392
|
+
</p>
|
|
393
|
+
<button
|
|
394
|
+
type="button"
|
|
395
|
+
onClick={() => setShowHowTo(showHowTo === 'anthropic' ? null : 'anthropic')}
|
|
396
|
+
className="mb-3 flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
397
|
+
>
|
|
398
|
+
{showHowTo === 'anthropic' ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
|
|
399
|
+
How to get an Anthropic API key
|
|
400
|
+
</button>
|
|
401
|
+
{showHowTo === 'anthropic' && (
|
|
402
|
+
<div className="mb-3 rounded-md bg-muted/50 p-3 text-xs text-muted-foreground space-y-1.5">
|
|
403
|
+
<p>1. Go to{' '}
|
|
404
|
+
<a href="https://console.anthropic.com/settings/keys" target="_blank" rel="noopener noreferrer" className="underline inline-flex items-center gap-0.5">
|
|
405
|
+
console.anthropic.com/settings/keys <ExternalLink className="h-2.5 w-2.5" />
|
|
406
|
+
</a>
|
|
407
|
+
</p>
|
|
408
|
+
<p>2. Sign in or create an Anthropic account</p>
|
|
409
|
+
<p>3. Click <strong>Create Key</strong>, name it (e.g. "ifc-lite")</p>
|
|
410
|
+
<p>4. Copy the key (starts with <code className="bg-muted px-1 rounded">sk-ant-api03-...</code>)</p>
|
|
411
|
+
<p>5. Paste it below and click Save</p>
|
|
412
|
+
<p className="pt-1 text-muted-foreground/70">Anthropic offers $5 free credit on new accounts. After that, usage is pay-as-you-go on your Anthropic billing.</p>
|
|
413
|
+
</div>
|
|
414
|
+
)}
|
|
415
|
+
<ApiKeyInput
|
|
416
|
+
id="anthropic-key"
|
|
417
|
+
value={anthropicKey}
|
|
418
|
+
onChange={setAnthropicKey}
|
|
419
|
+
onSave={saveAnthropicKey}
|
|
420
|
+
placeholder="sk-ant-api03-..."
|
|
421
|
+
isSaved={anthropicKey.trim() === apiKeys.anthropicKey}
|
|
422
|
+
/>
|
|
423
|
+
</div>
|
|
424
|
+
|
|
425
|
+
{/* OpenAI */}
|
|
426
|
+
<div className="rounded-md border p-4">
|
|
427
|
+
<div className="mb-2 flex items-center justify-between">
|
|
428
|
+
<label className="text-sm font-medium" htmlFor="openai-key">OpenAI API Key</label>
|
|
429
|
+
{apiKeys.openaiKey ? (
|
|
430
|
+
<Badge variant="default" className="text-[10px]">
|
|
431
|
+
<Check className="mr-1 h-3 w-3" />
|
|
432
|
+
Configured
|
|
433
|
+
</Badge>
|
|
434
|
+
) : (
|
|
435
|
+
<Badge variant="outline" className="text-[10px]">Not set</Badge>
|
|
436
|
+
)}
|
|
437
|
+
</div>
|
|
438
|
+
<p className="mb-2 text-xs text-muted-foreground">
|
|
439
|
+
Unlocks <strong>GPT-5.4</strong>, <strong>GPT-5.3 Codex</strong>, and <strong>GPT-5.4 Mini</strong>.
|
|
440
|
+
</p>
|
|
441
|
+
<button
|
|
442
|
+
type="button"
|
|
443
|
+
onClick={() => setShowHowTo(showHowTo === 'openai' ? null : 'openai')}
|
|
444
|
+
className="mb-3 flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
445
|
+
>
|
|
446
|
+
{showHowTo === 'openai' ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
|
|
447
|
+
How to get an OpenAI API key
|
|
448
|
+
</button>
|
|
449
|
+
{showHowTo === 'openai' && (
|
|
450
|
+
<div className="mb-3 rounded-md bg-muted/50 p-3 text-xs text-muted-foreground space-y-1.5">
|
|
451
|
+
<p>1. Go to{' '}
|
|
452
|
+
<a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer" className="underline inline-flex items-center gap-0.5">
|
|
453
|
+
platform.openai.com/api-keys <ExternalLink className="h-2.5 w-2.5" />
|
|
454
|
+
</a>
|
|
455
|
+
</p>
|
|
456
|
+
<p>2. Sign in or create an OpenAI account</p>
|
|
457
|
+
<p>3. Click <strong>Create new secret key</strong>, name it (e.g. "ifc-lite")</p>
|
|
458
|
+
<p>4. Copy the key (starts with <code className="bg-muted px-1 rounded">sk-...</code>)</p>
|
|
459
|
+
<p>5. Paste it below and click Save</p>
|
|
460
|
+
<p className="pt-1 text-muted-foreground/70">OpenAI requires prepaid credits or a payment method. Usage is billed to your OpenAI account.</p>
|
|
461
|
+
</div>
|
|
462
|
+
)}
|
|
463
|
+
<ApiKeyInput
|
|
464
|
+
id="openai-key"
|
|
465
|
+
value={openaiKey}
|
|
466
|
+
onChange={setOpenaiKey}
|
|
467
|
+
onSave={saveOpenaiKey}
|
|
468
|
+
placeholder="sk-..."
|
|
469
|
+
isSaved={openaiKey.trim() === apiKeys.openaiKey}
|
|
470
|
+
/>
|
|
471
|
+
</div>
|
|
472
|
+
|
|
473
|
+
{hasAnyKey && (
|
|
474
|
+
<div className="flex justify-end">
|
|
475
|
+
<Button variant="outline" size="sm" onClick={handleClearAll}>
|
|
476
|
+
<Trash2 className="mr-2 h-3.5 w-3.5" />
|
|
477
|
+
Remove all keys
|
|
478
|
+
</Button>
|
|
479
|
+
</div>
|
|
480
|
+
)}
|
|
481
|
+
|
|
482
|
+
{/* Security & Privacy notice */}
|
|
483
|
+
<div className="rounded-md border border-dashed p-4 text-xs space-y-2">
|
|
484
|
+
<p className="font-medium text-foreground">Your API keys never leave your machine.</p>
|
|
485
|
+
<ul className="list-disc pl-4 space-y-1 text-muted-foreground">
|
|
486
|
+
<li>Keys are stored in your browser's <code className="bg-muted px-1 rounded">localStorage</code> and persist across page reloads.</li>
|
|
487
|
+
<li>When you use a BYOK model, requests go <strong>directly from your browser to the provider</strong> (Anthropic or OpenAI). They never pass through our servers.</li>
|
|
488
|
+
<li>Free models use our server proxy and do not require any API key.</li>
|
|
489
|
+
<li>Clearing your browser data or clicking "Remove all keys" above permanently deletes them.</li>
|
|
490
|
+
</ul>
|
|
491
|
+
<p className="text-muted-foreground pt-1">
|
|
492
|
+
<strong>Verify in your browser console:</strong> open DevTools (F12), go to the Console tab, and run:
|
|
493
|
+
</p>
|
|
494
|
+
<pre className="bg-muted rounded px-2 py-1.5 font-mono text-[11px] overflow-x-auto text-foreground">
|
|
495
|
+
{`JSON.parse(localStorage.getItem('ifc-lite:api-keys:v1') ?? '{}')`}
|
|
496
|
+
</pre>
|
|
497
|
+
<p className="text-muted-foreground">
|
|
498
|
+
This shows exactly what is stored. Only you can see it. To delete everything:
|
|
499
|
+
</p>
|
|
500
|
+
<pre className="bg-muted rounded px-2 py-1.5 font-mono text-[11px] overflow-x-auto text-foreground">
|
|
501
|
+
{`localStorage.removeItem('ifc-lite:api-keys:v1')`}
|
|
502
|
+
</pre>
|
|
503
|
+
</div>
|
|
504
|
+
</div>
|
|
505
|
+
</section>
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
510
|
+
|
|
511
|
+
function formatTimestamp(value: number | null): string {
|
|
512
|
+
if (!value) {
|
|
513
|
+
return 'Not validated yet';
|
|
514
|
+
}
|
|
515
|
+
return new Date(value).toLocaleString();
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function formatOfflineGrace(entitlement: DesktopEntitlement): string {
|
|
519
|
+
if (!entitlement.graceUntil) {
|
|
520
|
+
return 'No offline grace cached yet';
|
|
521
|
+
}
|
|
522
|
+
const remainingDays = Math.max(0, Math.ceil((entitlement.graceUntil - Date.now()) / (24 * 60 * 60 * 1000)));
|
|
523
|
+
return `${new Date(entitlement.graceUntil).toLocaleString()}${remainingDays > 0 ? ` (${remainingDays} day${remainingDays === 1 ? '' : 's'} left)` : ''}`;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function describeDesktopStatus(entitlement: DesktopEntitlement): string {
|
|
527
|
+
switch (entitlement.status) {
|
|
528
|
+
case 'trial':
|
|
529
|
+
return entitlement.trialEndsAt
|
|
530
|
+
? `Trial active until ${new Date(entitlement.trialEndsAt).toLocaleDateString()}`
|
|
531
|
+
: 'Trial active';
|
|
532
|
+
case 'grace_offline':
|
|
533
|
+
return entitlement.graceUntil
|
|
534
|
+
? `Offline grace until ${new Date(entitlement.graceUntil).toLocaleDateString()}`
|
|
535
|
+
: 'Offline grace active';
|
|
536
|
+
case 'expired':
|
|
537
|
+
return 'Subscription expired';
|
|
538
|
+
case 'active':
|
|
539
|
+
return 'Subscription active';
|
|
540
|
+
case 'signed_out':
|
|
541
|
+
return 'Signed out';
|
|
542
|
+
case 'anonymous':
|
|
543
|
+
return 'No account configured';
|
|
544
|
+
default:
|
|
545
|
+
return entitlement.status;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function getStatusBadgeVariant(entitlement: DesktopEntitlement): 'default' | 'secondary' | 'destructive' | 'outline' {
|
|
550
|
+
switch (entitlement.status) {
|
|
551
|
+
case 'active':
|
|
552
|
+
case 'trial':
|
|
553
|
+
return 'default';
|
|
554
|
+
case 'grace_offline':
|
|
555
|
+
return 'secondary';
|
|
556
|
+
case 'expired':
|
|
557
|
+
return 'destructive';
|
|
558
|
+
default:
|
|
559
|
+
return 'outline';
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function StatusBadge({ entitlement }: { entitlement: DesktopEntitlement }) {
|
|
564
|
+
return (
|
|
565
|
+
<Badge variant={getStatusBadgeVariant(entitlement)}>
|
|
566
|
+
{describeDesktopStatus(entitlement)}
|
|
567
|
+
</Badge>
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function InfoCard({ title, body, icon }: { title: string; body: string; icon: React.ReactNode }) {
|
|
572
|
+
return (
|
|
573
|
+
<div className="rounded-md border p-4">
|
|
574
|
+
<div className="mb-2 flex items-center gap-2 text-sm font-medium">
|
|
575
|
+
{icon}
|
|
576
|
+
{title}
|
|
577
|
+
</div>
|
|
578
|
+
<p className="text-sm text-muted-foreground">{body}</p>
|
|
579
|
+
</div>
|
|
580
|
+
);
|
|
581
|
+
}
|