Clean up tauri
@ -26,14 +26,6 @@
|
|||||||
"@lezer/lr": "^1.4.1",
|
"@lezer/lr": "^1.4.1",
|
||||||
"@react-hook/resize-observer": "^2.0.1",
|
"@react-hook/resize-observer": "^2.0.1",
|
||||||
"@replit/codemirror-interact": "^6.3.1",
|
"@replit/codemirror-interact": "^6.3.1",
|
||||||
"@tauri-apps/api": "^2.0.0-beta.14",
|
|
||||||
"@tauri-apps/plugin-dialog": "^2.0.0-beta.6",
|
|
||||||
"@tauri-apps/plugin-fs": "^2.0.0-beta.6",
|
|
||||||
"@tauri-apps/plugin-http": "^2.0.0-beta.7",
|
|
||||||
"@tauri-apps/plugin-os": "^2.0.0-beta.6",
|
|
||||||
"@tauri-apps/plugin-process": "^2.0.0-beta.6",
|
|
||||||
"@tauri-apps/plugin-shell": "^2.0.0-beta.7",
|
|
||||||
"@tauri-apps/plugin-updater": "^2.0.0-beta.6",
|
|
||||||
"@ts-stack/markdown": "^1.5.0",
|
"@ts-stack/markdown": "^1.5.0",
|
||||||
"@tweenjs/tween.js": "^23.1.1",
|
"@tweenjs/tween.js": "^23.1.1",
|
||||||
"@xstate/inspect": "^0.8.0",
|
"@xstate/inspect": "^0.8.0",
|
||||||
|
3
src-tauri/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
# Generated by Cargo
|
|
||||||
# will have compiled files and executables
|
|
||||||
/target/
|
|
7295
src-tauri/Cargo.lock
generated
@ -1,47 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "app"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "The Zoo Modeling App"
|
|
||||||
authors = ["Zoo Engineers <eng@zoo.dev>"]
|
|
||||||
license = ""
|
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
|
||||||
default-run = "app"
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.70"
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
tauri-build = { version = "2.0.0-beta.18", features = [] }
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow = "1"
|
|
||||||
kcl-lib = { version = "0.2", path = "../src/wasm-lib/kcl" }
|
|
||||||
kittycad = "0.3.7"
|
|
||||||
log = "0.4.21"
|
|
||||||
mdns-sd = "0.11.1"
|
|
||||||
oauth2 = "4.4.2"
|
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
|
||||||
serde_json = "1.0"
|
|
||||||
tauri = { version = "2.0.0-beta.23", features = [ "devtools", "unstable"] }
|
|
||||||
tauri-plugin-cli = { version = "2.0.0-beta.7" }
|
|
||||||
tauri-plugin-deep-link = { version = "2.0.0-beta.8" }
|
|
||||||
tauri-plugin-dialog = { version = "2.0.0-beta.6" }
|
|
||||||
tauri-plugin-fs = { version = "2.0.0-beta.10" }
|
|
||||||
tauri-plugin-http = { version = "2.0.0-beta.11" }
|
|
||||||
tauri-plugin-log = { version = "2.0.0-beta.7" }
|
|
||||||
tauri-plugin-os = { version = "2.0.0-beta.7" }
|
|
||||||
tauri-plugin-persisted-scope = { version = "2.0.0-beta.10" }
|
|
||||||
tauri-plugin-process = { version = "2.0.0-beta.7" }
|
|
||||||
tauri-plugin-shell = { version = "2.0.0-beta.8" }
|
|
||||||
tauri-plugin-updater = { version = "2.0.0-beta.9" }
|
|
||||||
tokio = { version = "1.37.0", features = ["time", "fs", "process"] }
|
|
||||||
toml = "0.8.2"
|
|
||||||
url = "2.5.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["updater"]
|
|
||||||
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
|
|
||||||
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
|
|
||||||
# DO NOT REMOVE!!
|
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
|
||||||
updater = []
|
|
@ -1,376 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>NSDesktopFolderUsageDescription</key>
|
|
||||||
<string>Zoo Modeling App accesses the Desktop to load and save your project files and/or exported files here</string>
|
|
||||||
<key>NSDocumentsFolderUsageDescription</key>
|
|
||||||
<string>Zoo Modeling App accesses the Documents folder to load and save your project files and/or exported files here</string>
|
|
||||||
<key>NSDownloadsFolderUsageDescription</key>
|
|
||||||
<string>Zoo Modeling App accesses the Downloads folder to load and save your project files and/or exported files here</string>
|
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
|
||||||
<false/>
|
|
||||||
<key>DTXcode</key>
|
|
||||||
<string>1501</string>
|
|
||||||
<key>DTXcodeBuild</key>
|
|
||||||
<string>15A507</string>
|
|
||||||
<key>CFBundleURLTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>dev.zoo.modeling-app</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>zoo-modeling-app</string>
|
|
||||||
<string>zoo</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>LSFileQuarantineEnabled</key>
|
|
||||||
<false/>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.kcl</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>KCL</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Owner</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.toml</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>TOML</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.gltf</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>glTF</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.glb</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>glb</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.step</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>STEP</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.fbx</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>FBX</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>dev.zoo.sldprt</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Solidworks Part</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.geometry-definition-format</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>OBJ</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.polygon-file-format</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>PLY</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.standard-tesselated-geometry-format</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>STL</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<false/>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>public.folder</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Folders</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Alternate</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>UTExportedTypeDeclarations</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.kcl</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://zoo.dev/docs/kcl</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.source-code</string>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.text</string>
|
|
||||||
<string>public.plain-text</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
<string>public.script</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>KCL (KittyCAD Language) document</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>kcl</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>text/vnd.zoo.kcl</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.gltf</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.text</string>
|
|
||||||
<string>public.plain-text</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
<string>public.json</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Graphics Library Transmission Format (glTF)</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>gltf</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/gltf+json</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.glb</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Graphics Library Transmission Format (glTF) binary</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>glb</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/gltf-binary</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.step</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://www.loc.gov/preservation/digital/formats/fdd/fdd000448.shtml</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
<string>public.text</string>
|
|
||||||
<string>public.plain-text</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>STEP-file, ISO 10303-21</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>step</string>
|
|
||||||
<string>stp</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/step</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.sldprt</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://docs.fileformat.com/cad/sldprt/</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Solidworks Part</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>sldprt</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/vnd.solidworks.sldprt</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.fbx</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://en.wikipedia.org/wiki/FBX</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.3d-content</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Autodesk Filmbox (FBX) format</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>fbx</string>
|
|
||||||
<string>fbxb</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>model/vnd.autodesk.fbx</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>dev.zoo.toml</string>
|
|
||||||
<key>UTTypeReferenceURL</key>
|
|
||||||
<string>https://toml.io/en/</string>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.data</string>
|
|
||||||
<string>public.text</string>
|
|
||||||
<string>public.plain-text</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Tom's Obvious Minimal Language</string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>kcl</string>
|
|
||||||
</array>
|
|
||||||
<key>public.mime-type</key>
|
|
||||||
<array>
|
|
||||||
<string>text/toml</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
tauri_build::build()
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../gen/schemas/desktop-schema.json",
|
|
||||||
"identifier": "main-capability",
|
|
||||||
"description": "Capability for the main window",
|
|
||||||
"context": "local",
|
|
||||||
"windows": [
|
|
||||||
"main"
|
|
||||||
],
|
|
||||||
"permissions": [
|
|
||||||
"cli:default",
|
|
||||||
"deep-link:default",
|
|
||||||
"log:default",
|
|
||||||
"path:default",
|
|
||||||
"event:default",
|
|
||||||
"window:default",
|
|
||||||
"app:default",
|
|
||||||
"resources:default",
|
|
||||||
"menu:default",
|
|
||||||
"tray:default",
|
|
||||||
"fs:allow-create",
|
|
||||||
"fs:allow-read-file",
|
|
||||||
"fs:allow-read-text-file",
|
|
||||||
"fs:allow-write-file",
|
|
||||||
"fs:allow-write-text-file",
|
|
||||||
"fs:allow-read-dir",
|
|
||||||
"fs:allow-copy-file",
|
|
||||||
"fs:allow-mkdir",
|
|
||||||
"fs:allow-remove",
|
|
||||||
"fs:allow-rename",
|
|
||||||
"fs:allow-exists",
|
|
||||||
"fs:allow-stat",
|
|
||||||
{
|
|
||||||
"identifier": "fs:scope",
|
|
||||||
"allow": [
|
|
||||||
{
|
|
||||||
"path": "$TEMP"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "$TEMP/**/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "$HOME"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "$HOME/**/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "$HOME/.config"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "$HOME/.config/**/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "$APPCONFIG"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "$APPCONFIG/**/*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "$DOCUMENT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "$DOCUMENT/**/*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"shell:allow-open",
|
|
||||||
{
|
|
||||||
"identifier": "shell:allow-execute",
|
|
||||||
"allow": [
|
|
||||||
{
|
|
||||||
"name": "open",
|
|
||||||
"cmd": "open",
|
|
||||||
"args": [
|
|
||||||
"-R",
|
|
||||||
{
|
|
||||||
"validator": "\\S+"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"sidecar": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "explorer",
|
|
||||||
"cmd": "explorer",
|
|
||||||
"args": [
|
|
||||||
"/select",
|
|
||||||
{
|
|
||||||
"validator": "\\S+"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"sidecar": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"dialog:allow-open",
|
|
||||||
"dialog:allow-save",
|
|
||||||
"dialog:allow-message",
|
|
||||||
"dialog:allow-ask",
|
|
||||||
"dialog:allow-confirm",
|
|
||||||
{
|
|
||||||
"identifier": "http:default",
|
|
||||||
"allow": [
|
|
||||||
"https://dev.kittycad.io/*",
|
|
||||||
"https://dev.zoo.dev/*",
|
|
||||||
"https://kittycad.io/*",
|
|
||||||
"https://zoo.dev/*",
|
|
||||||
"https://api.dev.kittycad.io/*",
|
|
||||||
"https://api.dev.zoo.dev/*"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"os:allow-platform",
|
|
||||||
"os:allow-version",
|
|
||||||
"os:allow-os-type",
|
|
||||||
"os:allow-family",
|
|
||||||
"os:allow-arch",
|
|
||||||
"os:allow-exe-extension",
|
|
||||||
"os:allow-locale",
|
|
||||||
"os:allow-hostname",
|
|
||||||
"process:allow-restart",
|
|
||||||
"updater:default"
|
|
||||||
],
|
|
||||||
"platforms": [
|
|
||||||
"linux",
|
|
||||||
"macOS",
|
|
||||||
"windows"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>com.apple.security.app-sandbox</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.network.server</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.network.client</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.files.user-selected.read-write</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.security.files.downloads.read-write</key>
|
|
||||||
<true/>
|
|
||||||
<key>com.apple.application-identifier</key>
|
|
||||||
<string>92H8YB3B95.dev.zoo.modeling-app</string>
|
|
||||||
<key>com.apple.developer.team-identifier</key>
|
|
||||||
<string>92H8YB3B95</string>
|
|
||||||
<key>com.apple.developer.associated-domains</key>
|
|
||||||
<array>
|
|
||||||
<string>applinks:app.zoo.dev</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 793 B |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 8.6 KiB |
@ -1,6 +0,0 @@
|
|||||||
max_width = 120
|
|
||||||
edition = "2018"
|
|
||||||
format_code_in_doc_comments = true
|
|
||||||
format_strings = false
|
|
||||||
imports_granularity = "Crate"
|
|
||||||
group_imports = "StdExternalCrate"
|
|
@ -1,603 +0,0 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
||||||
|
|
||||||
pub(crate) mod state;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
env,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use kcl_lib::settings::types::{
|
|
||||||
file::{FileEntry, Project, ProjectRoute, ProjectState},
|
|
||||||
project::ProjectConfiguration,
|
|
||||||
Configuration,
|
|
||||||
};
|
|
||||||
use oauth2::TokenResponse;
|
|
||||||
use tauri::{ipc::InvokeError, Manager};
|
|
||||||
use tauri_plugin_cli::CliExt;
|
|
||||||
use tauri_plugin_shell::ShellExt;
|
|
||||||
|
|
||||||
const DEFAULT_HOST: &str = "https://api.zoo.dev";
|
|
||||||
const SETTINGS_FILE_NAME: &str = "settings.toml";
|
|
||||||
const PROJECT_SETTINGS_FILE_NAME: &str = "project.toml";
|
|
||||||
const PROJECT_FOLDER: &str = "zoo-modeling-app-projects";
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn rename_project_directory(project_path: &str, new_name: &str) -> Result<PathBuf, InvokeError> {
|
|
||||||
let project_dir = std::path::Path::new(project_path);
|
|
||||||
|
|
||||||
kcl_lib::settings::types::file::rename_project_directory(project_dir, new_name)
|
|
||||||
.await
|
|
||||||
.map_err(InvokeError::from_anyhow)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
fn get_initial_default_dir(app: tauri::AppHandle) -> Result<PathBuf, InvokeError> {
|
|
||||||
let dir = match app.path().document_dir() {
|
|
||||||
Ok(dir) => dir,
|
|
||||||
Err(_) => {
|
|
||||||
// for headless Linux (eg. Github Actions)
|
|
||||||
let home_dir = app.path().home_dir()?;
|
|
||||||
home_dir.join("Documents")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(dir.join(PROJECT_FOLDER))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn get_state(app: tauri::AppHandle) -> Result<Option<ProjectState>, InvokeError> {
|
|
||||||
let store = app.state::<state::Store>();
|
|
||||||
Ok(store.get().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn set_state(app: tauri::AppHandle, state: Option<ProjectState>) -> Result<(), InvokeError> {
|
|
||||||
let store = app.state::<state::Store>();
|
|
||||||
store.set(state).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_app_settings_file_path(app: &tauri::AppHandle) -> Result<PathBuf, InvokeError> {
|
|
||||||
let app_config_dir = app.path().app_config_dir()?;
|
|
||||||
|
|
||||||
// Ensure this directory exists.
|
|
||||||
if !app_config_dir.exists() {
|
|
||||||
tokio::fs::create_dir_all(&app_config_dir)
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(app_config_dir.join(SETTINGS_FILE_NAME))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn read_app_settings_file(app: tauri::AppHandle) -> Result<Configuration, InvokeError> {
|
|
||||||
let mut settings_path = get_app_settings_file_path(&app).await?;
|
|
||||||
let mut needs_migration = false;
|
|
||||||
|
|
||||||
// Check if this file exists.
|
|
||||||
if !settings_path.exists() {
|
|
||||||
// Try the backwards compatible path.
|
|
||||||
// TODO: Remove this after a few releases.
|
|
||||||
let app_config_dir = app.path().app_config_dir()?;
|
|
||||||
settings_path = format!(
|
|
||||||
"{}user.toml",
|
|
||||||
app_config_dir.display().to_string().trim_end_matches('/')
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
needs_migration = true;
|
|
||||||
// Check if this path exists.
|
|
||||||
if !settings_path.exists() {
|
|
||||||
let mut default = Configuration::default();
|
|
||||||
default.settings.project.directory = get_initial_default_dir(app.clone())?;
|
|
||||||
// Return the default configuration.
|
|
||||||
return Ok(default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let contents = tokio::fs::read_to_string(&settings_path)
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
let mut parsed = Configuration::backwards_compatible_toml_parse(&contents).map_err(InvokeError::from_anyhow)?;
|
|
||||||
if parsed.settings.project.directory == PathBuf::new() {
|
|
||||||
parsed.settings.project.directory = get_initial_default_dir(app.clone())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove this after a few releases.
|
|
||||||
if needs_migration {
|
|
||||||
write_app_settings_file(app, parsed.clone()).await?;
|
|
||||||
// Delete the old file.
|
|
||||||
tokio::fs::remove_file(settings_path)
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(parsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn write_app_settings_file(app: tauri::AppHandle, configuration: Configuration) -> Result<(), InvokeError> {
|
|
||||||
let settings_path = get_app_settings_file_path(&app).await?;
|
|
||||||
let contents = toml::to_string_pretty(&configuration).map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
tokio::fs::write(settings_path, contents.as_bytes())
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_project_settings_file_path(project_path: &str) -> Result<PathBuf, InvokeError> {
|
|
||||||
let project_dir = std::path::Path::new(project_path);
|
|
||||||
|
|
||||||
if !project_dir.exists() {
|
|
||||||
tokio::fs::create_dir_all(&project_dir)
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(project_dir.join(PROJECT_SETTINGS_FILE_NAME))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn read_project_settings_file(project_path: &str) -> Result<ProjectConfiguration, InvokeError> {
|
|
||||||
let settings_path = get_project_settings_file_path(project_path).await?;
|
|
||||||
|
|
||||||
// Check if this file exists.
|
|
||||||
if !settings_path.exists() {
|
|
||||||
// Return the default configuration.
|
|
||||||
return Ok(ProjectConfiguration::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
let contents = tokio::fs::read_to_string(&settings_path)
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
let parsed = ProjectConfiguration::backwards_compatible_toml_parse(&contents).map_err(InvokeError::from_anyhow)?;
|
|
||||||
|
|
||||||
Ok(parsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn write_project_settings_file(
|
|
||||||
project_path: &str,
|
|
||||||
configuration: ProjectConfiguration,
|
|
||||||
) -> Result<(), InvokeError> {
|
|
||||||
let settings_path = get_project_settings_file_path(project_path).await?;
|
|
||||||
let contents = toml::to_string_pretty(&configuration).map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
tokio::fs::write(settings_path, contents.as_bytes())
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize the directory that holds all the projects.
|
|
||||||
#[tauri::command]
|
|
||||||
async fn initialize_project_directory(configuration: Configuration) -> Result<PathBuf, InvokeError> {
|
|
||||||
configuration
|
|
||||||
.ensure_project_directory_exists()
|
|
||||||
.await
|
|
||||||
.map_err(InvokeError::from_anyhow)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new project directory.
|
|
||||||
#[tauri::command]
|
|
||||||
async fn create_new_project_directory(
|
|
||||||
configuration: Configuration,
|
|
||||||
project_name: &str,
|
|
||||||
initial_code: Option<&str>,
|
|
||||||
) -> Result<Project, InvokeError> {
|
|
||||||
configuration
|
|
||||||
.create_new_project_directory(project_name, initial_code)
|
|
||||||
.await
|
|
||||||
.map_err(InvokeError::from_anyhow)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List all the projects in the project directory.
|
|
||||||
#[tauri::command]
|
|
||||||
async fn list_projects(configuration: Configuration) -> Result<Vec<Project>, InvokeError> {
|
|
||||||
configuration.list_projects().await.map_err(InvokeError::from_anyhow)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get information about a project.
|
|
||||||
#[tauri::command]
|
|
||||||
async fn get_project_info(configuration: Configuration, project_path: &str) -> Result<Project, InvokeError> {
|
|
||||||
configuration
|
|
||||||
.get_project_info(project_path)
|
|
||||||
.await
|
|
||||||
.map_err(InvokeError::from_anyhow)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse the project route.
|
|
||||||
#[tauri::command]
|
|
||||||
async fn parse_project_route(configuration: Configuration, route: &str) -> Result<ProjectRoute, InvokeError> {
|
|
||||||
ProjectRoute::from_route(&configuration, route).map_err(InvokeError::from_anyhow)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn read_dir_recursive(path: &str) -> Result<FileEntry, InvokeError> {
|
|
||||||
kcl_lib::settings::utils::walk_dir(Path::new(path).to_path_buf())
|
|
||||||
.await
|
|
||||||
.map_err(InvokeError::from_anyhow)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This command instantiates a new window with auth.
|
|
||||||
/// The string returned from this method is the access token.
|
|
||||||
#[tauri::command]
|
|
||||||
async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError> {
|
|
||||||
log::debug!("Logging in...");
|
|
||||||
// Do an OAuth 2.0 Device Authorization Grant dance to get a token.
|
|
||||||
let device_auth_url = oauth2::DeviceAuthorizationUrl::new(format!("{host}/oauth2/device/auth"))
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
// We can hardcode the client ID.
|
|
||||||
// This value is safe to be embedded in version control.
|
|
||||||
// This is the client ID of the KittyCAD app.
|
|
||||||
let client_id = "2af127fb-e14e-400a-9c57-a9ed08d1a5b7".to_string();
|
|
||||||
let auth_client = oauth2::basic::BasicClient::new(
|
|
||||||
oauth2::ClientId::new(client_id),
|
|
||||||
None,
|
|
||||||
oauth2::AuthUrl::new(format!("{host}/authorize")).map_err(|e| InvokeError::from_anyhow(e.into()))?,
|
|
||||||
Some(
|
|
||||||
oauth2::TokenUrl::new(format!("{host}/oauth2/device/token"))
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.set_auth_type(oauth2::AuthType::RequestBody)
|
|
||||||
.set_device_authorization_url(device_auth_url);
|
|
||||||
|
|
||||||
let details: oauth2::devicecode::StandardDeviceAuthorizationResponse = auth_client
|
|
||||||
.exchange_device_code()
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?
|
|
||||||
.request_async(oauth2::reqwest::async_http_client)
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
|
|
||||||
let Some(auth_uri) = details.verification_uri_complete() else {
|
|
||||||
return Err(InvokeError::from("getting the verification uri failed"));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Open the system browser with the auth_uri.
|
|
||||||
// We do this in the browser and not a separate window because we want 1password and
|
|
||||||
// other crap to work well.
|
|
||||||
// TODO: find a better way to share this value with tauri e2e tests
|
|
||||||
// Here we're using an env var to enable the /tmp file (windows not supported for now)
|
|
||||||
// and bypass the shell::open call as it fails on GitHub Actions.
|
|
||||||
let e2e_tauri_enabled = env::var("E2E_TAURI_ENABLED").is_ok();
|
|
||||||
if e2e_tauri_enabled {
|
|
||||||
log::warn!("E2E_TAURI_ENABLED is set, won't open {} externally", auth_uri.secret());
|
|
||||||
let mut temp = String::from("/tmp");
|
|
||||||
// Overwrite with Windows variable
|
|
||||||
match env::var("TEMP") {
|
|
||||||
Ok(val) => temp = val,
|
|
||||||
Err(_e) => println!("Fallback to default /tmp"),
|
|
||||||
}
|
|
||||||
let path = Path::new(&temp).join("kittycad_user_code");
|
|
||||||
println!("Writing to {}", path.to_string_lossy());
|
|
||||||
tokio::fs::write(path, details.user_code().secret())
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
} else {
|
|
||||||
app.shell()
|
|
||||||
.open(auth_uri.secret(), None)
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the user to login.
|
|
||||||
let token = auth_client
|
|
||||||
.exchange_device_access_token(&details)
|
|
||||||
.request_async(oauth2::reqwest::async_http_client, tokio::time::sleep, None)
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?
|
|
||||||
.access_token()
|
|
||||||
.secret()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
Ok(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
///This command returns the KittyCAD user info given a token.
|
|
||||||
/// The string returned from this method is the user info as a json string.
|
|
||||||
#[tauri::command]
|
|
||||||
async fn get_user(token: &str, hostname: &str) -> Result<kittycad::types::User, InvokeError> {
|
|
||||||
// Use the host passed in if it's set.
|
|
||||||
// Otherwise, use the default host.
|
|
||||||
let host = if hostname.is_empty() {
|
|
||||||
DEFAULT_HOST.to_string()
|
|
||||||
} else {
|
|
||||||
hostname.to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Change the baseURL to the one we want.
|
|
||||||
let mut baseurl = host.to_string();
|
|
||||||
if !host.starts_with("http://") && !host.starts_with("https://") {
|
|
||||||
baseurl = format!("https://{host}");
|
|
||||||
if host.starts_with("localhost") {
|
|
||||||
baseurl = format!("http://{host}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log::debug!("Getting user info...");
|
|
||||||
|
|
||||||
// use kittycad library to fetch the user info from /user/me
|
|
||||||
let mut client = kittycad::Client::new(token);
|
|
||||||
|
|
||||||
if baseurl != DEFAULT_HOST {
|
|
||||||
client.set_base_url(&baseurl);
|
|
||||||
}
|
|
||||||
|
|
||||||
let user_info: kittycad::types::User = client
|
|
||||||
.users()
|
|
||||||
.get_self()
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
|
|
||||||
Ok(user_info)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open the selected path in the system file manager.
|
|
||||||
/// From this GitHub comment: https://github.com/tauri-apps/tauri/issues/4062#issuecomment-1338048169
|
|
||||||
/// But with the Linux support removed since we don't need it for now.
|
|
||||||
#[tauri::command]
|
|
||||||
fn show_in_folder(app: tauri::AppHandle, path: &str) -> Result<(), InvokeError> {
|
|
||||||
// Check if the file exists.
|
|
||||||
// If it doesn't, return an error.
|
|
||||||
if !Path::new(path).exists() {
|
|
||||||
return Err(InvokeError::from_anyhow(anyhow::anyhow!(
|
|
||||||
"The file `{}` does not exist",
|
|
||||||
path
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
|
||||||
{
|
|
||||||
app.shell()
|
|
||||||
.command("explorer")
|
|
||||||
.args(["/select,", path]) // The comma after select is not a typo
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
app.shell()
|
|
||||||
.command("open")
|
|
||||||
.args(["-R", path])
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const SERVICE_NAME: &str = "_machine-api._tcp.local.";
|
|
||||||
|
|
||||||
async fn find_machine_api() -> Result<Option<String>> {
|
|
||||||
println!("Looking for machine API...");
|
|
||||||
// Timeout if no response is received after 5 seconds.
|
|
||||||
let timeout_duration = std::time::Duration::from_secs(5);
|
|
||||||
|
|
||||||
let mdns = mdns_sd::ServiceDaemon::new()?;
|
|
||||||
|
|
||||||
// Browse for a service type.
|
|
||||||
let receiver = mdns.browse(SERVICE_NAME)?;
|
|
||||||
let resp = tokio::time::timeout(
|
|
||||||
timeout_duration,
|
|
||||||
tokio::spawn(async move {
|
|
||||||
while let Ok(event) = receiver.recv() {
|
|
||||||
if let mdns_sd::ServiceEvent::ServiceResolved(info) = event {
|
|
||||||
if let Some(addr) = info.get_addresses().iter().next() {
|
|
||||||
return Some(format!("{}:{}", addr, info.get_port()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Shut down.
|
|
||||||
mdns.shutdown()?;
|
|
||||||
|
|
||||||
let Ok(Ok(Some(addr))) = resp else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(addr))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn get_machine_api_ip() -> Result<Option<String>, InvokeError> {
|
|
||||||
let machine_api = find_machine_api().await.map_err(InvokeError::from_anyhow)?;
|
|
||||||
|
|
||||||
Ok(machine_api)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn list_machines() -> Result<String, InvokeError> {
|
|
||||||
let machine_api = find_machine_api().await.map_err(InvokeError::from_anyhow)?;
|
|
||||||
|
|
||||||
let Some(machine_api) = machine_api else {
|
|
||||||
// Empty array.
|
|
||||||
return Ok("[]".to_string());
|
|
||||||
};
|
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
let response = client
|
|
||||||
.get(format!("http://{}/machines", machine_api))
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
|
|
||||||
let text = response.text().await.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
|
||||||
Ok(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn open_url_sync(app: &tauri::AppHandle, url: &url::Url) {
|
|
||||||
log::debug!("Opening URL: {:?}", url);
|
|
||||||
let cloned_url = url.clone();
|
|
||||||
let runner: tauri::async_runtime::JoinHandle<Result<ProjectState>> = tauri::async_runtime::spawn(async move {
|
|
||||||
let url_str = cloned_url.path().to_string();
|
|
||||||
|
|
||||||
log::debug!("Opening URL path : {}", url_str);
|
|
||||||
let path = Path::new(url_str.as_str());
|
|
||||||
ProjectState::new_from_path(path.to_path_buf()).await
|
|
||||||
});
|
|
||||||
|
|
||||||
// Block on the handle.
|
|
||||||
match tauri::async_runtime::block_on(runner) {
|
|
||||||
Ok(Ok(store)) => {
|
|
||||||
// Create a state object to hold the project.
|
|
||||||
app.manage(state::Store::new(store));
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("Error opening URL:{} {:?}", url, e);
|
|
||||||
}
|
|
||||||
Ok(Err(e)) => {
|
|
||||||
log::warn!("Error opening URL:{} {:?}", url, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
tauri::Builder::default()
|
|
||||||
.invoke_handler(tauri::generate_handler![
|
|
||||||
get_state,
|
|
||||||
set_state,
|
|
||||||
get_initial_default_dir,
|
|
||||||
initialize_project_directory,
|
|
||||||
create_new_project_directory,
|
|
||||||
list_projects,
|
|
||||||
get_project_info,
|
|
||||||
parse_project_route,
|
|
||||||
get_user,
|
|
||||||
login,
|
|
||||||
read_dir_recursive,
|
|
||||||
show_in_folder,
|
|
||||||
read_app_settings_file,
|
|
||||||
write_app_settings_file,
|
|
||||||
read_project_settings_file,
|
|
||||||
write_project_settings_file,
|
|
||||||
rename_project_directory,
|
|
||||||
get_machine_api_ip,
|
|
||||||
list_machines
|
|
||||||
])
|
|
||||||
.plugin(tauri_plugin_cli::init())
|
|
||||||
.plugin(tauri_plugin_deep_link::init())
|
|
||||||
.plugin(tauri_plugin_dialog::init())
|
|
||||||
.plugin(tauri_plugin_fs::init())
|
|
||||||
.plugin(tauri_plugin_http::init())
|
|
||||||
.plugin(
|
|
||||||
tauri_plugin_log::Builder::new()
|
|
||||||
.targets([
|
|
||||||
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout),
|
|
||||||
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir { file_name: None }),
|
|
||||||
])
|
|
||||||
.level(log::LevelFilter::Debug)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.plugin(tauri_plugin_os::init())
|
|
||||||
.plugin(tauri_plugin_persisted_scope::init())
|
|
||||||
.plugin(tauri_plugin_process::init())
|
|
||||||
.plugin(tauri_plugin_shell::init())
|
|
||||||
.setup(|app| {
|
|
||||||
// Do update things.
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
{
|
|
||||||
app.get_webview("main").unwrap().open_devtools();
|
|
||||||
}
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
#[cfg(feature = "updater")]
|
|
||||||
{
|
|
||||||
app.handle().plugin(tauri_plugin_updater::Builder::new().build())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut verbose = false;
|
|
||||||
let mut source_path: Option<PathBuf> = None;
|
|
||||||
match app.cli().matches() {
|
|
||||||
// `matches` here is a Struct with { args, subcommand }.
|
|
||||||
// `args` is `HashMap<String, ArgData>` where `ArgData` is a struct with { value, occurrences }.
|
|
||||||
// `subcommand` is `Option<Box<SubcommandMatches>>` where `SubcommandMatches` is a struct with { name, matches }.
|
|
||||||
Ok(matches) => {
|
|
||||||
if let Some(verbose_flag) = matches.args.get("verbose") {
|
|
||||||
let Some(value) = verbose_flag.value.as_bool() else {
|
|
||||||
return Err(
|
|
||||||
anyhow::anyhow!("Error parsing CLI arguments: verbose flag is not a boolean").into(),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
verbose = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the path we are trying to open.
|
|
||||||
if let Some(source_arg) = matches.args.get("source") {
|
|
||||||
// We don't do an else here because this can be null.
|
|
||||||
if let Some(value) = source_arg.value.as_str() {
|
|
||||||
log::info!("Got path in cli argument: {}", value);
|
|
||||||
source_path = Some(Path::new(value).to_path_buf());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
return Err(anyhow::anyhow!("Error parsing CLI arguments: {:?}", err).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if verbose {
|
|
||||||
log::debug!("Verbose mode enabled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have a source path to open, make sure it exists.
|
|
||||||
let Some(source_path) = source_path else {
|
|
||||||
// The user didn't provide a source path to open.
|
|
||||||
// Run the app as normal.
|
|
||||||
app.manage(state::Store::default());
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
if !source_path.exists() {
|
|
||||||
return Err(anyhow::anyhow!(
|
|
||||||
"Error: the path `{}` you are trying to open does not exist",
|
|
||||||
source_path.display()
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let runner: tauri::async_runtime::JoinHandle<Result<ProjectState>> =
|
|
||||||
tauri::async_runtime::spawn(async move { ProjectState::new_from_path(source_path).await });
|
|
||||||
|
|
||||||
// Block on the handle.
|
|
||||||
let store = tauri::async_runtime::block_on(runner)??;
|
|
||||||
|
|
||||||
// Create a state object to hold the project.
|
|
||||||
app.manage(state::Store::new(store));
|
|
||||||
|
|
||||||
// Listen on the deep links.
|
|
||||||
app.listen("deep-link://new-url", |event| {
|
|
||||||
log::info!("got deep-link url: {:?}", event);
|
|
||||||
// TODO: open_url_sync(app.handle(), event.url);
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.build(tauri::generate_context!())?
|
|
||||||
.run(
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
|app, event| {
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
|
||||||
if let tauri::RunEvent::Opened { urls } = event {
|
|
||||||
log::info!("Opened URLs: {:?}", urls);
|
|
||||||
|
|
||||||
// Handle the first URL.
|
|
||||||
// TODO: do we want to handle more than one URL?
|
|
||||||
// Under what conditions would we even have more than one?
|
|
||||||
if let Some(url) = urls.first() {
|
|
||||||
open_url_sync(app, url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
//! State management for the application.
|
|
||||||
|
|
||||||
use kcl_lib::settings::types::file::ProjectState;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct Store(Mutex<Option<ProjectState>>);
|
|
||||||
|
|
||||||
impl Store {
|
|
||||||
pub fn new(p: ProjectState) -> Self {
|
|
||||||
Self(Mutex::new(Some(p)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get(&self) -> Option<ProjectState> {
|
|
||||||
self.0.lock().await.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn set(&self, p: Option<ProjectState>) {
|
|
||||||
*self.0.lock().await = p;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
|
||||||
"bundle": {
|
|
||||||
"macOS": {
|
|
||||||
"entitlements": "entitlements/app-store.entitlements"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
|
||||||
"app": {
|
|
||||||
"security": {
|
|
||||||
"csp": null
|
|
||||||
},
|
|
||||||
"windows": [
|
|
||||||
{
|
|
||||||
"fullscreen": false,
|
|
||||||
"height": 1200,
|
|
||||||
"resizable": true,
|
|
||||||
"title": "Zoo Modeling App",
|
|
||||||
"width": 1800
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"beforeDevCommand": "yarn start",
|
|
||||||
"devUrl": "http://localhost:3000",
|
|
||||||
"frontendDist": "../build"
|
|
||||||
},
|
|
||||||
"bundle": {
|
|
||||||
"active": true,
|
|
||||||
"category": "DeveloperTool",
|
|
||||||
"copyright": "",
|
|
||||||
"externalBin": [],
|
|
||||||
"icon": [
|
|
||||||
"icons/32x32.png",
|
|
||||||
"icons/128x128.png",
|
|
||||||
"icons/128x128@2x.png",
|
|
||||||
"icons/icon.icns",
|
|
||||||
"icons/icon.ico"
|
|
||||||
],
|
|
||||||
"linux": {
|
|
||||||
"deb": {
|
|
||||||
"depends": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"longDescription": "",
|
|
||||||
"macOS": {},
|
|
||||||
"resources": [],
|
|
||||||
"shortDescription": "",
|
|
||||||
"targets": "all"
|
|
||||||
},
|
|
||||||
"identifier": "dev.zoo.modeling-app",
|
|
||||||
"plugins": {
|
|
||||||
"cli": {
|
|
||||||
"description": "Zoo Modeling App CLI",
|
|
||||||
"args": [
|
|
||||||
{
|
|
||||||
"short": "v",
|
|
||||||
"name": "verbose",
|
|
||||||
"description": "Verbosity level"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "source",
|
|
||||||
"description": "The file or directory to open",
|
|
||||||
"required": false,
|
|
||||||
"index": 1,
|
|
||||||
"takesValue": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"subcommands": {}
|
|
||||||
},
|
|
||||||
"deep-link": {
|
|
||||||
"mobile": [
|
|
||||||
{
|
|
||||||
"host": "app.zoo.dev"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"desktop": {
|
|
||||||
"schemes": [
|
|
||||||
"zoo",
|
|
||||||
"zoo-modeling-app"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"shell": {
|
|
||||||
"open": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"productName": "Zoo Modeling App",
|
|
||||||
"version": "0.24.10"
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
|
||||||
"bundle": {
|
|
||||||
"windows": {
|
|
||||||
"certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D",
|
|
||||||
"digestAlgorithm": "sha256",
|
|
||||||
"timestampUrl": "http://timestamp.digicert.com"
|
|
||||||
},
|
|
||||||
"fileAssociations": [
|
|
||||||
{
|
|
||||||
"ext": ["kcl"],
|
|
||||||
"mimeType": "text/vnd.zoo.kcl"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ext": ["obj"],
|
|
||||||
"mimeType": "model/obj"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ext": ["gltf"],
|
|
||||||
"mimeType": "model/gltf+json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ext": ["glb"],
|
|
||||||
"mimeType": "model/gltf+binary"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ext": ["fbx", "fbxb"],
|
|
||||||
"mimeType": "model/fbx"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ext": ["stl"],
|
|
||||||
"mimeType": "model/stl"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ext": ["ply"],
|
|
||||||
"mimeType": "model/ply"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ext": ["step", "stp"],
|
|
||||||
"mimeType": "model/step"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ext": ["sldprt"],
|
|
||||||
"mimeType": "model/sldprt"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"plugins": {
|
|
||||||
"updater": {
|
|
||||||
"active": true,
|
|
||||||
"endpoints": [
|
|
||||||
"https://dl.zoo.dev/releases/modeling-app/last_update.json"
|
|
||||||
],
|
|
||||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUzNzA4MjBEQjFBRTY4NzYKUldSMmFLNnhEWUp3NCtsT21Jd05wQktOaGVkOVp6MUFma0hNTDRDSnI2RkJJTEZOWG1ncFhqcU8K"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,6 @@ import {
|
|||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { fileMachine } from 'machines/fileMachine'
|
import { fileMachine } from 'machines/fileMachine'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
import { join, sep } from '@tauri-apps/api/path'
|
|
||||||
import { DEFAULT_FILE_NAME, FILE_EXT } from 'lib/constants'
|
import { DEFAULT_FILE_NAME, FILE_EXT } from 'lib/constants'
|
||||||
import { getProjectInfo } from 'lib/desktop'
|
import { getProjectInfo } from 'lib/desktop'
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ import { APP_VERSION } from 'routes/Settings'
|
|||||||
import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/desktopFS'
|
import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/desktopFS'
|
||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
||||||
import { sep } from '@tauri-apps/api/path'
|
|
||||||
import { ForwardedRef, forwardRef, useEffect } from 'react'
|
import { ForwardedRef, forwardRef, useEffect } from 'react'
|
||||||
import { useLspContext } from 'components/LspProvider'
|
import { useLspContext } from 'components/LspProvider'
|
||||||
|
import { ForwardedRef, forwardRef, useEffect } from 'react'
|
||||||
|
|
||||||
interface AllSettingsFieldsProps {
|
interface AllSettingsFieldsProps {
|
||||||
searchParamTab: SettingsLevel
|
searchParamTab: SettingsLevel
|
||||||
|
@ -27,7 +27,7 @@ export default class CodeManager {
|
|||||||
|
|
||||||
const storedCode = safeLSGetItem(PERSIST_CODE_KEY)
|
const storedCode = safeLSGetItem(PERSIST_CODE_KEY)
|
||||||
// TODO #819 remove zustand persistence logic in a few months
|
// TODO #819 remove zustand persistence logic in a few months
|
||||||
// short term migration, shouldn't make a difference for tauri app users
|
// short term migration, shouldn't make a difference for desktop app users
|
||||||
// anyway since that's filesystem based.
|
// anyway since that's filesystem based.
|
||||||
const zustandStore = JSON.parse(safeLSGetItem('store') || '{}')
|
const zustandStore = JSON.parse(safeLSGetItem('store') || '{}')
|
||||||
if (storedCode === null && zustandStore?.state?.code) {
|
if (storedCode === null && zustandStore?.state?.code) {
|
||||||
|
@ -932,10 +932,142 @@ class EngineConnection extends EventTarget {
|
|||||||
event.data
|
event.data
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!message.success) {
|
const createWebSocketConnection = () => {
|
||||||
const errorsString = message?.errors
|
this.state = {
|
||||||
?.map((error) => {
|
type: EngineConnectionStateType.Connecting,
|
||||||
return ` - ${error.error_code}: ${error.message}`
|
value: {
|
||||||
|
type: ConnectingType.WebSocketConnecting,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
this.websocket = new WebSocket(this.url, [])
|
||||||
|
this.websocket.binaryType = 'arraybuffer'
|
||||||
|
|
||||||
|
this.onWebSocketOpen = (event) => {
|
||||||
|
this.state = {
|
||||||
|
type: EngineConnectionStateType.Connecting,
|
||||||
|
value: {
|
||||||
|
type: ConnectingType.WebSocketOpen,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is required for when KCMA is running stand-alone / within desktop.
|
||||||
|
// Otherwise when run in a browser, the token is sent implicitly via
|
||||||
|
// the Cookie header.
|
||||||
|
if (this.token) {
|
||||||
|
this.send({
|
||||||
|
type: 'headers',
|
||||||
|
headers: { Authorization: `Bearer ${this.token}` },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send an initial ping
|
||||||
|
this.send({ type: 'ping' })
|
||||||
|
this.pingPongSpan.ping = new Date()
|
||||||
|
}
|
||||||
|
this.websocket.addEventListener('open', this.onWebSocketOpen)
|
||||||
|
|
||||||
|
this.onWebSocketClose = (event) => {
|
||||||
|
this.disconnectAll()
|
||||||
|
this.finalizeIfAllConnectionsClosed()
|
||||||
|
}
|
||||||
|
this.websocket.addEventListener('close', this.onWebSocketClose)
|
||||||
|
|
||||||
|
this.onWebSocketError = (event) => {
|
||||||
|
this.disconnectAll()
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
type: EngineConnectionStateType.Disconnecting,
|
||||||
|
value: {
|
||||||
|
type: DisconnectingType.Error,
|
||||||
|
value: {
|
||||||
|
error: ConnectionError.WebSocketError,
|
||||||
|
context: event,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.websocket.addEventListener('error', this.onWebSocketError)
|
||||||
|
|
||||||
|
this.onWebSocketMessage = (event) => {
|
||||||
|
// In the EngineConnection, we're looking for messages to/from
|
||||||
|
// the server that relate to the ICE handshake, or WebRTC
|
||||||
|
// negotiation. There may be other messages (including ArrayBuffer
|
||||||
|
// messages) that are intended for the GUI itself, so be careful
|
||||||
|
// when assuming we're the only consumer or that all messages will
|
||||||
|
// be carefully formatted here.
|
||||||
|
|
||||||
|
if (typeof event.data !== 'string') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const message: Models['WebSocketResponse_type'] = JSON.parse(event.data)
|
||||||
|
|
||||||
|
if (!message.success) {
|
||||||
|
const errorsString = message?.errors
|
||||||
|
?.map((error) => {
|
||||||
|
return ` - ${error.error_code}: ${error.message}`
|
||||||
|
})
|
||||||
|
.join('\n')
|
||||||
|
if (message.request_id) {
|
||||||
|
const artifactThatFailed =
|
||||||
|
this.engineCommandManager.artifactMap[message.request_id]
|
||||||
|
console.error(
|
||||||
|
`Error in response to request ${message.request_id}:\n${errorsString}
|
||||||
|
failed cmd type was ${artifactThatFailed?.type}`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
console.error(`Error from server:\n${errorsString}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstError = message?.errors[0]
|
||||||
|
if (firstError.error_code === 'auth_token_invalid') {
|
||||||
|
this.state = {
|
||||||
|
type: EngineConnectionStateType.Disconnecting,
|
||||||
|
value: {
|
||||||
|
type: DisconnectingType.Error,
|
||||||
|
value: {
|
||||||
|
error: ConnectionError.BadAuthToken,
|
||||||
|
context: firstError.message,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
this.disconnectAll()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let resp = message.resp
|
||||||
|
|
||||||
|
// If there's no body to the response, we can bail here.
|
||||||
|
if (!resp || !resp.type) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (resp.type) {
|
||||||
|
case 'pong':
|
||||||
|
this.pingPongSpan.pong = new Date()
|
||||||
|
break
|
||||||
|
case 'ice_server_info':
|
||||||
|
let ice_servers = resp.data?.ice_servers
|
||||||
|
|
||||||
|
// Now that we have some ICE servers it makes sense
|
||||||
|
// to start initializing the RTCPeerConnection. RTCPeerConnection
|
||||||
|
// will begin the ICE process.
|
||||||
|
createPeerConnection()
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
type: EngineConnectionStateType.Connecting,
|
||||||
|
value: {
|
||||||
|
type: ConnectingType.PeerConnectionCreated,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// No ICE servers can be valid in a local dev. env.
|
||||||
|
if (ice_servers?.length === 0) {
|
||||||
|
console.warn('No ICE servers')
|
||||||
|
this.pc?.setConfiguration({
|
||||||
|
bundlePolicy: 'max-bundle',
|
||||||
})
|
})
|
||||||
.join('\n')
|
.join('\n')
|
||||||
if (message.request_id) {
|
if (message.request_id) {
|
||||||
|
@ -9,7 +9,7 @@ export const openExternalBrowserIfDesktop = (to) => function(e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open a new browser window tauri style or browser style.
|
// Open a new browser window desktop style or browser style.
|
||||||
export default async function openWindow(url: string) {
|
export default async function openWindow(url: string) {
|
||||||
if (isDesktop()) {
|
if (isDesktop()) {
|
||||||
await window.electron.openExternal(url)
|
await window.electron.openExternal(url)
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
} from 'lib/constants'
|
} from 'lib/constants'
|
||||||
import { loadAndValidateSettings } from './settings/settingsUtils'
|
import { loadAndValidateSettings } from './settings/settingsUtils'
|
||||||
import makeUrlPathRelative from './makeUrlPathRelative'
|
import makeUrlPathRelative from './makeUrlPathRelative'
|
||||||
import { sep } from '@tauri-apps/api/path'
|
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
||||||
import {
|
import {
|
||||||
|
@ -206,7 +206,7 @@ export function createSettings() {
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
// In Tauri end-to-end tests we can't control the file picker,
|
// In desktop end-to-end tests we can't control the file picker,
|
||||||
// so we seed the new directory value in the element's dataset
|
// so we seed the new directory value in the element's dataset
|
||||||
const inputRefVal = inputRef.current?.dataset.testValue
|
const inputRefVal = inputRef.current?.dataset.testValue
|
||||||
if (inputRef.current && inputRefVal && !Array.isArray(inputRefVal)) {
|
if (inputRef.current && inputRefVal && !Array.isArray(inputRefVal)) {
|
||||||
|
@ -8,7 +8,6 @@ import { isDesktop } from 'lib/isDesktop'
|
|||||||
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
import { join } from '@tauri-apps/api/path'
|
|
||||||
import {
|
import {
|
||||||
APP_NAME,
|
APP_NAME,
|
||||||
ONBOARDING_PROJECT_NAME,
|
ONBOARDING_PROJECT_NAME,
|
||||||
|
@ -7,7 +7,7 @@ export default function ProjectMenu() {
|
|||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
const dismiss = useDismiss()
|
const dismiss = useDismiss()
|
||||||
const next = useNextClick(onboardingPaths.EXPORT)
|
const next = useNextClick(onboardingPaths.EXPORT)
|
||||||
const tauri = isDesktop()
|
const onDesktop = isDesktop()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed grid justify-center items-start inset-0 z-50 pointer-events-none">
|
<div className="fixed grid justify-center items-start inset-0 z-50 pointer-events-none">
|
||||||
@ -20,14 +20,14 @@ export default function ProjectMenu() {
|
|||||||
<section className="flex-1">
|
<section className="flex-1">
|
||||||
<h2 className="text-2xl font-bold">Project Menu</h2>
|
<h2 className="text-2xl font-bold">Project Menu</h2>
|
||||||
<p className="my-4">
|
<p className="my-4">
|
||||||
Click on {tauri ? `your part's name` : `the app name`} in the upper
|
Click on {onDesktop ? `your part's name` : `the app name`} in the upper
|
||||||
left to open the project menu, where you can open the project
|
left to open the project menu, where you can open the project
|
||||||
settings and export your current part.
|
settings and export your current part.
|
||||||
{tauri && (
|
{onDesktop && (
|
||||||
<> You can click the Zoo logo to quickly navigate home.</>
|
<> You can click the Zoo logo to quickly navigate home.</>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
{tauri ? (
|
{onDesktop ? (
|
||||||
<>
|
<>
|
||||||
<p className="my-4">
|
<p className="my-4">
|
||||||
From here you can manage files in your project and export your
|
From here you can manage files in your project and export your
|
||||||
|