From 20a8f2aa6a75fa640ba01a994486ceb4ba4f482d Mon Sep 17 00:00:00 2001 From: Jess Frazelle Date: Tue, 30 Apr 2024 15:50:02 -0700 Subject: [PATCH] Cut release vwhatever (not really cutting release, mucking with info.plist) (#2272) * muck with info.plist Signed-off-by: Jess Frazelle * updates Signed-off-by: Jess Frazelle * handle urls Signed-off-by: Jess Frazelle * fixups Signed-off-by: Jess Frazelle * config args Signed-off-by: Jess Frazelle * updates Signed-off-by: Jess Frazelle * macos Signed-off-by: Jess Frazelle * updates Signed-off-by: Jess Frazelle * error on non relavent file Signed-off-by: Jess Frazelle * updates Signed-off-by: Jess Frazelle * updates Signed-off-by: Jess Frazelle --------- Signed-off-by: Jess Frazelle --- .github/workflows/ci.yml | 12 +- src-tauri/Cargo.lock | 25 +-- src-tauri/Cargo.toml | 1 + src-tauri/Info.plist | 155 ++++++++++++++++++ src-tauri/src/main.rs | 124 ++++++-------- ...re.conf.json => tauri.app-store.conf.json} | 0 src-tauri/tauri.release-macos.conf.json | 19 +++ src-tauri/tauri.release.conf.json | 40 ++++- src/wasm-lib/kcl/src/settings/types/file.rs | 86 +++++++++- src/wasm-lib/kcl/src/settings/utils.rs | 4 +- 10 files changed, 372 insertions(+), 94 deletions(-) rename src-tauri/{tauri.appstore.conf.json => tauri.app-store.conf.json} (100%) create mode 100644 src-tauri/tauri.release-macos.conf.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f477b8de..ff9bf59a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,8 +130,9 @@ jobs: matrix: os: [macos-14, ubuntu-latest, windows-latest] env: - TAURI_ARGS_MACOS: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }} - TAURI_ARGS_UBUNTU: ${{ matrix.os == 'ubuntu-latest' && '--bundles' || '' }} + TAURI_ARGS_MACOS: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin --config src-tauri/tauri.release-macos.conf.json' || '' }} + TAURI_ARGS_UBUNTU: ${{ matrix.os == 'ubuntu-latest' && '--bundles --config src-tauri/tauri.release.conf.json' || '' }} + TAURI_ARGS_WINDOWS: ${{ matrix.os == 'windows-latest' && '--bundles --config src-tauri\\tauri.release.conf.json' || '' }} steps: - uses: actions/checkout@v4 @@ -235,7 +236,7 @@ jobs: with: includeRelease: false includeDebug: true - args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}" + args: "${{ env.TAURI_ARGS_WINDOWS }} ${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}" - name: Mac App Store if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'macos-14' }} @@ -276,7 +277,7 @@ jobs: rm src-tauri/Cargo.toml.bu git diff src-tauri/Cargo.toml - yarn tauri build --target "${target}" --verbose --config src-tauri/tauri.appstore.conf.json + yarn tauri build --target "${target}" --verbose --config src-tauri/tauri.app-store.conf.json app_path="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.app" build_name="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.pkg" @@ -336,9 +337,8 @@ jobs: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}" with: - args: "${{ env.TAURI_CONF_ARGS }} ${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}" + args: "${{ env.TAURI_ARGS_WINDOWS }} ${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}" - uses: actions/upload-artifact@v3 if: matrix.os != 'ubuntu-latest' diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index b4a426303..c105d3f95 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -169,6 +169,7 @@ dependencies = [ "tauri-plugin-updater", "tokio", "toml 0.8.12", + "url", ] [[package]] @@ -4198,9 +4199,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309" dependencies = [ "bigdecimal", "bytes", @@ -4216,14 +4217,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -4302,9 +4303,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] @@ -4320,9 +4321,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", @@ -4331,13 +4332,13 @@ dependencies = [ [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2958e13a8..ca7dfcebf 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -31,6 +31,7 @@ tauri-plugin-shell = { version = "2.0.0-beta.2" } tauri-plugin-updater = { version = "2.0.0-beta.4" } tokio = { version = "1.37.0", features = ["time", "fs", "process"] } toml = "0.8.2" +url = "2.5.0" [features] default = ["updater"] diff --git a/src-tauri/Info.plist b/src-tauri/Info.plist index af12f55cf..8a6b8806e 100644 --- a/src-tauri/Info.plist +++ b/src-tauri/Info.plist @@ -34,6 +34,161 @@ LSFileQuarantineEnabled + CFBundleDocumentTypes + + + LSItemContentTypes + + dev.zoo.kcl + + CFBundleTypeName + KCL + CFBundleTypeRole + Editor + LSTypeIsPackage + + LSHandlerRank + Owner + + + LSItemContentTypes + + dev.zoo.toml + + CFBundleTypeName + TOML + CFBundleTypeRole + Editor + LSTypeIsPackage + + LSHandlerRank + Default + + + LSItemContentTypes + + dev.zoo.gltf + + CFBundleTypeName + glTF + CFBundleTypeRole + Editor + LSTypeIsPackage + + LSHandlerRank + Default + + + LSItemContentTypes + + dev.zoo.glb + + CFBundleTypeName + glb + CFBundleTypeRole + Editor + LSTypeIsPackage + + LSHandlerRank + Default + + + LSItemContentTypes + + dev.zoo.step + + CFBundleTypeName + STEP + CFBundleTypeRole + Editor + LSTypeIsPackage + + LSHandlerRank + Default + + + LSItemContentTypes + + dev.zoo.fbx + + CFBundleTypeName + FBX + CFBundleTypeRole + Editor + LSTypeIsPackage + + LSHandlerRank + Default + + + LSItemContentTypes + + dev.zoo.sldprt + + CFBundleTypeName + Solidworks Part + CFBundleTypeRole + Viewer + LSTypeIsPackage + + LSHandlerRank + Default + + + LSItemContentTypes + + public.geometry-definition-format + + CFBundleTypeName + OBJ + CFBundleTypeRole + Editor + LSTypeIsPackage + + LSHandlerRank + Default + + + LSItemContentTypes + + public.polygon-file-format + + CFBundleTypeName + PLY + CFBundleTypeRole + Editor + LSTypeIsPackage + + LSHandlerRank + Default + + + LSItemContentTypes + + public.standard-tesselated-geometry-format + + CFBundleTypeName + STL + CFBundleTypeRole + Editor + LSTypeIsPackage + + LSHandlerRank + Default + + + LSItemContentTypes + + public.folder + + CFBundleTypeName + Folders + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + + UTExportedTypeDeclarations diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a50e6c51e..d7882dab1 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -12,7 +12,7 @@ use anyhow::Result; use kcl_lib::settings::types::{ file::{FileEntry, Project, ProjectRoute, ProjectState}, project::ProjectConfiguration, - Configuration, DEFAULT_PROJECT_KCL_FILE, + Configuration, }; use oauth2::TokenResponse; use tauri::{ipc::InvokeError, Manager}; @@ -350,6 +350,31 @@ fn show_in_folder(path: &str) -> Result<(), InvokeError> { Ok(()) } +#[allow(dead_code)] +fn open_url_sync(app: &tauri::AppHandle, url: &url::Url) { + println!("Opening URL: {:?}", url); + let cloned_url = url.clone(); + let runner: tauri::async_runtime::JoinHandle> = tauri::async_runtime::spawn(async move { + let url_str = cloned_url.to_string(); + 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) => { + println!("Error opening URL:{} {:?}", url, e); + } + Ok(Err(e)) => { + println!("Error opening URL:{} {:?}", url, e); + } + } +} + fn main() -> Result<()> { tauri::Builder::default() .invoke_handler(tauri::generate_handler![ @@ -410,6 +435,7 @@ fn main() -> Result<()> { 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() { + println!("Got path in cli argument: {}", value); source_path = Some(Path::new(value).to_path_buf()); } } @@ -419,6 +445,10 @@ fn main() -> Result<()> { } } + if verbose { + println!("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. @@ -436,74 +466,7 @@ fn main() -> Result<()> { } let runner: tauri::async_runtime::JoinHandle> = - tauri::async_runtime::spawn(async move { - // Fix for "." path, which is the current directory. - let source_path = if source_path == Path::new(".") { - std::env::current_dir() - .map_err(|e| anyhow::anyhow!("Error getting the current directory: {:?}", e))? - } else { - source_path - }; - - // If the path does not start with a slash, it is a relative path. - // We need to convert it to an absolute path. - let source_path = if source_path.is_relative() { - std::env::current_dir() - .map_err(|e| anyhow::anyhow!("Error getting the current directory: {:?}", e))? - .join(source_path) - } else { - source_path - }; - - // If the path is a directory, let's assume it is a project directory. - if source_path.is_dir() { - // Load the details about the project from the path. - let project = Project::from_path(&source_path).await.map_err(|e| { - anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e) - })?; - - if verbose { - println!("Project loaded from path: {}", source_path.display()); - } - - // Create the default file in the project. - // Write the initial project file. - let project_file = source_path.join(DEFAULT_PROJECT_KCL_FILE); - tokio::fs::write(&project_file, vec![]).await?; - - return Ok(ProjectState { - project, - current_file: Some(project_file.display().to_string()), - }); - } - - // We were given a file path, not a directory. - // Let's get the parent directory of the file. - let parent = source_path.parent().ok_or_else(|| { - anyhow::anyhow!( - "Error getting the parent directory of the file: {}", - source_path.display() - ) - })?; - - // Load the details about the project from the parent directory. - let project = Project::from_path(&parent).await.map_err(|e| { - anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e) - })?; - - if verbose { - println!( - "Project loaded from path: {}, current file: {}", - parent.display(), - source_path.display() - ); - } - - Ok(ProjectState { - project, - current_file: Some(source_path.display().to_string()), - }) - }); + 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)??; @@ -512,13 +475,30 @@ fn main() -> Result<()> { app.manage(state::Store::new(store)); // Listen on the deep links. - app.listen("deep-link://new-url", |url| { - dbg!(url); + app.listen("deep-link://new-url", |event| { + println!("got deep-link url: {:?}", event); + // TODO: open_url_sync(app.handle(), event.url); }); Ok(()) }) - .run(tauri::generate_context!())?; + .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 { + println!("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(()) } diff --git a/src-tauri/tauri.appstore.conf.json b/src-tauri/tauri.app-store.conf.json similarity index 100% rename from src-tauri/tauri.appstore.conf.json rename to src-tauri/tauri.app-store.conf.json diff --git a/src-tauri/tauri.release-macos.conf.json b/src-tauri/tauri.release-macos.conf.json new file mode 100644 index 000000000..b77568ed2 --- /dev/null +++ b/src-tauri/tauri.release-macos.conf.json @@ -0,0 +1,19 @@ +{ + "$schema": "../node_modules/@tauri-apps/cli/schema.json", + "bundle": { + "windows": { + "certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D", + "digestAlgorithm": "sha256", + "timestampUrl": "http://timestamp.digicert.com" + } + }, + "plugins": { + "updater": { + "active": true, + "endpoints": [ + "https://dl.zoo.dev/releases/modeling-app/last_update.json" + ], + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUzNzA4MjBEQjFBRTY4NzYKUldSMmFLNnhEWUp3NCtsT21Jd05wQktOaGVkOVp6MUFma0hNTDRDSnI2RkJJTEZOWG1ncFhqcU8K" + } + } +} diff --git a/src-tauri/tauri.release.conf.json b/src-tauri/tauri.release.conf.json index b77568ed2..7d88d3201 100644 --- a/src-tauri/tauri.release.conf.json +++ b/src-tauri/tauri.release.conf.json @@ -5,7 +5,45 @@ "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": { diff --git a/src/wasm-lib/kcl/src/settings/types/file.rs b/src/wasm-lib/kcl/src/settings/types/file.rs index 819d533c8..fbb63893d 100644 --- a/src/wasm-lib/kcl/src/settings/types/file.rs +++ b/src/wasm-lib/kcl/src/settings/types/file.rs @@ -1,11 +1,13 @@ //! Types for interacting with files in projects. +use std::path::{Path, PathBuf}; + use anyhow::Result; use parse_display::{Display, FromStr}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use super::Configuration; +use crate::settings::types::{Configuration, DEFAULT_PROJECT_KCL_FILE}; /// State management for the application. #[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq)] @@ -16,6 +18,88 @@ pub struct ProjectState { pub current_file: Option, } +impl ProjectState { + /// Create a new project state from a path. + #[cfg(not(target_arch = "wasm32"))] + pub async fn new_from_path(path: PathBuf) -> Result { + // Fix for "." path, which is the current directory. + + let source_path = if path == Path::new(".") { + std::env::current_dir().map_err(|e| anyhow::anyhow!("Error getting the current directory: {:?}", e))? + } else { + path + }; + + // If the path does not start with a slash, it is a relative path. + // We need to convert it to an absolute path. + let source_path = if source_path.is_relative() { + std::env::current_dir() + .map_err(|e| anyhow::anyhow!("Error getting the current directory: {:?}", e))? + .join(source_path) + } else { + source_path + }; + + // If the path is a directory, let's assume it is a project directory. + if source_path.is_dir() { + // Load the details about the project from the path. + let project = Project::from_path(&source_path) + .await + .map_err(|e| anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e))?; + + // Check if we have a main.kcl file in the project. + let project_file = source_path.join(DEFAULT_PROJECT_KCL_FILE); + + if !project_file.exists() { + // Create the default file in the project. + // Write the initial project file. + tokio::fs::write(&project_file, vec![]).await?; + } + + return Ok(ProjectState { + project, + current_file: Some(project_file.display().to_string()), + }); + } + + // Check if the extension on what we are trying to open is a relevant file type. + // Get the extension of the file. + let extension = source_path + .extension() + .ok_or_else(|| anyhow::anyhow!("Error getting the extension of the file: {}", source_path.display()))?; + let ext = extension.to_string_lossy().to_string(); + + // Check if the extension is a relevant file type. + if !crate::settings::utils::RELEVANT_EXTENSIONS.contains(&ext) || ext == "toml" { + return Err(anyhow::anyhow!( + "File type ({}) cannot be opened with this app: {}, try opening one of the following file types: {}", + ext, + source_path.display(), + crate::settings::utils::RELEVANT_EXTENSIONS.join(", ") + )); + } + + // We were given a file path, not a directory. + // Let's get the parent directory of the file. + let parent = source_path.parent().ok_or_else(|| { + anyhow::anyhow!( + "Error getting the parent directory of the file: {}", + source_path.display() + ) + })?; + + // Load the details about the project from the parent directory. + let project = Project::from_path(&parent) + .await + .map_err(|e| anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e))?; + + Ok(ProjectState { + project, + current_file: Some(source_path.display().to_string()), + }) + } +} + /// Project route information. #[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq)] #[ts(export)] diff --git a/src/wasm-lib/kcl/src/settings/utils.rs b/src/wasm-lib/kcl/src/settings/utils.rs index b91ff7072..044c40489 100644 --- a/src/wasm-lib/kcl/src/settings/utils.rs +++ b/src/wasm-lib/kcl/src/settings/utils.rs @@ -8,8 +8,8 @@ use clap::ValueEnum; use crate::settings::types::file::FileEntry; lazy_static::lazy_static! { - static ref RELEVANT_EXTENSIONS: Vec = { - let mut relevant_extensions = vec!["kcl".to_string(), "stp".to_string(), "glb".to_string()]; + pub static ref RELEVANT_EXTENSIONS: Vec = { + let mut relevant_extensions = vec!["kcl".to_string(), "stp".to_string(), "glb".to_string(), "fbxb".to_string()]; let named_extensions = kittycad::types::FileImportFormat::value_variants() .iter() .map(|x| format!("{}", x))