From b4622ea4d32720bc3bb2a8c740bb70cfe32fed93 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 11 Aug 2022 10:30:55 -0300 Subject: [PATCH 001/423] refactor(app): run setup and window creation when event loop is ready (#4914) --- .changes/config.json | 2 - .changes/refactor-setup.md | 5 +++ core/tauri/src/app.rs | 92 +++++++++++++++++++++++++++++--------- core/tauri/src/test/mod.rs | 16 ++++--- 4 files changed, 86 insertions(+), 29 deletions(-) create mode 100644 .changes/refactor-setup.md diff --git a/.changes/config.json b/.changes/config.json index eb8bdeb0c80..9376b807afb 100644 --- a/.changes/config.json +++ b/.changes/config.json @@ -3,7 +3,6 @@ "timeout": 3600000, "pkgManagers": { "rust": { - "errorOnVersionRange": "^2.0.0-0", "version": true, "getPublishedVersion": "cargo search ${ pkgFile.pkg.package.name } --limit 1 | sed -nE \"s/^[^\\\"]*\\\"//; s/\\\".*//1p\"", "prepublish": [ @@ -78,7 +77,6 @@ ] }, "javascript": { - "errorOnVersionRange": "^2.0.0-0", "version": true, "getPublishedVersion": "npm view ${ pkgFile.pkg.name } version", "prepublish": [ diff --git a/.changes/refactor-setup.md b/.changes/refactor-setup.md new file mode 100644 index 00000000000..657cf32d2ad --- /dev/null +++ b/.changes/refactor-setup.md @@ -0,0 +1,5 @@ +--- +"tauri": major +--- + +**Breaking change:** The window creation and setup hook are now called when the event loop is ready. diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 7084b0924f4..9e9e4a81131 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -40,6 +40,7 @@ use tauri_utils::PackageInfo; use std::{ collections::HashMap, + fmt, path::{Path, PathBuf}, sync::{mpsc::Sender, Arc, Weak}, }; @@ -526,9 +527,10 @@ impl ManagerBase for AppHandle { /// /// This type implements [`Manager`] which allows for manipulation of global application items. #[default_runtime(crate::Wry, wry)] -#[derive(Debug)] pub struct App { runtime: Option, + pending_windows: Option>>, + setup: Option>, manager: WindowManager, #[cfg(all(desktop, feature = "global-shortcut"))] global_shortcut_manager: R::GlobalShortcutManager, @@ -537,6 +539,22 @@ pub struct App { handle: AppHandle, } +impl fmt::Debug for App { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("App"); + d.field("runtime", &self.runtime) + .field("manager", &self.manager) + .field("handle", &self.handle); + + #[cfg(all(desktop, feature = "global-shortcut"))] + d.field("global_shortcut_manager", &self.global_shortcut_manager); + #[cfg(feature = "clipboard")] + d.field("clipboard_manager", &self.clipboard_manager); + + d.finish() + } +} + impl Manager for App {} impl ManagerBase for App { fn manager(&self) -> &WindowManager { @@ -544,7 +562,11 @@ impl ManagerBase for App { } fn runtime(&self) -> RuntimeOrDispatch<'_, R> { - RuntimeOrDispatch::Runtime(self.runtime.as_ref().unwrap()) + if let Some(runtime) = self.runtime.as_ref() { + RuntimeOrDispatch::Runtime(runtime) + } else { + self.handle.runtime() + } } fn managed_app_handle(&self) -> AppHandle { @@ -775,6 +797,17 @@ impl App { let app_handle = self.handle(); let manager = self.manager.clone(); self.runtime.take().unwrap().run(move |event| match event { + RuntimeRunEvent::Ready => { + if let Err(e) = setup(&mut self) { + panic!("Failed to setup app: {}", e); + } + on_event_loop_event( + &app_handle, + RuntimeRunEvent::Ready, + &manager, + Some(&mut callback), + ); + } RuntimeRunEvent::Exit => { on_event_loop_event( &app_handle, @@ -1460,8 +1493,11 @@ impl Builder { #[cfg(feature = "clipboard")] let clipboard_manager = runtime.clipboard_manager(); + #[allow(unused_mut)] let mut app = App { runtime: Some(runtime), + pending_windows: Some(self.pending_windows), + setup: Some(self.setup), manager: manager.clone(), #[cfg(all(desktop, feature = "global-shortcut"))] global_shortcut_manager: global_shortcut_manager.clone(), @@ -1551,26 +1587,6 @@ impl Builder { app.manager.initialize_plugins(&app.handle())?; - let window_labels = self - .pending_windows - .iter() - .map(|p| p.label.clone()) - .collect::>(); - - for pending in self.pending_windows { - let pending = - app - .manager - .prepare_window(app.handle.clone(), pending, &window_labels, None)?; - let detached = app.runtime.as_ref().unwrap().create_window(pending)?; - let _window = app.manager.attach_window(app.handle(), detached); - } - - (self.setup)(&mut app).map_err(|e| crate::Error::Setup(e.into()))?; - - #[cfg(updater)] - app.run_updater(); - Ok(app) } @@ -1593,6 +1609,38 @@ unsafe impl HasRawDisplayHandle for App { } } +fn setup(app: &mut App) -> crate::Result<()> { + let pending_windows = app.pending_windows.take(); + if let Some(pending_windows) = pending_windows { + let window_labels = pending_windows + .iter() + .map(|p| p.label.clone()) + .collect::>(); + + for pending in pending_windows { + let pending = + app + .manager + .prepare_window(app.handle.clone(), pending, &window_labels, None)?; + let detached = if let RuntimeOrDispatch::RuntimeHandle(runtime) = app.handle().runtime() { + runtime.create_window(pending)? + } else { + // the AppHandle's runtime is always RuntimeOrDispatch::RuntimeHandle + unreachable!() + }; + let _window = app.manager.attach_window(app.handle(), detached); + } + } + + if let Some(setup) = app.setup.take() { + (setup)(app).map_err(|e| crate::Error::Setup(e.into()))?; + } + + #[cfg(updater)] + app.run_updater(); + Ok(()) +} + fn on_event_loop_event, RunEvent) + 'static>( app_handle: &AppHandle, event: RuntimeRunEvent, diff --git a/core/tauri/src/test/mod.rs b/core/tauri/src/test/mod.rs index 61aa59664e9..bf0bb33e074 100644 --- a/core/tauri/src/test/mod.rs +++ b/core/tauri/src/test/mod.rs @@ -13,10 +13,10 @@ use std::{borrow::Cow, sync::Arc}; #[cfg(shell_scope)] use crate::ShellScopeConfig; -use crate::{Manager, Pattern}; +use crate::{Manager, Pattern, WindowBuilder}; use tauri_utils::{ assets::{AssetKey, Assets, CspHash}, - config::{CliConfig, Config, PatternKind, TauriConfig}, + config::{CliConfig, Config, PatternKind, TauriConfig, WindowUrl}, }; pub struct NoopAsset { @@ -46,7 +46,7 @@ pub fn mock_context(assets: A) -> crate::Context { package: Default::default(), tauri: TauriConfig { pattern: PatternKind::Brownfield, - windows: vec![Default::default()], + windows: Vec::new(), cli: Some(CliConfig { description: None, long_description: None, @@ -86,9 +86,15 @@ pub fn mock_context(assets: A) -> crate::Context { } pub fn mock_app() -> crate::App { - crate::Builder::::new() + let app = crate::Builder::::new() .build(mock_context(noop_assets())) - .unwrap() + .unwrap(); + + WindowBuilder::new(&app, "main", WindowUrl::App("index.html".into())) + .build() + .unwrap(); + + app } pub(crate) fn mock_invoke_context() -> crate::endpoints::InvokeContext { From 6aee91a181d08387746d33501a576e4645bd94b3 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Fri, 12 Aug 2022 14:11:14 -0300 Subject: [PATCH 002/423] feat(core): prepare for Android --- core/tauri/src/endpoints/operating_system.rs | 2 ++ core/tauri/src/pattern.rs | 2 +- examples/api/src-tauri/Cargo.toml | 1 + examples/api/src-tauri/mobile.toml | 2 +- examples/api/src-tauri/src/mobile.rs | 2 +- 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/tauri/src/endpoints/operating_system.rs b/core/tauri/src/endpoints/operating_system.rs index 1ab69608e32..1b56791a59f 100644 --- a/core/tauri/src/endpoints/operating_system.rs +++ b/core/tauri/src/endpoints/operating_system.rs @@ -78,6 +78,8 @@ fn os_type() -> &'static str { return "Darwin"; #[cfg(target_os = "ios")] return "iOS"; + #[cfg(target_os = "android")] + return "Android"; } #[cfg(os_all)] diff --git a/core/tauri/src/pattern.rs b/core/tauri/src/pattern.rs index 19b3713033e..76f43e80980 100644 --- a/core/tauri/src/pattern.rs +++ b/core/tauri/src/pattern.rs @@ -86,7 +86,7 @@ pub(crate) struct PatternJavascript { #[allow(dead_code)] pub(crate) fn format_real_schema(schema: &str) -> String { - if cfg!(windows) { + if cfg!(windows) || cfg!(target_os = "android") { format!("https://{}.localhost", schema) } else { format!("{}://localhost", schema) diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index baae9446c40..4cc4cbb061e 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -30,6 +30,7 @@ features = [ "macos-private-api", "windows7-compat", "reqwest-client", + "reqwest-native-tls-vendored", "system-tray", "updater" ] diff --git a/examples/api/src-tauri/mobile.toml b/examples/api/src-tauri/mobile.toml index c74c1c2ac1e..998b18530dd 100644 --- a/examples/api/src-tauri/mobile.toml +++ b/examples/api/src-tauri/mobile.toml @@ -1,7 +1,7 @@ [app] name = "api" stylized-name = "Tauri API" -domain = "com.tauri" +domain = "tauri.studio" template-pack = "tauri" [apple] diff --git a/examples/api/src-tauri/src/mobile.rs b/examples/api/src-tauri/src/mobile.rs index beb20e20cd8..73fe7a36d56 100644 --- a/examples/api/src-tauri/src/mobile.rs +++ b/examples/api/src-tauri/src/mobile.rs @@ -33,7 +33,7 @@ fn _start_app() { #[inline(never)] pub extern "C" fn start_app() { #[cfg(target_os = "android")] - android_fn!(com.tauri, api); + android_fn!(studio_tauri, api); _start_app() } From d14322de6821def21a97924a74b79b6867926d5e Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 14 Aug 2022 17:51:09 -0300 Subject: [PATCH 003/423] chore(deps): update to wry 0.20.2 refactor --- core/tauri-runtime-wry/Cargo.toml | 2 +- examples/api/src-tauri/Cargo.lock | 19 +++++++++++++++---- examples/api/src-tauri/src/mobile.rs | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 5780814d478..a0475d06ca8 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -13,7 +13,7 @@ exclude = [ ".license_template", "CHANGELOG.md", "/target" ] readme = "README.md" [dependencies] -wry = { version = "0.20", default-features = false, features = [ "file-drop", "protocol" ] } +wry = { version = "0.20.2", default-features = false, features = [ "file-drop", "protocol" ] } tauri-runtime = { version = "0.10.2", path = "../tauri-runtime" } tauri-utils = { version = "1.0.3", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index b9dddd96380..6f0b89c6ad5 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -2075,6 +2075,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-src" +version = "111.22.0+1.1.1q" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.75" @@ -2084,6 +2093,7 @@ dependencies = [ "autocfg", "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -3089,9 +3099,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad691ca9fca6c2c76c09ffcddf6ae6593fba65d95477cf31780910ed272f5b8" +checksum = "a2093fa6bba3cc0c185b21c900de1b757e66637e78848cbcdda967b836d8c0ec" dependencies = [ "bitflags", "cairo-rs", @@ -4202,13 +4212,14 @@ dependencies = [ [[package]] name = "wry" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a806297d9fae9fef5a9d52480e5edc5452f97ea9d15b55c8a58ab93c9582f1" +checksum = "ea48fb3b68ab76f62837bfcea63baed9a185dbec9c14d4e5d70033e22fefffd2" dependencies = [ "block", "cocoa", "core-graphics", + "crossbeam-channel", "gdk", "gio", "glib", diff --git a/examples/api/src-tauri/src/mobile.rs b/examples/api/src-tauri/src/mobile.rs index 73fe7a36d56..05c5bda021c 100644 --- a/examples/api/src-tauri/src/mobile.rs +++ b/examples/api/src-tauri/src/mobile.rs @@ -1,5 +1,5 @@ #[cfg(target_os = "android")] -use tauri_runtime_wry::wry::application::{android_fn, platform::android::ndk_glue::*}; +use tauri_runtime_wry::wry::android_binding; #[cfg(target_os = "android")] fn init_logging(app_name: &str) { @@ -33,7 +33,7 @@ fn _start_app() { #[inline(never)] pub extern "C" fn start_app() { #[cfg(target_os = "android")] - android_fn!(studio_tauri, api); + android_binding!(studio_tauri, api, _start_app, tauri_runtime_wry::wry); _start_app() } From d3179b84b51f4b66afd06de2183f1999c7634c46 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 14 Aug 2022 20:54:14 -0300 Subject: [PATCH 004/423] chore(examples): readd gitignore rules for mobile api example --- examples/api/src-tauri/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/api/src-tauri/.gitignore b/examples/api/src-tauri/.gitignore index aba21e242c9..e5f47c4d3d3 100644 --- a/examples/api/src-tauri/.gitignore +++ b/examples/api/src-tauri/.gitignore @@ -1,3 +1,7 @@ # Generated by Cargo # will have compiled files and executables /target/ + +# cargo-mobile +.cargo/ +/gen From d44f67f7afd30a81d53a973ec603b2a253150bde Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 15 Aug 2022 12:43:50 -0300 Subject: [PATCH 005/423] feat: add `android init` and `ios init` commands (#4942) --- .changes/mobile-init.md | 6 + tooling/cli/Cargo.lock | 642 +++++++++++++++++- tooling/cli/Cargo.toml | 8 + tooling/cli/src/helpers/template.rs | 68 +- tooling/cli/src/lib.rs | 7 + tooling/cli/src/mobile/android.rs | 36 + tooling/cli/src/mobile/android/project.rs | 184 +++++ tooling/cli/src/mobile/init.rs | 435 ++++++++++++ tooling/cli/src/mobile/ios.rs | 36 + tooling/cli/src/mobile/ios/project.rs | 193 ++++++ tooling/cli/src/mobile/mod.rs | 8 + .../templates/mobile/android/.editorconfig | 12 + .../cli/templates/mobile/android/.gitignore | 15 + .../templates/mobile/android/app/.gitignore | 1 + .../mobile/android/app/build.gradle.kts | 85 +++ .../mobile/android/app/proguard-rules.pro | 21 + .../android/app/src/main/AndroidManifest.xml | 23 + .../mobile/android/app/src/main/Ipc.kt | 18 + .../android/app/src/main/MainActivity.kt | 3 + .../app/src/main/RustWebChromeClient.kt | 26 + .../android/app/src/main/RustWebViewClient.kt | 24 + .../android/app/src/main/TauriActivity.kt | 71 ++ .../drawable-v24/ic_launcher_foreground.xml | 30 + .../res/drawable/ic_launcher_background.xml | 170 +++++ .../app/src/main/res/layout/activity_main.xml | 18 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../app/src/main/res/values-night/themes.xml | 16 + .../app/src/main/res/values/colors.xml | 10 + .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/themes.xml | 16 + .../templates/mobile/android/build.gradle.kts | 27 + .../mobile/android/buildSrc/build.gradle.kts | 27 + .../buildSrc/src/main/kotlin/BuildTask.kt | 54 ++ .../buildSrc/src/main/kotlin/RustPlugin.kt | 51 ++ .../mobile/android/gradle.properties | 23 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + tooling/cli/templates/mobile/android/gradlew | 185 +++++ .../cli/templates/mobile/android/gradlew.bat | 89 +++ .../templates/mobile/android/settings.gradle | 3 + tooling/cli/templates/mobile/ios/.gitignore | 2 + .../templates/mobile/ios/ExportOptions.plist | 8 + tooling/cli/templates/mobile/ios/Podfile | 25 + .../Sources/{{app.name}}/bindings/bindings.h | 8 + .../mobile/ios/Sources/{{app.name}}/main.mm | 6 + tooling/cli/templates/mobile/ios/project.yml | 280 ++++++++ .../xcshareddata/WorkspaceSettings.xcsettings | 12 + 58 files changed, 2973 insertions(+), 28 deletions(-) create mode 100644 .changes/mobile-init.md create mode 100644 tooling/cli/src/mobile/android.rs create mode 100644 tooling/cli/src/mobile/android/project.rs create mode 100644 tooling/cli/src/mobile/init.rs create mode 100644 tooling/cli/src/mobile/ios.rs create mode 100644 tooling/cli/src/mobile/ios/project.rs create mode 100644 tooling/cli/src/mobile/mod.rs create mode 100644 tooling/cli/templates/mobile/android/.editorconfig create mode 100644 tooling/cli/templates/mobile/android/.gitignore create mode 100644 tooling/cli/templates/mobile/android/app/.gitignore create mode 100644 tooling/cli/templates/mobile/android/app/build.gradle.kts create mode 100644 tooling/cli/templates/mobile/android/app/proguard-rules.pro create mode 100644 tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/Ipc.kt create mode 100644 tooling/cli/templates/mobile/android/app/src/main/MainActivity.kt create mode 100644 tooling/cli/templates/mobile/android/app/src/main/RustWebChromeClient.kt create mode 100644 tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt create mode 100644 tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/layout/activity_main.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/values-night/themes.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/values/colors.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/values/strings.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/values/themes.xml create mode 100644 tooling/cli/templates/mobile/android/build.gradle.kts create mode 100644 tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts create mode 100644 tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt create mode 100644 tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt create mode 100644 tooling/cli/templates/mobile/android/gradle.properties create mode 100644 tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 tooling/cli/templates/mobile/android/gradlew create mode 100644 tooling/cli/templates/mobile/android/gradlew.bat create mode 100644 tooling/cli/templates/mobile/android/settings.gradle create mode 100644 tooling/cli/templates/mobile/ios/.gitignore create mode 100644 tooling/cli/templates/mobile/ios/ExportOptions.plist create mode 100644 tooling/cli/templates/mobile/ios/Podfile create mode 100644 tooling/cli/templates/mobile/ios/Sources/{{app.name}}/bindings/bindings.h create mode 100644 tooling/cli/templates/mobile/ios/Sources/{{app.name}}/main.mm create mode 100644 tooling/cli/templates/mobile/ios/project.yml create mode 100644 tooling/cli/templates/mobile/ios/{{app.name}}.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/.changes/mobile-init.md b/.changes/mobile-init.md new file mode 100644 index 00000000000..97c17f26654 --- /dev/null +++ b/.changes/mobile-init.md @@ -0,0 +1,6 @@ +--- +"cli.rs": minor +"cli.js": minor +--- + +Added `android init` and `ios init` commands. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 92c3200f0c4..7dc82570871 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -58,6 +58,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "anyhow" version = "1.0.58" @@ -70,6 +79,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "attohttpc" version = "0.22.0" @@ -112,6 +127,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" +[[package]] +name = "bicycle" +version = "0.1.0" +source = "git+https://github.com/BrainiumLLC/bicycle?rev=28080e0c6fa4067d9dd1b0f2b7322b6b32178e1f#28080e0c6fa4067d9dd1b0f2b7322b6b32178e1f" +dependencies = [ + "handlebars 3.5.5", + "log", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "bit_field" version = "0.10.1" @@ -136,6 +163,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "block-buffer" version = "0.7.3" @@ -166,6 +199,16 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "bossy" +version = "0.2.1" +source = "git+https://github.com/lucasfernog/bossy?branch=fix/winapi-features#83ee04daddbc9b985b5f8dcf54d7229f0542d41d" +dependencies = [ + "libc", + "log", + "winapi 0.3.9", +] + [[package]] name = "bstr" version = "0.2.17" @@ -226,6 +269,48 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cargo-mobile" +version = "0.1.0" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=feat/library#d2b3f7248657c60dcc0669303329b802f04a6291" +dependencies = [ + "bicycle", + "bossy", + "cocoa", + "colored 1.9.3", + "const-utf16", + "core-foundation 0.7.0", + "deunicode", + "dunce", + "embed-resource", + "english-numbers", + "env_logger 0.7.1", + "freedesktop_entry_parser", + "heck 0.4.0", + "hit", + "home", + "ignore", + "indexmap", + "java-properties", + "lexical-core", + "log", + "objc", + "objc_id", + "once-cell-regex", + "openssl", + "path_abs", + "reserved-names", + "serde", + "serde_json", + "structopt", + "textwrap 0.11.0", + "thiserror", + "toml", + "ureq", + "windows 0.26.0", + "yes-or-no", +] + [[package]] name = "cc" version = "1.0.73" @@ -276,6 +361,21 @@ dependencies = [ "generic-array 0.14.5", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + [[package]] name = "clap" version = "3.2.7" @@ -288,9 +388,9 @@ dependencies = [ "clap_lex", "indexmap", "once_cell", - "strsim", + "strsim 0.10.0", "termcolor", - "textwrap", + "textwrap 0.15.0", ] [[package]] @@ -299,7 +399,7 @@ version = "3.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" dependencies = [ - "heck", + "heck 0.4.0", "proc-macro-error", "proc-macro2", "quote", @@ -315,12 +415,38 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "cocoa" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c49e86fc36d5704151f5996b7b3795385f50ce09e3be0f47a0cfde869681cf8" +dependencies = [ + "bitflags", + "block", + "core-foundation 0.7.0", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi 0.3.9", +] + [[package]] name = "colored" version = "2.0.0" @@ -357,6 +483,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "const-utf16" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90feefab165fe011746e3be2f0708b7b180fcbd9f5054ff81a454d7bd93d8285" + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -375,22 +507,50 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + [[package]] name = "core-foundation" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.8.3", "libc", ] +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + [[package]] name = "core-foundation-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "core-graphics" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" +dependencies = [ + "bitflags", + "core-foundation 0.7.0", + "foreign-types", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.2" @@ -550,7 +710,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn", ] @@ -564,7 +724,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn", ] @@ -622,6 +782,12 @@ dependencies = [ "syn", ] +[[package]] +name = "deunicode" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c9736e15e7df1638a7f6eee92a6511615c738246a052af5ba86f039b65aede" + [[package]] name = "dialoguer" version = "0.10.1" @@ -689,6 +855,12 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dunce" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" + [[package]] name = "dyn-clone" version = "1.0.6" @@ -701,12 +873,108 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "embed-resource" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc24ff8d764818e9ab17963b0593c535f077a513f565e75e4352d758bc4d8c0" +dependencies = [ + "cc", + "rustc_version", + "toml", + "vswhom", + "winreg", +] + [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +dependencies = [ + "encoding-index-japanese", + "encoding-index-korean", + "encoding-index-simpchinese", + "encoding-index-singlebyte", + "encoding-index-tradchinese", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" + +[[package]] +name = "english-numbers" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4f5d6e192964d498b45abee72ca445e91909094bc8e8791259e82c2a0d1aa6" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime 1.3.0", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.9.0" @@ -714,7 +982,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" dependencies = [ "atty", - "humantime", + "humantime 2.1.0", "log", "regex", "termcolor", @@ -838,6 +1106,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "freedesktop_entry_parser" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4" +dependencies = [ + "nom", + "thiserror", +] + [[package]] name = "fsevent" version = "0.4.0" @@ -992,6 +1270,20 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "handlebars" +version = "3.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3" +dependencies = [ + "log", + "pest", + "pest_derive", + "quick-error 2.0.1", + "serde", + "serde_json", +] + [[package]] name = "handlebars" version = "4.3.1" @@ -1012,6 +1304,15 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.0" @@ -1033,6 +1334,18 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hit" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a8b280c3c6e2a2a51b3dde2f1071b28afff0ebce59e29443b1b391e7efe32fd" +dependencies = [ + "bossy", + "log", + "once-cell-regex", + "thiserror", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1042,6 +1355,15 @@ dependencies = [ "digest 0.10.3", ] +[[package]] +name = "home" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "html5ever" version = "0.25.2" @@ -1067,6 +1389,15 @@ dependencies = [ "itoa 1.0.2", ] +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error 1.2.3", +] + [[package]] name = "humantime" version = "2.1.0" @@ -1241,6 +1572,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +[[package]] +name = "java-properties" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1904d8654a1ef51034d02d5a9411b50bf91bea15b0ab644ae179d1325976263" +dependencies = [ + "encoding", + "lazy_static", + "regex", +] + [[package]] name = "jobserver" version = "0.1.24" @@ -1349,6 +1691,19 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff" +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 1.0.0", + "ryu", + "static_assertions", +] + [[package]] name = "libc" version = "0.2.126" @@ -1426,6 +1781,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "maplit" version = "1.0.2" @@ -1473,6 +1837,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "minisign" version = "0.7.0" @@ -1661,6 +2031,16 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "notify" version = "4.0.17" @@ -1728,6 +2108,34 @@ dependencies = [ "libc", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "once-cell-regex" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de7e389a5043420c8f2b95ed03f3f104ad6f4c41f7d7e27298f033abc253e8" +dependencies = [ + "once_cell", + "regex", +] + [[package]] name = "once_cell" version = "1.13.0" @@ -1852,6 +2260,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "path_abs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ef02f6342ac01d8a93b65f96db53fe68a92a15f41144f97fb00a9e669633c3" +dependencies = [ + "serde", + "serde_derive", + "std_prelude", + "stfu8", +] + [[package]] name = "pbkdf2" version = "0.10.1" @@ -2148,6 +2568,18 @@ dependencies = [ "url", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.20" @@ -2308,6 +2740,14 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "reserved-names" +version = "0.1.0" +source = "git+https://github.com/BrainiumLLC/reserved-names#cc46545db485b13851e9136280b7d8e5a95a9d18" +dependencies = [ + "thiserror", +] + [[package]] name = "ring" version = "0.16.20" @@ -2481,8 +2921,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ "bitflags", - "core-foundation", - "core-foundation-sys", + "core-foundation 0.9.3", + "core-foundation-sys 0.8.3", "libc", "security-framework-sys", ] @@ -2493,7 +2933,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.8.3", "libc", ] @@ -2736,6 +3176,28 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "std_prelude" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8207e78455ffdf55661170876f88daf85356e4edd54e0a3dbc79586ca1e50cbe" + +[[package]] +name = "stfu8" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "019f0c664fd85d5a87dcfb62b40b691055392a35a6e59f4df83d4b770db7e876" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "string_cache" version = "0.8.4" @@ -2762,12 +3224,42 @@ dependencies = [ "quote", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap 2.34.0", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "subtle" version = "2.4.1" @@ -2825,8 +3317,8 @@ dependencies = [ "bitness", "dirs-next", "glob", - "handlebars", - "heck", + "handlebars 4.3.1", + "heck 0.4.0", "hex", "icns", "image", @@ -2839,7 +3331,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "strsim", + "strsim 0.10.0", "tar", "tauri-utils", "tempfile", @@ -2858,14 +3350,16 @@ version = "1.0.5" dependencies = [ "anyhow", "base64", - "clap", - "colored", + "bossy", + "cargo-mobile", + "clap 3.2.7", + "colored 2.0.0", "ctrlc", "dialoguer", - "env_logger", + "env_logger 0.9.0", "glob", - "handlebars", - "heck", + "handlebars 4.3.1", + "heck 0.4.0", "ignore", "include_dir", "json-patch", @@ -2888,6 +3382,8 @@ dependencies = [ "tauri-utils", "tempfile", "terminal_size 0.2.1", + "textwrap 0.11.0", + "thiserror", "toml", "toml_edit", "unicode-width", @@ -2917,7 +3413,7 @@ dependencies = [ "ctor", "getrandom 0.2.7", "glob", - "heck", + "heck 0.4.0", "html5ever", "json-patch", "json5", @@ -2934,7 +3430,7 @@ dependencies = [ "toml", "url", "walkdir", - "windows", + "windows 0.37.0", ] [[package]] @@ -2962,6 +3458,16 @@ dependencies = [ "utf-8", ] +[[package]] +name = "term_size" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -2991,6 +3497,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "term_size", + "unicode-width", +] + [[package]] name = "textwrap" version = "0.15.0" @@ -3103,6 +3619,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ + "indexmap", "serde", ] @@ -3168,6 +3685,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + [[package]] name = "unicode-width" version = "0.1.9" @@ -3293,12 +3816,38 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22025f6d8eb903ebf920ea6933b70b1e495be37e2cb4099e62c80454aaf57c39" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "walkdir" version = "2.3.2" @@ -3460,6 +4009,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "347cdcaae1addebdff584aea1f9fbc0426dedbe1315f1dcf30c7a9876401cd25" +dependencies = [ + "windows_aarch64_msvc 0.26.0", + "windows_i686_gnu 0.26.0", + "windows_i686_msvc 0.26.0", + "windows_x86_64_gnu 0.26.0", + "windows_x86_64_msvc 0.26.0", +] + [[package]] name = "windows" version = "0.37.0" @@ -3503,6 +4065,12 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169" +[[package]] +name = "windows_aarch64_msvc" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7758986b022add546ae53ccad31f4852ce6bd2e2c2d3cc2b1d7d06dea0b90da" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -3515,6 +4083,12 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" +[[package]] +name = "windows_i686_gnu" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29261214caab8e589f61031ba1ccd5c3c25e61db2118a3aec4459f58ff798726" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -3527,6 +4101,12 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" +[[package]] +name = "windows_i686_msvc" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43984fb3b944743142112ae926e7adeccb60f35bb81d43114f4d0fe2871f60ba" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -3539,6 +4119,12 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" +[[package]] +name = "windows_x86_64_gnu" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a80fc90e1ad19769e596a3f58d0776319059e21cac9069a5a2a791362ce7190" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -3551,6 +4137,12 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc24ddac19a0cf02ad2b32d8897f202fc1a13ef285e2d4774e6610783cc8398f" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -3597,6 +4189,16 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +[[package]] +name = "yes-or-no" +version = "0.1.0" +source = "git+https://github.com/BrainiumLLC/yes-or-no#71d932693601fcf93ff906b90ad61f85b52117c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.5.5" diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index c2a77100a48..8fd0c9263af 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -26,7 +26,15 @@ include = [ name = "cargo-tauri" path = "src/main.rs" +[patch.crates-io] +bossy = { git = "https://github.com/lucasfernog/bossy", branch = "fix/winapi-features" } + [dependencies] +# cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } +cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "feat/library", default-features = false } +bossy = "0.2" +textwrap = { version = "0.11.0", features = ["term_size"] } +thiserror = "1" clap = { version = "3.2", features = [ "derive" ] } anyhow = "1.0" tauri-bundler = { version = "1.0.5", path = "../bundler" } diff --git a/tooling/cli/src/helpers/template.rs b/tooling/cli/src/helpers/template.rs index 5ff775d17c7..4aa8b7343d8 100644 --- a/tooling/cli/src/helpers/template.rs +++ b/tooling/cli/src/helpers/template.rs @@ -3,22 +3,74 @@ // SPDX-License-Identifier: MIT use std::{ - collections::BTreeMap, fs::{create_dir_all, File}, io::Write, - path::Path, + path::{Path, PathBuf}, }; -use handlebars::Handlebars; +use handlebars::{to_json, Handlebars}; use include_dir::Dir; +use serde::Serialize; +use serde_json::value::{Map, Value as JsonValue}; -pub fn render>( +/// Map of template variable names and values. +#[derive(Clone, Debug)] +#[repr(transparent)] +pub struct JsonMap(Map); + +impl Default for JsonMap { + fn default() -> Self { + Self(Map::new()) + } +} + +impl JsonMap { + pub fn insert(&mut self, name: &str, value: impl Serialize) { + self.0.insert(name.to_owned(), to_json(value)); + } + + pub fn inner(&self) -> &Map { + &self.0 + } +} + +pub fn render, D: Serialize>( + handlebars: &Handlebars<'_>, + data: &D, + dir: &Dir<'_>, + out_dir: P, +) -> crate::Result<()> { + let out_dir = out_dir.as_ref(); + let mut created_dirs = Vec::new(); + render_with_generator( + handlebars, + data, + dir, + &out_dir, + &mut |file_path: &PathBuf| { + let path = out_dir.join(file_path); + let parent = path.parent().unwrap().to_path_buf(); + if !created_dirs.contains(&parent) { + create_dir_all(&parent)?; + created_dirs.push(parent); + } + File::create(path) + }, + ) +} + +pub fn render_with_generator< + P: AsRef, + D: Serialize, + F: FnMut(&PathBuf) -> std::io::Result, +>( handlebars: &Handlebars<'_>, - data: &BTreeMap<&str, serde_json::Value>, + data: &D, dir: &Dir<'_>, out_dir: P, + out_file_generator: &mut F, ) -> crate::Result<()> { - create_dir_all(out_dir.as_ref().join(dir.path()))?; + let out_dir = out_dir.as_ref(); for file in dir.files() { let mut file_path = file.path().to_path_buf(); // cargo for some reason ignores the /templates folder packaging when it has a Cargo.toml file inside @@ -28,7 +80,7 @@ pub fn render>( file_path.set_extension("toml"); } } - let mut output_file = File::create(out_dir.as_ref().join(file_path))?; + let mut output_file = out_file_generator(&file_path)?; if let Some(utf8) = file.contents_utf8() { handlebars .render_template_to_write(utf8, &data, &mut output_file) @@ -38,7 +90,7 @@ pub fn render>( } } for dir in dir.dirs() { - render(handlebars, data, dir, out_dir.as_ref())?; + render_with_generator(handlebars, data, dir, out_dir, out_file_generator)?; } Ok(()) } diff --git a/tooling/cli/src/lib.rs b/tooling/cli/src/lib.rs index f962940601a..5fc4b6433e8 100644 --- a/tooling/cli/src/lib.rs +++ b/tooling/cli/src/lib.rs @@ -10,6 +10,7 @@ mod helpers; mod info; mod init; mod interface; +mod mobile; mod plugin; mod signer; @@ -66,6 +67,9 @@ enum Commands { Init(init::Options), Plugin(plugin::Cli), Signer(signer::Cli), + Android(mobile::android::Cli), + #[cfg(target_os = "macos")] + Ios(mobile::ios::Cli), } fn format_error(err: clap::Error) -> clap::Error { @@ -164,6 +168,9 @@ where Commands::Init(options) => init::command(options)?, Commands::Plugin(cli) => plugin::command(cli)?, Commands::Signer(cli) => signer::command(cli)?, + Commands::Android(cli) => mobile::android::command(cli)?, + #[cfg(target_os = "macos")] + Commands::Ios(cli) => mobile::ios::command(cli)?, } Ok(()) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs new file mode 100644 index 00000000000..b3b3a243548 --- /dev/null +++ b/tooling/cli/src/mobile/android.rs @@ -0,0 +1,36 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use clap::{Parser, Subcommand}; + +use super::init::{command as init_command, Options as InitOptions, Target as InitTarget}; +use crate::Result; + +pub(crate) mod project; + +#[derive(Parser)] +#[clap( + author, + version, + about = "Android commands", + subcommand_required(true), + arg_required_else_help(true) +)] +pub struct Cli { + #[clap(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + Init(InitOptions), +} + +pub fn command(cli: Cli) -> Result<()> { + match cli.command { + Commands::Init(options) => init_command(options, InitTarget::Android)?, + } + + Ok(()) +} diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs new file mode 100644 index 00000000000..b70771b0787 --- /dev/null +++ b/tooling/cli/src/mobile/android/project.rs @@ -0,0 +1,184 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use crate::helpers::template; +use cargo_mobile::{ + android::{ + config::{Config, Metadata}, + env::Env, + ndk, + target::Target, + }, + dot_cargo, os, + target::TargetTrait as _, + util::{ + self, + cli::{Report, TextWrapper}, + ln, prefix_path, + }, +}; +use handlebars::Handlebars; +use include_dir::{include_dir, Dir}; + +use std::{ + ffi::OsStr, + fs, + path::{Path, PathBuf}, +}; + +const TEMPLATE_DIR: Dir<'_> = include_dir!("templates/mobile/android"); + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("failed to run rustup: {0}")] + RustupFailed(bossy::Error), + #[error("failed to process template: {0}")] + TemplateProcessingFailed(String), + #[error("failed to create directory at {path}: {cause}")] + DirectoryCreationFailed { + path: PathBuf, + cause: std::io::Error, + }, + #[error("failed to symlink asset directory")] + AssetDirSymlinkFailed, + #[error(transparent)] + DotCargoGenFailed(ndk::MissingToolError), + #[error("failed to copy {src} to {dest}: {cause}")] + FileCopyFailed { + src: PathBuf, + dest: PathBuf, + cause: std::io::Error, + }, + #[error("asset source {0} is invalid")] + AssetSourceInvalid(PathBuf), +} + +pub fn gen( + config: &Config, + metadata: &Metadata, + env: &Env, + (handlebars, mut map): (Handlebars, template::JsonMap), + wrapper: &TextWrapper, + dot_cargo: &mut dot_cargo::DotCargo, +) -> Result<(), Error> { + println!("Installing Android toolchains..."); + Target::install_all().map_err(Error::RustupFailed)?; + println!("Generating Android Studio project..."); + let dest = config.project_dir(); + let asset_packs = metadata.asset_packs().unwrap_or_default(); + + map.insert( + "root-dir-rel", + Path::new(&os::replace_path_separator( + util::relativize_path(config.app().root_dir(), config.project_dir()).into_os_string(), + )), + ); + map.insert("root-dir", config.app().root_dir()); + map.insert("targets", Target::all().values().collect::>()); + map.insert("target-names", Target::all().keys().collect::>()); + map.insert( + "arches", + Target::all() + .values() + .map(|target| target.arch) + .collect::>(), + ); + map.insert("android-app-plugins", metadata.app_plugins()); + map.insert( + "android-project-dependencies", + metadata.project_dependencies(), + ); + map.insert("android-app-dependencies", metadata.app_dependencies()); + map.insert( + "android-app-dependencies-platform", + metadata.app_dependencies_platform(), + ); + map.insert( + "has-code", + metadata.project_dependencies().is_some() + || metadata.app_dependencies().is_some() + || metadata.app_dependencies_platform().is_some(), + ); + map.insert( + "asset-packs", + asset_packs + .iter() + .map(|p| p.name.as_str()) + .collect::>(), + ); + map.insert("windows", cfg!(windows)); + + let domain = config.app().reverse_domain().replace('.', "/"); + let package_path = format!("java/{}/{}", domain, config.app().name()); + + let mut created_dirs = Vec::new(); + template::render_with_generator( + &handlebars, + map.inner(), + &TEMPLATE_DIR, + &dest, + &mut |path| { + let path = if path.extension() == Some(OsStr::new("kt")) { + let parent = path.parent().unwrap(); + let file_name = path.file_name().unwrap(); + let out_dir = dest.join(parent).join(&package_path); + out_dir.join(file_name) + } else { + dest.join(path) + }; + + let parent = path.parent().unwrap().to_path_buf(); + if !created_dirs.contains(&parent) { + fs::create_dir_all(&parent)?; + created_dirs.push(parent); + } + + fs::File::create(path) + }, + ) + .map_err(|e| Error::TemplateProcessingFailed(e.to_string()))?; + + if !asset_packs.is_empty() { + Report::action_request( + "When running from Android Studio, you must first set your deployment option to \"APK from app bundle\".", + "Android Studio will not be able to find your asset packs otherwise. The option can be found under \"Run > Edit Configurations > Deploy\"." + ).print(wrapper); + } + + let source_dest = dest.join("app"); + for source in metadata.app_sources() { + let source_src = config.app().root_dir().join(&source); + let source_file = source_src + .file_name() + .ok_or_else(|| Error::AssetSourceInvalid(source_src.clone()))?; + fs::copy(&source_src, source_dest.join(source_file)).map_err(|cause| { + Error::FileCopyFailed { + src: source_src, + dest: source_dest.clone(), + cause, + } + })?; + } + + let dest = prefix_path(dest, "app/src/main/"); + fs::create_dir_all(&dest).map_err(|cause| Error::DirectoryCreationFailed { + path: dest.clone(), + cause, + })?; + os::ln::force_symlink_relative(config.app().asset_dir(), dest, ln::TargetStyle::Directory) + .map_err(|_| Error::AssetDirSymlinkFailed)?; + + { + for target in Target::all().values() { + dot_cargo.insert_target( + target.triple.to_owned(), + target + .generate_cargo_config(config, env) + .map_err(Error::DotCargoGenFailed)?, + ); + } + } + + Ok(()) +} diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs new file mode 100644 index 00000000000..1089eff7130 --- /dev/null +++ b/tooling/cli/src/mobile/init.rs @@ -0,0 +1,435 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use crate::helpers::{app_paths::tauri_dir, template::JsonMap}; +use crate::Result; +use cargo_mobile::{ + android, + config::{ + self, + metadata::{self, Metadata}, + Config, + }, + dot_cargo, + init::{DOT_FIRST_INIT_CONTENTS, DOT_FIRST_INIT_FILE_NAME}, + opts, + os::code_command, + util::{ + self, + cli::{Report, TextWrapper}, + }, +}; +use clap::Parser; +use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError}; + +use std::{ + fs, io, + path::{Path, PathBuf}, +}; + +use opts::{NonInteractive, OpenInEditor, ReinstallDeps, SkipDevTools}; + +#[derive(Debug, Parser)] +#[clap(about = "Initializes a Tauri Android project")] +pub struct Options { + /// Skip prompting for values + #[clap(long)] + ci: bool, +} + +pub fn command(mut options: Options, target: Target) -> Result<()> { + options.ci = options.ci || std::env::var("CI").is_ok(); + + let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); + exec( + target, + &wrapper, + options.ci.into(), + SkipDevTools::No, + ReinstallDeps::Yes, + OpenInEditor::No, + tauri_dir(), + ) + .map_err(|e| anyhow::anyhow!("{:#}", e))?; + Ok(()) +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + ConfigLoadOrGen(config::LoadOrGenError), + #[error("failed to init first init file {path}: {cause}")] + DotFirstInitWrite { path: PathBuf, cause: io::Error }, + #[error("failed to create asset dir {asset_dir}: {cause}")] + AssetDirCreation { + asset_dir: PathBuf, + cause: io::Error, + }, + #[error("failed to install LLDB VS Code extension: {0}")] + LldbExtensionInstall(bossy::Error), + #[error(transparent)] + DotCargoLoad(dot_cargo::LoadError), + #[error(transparent)] + HostTargetTripleDetection(util::HostTargetTripleError), + #[error(transparent)] + Metadata(metadata::Error), + #[cfg(target_os = "macos")] + #[error(transparent)] + IosInit(super::ios::project::Error), + #[error(transparent)] + AndroidEnv(android::env::Error), + #[error(transparent)] + AndroidInit(super::android::project::Error), + #[error(transparent)] + DotCargoWrite(dot_cargo::WriteError), + #[error("failed to delete first init file {path}: {cause}")] + DotFirstInitDelete { path: PathBuf, cause: io::Error }, + #[error(transparent)] + OpenInEditor(util::OpenInEditorError), +} + +#[derive(PartialEq, Eq)] +pub enum Target { + Android, + #[cfg(target_os = "macos")] + Ios, +} + +pub fn exec( + target: Target, + wrapper: &TextWrapper, + non_interactive: NonInteractive, + skip_dev_tools: SkipDevTools, + #[allow(unused_variables)] reinstall_deps: ReinstallDeps, + open_in_editor: OpenInEditor, + cwd: impl AsRef, +) -> Result { + let cwd = cwd.as_ref(); + let (config, config_origin) = + Config::load_or_gen(cwd, non_interactive, wrapper).map_err(Error::ConfigLoadOrGen)?; + let dot_first_init_path = config.app().root_dir().join(DOT_FIRST_INIT_FILE_NAME); + let dot_first_init_exists = { + let dot_first_init_exists = dot_first_init_path.exists(); + if config_origin.freshly_minted() && !dot_first_init_exists { + // indicate first init is ongoing, so that if we error out and exit + // the next init will know to still use `WildWest` filtering + log::info!("creating first init dot file at {:?}", dot_first_init_path); + fs::write(&dot_first_init_path, DOT_FIRST_INIT_CONTENTS).map_err(|cause| { + Error::DotFirstInitWrite { + path: dot_first_init_path.clone(), + cause, + } + })?; + true + } else { + dot_first_init_exists + } + }; + + let asset_dir = config.app().asset_dir(); + if !asset_dir.is_dir() { + fs::create_dir_all(&asset_dir).map_err(|cause| Error::AssetDirCreation { asset_dir, cause })?; + } + if skip_dev_tools.no() && util::command_present("code").unwrap_or_default() { + let mut command = code_command(); + command.add_args(&["--install-extension", "vadimcn.vscode-lldb"]); + if non_interactive.yes() { + command.add_arg("--force"); + } + command + .run_and_wait() + .map_err(Error::LldbExtensionInstall)?; + } + let mut dot_cargo = dot_cargo::DotCargo::load(config.app()).map_err(Error::DotCargoLoad)?; + // Mysteriously, builds that don't specify `--target` seem to fight over + // the build cache with builds that use `--target`! This means that + // alternating between i.e. `cargo run` and `cargo apple run` would + // result in clean builds being made each time you switched... which is + // pretty nightmarish. Specifying `build.target` in `.cargo/config` + // fortunately has the same effect as specifying `--target`, so now we can + // `cargo run` with peace of mind! + // + // This behavior could be explained here: + // https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags + dot_cargo + .set_default_target(util::host_target_triple().map_err(Error::HostTargetTripleDetection)?); + + let metadata = Metadata::load(config.app().root_dir()).map_err(Error::Metadata)?; + + // Generate Xcode project + #[cfg(target_os = "macos")] + if target == Target::Ios && metadata.apple().supported() { + super::ios::project::gen( + config.apple(), + metadata.apple(), + handlebars(&config), + wrapper, + non_interactive, + skip_dev_tools, + reinstall_deps, + ) + .map_err(Error::IosInit)?; + } else { + println!("Skipping iOS init, since it's marked as unsupported in your Cargo.toml metadata"); + } + + // Generate Android Studio project + if target == Target::Android && metadata.android().supported() { + match android::env::Env::new() { + Ok(env) => super::android::project::gen( + config.android(), + metadata.android(), + &env, + handlebars(&config), + wrapper, + &mut dot_cargo, + ) + .map_err(Error::AndroidInit)?, + Err(err) => { + if err.sdk_or_ndk_issue() { + Report::action_request( + " to initialize Android environment; Android support won't be usable until you fix the issue below and re-run `cargo mobile init`!", + err, + ) + .print(wrapper); + } else { + return Err(Error::AndroidEnv(err)); + } + } + } + } else { + println!("Skipping Android init, since it's marked as unsupported in your Cargo.toml metadata"); + } + + dot_cargo + .write(config.app()) + .map_err(Error::DotCargoWrite)?; + if dot_first_init_exists { + log::info!("deleting first init dot file at {:?}", dot_first_init_path); + fs::remove_file(&dot_first_init_path).map_err(|cause| Error::DotFirstInitDelete { + path: dot_first_init_path, + cause, + })?; + } + Report::victory( + "Project generated successfully!", + "Make cool apps! 🌻 🐕 🎉", + ) + .print(wrapper); + if open_in_editor.yes() { + util::open_in_editor(cwd).map_err(Error::OpenInEditor)?; + } + Ok(config) +} + +fn handlebars(config: &Config) -> (Handlebars<'static>, JsonMap) { + let mut h = Handlebars::new(); + h.register_escape_fn(handlebars::no_escape); + + h.register_helper("html-escape", Box::new(html_escape)); + h.register_helper("join", Box::new(join)); + h.register_helper("quote-and-join", Box::new(quote_and_join)); + h.register_helper( + "quote-and-join-colon-prefix", + Box::new(quote_and_join_colon_prefix), + ); + h.register_helper("snake-case", Box::new(snake_case)); + h.register_helper("reverse-domain", Box::new(reverse_domain)); + h.register_helper( + "reverse-domain-snake-case", + Box::new(reverse_domain_snake_case), + ); + // don't mix these up or very bad things will happen to all of us + h.register_helper("prefix-path", Box::new(prefix_path)); + h.register_helper("unprefix-path", Box::new(unprefix_path)); + + let mut map = JsonMap::default(); + map.insert("app", config.app()); + #[cfg(target_os = "macos")] + map.insert("apple", config.apple()); + map.insert("android", config.android()); + + (h, map) +} + +fn get_str<'a>(helper: &'a Helper) -> &'a str { + helper + .param(0) + .and_then(|v| v.value().as_str()) + .unwrap_or("") +} + +fn get_str_array<'a>( + helper: &'a Helper, + formatter: impl Fn(&str) -> String, +) -> Option> { + helper.param(0).and_then(|v| { + v.value().as_array().and_then(|arr| { + arr + .iter() + .map(|val| { + val.as_str().map( + #[allow(clippy::redundant_closure)] + |s| formatter(s), + ) + }) + .collect() + }) + }) +} + +fn html_escape( + helper: &Helper, + _: &Handlebars, + _ctx: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + out + .write(&handlebars::html_escape(get_str(helper))) + .map_err(Into::into) +} + +fn join( + helper: &Helper, + _: &Handlebars, + _: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + out + .write( + &get_str_array(helper, |s| s.to_string()) + .ok_or_else(|| RenderError::new("`join` helper wasn't given an array"))? + .join(", "), + ) + .map_err(Into::into) +} + +fn quote_and_join( + helper: &Helper, + _: &Handlebars, + _: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + out + .write( + &get_str_array(helper, |s| format!("{:?}", s)) + .ok_or_else(|| RenderError::new("`quote-and-join` helper wasn't given an array"))? + .join(", "), + ) + .map_err(Into::into) +} + +fn quote_and_join_colon_prefix( + helper: &Helper, + _: &Handlebars, + _: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + out + .write( + &get_str_array(helper, |s| format!("{:?}", format!(":{}", s))) + .ok_or_else(|| { + RenderError::new("`quote-and-join-colon-prefix` helper wasn't given an array") + })? + .join(", "), + ) + .map_err(Into::into) +} + +fn snake_case( + helper: &Helper, + _: &Handlebars, + _: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + use heck::ToSnekCase as _; + out + .write(&get_str(helper).to_snek_case()) + .map_err(Into::into) +} + +fn reverse_domain( + helper: &Helper, + _: &Handlebars, + _: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + out + .write(&util::reverse_domain(get_str(helper))) + .map_err(Into::into) +} + +fn reverse_domain_snake_case( + helper: &Helper, + _: &Handlebars, + _: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + use heck::ToSnekCase as _; + out + .write(&util::reverse_domain(get_str(helper)).to_snek_case()) + .map_err(Into::into) +} + +fn app_root(ctx: &Context) -> Result<&str, RenderError> { + let app_root = ctx + .data() + .get("app") + .ok_or_else(|| RenderError::new("`app` missing from template data."))? + .get("root-dir") + .ok_or_else(|| RenderError::new("`app.root-dir` missing from template data."))?; + app_root + .as_str() + .ok_or_else(|| RenderError::new("`app.root-dir` contained invalid UTF-8.")) +} + +fn prefix_path( + helper: &Helper, + _: &Handlebars, + ctx: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + out + .write( + util::prefix_path(app_root(ctx)?, get_str(helper)) + .to_str() + .ok_or_else(|| { + RenderError::new( + "Either the `app.root-dir` or the specified path contained invalid UTF-8.", + ) + })?, + ) + .map_err(Into::into) +} + +fn unprefix_path( + helper: &Helper, + _: &Handlebars, + ctx: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + out + .write( + util::unprefix_path(app_root(ctx)?, get_str(helper)) + .map_err(|_| { + RenderError::new("Attempted to unprefix a path that wasn't in the app root dir.") + })? + .to_str() + .ok_or_else(|| { + RenderError::new( + "Either the `app.root-dir` or the specified path contained invalid UTF-8.", + ) + })?, + ) + .map_err(Into::into) +} diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs new file mode 100644 index 00000000000..0b10b2d648b --- /dev/null +++ b/tooling/cli/src/mobile/ios.rs @@ -0,0 +1,36 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use clap::{Parser, Subcommand}; + +use super::init::{command as init_command, Options as InitOptions, Target as InitTarget}; +use crate::Result; + +pub(crate) mod project; + +#[derive(Parser)] +#[clap( + author, + version, + about = "iOS commands", + subcommand_required(true), + arg_required_else_help(true) +)] +pub struct Cli { + #[clap(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + Init(InitOptions), +} + +pub fn command(cli: Cli) -> Result<()> { + match cli.command { + Commands::Init(options) => init_command(options, InitTarget::Ios)?, + } + + Ok(()) +} diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs new file mode 100644 index 00000000000..b743151ada6 --- /dev/null +++ b/tooling/cli/src/mobile/ios/project.rs @@ -0,0 +1,193 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use crate::helpers::template; +use cargo_mobile::{ + apple::{ + config::{Config, Metadata}, + deps, rust_version_check, + target::Target, + }, + opts, + target::TargetTrait as _, + util::{self, cli::TextWrapper, ln}, +}; +use handlebars::Handlebars; +use include_dir::{include_dir, Dir}; +use std::{ + ffi::OsString, + fs::{create_dir_all, File}, + path::{Component, PathBuf}, +}; + +const TEMPLATE_DIR: Dir<'_> = include_dir!("templates/mobile/ios"); + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Rustup(bossy::Error), + #[error(transparent)] + RustVersionCheck(util::RustVersionError), + #[error("failed to install Apple dependencies: {0}")] + DepsInstall(deps::Error), + #[error("failed to process template: {0}")] + TemplateProcessing(String), + #[error("failed to symlink asset directory")] + AssetDirSymlink, + #[error("failed to create directory at {path}: {cause}")] + DirectoryCreation { + path: PathBuf, + cause: std::io::Error, + }, + #[error("failed to run `xcodegen`: {0}")] + Xcodegen(bossy::Error), + #[error("failed to run `pod install`: {0}")] + PodInstall(bossy::Error), +} + +// unprefixed app_root seems pretty dangerous!! +// TODO: figure out what cargo-mobile meant by that +pub fn gen( + config: &Config, + metadata: &Metadata, + (handlebars, mut map): (Handlebars, template::JsonMap), + wrapper: &TextWrapper, + non_interactive: opts::NonInteractive, + skip_dev_tools: opts::SkipDevTools, + reinstall_deps: opts::ReinstallDeps, +) -> Result<(), Error> { + println!("Installing iOS toolchains..."); + Target::install_all().map_err(Error::Rustup)?; + rust_version_check(wrapper).map_err(Error::RustVersionCheck)?; + + deps::install_all(wrapper, non_interactive, skip_dev_tools, reinstall_deps) + .map_err(Error::DepsInstall)?; + + let dest = config.project_dir(); + let rel_prefix = util::relativize_path(config.app().root_dir(), &dest); + let source_dirs = vec![rel_prefix.join("src")]; + + let asset_catalogs = metadata.ios().asset_catalogs().unwrap_or_default(); + let ios_pods = metadata.ios().pods().unwrap_or_default(); + let macos_pods = metadata.macos().pods().unwrap_or_default(); + + let default_archs = [String::from("arm64"), String::from("x86_64")]; + + map.insert("file-groups", &source_dirs); + map.insert("ios-frameworks", metadata.ios().frameworks()); + map.insert( + "ios-valid-archs", + metadata.ios().valid_archs().unwrap_or(&default_archs), + ); + map.insert("ios-vendor-frameworks", metadata.ios().vendor_frameworks()); + map.insert("ios-vendor-sdks", metadata.ios().vendor_sdks()); + map.insert("macos-frameworks", metadata.macos().frameworks()); + map.insert( + "macos-vendor-frameworks", + metadata.macos().vendor_frameworks(), + ); + map.insert("macos-vendor-sdks", metadata.macos().vendor_frameworks()); + map.insert("asset-catalogs", asset_catalogs); + map.insert("ios-pods", ios_pods); + map.insert("macos-pods", macos_pods); + map.insert( + "ios-additional-targets", + metadata.ios().additional_targets(), + ); + map.insert( + "macos-additional-targets", + metadata.macos().additional_targets(), + ); + map.insert("ios-pre-build-scripts", metadata.ios().pre_build_scripts()); + map.insert( + "ios-post-compile-scripts", + metadata.ios().post_compile_scripts(), + ); + map.insert( + "ios-post-build-scripts", + metadata.ios().post_build_scripts(), + ); + map.insert( + "macos-pre-build-scripts", + metadata.macos().pre_build_scripts(), + ); + map.insert( + "macos-post-compile-scripts", + metadata.macos().post_compile_scripts(), + ); + map.insert( + "macos-post-build-scripts", + metadata.macos().post_build_scripts(), + ); + map.insert( + "ios-command-line-arguments", + metadata.ios().command_line_arguments(), + ); + map.insert( + "macos-command-line-arguments", + metadata.macos().command_line_arguments(), + ); + + let mut created_dirs = Vec::new(); + template::render_with_generator( + &handlebars, + map.inner(), + &TEMPLATE_DIR, + &dest, + &mut |path| { + let mut components: Vec<_> = path.components().collect(); + let mut new_component = None; + for component in &mut components { + if let Component::Normal(c) = component { + let c = c.to_string_lossy(); + if c.contains("{{app.name}}") { + new_component.replace(OsString::from( + &c.replace("{{app.name}}", config.app().name()), + )); + *component = Component::Normal(new_component.as_ref().unwrap()); + break; + } + } + } + let path = dest.join(components.iter().collect::()); + + let parent = path.parent().unwrap().to_path_buf(); + if !created_dirs.contains(&parent) { + create_dir_all(&parent)?; + created_dirs.push(parent); + } + + File::create(path) + }, + ) + .map_err(|e| Error::TemplateProcessing(e.to_string()))?; + + ln::force_symlink_relative(config.app().asset_dir(), &dest, ln::TargetStyle::Directory) + .map_err(|_| Error::AssetDirSymlink)?; + + // Create all asset catalog directories if they don't already exist + for dir in asset_catalogs { + std::fs::create_dir_all(dir).map_err(|cause| Error::DirectoryCreation { + path: dest.clone(), + cause, + })?; + } + + // Note that Xcode doesn't always reload the project nicely; reopening is + // often necessary. + println!("Generating Xcode project..."); + bossy::Command::impure("xcodegen") + .with_args(&["generate", "--spec"]) + .with_arg(dest.join("project.yml")) + .run_and_wait() + .map_err(Error::Xcodegen)?; + + if !ios_pods.is_empty() || !macos_pods.is_empty() { + bossy::Command::impure_parse("pod install") + .with_arg(format!("--project-directory={}", dest.display())) + .run_and_wait() + .map_err(Error::PodInstall)?; + } + Ok(()) +} diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs new file mode 100644 index 00000000000..83baa249cff --- /dev/null +++ b/tooling/cli/src/mobile/mod.rs @@ -0,0 +1,8 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +pub mod android; +mod init; +#[cfg(target_os = "macos")] +pub mod ios; diff --git a/tooling/cli/templates/mobile/android/.editorconfig b/tooling/cli/templates/mobile/android/.editorconfig new file mode 100644 index 00000000000..ebe51d3bfa4 --- /dev/null +++ b/tooling/cli/templates/mobile/android/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/.gitignore b/tooling/cli/templates/mobile/android/.gitignore new file mode 100644 index 00000000000..aa724b77071 --- /dev/null +++ b/tooling/cli/templates/mobile/android/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/tooling/cli/templates/mobile/android/app/.gitignore b/tooling/cli/templates/mobile/android/app/.gitignore new file mode 100644 index 00000000000..42afabfd2ab --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/build.gradle.kts b/tooling/cli/templates/mobile/android/app/build.gradle.kts new file mode 100644 index 00000000000..13906b47c2d --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/build.gradle.kts @@ -0,0 +1,85 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("rustPlugin") + {{~#each android-app-plugins}} + id("{{this}}"){{/each}} +} + +android { + compileSdk = 31 + defaultConfig { + applicationId = "{{reverse-domain app.domain}}.{{snake-case app.name}}" + minSdk = {{android.min-sdk-version}} + targetSdk = 31 + versionCode = 1 + versionName = "1.0" + } + sourceSets.getByName("main") { + {{#if android.vulkan-validation}}// Vulkan validation layers + val ndkHome = System.getenv("NDK_HOME") + jniLibs.srcDir("${ndkHome}/sources/third_party/vulkan/src/build-android/jniLibs") + {{/if}} + } + buildTypes { + getByName("debug") { + isDebuggable = true + isJniDebuggable = true + isMinifyEnabled = false + packagingOptions { + {{~#each targets}} + + jniLibs.keepDebugSymbols.add("*/{{this.abi}}/*.so") + {{/each}} + } + } + getByName("release") { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") + } + } + flavorDimensions.add("abi") + productFlavors { + {{~#each targets}} + + create("{{this.arch}}") { + dimension = "abi" + ndk { + abiFilters += listOf("{{this.abi}}") + } + } + {{/each}} + } + + assetPacks += mutableSetOf({{quote-and-join-colon-prefix asset-packs}}) +} + +rust { + rootDirRel = "{{root-dir-rel}}" + targets = listOf({{quote-and-join target-names}}) + arches = listOf({{quote-and-join arches}}) +} + +dependencies { + {{~#each android-app-dependencies-platform}} + implementation(platform("{{this}}")){{/each}} + {{~#each android-app-dependencies}} + implementation("{{this}}"){{/each}} + implementation("androidx.webkit:webkit:1.4.0") + implementation("androidx.appcompat:appcompat:1.4.1") + implementation("com.google.android.material:material:1.6.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.3") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.3") + androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") +} + +afterEvaluate { + android.applicationVariants.all { + val buildType = "${buildType.name.capitalize()}" + productFlavors.forEach { + val archAndBuildType = name.capitalize() + tasks["merge${archAndBuildType}JniLibFolders"].dependsOn(tasks["rustBuild${archAndBuildType}"]) + } + } +} diff --git a/tooling/cli/templates/mobile/android/app/proguard-rules.pro b/tooling/cli/templates/mobile/android/app/proguard-rules.pro new file mode 100644 index 00000000000..481bb434814 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml b/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..cf42dcd2cb4 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/tooling/cli/templates/mobile/android/app/src/main/Ipc.kt b/tooling/cli/templates/mobile/android/app/src/main/Ipc.kt new file mode 100644 index 00000000000..f2bb26f1575 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/Ipc.kt @@ -0,0 +1,18 @@ +package {{reverse-domain app.domain}}.{{snake-case app.name}} + +import android.webkit.* + +class Ipc { + @JavascriptInterface + fun postMessage(message: String) { + this.ipc(message) + } + + companion object { + init { + System.loadLibrary("{{snake-case app.name}}") + } + } + + private external fun ipc(message: String) +} diff --git a/tooling/cli/templates/mobile/android/app/src/main/MainActivity.kt b/tooling/cli/templates/mobile/android/app/src/main/MainActivity.kt new file mode 100644 index 00000000000..187d1996cab --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/MainActivity.kt @@ -0,0 +1,3 @@ +package {{reverse-domain app.domain}}.{{snake-case app.name}} + +class MainActivity : TauriActivity() {} diff --git a/tooling/cli/templates/mobile/android/app/src/main/RustWebChromeClient.kt b/tooling/cli/templates/mobile/android/app/src/main/RustWebChromeClient.kt new file mode 100644 index 00000000000..d810e18a910 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/RustWebChromeClient.kt @@ -0,0 +1,26 @@ +package {{reverse-domain app.domain}}.{{snake-case app.name}} + +import android.webkit.* + +class RustWebChromeClient: WebChromeClient() { + private var loadedUrl: String? = null + + override fun onProgressChanged(view: WebView, progress: Int) { + var url = view.url ?: "" + if (url.endsWith("##")) { + url = url.dropLast(2) + } + if (loadedUrl != url) { + loadedUrl = url + runInitializationScripts() + } + } + + companion object { + init { + System.loadLibrary("{{snake-case app.name}}") + } + } + + private external fun runInitializationScripts() +} diff --git a/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt b/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt new file mode 100644 index 00000000000..c6c65fa951e --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt @@ -0,0 +1,24 @@ +package {{reverse-domain app.domain}}.{{snake-case app.name}} + +import android.webkit.* + +class RustWebViewClient: WebViewClient() { + override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { + return false + } + + override fun shouldInterceptRequest( + view: WebView, + request: WebResourceRequest + ): WebResourceResponse? { + return handleRequest(request) + } + + companion object { + init { + System.loadLibrary("{{snake-case app.name}}") + } + } + + private external fun handleRequest(request: WebResourceRequest): WebResourceResponse? +} diff --git a/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt b/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt new file mode 100644 index 00000000000..84b13f949a8 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt @@ -0,0 +1,71 @@ +package {{reverse-domain app.domain}}.{{snake-case app.name}} + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity + +abstract class TauriActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + create(this) + } + + override fun onStart() { + super.onStart() + start() + } + + override fun onResume() { + super.onResume() + resume() + } + + override fun onPause() { + super.onPause() + pause() + } + + override fun onStop() { + super.onStop() + stop() + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + focus(hasFocus) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + save() + } + + override fun onDestroy() { + super.onDestroy() + destroy() + } + + override fun onLowMemory() { + super.onLowMemory() + memory() + } + + fun getAppClass(name: String): Class<*> { + return Class.forName(name) + } + + companion object { + init { + System.loadLibrary("{{snake-case app.name}}") + } + } + + private external fun create(activity: TauriActivity) + private external fun start() + private external fun resume() + private external fun pause() + private external fun stop() + private external fun save() + private external fun destroy() + private external fun memory() + private external fun focus(focus: Boolean) +} diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/tooling/cli/templates/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000000..2b068d11462 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml b/tooling/cli/templates/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000000..07d5da9cbf1 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/layout/activity_main.xml b/tooling/cli/templates/mobile/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000000..4fc244418b5 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000000..eca70cfe52e --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000000..eca70cfe52e --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/values-night/themes.xml b/tooling/cli/templates/mobile/android/app/src/main/res/values-night/themes.xml new file mode 100644 index 00000000000..73c60177cdb --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/values/colors.xml b/tooling/cli/templates/mobile/android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000000..f8c6127d327 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/values/strings.xml b/tooling/cli/templates/mobile/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000000..d7c29c74f3e --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + qoo + \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/values/themes.xml b/tooling/cli/templates/mobile/android/app/src/main/res/values/themes.xml new file mode 100644 index 00000000000..16a25905247 --- /dev/null +++ b/tooling/cli/templates/mobile/android/app/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + diff --git a/tooling/cli/templates/mobile/android/build.gradle.kts b/tooling/cli/templates/mobile/android/build.gradle.kts new file mode 100644 index 00000000000..f45e9c35ae5 --- /dev/null +++ b/tooling/cli/templates/mobile/android/build.gradle.kts @@ -0,0 +1,27 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:7.0.2") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") + {{~#each android-project-dependencies}} + classpath("{{this}}"){{/each}} + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +tasks.register("clean").configure { + delete("build") +} + diff --git a/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts b/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts new file mode 100644 index 00000000000..b0d0d4e6568 --- /dev/null +++ b/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + `kotlin-dsl` +} + +kotlinDslPluginOptions { + experimentalWarning.set(false) +} + +gradlePlugin { + plugins { + create("pluginsForCoolKids") { + id = "rustPlugin" + implementationClass = "{{reverse-domain app.domain}}.RustPlugin" + } + } +} + +repositories { + google() + mavenCentral() +} + +dependencies { + compileOnly(gradleApi()) + implementation("com.android.tools.build:gradle:7.0.2") +} + diff --git a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt new file mode 100644 index 00000000000..454531d0a9d --- /dev/null +++ b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt @@ -0,0 +1,54 @@ +package {{reverse-domain app.domain}} + +import com.android.build.gradle.* +import java.io.File +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.api.logging.LogLevel +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction + +open class BuildTask : DefaultTask() { + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + var rootDirRel: File? = null + @Input + var target: String? = null + @Input + var release: Boolean? = null + + @TaskAction + fun build() { + val rootDirRel = rootDirRel + if (rootDirRel == null) { + throw GradleException("rootDirRel cannot be null") + } + val target = target + if (target == null) { + throw GradleException("target cannot be null") + } + val release = release + if (release == null) { + throw GradleException("release cannot be null") + } + project.exec { + workingDir(File(project.getProjectDir(), rootDirRel.getPath())) + executable("cargo") + args(listOf("android", "build")) + if (project.logger.isEnabled(LogLevel.DEBUG)) { + args("-vv") + } else if (project.logger.isEnabled(LogLevel.INFO)) { + args("-v") + } + if (release) { + args("--release") + } + args("${target}") + }.assertNormalExitValue() + } +} + diff --git a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt new file mode 100644 index 00000000000..86b86cd4fa8 --- /dev/null +++ b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt @@ -0,0 +1,51 @@ +package {{reverse-domain app.domain}} + +import com.android.build.gradle.* +import java.io.File +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project + +const val TASK_GROUP = "rust" + +open class Config { + var rootDirRel: String? = null + var targets: List? = null + var arches: List? = null +} + +open class RustPlugin : Plugin { + internal lateinit var config: Config + + override fun apply(project: Project) { + config = project.extensions.create("rust", Config::class.java) + project.afterEvaluate { + if (config.targets == null) { + throw GradleException("targets cannot be null") + } + if (config.arches == null) { + throw GradleException("arches cannot be null") + } + for (profile in listOf("debug", "release")) { + val buildTask = project.tasks.maybeCreate("rustBuild${profile.capitalize()}", DefaultTask::class.java).apply { + group = TASK_GROUP + description = "Build dynamic library in ${profile} mode for all targets" + } + for (targetPair in config.targets!!.withIndex()) { + val targetName = targetPair.value + val targetArch = config.arches!![targetPair.index] + val targetBuildTask = project.tasks.maybeCreate("rustBuild${targetArch.capitalize()}${profile.capitalize()}", BuildTask::class.java).apply { + group = TASK_GROUP + description = "Build dynamic library in ${profile} mode for $targetArch" + rootDirRel = File(config.rootDirRel) + target = targetName + release = profile == "release" + } + buildTask.dependsOn(targetBuildTask) + project.tasks.findByName("preBuild")?.mustRunAfter(targetBuildTask) + } + } + } + } +} diff --git a/tooling/cli/templates/mobile/android/gradle.properties b/tooling/cli/templates/mobile/android/gradle.properties new file mode 100644 index 00000000000..cd0519bb2a9 --- /dev/null +++ b/tooling/cli/templates/mobile/android/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.jar b/tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties b/tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..543ef62a1a7 --- /dev/null +++ b/tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue May 10 19:22:52 CST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/tooling/cli/templates/mobile/android/gradlew b/tooling/cli/templates/mobile/android/gradlew new file mode 100755 index 00000000000..4f906e0c811 --- /dev/null +++ b/tooling/cli/templates/mobile/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/tooling/cli/templates/mobile/android/gradlew.bat b/tooling/cli/templates/mobile/android/gradlew.bat new file mode 100644 index 00000000000..ac1b06f9382 --- /dev/null +++ b/tooling/cli/templates/mobile/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/tooling/cli/templates/mobile/android/settings.gradle b/tooling/cli/templates/mobile/android/settings.gradle new file mode 100644 index 00000000000..74680c9c1bd --- /dev/null +++ b/tooling/cli/templates/mobile/android/settings.gradle @@ -0,0 +1,3 @@ +include ':app' +{{~#each asset-packs}} +include ':{{this}}'{{/each}} diff --git a/tooling/cli/templates/mobile/ios/.gitignore b/tooling/cli/templates/mobile/ios/.gitignore new file mode 100644 index 00000000000..6aecd276af5 --- /dev/null +++ b/tooling/cli/templates/mobile/ios/.gitignore @@ -0,0 +1,2 @@ +xcuserdata/ +build/ diff --git a/tooling/cli/templates/mobile/ios/ExportOptions.plist b/tooling/cli/templates/mobile/ios/ExportOptions.plist new file mode 100644 index 00000000000..b69cf1de8f5 --- /dev/null +++ b/tooling/cli/templates/mobile/ios/ExportOptions.plist @@ -0,0 +1,8 @@ + + + + + method + development + + diff --git a/tooling/cli/templates/mobile/ios/Podfile b/tooling/cli/templates/mobile/ios/Podfile new file mode 100644 index 00000000000..0c0df4e6462 --- /dev/null +++ b/tooling/cli/templates/mobile/ios/Podfile @@ -0,0 +1,25 @@ +# Uncomment the next line to define a global platform for your project + +target '{{app.name}}_iOS' do +platform :ios, '{{apple.ios-version}}' + # Pods for {{app.name}}_iOS + {{~#each ios-pods}} + pod '{{this.name}}'{{#if this.version}}, '{{this.version}}'{{/if}}{{/each}} +end + +target '{{app.name}}_macOS' do +platform :osx, '{{apple.macos-version}}' + # Pods for {{app.name}}_macOS + {{~#each macos-pods}} + pod '{{this.name}}'{{#if this.version}}, '{{this.version}}'{{/if}}{{/each}} +end + +# Delete the deployment target for iOS and macOS, causing it to be inherited from the Podfile +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' + config.build_settings.delete 'MACOSX_DEPLOYMENT_TARGET' + end + end +end diff --git a/tooling/cli/templates/mobile/ios/Sources/{{app.name}}/bindings/bindings.h b/tooling/cli/templates/mobile/ios/Sources/{{app.name}}/bindings/bindings.h new file mode 100644 index 00000000000..51522007b6a --- /dev/null +++ b/tooling/cli/templates/mobile/ios/Sources/{{app.name}}/bindings/bindings.h @@ -0,0 +1,8 @@ +#pragma once + +namespace ffi { + extern "C" { + void start_app(); + } +} + diff --git a/tooling/cli/templates/mobile/ios/Sources/{{app.name}}/main.mm b/tooling/cli/templates/mobile/ios/Sources/{{app.name}}/main.mm new file mode 100644 index 00000000000..7793a9d5cfa --- /dev/null +++ b/tooling/cli/templates/mobile/ios/Sources/{{app.name}}/main.mm @@ -0,0 +1,6 @@ +#include "bindings/bindings.h" + +int main(int argc, char * argv[]) { + ffi::start_app(); + return 0; +} diff --git a/tooling/cli/templates/mobile/ios/project.yml b/tooling/cli/templates/mobile/ios/project.yml new file mode 100644 index 00000000000..b4e3a5ddacf --- /dev/null +++ b/tooling/cli/templates/mobile/ios/project.yml @@ -0,0 +1,280 @@ +name: {{app.name}} +options: + bundleIdPrefix: {{reverse-domain app.domain}} + deploymentTarget: + iOS: {{apple.ios-version}} + macOS: {{apple.macos-version}} +fileGroups: [{{join file-groups}}] +configs: + debug: debug + release: release +settingGroups: + app: + base: + PRODUCT_NAME: {{app.name}} + PRODUCT_BUNDLE_IDENTIFIER: {{reverse-domain app.domain}}.{{app.name}} + DEVELOPMENT_TEAM: {{apple.development-team}} +targetTemplates: + app: + type: application + sources: + - path: Sources + scheme: + environmentVariables: + RUST_BACKTRACE: full + RUST_LOG: info + settings: + groups: [app] +targets: + {{app.name}}_iOS: + type: application + platform: iOS + sources: + - path: Sources + - path: {{app.asset-dir}} + buildPhase: resources + type: folder + {{~#each asset-catalogs}} + - {{prefix-path this}}{{/each}} + {{~#each ios-additional-targets}} + - path: {{prefix-path this}}{{/each}} + info: + path: {{app.name}}_iOS/Info.plist + properties: + LSRequiresIPhoneOS: true + UILaunchStoryboardName: LaunchScreen + UIRequiredDeviceCapabilities: [arm64, metal] + UISupportedInterfaceOrientations: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + UISupportedInterfaceOrientations~ipad: + - UIInterfaceOrientationPortrait + - UIInterfaceOrientationPortraitUpsideDown + - UIInterfaceOrientationLandscapeLeft + - UIInterfaceOrientationLandscapeRight + CFBundleShortVersionString: {{apple.bundle-version-short}} + CFBundleVersion: {{apple.bundle-version}} + {{~#each apple.plist-pairs}} + {{this.key}}: {{this.value}}{{/each}} + scheme: + environmentVariables: + RUST_BACKTRACE: full + RUST_LOG: info + {{~#if ios-command-line-arguments}} + commandLineArguments: + {{~#each ios-command-line-arguments}} + "{{this}}": true + {{/each}}{{~/if}} + settings: + base: + ENABLE_BITCODE: false + ARCHS: [{{join ios-valid-archs}}] + VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} + LIBRARY_SEARCH_PATHS[sdk=iphoneos*]: $(inherited) "{{prefix-path "target/aarch64-apple-ios/$(CONFIGURATION)"}}" + LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]: $(inherited) "{{prefix-path "target/x86_64-apple-ios/$(CONFIGURATION)"}}" + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true + groups: [app] + dependencies: + - target: lib_{{app.name}}_iOS + embed: false + link: false + - framework: lib{{snake-case app.name}}.a + embed: false + {{~#each ios-vendor-frameworks}} + - framework: {{prefix-path this}}{{/each}} + {{~#each ios-vendor-sdks}} + - sdk: {{prefix-path this}}{{/each}} + - sdk: CoreGraphics.framework + - sdk: Metal.framework + - sdk: MetalKit.framework + - sdk: QuartzCore.framework + - sdk: Security.framework + - sdk: UIKit.framework + {{~#each ios-frameworks}} + - sdk: {{this}}.framework{{/each}} + - sdk: WebKit.framework + {{~#if ios-pre-build-scripts}} + preBuildScripts: + {{~#each ios-pre-build-scripts}}{{#if this.path}} + - path {{this.path}}{{/if}}{{#if this.script}} + - script: {{this.script}}{{/if}}{{#if this.name}} + name: {{this.name}}{{/if}}{{#if this.input-files}} + inputFiles: {{~#each this.input-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-files}} + outputFiles: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} + inputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} + outputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.shell}} + shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} + showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} + runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} + basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} + discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} + {{~/each~}} + {{~/if~}} + {{~#if ios-post-compile-scripts}} + postCompileScripts: + {{~#each ios-post-compile-scripts}}{{#if this.path}} + - path {{this.path}}{{/if}}{{#if this.script}} + - script: {{this.script}}{{/if}}{{#if this.name}} + name: {{this.name}}{{/if}}{{#if this.input-files}} + inputFiles: {{~#each this.input-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-files}} + outputFiles: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} + inputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} + outputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.shell}} + shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} + showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} + runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} + basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} + discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} + {{~/each~}} + {{~/if~}} + {{~#if ios-post-build-scripts}} + postBuildScripts: + {{~#each ios-post-build-scripts}}{{#if this.path}} + - path {{this.path}}{{/if}}{{#if this.script}} + - script: {{this.script}}{{/if}}{{#if this.name}} + name: {{this.name}}{{/if}}{{#if this.input-files}} + inputFiles: {{~#each this.input-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-files}} + outputFiles: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} + inputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} + outputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.shell}} + shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} + showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} + runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} + basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} + discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} + {{~/each~}} + {{~/if}} + + {{app.name}}_macOS: + type: application + platform: macOS + sources: Sources + {{~#each macos-additional-targets}} + - path: {{prefix-path this}}{{/each}} + info: + path: {{app.name}}_macOS/Info.plist + properties: + NSHighResolutionCapable: true + scheme: + environmentVariables: + RUST_BACKTRACE: full + RUST_LOG: info + {{~#if ios-command-line-arguments}} + commandLineArguments: + {{~#each ios-command-line-arguments}} + "{{this}}": true + {{/each}}{{~/if}} + settings: + base: + LIBRARY_SEARCH_PATHS: $(inherited) "{{prefix-path "target/x86_64-apple-darwin/$(CONFIGURATION)"}}" + groups: [app] + dependencies: + - target: lib_{{app.name}}_macOS + embed: false + link: false + - framework: lib{{snake-case app.name}}.a + embed: false + {{~#each macos-vendor-frameworks}} + - framework: {{prefix-path this}}{{/each}} + {{~#each macos-vendor-sdks}} + - sdk: {{prefix-path this}}{{/each}} + - sdk: Metal.framework + {{~#each macos-frameworks}} + - sdk: {{this}}.framework{{/each}} + {{~#if macos-pre-build-scripts}} + preBuildScripts: + {{~#each macos-pre-build-scripts}}{{#if this.path}} + - path {{this.path}}{{/if}}{{#if this.script}} + - script: {{this.script}}{{/if}}{{#if this.name}} + name: {{this.name}}{{/if}}{{#if this.input-files}} + inputFiles: {{~#each this.input-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-files}} + outputFiles: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} + inputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} + outputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.shell}} + shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} + showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} + runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} + basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} + discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} + {{~/each~}} + {{~/if~}} + {{#if macos-post-compile-scripts}} + postCompileScripts: + {{~#each macos-post-compile-scripts}}{{#if this.path}} + - path {{this.path}}{{/if}}{{#if this.script}} + - script: {{this.script}}{{/if}}{{#if this.name}} + name: {{this.name}}{{/if}}{{#if this.input-files}} + inputFiles: {{~#each this.input-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-files}} + outputFiles: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} + inputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} + outputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.shell}} + shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} + showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} + runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} + basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} + discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} + {{~/each~}} + {{~/if~}} + {{#if macos-post-build-scripts}} + postBuildScripts: + {{~#each macos-post-build-scripts}}{{#if this.path}} + - path {{this.path}}{{/if}}{{#if this.script}} + - script: {{this.script}}{{/if}}{{#if this.name}} + name: {{this.name}}{{/if}}{{#if this.input-files}} + inputFiles: {{~#each this.input-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-files}} + outputFiles: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} + inputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} + outputFileLists: {{~#each this.output-files}} + - {{this}}{{/each}}{{/if}}{{#if this.shell}} + shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} + showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} + runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} + basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} + discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} + {{~/each~}} + {{~/if}} + + lib_{{app.name}}_iOS: + type: "" + platform: iOS + settings: + ENABLE_BITCODE: false + ARCHS: [{{join ios-valid-archs}}] + VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} + legacy: + toolPath: ${HOME}/.cargo/bin/cargo-apple + arguments: xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + passSettings: false # prevents evil linker errors + workingDirectory: $(SRCROOT)/.. + lib_{{app.name}}_macOS: + type: "" + platform: macOS + legacy: + toolPath: ${HOME}/.cargo/bin/cargo-apple + arguments: xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + passSettings: false + workingDirectory: $(SRCROOT)/.. diff --git a/tooling/cli/templates/mobile/ios/{{app.name}}.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/tooling/cli/templates/mobile/ios/{{app.name}}.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000000..0225e897316 --- /dev/null +++ b/tooling/cli/templates/mobile/ios/{{app.name}}.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,12 @@ + + + + +{{#if apple.use-legacy-build-system}} + BuildSystemType + Original + DisableBuildSystemDeprecationDiagnostic + +{{/if}} + + From a9f8ac7f965c6d5920ca9b0b5791944c9e798c63 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 15 Aug 2022 13:24:26 -0300 Subject: [PATCH 006/423] fix(examples): set API lib crate-type --- examples/api/src-tauri/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index fd20408f238..c4d4d321aef 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -6,6 +6,9 @@ edition = "2021" rust-version = "1.57" license = "Apache-2.0 OR MIT" +[lib] +crate-type = ["staticlib", "cdylib", "rlib"] + [build-dependencies] tauri-build = { path = "../../../core/tauri-build", features = ["codegen", "isolation"] } From a9c8e565c6495961940877df7090f307be16b554 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 15 Aug 2022 13:38:17 -0300 Subject: [PATCH 007/423] feat: add `android open` and `ios open` commands (#4946) --- .changes/mobile-open.md | 6 ++++ tooling/cli/Cargo.lock | 2 +- tooling/cli/Cargo.toml | 2 +- tooling/cli/src/mobile/android.rs | 54 +++++++++++++++++++++++++++++-- tooling/cli/src/mobile/init.rs | 8 +---- tooling/cli/src/mobile/ios.rs | 54 +++++++++++++++++++++++++++++-- tooling/cli/src/mobile/mod.rs | 41 +++++++++++++++++++++++ 7 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 .changes/mobile-open.md diff --git a/.changes/mobile-open.md b/.changes/mobile-open.md new file mode 100644 index 00000000000..d504ac24a96 --- /dev/null +++ b/.changes/mobile-open.md @@ -0,0 +1,6 @@ +--- +"cli.rs": minor +"cli.js": minor +--- + +Added `android open` and `ios open` commands. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 7dc82570871..0945082a6e2 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -272,7 +272,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=feat/library#d2b3f7248657c60dcc0669303329b802f04a6291" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=feat/library#c1365b7e90d341d67cf40f4d2f5532e932631907" dependencies = [ "bicycle", "bossy", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 8fd0c9263af..777d4d53f09 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -30,7 +30,7 @@ path = "src/main.rs" bossy = { git = "https://github.com/lucasfernog/bossy", branch = "fix/winapi-features" } [dependencies] -# cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } +#cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "feat/library", default-features = false } bossy = "0.2" textwrap = { version = "0.11.0", features = ["term_size"] } diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index b3b3a243548..59dd3eba15a 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -2,13 +2,37 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use cargo_mobile::{ + android::config::{Config as AndroidConfig, Metadata as AndroidMetadata}, + config::{metadata::Metadata, Config}, + os, + util::cli::TextWrapper, +}; use clap::{Parser, Subcommand}; -use super::init::{command as init_command, Options as InitOptions, Target as InitTarget}; +use super::{ + ensure_init, + init::{command as init_command, Options as InitOptions}, + Target, +}; use crate::Result; pub(crate) mod project; +#[derive(Debug, thiserror::Error)] +enum Error { + #[error("{0}")] + ProjectNotInitialized(String), + #[error(transparent)] + ConfigFailed(cargo_mobile::config::LoadOrGenError), + #[error(transparent)] + MetadataFailed(cargo_mobile::config::metadata::Error), + #[error("Android is marked as unsupported in your configuration file")] + Unsupported, + #[error(transparent)] + OpenFailed(os::OpenFileError), +} + #[derive(Parser)] #[clap( author, @@ -25,12 +49,38 @@ pub struct Cli { #[derive(Subcommand)] enum Commands { Init(InitOptions), + Open, } pub fn command(cli: Cli) -> Result<()> { match cli.command { - Commands::Init(options) => init_command(options, InitTarget::Android)?, + Commands::Init(options) => init_command(options, Target::Android)?, + Commands::Open => open()?, + } + + Ok(()) +} + +fn with_config( + wrapper: &TextWrapper, + f: impl FnOnce(&AndroidConfig, &AndroidMetadata) -> Result<(), Error>, +) -> Result<(), Error> { + let (config, _origin) = + Config::load_or_gen(".", true.into(), wrapper).map_err(Error::ConfigFailed)?; + let metadata = Metadata::load(config.app().root_dir()).map_err(Error::MetadataFailed)?; + if metadata.android().supported() { + f(config.android(), metadata.android()) + } else { + Err(Error::Unsupported) } +} +fn open() -> Result<()> { + let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); + with_config(&wrapper, |config, _metadata| { + ensure_init(config.project_dir(), Target::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) + })?; Ok(()) } diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 1089eff7130..1fdd9e67b11 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use super::Target; use crate::helpers::{app_paths::tauri_dir, template::JsonMap}; use crate::Result; use cargo_mobile::{ @@ -89,13 +90,6 @@ pub enum Error { OpenInEditor(util::OpenInEditorError), } -#[derive(PartialEq, Eq)] -pub enum Target { - Android, - #[cfg(target_os = "macos")] - Ios, -} - pub fn exec( target: Target, wrapper: &TextWrapper, diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 0b10b2d648b..ff781d302d7 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -2,13 +2,37 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use cargo_mobile::{ + apple::config::{Config as AppleConfig, Metadata as AppleMetadata}, + config::{metadata::Metadata, Config}, + os, + util::cli::TextWrapper, +}; use clap::{Parser, Subcommand}; -use super::init::{command as init_command, Options as InitOptions, Target as InitTarget}; +use super::{ + ensure_init, + init::{command as init_command, Options as InitOptions}, + Target, +}; use crate::Result; pub(crate) mod project; +#[derive(Debug, thiserror::Error)] +enum Error { + #[error("{0}")] + ProjectNotInitialized(String), + #[error(transparent)] + ConfigFailed(cargo_mobile::config::LoadOrGenError), + #[error(transparent)] + MetadataFailed(cargo_mobile::config::metadata::Error), + #[error("iOS is marked as unsupported in your configuration file")] + Unsupported, + #[error(transparent)] + OpenFailed(os::OpenFileError), +} + #[derive(Parser)] #[clap( author, @@ -25,12 +49,38 @@ pub struct Cli { #[derive(Subcommand)] enum Commands { Init(InitOptions), + Open, } pub fn command(cli: Cli) -> Result<()> { match cli.command { - Commands::Init(options) => init_command(options, InitTarget::Ios)?, + Commands::Init(options) => init_command(options, Target::Ios)?, + Commands::Open => open()?, + } + + Ok(()) +} + +fn with_config( + wrapper: &TextWrapper, + f: impl FnOnce(&AppleConfig, &AppleMetadata) -> Result<(), Error>, +) -> Result<(), Error> { + let (config, _origin) = + Config::load_or_gen(".", true.into(), wrapper).map_err(Error::ConfigFailed)?; + let metadata = Metadata::load(config.app().root_dir()).map_err(Error::MetadataFailed)?; + if metadata.apple().supported() { + f(config.apple(), metadata.apple()) + } else { + Err(Error::Unsupported) } +} +fn open() -> Result<()> { + let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); + with_config(&wrapper, |config, _metadata| { + ensure_init(config.project_dir(), Target::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) + })?; Ok(()) } diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 83baa249cff..16ea4040a75 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -2,7 +2,48 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use anyhow::{bail, Result}; +use std::path::PathBuf; + pub mod android; mod init; #[cfg(target_os = "macos")] pub mod ios; + +#[derive(PartialEq, Eq)] +pub enum Target { + Android, + #[cfg(target_os = "macos")] + Ios, +} + +impl Target { + fn ide_name(&self) -> &'static str { + match self { + Self::Android => "Android Studio", + #[cfg(target_os = "macos")] + Self::Ios => "Xcode", + } + } + + fn command_name(&self) -> &'static str { + match self { + Self::Android => "android", + #[cfg(target_os = "macos")] + Self::Ios => "ios", + } + } +} + +fn ensure_init(project_dir: PathBuf, target: Target) -> Result<()> { + if !project_dir.exists() { + bail!( + "{} project directory {} doesn't exist. Please run `tauri {} init` and try again.", + target.ide_name(), + project_dir.display(), + target.command_name(), + ) + } else { + Ok(()) + } +} From 3f655d62808520d06e24f875a84e96f1872f7186 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Tue, 16 Aug 2022 09:44:55 -0300 Subject: [PATCH 008/423] refactor: pull mobile config from tauri config instead of mobile.toml (#4948) --- core/tauri-utils/src/config.rs | 20 +- core/tauri/src/test/mod.rs | 1 + examples/api/src-tauri/mobile.toml | 8 - tooling/cli/Cargo.lock | 2 +- tooling/cli/Cargo.toml | 4 +- tooling/cli/schema.json | 24 +++ tooling/cli/src/mobile/android.rs | 143 ++++++++++--- tooling/cli/src/mobile/android/project.rs | 6 +- tooling/cli/src/mobile/init.rs | 105 ++++------ tooling/cli/src/mobile/ios.rs | 192 +++++++++++++++--- tooling/cli/src/mobile/mod.rs | 89 ++++++++ .../buildSrc/src/main/kotlin/BuildTask.kt | 6 +- tooling/cli/templates/mobile/ios/project.yml | 12 +- 13 files changed, 476 insertions(+), 136 deletions(-) delete mode 100644 examples/api/src-tauri/mobile.toml diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 7810d376b18..5e6a7209111 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -2085,6 +2085,9 @@ pub struct TauriConfig { /// MacOS private API configuration. Enables the transparent background API and sets the `fullScreenEnabled` preference to `true`. #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)] pub macos_private_api: bool, + /// iOS configuration. + #[serde(rename = "iOS", default)] + pub ios: IosConfig, } impl TauriConfig { @@ -2354,6 +2357,18 @@ fn default_dialog() -> bool { true } +/// General configuration for the iOS target. +#[skip_serializing_none] +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct IosConfig { + /// The development team. This value is required for iOS development because code signing is enforced. + /// The `APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it. + #[serde(alias = "development-team")] + pub development_team: Option, +} + /// Defines the URL or assets to embed in the application. #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] #[cfg_attr(feature = "schema", derive(JsonSchema))] @@ -3443,6 +3458,7 @@ mod build { let system_tray = opt_lit(self.system_tray.as_ref()); let allowlist = &self.allowlist; let macos_private_api = self.macos_private_api; + let ios = quote!(Default::default()); literal_struct!( tokens, @@ -3455,7 +3471,8 @@ mod build { security, system_tray, allowlist, - macos_private_api + macos_private_api, + ios ); } } @@ -3553,6 +3570,7 @@ mod test { allowlist: AllowlistConfig::default(), system_tray: None, macos_private_api: false, + ios: Default::default(), }; // create a build config diff --git a/core/tauri/src/test/mod.rs b/core/tauri/src/test/mod.rs index bf0bb33e074..9f4357b36ef 100644 --- a/core/tauri/src/test/mod.rs +++ b/core/tauri/src/test/mod.rs @@ -61,6 +61,7 @@ pub fn mock_context(assets: A) -> crate::Context { updater: Default::default(), system_tray: None, macos_private_api: false, + ios: Default::default(), }, build: Default::default(), plugins: Default::default(), diff --git a/examples/api/src-tauri/mobile.toml b/examples/api/src-tauri/mobile.toml deleted file mode 100644 index 998b18530dd..00000000000 --- a/examples/api/src-tauri/mobile.toml +++ /dev/null @@ -1,8 +0,0 @@ -[app] -name = "api" -stylized-name = "Tauri API" -domain = "tauri.studio" -template-pack = "tauri" - -[apple] -development-team = "0" diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 0945082a6e2..64621941648 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -272,7 +272,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=feat/library#c1365b7e90d341d67cf40f4d2f5532e932631907" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#32de2201d4a607c2d4b910cc2a33d052fe852bd8" dependencies = [ "bicycle", "bossy", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 777d4d53f09..9c2b987ab36 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -30,8 +30,8 @@ path = "src/main.rs" bossy = { git = "https://github.com/lucasfernog/bossy", branch = "fix/winapi-features" } [dependencies] -#cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } -cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "feat/library", default-features = false } +# cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } +cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } bossy = "0.2" textwrap = { version = "0.11.0", features = ["term_size"] } thiserror = "1" diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index 60ab3fc4f15..91ec0a3995f 100644 --- a/tooling/cli/schema.json +++ b/tooling/cli/schema.json @@ -144,6 +144,7 @@ "wix": null } }, + "iOS": {}, "macOSPrivateApi": false, "pattern": { "use": "brownfield" @@ -426,6 +427,15 @@ "description": "MacOS private API configuration. Enables the transparent background API and sets the `fullScreenEnabled` preference to `true`.", "default": false, "type": "boolean" + }, + "iOS": { + "description": "iOS configuration.", + "default": {}, + "allOf": [ + { + "$ref": "#/definitions/IosConfig" + } + ] } }, "additionalProperties": false @@ -2418,6 +2428,20 @@ }, "additionalProperties": false }, + "IosConfig": { + "description": "General configuration for the iOS target.", + "type": "object", + "properties": { + "developmentTeam": { + "description": "The development team. This value is required for iOS development because code signing is enforced. The `APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, "BuildConfig": { "description": "The Build configuration object.", "type": "object", diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 59dd3eba15a..ba3f7f0304f 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -3,34 +3,45 @@ // SPDX-License-Identifier: MIT use cargo_mobile::{ - android::config::{Config as AndroidConfig, Metadata as AndroidMetadata}, - config::{metadata::Metadata, Config}, + android::{ + adb, + config::{Config as AndroidConfig, Metadata as AndroidMetadata}, + device::Device, + env::{Env, Error as EnvError}, + target::{BuildError, Target}, + }, + device::PromptError, + opts::{NoiseLevel, Profile}, os, - util::cli::TextWrapper, + target::call_for_targets_with_fallback, + util::prompt, }; use clap::{Parser, Subcommand}; use super::{ - ensure_init, + ensure_init, get_config, get_metadata, init::{command as init_command, Options as InitOptions}, - Target, + Target as MobileTarget, }; +use crate::helpers::config::get as get_tauri_config; use crate::Result; pub(crate) mod project; #[derive(Debug, thiserror::Error)] enum Error { + #[error(transparent)] + EnvInitFailed(EnvError), + #[error("invalid tauri configuration: {0}")] + InvalidTauriConfig(String), #[error("{0}")] ProjectNotInitialized(String), #[error(transparent)] - ConfigFailed(cargo_mobile::config::LoadOrGenError), - #[error(transparent)] - MetadataFailed(cargo_mobile::config::metadata::Error), - #[error("Android is marked as unsupported in your configuration file")] - Unsupported, - #[error(transparent)] OpenFailed(os::OpenFileError), + #[error(transparent)] + BuildFailed(BuildError), + #[error("{0}")] + TargetInvalid(String), } #[derive(Parser)] @@ -46,41 +57,125 @@ pub struct Cli { command: Commands, } +#[derive(Debug, Parser)] +pub struct BuildOptions { + /// Targets to build. + #[clap( + short, + long = "target", + multiple_occurrences(true), + multiple_values(true), + value_parser(clap::builder::PossibleValuesParser::new(["aarch64", "armv7", "i686", "x86_64"])) + )] + targets: Option>, + /// Builds with the debug flag + #[clap(short, long)] + debug: bool, +} + #[derive(Subcommand)] enum Commands { Init(InitOptions), + /// Open project in Android Studio Open, + #[clap(hide(true))] + Build(BuildOptions), } pub fn command(cli: Cli) -> Result<()> { match cli.command { - Commands::Init(options) => init_command(options, Target::Android)?, + Commands::Init(options) => init_command(options, MobileTarget::Android)?, Commands::Open => open()?, + Commands::Build(options) => build(options)?, } Ok(()) } fn with_config( - wrapper: &TextWrapper, f: impl FnOnce(&AndroidConfig, &AndroidMetadata) -> Result<(), Error>, ) -> Result<(), Error> { - let (config, _origin) = - Config::load_or_gen(".", true.into(), wrapper).map_err(Error::ConfigFailed)?; - let metadata = Metadata::load(config.app().root_dir()).map_err(Error::MetadataFailed)?; - if metadata.android().supported() { - f(config.android(), metadata.android()) - } else { - Err(Error::Unsupported) - } + let tauri_config = + get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + let config = get_config(tauri_config_); + let metadata = get_metadata(tauri_config_); + f(config.android(), metadata.android()) } fn open() -> Result<()> { - let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); - with_config(&wrapper, |config, _metadata| { - ensure_init(config.project_dir(), Target::Android) + with_config(|config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) })?; Ok(()) } + +fn build(options: BuildOptions) -> Result<()> { + let profile = if options.debug { + Profile::Debug + } else { + Profile::Release + }; + + fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { + let device_list = + adb::device_list(env).map_err(|cause| PromptError::detection_failed("Android", cause))?; + if !device_list.is_empty() { + let index = if device_list.len() > 1 { + prompt::list( + concat!("Detected ", "Android", " devices"), + device_list.iter(), + "device", + None, + "Device", + ) + .map_err(|cause| PromptError::prompt_failed("Android", cause))? + } else { + 0 + }; + let device = device_list.into_iter().nth(index).unwrap(); + println!( + "Detected connected device: {} with target {:?}", + device, + device.target().triple, + ); + Ok(device) + } else { + Err(PromptError::none_detected("Android")) + } + } + + fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { + device_prompt(env).map(|device| device.target()).ok() + } + + with_config(|config, metadata| { + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + + let env = Env::new().map_err(Error::EnvInitFailed)?; + + call_for_targets_with_fallback( + options.targets.unwrap_or_default().iter(), + &detect_target_ok, + &env, + |target: &Target| { + target + .build( + config, + metadata, + &env, + NoiseLevel::Polite, + true.into(), + profile, + ) + .map_err(Error::BuildFailed) + }, + ) + .map_err(|e| Error::TargetInvalid(e.to_string()))? + })?; + Ok(()) +} diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs index b70771b0787..0d4c504f72b 100644 --- a/tooling/cli/src/mobile/android/project.rs +++ b/tooling/cli/src/mobile/android/project.rs @@ -71,7 +71,11 @@ pub fn gen( map.insert( "root-dir-rel", Path::new(&os::replace_path_separator( - util::relativize_path(config.app().root_dir(), config.project_dir()).into_os_string(), + util::relativize_path( + config.app().root_dir(), + config.project_dir().join(config.app().name()), + ) + .into_os_string(), )), ); map.insert("root-dir", config.app().root_dir()); diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 1fdd9e67b11..326c72fad24 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -2,19 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use super::Target; -use crate::helpers::{app_paths::tauri_dir, template::JsonMap}; +use super::{get_config, get_metadata, Target}; +use crate::helpers::{app_paths::tauri_dir, config::get as get_tauri_config, template::JsonMap}; use crate::Result; use cargo_mobile::{ android, - config::{ - self, - metadata::{self, Metadata}, - Config, - }, - dot_cargo, - init::{DOT_FIRST_INIT_CONTENTS, DOT_FIRST_INIT_FILE_NAME}, - opts, + config::Config, + dot_cargo, opts, os::code_command, util::{ self, @@ -58,10 +52,8 @@ pub fn command(mut options: Options, target: Target) -> Result<()> { #[derive(Debug, thiserror::Error)] pub enum Error { - #[error(transparent)] - ConfigLoadOrGen(config::LoadOrGenError), - #[error("failed to init first init file {path}: {cause}")] - DotFirstInitWrite { path: PathBuf, cause: io::Error }, + #[error("invalid tauri configuration: {0}")] + InvalidTauriConfig(String), #[error("failed to create asset dir {asset_dir}: {cause}")] AssetDirCreation { asset_dir: PathBuf, @@ -73,8 +65,6 @@ pub enum Error { DotCargoLoad(dot_cargo::LoadError), #[error(transparent)] HostTargetTripleDetection(util::HostTargetTripleError), - #[error(transparent)] - Metadata(metadata::Error), #[cfg(target_os = "macos")] #[error(transparent)] IosInit(super::ios::project::Error), @@ -84,8 +74,6 @@ pub enum Error { AndroidInit(super::android::project::Error), #[error(transparent)] DotCargoWrite(dot_cargo::WriteError), - #[error("failed to delete first init file {path}: {cause}")] - DotFirstInitDelete { path: PathBuf, cause: io::Error }, #[error(transparent)] OpenInEditor(util::OpenInEditorError), } @@ -100,26 +88,13 @@ pub fn exec( cwd: impl AsRef, ) -> Result { let cwd = cwd.as_ref(); - let (config, config_origin) = - Config::load_or_gen(cwd, non_interactive, wrapper).map_err(Error::ConfigLoadOrGen)?; - let dot_first_init_path = config.app().root_dir().join(DOT_FIRST_INIT_FILE_NAME); - let dot_first_init_exists = { - let dot_first_init_exists = dot_first_init_path.exists(); - if config_origin.freshly_minted() && !dot_first_init_exists { - // indicate first init is ongoing, so that if we error out and exit - // the next init will know to still use `WildWest` filtering - log::info!("creating first init dot file at {:?}", dot_first_init_path); - fs::write(&dot_first_init_path, DOT_FIRST_INIT_CONTENTS).map_err(|cause| { - Error::DotFirstInitWrite { - path: dot_first_init_path.clone(), - cause, - } - })?; - true - } else { - dot_first_init_exists - } - }; + let tauri_config = + get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + + let config = get_config(tauri_config_); + let metadata = get_metadata(tauri_config_); let asset_dir = config.app().asset_dir(); if !asset_dir.is_dir() { @@ -149,33 +124,24 @@ pub fn exec( dot_cargo .set_default_target(util::host_target_triple().map_err(Error::HostTargetTripleDetection)?); - let metadata = Metadata::load(config.app().root_dir()).map_err(Error::Metadata)?; - - // Generate Xcode project - #[cfg(target_os = "macos")] - if target == Target::Ios && metadata.apple().supported() { - super::ios::project::gen( - config.apple(), - metadata.apple(), - handlebars(&config), - wrapper, - non_interactive, - skip_dev_tools, - reinstall_deps, - ) - .map_err(Error::IosInit)?; - } else { - println!("Skipping iOS init, since it's marked as unsupported in your Cargo.toml metadata"); - } + let (handlebars, mut map) = handlebars(&config); + // TODO: make this a relative path + map.insert( + "tauri-binary", + std::env::args_os() + .next() + .unwrap_or_else(|| std::ffi::OsString::from("cargo")) + .to_string_lossy(), + ); // Generate Android Studio project - if target == Target::Android && metadata.android().supported() { + if target == Target::Android { match android::env::Env::new() { Ok(env) => super::android::project::gen( config.android(), metadata.android(), &env, - handlebars(&config), + (handlebars, map), wrapper, &mut dot_cargo, ) @@ -193,19 +159,26 @@ pub fn exec( } } } else { - println!("Skipping Android init, since it's marked as unsupported in your Cargo.toml metadata"); + // Generate Xcode project + #[cfg(target_os = "macos")] + if target == Target::Ios { + super::ios::project::gen( + config.apple(), + metadata.apple(), + (handlebars, map), + wrapper, + non_interactive, + skip_dev_tools, + reinstall_deps, + ) + .map_err(Error::IosInit)?; + } } dot_cargo .write(config.app()) .map_err(Error::DotCargoWrite)?; - if dot_first_init_exists { - log::info!("deleting first init dot file at {:?}", dot_first_init_path); - fs::remove_file(&dot_first_init_path).map_err(|cause| Error::DotFirstInitDelete { - path: dot_first_init_path, - cause, - })?; - } + Report::victory( "Project generated successfully!", "Make cool apps! 🌻 🐕 🎉", diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index ff781d302d7..fcd56851241 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -2,35 +2,50 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use crate::helpers::config::get as get_tauri_config; use cargo_mobile::{ - apple::config::{Config as AppleConfig, Metadata as AppleMetadata}, - config::{metadata::Metadata, Config}, - os, - util::cli::TextWrapper, + apple::{ + config::{Config as AppleConfig, Metadata as AppleMetadata}, + target::{CompileLibError, Target}, + }, + env::{Env, Error as EnvError}, + opts::{NoiseLevel, Profile}, + os, util, }; use clap::{Parser, Subcommand}; use super::{ - ensure_init, + ensure_init, get_config, get_metadata, init::{command as init_command, Options as InitOptions}, - Target, + Target as MobileTarget, }; use crate::Result; +use std::{collections::HashMap, ffi::OsStr, path::PathBuf}; pub(crate) mod project; #[derive(Debug, thiserror::Error)] enum Error { + #[error(transparent)] + EnvInitFailed(EnvError), + #[error("invalid tauri configuration: {0}")] + InvalidTauriConfig(String), #[error("{0}")] ProjectNotInitialized(String), #[error(transparent)] - ConfigFailed(cargo_mobile::config::LoadOrGenError), + OpenFailed(os::OpenFileError), #[error(transparent)] - MetadataFailed(cargo_mobile::config::metadata::Error), - #[error("iOS is marked as unsupported in your configuration file")] - Unsupported, + NoHomeDir(util::NoHomeDir), + #[error("SDK root provided by Xcode was invalid. {sdk_root} doesn't exist or isn't a directory")] + SdkRootInvalid { sdk_root: PathBuf }, + #[error("Include dir was invalid. {include_dir} doesn't exist or isn't a directory")] + IncludeDirInvalid { include_dir: PathBuf }, + #[error("macOS SDK root was invalid. {macos_sdk_root} doesn't exist or isn't a directory")] + MacosSdkRootInvalid { macos_sdk_root: PathBuf }, + #[error("Arch specified by Xcode was invalid. {arch} isn't a known arch")] + ArchInvalid { arch: String }, #[error(transparent)] - OpenFailed(os::OpenFileError), + CompileLibFailed(CompileLibError), } #[derive(Parser)] @@ -46,41 +61,170 @@ pub struct Cli { command: Commands, } +#[derive(Debug, Parser)] +pub struct XcodeScriptOptions { + /// Value of `PLATFORM_DISPLAY_NAME` env var + #[clap(long)] + platform: String, + /// Value of `SDKROOT` env var + #[clap(long)] + sdk_root: PathBuf, + /// Value of `CONFIGURATION` env var + #[clap(long)] + configuration: String, + /// Value of `FORCE_COLOR` env var + #[clap(long)] + force_color: bool, + /// Value of `ARCHS` env var + #[clap(index = 1, required = true)] + arches: Vec, +} + #[derive(Subcommand)] enum Commands { Init(InitOptions), Open, + #[clap(hide(true))] + XcodeScript(XcodeScriptOptions), } pub fn command(cli: Cli) -> Result<()> { match cli.command { - Commands::Init(options) => init_command(options, Target::Ios)?, + Commands::Init(options) => init_command(options, MobileTarget::Ios)?, Commands::Open => open()?, + Commands::XcodeScript(options) => xcode_script(options)?, } Ok(()) } fn with_config( - wrapper: &TextWrapper, f: impl FnOnce(&AppleConfig, &AppleMetadata) -> Result<(), Error>, ) -> Result<(), Error> { - let (config, _origin) = - Config::load_or_gen(".", true.into(), wrapper).map_err(Error::ConfigFailed)?; - let metadata = Metadata::load(config.app().root_dir()).map_err(Error::MetadataFailed)?; - if metadata.apple().supported() { - f(config.apple(), metadata.apple()) - } else { - Err(Error::Unsupported) - } + let tauri_config = + get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + let config = get_config(tauri_config_); + let metadata = get_metadata(tauri_config_); + f(config.apple(), metadata.apple()) } fn open() -> Result<()> { - let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); - with_config(&wrapper, |config, _metadata| { - ensure_init(config.project_dir(), Target::Ios) + with_config(|config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) })?; Ok(()) } + +fn xcode_script(options: XcodeScriptOptions) -> Result<()> { + fn macos_from_platform(platform: &str) -> bool { + platform == "macOS" + } + + fn profile_from_configuration(configuration: &str) -> Profile { + if configuration == "release" { + Profile::Release + } else { + Profile::Debug + } + } + + let profile = profile_from_configuration(&options.configuration); + let macos = macos_from_platform(&options.platform); + + with_config(|config, metadata| { + let env = Env::new().map_err(Error::EnvInitFailed)?; + // The `PATH` env var Xcode gives us is missing any additions + // made by the user's profile, so we'll manually add cargo's + // `PATH`. + let env = env.prepend_to_path( + util::home_dir() + .map_err(Error::NoHomeDir)? + .join(".cargo/bin"), + ); + + if !options.sdk_root.is_dir() { + return Err(Error::SdkRootInvalid { + sdk_root: options.sdk_root, + }); + } + let include_dir = options.sdk_root.join("usr/include"); + if !include_dir.is_dir() { + return Err(Error::IncludeDirInvalid { include_dir }); + } + + let mut host_env = HashMap::<&str, &OsStr>::new(); + + // Host flags that are used by build scripts + let (macos_isysroot, library_path) = { + let macos_sdk_root = options + .sdk_root + .join("../../../../MacOSX.platform/Developer/SDKs/MacOSX.sdk"); + if !macos_sdk_root.is_dir() { + return Err(Error::MacosSdkRootInvalid { macos_sdk_root }); + } + ( + format!("-isysroot {}", macos_sdk_root.display()), + format!("{}/usr/lib", macos_sdk_root.display()), + ) + }; + host_env.insert("MAC_FLAGS", macos_isysroot.as_ref()); + host_env.insert("CFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); + host_env.insert("CXXFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); + + host_env.insert( + "OBJC_INCLUDE_PATH_x86_64_apple_darwin", + include_dir.as_os_str(), + ); + + host_env.insert("RUST_BACKTRACE", "1".as_ref()); + + let macos_target = Target::macos(); + + let isysroot = format!("-isysroot {}", options.sdk_root.display()); + + for arch in options.arches { + // Set target-specific flags + let triple = match arch.as_str() { + "arm64" => "aarch64_apple_ios", + "x86_64" => "x86_64_apple_ios", + _ => return Err(Error::ArchInvalid { arch }), + }; + let cflags = format!("CFLAGS_{}", triple); + let cxxflags = format!("CFLAGS_{}", triple); + let objc_include_path = format!("OBJC_INCLUDE_PATH_{}", triple); + let mut target_env = host_env.clone(); + target_env.insert(cflags.as_ref(), isysroot.as_ref()); + target_env.insert(cxxflags.as_ref(), isysroot.as_ref()); + target_env.insert(objc_include_path.as_ref(), include_dir.as_ref()); + // Prevents linker errors in build scripts and proc macros: + // https://github.com/signalapp/libsignal-client/commit/02899cac643a14b2ced7c058cc15a836a2165b6d + target_env.insert("LIBRARY_PATH", library_path.as_ref()); + + let target = if macos { + &macos_target + } else { + Target::for_arch(&arch).ok_or_else(|| Error::ArchInvalid { + arch: arch.to_owned(), + })? + }; + target + .compile_lib( + config, + metadata, + NoiseLevel::Polite, + true.into(), + profile, + &env, + target_env, + ) + .map_err(Error::CompileLibFailed)?; + } + Ok(()) + })?; + + Ok(()) +} diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 16ea4040a75..6aced9de5d1 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -2,7 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use crate::helpers::{app_paths::tauri_dir, config::Config as TauriConfig}; use anyhow::{bail, Result}; +#[cfg(target_os = "macos")] +use cargo_mobile::apple::config::{ + Metadata as AppleMetadata, Platform as ApplePlatform, Raw as RawAppleConfig, +}; +use cargo_mobile::{ + android::config::{Metadata as AndroidMetadata, Raw as RawAndroidConfig}, + config::{app::Raw as RawAppConfig, metadata::Metadata, Config, Raw}, +}; use std::path::PathBuf; pub mod android; @@ -35,6 +44,86 @@ impl Target { } } +fn get_metadata(_config: &TauriConfig) -> Metadata { + Metadata { + #[cfg(target_os = "macos")] + apple: AppleMetadata { + supported: true, + ios: ApplePlatform { + features: None, + frameworks: None, + valid_archs: None, + vendor_frameworks: None, + vendor_sdks: None, + asset_catalogs: None, + pods: None, + additional_targets: None, + pre_build_scripts: None, + post_compile_scripts: None, + post_build_scripts: None, + command_line_arguments: None, + }, + macos: Default::default(), + }, + android: AndroidMetadata { + supported: true, + features: None, + app_sources: None, + app_plugins: None, + project_dependencies: None, + app_dependencies: None, + app_dependencies_platform: None, + asset_packs: None, + }, + } +} + +fn get_config(config: &TauriConfig) -> Config { + let mut s = config.tauri.bundle.identifier.rsplit('.'); + let app_name = s.next().unwrap_or("app").to_string(); + let mut domain = String::new(); + for w in s { + domain.push_str(w); + domain.push('.'); + } + domain.pop(); + let raw = Raw { + app: RawAppConfig { + name: app_name, + stylized_name: config.package.product_name.clone(), + domain, + asset_dir: None, + template_pack: None, + }, + #[cfg(target_os = "macos")] + apple: Some(RawAppleConfig { + development_team: std::env::var("APPLE_DEVELOPMENT_TEAM") + .ok() + .or_else(|| config.tauri.ios.development_team.clone()) + .expect("you must set `tauri > iOS > developmentTeam` config value or the `APPLE_DEVELOPMENT_TEAM` environment variable"), + project_dir: None, + ios_no_default_features: None, + ios_features: None, + macos_no_default_features: None, + macos_features: None, + bundle_version: None, + bundle_version_short: None, + ios_version: None, + macos_version: None, + use_legacy_build_system: None, + plist_pairs: None, + }), + android: Some(RawAndroidConfig { + min_sdk_version: None, + vulkan_validation: None, + project_dir: None, + no_default_features: None, + features: None, + }), + }; + Config::from_raw(tauri_dir(), raw).unwrap() +} + fn ensure_init(project_dir: PathBuf, target: Target) -> Result<()> { if !project_dir.exists() { bail!( diff --git a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt index 454531d0a9d..9796cc8c350 100644 --- a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt +++ b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt @@ -37,8 +37,8 @@ open class BuildTask : DefaultTask() { } project.exec { workingDir(File(project.getProjectDir(), rootDirRel.getPath())) - executable("cargo") - args(listOf("android", "build")) + executable("{{ tauri-binary }}") + args(listOf("tauri", "android", "build")) if (project.logger.isEnabled(LogLevel.DEBUG)) { args("-vv") } else if (project.logger.isEnabled(LogLevel.INFO)) { @@ -47,7 +47,7 @@ open class BuildTask : DefaultTask() { if (release) { args("--release") } - args("${target}") + args(listOf("--target", "${target}")) }.assertNormalExitValue() } } diff --git a/tooling/cli/templates/mobile/ios/project.yml b/tooling/cli/templates/mobile/ios/project.yml index b4e3a5ddacf..54bd3bb23c7 100644 --- a/tooling/cli/templates/mobile/ios/project.yml +++ b/tooling/cli/templates/mobile/ios/project.yml @@ -266,15 +266,15 @@ targets: ARCHS: [{{join ios-valid-archs}}] VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} legacy: - toolPath: ${HOME}/.cargo/bin/cargo-apple - arguments: xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + toolPath: {{ tauri-binary }} + arguments: ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} passSettings: false # prevents evil linker errors - workingDirectory: $(SRCROOT)/.. + workingDirectory: $(SRCROOT)/../.. lib_{{app.name}}_macOS: type: "" platform: macOS legacy: - toolPath: ${HOME}/.cargo/bin/cargo-apple - arguments: xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + toolPath: {{ tauri-binary }} + arguments: ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} passSettings: false - workingDirectory: $(SRCROOT)/.. + workingDirectory: $(SRCROOT)/../.. From e20145cccce58ec435571dfccf1019183c37a2dc Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 16 Aug 2022 11:48:01 -0300 Subject: [PATCH 009/423] fix(examples): change API example domain, export mobile mod --- examples/api/src-tauri/src/lib.rs | 5 +++++ examples/api/src-tauri/src/mobile.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index 75a4298cf57..651bb94ec49 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -9,6 +9,11 @@ mod cmd; +#[cfg(mobile)] +mod mobile; +#[cfg(mobile)] +pub use mobile::*; + use serde::Serialize; use tauri::{window::WindowBuilder, App, AppHandle, RunEvent, WindowUrl}; diff --git a/examples/api/src-tauri/src/mobile.rs b/examples/api/src-tauri/src/mobile.rs index 05c5bda021c..8d47ad0ed5b 100644 --- a/examples/api/src-tauri/src/mobile.rs +++ b/examples/api/src-tauri/src/mobile.rs @@ -33,7 +33,7 @@ fn _start_app() { #[inline(never)] pub extern "C" fn start_app() { #[cfg(target_os = "android")] - android_binding!(studio_tauri, api, _start_app, tauri_runtime_wry::wry); + android_binding!(com_tauri, api, _start_app, tauri_runtime_wry::wry); _start_app() } From a3680ef2bc0d1712835a69f2df8016830b362438 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 17 Aug 2022 16:40:04 -0300 Subject: [PATCH 010/423] feat(cli): skip dev tools installation on mobile init --- tooling/cli/src/mobile/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 326c72fad24..54bb1ff0117 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -41,7 +41,7 @@ pub fn command(mut options: Options, target: Target) -> Result<()> { target, &wrapper, options.ci.into(), - SkipDevTools::No, + SkipDevTools::Yes, ReinstallDeps::Yes, OpenInEditor::No, tauri_dir(), From f445f374a3642254d41ef224b4672081e66fb985 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 18 Aug 2022 11:31:07 -0300 Subject: [PATCH 011/423] feat(android): update project dependencies --- .../templates/mobile/android/app/build.gradle.kts | 12 +++++------- .../cli/templates/mobile/android/build.gradle.kts | 2 +- .../mobile/android/buildSrc/build.gradle.kts | 2 +- .../android/gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tooling/cli/templates/mobile/android/app/build.gradle.kts b/tooling/cli/templates/mobile/android/app/build.gradle.kts index 13906b47c2d..cc33f0d8b57 100644 --- a/tooling/cli/templates/mobile/android/app/build.gradle.kts +++ b/tooling/cli/templates/mobile/android/app/build.gradle.kts @@ -7,11 +7,11 @@ plugins { } android { - compileSdk = 31 + compileSdk = 33 defaultConfig { applicationId = "{{reverse-domain app.domain}}.{{snake-case app.name}}" minSdk = {{android.min-sdk-version}} - targetSdk = 31 + targetSdk = 33 versionCode = 1 versionName = "1.0" } @@ -66,9 +66,8 @@ dependencies { {{~#each android-app-dependencies}} implementation("{{this}}"){{/each}} implementation("androidx.webkit:webkit:1.4.0") - implementation("androidx.appcompat:appcompat:1.4.1") - implementation("com.google.android.material:material:1.6.0") - implementation("androidx.constraintlayout:constraintlayout:2.1.3") + implementation("androidx.appcompat:appcompat:1.5.0") + implementation("com.google.android.material:material:1.6.1") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.3") androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") @@ -76,8 +75,7 @@ dependencies { afterEvaluate { android.applicationVariants.all { - val buildType = "${buildType.name.capitalize()}" - productFlavors.forEach { + productFlavors.forEach { _ -> val archAndBuildType = name.capitalize() tasks["merge${archAndBuildType}JniLibFolders"].dependsOn(tasks["rustBuild${archAndBuildType}"]) } diff --git a/tooling/cli/templates/mobile/android/build.gradle.kts b/tooling/cli/templates/mobile/android/build.gradle.kts index f45e9c35ae5..8751beac14f 100644 --- a/tooling/cli/templates/mobile/android/build.gradle.kts +++ b/tooling/cli/templates/mobile/android/build.gradle.kts @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:7.0.2") + classpath("com.android.tools.build:gradle:7.2.2") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") {{~#each android-project-dependencies}} classpath("{{this}}"){{/each}} diff --git a/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts b/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts index b0d0d4e6568..f3753b20689 100644 --- a/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts +++ b/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts @@ -22,6 +22,6 @@ repositories { dependencies { compileOnly(gradleApi()) - implementation("com.android.tools.build:gradle:7.0.2") + implementation("com.android.tools.build:gradle:7.2.2") } diff --git a/tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties b/tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties index 543ef62a1a7..5303f9c743a 100644 --- a/tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties +++ b/tooling/cli/templates/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue May 10 19:22:52 CST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 6f0615044d09ec58393a7ebca5e45bb175e20db3 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sat, 20 Aug 2022 16:53:07 -0300 Subject: [PATCH 012/423] feat(cli): add `android dev` and `ios dev` commands (#4982) --- .changes/cli-mobile-dev.md | 6 + .changes/codegen-mobile-devurl.md | 5 + .changes/dev-proxy.md | 5 + core/tauri-codegen/Cargo.toml | 2 + core/tauri-codegen/src/context.rs | 19 +- core/tauri-codegen/src/embedded_assets.rs | 1 + core/tauri-utils/src/config.rs | 2 +- core/tauri/Cargo.toml | 4 +- core/tauri/src/manager.rs | 89 +++-- examples/api/dist/assets/index.js | 44 +-- examples/api/package.json | 7 +- examples/api/src-tauri/Cargo.lock | 37 +- examples/api/src-tauri/Cargo.toml | 2 - examples/api/src-tauri/tauri.conf.json | 4 +- examples/api/vite.config.js | 34 +- examples/api/yarn.lock | 361 +++++++++-------- tooling/cli/Cargo.lock | 135 ++----- tooling/cli/Cargo.toml | 5 +- tooling/cli/schema.json | 2 +- tooling/cli/src/build.rs | 3 +- tooling/cli/src/dev.rs | 21 +- tooling/cli/src/helpers/flock.rs | 363 ++++++++++++++++++ tooling/cli/src/helpers/mod.rs | 1 + tooling/cli/src/interface/mod.rs | 12 +- tooling/cli/src/interface/rust.rs | 188 ++++----- tooling/cli/src/interface/rust/desktop.rs | 35 +- tooling/cli/src/lib.rs | 26 +- tooling/cli/src/mobile/android.rs | 265 ++++++++++--- tooling/cli/src/mobile/android/project.rs | 29 +- tooling/cli/src/mobile/init.rs | 126 +++--- tooling/cli/src/mobile/ios.rs | 230 +++++++++-- tooling/cli/src/mobile/ios/project.rs | 8 +- tooling/cli/src/mobile/mod.rs | 172 +++++++-- .../mobile/android/app/build.gradle.kts | 14 +- .../android/app/src/main/AndroidManifest.xml | 3 - .../android/app/src/main/MainActivity.kt | 2 +- .../app/src/main/RustWebChromeClient.kt | 2 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 2898 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 1772 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 3918 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 5914 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 7778 -> 0 bytes .../app/src/main/res/values/strings.xml | 2 +- .../mobile/android/buildSrc/build.gradle.kts | 4 - .../buildSrc/src/main/kotlin/BuildTask.kt | 21 +- .../buildSrc/src/main/kotlin/RustPlugin.kt | 24 +- 47 files changed, 1629 insertions(+), 691 deletions(-) create mode 100644 .changes/cli-mobile-dev.md create mode 100644 .changes/codegen-mobile-devurl.md create mode 100644 .changes/dev-proxy.md create mode 100644 tooling/cli/src/helpers/flock.rs delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/.changes/cli-mobile-dev.md b/.changes/cli-mobile-dev.md new file mode 100644 index 00000000000..bd0df7aa968 --- /dev/null +++ b/.changes/cli-mobile-dev.md @@ -0,0 +1,6 @@ +--- +"cli.rs": minor +"cli.js": minor +--- + +Added `android dev` and `ios dev` commands. diff --git a/.changes/codegen-mobile-devurl.md b/.changes/codegen-mobile-devurl.md new file mode 100644 index 00000000000..da7c8812d2a --- /dev/null +++ b/.changes/codegen-mobile-devurl.md @@ -0,0 +1,5 @@ +--- +"tauri-codegen": patch +--- + +Change `devPath` URL to use the local IP address on iOS and Android. diff --git a/.changes/dev-proxy.md b/.changes/dev-proxy.md new file mode 100644 index 00000000000..3e4e883162b --- /dev/null +++ b/.changes/dev-proxy.md @@ -0,0 +1,5 @@ +--- +"tauri": major +--- + +**Breaking change:** Use the custom protocol as a proxy to the development server on all platforms except Linux. diff --git a/core/tauri-codegen/Cargo.toml b/core/tauri-codegen/Cargo.toml index 590465c8f63..b2b08da1535 100644 --- a/core/tauri-codegen/Cargo.toml +++ b/core/tauri-codegen/Cargo.toml @@ -29,6 +29,8 @@ semver = "1" ico = "0.1" png = "0.17" json-patch = "0.2" +local-ip-address = "0.4" +url = "2" [target."cfg(target_os = \"macos\")".dependencies] plist = "1" diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index d5e5cd5789a..28264dbce8e 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -122,7 +122,7 @@ enum Target { pub fn context_codegen(data: ContextData) -> Result { let ContextData { dev, - config, + mut config, config_parent, root, } = data; @@ -155,6 +155,23 @@ pub fn context_codegen(data: ContextData) -> Result d == "localhost", + Some(url::Host::Ipv4(i)) => { + i == std::net::Ipv4Addr::LOCALHOST || i == std::net::Ipv4Addr::UNSPECIFIED + } + _ => false, + }; + if localhost { + if let Ok(ip) = local_ip_address::local_ip() { + url.set_host(Some(&ip.to_string())).unwrap(); + } + } + } + } + let mut options = AssetOptions::new(config.tauri.pattern.clone()) .freeze_prototype(config.tauri.security.freeze_prototype) .dangerous_disable_asset_csp_modification( diff --git a/core/tauri-codegen/src/embedded_assets.rs b/core/tauri-codegen/src/embedded_assets.rs index a5983fa6e0e..7e2437efc16 100644 --- a/core/tauri-codegen/src/embedded_assets.rs +++ b/core/tauri-codegen/src/embedded_assets.rs @@ -431,6 +431,7 @@ impl ToTokens for EmbeddedAssets { // we expect phf related items to be in path when generating the path code tokens.append_all(quote! {{ + #[allow(unused_imports)] use ::tauri::utils::assets::{CspHash, EmbeddedAssets, phf, phf::phf_map}; EmbeddedAssets::new(phf_map! { #assets }, &[#global_hashes], phf_map! { #html_hashes }) }}); diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 5e6a7209111..0545b04ce5d 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -2364,7 +2364,7 @@ fn default_dialog() -> bool { #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct IosConfig { /// The development team. This value is required for iOS development because code signing is enforced. - /// The `APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it. + /// The `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it. #[serde(alias = "development-team")] pub development_team: Option, } diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index d11edae250c..879245f6585 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -75,7 +75,7 @@ base64 = { version = "0.13", optional = true } clap = { version = "3", optional = true } reqwest = { version = "0.11", features = [ "json", "stream" ], optional = true } bytes = { version = "1", features = [ "serde" ], optional = true } -attohttpc = { version = "0.20", features = [ "compress", "json", "form" ], optional = true } +attohttpc = { version = "0.20", features = [ "compress", "json", "form" ] } open = { version = "3.0", optional = true } shared_child = { version = "1.0", optional = true } os_pipe = { version = "1.0", optional = true } @@ -146,7 +146,7 @@ updater = [ "dialog-ask", "fs-extract-api" ] -http-api = [ "attohttpc" ] +http-api = [ ] http-multipart = [ "attohttpc/multipart-form", "reqwest/multipart" ] shell-open-api = [ "open", "regex", "tauri-macros/shell-scope" ] fs-extract-api = [ "zip" ] diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 673fb3c6be1..1f670d2352e 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -142,11 +142,6 @@ fn set_csp( Csp::DirectiveMap(csp).to_string() } -#[cfg(target_os = "linux")] -fn set_html_csp(html: &str, csp: &str) -> String { - html.replacen(tauri_utils::html::CSP_TOKEN, csp, 1) -} - // inspired by https://github.com/rust-lang/rust/blob/1be5c8f90912c446ecbdc405cbc4a89f9acd20fd/library/alloc/src/str.rs#L260-L297 fn replace_with_callback String>( original: &str, @@ -374,7 +369,13 @@ impl WindowManager { /// Get the origin as it will be seen in the webview. fn get_browser_origin(&self) -> String { match self.base_path() { - AppUrl::Url(WindowUrl::External(url)) => url.origin().ascii_serialization(), + AppUrl::Url(WindowUrl::External(url)) => { + if cfg!(dev) && !cfg!(target_os = "linux") { + format_real_schema("tauri") + } else { + url.origin().ascii_serialization() + } + } _ => format_real_schema("tauri"), } } @@ -820,8 +821,12 @@ impl WindowManager { >, ) -> Box Result> + Send + Sync> { + #[cfg(dev)] + let url = self.get_url().into_owned(); + #[cfg(not(dev))] let manager = self.clone(); let window_origin = window_origin.to_string(); + Box::new(move |request| { let path = request .uri() @@ -834,32 +839,47 @@ impl WindowManager { // the `strip_prefix` only returns None when a request is made to `https://tauri.$P` on Windows // where `$P` is not `localhost/*` .unwrap_or_else(|| "".to_string()); - let asset = manager.get_asset(path)?; - let mut builder = HttpResponseBuilder::new() - .header("Access-Control-Allow-Origin", &window_origin) - .mimetype(&asset.mime_type); - if let Some(csp) = &asset.csp_header { - builder = builder.header("Content-Security-Policy", csp); - } - let mut response = builder.body(asset.bytes)?; - if let Some(handler) = &web_resource_request_handler { - handler(request, &mut response); - // if it's an HTML file, we need to set the CSP meta tag on Linux - #[cfg(target_os = "linux")] - if let Some(response_csp) = response.headers().get("Content-Security-Policy") { - let response_csp = String::from_utf8_lossy(response_csp.as_bytes()); - let body = set_html_csp(&String::from_utf8_lossy(response.body()), &response_csp); - *response.body_mut() = body.as_bytes().to_vec(); - } - } else { - #[cfg(target_os = "linux")] - { - if let Some(csp) = &asset.csp_header { - let body = set_html_csp(&String::from_utf8_lossy(response.body()), csp); - *response.body_mut() = body.as_bytes().to_vec(); + let mut builder = + HttpResponseBuilder::new().header("Access-Control-Allow-Origin", &window_origin); + + #[cfg(dev)] + let mut response = { + let mut url = url.clone(); + url.set_path(&path); + match attohttpc::get(url.as_str()).send() { + Ok(r) => { + for (name, value) in r.headers() { + builder = builder.header(name, value); + } + builder.status(r.status()).body(r.bytes()?)? + } + Err(e) => { + debug_eprintln!("Failed to request {}: {}", url.path(), e); + return Err(Box::new(e)); } } + }; + + #[cfg(not(dev))] + let mut response = { + let asset = manager.get_asset(path)?; + builder = builder.mimetype(&asset.mime_type); + if let Some(csp) = &asset.csp_header { + builder = builder.header("Content-Security-Policy", csp); + } + builder.body(asset.bytes)? + }; + if let Some(handler) = &web_resource_request_handler { + handler(request, &mut response); + } + // if it's an HTML file, we need to set the CSP meta tag on Linux + #[cfg(all(not(dev), target_os = "linux"))] + if let Some(response_csp) = response.headers().get("Content-Security-Policy") { + let response_csp = String::from_utf8_lossy(response_csp.as_bytes()); + let html = String::from_utf8_lossy(response.body()); + let body = html.replacen(tauri_utils::html::CSP_TOKEN, &response_csp, 1); + *response.body_mut() = body.as_bytes().to_vec(); } Ok(response) }) @@ -1061,7 +1081,10 @@ impl WindowManager { #[allow(unused_mut)] // mut url only for the data-url parsing let (is_local, mut url) = match &pending.webview_attributes.url { WindowUrl::App(path) => { + #[cfg(target_os = "linux")] let url = self.get_url(); + #[cfg(not(target_os = "linux"))] + let url: Cow<'_, Url> = Cow::Owned(Url::parse("tauri://localhost").unwrap()); ( true, // ignore "index.html" just to simplify the url @@ -1078,7 +1101,13 @@ impl WindowManager { } WindowUrl::External(url) => { let config_url = self.get_url(); - (config_url.make_relative(url).is_some(), url.clone()) + let is_local = config_url.make_relative(url).is_some(); + let mut url = url.clone(); + if is_local && !cfg!(target_os = "linux") { + url.set_scheme("tauri").unwrap(); + url.set_host(Some("localhost")).unwrap(); + } + (is_local, url) } _ => unimplemented!(), }; diff --git a/examples/api/dist/assets/index.js b/examples/api/dist/assets/index.js index 7ed23367835..52f29c45bf7 100644 --- a/examples/api/dist/assets/index.js +++ b/examples/api/dist/assets/index.js @@ -1,44 +1,44 @@ -const Zl=function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))i(o);new MutationObserver(o=>{for(const l of o)if(l.type==="childList")for(const u of l.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&i(u)}).observe(document,{childList:!0,subtree:!0});function n(o){const l={};return o.integrity&&(l.integrity=o.integrity),o.referrerpolicy&&(l.referrerPolicy=o.referrerpolicy),o.crossorigin==="use-credentials"?l.credentials="include":o.crossorigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function i(o){if(o.ep)return;o.ep=!0;const l=n(o);fetch(o.href,l)}};Zl();function J(){}function vl(t){return t()}function Io(){return Object.create(null)}function pe(t){t.forEach(vl)}function $l(t){return typeof t=="function"}function ke(t,e){return t!=t?e==e:t!==e||t&&typeof t=="object"||typeof t=="function"}let Un;function er(t,e){return Un||(Un=document.createElement("a")),Un.href=e,t===Un.href}function tr(t){return Object.keys(t).length===0}function nr(t,...e){if(t==null)return J;const n=t.subscribe(...e);return n.unsubscribe?()=>n.unsubscribe():n}function _l(t,e,n){t.$$.on_destroy.push(nr(e,n))}function r(t,e){t.appendChild(e)}function m(t,e,n){t.insertBefore(e,n||null)}function h(t){t.parentNode.removeChild(t)}function dt(t,e){for(let n=0;nt.removeEventListener(e,n,i)}function Xn(t){return function(e){return e.preventDefault(),t.call(this,e)}}function a(t,e,n){n==null?t.removeAttribute(e):t.getAttribute(e)!==n&&t.setAttribute(e,n)}function se(t){return t===""?null:+t}function or(t){return Array.from(t.childNodes)}function Z(t,e){e=""+e,t.wholeText!==e&&(t.data=e)}function G(t,e){t.value=e==null?"":e}function zt(t,e){for(let n=0;n{Vn.delete(t),i&&(n&&t.d(1),i())}),t.o(e)}else i&&i()}function Kn(t){t&&t.c()}function Vt(t,e,n,i){const{fragment:o,on_mount:l,on_destroy:u,after_update:f}=t.$$;o&&o.m(e,n),i||Lt(()=>{const c=l.map(vl).filter($l);u?u.push(...c):pe(c),t.$$.on_mount=[]}),f.forEach(Lt)}function Gt(t,e){const n=t.$$;n.fragment!==null&&(pe(n.on_destroy),n.fragment&&n.fragment.d(e),n.on_destroy=n.fragment=null,n.ctx=[])}function ar(t,e){t.$$.dirty[0]===-1&&(Ut.push(t),sr(),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<{const g=y.length?y[0]:_;return p.ctx&&o(p.ctx[k],p.ctx[k]=g)&&(!p.skip_bound&&p.bound[k]&&p.bound[k](g),d&&ar(t,k)),_}):[],p.update(),d=!0,pe(p.before_update),p.fragment=i?i(p.ctx):!1,e.target){if(e.hydrate){const k=or(e.target);p.fragment&&p.fragment.l(k),k.forEach(h)}else p.fragment&&p.fragment.c();e.intro&&Ee(t.$$.fragment),Vt(t,e.target,e.anchor,e.customElement),gl()}qt(c)}class Se{$destroy(){Gt(this,1),this.$destroy=J}$on(e,n){const i=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return i.push(n),()=>{const o=i.indexOf(n);o!==-1&&i.splice(o,1)}}$set(e){this.$$set&&!tr(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}}const Ct=[];function yl(t,e=J){let n;const i=new Set;function o(f){if(ke(t,f)&&(t=f,n)){const c=!Ct.length;for(const p of i)p[1](),Ct.push(p,t);if(c){for(let p=0;p{i.delete(p),i.size===0&&(n(),n=null)}}return{set:o,update:l,subscribe:u}}var Ei=function(t,e){return Ei=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,i){n.__proto__=i}||function(n,i){for(var o in i)Object.prototype.hasOwnProperty.call(i,o)&&(n[o]=i[o])},Ei(t,e)};function ji(t,e){if(typeof e!="function"&&e!==null)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function n(){this.constructor=t}Ei(t,e),t.prototype=e===null?Object.create(e):(n.prototype=e.prototype,new n)}var ue=function(){return ue=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0&&o[o.length-1])||d[0]!==6&&d[0]!==2)){u=0;continue}if(d[0]===3&&(!o||d[1]>o[0]&&d[1]{for(const l of o)if(l.type==="childList")for(const a of l.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&i(a)}).observe(document,{childList:!0,subtree:!0});function n(o){const l={};return o.integrity&&(l.integrity=o.integrity),o.referrerpolicy&&(l.referrerPolicy=o.referrerpolicy),o.crossorigin==="use-credentials"?l.credentials="include":o.crossorigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function i(o){if(o.ep)return;o.ep=!0;const l=n(o);fetch(o.href,l)}})();function J(){}function vl(t){return t()}function Io(){return Object.create(null)}function pe(t){t.forEach(vl)}function xl(t){return typeof t=="function"}function ke(t,e){return t!=t?e==e:t!==e||t&&typeof t=="object"||typeof t=="function"}let Un;function $l(t,e){return Un||(Un=document.createElement("a")),Un.href=e,t===Un.href}function er(t){return Object.keys(t).length===0}function tr(t,...e){if(t==null)return J;const n=t.subscribe(...e);return n.unsubscribe?()=>n.unsubscribe():n}function _l(t,e,n){t.$$.on_destroy.push(tr(e,n))}function r(t,e){t.appendChild(e)}function m(t,e,n){t.insertBefore(e,n||null)}function h(t){t.parentNode.removeChild(t)}function dt(t,e){for(let n=0;nt.removeEventListener(e,n,i)}function Xn(t){return function(e){return e.preventDefault(),t.call(this,e)}}function u(t,e,n){n==null?t.removeAttribute(e):t.getAttribute(e)!==n&&t.setAttribute(e,n)}function se(t){return t===""?null:+t}function ir(t){return Array.from(t.childNodes)}function x(t,e){e=""+e,t.wholeText!==e&&(t.data=e)}function G(t,e){t.value=e==null?"":e}function zt(t,e){for(let n=0;n{Vn.delete(t),i&&(n&&t.d(1),i())}),t.o(e)}else i&&i()}function Kn(t){t&&t.c()}function Vt(t,e,n,i){const{fragment:o,on_mount:l,on_destroy:a,after_update:f}=t.$$;o&&o.m(e,n),i||Lt(()=>{const c=l.map(vl).filter(xl);a?a.push(...c):pe(c),t.$$.on_mount=[]}),f.forEach(Lt)}function Gt(t,e){const n=t.$$;n.fragment!==null&&(pe(n.on_destroy),n.fragment&&n.fragment.d(e),n.on_destroy=n.fragment=null,n.ctx=[])}function ur(t,e){t.$$.dirty[0]===-1&&(Ut.push(t),rr(),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<{const g=y.length?y[0]:_;return p.ctx&&o(p.ctx[w],p.ctx[w]=g)&&(!p.skip_bound&&p.bound[w]&&p.bound[w](g),d&&ur(t,w)),_}):[],p.update(),d=!0,pe(p.before_update),p.fragment=i?i(p.ctx):!1,e.target){if(e.hydrate){const w=ir(e.target);p.fragment&&p.fragment.l(w),w.forEach(h)}else p.fragment&&p.fragment.c();e.intro&&Ee(t.$$.fragment),Vt(t,e.target,e.anchor,e.customElement),gl()}qt(c)}class Se{$destroy(){Gt(this,1),this.$destroy=J}$on(e,n){const i=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return i.push(n),()=>{const o=i.indexOf(n);o!==-1&&i.splice(o,1)}}$set(e){this.$$set&&!er(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}}const Ct=[];function yl(t,e=J){let n;const i=new Set;function o(f){if(ke(t,f)&&(t=f,n)){const c=!Ct.length;for(const p of i)p[1](),Ct.push(p,t);if(c){for(let p=0;p{i.delete(p),i.size===0&&(n(),n=null)}}return{set:o,update:l,subscribe:a}}var Ei=function(t,e){return Ei=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,i){n.__proto__=i}||function(n,i){for(var o in i)Object.prototype.hasOwnProperty.call(i,o)&&(n[o]=i[o])},Ei(t,e)};function ji(t,e){if(typeof e!="function"&&e!==null)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function n(){this.constructor=t}Ei(t,e),t.prototype=e===null?Object.create(e):(n.prototype=e.prototype,new n)}var ue=function(){return ue=Object.assign||function(t){for(var e,n=1,i=arguments.length;n0&&o[o.length-1])||d[0]!==6&&d[0]!==2)){a=0;continue}if(d[0]===3&&(!o||d[1]>o[0]&&d[1]@tauri-apps/api package. It's used as the main validation app, serving as the test bed of our +`;function El(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Os",message:{cmd:"platform"}})]})})}function mr(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Os",message:{cmd:"version"}})]})})}function vr(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Os",message:{cmd:"osType"}})]})})}function _r(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Os",message:{cmd:"arch"}})]})})}function br(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Os",message:{cmd:"tempdir"}})]})})}Object.freeze({__proto__:null,EOL:hr,platform:El,version:mr,type:vr,arch:_r,tempdir:br});function Wl(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"App",message:{cmd:"getAppVersion"}})]})})}function Dl(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"App",message:{cmd:"getAppName"}})]})})}function jl(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"App",message:{cmd:"getTauriVersion"}})]})})}Object.freeze({__proto__:null,getName:Dl,getVersion:Wl,getTauriVersion:jl});function Rl(t){return t===void 0&&(t=0),M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"Process",message:{cmd:"exit",exitCode:t}})]})})}function Fi(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Process",message:{cmd:"relaunch"}})]})})}Object.freeze({__proto__:null,exit:Rl,relaunch:Fi});function gr(t){let e,n,i,o,l,a,f,c,p,d,w,_,y,g,b,L,W,U,j,q,A,S,z,D,C,N;return{c(){e=s("p"),e.innerHTML=`This is a demo of Tauri's API capabilities using the @tauri-apps/api package. It's used as the main validation app, serving as the test bed of our development process. In the future, this app will be used on Tauri's integration - tests.`,n=v(),i=s("br"),o=v(),l=s("br"),u=v(),f=s("pre"),c=E("App name: "),p=s("code"),d=E(t[2]),k=E(` + tests.`,n=v(),i=s("br"),o=v(),l=s("br"),a=v(),f=s("pre"),c=E("App name: "),p=s("code"),d=E(t[2]),w=E(` App version: `),_=s("code"),y=E(t[0]),g=E(` Tauri version: `),b=s("code"),L=E(t[1]),W=E(` -`),U=v(),j=s("br"),q=v(),A=s("div"),S=s("button"),S.textContent="Close application",z=v(),D=s("button"),D.textContent="Relaunch application",a(S,"class","btn"),a(D,"class","btn"),a(A,"class","flex flex-wrap gap-1 shadow-")},m(B,Y){m(B,e,Y),m(B,n,Y),m(B,i,Y),m(B,o,Y),m(B,l,Y),m(B,u,Y),m(B,f,Y),r(f,c),r(f,p),r(p,d),r(f,k),r(f,_),r(_,y),r(f,g),r(f,b),r(b,L),r(f,W),m(B,U,Y),m(B,j,Y),m(B,q,Y),m(B,A,Y),r(A,S),r(A,z),r(A,D),C||(N=[O(S,"click",t[3]),O(D,"click",t[4])],C=!0)},p(B,[Y]){Y&4&&Z(d,B[2]),Y&1&&Z(y,B[0]),Y&2&&Z(L,B[1])},i:J,o:J,d(B){B&&h(e),B&&h(n),B&&h(i),B&&h(o),B&&h(l),B&&h(u),B&&h(f),B&&h(U),B&&h(j),B&&h(q),B&&h(A),C=!1,pe(N)}}}function wr(t,e,n){let i="0.0.0",o="0.0.0",l="Unknown";Dl().then(c=>{n(2,l=c)}),Wl().then(c=>{n(0,i=c)}),jl().then(c=>{n(1,o=c)});async function u(){await Rl()}async function f(){await Fi()}return[i,o,l,u,f]}class kr extends Se{constructor(e){super(),Ce(this,e,wr,yr,ke,{})}}function Hl(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Cli",message:{cmd:"cliMatches"}})]})})}Object.freeze({__proto__:null,getMatches:Hl});function Mr(t){let e,n,i,o,l,u,f,c,p,d,k,_,y;return{c(){e=s("p"),e.innerHTML=`This binary can be run from the terminal and takes the following arguments: +`),U=v(),j=s("br"),q=v(),A=s("div"),S=s("button"),S.textContent="Close application",z=v(),D=s("button"),D.textContent="Relaunch application",u(S,"class","btn"),u(D,"class","btn"),u(A,"class","flex flex-wrap gap-1 shadow-")},m(B,Y){m(B,e,Y),m(B,n,Y),m(B,i,Y),m(B,o,Y),m(B,l,Y),m(B,a,Y),m(B,f,Y),r(f,c),r(f,p),r(p,d),r(f,w),r(f,_),r(_,y),r(f,g),r(f,b),r(b,L),r(f,W),m(B,U,Y),m(B,j,Y),m(B,q,Y),m(B,A,Y),r(A,S),r(A,z),r(A,D),C||(N=[O(S,"click",t[3]),O(D,"click",t[4])],C=!0)},p(B,[Y]){Y&4&&x(d,B[2]),Y&1&&x(y,B[0]),Y&2&&x(L,B[1])},i:J,o:J,d(B){B&&h(e),B&&h(n),B&&h(i),B&&h(o),B&&h(l),B&&h(a),B&&h(f),B&&h(U),B&&h(j),B&&h(q),B&&h(A),C=!1,pe(N)}}}function yr(t,e,n){let i="0.0.0",o="0.0.0",l="Unknown";Dl().then(c=>{n(2,l=c)}),Wl().then(c=>{n(0,i=c)}),jl().then(c=>{n(1,o=c)});async function a(){await Rl()}async function f(){await Fi()}return[i,o,l,a,f]}class wr extends Se{constructor(e){super(),Ce(this,e,yr,gr,ke,{})}}function Hl(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Cli",message:{cmd:"cliMatches"}})]})})}Object.freeze({__proto__:null,getMatches:Hl});function kr(t){let e,n,i,o,l,a,f,c,p,d,w,_,y;return{c(){e=s("p"),e.innerHTML=`This binary can be run from the terminal and takes the following arguments:

  --config <PATH>
   --theme <light|dark|system>
   --verbose
- Additionally, it has a update --background subcommand.`,n=v(),i=s("br"),o=v(),l=s("div"),l.textContent="Note that the arguments are only parsed, not implemented.",u=v(),f=s("br"),c=v(),p=s("br"),d=v(),k=s("button"),k.textContent="Get matches",a(l,"class","note"),a(k,"class","btn"),a(k,"id","cli-matches")},m(g,b){m(g,e,b),m(g,n,b),m(g,i,b),m(g,o,b),m(g,l,b),m(g,u,b),m(g,f,b),m(g,c,b),m(g,p,b),m(g,d,b),m(g,k,b),_||(y=O(k,"click",t[0]),_=!0)},p:J,i:J,o:J,d(g){g&&h(e),g&&h(n),g&&h(i),g&&h(o),g&&h(l),g&&h(u),g&&h(f),g&&h(c),g&&h(p),g&&h(d),g&&h(k),_=!1,y()}}}function Tr(t,e,n){let{onMessage:i}=e;function o(){Hl().then(i).catch(i)}return t.$$set=l=>{"onMessage"in l&&n(1,i=l.onMessage)},[o,i]}class Cr extends Se{constructor(e){super(),Ce(this,e,Tr,Mr,ke,{onMessage:1})}}function Xt(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,Hi(t,null,e)]})})}function Fl(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,Sl(t,null,e)]})})}function ni(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,Cl(t,void 0,e)]})})}Object.freeze({__proto__:null,listen:Xt,once:Fl,emit:ni});function Sr(t){let e,n,i,o,l,u,f,c;return{c(){e=s("div"),n=s("button"),n.textContent="Call Log API",i=v(),o=s("button"),o.textContent="Call Request (async) API",l=v(),u=s("button"),u.textContent="Send event to Rust",a(n,"class","btn"),a(n,"id","log"),a(o,"class","btn"),a(o,"id","request"),a(u,"class","btn"),a(u,"id","event")},m(p,d){m(p,e,d),r(e,n),r(e,i),r(e,o),r(e,l),r(e,u),f||(c=[O(n,"click",t[0]),O(o,"click",t[1]),O(u,"click",t[2])],f=!0)},p:J,i:J,o:J,d(p){p&&h(e),f=!1,pe(c)}}}function zr(t,e,n){let{onMessage:i}=e,o;at(async()=>{o=await Xt("rust-event",i)}),Di(()=>{o&&o()});function l(){xn("log_operation",{event:"tauri-click",payload:"this payload is optional because we used Option in Rust"})}function u(){xn("perform_request",{endpoint:"dummy endpoint arg",body:{id:5,name:"test"}}).then(i).catch(i)}function f(){ni("js-event","this is the payload string")}return t.$$set=c=>{"onMessage"in c&&n(3,i=c.onMessage)},[l,u,f,i]}class Lr extends Se{constructor(e){super(),Ce(this,e,zr,Sr,ke,{onMessage:3})}}function Ii(t){return t===void 0&&(t={}),M(this,void 0,void 0,function(){return T(this,function(e){return typeof t=="object"&&Object.freeze(t),[2,P({__tauriModule:"Dialog",message:{cmd:"openDialog",options:t}})]})})}function Il(t){return t===void 0&&(t={}),M(this,void 0,void 0,function(){return T(this,function(e){return typeof t=="object"&&Object.freeze(t),[2,P({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:t}})]})})}function Ar(t,e){var n;return M(this,void 0,void 0,function(){var i;return T(this,function(o){return i=typeof e=="string"?{title:e}:e,[2,P({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:t.toString(),title:(n=i==null?void 0:i.title)===null||n===void 0?void 0:n.toString(),type:i==null?void 0:i.type}})]})})}function Ul(t,e){var n;return M(this,void 0,void 0,function(){var i;return T(this,function(o){return i=typeof e=="string"?{title:e}:e,[2,P({__tauriModule:"Dialog",message:{cmd:"askDialog",message:t.toString(),title:(n=i==null?void 0:i.title)===null||n===void 0?void 0:n.toString(),type:i==null?void 0:i.type}})]})})}function Pr(t,e){var n;return M(this,void 0,void 0,function(){var i;return T(this,function(o){return i=typeof e=="string"?{title:e}:e,[2,P({__tauriModule:"Dialog",message:{cmd:"confirmDialog",message:t.toString(),title:(n=i==null?void 0:i.title)===null||n===void 0?void 0:n.toString(),type:i==null?void 0:i.type}})]})})}Object.freeze({__proto__:null,open:Ii,save:Il,message:Ar,ask:Ul,confirm:Pr});var Pt;function Or(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"readTextFile",path:t,options:e}})]})})}function Ui(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){var n;return T(this,function(i){switch(i.label){case 0:return[4,P({__tauriModule:"Fs",message:{cmd:"readFile",path:t,options:e}})];case 1:return n=i.sent(),[2,Uint8Array.from(n)]}})})}function Wi(t,e,n){return M(this,void 0,void 0,function(){var i,o;return T(this,function(l){return typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t),i={path:"",contents:""},o=n,typeof t=="string"?i.path=t:(i.path=t.path,i.contents=t.contents),typeof e=="string"?i.contents=e!=null?e:"":o=e,[2,P({__tauriModule:"Fs",message:{cmd:"writeFile",path:i.path,contents:Array.from(new TextEncoder().encode(i.contents)),options:o}})]})})}function Er(t,e,n){return M(this,void 0,void 0,function(){var i,o;return T(this,function(l){return typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t),i={path:"",contents:[]},o=n,typeof t=="string"?i.path=t:(i.path=t.path,i.contents=t.contents),e&&"dir"in e?o=e:typeof t=="string"&&(i.contents=e!=null?e:[]),[2,P({__tauriModule:"Fs",message:{cmd:"writeFile",path:i.path,contents:Array.from(i.contents instanceof ArrayBuffer?new Uint8Array(i.contents):i.contents),options:o}})]})})}function Nl(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"readDir",path:t,options:e}})]})})}function Wr(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"createDir",path:t,options:e}})]})})}function Dr(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"removeDir",path:t,options:e}})]})})}function jr(t,e,n){return n===void 0&&(n={}),M(this,void 0,void 0,function(){return T(this,function(i){return[2,P({__tauriModule:"Fs",message:{cmd:"copyFile",source:t,destination:e,options:n}})]})})}function Rr(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"removeFile",path:t,options:e}})]})})}function Hr(t,e,n){return n===void 0&&(n={}),M(this,void 0,void 0,function(){return T(this,function(i){return[2,P({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:t,newPath:e,options:n}})]})})}(function(t){t[t.Audio=1]="Audio",t[t.Cache=2]="Cache",t[t.Config=3]="Config",t[t.Data=4]="Data",t[t.LocalData=5]="LocalData",t[t.Desktop=6]="Desktop",t[t.Document=7]="Document",t[t.Download=8]="Download",t[t.Executable=9]="Executable",t[t.Font=10]="Font",t[t.Home=11]="Home",t[t.Picture=12]="Picture",t[t.Public=13]="Public",t[t.Runtime=14]="Runtime",t[t.Template=15]="Template",t[t.Video=16]="Video",t[t.Resource=17]="Resource",t[t.App=18]="App",t[t.Log=19]="Log",t[t.Temp=20]="Temp"})(Pt||(Pt={}));Object.freeze({__proto__:null,get BaseDirectory(){return Pt},get Dir(){return Pt},readTextFile:Or,readBinaryFile:Ui,writeTextFile:Wi,writeFile:Wi,writeBinaryFile:Er,readDir:Nl,createDir:Wr,removeDir:Dr,copyFile:jr,removeFile:Rr,renameFile:Hr});function Fr(t){let e,n,i,o,l,u,f,c,p,d,k,_,y,g,b,L,W,U,j,q,A,S,z,D;return{c(){e=s("div"),n=s("input"),i=v(),o=s("input"),l=v(),u=s("br"),f=v(),c=s("div"),p=s("input"),d=v(),k=s("label"),k.textContent="Multiple",_=v(),y=s("div"),g=s("input"),b=v(),L=s("label"),L.textContent="Directory",W=v(),U=s("br"),j=v(),q=s("button"),q.textContent="Open dialog",A=v(),S=s("button"),S.textContent="Open save dialog",a(n,"class","input"),a(n,"id","dialog-default-path"),a(n,"placeholder","Default path"),a(o,"class","input"),a(o,"id","dialog-filter"),a(o,"placeholder","Extensions filter, comma-separated"),a(e,"class","flex gap-2 children:grow"),a(p,"type","checkbox"),a(p,"id","dialog-multiple"),a(k,"for","dialog-multiple"),a(g,"type","checkbox"),a(g,"id","dialog-directory"),a(L,"for","dialog-directory"),a(q,"class","btn"),a(q,"id","open-dialog"),a(S,"class","btn"),a(S,"id","save-dialog")},m(C,N){m(C,e,N),r(e,n),G(n,t[0]),r(e,i),r(e,o),G(o,t[1]),m(C,l,N),m(C,u,N),m(C,f,N),m(C,c,N),r(c,p),p.checked=t[2],r(c,d),r(c,k),m(C,_,N),m(C,y,N),r(y,g),g.checked=t[3],r(y,b),r(y,L),m(C,W,N),m(C,U,N),m(C,j,N),m(C,q,N),m(C,A,N),m(C,S,N),z||(D=[O(n,"input",t[8]),O(o,"input",t[9]),O(p,"change",t[10]),O(g,"change",t[11]),O(q,"click",t[4]),O(S,"click",t[5])],z=!0)},p(C,[N]){N&1&&n.value!==C[0]&&G(n,C[0]),N&2&&o.value!==C[1]&&G(o,C[1]),N&4&&(p.checked=C[2]),N&8&&(g.checked=C[3])},i:J,o:J,d(C){C&&h(e),C&&h(l),C&&h(u),C&&h(f),C&&h(c),C&&h(_),C&&h(y),C&&h(W),C&&h(U),C&&h(j),C&&h(q),C&&h(A),C&&h(S),z=!1,pe(D)}}}function Ir(t,e){var n=new Blob([t],{type:"application/octet-binary"}),i=new FileReader;i.onload=function(o){var l=o.target.result;e(l.substr(l.indexOf(",")+1))},i.readAsDataURL(n)}function Ur(t,e,n){let{onMessage:i}=e,{insecureRenderHtml:o}=e,l=null,u=null,f=!1,c=!1;function p(){Ii({title:"My wonderful open dialog",defaultPath:l,filters:u?[{name:"Tauri Example",extensions:u.split(",").map(b=>b.trim())}]:[],multiple:f,directory:c}).then(function(b){if(Array.isArray(b))i(b);else{var L=b,W=L.match(/\S+\.\S+$/g);Ui(L).then(function(U){W&&(L.includes(".png")||L.includes(".jpg"))?Ir(new Uint8Array(U),function(j){var q="data:image/png;base64,"+j;o('')}):i(b)}).catch(i(b))}}).catch(i)}function d(){Il({title:"My wonderful save dialog",defaultPath:l,filters:u?[{name:"Tauri Example",extensions:u.split(",").map(b=>b.trim())}]:[]}).then(i).catch(i)}function k(){l=this.value,n(0,l)}function _(){u=this.value,n(1,u)}function y(){f=this.checked,n(2,f)}function g(){c=this.checked,n(3,c)}return t.$$set=b=>{"onMessage"in b&&n(6,i=b.onMessage),"insecureRenderHtml"in b&&n(7,o=b.insecureRenderHtml)},[l,u,f,c,p,d,i,o,k,_,y,g]}class Nr extends Se{constructor(e){super(),Ce(this,e,Ur,Fr,ke,{onMessage:6,insecureRenderHtml:7})}}function qo(t,e,n){const i=t.slice();return i[9]=e[n],i}function Bo(t){let e,n=t[9][0]+"",i,o;return{c(){e=s("option"),i=E(n),e.__value=o=t[9][1],e.value=e.__value},m(l,u){m(l,e,u),r(e,i)},p:J,d(l){l&&h(e)}}}function qr(t){let e,n,i,o,l,u,f,c,p,d,k,_,y,g,b,L,W,U,j,q=t[2],A=[];for(let S=0;SisNaN(parseInt(_))).map(_=>[_,Pt[_]]);function c(){const _=l.match(/\S+\.\S+$/g),y={dir:Vo()};(_?Ui(l,y):Nl(l,y)).then(function(b){if(_)if(l.includes(".png")||l.includes(".jpg"))Br(new Uint8Array(b),function(L){const W="data:image/png;base64,"+L;o('')});else{const L=String.fromCharCode.apply(null,b);o(''),setTimeout(()=>{const W=document.getElementById("file-response");W.value=L,document.getElementById("file-save").addEventListener("click",function(){Wi(l,W.value,{dir:Vo()}).catch(i)})})}else i(b)}).catch(i)}function p(){n(1,u.src=wl(l),u)}function d(){l=this.value,n(0,l)}function k(_){Yn[_?"unshift":"push"](()=>{u=_,n(1,u)})}return t.$$set=_=>{"onMessage"in _&&n(5,i=_.onMessage),"insecureRenderHtml"in _&&n(6,o=_.insecureRenderHtml)},[l,u,f,c,p,i,o,d,k]}class Gr extends Se{constructor(e){super(),Ce(this,e,Vr,qr,ke,{onMessage:5,insecureRenderHtml:6})}}var Ot;(function(t){t[t.JSON=1]="JSON",t[t.Text=2]="Text",t[t.Binary=3]="Binary"})(Ot||(Ot={}));var Jn=function(){function t(e,n){this.type=e,this.payload=n}return t.form=function(e){var n={};for(var i in e){var o=e[i],l=void 0;l=typeof o=="string"?o:o instanceof Uint8Array||Array.isArray(o)?Array.from(o):typeof o.file=="string"?{file:o.file,mime:o.mime,fileName:o.fileName}:{file:Array.from(o.file),mime:o.mime,fileName:o.fileName},n[i]=l}return new t("Form",n)},t.json=function(e){return new t("Json",e)},t.text=function(e){return new t("Text",e)},t.bytes=function(e){return new t("Bytes",Array.from(e instanceof ArrayBuffer?new Uint8Array(e):e))},t}(),ql=function(t){this.url=t.url,this.status=t.status,this.ok=this.status>=200&&this.status<300,this.headers=t.headers,this.rawHeaders=t.rawHeaders,this.data=t.data},Bl=function(){function t(e){this.id=e}return t.prototype.drop=function(){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}})]})})},t.prototype.request=function(e){return M(this,void 0,void 0,function(){var n;return T(this,function(i){return(n=!e.responseType||e.responseType===Ot.JSON)&&(e.responseType=Ot.Text),[2,P({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:e}}).then(function(o){var l=new ql(o);if(n){try{l.data=JSON.parse(l.data)}catch(u){if(l.ok&&l.data==="")l.data={};else if(l.ok)throw Error("Failed to parse response `".concat(l.data,"` as JSON: ").concat(u,";\n try setting the `responseType` option to `ResponseType.Text` or `ResponseType.Binary` if the API does not return a JSON response."))}return l}return l})]})})},t.prototype.get=function(e,n){return M(this,void 0,void 0,function(){return T(this,function(i){return[2,this.request(ue({method:"GET",url:e},n))]})})},t.prototype.post=function(e,n,i){return M(this,void 0,void 0,function(){return T(this,function(o){return[2,this.request(ue({method:"POST",url:e,body:n},i))]})})},t.prototype.put=function(e,n,i){return M(this,void 0,void 0,function(){return T(this,function(o){return[2,this.request(ue({method:"PUT",url:e,body:n},i))]})})},t.prototype.patch=function(e,n){return M(this,void 0,void 0,function(){return T(this,function(i){return[2,this.request(ue({method:"PATCH",url:e},n))]})})},t.prototype.delete=function(e,n){return M(this,void 0,void 0,function(){return T(this,function(i){return[2,this.request(ue({method:"DELETE",url:e},n))]})})},t}();function Zn(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"Http",message:{cmd:"createClient",options:t}}).then(function(n){return new Bl(n)})]})})}var Ai=null;function Jr(t,e){var n;return M(this,void 0,void 0,function(){return T(this,function(i){switch(i.label){case 0:return Ai!==null?[3,2]:[4,Zn()];case 1:Ai=i.sent(),i.label=2;case 2:return[2,Ai.request(ue({url:t,method:(n=e==null?void 0:e.method)!==null&&n!==void 0?n:"GET"},e))]}})})}Object.freeze({__proto__:null,getClient:Zn,fetch:Jr,Body:Jn,Client:Bl,Response:ql,get ResponseType(){return Ot}});function Go(t,e,n){const i=t.slice();return i[12]=e[n],i[14]=n,i}function Jo(t){let e,n,i,o,l,u,f,c,p,d,k,_,y,g,b,L,W,U=t[5],j=[];for(let z=0;zFe(j[z],1,1,()=>{j[z]=null});let A=!t[3]&&xo(),S=!t[3]&&t[8]&&Qo();return{c(){e=s("span"),n=s("span"),i=E(t[6]),o=v(),l=s("ul");for(let z=0;z{d[g]=null}),ti(),l=d[o],l?l.p(_,y):(l=d[o]=p[o](_),l.c()),Ee(l,1),l.m(e,u))},i(_){f||(Ee(l),f=!0)},o(_){Fe(l),f=!1},d(_){_&&h(e),c&&c.d(),d[o].d()}}}function xo(t){let e;return{c(){e=s("span"),e.textContent=",",a(e,"class","comma svelte-gbh3pt")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Qo(t){let e;return{c(){e=s("span"),e.textContent=",",a(e,"class","comma svelte-gbh3pt")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Kr(t){let e,n,i=t[5].length&&Jo(t);return{c(){i&&i.c(),e=$n()},m(o,l){i&&i.m(o,l),m(o,e,l),n=!0},p(o,[l]){o[5].length?i?(i.p(o,l),l&32&&Ee(i,1)):(i=Jo(o),i.c(),Ee(i,1),i.m(e.parentNode,e)):i&&(ei(),Fe(i,1,1,()=>{i=null}),ti())},i(o){n||(Ee(i),n=!0)},o(o){Fe(i),n=!1},d(o){i&&i.d(o),o&&h(e)}}}const xr="...";function Qr(t,e,n){let{json:i}=e,{depth:o=1/0}=e,{_lvl:l=0}=e,{_last:u=!0}=e;const f=b=>b===null?"null":typeof b;let c,p,d,k,_;const y=b=>{switch(f(b)){case"string":return`"${b}"`;case"function":return"f () {...}";case"symbol":return b.toString();default:return b}},g=()=>{n(8,_=!_)};return t.$$set=b=>{"json"in b&&n(0,i=b.json),"depth"in b&&n(1,o=b.depth),"_lvl"in b&&n(2,l=b._lvl),"_last"in b&&n(3,u=b._last)},t.$$.update=()=>{t.$$.dirty&17&&(n(5,c=f(i)==="object"?Object.keys(i):[]),n(4,p=Array.isArray(i)),n(6,d=p?"[":"{"),n(7,k=p?"]":"}")),t.$$.dirty&6&&n(8,_=ot[9].call(n)),a(k,"class","input h-auto w-100%"),a(k,"id","request-body"),a(k,"placeholder","Request body"),a(k,"rows","5"),a(b,"class","btn"),a(b,"id","make-request"),a(S,"class","input"),a(D,"class","input"),a(A,"class","flex gap-2 children:grow"),a($,"type","checkbox"),a(x,"class","btn"),a(x,"type","button")},m(R,V){m(R,e,V),r(e,n),r(n,i),r(n,o),r(n,l),r(n,u),r(n,f),zt(n,t[0]),r(e,c),r(e,p),r(e,d),r(e,k),G(k,t[1]),r(e,_),r(e,y),r(e,g),r(e,b),m(R,L,V),m(R,W,V),m(R,U,V),m(R,j,V),m(R,q,V),m(R,A,V),r(A,S),G(S,t[2]),r(A,z),r(A,D),G(D,t[3]),m(R,C,V),m(R,N,V),m(R,B,V),m(R,Y,V),r(Y,$),$.checked=t[5],r(Y,_e),m(R,te,V),m(R,oe,V),m(R,K,V),m(R,be,V),m(R,H,V),m(R,x,V),m(R,le,V),m(R,ae,V),m(R,ee,V),m(R,ve,V),m(R,re,V),Vt(ge,R,V),ne=!0,Me||(Le=[O(n,"change",t[9]),O(k,"input",t[10]),O(e,"submit",Xn(t[6])),O(S,"input",t[11]),O(D,"input",t[12]),O($,"change",t[13]),O(x,"click",t[7])],Me=!0)},p(R,[V]){V&1&&zt(n,R[0]),V&2&&G(k,R[1]),V&4&&S.value!==R[2]&&G(S,R[2]),V&8&&D.value!==R[3]&&G(D,R[3]),V&32&&($.checked=R[5]);const Re={};V&16&&(Re.json=R[4]),ge.$set(Re)},i(R){ne||(Ee(ge.$$.fragment,R),ne=!0)},o(R){Fe(ge.$$.fragment,R),ne=!1},d(R){R&&h(e),R&&h(L),R&&h(W),R&&h(U),R&&h(j),R&&h(q),R&&h(A),R&&h(C),R&&h(N),R&&h(B),R&&h(Y),R&&h(te),R&&h(oe),R&&h(K),R&&h(be),R&&h(H),R&&h(x),R&&h(le),R&&h(ae),R&&h(ee),R&&h(ve),R&&h(re),Gt(ge,R),Me=!1,pe(Le)}}}function $r(t,e,n){let i="GET",o="",{onMessage:l}=e;async function u(){const W=await Zn().catch(q=>{throw l(q),q}),j={url:"http://localhost:3003",method:i||"GET"||"GET"};o.startsWith("{")&&o.endsWith("}")||o.startsWith("[")&&o.endsWith("]")?j.body=Jn.json(JSON.parse(o)):o!==""&&(j.body=Jn.text(o)),W.request(j).then(l).catch(l)}let f="baz",c="qux",p=null,d=!0;async function k(){const W=await Zn().catch(U=>{throw l(U),U});n(4,p=await W.request({url:"http://localhost:3003",method:"POST",body:Jn.form({foo:f,bar:c}),headers:d?{"Content-Type":"multipart/form-data"}:void 0,responseType:Ot.Text}))}function _(){i=Pi(this),n(0,i)}function y(){o=this.value,n(1,o)}function g(){f=this.value,n(2,f)}function b(){c=this.value,n(3,c)}function L(){d=this.checked,n(5,d)}return t.$$set=W=>{"onMessage"in W&&n(8,l=W.onMessage)},[i,o,f,c,p,d,u,k,l,_,y,g,b,L]}class es extends Se{constructor(e){super(),Ce(this,e,$r,Zr,ke,{onMessage:8})}}function ts(t){let e,n,i;return{c(){e=s("button"),e.textContent="Send test notification",a(e,"class","btn"),a(e,"id","notification")},m(o,l){m(o,e,l),n||(i=O(e,"click",ns),n=!0)},p:J,i:J,o:J,d(o){o&&h(e),n=!1,i()}}}function ns(){new Notification("Notification title",{body:"This is the notification body"})}function is(t,e,n){let{onMessage:i}=e;return t.$$set=o=>{"onMessage"in o&&n(0,i=o.onMessage)},[i]}class os extends Se{constructor(e){super(),Ce(this,e,is,ts,ke,{onMessage:0})}}function Zo(t,e,n){const i=t.slice();return i[65]=e[n],i}function $o(t,e,n){const i=t.slice();return i[68]=e[n],i}function el(t){let e,n,i,o,l,u,f=Object.keys(t[1]),c=[];for(let p=0;pt[38].call(i))},m(p,d){m(p,e,d),m(p,n,d),m(p,i,d),r(i,o);for(let k=0;kupdate --background subcommand.`,n=v(),i=s("br"),o=v(),l=s("div"),l.textContent="Note that the arguments are only parsed, not implemented.",a=v(),f=s("br"),c=v(),p=s("br"),d=v(),w=s("button"),w.textContent="Get matches",u(l,"class","note"),u(w,"class","btn"),u(w,"id","cli-matches")},m(g,b){m(g,e,b),m(g,n,b),m(g,i,b),m(g,o,b),m(g,l,b),m(g,a,b),m(g,f,b),m(g,c,b),m(g,p,b),m(g,d,b),m(g,w,b),_||(y=O(w,"click",t[0]),_=!0)},p:J,i:J,o:J,d(g){g&&h(e),g&&h(n),g&&h(i),g&&h(o),g&&h(l),g&&h(a),g&&h(f),g&&h(c),g&&h(p),g&&h(d),g&&h(w),_=!1,y()}}}function Mr(t,e,n){let{onMessage:i}=e;function o(){Hl().then(i).catch(i)}return t.$$set=l=>{"onMessage"in l&&n(1,i=l.onMessage)},[o,i]}class Tr extends Se{constructor(e){super(),Ce(this,e,Mr,kr,ke,{onMessage:1})}}function Xt(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,Hi(t,null,e)]})})}function Fl(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,Sl(t,null,e)]})})}function ni(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,Cl(t,void 0,e)]})})}Object.freeze({__proto__:null,listen:Xt,once:Fl,emit:ni});function Cr(t){let e,n,i,o,l,a,f,c;return{c(){e=s("div"),n=s("button"),n.textContent="Call Log API",i=v(),o=s("button"),o.textContent="Call Request (async) API",l=v(),a=s("button"),a.textContent="Send event to Rust",u(n,"class","btn"),u(n,"id","log"),u(o,"class","btn"),u(o,"id","request"),u(a,"class","btn"),u(a,"id","event")},m(p,d){m(p,e,d),r(e,n),r(e,i),r(e,o),r(e,l),r(e,a),f||(c=[O(n,"click",t[0]),O(o,"click",t[1]),O(a,"click",t[2])],f=!0)},p:J,i:J,o:J,d(p){p&&h(e),f=!1,pe(c)}}}function Sr(t,e,n){let{onMessage:i}=e,o;at(async()=>{o=await Xt("rust-event",i)}),Di(()=>{o&&o()});function l(){Qn("log_operation",{event:"tauri-click",payload:"this payload is optional because we used Option in Rust"})}function a(){Qn("perform_request",{endpoint:"dummy endpoint arg",body:{id:5,name:"test"}}).then(i).catch(i)}function f(){ni("js-event","this is the payload string")}return t.$$set=c=>{"onMessage"in c&&n(3,i=c.onMessage)},[l,a,f,i]}class zr extends Se{constructor(e){super(),Ce(this,e,Sr,Cr,ke,{onMessage:3})}}function Ii(t){return t===void 0&&(t={}),M(this,void 0,void 0,function(){return T(this,function(e){return typeof t=="object"&&Object.freeze(t),[2,P({__tauriModule:"Dialog",message:{cmd:"openDialog",options:t}})]})})}function Il(t){return t===void 0&&(t={}),M(this,void 0,void 0,function(){return T(this,function(e){return typeof t=="object"&&Object.freeze(t),[2,P({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:t}})]})})}function Lr(t,e){var n;return M(this,void 0,void 0,function(){var i;return T(this,function(o){return i=typeof e=="string"?{title:e}:e,[2,P({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:t.toString(),title:(n=i==null?void 0:i.title)===null||n===void 0?void 0:n.toString(),type:i==null?void 0:i.type}})]})})}function Ul(t,e){var n;return M(this,void 0,void 0,function(){var i;return T(this,function(o){return i=typeof e=="string"?{title:e}:e,[2,P({__tauriModule:"Dialog",message:{cmd:"askDialog",message:t.toString(),title:(n=i==null?void 0:i.title)===null||n===void 0?void 0:n.toString(),type:i==null?void 0:i.type}})]})})}function Ar(t,e){var n;return M(this,void 0,void 0,function(){var i;return T(this,function(o){return i=typeof e=="string"?{title:e}:e,[2,P({__tauriModule:"Dialog",message:{cmd:"confirmDialog",message:t.toString(),title:(n=i==null?void 0:i.title)===null||n===void 0?void 0:n.toString(),type:i==null?void 0:i.type}})]})})}Object.freeze({__proto__:null,open:Ii,save:Il,message:Lr,ask:Ul,confirm:Ar});var Pt;function Pr(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"readTextFile",path:t,options:e}})]})})}function Ui(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){var n;return T(this,function(i){switch(i.label){case 0:return[4,P({__tauriModule:"Fs",message:{cmd:"readFile",path:t,options:e}})];case 1:return n=i.sent(),[2,Uint8Array.from(n)]}})})}function Wi(t,e,n){return M(this,void 0,void 0,function(){var i,o;return T(this,function(l){return typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t),i={path:"",contents:""},o=n,typeof t=="string"?i.path=t:(i.path=t.path,i.contents=t.contents),typeof e=="string"?i.contents=e!=null?e:"":o=e,[2,P({__tauriModule:"Fs",message:{cmd:"writeFile",path:i.path,contents:Array.from(new TextEncoder().encode(i.contents)),options:o}})]})})}function Or(t,e,n){return M(this,void 0,void 0,function(){var i,o;return T(this,function(l){return typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t),i={path:"",contents:[]},o=n,typeof t=="string"?i.path=t:(i.path=t.path,i.contents=t.contents),e&&"dir"in e?o=e:typeof t=="string"&&(i.contents=e!=null?e:[]),[2,P({__tauriModule:"Fs",message:{cmd:"writeFile",path:i.path,contents:Array.from(i.contents instanceof ArrayBuffer?new Uint8Array(i.contents):i.contents),options:o}})]})})}function Nl(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"readDir",path:t,options:e}})]})})}function Er(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"createDir",path:t,options:e}})]})})}function Wr(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"removeDir",path:t,options:e}})]})})}function Dr(t,e,n){return n===void 0&&(n={}),M(this,void 0,void 0,function(){return T(this,function(i){return[2,P({__tauriModule:"Fs",message:{cmd:"copyFile",source:t,destination:e,options:n}})]})})}function jr(t,e){return e===void 0&&(e={}),M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"Fs",message:{cmd:"removeFile",path:t,options:e}})]})})}function Rr(t,e,n){return n===void 0&&(n={}),M(this,void 0,void 0,function(){return T(this,function(i){return[2,P({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:t,newPath:e,options:n}})]})})}(function(t){t[t.Audio=1]="Audio",t[t.Cache=2]="Cache",t[t.Config=3]="Config",t[t.Data=4]="Data",t[t.LocalData=5]="LocalData",t[t.Desktop=6]="Desktop",t[t.Document=7]="Document",t[t.Download=8]="Download",t[t.Executable=9]="Executable",t[t.Font=10]="Font",t[t.Home=11]="Home",t[t.Picture=12]="Picture",t[t.Public=13]="Public",t[t.Runtime=14]="Runtime",t[t.Template=15]="Template",t[t.Video=16]="Video",t[t.Resource=17]="Resource",t[t.App=18]="App",t[t.Log=19]="Log",t[t.Temp=20]="Temp"})(Pt||(Pt={}));Object.freeze({__proto__:null,get BaseDirectory(){return Pt},get Dir(){return Pt},readTextFile:Pr,readBinaryFile:Ui,writeTextFile:Wi,writeFile:Wi,writeBinaryFile:Or,readDir:Nl,createDir:Er,removeDir:Wr,copyFile:Dr,removeFile:jr,renameFile:Rr});function Hr(t){let e,n,i,o,l,a,f,c,p,d,w,_,y,g,b,L,W,U,j,q,A,S,z,D;return{c(){e=s("div"),n=s("input"),i=v(),o=s("input"),l=v(),a=s("br"),f=v(),c=s("div"),p=s("input"),d=v(),w=s("label"),w.textContent="Multiple",_=v(),y=s("div"),g=s("input"),b=v(),L=s("label"),L.textContent="Directory",W=v(),U=s("br"),j=v(),q=s("button"),q.textContent="Open dialog",A=v(),S=s("button"),S.textContent="Open save dialog",u(n,"class","input"),u(n,"id","dialog-default-path"),u(n,"placeholder","Default path"),u(o,"class","input"),u(o,"id","dialog-filter"),u(o,"placeholder","Extensions filter, comma-separated"),u(e,"class","flex gap-2 children:grow"),u(p,"type","checkbox"),u(p,"id","dialog-multiple"),u(w,"for","dialog-multiple"),u(g,"type","checkbox"),u(g,"id","dialog-directory"),u(L,"for","dialog-directory"),u(q,"class","btn"),u(q,"id","open-dialog"),u(S,"class","btn"),u(S,"id","save-dialog")},m(C,N){m(C,e,N),r(e,n),G(n,t[0]),r(e,i),r(e,o),G(o,t[1]),m(C,l,N),m(C,a,N),m(C,f,N),m(C,c,N),r(c,p),p.checked=t[2],r(c,d),r(c,w),m(C,_,N),m(C,y,N),r(y,g),g.checked=t[3],r(y,b),r(y,L),m(C,W,N),m(C,U,N),m(C,j,N),m(C,q,N),m(C,A,N),m(C,S,N),z||(D=[O(n,"input",t[8]),O(o,"input",t[9]),O(p,"change",t[10]),O(g,"change",t[11]),O(q,"click",t[4]),O(S,"click",t[5])],z=!0)},p(C,[N]){N&1&&n.value!==C[0]&&G(n,C[0]),N&2&&o.value!==C[1]&&G(o,C[1]),N&4&&(p.checked=C[2]),N&8&&(g.checked=C[3])},i:J,o:J,d(C){C&&h(e),C&&h(l),C&&h(a),C&&h(f),C&&h(c),C&&h(_),C&&h(y),C&&h(W),C&&h(U),C&&h(j),C&&h(q),C&&h(A),C&&h(S),z=!1,pe(D)}}}function Fr(t,e){var n=new Blob([t],{type:"application/octet-binary"}),i=new FileReader;i.onload=function(o){var l=o.target.result;e(l.substr(l.indexOf(",")+1))},i.readAsDataURL(n)}function Ir(t,e,n){let{onMessage:i}=e,{insecureRenderHtml:o}=e,l=null,a=null,f=!1,c=!1;function p(){Ii({title:"My wonderful open dialog",defaultPath:l,filters:a?[{name:"Tauri Example",extensions:a.split(",").map(b=>b.trim())}]:[],multiple:f,directory:c}).then(function(b){if(Array.isArray(b))i(b);else{var L=b,W=L.match(/\S+\.\S+$/g);Ui(L).then(function(U){W&&(L.includes(".png")||L.includes(".jpg"))?Fr(new Uint8Array(U),function(j){var q="data:image/png;base64,"+j;o('')}):i(b)}).catch(i(b))}}).catch(i)}function d(){Il({title:"My wonderful save dialog",defaultPath:l,filters:a?[{name:"Tauri Example",extensions:a.split(",").map(b=>b.trim())}]:[]}).then(i).catch(i)}function w(){l=this.value,n(0,l)}function _(){a=this.value,n(1,a)}function y(){f=this.checked,n(2,f)}function g(){c=this.checked,n(3,c)}return t.$$set=b=>{"onMessage"in b&&n(6,i=b.onMessage),"insecureRenderHtml"in b&&n(7,o=b.insecureRenderHtml)},[l,a,f,c,p,d,i,o,w,_,y,g]}class Ur extends Se{constructor(e){super(),Ce(this,e,Ir,Hr,ke,{onMessage:6,insecureRenderHtml:7})}}function qo(t,e,n){const i=t.slice();return i[9]=e[n],i}function Bo(t){let e,n=t[9][0]+"",i;return{c(){e=s("option"),i=E(n),e.__value=t[9][1],e.value=e.__value},m(o,l){m(o,e,l),r(e,i)},p:J,d(o){o&&h(e)}}}function Nr(t){let e,n,i,o,l,a,f,c,p,d,w,_,y,g,b,L,W,U,j,q=t[2],A=[];for(let S=0;SisNaN(parseInt(_))).map(_=>[_,Pt[_]]);function c(){const _=l.match(/\S+\.\S+$/g),y={dir:Vo()};(_?Ui(l,y):Nl(l,y)).then(function(b){if(_)if(l.includes(".png")||l.includes(".jpg"))qr(new Uint8Array(b),function(L){const W="data:image/png;base64,"+L;o('')});else{const L=String.fromCharCode.apply(null,b);o(''),setTimeout(()=>{const W=document.getElementById("file-response");W.value=L,document.getElementById("file-save").addEventListener("click",function(){Wi(l,W.value,{dir:Vo()}).catch(i)})})}else i(b)}).catch(i)}function p(){n(1,a.src=wl(l),a)}function d(){l=this.value,n(0,l)}function w(_){Yn[_?"unshift":"push"](()=>{a=_,n(1,a)})}return t.$$set=_=>{"onMessage"in _&&n(5,i=_.onMessage),"insecureRenderHtml"in _&&n(6,o=_.insecureRenderHtml)},[l,a,f,c,p,i,o,d,w]}class Vr extends Se{constructor(e){super(),Ce(this,e,Br,Nr,ke,{onMessage:5,insecureRenderHtml:6})}}var Ot;(function(t){t[t.JSON=1]="JSON",t[t.Text=2]="Text",t[t.Binary=3]="Binary"})(Ot||(Ot={}));var Jn=function(){function t(e,n){this.type=e,this.payload=n}return t.form=function(e){var n={};for(var i in e){var o=e[i],l=void 0;l=typeof o=="string"?o:o instanceof Uint8Array||Array.isArray(o)?Array.from(o):typeof o.file=="string"?{file:o.file,mime:o.mime,fileName:o.fileName}:{file:Array.from(o.file),mime:o.mime,fileName:o.fileName},n[i]=l}return new t("Form",n)},t.json=function(e){return new t("Json",e)},t.text=function(e){return new t("Text",e)},t.bytes=function(e){return new t("Bytes",Array.from(e instanceof ArrayBuffer?new Uint8Array(e):e))},t}(),ql=function(t){this.url=t.url,this.status=t.status,this.ok=this.status>=200&&this.status<300,this.headers=t.headers,this.rawHeaders=t.rawHeaders,this.data=t.data},Bl=function(){function t(e){this.id=e}return t.prototype.drop=function(){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}})]})})},t.prototype.request=function(e){return M(this,void 0,void 0,function(){var n;return T(this,function(i){return(n=!e.responseType||e.responseType===Ot.JSON)&&(e.responseType=Ot.Text),[2,P({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:e}}).then(function(o){var l=new ql(o);if(n){try{l.data=JSON.parse(l.data)}catch(a){if(l.ok&&l.data==="")l.data={};else if(l.ok)throw Error("Failed to parse response `".concat(l.data,"` as JSON: ").concat(a,";\n try setting the `responseType` option to `ResponseType.Text` or `ResponseType.Binary` if the API does not return a JSON response."))}return l}return l})]})})},t.prototype.get=function(e,n){return M(this,void 0,void 0,function(){return T(this,function(i){return[2,this.request(ue({method:"GET",url:e},n))]})})},t.prototype.post=function(e,n,i){return M(this,void 0,void 0,function(){return T(this,function(o){return[2,this.request(ue({method:"POST",url:e,body:n},i))]})})},t.prototype.put=function(e,n,i){return M(this,void 0,void 0,function(){return T(this,function(o){return[2,this.request(ue({method:"PUT",url:e,body:n},i))]})})},t.prototype.patch=function(e,n){return M(this,void 0,void 0,function(){return T(this,function(i){return[2,this.request(ue({method:"PATCH",url:e},n))]})})},t.prototype.delete=function(e,n){return M(this,void 0,void 0,function(){return T(this,function(i){return[2,this.request(ue({method:"DELETE",url:e},n))]})})},t}();function xn(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"Http",message:{cmd:"createClient",options:t}}).then(function(n){return new Bl(n)})]})})}var Ai=null;function Gr(t,e){var n;return M(this,void 0,void 0,function(){return T(this,function(i){switch(i.label){case 0:return Ai!==null?[3,2]:[4,xn()];case 1:Ai=i.sent(),i.label=2;case 2:return[2,Ai.request(ue({url:t,method:(n=e==null?void 0:e.method)!==null&&n!==void 0?n:"GET"},e))]}})})}Object.freeze({__proto__:null,getClient:xn,fetch:Gr,Body:Jn,Client:Bl,Response:ql,get ResponseType(){return Ot}});function Go(t,e,n){const i=t.slice();return i[12]=e[n],i[14]=n,i}function Jo(t){let e,n,i,o,l,a,f,c,p,d,w,_,y,g,b,L,W,U=t[5],j=[];for(let z=0;zFe(j[z],1,1,()=>{j[z]=null});let A=!t[3]&&Qo(),S=!t[3]&&t[8]&&Zo();return{c(){e=s("span"),n=s("span"),i=E(t[6]),o=v(),l=s("ul");for(let z=0;z{d[g]=null}),ti(),l=d[o],l?l.p(_,y):(l=d[o]=p[o](_),l.c()),Ee(l,1),l.m(e,a))},i(_){f||(Ee(l),f=!0)},o(_){Fe(l),f=!1},d(_){_&&h(e),c&&c.d(),d[o].d()}}}function Qo(t){let e;return{c(){e=s("span"),e.textContent=",",u(e,"class","comma svelte-gbh3pt")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Zo(t){let e;return{c(){e=s("span"),e.textContent=",",u(e,"class","comma svelte-gbh3pt")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Yr(t){let e,n,i=t[5].length&&Jo(t);return{c(){i&&i.c(),e=$n()},m(o,l){i&&i.m(o,l),m(o,e,l),n=!0},p(o,[l]){o[5].length?i?(i.p(o,l),l&32&&Ee(i,1)):(i=Jo(o),i.c(),Ee(i,1),i.m(e.parentNode,e)):i&&(ei(),Fe(i,1,1,()=>{i=null}),ti())},i(o){n||(Ee(i),n=!0)},o(o){Fe(i),n=!1},d(o){i&&i.d(o),o&&h(e)}}}const Kr="...";function Qr(t,e,n){let{json:i}=e,{depth:o=1/0}=e,{_lvl:l=0}=e,{_last:a=!0}=e;const f=b=>b===null?"null":typeof b;let c,p,d,w,_;const y=b=>{switch(f(b)){case"string":return`"${b}"`;case"function":return"f () {...}";case"symbol":return b.toString();default:return b}},g=()=>{n(8,_=!_)};return t.$$set=b=>{"json"in b&&n(0,i=b.json),"depth"in b&&n(1,o=b.depth),"_lvl"in b&&n(2,l=b._lvl),"_last"in b&&n(3,a=b._last)},t.$$.update=()=>{t.$$.dirty&17&&(n(5,c=f(i)==="object"?Object.keys(i):[]),n(4,p=Array.isArray(i)),n(6,d=p?"[":"{"),n(7,w=p?"]":"}")),t.$$.dirty&6&&n(8,_=ot[9].call(n)),u(w,"class","input h-auto w-100%"),u(w,"id","request-body"),u(w,"placeholder","Request body"),u(w,"rows","5"),u(b,"class","btn"),u(b,"id","make-request"),u(S,"class","input"),u(D,"class","input"),u(A,"class","flex gap-2 children:grow"),u($,"type","checkbox"),u(Q,"class","btn"),u(Q,"type","button")},m(R,V){m(R,e,V),r(e,n),r(n,i),r(n,o),r(n,l),r(n,a),r(n,f),zt(n,t[0]),r(e,c),r(e,p),r(e,d),r(e,w),G(w,t[1]),r(e,_),r(e,y),r(e,g),r(e,b),m(R,L,V),m(R,W,V),m(R,U,V),m(R,j,V),m(R,q,V),m(R,A,V),r(A,S),G(S,t[2]),r(A,z),r(A,D),G(D,t[3]),m(R,C,V),m(R,N,V),m(R,B,V),m(R,Y,V),r(Y,$),$.checked=t[5],r(Y,_e),m(R,te,V),m(R,oe,V),m(R,K,V),m(R,be,V),m(R,H,V),m(R,Q,V),m(R,le,V),m(R,ae,V),m(R,ee,V),m(R,ve,V),m(R,re,V),Vt(ge,R,V),ne=!0,Me||(Le=[O(n,"change",t[9]),O(w,"input",t[10]),O(e,"submit",Xn(t[6])),O(S,"input",t[11]),O(D,"input",t[12]),O($,"change",t[13]),O(Q,"click",t[7])],Me=!0)},p(R,[V]){V&1&&zt(n,R[0]),V&2&&G(w,R[1]),V&4&&S.value!==R[2]&&G(S,R[2]),V&8&&D.value!==R[3]&&G(D,R[3]),V&32&&($.checked=R[5]);const Re={};V&16&&(Re.json=R[4]),ge.$set(Re)},i(R){ne||(Ee(ge.$$.fragment,R),ne=!0)},o(R){Fe(ge.$$.fragment,R),ne=!1},d(R){R&&h(e),R&&h(L),R&&h(W),R&&h(U),R&&h(j),R&&h(q),R&&h(A),R&&h(C),R&&h(N),R&&h(B),R&&h(Y),R&&h(te),R&&h(oe),R&&h(K),R&&h(be),R&&h(H),R&&h(Q),R&&h(le),R&&h(ae),R&&h(ee),R&&h(ve),R&&h(re),Gt(ge,R),Me=!1,pe(Le)}}}function xr(t,e,n){let i="GET",o="",{onMessage:l}=e;async function a(){const W=await xn().catch(q=>{throw l(q),q}),j={url:"http://localhost:3003",method:i||"GET"||"GET"};o.startsWith("{")&&o.endsWith("}")||o.startsWith("[")&&o.endsWith("]")?j.body=Jn.json(JSON.parse(o)):o!==""&&(j.body=Jn.text(o)),W.request(j).then(l).catch(l)}let f="baz",c="qux",p=null,d=!0;async function w(){const W=await xn().catch(U=>{throw l(U),U});n(4,p=await W.request({url:"http://localhost:3003",method:"POST",body:Jn.form({foo:f,bar:c}),headers:d?{"Content-Type":"multipart/form-data"}:void 0,responseType:Ot.Text}))}function _(){i=Pi(this),n(0,i)}function y(){o=this.value,n(1,o)}function g(){f=this.value,n(2,f)}function b(){c=this.value,n(3,c)}function L(){d=this.checked,n(5,d)}return t.$$set=W=>{"onMessage"in W&&n(8,l=W.onMessage)},[i,o,f,c,p,d,a,w,l,_,y,g,b,L]}class $r extends Se{constructor(e){super(),Ce(this,e,xr,Zr,ke,{onMessage:8})}}function es(t){let e,n,i;return{c(){e=s("button"),e.textContent="Send test notification",u(e,"class","btn"),u(e,"id","notification")},m(o,l){m(o,e,l),n||(i=O(e,"click",ts),n=!0)},p:J,i:J,o:J,d(o){o&&h(e),n=!1,i()}}}function ts(){new Notification("Notification title",{body:"This is the notification body"})}function ns(t,e,n){let{onMessage:i}=e;return t.$$set=o=>{"onMessage"in o&&n(0,i=o.onMessage)},[i]}class is extends Se{constructor(e){super(),Ce(this,e,ns,es,ke,{onMessage:0})}}function xo(t,e,n){const i=t.slice();return i[65]=e[n],i}function $o(t,e,n){const i=t.slice();return i[68]=e[n],i}function el(t){let e,n,i,o,l,a,f=Object.keys(t[1]),c=[];for(let p=0;pt[38].call(i))},m(p,d){m(p,e,d),m(p,n,d),m(p,i,d),r(i,o);for(let w=0;wt[55].call(Ne)),a(Qe,"class","input"),a(Qe,"type","number"),a(Ze,"class","input"),a(Ze,"type","number"),a(Ue,"class","flex gap-2"),a($e,"class","input grow"),a($e,"id","title"),a(Ft,"class","btn"),a(Ft,"type","submit"),a(st,"class","flex gap-1"),a(et,"class","input grow"),a(et,"id","url"),a(It,"class","btn"),a(It,"id","open-url"),a(ut,"class","flex gap-1"),a(rt,"class","flex flex-col gap-1")},m(w,I){m(w,e,I),m(w,n,I),m(w,i,I),r(i,o),r(i,l),r(i,u),r(i,f),r(i,c),r(i,p),r(i,d),r(i,k),r(i,_),m(w,y,I),m(w,g,I),m(w,b,I),m(w,L,I),r(L,W),r(W,U),r(W,j),j.checked=t[3],r(L,q),r(L,A),r(A,S),r(A,z),z.checked=t[2],r(L,D),r(L,C),r(C,N),r(C,B),B.checked=t[4],r(L,Y),r(L,$),r($,_e),r($,te),te.checked=t[5],r(L,oe),r(L,K),r(K,be),r(K,H),H.checked=t[6],m(w,x,I),m(w,le,I),m(w,ae,I),m(w,ee,I),r(ee,ve),r(ve,re),r(re,ge),r(re,ne),G(ne,t[13]),r(ve,Me),r(ve,Le),r(Le,R),r(Le,V),G(V,t[14]),r(ee,Re),r(ee,Ae),r(Ae,Te),r(Te,ce),r(Te,he),G(he,t[7]),r(Ae,fe),r(Ae,Pe),r(Pe,tt),r(Pe,me),G(me,t[8]),r(ee,de),r(ee,F),r(F,ie),r(ie,X),r(ie,ye),G(ye,t[9]),r(F,Yt),r(F,ht),r(ht,Kt),r(ht,He),G(He,t[10]),r(ee,xt),r(ee,Be),r(Be,Q),r(Q,Et),r(Q,We),G(We,t[11]),r(Be,Wt),r(Be,nt),r(nt,Dt),r(nt,De),G(De,t[12]),m(w,mt,I),m(w,vt,I),m(w,_t,I),m(w,Oe,I),r(Oe,Ie),r(Ie,je),r(je,it),r(je,jt),r(je,ot),r(ot,Rt),r(ot,ii),r(je,qi),r(je,Qt),r(Qt,Bi),r(Qt,oi),r(Ie,Vi),r(Ie,Ve),r(Ve,$t),r(Ve,Gi),r(Ve,en),r(en,Ji),r(en,li),r(Ve,Xi),r(Ve,nn),r(nn,Yi),r(nn,ri),r(Oe,Ki),r(Oe,gt),r(gt,Ge),r(Ge,ln),r(Ge,xi),r(Ge,rn),r(rn,Qi),r(rn,si),r(Ge,Zi),r(Ge,un),r(un,$i),r(un,ui),r(gt,eo),r(gt,Je),r(Je,cn),r(Je,to),r(Je,fn),r(fn,no),r(fn,ai),r(Je,io),r(Je,pn),r(pn,oo),r(pn,ci),r(Oe,lo),r(Oe,yt),r(yt,Xe),r(Xe,mn),r(Xe,ro),r(Xe,vn),r(vn,so),r(vn,fi),r(Xe,uo),r(Xe,bn),r(bn,ao),r(bn,di),r(yt,co),r(yt,Ye),r(Ye,yn),r(Ye,fo),r(Ye,wn),r(wn,po),r(wn,pi),r(Ye,ho),r(Ye,Mn),r(Mn,mo),r(Mn,hi),r(Oe,vo),r(Oe,wt),r(wt,Ke),r(Ke,Cn),r(Ke,_o),r(Ke,Sn),r(Sn,bo),r(Sn,mi),r(Ke,go),r(Ke,Ln),r(Ln,yo),r(Ln,vi),r(wt,wo),r(wt,xe),r(xe,Pn),r(xe,ko),r(xe,On),r(On,Mo),r(On,_i),r(xe,To),r(xe,Wn),r(Wn,Co),r(Wn,bi),m(w,gi,I),m(w,yi,I),m(w,wi,I),m(w,Ht,I),m(w,ki,I),m(w,lt,I),r(lt,jn),r(jn,kt),kt.checked=t[15],r(jn,So),r(lt,zo),r(lt,Rn),r(Rn,Mt),Mt.checked=t[16],r(Rn,Lo),m(w,Mi,I),m(w,Ue,I),r(Ue,Hn),r(Hn,Ao),r(Hn,Ne);for(let we=0;we=1,d,k,_,y=p&&el(t),g=t[1][t[0]]&&nl(t);return{c(){e=s("div"),n=s("div"),i=s("input"),o=v(),l=s("button"),l.textContent="New window",u=v(),f=s("br"),c=v(),y&&y.c(),d=v(),g&&g.c(),a(i,"class","input grow"),a(i,"type","text"),a(i,"placeholder","New Window label.."),a(l,"class","btn"),a(n,"class","flex gap-1"),a(e,"class","flex flex-col children:grow gap-2")},m(b,L){m(b,e,L),r(e,n),r(n,i),G(i,t[20]),r(n,o),r(n,l),r(e,u),r(e,f),r(e,c),y&&y.m(e,null),r(e,d),g&&g.m(e,null),k||(_=[O(i,"input",t[37]),O(l,"click",t[34])],k=!0)},p(b,L){L[0]&1048576&&i.value!==b[20]&&G(i,b[20]),L[0]&2&&(p=Object.keys(b[1]).length>=1),p?y?y.p(b,L):(y=el(b),y.c(),y.m(e,d)):y&&(y.d(1),y=null),b[1][b[0]]?g?g.p(b,L):(g=nl(b),g.c(),g.m(e,null)):g&&(g.d(1),g=null)},i:J,o:J,d(b){b&&h(e),y&&y.d(),g&&g.d(),k=!1,pe(_)}}}function rs(t,e,n){let i=qe.label;const o={[qe.label]:qe},l=["default","crosshair","hand","arrow","move","text","wait","help","progress","notAllowed","contextMenu","cell","verticalText","alias","copy","noDrop","grab","grabbing","allScroll","zoomIn","zoomOut","eResize","nResize","neResize","nwResize","sResize","seResize","swResize","wResize","ewResize","nsResize","neswResize","nwseResize","colResize","rowResize"];let{onMessage:u}=e,f,c="https://tauri.app",p=!0,d=!1,k=!0,_=!1,y=!1,g=null,b=null,L=null,W=null,U=null,j=null,q=null,A=null,S=1,z=new ft(q,A),D=new ft(q,A),C=new St(g,b),N=new St(g,b),B,Y,$=!1,_e=!0,te=null,oe=null,K="default",be="Awesome Tauri Example!";function H(){Ri(c)}function x(){o[i].setTitle(be)}function le(){o[i].hide(),setTimeout(o[i].show,2e3)}function ae(){o[i].minimize(),setTimeout(o[i].unminimize,2e3)}function ee(){Ii({multiple:!1}).then(Q=>{typeof Q=="string"&&o[i].setIcon(Q)})}function ve(){if(!f)return;const Q=new At(f);n(1,o[f]=Q,o),Q.once("tauri://error",function(){u("Error creating new webview")})}function re(){o[i].innerSize().then(Q=>{n(25,C=Q),n(7,g=C.width),n(8,b=C.height)}),o[i].outerSize().then(Q=>{n(26,N=Q)})}function ge(){o[i].innerPosition().then(Q=>{n(23,z=Q)}),o[i].outerPosition().then(Q=>{n(24,D=Q),n(13,q=D.x),n(14,A=D.y)})}async function ne(Q){!Q||(B&&B(),Y&&Y(),Y=await Q.listen("tauri://move",ge),B=await Q.listen("tauri://resize",re))}async function Me(){await o[i].minimize(),await o[i].requestUserAttention(Jt.Critical),await new Promise(Q=>setTimeout(Q,3e3)),await o[i].requestUserAttention(null)}function Le(){f=this.value,n(20,f)}function R(){i=Pi(this),n(0,i),n(1,o)}const V=()=>o[i].center();function Re(){d=this.checked,n(3,d)}function Ae(){p=this.checked,n(2,p)}function Te(){k=this.checked,n(4,k)}function ce(){_=this.checked,n(5,_)}function he(){y=this.checked,n(6,y)}function fe(){q=se(this.value),n(13,q)}function Pe(){A=se(this.value),n(14,A)}function tt(){g=se(this.value),n(7,g)}function me(){b=se(this.value),n(8,b)}function de(){L=se(this.value),n(9,L)}function F(){W=se(this.value),n(10,W)}function ie(){U=se(this.value),n(11,U)}function X(){j=se(this.value),n(12,j)}function ye(){$=this.checked,n(15,$)}function Yt(){_e=this.checked,n(16,_e)}function ht(){K=Pi(this),n(19,K),n(28,l)}function Kt(){te=se(this.value),n(17,te)}function He(){oe=se(this.value),n(18,oe)}function xt(){be=this.value,n(27,be)}function Be(){c=this.value,n(21,c)}return t.$$set=Q=>{"onMessage"in Q&&n(36,u=Q.onMessage)},t.$$.update=()=>{var Q,Et,We,Wt,nt,Dt,De,mt,vt,_t,Oe,Ie,je,it,jt,ot,Rt;t.$$.dirty[0]&3&&(o[i],ge(),re()),t.$$.dirty[0]&7&&((Q=o[i])==null||Q.setResizable(p)),t.$$.dirty[0]&11&&(d?(Et=o[i])==null||Et.maximize():(We=o[i])==null||We.unmaximize()),t.$$.dirty[0]&19&&((Wt=o[i])==null||Wt.setDecorations(k)),t.$$.dirty[0]&35&&((nt=o[i])==null||nt.setAlwaysOnTop(_)),t.$$.dirty[0]&67&&((Dt=o[i])==null||Dt.setFullscreen(y)),t.$$.dirty[0]&387&&g&&b&&((De=o[i])==null||De.setSize(new St(g,b))),t.$$.dirty[0]&1539&&(L&&W?(mt=o[i])==null||mt.setMinSize(new Qn(L,W)):(vt=o[i])==null||vt.setMinSize(null)),t.$$.dirty[0]&6147&&(U>800&&j>400?(_t=o[i])==null||_t.setMaxSize(new Qn(U,j)):(Oe=o[i])==null||Oe.setMaxSize(null)),t.$$.dirty[0]&24579&&q!==null&&A!==null&&((Ie=o[i])==null||Ie.setPosition(new ft(q,A))),t.$$.dirty[0]&3&&((je=o[i])==null||je.scaleFactor().then(bt=>n(22,S=bt))),t.$$.dirty[0]&3&&ne(o[i]),t.$$.dirty[0]&32771&&((it=o[i])==null||it.setCursorGrab($)),t.$$.dirty[0]&65539&&((jt=o[i])==null||jt.setCursorVisible(_e)),t.$$.dirty[0]&524291&&((ot=o[i])==null||ot.setCursorIcon(K)),t.$$.dirty[0]&393219&&te!==null&&oe!==null&&((Rt=o[i])==null||Rt.setCursorPosition(new ft(te,oe)))},[i,o,p,d,k,_,y,g,b,L,W,U,j,q,A,$,_e,te,oe,K,f,c,S,z,D,C,N,be,l,H,x,le,ae,ee,ve,Me,u,Le,R,V,Re,Ae,Te,ce,he,fe,Pe,tt,me,de,F,ie,X,ye,Yt,ht,Kt,He,xt,Be]}class ss extends Se{constructor(e){super(),Ce(this,e,rs,ls,ke,{onMessage:36},null,[-1,-1,-1])}}function Gl(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:t,handler:pt(e)}})]})})}function us(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:t,handler:pt(e)}})]})})}function as(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:t}})]})})}function Jl(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:t}})]})})}function Xl(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}})]})})}Object.freeze({__proto__:null,register:Gl,registerAll:us,isRegistered:as,unregister:Jl,unregisterAll:Xl});function ol(t,e,n){const i=t.slice();return i[9]=e[n],i}function ll(t){let e,n=t[9]+"",i,o,l,u,f;function c(){return t[8](t[9])}return{c(){e=s("div"),i=E(n),o=v(),l=s("button"),l.textContent="Unregister",a(l,"class","btn"),a(l,"type","button"),a(e,"class","flex justify-between")},m(p,d){m(p,e,d),r(e,i),r(e,o),r(e,l),u||(f=O(l,"click",c),u=!0)},p(p,d){t=p,d&2&&n!==(n=t[9]+"")&&Z(i,n)},d(p){p&&h(e),u=!1,f()}}}function rl(t){let e,n,i,o,l;return{c(){e=s("br"),n=v(),i=s("button"),i.textContent="Unregister all",a(i,"class","btn"),a(i,"type","button")},m(u,f){m(u,e,f),m(u,n,f),m(u,i,f),o||(l=O(i,"click",t[5]),o=!0)},p:J,d(u){u&&h(e),u&&h(n),u&&h(i),o=!1,l()}}}function cs(t){let e,n,i,o,l,u,f,c,p,d,k,_=t[1],y=[];for(let b=0;b<_.length;b+=1)y[b]=ll(ol(t,_,b));let g=t[1].length>1&&rl(t);return{c(){e=s("div"),n=s("input"),i=v(),o=s("button"),o.textContent="Register",l=v(),u=s("br"),f=v(),c=s("div");for(let b=0;b1?g?g.p(b,L):(g=rl(b),g.c(),g.m(c,null)):g&&(g.d(1),g=null)},i:J,o:J,d(b){b&&h(e),b&&h(l),b&&h(u),b&&h(f),b&&h(c),dt(y,b),g&&g.d(),d=!1,pe(k)}}}function fs(t,e,n){let i,{onMessage:o}=e;const l=yl([]);_l(t,l,_=>n(1,i=_));let u="CmdOrControl+X";function f(){const _=u;Gl(_,()=>{o(`Shortcut ${_} triggered`)}).then(()=>{l.update(y=>[...y,_]),o(`Shortcut ${_} registered successfully`)}).catch(o)}function c(_){const y=_;Jl(y).then(()=>{l.update(g=>g.filter(b=>b!==y)),o(`Shortcut ${y} unregistered`)}).catch(o)}function p(){Xl().then(()=>{l.update(()=>[]),o("Unregistered all shortcuts")}).catch(o)}function d(){u=this.value,n(0,u)}const k=_=>c(_);return t.$$set=_=>{"onMessage"in _&&n(6,o=_.onMessage)},[u,i,l,f,c,p,o,d,k]}class ds extends Se{constructor(e){super(),Ce(this,e,fs,cs,ke,{onMessage:6})}}function sl(t){let e,n,i,o,l,u,f;return{c(){e=s("br"),n=v(),i=s("input"),o=v(),l=s("button"),l.textContent="Write",a(i,"class","input"),a(i,"placeholder","write to stdin"),a(l,"class","btn")},m(c,p){m(c,e,p),m(c,n,p),m(c,i,p),G(i,t[4]),m(c,o,p),m(c,l,p),u||(f=[O(i,"input",t[14]),O(l,"click",t[8])],u=!0)},p(c,p){p&16&&i.value!==c[4]&&G(i,c[4])},d(c){c&&h(e),c&&h(n),c&&h(i),c&&h(o),c&&h(l),u=!1,pe(f)}}}function ps(t){let e,n,i,o,l,u,f,c,p,d,k,_,y,g,b,L,W,U,j,q,A,S,z,D,C=t[5]&&sl(t);return{c(){e=s("div"),n=s("div"),i=E(`Script: - `),o=s("input"),l=v(),u=s("div"),f=E(`Encoding: - `),c=s("input"),p=v(),d=s("div"),k=E(`Working directory: + `),Ne=s("select");for(let k=0;kt[55].call(Ne)),u(Ze,"class","input"),u(Ze,"type","number"),u(xe,"class","input"),u(xe,"type","number"),u(Ue,"class","flex gap-2"),u($e,"class","input grow"),u($e,"id","title"),u(Ft,"class","btn"),u(Ft,"type","submit"),u(st,"class","flex gap-1"),u(et,"class","input grow"),u(et,"id","url"),u(It,"class","btn"),u(It,"id","open-url"),u(ut,"class","flex gap-1"),u(rt,"class","flex flex-col gap-1")},m(k,I){m(k,e,I),m(k,n,I),m(k,i,I),r(i,o),r(i,l),r(i,a),r(i,f),r(i,c),r(i,p),r(i,d),r(i,w),r(i,_),m(k,y,I),m(k,g,I),m(k,b,I),m(k,L,I),r(L,W),r(W,U),r(W,j),j.checked=t[3],r(L,q),r(L,A),r(A,S),r(A,z),z.checked=t[2],r(L,D),r(L,C),r(C,N),r(C,B),B.checked=t[4],r(L,Y),r(L,$),r($,_e),r($,te),te.checked=t[5],r(L,oe),r(L,K),r(K,be),r(K,H),H.checked=t[6],m(k,Q,I),m(k,le,I),m(k,ae,I),m(k,ee,I),r(ee,ve),r(ve,re),r(re,ge),r(re,ne),G(ne,t[13]),r(ve,Me),r(ve,Le),r(Le,R),r(Le,V),G(V,t[14]),r(ee,Re),r(ee,Ae),r(Ae,Te),r(Te,ce),r(Te,he),G(he,t[7]),r(Ae,fe),r(Ae,Pe),r(Pe,tt),r(Pe,me),G(me,t[8]),r(ee,de),r(ee,F),r(F,ie),r(ie,X),r(ie,ye),G(ye,t[9]),r(F,Yt),r(F,ht),r(ht,Kt),r(ht,He),G(He,t[10]),r(ee,Qt),r(ee,Be),r(Be,Z),r(Z,Et),r(Z,We),G(We,t[11]),r(Be,Wt),r(Be,nt),r(nt,Dt),r(nt,De),G(De,t[12]),m(k,mt,I),m(k,vt,I),m(k,_t,I),m(k,Oe,I),r(Oe,Ie),r(Ie,je),r(je,it),r(je,jt),r(je,ot),r(ot,Rt),r(ot,ii),r(je,qi),r(je,Zt),r(Zt,Bi),r(Zt,oi),r(Ie,Vi),r(Ie,Ve),r(Ve,$t),r(Ve,Gi),r(Ve,en),r(en,Ji),r(en,li),r(Ve,Xi),r(Ve,nn),r(nn,Yi),r(nn,ri),r(Oe,Ki),r(Oe,gt),r(gt,Ge),r(Ge,ln),r(Ge,Qi),r(Ge,rn),r(rn,Zi),r(rn,si),r(Ge,xi),r(Ge,un),r(un,$i),r(un,ui),r(gt,eo),r(gt,Je),r(Je,cn),r(Je,to),r(Je,fn),r(fn,no),r(fn,ai),r(Je,io),r(Je,pn),r(pn,oo),r(pn,ci),r(Oe,lo),r(Oe,yt),r(yt,Xe),r(Xe,mn),r(Xe,ro),r(Xe,vn),r(vn,so),r(vn,fi),r(Xe,uo),r(Xe,bn),r(bn,ao),r(bn,di),r(yt,co),r(yt,Ye),r(Ye,yn),r(Ye,fo),r(Ye,wn),r(wn,po),r(wn,pi),r(Ye,ho),r(Ye,Mn),r(Mn,mo),r(Mn,hi),r(Oe,vo),r(Oe,wt),r(wt,Ke),r(Ke,Cn),r(Ke,_o),r(Ke,Sn),r(Sn,bo),r(Sn,mi),r(Ke,go),r(Ke,Ln),r(Ln,yo),r(Ln,vi),r(wt,wo),r(wt,Qe),r(Qe,Pn),r(Qe,ko),r(Qe,On),r(On,Mo),r(On,_i),r(Qe,To),r(Qe,Wn),r(Wn,Co),r(Wn,bi),m(k,gi,I),m(k,yi,I),m(k,wi,I),m(k,Ht,I),m(k,ki,I),m(k,lt,I),r(lt,jn),r(jn,kt),kt.checked=t[15],r(jn,So),r(lt,zo),r(lt,Rn),r(Rn,Mt),Mt.checked=t[16],r(Rn,Lo),m(k,Mi,I),m(k,Ue,I),r(Ue,Hn),r(Hn,Ao),r(Hn,Ne);for(let we=0;we=1,d,w,_,y=p&&el(t),g=t[1][t[0]]&&nl(t);return{c(){e=s("div"),n=s("div"),i=s("input"),o=v(),l=s("button"),l.textContent="New window",a=v(),f=s("br"),c=v(),y&&y.c(),d=v(),g&&g.c(),u(i,"class","input grow"),u(i,"type","text"),u(i,"placeholder","New Window label.."),u(l,"class","btn"),u(n,"class","flex gap-1"),u(e,"class","flex flex-col children:grow gap-2")},m(b,L){m(b,e,L),r(e,n),r(n,i),G(i,t[20]),r(n,o),r(n,l),r(e,a),r(e,f),r(e,c),y&&y.m(e,null),r(e,d),g&&g.m(e,null),w||(_=[O(i,"input",t[37]),O(l,"click",t[34])],w=!0)},p(b,L){L[0]&1048576&&i.value!==b[20]&&G(i,b[20]),L[0]&2&&(p=Object.keys(b[1]).length>=1),p?y?y.p(b,L):(y=el(b),y.c(),y.m(e,d)):y&&(y.d(1),y=null),b[1][b[0]]?g?g.p(b,L):(g=nl(b),g.c(),g.m(e,null)):g&&(g.d(1),g=null)},i:J,o:J,d(b){b&&h(e),y&&y.d(),g&&g.d(),w=!1,pe(_)}}}function ls(t,e,n){let i=qe.label;const o={[qe.label]:qe},l=["default","crosshair","hand","arrow","move","text","wait","help","progress","notAllowed","contextMenu","cell","verticalText","alias","copy","noDrop","grab","grabbing","allScroll","zoomIn","zoomOut","eResize","nResize","neResize","nwResize","sResize","seResize","swResize","wResize","ewResize","nsResize","neswResize","nwseResize","colResize","rowResize"];let{onMessage:a}=e,f,c="https://tauri.app",p=!0,d=!1,w=!0,_=!1,y=!1,g=null,b=null,L=null,W=null,U=null,j=null,q=null,A=null,S=1,z=new ft(q,A),D=new ft(q,A),C=new St(g,b),N=new St(g,b),B,Y,$=!1,_e=!0,te=null,oe=null,K="default",be="Awesome Tauri Example!";function H(){Ri(c)}function Q(){o[i].setTitle(be)}function le(){o[i].hide(),setTimeout(o[i].show,2e3)}function ae(){o[i].minimize(),setTimeout(o[i].unminimize,2e3)}function ee(){Ii({multiple:!1}).then(Z=>{typeof Z=="string"&&o[i].setIcon(Z)})}function ve(){if(!f)return;const Z=new At(f);n(1,o[f]=Z,o),Z.once("tauri://error",function(){a("Error creating new webview")})}function re(){o[i].innerSize().then(Z=>{n(25,C=Z),n(7,g=C.width),n(8,b=C.height)}),o[i].outerSize().then(Z=>{n(26,N=Z)})}function ge(){o[i].innerPosition().then(Z=>{n(23,z=Z)}),o[i].outerPosition().then(Z=>{n(24,D=Z),n(13,q=D.x),n(14,A=D.y)})}async function ne(Z){!Z||(B&&B(),Y&&Y(),Y=await Z.listen("tauri://move",ge),B=await Z.listen("tauri://resize",re))}async function Me(){await o[i].minimize(),await o[i].requestUserAttention(Jt.Critical),await new Promise(Z=>setTimeout(Z,3e3)),await o[i].requestUserAttention(null)}function Le(){f=this.value,n(20,f)}function R(){i=Pi(this),n(0,i),n(1,o)}const V=()=>o[i].center();function Re(){d=this.checked,n(3,d)}function Ae(){p=this.checked,n(2,p)}function Te(){w=this.checked,n(4,w)}function ce(){_=this.checked,n(5,_)}function he(){y=this.checked,n(6,y)}function fe(){q=se(this.value),n(13,q)}function Pe(){A=se(this.value),n(14,A)}function tt(){g=se(this.value),n(7,g)}function me(){b=se(this.value),n(8,b)}function de(){L=se(this.value),n(9,L)}function F(){W=se(this.value),n(10,W)}function ie(){U=se(this.value),n(11,U)}function X(){j=se(this.value),n(12,j)}function ye(){$=this.checked,n(15,$)}function Yt(){_e=this.checked,n(16,_e)}function ht(){K=Pi(this),n(19,K),n(28,l)}function Kt(){te=se(this.value),n(17,te)}function He(){oe=se(this.value),n(18,oe)}function Qt(){be=this.value,n(27,be)}function Be(){c=this.value,n(21,c)}return t.$$set=Z=>{"onMessage"in Z&&n(36,a=Z.onMessage)},t.$$.update=()=>{var Z,Et,We,Wt,nt,Dt,De,mt,vt,_t,Oe,Ie,je,it,jt,ot,Rt;t.$$.dirty[0]&3&&(o[i],ge(),re()),t.$$.dirty[0]&7&&((Z=o[i])==null||Z.setResizable(p)),t.$$.dirty[0]&11&&(d?(Et=o[i])==null||Et.maximize():(We=o[i])==null||We.unmaximize()),t.$$.dirty[0]&19&&((Wt=o[i])==null||Wt.setDecorations(w)),t.$$.dirty[0]&35&&((nt=o[i])==null||nt.setAlwaysOnTop(_)),t.$$.dirty[0]&67&&((Dt=o[i])==null||Dt.setFullscreen(y)),t.$$.dirty[0]&387&&g&&b&&((De=o[i])==null||De.setSize(new St(g,b))),t.$$.dirty[0]&1539&&(L&&W?(mt=o[i])==null||mt.setMinSize(new Zn(L,W)):(vt=o[i])==null||vt.setMinSize(null)),t.$$.dirty[0]&6147&&(U>800&&j>400?(_t=o[i])==null||_t.setMaxSize(new Zn(U,j)):(Oe=o[i])==null||Oe.setMaxSize(null)),t.$$.dirty[0]&24579&&q!==null&&A!==null&&((Ie=o[i])==null||Ie.setPosition(new ft(q,A))),t.$$.dirty[0]&3&&((je=o[i])==null||je.scaleFactor().then(bt=>n(22,S=bt))),t.$$.dirty[0]&3&&ne(o[i]),t.$$.dirty[0]&32771&&((it=o[i])==null||it.setCursorGrab($)),t.$$.dirty[0]&65539&&((jt=o[i])==null||jt.setCursorVisible(_e)),t.$$.dirty[0]&524291&&((ot=o[i])==null||ot.setCursorIcon(K)),t.$$.dirty[0]&393219&&te!==null&&oe!==null&&((Rt=o[i])==null||Rt.setCursorPosition(new ft(te,oe)))},[i,o,p,d,w,_,y,g,b,L,W,U,j,q,A,$,_e,te,oe,K,f,c,S,z,D,C,N,be,l,H,Q,le,ae,ee,ve,Me,a,Le,R,V,Re,Ae,Te,ce,he,fe,Pe,tt,me,de,F,ie,X,ye,Yt,ht,Kt,He,Qt,Be]}class rs extends Se{constructor(e){super(),Ce(this,e,ls,os,ke,{onMessage:36},null,[-1,-1,-1])}}function Gl(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:t,handler:pt(e)}})]})})}function ss(t,e){return M(this,void 0,void 0,function(){return T(this,function(n){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:t,handler:pt(e)}})]})})}function us(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:t}})]})})}function Jl(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:t}})]})})}function Xl(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}})]})})}Object.freeze({__proto__:null,register:Gl,registerAll:ss,isRegistered:us,unregister:Jl,unregisterAll:Xl});function ol(t,e,n){const i=t.slice();return i[9]=e[n],i}function ll(t){let e,n=t[9]+"",i,o,l,a,f;function c(){return t[8](t[9])}return{c(){e=s("div"),i=E(n),o=v(),l=s("button"),l.textContent="Unregister",u(l,"class","btn"),u(l,"type","button"),u(e,"class","flex justify-between")},m(p,d){m(p,e,d),r(e,i),r(e,o),r(e,l),a||(f=O(l,"click",c),a=!0)},p(p,d){t=p,d&2&&n!==(n=t[9]+"")&&x(i,n)},d(p){p&&h(e),a=!1,f()}}}function rl(t){let e,n,i,o,l;return{c(){e=s("br"),n=v(),i=s("button"),i.textContent="Unregister all",u(i,"class","btn"),u(i,"type","button")},m(a,f){m(a,e,f),m(a,n,f),m(a,i,f),o||(l=O(i,"click",t[5]),o=!0)},p:J,d(a){a&&h(e),a&&h(n),a&&h(i),o=!1,l()}}}function as(t){let e,n,i,o,l,a,f,c,p,d,w,_=t[1],y=[];for(let b=0;b<_.length;b+=1)y[b]=ll(ol(t,_,b));let g=t[1].length>1&&rl(t);return{c(){e=s("div"),n=s("input"),i=v(),o=s("button"),o.textContent="Register",l=v(),a=s("br"),f=v(),c=s("div");for(let b=0;b1?g?g.p(b,L):(g=rl(b),g.c(),g.m(c,null)):g&&(g.d(1),g=null)},i:J,o:J,d(b){b&&h(e),b&&h(l),b&&h(a),b&&h(f),b&&h(c),dt(y,b),g&&g.d(),d=!1,pe(w)}}}function cs(t,e,n){let i,{onMessage:o}=e;const l=yl([]);_l(t,l,_=>n(1,i=_));let a="CmdOrControl+X";function f(){const _=a;Gl(_,()=>{o(`Shortcut ${_} triggered`)}).then(()=>{l.update(y=>[...y,_]),o(`Shortcut ${_} registered successfully`)}).catch(o)}function c(_){const y=_;Jl(y).then(()=>{l.update(g=>g.filter(b=>b!==y)),o(`Shortcut ${y} unregistered`)}).catch(o)}function p(){Xl().then(()=>{l.update(()=>[]),o("Unregistered all shortcuts")}).catch(o)}function d(){a=this.value,n(0,a)}const w=_=>c(_);return t.$$set=_=>{"onMessage"in _&&n(6,o=_.onMessage)},[a,i,l,f,c,p,o,d,w]}class fs extends Se{constructor(e){super(),Ce(this,e,cs,as,ke,{onMessage:6})}}function sl(t){let e,n,i,o,l,a,f;return{c(){e=s("br"),n=v(),i=s("input"),o=v(),l=s("button"),l.textContent="Write",u(i,"class","input"),u(i,"placeholder","write to stdin"),u(l,"class","btn")},m(c,p){m(c,e,p),m(c,n,p),m(c,i,p),G(i,t[4]),m(c,o,p),m(c,l,p),a||(f=[O(i,"input",t[14]),O(l,"click",t[8])],a=!0)},p(c,p){p&16&&i.value!==c[4]&&G(i,c[4])},d(c){c&&h(e),c&&h(n),c&&h(i),c&&h(o),c&&h(l),a=!1,pe(f)}}}function ds(t){let e,n,i,o,l,a,f,c,p,d,w,_,y,g,b,L,W,U,j,q,A,S,z,D,C=t[5]&&sl(t);return{c(){e=s("div"),n=s("div"),i=E(`Script: + `),o=s("input"),l=v(),a=s("div"),f=E(`Encoding: + `),c=s("input"),p=v(),d=s("div"),w=E(`Working directory: `),_=s("input"),y=v(),g=s("div"),b=E(`Arguments: - `),L=s("input"),W=v(),U=s("div"),j=s("button"),j.textContent="Run",q=v(),A=s("button"),A.textContent="Kill",S=v(),C&&C.c(),a(o,"class","grow input"),a(n,"class","flex items-center gap-1"),a(c,"class","grow input"),a(u,"class","flex items-center gap-1"),a(_,"class","grow input"),a(_,"placeholder","Working directory"),a(d,"class","flex items-center gap-1"),a(L,"class","grow input"),a(L,"placeholder","Environment variables"),a(g,"class","flex items-center gap-1"),a(j,"class","btn"),a(A,"class","btn"),a(U,"class","flex children:grow gap-1"),a(e,"class","flex flex-col childre:grow gap-1")},m(N,B){m(N,e,B),r(e,n),r(n,i),r(n,o),G(o,t[0]),r(e,l),r(e,u),r(u,f),r(u,c),G(c,t[3]),r(e,p),r(e,d),r(d,k),r(d,_),G(_,t[1]),r(e,y),r(e,g),r(g,b),r(g,L),G(L,t[2]),r(e,W),r(e,U),r(U,j),r(U,q),r(U,A),r(e,S),C&&C.m(e,null),z||(D=[O(o,"input",t[10]),O(c,"input",t[11]),O(_,"input",t[12]),O(L,"input",t[13]),O(j,"click",t[6]),O(A,"click",t[7])],z=!0)},p(N,[B]){B&1&&o.value!==N[0]&&G(o,N[0]),B&8&&c.value!==N[3]&&G(c,N[3]),B&2&&_.value!==N[1]&&G(_,N[1]),B&4&&L.value!==N[2]&&G(L,N[2]),N[5]?C?C.p(N,B):(C=sl(N),C.c(),C.m(e,null)):C&&(C.d(1),C=null)},i:J,o:J,d(N){N&&h(e),C&&C.d(),z=!1,pe(D)}}}function hs(t,e,n){const i=navigator.userAgent.includes("Windows");let o=i?"cmd":"sh",l=i?["/C"]:["-c"],{onMessage:u}=e,f='echo "hello world"',c=null,p="SOMETHING=value ANOTHER=2",d="",k="",_;function y(){return p.split(" ").reduce((S,z)=>{let[D,C]=z.split("=");return{...S,[D]:C}},{})}function g(){n(5,_=null);const S=new Ml(o,[...l,f],{cwd:c||null,env:y(),encoding:d});S.on("close",z=>{u(`command finished with code ${z.code} and signal ${z.signal}`),n(5,_=null)}),S.on("error",z=>u(`command error: "${z}"`)),S.stdout.on("data",z=>u(`command stdout: "${z}"`)),S.stderr.on("data",z=>u(`command stderr: "${z}"`)),S.spawn().then(z=>{n(5,_=z)}).catch(u)}function b(){_.kill().then(()=>u("killed child process")).catch(u)}function L(){_.write(k).catch(u)}function W(){f=this.value,n(0,f)}function U(){d=this.value,n(3,d)}function j(){c=this.value,n(1,c)}function q(){p=this.value,n(2,p)}function A(){k=this.value,n(4,k)}return t.$$set=S=>{"onMessage"in S&&n(9,u=S.onMessage)},[f,c,p,d,k,_,g,b,L,u,W,U,j,q,A]}class ms extends Se{constructor(e){super(),Ce(this,e,hs,ps,ke,{onMessage:9})}}function Ni(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,Xt("tauri://update-status",function(n){t(n==null?void 0:n.payload)})]})})}function Yl(){return M(this,void 0,void 0,function(){function t(){e&&e(),e=void 0}var e;return T(this,function(n){return[2,new Promise(function(i,o){Ni(function(l){return l.error?(t(),o(l.error)):l.status==="DONE"?(t(),i()):void 0}).then(function(l){e=l}).catch(function(l){throw t(),l}),ni("tauri://update-install").catch(function(l){throw t(),l})})]})})}function Kl(){return M(this,void 0,void 0,function(){function t(){e&&e(),e=void 0}var e;return T(this,function(n){return[2,new Promise(function(i,o){Fl("tauri://update-available",function(l){var u;u=l==null?void 0:l.payload,t(),i({manifest:u,shouldUpdate:!0})}).catch(function(l){throw t(),l}),Ni(function(l){return l.error?(t(),o(l.error)):l.status==="UPTODATE"?(t(),i({shouldUpdate:!1})):void 0}).then(function(l){e=l}).catch(function(l){throw t(),l}),ni("tauri://update").catch(function(l){throw t(),l})})]})})}Object.freeze({__proto__:null,onUpdaterEvent:Ni,installUpdate:Yl,checkUpdate:Kl});function vs(t){let e;return{c(){e=s("button"),e.innerHTML='
',a(e,"class","btn text-accentText dark:text-darkAccentText flex items-center justify-center")},m(n,i){m(n,e,i)},p:J,d(n){n&&h(e)}}}function _s(t){let e,n,i;return{c(){e=s("button"),e.textContent="Install update",a(e,"class","btn")},m(o,l){m(o,e,l),n||(i=O(e,"click",t[4]),n=!0)},p:J,d(o){o&&h(e),n=!1,i()}}}function bs(t){let e,n,i;return{c(){e=s("button"),e.textContent="Check update",a(e,"class","btn")},m(o,l){m(o,e,l),n||(i=O(e,"click",t[3]),n=!0)},p:J,d(o){o&&h(e),n=!1,i()}}}function gs(t){let e;function n(l,u){return!l[0]&&!l[2]?bs:!l[1]&&l[2]?_s:vs}let i=n(t),o=i(t);return{c(){e=s("div"),o.c(),a(e,"class","flex children:grow children:h10")},m(l,u){m(l,e,u),o.m(e,null)},p(l,[u]){i===(i=n(l))&&o?o.p(l,u):(o.d(1),o=i(l),o&&(o.c(),o.m(e,null)))},i:J,o:J,d(l){l&&h(e),o.d()}}}function ys(t,e,n){let{onMessage:i}=e,o;at(async()=>{o=await Xt("tauri://update-status",i)}),Di(()=>{o&&o()});let l,u,f;async function c(){n(0,l=!0);try{const{shouldUpdate:d,manifest:k}=await Kl();i(`Should update: ${d}`),i(k),n(2,f=d)}catch(d){i(d)}finally{n(0,l=!1)}}async function p(){n(1,u=!0);try{await Yl(),i("Installation complete, restart required."),await Fi()}catch(d){i(d)}finally{n(1,u=!1)}}return t.$$set=d=>{"onMessage"in d&&n(5,i=d.onMessage)},[l,u,f,c,p,i]}class ws extends Se{constructor(e){super(),Ce(this,e,ys,gs,ke,{onMessage:5})}}function xl(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"Clipboard",message:{cmd:"writeText",data:t}})]})})}function Ql(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Clipboard",message:{cmd:"readText",data:null}})]})})}Object.freeze({__proto__:null,writeText:xl,readText:Ql});function ks(t){let e,n,i,o,l,u,f,c;return{c(){e=s("div"),n=s("input"),i=v(),o=s("button"),o.textContent="Write",l=v(),u=s("button"),u.textContent="Read",a(n,"class","grow input"),a(n,"placeholder","Text to write to the clipboard"),a(o,"class","btn"),a(o,"type","button"),a(u,"class","btn"),a(u,"type","button"),a(e,"class","flex gap-1")},m(p,d){m(p,e,d),r(e,n),G(n,t[0]),r(e,i),r(e,o),r(e,l),r(e,u),f||(c=[O(n,"input",t[4]),O(o,"click",t[1]),O(u,"click",t[2])],f=!0)},p(p,[d]){d&1&&n.value!==p[0]&&G(n,p[0])},i:J,o:J,d(p){p&&h(e),f=!1,pe(c)}}}function Ms(t,e,n){let{onMessage:i}=e,o="clipboard message";function l(){xl(o).then(()=>{i("Wrote to the clipboard")}).catch(i)}function u(){Ql().then(c=>{i(`Clipboard contents: ${c}`)}).catch(i)}function f(){o=this.value,n(0,o)}return t.$$set=c=>{"onMessage"in c&&n(3,i=c.onMessage)},[o,l,u,i,f]}class Ts extends Se{constructor(e){super(),Ce(this,e,Ms,ks,ke,{onMessage:3})}}function Cs(t){let e;return{c(){e=s("div"),e.innerHTML=`
Not available for Linux
- `,a(e,"class","flex flex-col gap-2")},m(n,i){m(n,e,i)},p:J,i:J,o:J,d(n){n&&h(e)}}}function Ss(t,e,n){let{onMessage:i}=e;const o=window.constraints={audio:!0,video:!0};function l(f){const c=document.querySelector("video"),p=f.getVideoTracks();i("Got stream with constraints:",o),i(`Using video device: ${p[0].label}`),window.stream=f,c.srcObject=f}function u(f){if(f.name==="ConstraintNotSatisfiedError"){const c=o.video;i(`The resolution ${c.width.exact}x${c.height.exact} px is not supported by your device.`)}else f.name==="PermissionDeniedError"&&i("Permissions have not been granted to use your camera and microphone, you need to allow the page access to your devices in order for the demo to work.");i(`getUserMedia error: ${f.name}`,f)}return at(async()=>{try{const f=await navigator.mediaDevices.getUserMedia(o);l(f)}catch(f){u(f)}}),Di(()=>{window.stream.getTracks().forEach(function(f){f.stop()})}),t.$$set=f=>{"onMessage"in f&&n(0,i=f.onMessage)},[i]}class zs extends Se{constructor(e){super(),Ce(this,e,Ss,Cs,ke,{onMessage:0})}}function ul(t,e,n){const i=t.slice();return i[32]=e[n],i}function al(t,e,n){const i=t.slice();return i[35]=e[n],i}function cl(t){let e,n,i,o,l,u,f,c,p,d,k,_,y,g,b;function L(S,z){return S[3]?As:Ls}let W=L(t),U=W(t);function j(S,z){return S[2]?Os:Ps}let q=j(t),A=q(t);return{c(){e=s("div"),n=s("span"),n.textContent="Tauri API Validation",i=v(),o=s("span"),l=s("span"),U.c(),f=v(),c=s("span"),c.innerHTML='
',p=v(),d=s("span"),A.c(),_=v(),y=s("span"),y.innerHTML='
',a(n,"class","lt-sm:pl-10 text-darkPrimaryText"),a(l,"title",u=t[3]?"Switch to Light mode":"Switch to Dark mode"),a(l,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),a(c,"title","Minimize"),a(c,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),a(d,"title",k=t[2]?"Restore":"Maximize"),a(d,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),a(y,"title","Close"),a(y,"class","hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText "),a(o,"class","h-100% children:h-100% children:w-12 children:inline-flex children:items-center children:justify-center"),a(e,"class","w-screen select-none h-8 pl-2 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"),a(e,"data-tauri-drag-region","")},m(S,z){m(S,e,z),r(e,n),r(e,i),r(e,o),r(o,l),U.m(l,null),r(o,f),r(o,c),r(o,p),r(o,d),A.m(d,null),r(o,_),r(o,y),g||(b=[O(l,"click",t[12]),O(c,"click",t[9]),O(d,"click",t[10]),O(y,"click",t[11])],g=!0)},p(S,z){W!==(W=L(S))&&(U.d(1),U=W(S),U&&(U.c(),U.m(l,null))),z[0]&8&&u!==(u=S[3]?"Switch to Light mode":"Switch to Dark mode")&&a(l,"title",u),q!==(q=j(S))&&(A.d(1),A=q(S),A&&(A.c(),A.m(d,null))),z[0]&4&&k!==(k=S[2]?"Restore":"Maximize")&&a(d,"title",k)},d(S){S&&h(e),U.d(),A.d(),g=!1,pe(b)}}}function Ls(t){let e;return{c(){e=s("div"),a(e,"class","i-ph-moon")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function As(t){let e;return{c(){e=s("div"),a(e,"class","i-ph-sun")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Ps(t){let e;return{c(){e=s("div"),a(e,"class","i-codicon-chrome-maximize")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Os(t){let e;return{c(){e=s("div"),a(e,"class","i-codicon-chrome-restore")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Es(t){let e;return{c(){e=s("span"),a(e,"class","i-codicon-menu animate-duration-300ms animate-fade-in")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Ws(t){let e;return{c(){e=s("span"),a(e,"class","i-codicon-close animate-duration-300ms animate-fade-in")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function fl(t){let e,n,i,o,l,u,f,c,p;function d(y,g){return y[3]?js:Ds}let k=d(t),_=k(t);return{c(){e=s("a"),_.c(),n=v(),i=s("br"),o=v(),l=s("div"),u=v(),f=s("br"),a(e,"href","##"),a(e,"class","nv justify-between h-8"),a(l,"class","bg-white/5 h-2px")},m(y,g){m(y,e,g),_.m(e,null),m(y,n,g),m(y,i,g),m(y,o,g),m(y,l,g),m(y,u,g),m(y,f,g),c||(p=O(e,"click",t[12]),c=!0)},p(y,g){k!==(k=d(y))&&(_.d(1),_=k(y),_&&(_.c(),_.m(e,null)))},d(y){y&&h(e),_.d(),y&&h(n),y&&h(i),y&&h(o),y&&h(l),y&&h(u),y&&h(f),c=!1,p()}}}function Ds(t){let e,n;return{c(){e=E(`Switch to Dark mode - `),n=s("div"),a(n,"class","i-ph-moon")},m(i,o){m(i,e,o),m(i,n,o)},d(i){i&&h(e),i&&h(n)}}}function js(t){let e,n;return{c(){e=E(`Switch to Light mode - `),n=s("div"),a(n,"class","i-ph-sun")},m(i,o){m(i,e,o),m(i,n,o)},d(i){i&&h(e),i&&h(n)}}}function Rs(t){let e,n,i,o,l,u=t[35].label+"",f,c,p,d;function k(){return t[20](t[35])}return{c(){e=s("a"),n=s("div"),o=v(),l=s("p"),f=E(u),a(n,"class",i=t[35].icon+" mr-2"),a(e,"href","##"),a(e,"class",c="nv "+(t[1]===t[35]?"nv_selected":""))},m(_,y){m(_,e,y),r(e,n),r(e,o),r(e,l),r(l,f),p||(d=O(e,"click",k),p=!0)},p(_,y){t=_,y[0]&2&&c!==(c="nv "+(t[1]===t[35]?"nv_selected":""))&&a(e,"class",c)},d(_){_&&h(e),p=!1,d()}}}function dl(t){let e,n=t[35]&&Rs(t);return{c(){n&&n.c(),e=$n()},m(i,o){n&&n.m(i,o),m(i,e,o)},p(i,o){i[35]&&n.p(i,o)},d(i){n&&n.d(i),i&&h(e)}}}function pl(t){let e,n=t[32].html+"",i;return{c(){e=new lr(!1),i=$n(),e.a=i},m(o,l){e.m(n,o,l),m(o,i,l)},p(o,l){l[0]&64&&n!==(n=o[32].html+"")&&e.p(n)},d(o){o&&h(i),o&&e.d()}}}function Hs(t){let e,n,i,o,l,u,f,c,p,d,k,_,y,g,b,L,W,U,j,q,A,S,z,D,C,N,B,Y=t[1].label+"",$,_e,te,oe,K,be,H,x,le,ae,ee,ve,re,ge,ne,Me,Le,R,V=t[5]&&cl(t);function Re(F,ie){return F[0]?Ws:Es}let Ae=Re(t),Te=Ae(t),ce=!t[5]&&fl(t),he=t[7],fe=[];for(let F=0;F`,k=v(),_=s("a"),_.innerHTML=`Github + `),L=s("input"),W=v(),U=s("div"),j=s("button"),j.textContent="Run",q=v(),A=s("button"),A.textContent="Kill",S=v(),C&&C.c(),u(o,"class","grow input"),u(n,"class","flex items-center gap-1"),u(c,"class","grow input"),u(a,"class","flex items-center gap-1"),u(_,"class","grow input"),u(_,"placeholder","Working directory"),u(d,"class","flex items-center gap-1"),u(L,"class","grow input"),u(L,"placeholder","Environment variables"),u(g,"class","flex items-center gap-1"),u(j,"class","btn"),u(A,"class","btn"),u(U,"class","flex children:grow gap-1"),u(e,"class","flex flex-col childre:grow gap-1")},m(N,B){m(N,e,B),r(e,n),r(n,i),r(n,o),G(o,t[0]),r(e,l),r(e,a),r(a,f),r(a,c),G(c,t[3]),r(e,p),r(e,d),r(d,w),r(d,_),G(_,t[1]),r(e,y),r(e,g),r(g,b),r(g,L),G(L,t[2]),r(e,W),r(e,U),r(U,j),r(U,q),r(U,A),r(e,S),C&&C.m(e,null),z||(D=[O(o,"input",t[10]),O(c,"input",t[11]),O(_,"input",t[12]),O(L,"input",t[13]),O(j,"click",t[6]),O(A,"click",t[7])],z=!0)},p(N,[B]){B&1&&o.value!==N[0]&&G(o,N[0]),B&8&&c.value!==N[3]&&G(c,N[3]),B&2&&_.value!==N[1]&&G(_,N[1]),B&4&&L.value!==N[2]&&G(L,N[2]),N[5]?C?C.p(N,B):(C=sl(N),C.c(),C.m(e,null)):C&&(C.d(1),C=null)},i:J,o:J,d(N){N&&h(e),C&&C.d(),z=!1,pe(D)}}}function ps(t,e,n){const i=navigator.userAgent.includes("Windows");let o=i?"cmd":"sh",l=i?["/C"]:["-c"],{onMessage:a}=e,f='echo "hello world"',c=null,p="SOMETHING=value ANOTHER=2",d="",w="",_;function y(){return p.split(" ").reduce((S,z)=>{let[D,C]=z.split("=");return{...S,[D]:C}},{})}function g(){n(5,_=null);const S=new Ml(o,[...l,f],{cwd:c||null,env:y(),encoding:d});S.on("close",z=>{a(`command finished with code ${z.code} and signal ${z.signal}`),n(5,_=null)}),S.on("error",z=>a(`command error: "${z}"`)),S.stdout.on("data",z=>a(`command stdout: "${z}"`)),S.stderr.on("data",z=>a(`command stderr: "${z}"`)),S.spawn().then(z=>{n(5,_=z)}).catch(a)}function b(){_.kill().then(()=>a("killed child process")).catch(a)}function L(){_.write(w).catch(a)}function W(){f=this.value,n(0,f)}function U(){d=this.value,n(3,d)}function j(){c=this.value,n(1,c)}function q(){p=this.value,n(2,p)}function A(){w=this.value,n(4,w)}return t.$$set=S=>{"onMessage"in S&&n(9,a=S.onMessage)},[f,c,p,d,w,_,g,b,L,a,W,U,j,q,A]}class hs extends Se{constructor(e){super(),Ce(this,e,ps,ds,ke,{onMessage:9})}}function Ni(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,Xt("tauri://update-status",function(n){t(n==null?void 0:n.payload)})]})})}function Yl(){return M(this,void 0,void 0,function(){function t(){e&&e(),e=void 0}var e;return T(this,function(n){return[2,new Promise(function(i,o){Ni(function(l){return l.error?(t(),o(l.error)):l.status==="DONE"?(t(),i()):void 0}).then(function(l){e=l}).catch(function(l){throw t(),l}),ni("tauri://update-install").catch(function(l){throw t(),l})})]})})}function Kl(){return M(this,void 0,void 0,function(){function t(){e&&e(),e=void 0}var e;return T(this,function(n){return[2,new Promise(function(i,o){Fl("tauri://update-available",function(l){var a;a=l==null?void 0:l.payload,t(),i({manifest:a,shouldUpdate:!0})}).catch(function(l){throw t(),l}),Ni(function(l){return l.error?(t(),o(l.error)):l.status==="UPTODATE"?(t(),i({shouldUpdate:!1})):void 0}).then(function(l){e=l}).catch(function(l){throw t(),l}),ni("tauri://update").catch(function(l){throw t(),l})})]})})}Object.freeze({__proto__:null,onUpdaterEvent:Ni,installUpdate:Yl,checkUpdate:Kl});function ms(t){let e;return{c(){e=s("button"),e.innerHTML='
',u(e,"class","btn text-accentText dark:text-darkAccentText flex items-center justify-center")},m(n,i){m(n,e,i)},p:J,d(n){n&&h(e)}}}function vs(t){let e,n,i;return{c(){e=s("button"),e.textContent="Install update",u(e,"class","btn")},m(o,l){m(o,e,l),n||(i=O(e,"click",t[4]),n=!0)},p:J,d(o){o&&h(e),n=!1,i()}}}function _s(t){let e,n,i;return{c(){e=s("button"),e.textContent="Check update",u(e,"class","btn")},m(o,l){m(o,e,l),n||(i=O(e,"click",t[3]),n=!0)},p:J,d(o){o&&h(e),n=!1,i()}}}function bs(t){let e;function n(l,a){return!l[0]&&!l[2]?_s:!l[1]&&l[2]?vs:ms}let i=n(t),o=i(t);return{c(){e=s("div"),o.c(),u(e,"class","flex children:grow children:h10")},m(l,a){m(l,e,a),o.m(e,null)},p(l,[a]){i===(i=n(l))&&o?o.p(l,a):(o.d(1),o=i(l),o&&(o.c(),o.m(e,null)))},i:J,o:J,d(l){l&&h(e),o.d()}}}function gs(t,e,n){let{onMessage:i}=e,o;at(async()=>{o=await Xt("tauri://update-status",i)}),Di(()=>{o&&o()});let l,a,f;async function c(){n(0,l=!0);try{const{shouldUpdate:d,manifest:w}=await Kl();i(`Should update: ${d}`),i(w),n(2,f=d)}catch(d){i(d)}finally{n(0,l=!1)}}async function p(){n(1,a=!0);try{await Yl(),i("Installation complete, restart required."),await Fi()}catch(d){i(d)}finally{n(1,a=!1)}}return t.$$set=d=>{"onMessage"in d&&n(5,i=d.onMessage)},[l,a,f,c,p,i]}class ys extends Se{constructor(e){super(),Ce(this,e,gs,bs,ke,{onMessage:5})}}function Ql(t){return M(this,void 0,void 0,function(){return T(this,function(e){return[2,P({__tauriModule:"Clipboard",message:{cmd:"writeText",data:t}})]})})}function Zl(){return M(this,void 0,void 0,function(){return T(this,function(t){return[2,P({__tauriModule:"Clipboard",message:{cmd:"readText",data:null}})]})})}Object.freeze({__proto__:null,writeText:Ql,readText:Zl});function ws(t){let e,n,i,o,l,a,f,c;return{c(){e=s("div"),n=s("input"),i=v(),o=s("button"),o.textContent="Write",l=v(),a=s("button"),a.textContent="Read",u(n,"class","grow input"),u(n,"placeholder","Text to write to the clipboard"),u(o,"class","btn"),u(o,"type","button"),u(a,"class","btn"),u(a,"type","button"),u(e,"class","flex gap-1")},m(p,d){m(p,e,d),r(e,n),G(n,t[0]),r(e,i),r(e,o),r(e,l),r(e,a),f||(c=[O(n,"input",t[4]),O(o,"click",t[1]),O(a,"click",t[2])],f=!0)},p(p,[d]){d&1&&n.value!==p[0]&&G(n,p[0])},i:J,o:J,d(p){p&&h(e),f=!1,pe(c)}}}function ks(t,e,n){let{onMessage:i}=e,o="clipboard message";function l(){Ql(o).then(()=>{i("Wrote to the clipboard")}).catch(i)}function a(){Zl().then(c=>{i(`Clipboard contents: ${c}`)}).catch(i)}function f(){o=this.value,n(0,o)}return t.$$set=c=>{"onMessage"in c&&n(3,i=c.onMessage)},[o,l,a,i,f]}class Ms extends Se{constructor(e){super(),Ce(this,e,ks,ws,ke,{onMessage:3})}}function Ts(t){let e;return{c(){e=s("div"),e.innerHTML=`
Not available for Linux
+ `,u(e,"class","flex flex-col gap-2")},m(n,i){m(n,e,i)},p:J,i:J,o:J,d(n){n&&h(e)}}}function Cs(t,e,n){let{onMessage:i}=e;const o=window.constraints={audio:!0,video:!0};function l(f){const c=document.querySelector("video"),p=f.getVideoTracks();i("Got stream with constraints:",o),i(`Using video device: ${p[0].label}`),window.stream=f,c.srcObject=f}function a(f){if(f.name==="ConstraintNotSatisfiedError"){const c=o.video;i(`The resolution ${c.width.exact}x${c.height.exact} px is not supported by your device.`)}else f.name==="PermissionDeniedError"&&i("Permissions have not been granted to use your camera and microphone, you need to allow the page access to your devices in order for the demo to work.");i(`getUserMedia error: ${f.name}`,f)}return at(async()=>{try{const f=await navigator.mediaDevices.getUserMedia(o);l(f)}catch(f){a(f)}}),Di(()=>{window.stream.getTracks().forEach(function(f){f.stop()})}),t.$$set=f=>{"onMessage"in f&&n(0,i=f.onMessage)},[i]}class Ss extends Se{constructor(e){super(),Ce(this,e,Cs,Ts,ke,{onMessage:0})}}function ul(t,e,n){const i=t.slice();return i[32]=e[n],i}function al(t,e,n){const i=t.slice();return i[35]=e[n],i}function cl(t){let e,n,i,o,l,a,f,c,p,d,w,_,y,g,b;function L(S,z){return S[3]?Ls:zs}let W=L(t),U=W(t);function j(S,z){return S[2]?Ps:As}let q=j(t),A=q(t);return{c(){e=s("div"),n=s("span"),n.textContent="Tauri API Validation",i=v(),o=s("span"),l=s("span"),U.c(),f=v(),c=s("span"),c.innerHTML='
',p=v(),d=s("span"),A.c(),_=v(),y=s("span"),y.innerHTML='
',u(n,"class","lt-sm:pl-10 text-darkPrimaryText"),u(l,"title",a=t[3]?"Switch to Light mode":"Switch to Dark mode"),u(l,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),u(c,"title","Minimize"),u(c,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),u(d,"title",w=t[2]?"Restore":"Maximize"),u(d,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),u(y,"title","Close"),u(y,"class","hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText "),u(o,"class","h-100% children:h-100% children:w-12 children:inline-flex children:items-center children:justify-center"),u(e,"class","w-screen select-none h-8 pl-2 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"),u(e,"data-tauri-drag-region","")},m(S,z){m(S,e,z),r(e,n),r(e,i),r(e,o),r(o,l),U.m(l,null),r(o,f),r(o,c),r(o,p),r(o,d),A.m(d,null),r(o,_),r(o,y),g||(b=[O(l,"click",t[12]),O(c,"click",t[9]),O(d,"click",t[10]),O(y,"click",t[11])],g=!0)},p(S,z){W!==(W=L(S))&&(U.d(1),U=W(S),U&&(U.c(),U.m(l,null))),z[0]&8&&a!==(a=S[3]?"Switch to Light mode":"Switch to Dark mode")&&u(l,"title",a),q!==(q=j(S))&&(A.d(1),A=q(S),A&&(A.c(),A.m(d,null))),z[0]&4&&w!==(w=S[2]?"Restore":"Maximize")&&u(d,"title",w)},d(S){S&&h(e),U.d(),A.d(),g=!1,pe(b)}}}function zs(t){let e;return{c(){e=s("div"),u(e,"class","i-ph-moon")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Ls(t){let e;return{c(){e=s("div"),u(e,"class","i-ph-sun")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function As(t){let e;return{c(){e=s("div"),u(e,"class","i-codicon-chrome-maximize")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Ps(t){let e;return{c(){e=s("div"),u(e,"class","i-codicon-chrome-restore")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Os(t){let e;return{c(){e=s("span"),u(e,"class","i-codicon-menu animate-duration-300ms animate-fade-in")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function Es(t){let e;return{c(){e=s("span"),u(e,"class","i-codicon-close animate-duration-300ms animate-fade-in")},m(n,i){m(n,e,i)},d(n){n&&h(e)}}}function fl(t){let e,n,i,o,l,a,f,c,p;function d(y,g){return y[3]?Ds:Ws}let w=d(t),_=w(t);return{c(){e=s("a"),_.c(),n=v(),i=s("br"),o=v(),l=s("div"),a=v(),f=s("br"),u(e,"href","##"),u(e,"class","nv justify-between h-8"),u(l,"class","bg-white/5 h-2px")},m(y,g){m(y,e,g),_.m(e,null),m(y,n,g),m(y,i,g),m(y,o,g),m(y,l,g),m(y,a,g),m(y,f,g),c||(p=O(e,"click",t[12]),c=!0)},p(y,g){w!==(w=d(y))&&(_.d(1),_=w(y),_&&(_.c(),_.m(e,null)))},d(y){y&&h(e),_.d(),y&&h(n),y&&h(i),y&&h(o),y&&h(l),y&&h(a),y&&h(f),c=!1,p()}}}function Ws(t){let e,n;return{c(){e=E(`Switch to Dark mode + `),n=s("div"),u(n,"class","i-ph-moon")},m(i,o){m(i,e,o),m(i,n,o)},d(i){i&&h(e),i&&h(n)}}}function Ds(t){let e,n;return{c(){e=E(`Switch to Light mode + `),n=s("div"),u(n,"class","i-ph-sun")},m(i,o){m(i,e,o),m(i,n,o)},d(i){i&&h(e),i&&h(n)}}}function js(t){let e,n,i,o,l=t[35].label+"",a,f,c,p;function d(){return t[20](t[35])}return{c(){e=s("a"),n=s("div"),i=v(),o=s("p"),a=E(l),u(n,"class",t[35].icon+" mr-2"),u(e,"href","##"),u(e,"class",f="nv "+(t[1]===t[35]?"nv_selected":""))},m(w,_){m(w,e,_),r(e,n),r(e,i),r(e,o),r(o,a),c||(p=O(e,"click",d),c=!0)},p(w,_){t=w,_[0]&2&&f!==(f="nv "+(t[1]===t[35]?"nv_selected":""))&&u(e,"class",f)},d(w){w&&h(e),c=!1,p()}}}function dl(t){let e,n=t[35]&&js(t);return{c(){n&&n.c(),e=$n()},m(i,o){n&&n.m(i,o),m(i,e,o)},p(i,o){i[35]&&n.p(i,o)},d(i){n&&n.d(i),i&&h(e)}}}function pl(t){let e,n=t[32].html+"",i;return{c(){e=new or(!1),i=$n(),e.a=i},m(o,l){e.m(n,o,l),m(o,i,l)},p(o,l){l[0]&64&&n!==(n=o[32].html+"")&&e.p(n)},d(o){o&&h(i),o&&e.d()}}}function Rs(t){let e,n,i,o,l,a,f,c,p,d,w,_,y,g,b,L,W,U,j,q,A,S,z,D,C,N,B,Y=t[1].label+"",$,_e,te,oe,K,be,H,Q,le,ae,ee,ve,re,ge,ne,Me,Le,R,V=t[5]&&cl(t);function Re(F,ie){return F[0]?Es:Os}let Ae=Re(t),Te=Ae(t),ce=!t[5]&&fl(t),he=t[7],fe=[];for(let F=0;F`,w=v(),_=s("a"),_.innerHTML=`Github `,y=v(),g=s("a"),g.innerHTML=`Source - `,b=v(),L=s("br"),W=v(),U=s("div"),j=v(),q=s("br"),A=v(),S=s("div");for(let F=0;F',ge=v(),ne=s("div");for(let F=0;F{Gt(X,1)}),ti()}Pe?(K=new Pe(tt(F)),Kn(K.$$.fragment),Ee(K.$$.fragment,1),Vt(K,oe,null)):K=null}if(ie[0]&64){me=F[6];let X;for(X=0;X{await confirm("Are you sure?")||H.preventDefault()}),qe.onFileDropEvent(H=>{W(`File drop: ${JSON.stringify(H.payload)}`)});const o=navigator.userAgent.toLowerCase(),l=o.includes("android")||o.includes("iphone"),u=[{label:"Welcome",component:kr,icon:"i-ph-hand-waving"},{label:"Communication",component:Lr,icon:"i-codicon-radio-tower"},!l&&{label:"CLI",component:Cr,icon:"i-codicon-terminal"},!l&&{label:"Dialog",component:Nr,icon:"i-codicon-multiple-windows"},{label:"File system",component:Gr,icon:"i-codicon-files"},{label:"HTTP",component:es,icon:"i-ph-globe-hemisphere-west"},!l&&{label:"Notifications",component:os,icon:"i-codicon-bell-dot"},!l&&{label:"Window",component:ss,icon:"i-codicon-window"},!l&&{label:"Shortcuts",component:ds,icon:"i-codicon-record-keys"},{label:"Shell",component:ms,icon:"i-codicon-terminal-bash"},!l&&{label:"Updater",component:ws,icon:"i-codicon-cloud-download"},!l&&{label:"Clipboard",component:Ts,icon:"i-codicon-clippy"},{label:"WebRTC",component:zs,icon:"i-ph-broadcast"}];let f=u[0];function c(H){n(1,f=H)}let p;at(async()=>{const H=Nt();n(2,p=await H.isMaximized()),Xt("tauri://resize",async()=>{n(2,p=await H.isMaximized())})});function d(){Nt().minimize()}async function k(){const H=Nt();await H.isMaximized()?H.unmaximize():H.maximize()}let _=!1;async function y(){_||(_=await Ul("Are you sure that you want to close this window?",{title:"Tauri API"}),_&&Nt().close())}let g;at(()=>{n(3,g=localStorage&&localStorage.getItem("theme")=="dark"),ml(g)});function b(){n(3,g=!g),ml(g)}let L=yl([]);_l(t,L,H=>n(6,i=H));function W(H){L.update(x=>[{html:`
[${new Date().toLocaleTimeString()}]: `+(typeof H=="string"?H:JSON.stringify(H,null,1))+"
"},...x])}function U(H){L.update(x=>[{html:`
[${new Date().toLocaleTimeString()}]: `+H+"
"},...x])}function j(){L.update(()=>[])}let q,A,S;function z(H){S=H.clientY;const x=window.getComputedStyle(q);A=parseInt(x.height,10);const le=ee=>{const ve=ee.clientY-S,re=A-ve;n(4,q.style.height=`${re{document.removeEventListener("mouseup",ae),document.removeEventListener("mousemove",le)};document.addEventListener("mouseup",ae),document.addEventListener("mousemove",le)}let D;at(async()=>{n(5,D=await El()==="win32")});let C=!1,N,B,Y=!1,$=0,_e=0;const te=(H,x,le)=>Math.min(Math.max(x,H),le);at(()=>{n(18,N=document.querySelector("#sidebar")),B=document.querySelector("#sidebarToggle"),document.addEventListener("click",H=>{B.contains(H.target)?n(0,C=!C):C&&!N.contains(H.target)&&n(0,C=!1)}),document.addEventListener("touchstart",H=>{if(B.contains(H.target))return;const x=H.touches[0].clientX;(0{if(Y){const x=H.touches[0].clientX;_e=x;const le=(x-$)/10;N.style.setProperty("--translate-x",`-${te(0,C?0-le:18.75-le,18.75)}rem`)}}),document.addEventListener("touchend",()=>{if(Y){const H=(_e-$)/10;n(0,C=C?H>-(18.75/2):H>18.75/2)}Y=!1})});const oe=()=>Ri("https://tauri.app/"),K=H=>{c(H),n(0,C=!1)};function be(H){Yn[H?"unshift":"push"](()=>{q=H,n(4,q)})}return t.$$.update=()=>{if(t.$$.dirty[0]&1){const H=document.querySelector("#sidebar");H&&Fs(H,C)}},[C,f,p,g,q,D,i,u,c,d,k,y,b,L,W,U,j,z,N,oe,K,be]}class Us extends Se{constructor(e){super(),Ce(this,e,Is,Hs,ke,{},null,[-1,-1])}}new Us({target:document.querySelector("#app")}); + `,b=v(),L=s("br"),W=v(),U=s("div"),j=v(),q=s("br"),A=v(),S=s("div");for(let F=0;F',ge=v(),ne=s("div");for(let F=0;F{Gt(X,1)}),ti()}Pe?(K=new Pe(tt(F)),Kn(K.$$.fragment),Ee(K.$$.fragment,1),Vt(K,oe,null)):K=null}if(ie[0]&64){me=F[6];let X;for(X=0;X{await confirm("Are you sure?")||H.preventDefault()}),qe.onFileDropEvent(H=>{W(`File drop: ${JSON.stringify(H.payload)}`)});const o=navigator.userAgent.toLowerCase(),l=o.includes("android")||o.includes("iphone"),a=[{label:"Welcome",component:wr,icon:"i-ph-hand-waving"},{label:"Communication",component:zr,icon:"i-codicon-radio-tower"},!l&&{label:"CLI",component:Tr,icon:"i-codicon-terminal"},!l&&{label:"Dialog",component:Ur,icon:"i-codicon-multiple-windows"},{label:"File system",component:Vr,icon:"i-codicon-files"},{label:"HTTP",component:$r,icon:"i-ph-globe-hemisphere-west"},!l&&{label:"Notifications",component:is,icon:"i-codicon-bell-dot"},!l&&{label:"Window",component:rs,icon:"i-codicon-window"},!l&&{label:"Shortcuts",component:fs,icon:"i-codicon-record-keys"},{label:"Shell",component:hs,icon:"i-codicon-terminal-bash"},!l&&{label:"Updater",component:ys,icon:"i-codicon-cloud-download"},!l&&{label:"Clipboard",component:Ms,icon:"i-codicon-clippy"},{label:"WebRTC",component:Ss,icon:"i-ph-broadcast"}];let f=a[0];function c(H){n(1,f=H)}let p;at(async()=>{const H=Nt();n(2,p=await H.isMaximized()),Xt("tauri://resize",async()=>{n(2,p=await H.isMaximized())})});function d(){Nt().minimize()}async function w(){const H=Nt();await H.isMaximized()?H.unmaximize():H.maximize()}let _=!1;async function y(){_||(_=await Ul("Are you sure that you want to close this window?",{title:"Tauri API"}),_&&Nt().close())}let g;at(()=>{n(3,g=localStorage&&localStorage.getItem("theme")=="dark"),ml(g)});function b(){n(3,g=!g),ml(g)}let L=yl([]);_l(t,L,H=>n(6,i=H));function W(H){L.update(Q=>[{html:`
[${new Date().toLocaleTimeString()}]: `+(typeof H=="string"?H:JSON.stringify(H,null,1))+"
"},...Q])}function U(H){L.update(Q=>[{html:`
[${new Date().toLocaleTimeString()}]: `+H+"
"},...Q])}function j(){L.update(()=>[])}let q,A,S;function z(H){S=H.clientY;const Q=window.getComputedStyle(q);A=parseInt(Q.height,10);const le=ee=>{const ve=ee.clientY-S,re=A-ve;n(4,q.style.height=`${re{document.removeEventListener("mouseup",ae),document.removeEventListener("mousemove",le)};document.addEventListener("mouseup",ae),document.addEventListener("mousemove",le)}let D;at(async()=>{n(5,D=await El()==="win32")});let C=!1,N,B,Y=!1,$=0,_e=0;const te=(H,Q,le)=>Math.min(Math.max(Q,H),le);at(()=>{n(18,N=document.querySelector("#sidebar")),B=document.querySelector("#sidebarToggle"),document.addEventListener("click",H=>{B.contains(H.target)?n(0,C=!C):C&&!N.contains(H.target)&&n(0,C=!1)}),document.addEventListener("touchstart",H=>{if(B.contains(H.target))return;const Q=H.touches[0].clientX;(0{if(Y){const Q=H.touches[0].clientX;_e=Q;const le=(Q-$)/10;N.style.setProperty("--translate-x",`-${te(0,C?0-le:18.75-le,18.75)}rem`)}}),document.addEventListener("touchend",()=>{if(Y){const H=(_e-$)/10;n(0,C=C?H>-(18.75/2):H>18.75/2)}Y=!1})});const oe=()=>Ri("https://tauri.app/"),K=H=>{c(H),n(0,C=!1)};function be(H){Yn[H?"unshift":"push"](()=>{q=H,n(4,q)})}return t.$$.update=()=>{if(t.$$.dirty[0]&1){const H=document.querySelector("#sidebar");H&&Hs(H,C)}},[C,f,p,g,q,D,i,a,c,d,w,y,b,L,W,U,j,z,N,oe,K,be]}class Is extends Se{constructor(e){super(),Ce(this,e,Fs,Rs,ke,{},null,[-1,-1])}}new Is({target:document.querySelector("#app")}); diff --git a/examples/api/package.json b/examples/api/package.json index 9e89c09528d..29cff8b6135 100644 --- a/examples/api/package.json +++ b/examples/api/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "type": "module", "scripts": { - "dev": "vite --clearScreen false --port 5000", + "dev": "vite --clearScreen false", "build": "vite build", "serve": "vite preview", "tauri": "node ../../tooling/cli/node/tauri.js" @@ -15,9 +15,10 @@ "devDependencies": { "@iconify-json/codicon": "^1.1.10", "@iconify-json/ph": "^1.1.1", - "@sveltejs/vite-plugin-svelte": "^1.0.0-next.49", + "@sveltejs/vite-plugin-svelte": "^1.0.1", + "internal-ip": "^7.0.0", "svelte": "^3.49.0", "unocss": "^0.39.3", - "vite": "^2.9.12" + "vite": "^3.0.9" } } diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 6f0b89c6ad5..e868532a72a 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -112,9 +112,7 @@ version = "0.1.0" dependencies = [ "android_logger", "env_logger 0.9.0", - "jni", "log", - "mobile-entry-point", "paste", "serde", "serde_json", @@ -1659,6 +1657,18 @@ dependencies = [ "safemem", ] +[[package]] +name = "local-ip-address" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d6f43d42d775afa8073bfa6aba569b340a3635efa8c9f7702c9c6ed209692f" +dependencies = [ + "libc", + "neli", + "thiserror", + "windows-sys", +] + [[package]] name = "lock_api" version = "0.4.7" @@ -1808,17 +1818,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "mobile-entry-point" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bef5a90018326583471cccca10424d7b3e770397b02f03276543cbb9b6a1a6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "multipart" version = "0.18.0" @@ -1878,6 +1877,16 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "neli" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9053554eb5dcb7e10d9cdab1206965bde870eed5d0d341532ca035e3ba221508" +dependencies = [ + "byteorder", + "libc", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -3243,6 +3252,7 @@ dependencies = [ "brotli", "ico", "json-patch", + "local-ip-address", "plist", "png 0.17.5", "proc-macro2", @@ -3255,6 +3265,7 @@ dependencies = [ "tauri-utils", "thiserror", "time", + "url", "uuid 1.1.2", "walkdir", ] diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index c4d4d321aef..cbcd8c1aafc 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -45,11 +45,9 @@ tauri-runtime-wry = { path = "../../../core/tauri-runtime-wry/" } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.9.0" -jni = "0.19.0" paste = "1.0" [target.'cfg(target_os = "ios")'.dependencies] -mobile-entry-point = "0.1.0" env_logger = "0.9.0" [features] diff --git a/examples/api/src-tauri/tauri.conf.json b/examples/api/src-tauri/tauri.conf.json index 3f3dd772e28..6ad3567bc2f 100644 --- a/examples/api/src-tauri/tauri.conf.json +++ b/examples/api/src-tauri/tauri.conf.json @@ -2,8 +2,8 @@ "$schema": "../../../tooling/cli/schema.json", "build": { "distDir": "../dist", - "devPath": "http://localhost:5000", - "beforeDevCommand": "yarn dev", + "devPath": "http://localhost:5173", + "beforeDevCommand": "yarn dev --host", "beforeBuildCommand": "yarn build" }, "package": { diff --git a/examples/api/vite.config.js b/examples/api/vite.config.js index 10825d144ed..9157d89bbd6 100644 --- a/examples/api/vite.config.js +++ b/examples/api/vite.config.js @@ -1,22 +1,30 @@ import { defineConfig } from 'vite' import Unocss from 'unocss/vite' import { svelte } from '@sveltejs/vite-plugin-svelte' +import { internalIpV4 } from 'internal-ip' // https://vitejs.dev/config/ -export default defineConfig({ - plugins: [Unocss(), svelte()], - build: { - rollupOptions: { - output: { - entryFileNames: `assets/[name].js`, - chunkFileNames: `assets/[name].js`, - assetFileNames: `assets/[name].[ext]` +export default defineConfig(async ({ command, mode }) => { + const host = await internalIpV4() + return { + plugins: [Unocss(), svelte()], + build: { + rollupOptions: { + output: { + entryFileNames: `assets/[name].js`, + chunkFileNames: `assets/[name].js`, + assetFileNames: `assets/[name].[ext]` + } + } + }, + server: { + hmr: { + host, + port: 5183 + }, + fs: { + allow: ['.', '../../tooling/api/dist'] } - } - }, - server: { - fs: { - allow: ['.', '../../tooling/api/dist'] } } }) diff --git a/examples/api/yarn.lock b/examples/api/yarn.lock index fa6f5c15193..b9cd0c3c268 100644 --- a/examples/api/yarn.lock +++ b/examples/api/yarn.lock @@ -15,6 +15,11 @@ resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.5.2.tgz#8c2d931ff927be0ebe740169874a3d4004ab414b" integrity sha512-CQkeV+oJxUazwjlHD0/3ZD08QWKuGQkhnrKo3e6ly5pd48VUpXbb77q0xMU4+vc2CkJnDS02Eq/M9ugyX20XZA== +"@esbuild/linux-loong64@0.14.54": + version "0.14.54" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" + integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== + "@iconify-json/codicon@^1.1.10": version "1.1.10" resolved "https://registry.yarnpkg.com/@iconify-json/codicon/-/codicon-1.1.10.tgz#22fee909be51afebfbcc6cd57209064b5363f202" @@ -80,15 +85,15 @@ estree-walker "^2.0.1" picomatch "^2.2.2" -"@sveltejs/vite-plugin-svelte@^1.0.0-next.49": - version "1.0.0-next.49" - resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.49.tgz#44cc00a19c6c23002516b66c5ab90ee66720df57" - integrity sha512-AKh0Ka8EDgidnxWUs8Hh2iZLZovkETkefO99XxZ4sW4WGJ7VFeBx5kH/NIIGlaNHLcrIvK3CK0HkZwC3Cici0A== +"@sveltejs/vite-plugin-svelte@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.1.tgz#7f468f03c933fcdfc60d4773671c73f33b9ef4d6" + integrity sha512-PorCgUounn0VXcpeJu+hOweZODKmGuLHsLomwqSj+p26IwjjGffmYQfVHtiTWq+NqaUuuHWWG7vPge6UFw4Aeg== dependencies: "@rollup/pluginutils" "^4.2.1" debug "^4.3.4" deepmerge "^4.2.2" - kleur "^4.1.4" + kleur "^4.1.5" magic-string "^0.26.2" svelte-hmr "^0.14.12" @@ -323,6 +328,13 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + defu@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/defu/-/defu-6.0.0.tgz#b397a6709a2f3202747a3d9daf9446e41ad0c5fc" @@ -338,138 +350,139 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -esbuild-android-64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz#ef95b42c67bcf4268c869153fa3ad1466c4cea6b" - integrity sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g== - -esbuild-android-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.47.tgz#4ebd7ce9fb250b4695faa3ee46fd3b0754ecd9e6" - integrity sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ== - -esbuild-darwin-64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.47.tgz#e0da6c244f497192f951807f003f6a423ed23188" - integrity sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA== - -esbuild-darwin-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.47.tgz#cd40fd49a672fca581ed202834239dfe540a9028" - integrity sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw== - -esbuild-freebsd-64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.47.tgz#8da6a14c095b29c01fc8087a16cb7906debc2d67" - integrity sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ== - -esbuild-freebsd-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.47.tgz#ad31f9c92817ff8f33fd253af7ab5122dc1b83f6" - integrity sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ== - -esbuild-linux-32@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.47.tgz#de085e4db2e692ea30c71208ccc23fdcf5196c58" - integrity sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw== - -esbuild-linux-64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.47.tgz#2a9321bbccb01f01b04cebfcfccbabeba3658ba1" - integrity sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw== - -esbuild-linux-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.47.tgz#b9da7b6fc4b0ca7a13363a0c5b7bb927e4bc535a" - integrity sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw== - -esbuild-linux-arm@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.47.tgz#56fec2a09b9561c337059d4af53625142aded853" - integrity sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA== - -esbuild-linux-mips64le@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.47.tgz#9db21561f8f22ed79ef2aedb7bbef082b46cf823" - integrity sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg== - -esbuild-linux-ppc64le@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.47.tgz#dc3a3da321222b11e96e50efafec9d2de408198b" - integrity sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w== - -esbuild-linux-riscv64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.47.tgz#9bd6dcd3dca6c0357084ecd06e1d2d4bf105335f" - integrity sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g== - -esbuild-linux-s390x@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.47.tgz#a458af939b52f2cd32fc561410d441a51f69d41f" - integrity sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw== - -esbuild-netbsd-64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.47.tgz#6388e785d7e7e4420cb01348d7483ab511b16aa8" - integrity sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ== - -esbuild-openbsd-64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.47.tgz#309af806db561aa886c445344d1aacab850dbdc5" - integrity sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw== - -esbuild-sunos-64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.47.tgz#3f19612dcdb89ba6c65283a7ff6e16f8afbf8aaa" - integrity sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ== - -esbuild-windows-32@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.47.tgz#a92d279c8458d5dc319abcfeb30aa49e8f2e6f7f" - integrity sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ== - -esbuild-windows-64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.47.tgz#2564c3fcf0c23d701edb71af8c52d3be4cec5f8a" - integrity sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ== - -esbuild-windows-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.47.tgz#86d9db1a22d83360f726ac5fba41c2f625db6878" - integrity sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ== - -esbuild@^0.14.27: - version "0.14.47" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.47.tgz#0d6415f6bd8eb9e73a58f7f9ae04c5276cda0e4d" - integrity sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA== +esbuild-android-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" + integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ== + +esbuild-android-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771" + integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg== + +esbuild-darwin-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25" + integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug== + +esbuild-darwin-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73" + integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw== + +esbuild-freebsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d" + integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg== + +esbuild-freebsd-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48" + integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q== + +esbuild-linux-32@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5" + integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw== + +esbuild-linux-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652" + integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg== + +esbuild-linux-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b" + integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig== + +esbuild-linux-arm@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59" + integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw== + +esbuild-linux-mips64le@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34" + integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw== + +esbuild-linux-ppc64le@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e" + integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ== + +esbuild-linux-riscv64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8" + integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg== + +esbuild-linux-s390x@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6" + integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA== + +esbuild-netbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81" + integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w== + +esbuild-openbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b" + integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw== + +esbuild-sunos-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da" + integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw== + +esbuild-windows-32@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31" + integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w== + +esbuild-windows-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4" + integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ== + +esbuild-windows-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982" + integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg== + +esbuild@^0.14.47: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2" + integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA== optionalDependencies: - esbuild-android-64 "0.14.47" - esbuild-android-arm64 "0.14.47" - esbuild-darwin-64 "0.14.47" - esbuild-darwin-arm64 "0.14.47" - esbuild-freebsd-64 "0.14.47" - esbuild-freebsd-arm64 "0.14.47" - esbuild-linux-32 "0.14.47" - esbuild-linux-64 "0.14.47" - esbuild-linux-arm "0.14.47" - esbuild-linux-arm64 "0.14.47" - esbuild-linux-mips64le "0.14.47" - esbuild-linux-ppc64le "0.14.47" - esbuild-linux-riscv64 "0.14.47" - esbuild-linux-s390x "0.14.47" - esbuild-netbsd-64 "0.14.47" - esbuild-openbsd-64 "0.14.47" - esbuild-sunos-64 "0.14.47" - esbuild-windows-32 "0.14.47" - esbuild-windows-64 "0.14.47" - esbuild-windows-arm64 "0.14.47" + "@esbuild/linux-loong64" "0.14.54" + esbuild-android-64 "0.14.54" + esbuild-android-arm64 "0.14.54" + esbuild-darwin-64 "0.14.54" + esbuild-darwin-arm64 "0.14.54" + esbuild-freebsd-64 "0.14.54" + esbuild-freebsd-arm64 "0.14.54" + esbuild-linux-32 "0.14.54" + esbuild-linux-64 "0.14.54" + esbuild-linux-arm "0.14.54" + esbuild-linux-arm64 "0.14.54" + esbuild-linux-mips64le "0.14.54" + esbuild-linux-ppc64le "0.14.54" + esbuild-linux-riscv64 "0.14.54" + esbuild-linux-s390x "0.14.54" + esbuild-netbsd-64 "0.14.54" + esbuild-openbsd-64 "0.14.54" + esbuild-sunos-64 "0.14.54" + esbuild-windows-32 "0.14.54" + esbuild-windows-64 "0.14.54" + esbuild-windows-arm64 "0.14.54" estree-walker@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -execa@^5.1.1: +execa@^5.0.0, execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -558,6 +571,26 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +internal-ip@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-7.0.0.tgz#5b1c6a9d7e188aa73a1b69717daf50c8d8ed774f" + integrity sha512-qE4TeD4brqC45Vq/+VASeMiS1KRyfBkR6HT2sh9pZVVCzSjPkaCEfKFU+dL0PRv7NHJtvoKN2r82G6wTfzorkw== + dependencies: + default-gateway "^6.0.3" + ipaddr.js "^2.0.1" + is-ip "^3.1.0" + p-event "^4.2.0" + +ip-regex@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== + +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -584,6 +617,13 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-ip@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8" + integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q== + dependencies: + ip-regex "^4.0.0" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -604,10 +644,10 @@ jiti@^1.13.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.14.0.tgz#5350fff532a4d891ca4bcd700c47c1f40e6ee326" integrity sha512-4IwstlaKQc9vCTC+qUXLM1hajy2ImiL9KnLvVYiaHOtS/v3wRjhLlGl121AmgDgx/O43uKmxownJghS5XMya2A== -kleur@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" - integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== +kleur@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" + integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== kolorist@^1.5.1: version "1.5.1" @@ -710,6 +750,18 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +p-event@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" + integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== + dependencies: + p-timeout "^3.1.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -724,6 +776,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-timeout@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -764,10 +823,10 @@ picomatch@^2.2.2: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -postcss@^8.4.13: - version "8.4.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" - integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== +postcss@^8.4.16: + version "8.4.16" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c" + integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ== dependencies: nanoid "^3.3.4" picocolors "^1.0.0" @@ -785,7 +844,7 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -resolve@^1.22.0: +resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -799,10 +858,10 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rollup@^2.59.0: - version "2.75.7" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.75.7.tgz#221ff11887ae271e37dcc649ba32ce1590aaa0b9" - integrity sha512-VSE1iy0eaAYNCxEXaleThdFXqZJ42qDBatAwrfnPlENEZ8erQ+0LYX4JXOLPceWfZpV1VtZwZ3dFCuOZiSyFtQ== +"rollup@>=2.75.6 <2.77.0 || ~2.77.0": + version "2.77.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12" + integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g== optionalDependencies: fsevents "~2.3.2" @@ -926,15 +985,15 @@ unocss@^0.39.3: "@unocss/transformer-variant-group" "0.39.3" "@unocss/vite" "0.39.3" -vite@^2.9.12: - version "2.9.12" - resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.12.tgz#b1d636b0a8ac636afe9d83e3792d4895509a941b" - integrity sha512-suxC36dQo9Rq1qMB2qiRorNJtJAdxguu5TMvBHOc/F370KvqAe9t48vYp+/TbPKRNrMh/J55tOUmkuIqstZaew== +vite@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.9.tgz#45fac22c2a5290a970f23d66c1aef56a04be8a30" + integrity sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw== dependencies: - esbuild "^0.14.27" - postcss "^8.4.13" - resolve "^1.22.0" - rollup "^2.59.0" + esbuild "^0.14.47" + postcss "^8.4.16" + resolve "^1.22.1" + rollup ">=2.75.6 <2.77.0 || ~2.77.0" optionalDependencies: fsevents "~2.3.2" diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 64621941648..0ca964b023a 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -127,18 +127,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" -[[package]] -name = "bicycle" -version = "0.1.0" -source = "git+https://github.com/BrainiumLLC/bicycle?rev=28080e0c6fa4067d9dd1b0f2b7322b6b32178e1f#28080e0c6fa4067d9dd1b0f2b7322b6b32178e1f" -dependencies = [ - "handlebars 3.5.5", - "log", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "bit_field" version = "0.10.1" @@ -199,16 +187,6 @@ dependencies = [ "byte-tools", ] -[[package]] -name = "bossy" -version = "0.2.1" -source = "git+https://github.com/lucasfernog/bossy?branch=fix/winapi-features#83ee04daddbc9b985b5f8dcf54d7229f0542d41d" -dependencies = [ - "libc", - "log", - "winapi 0.3.9", -] - [[package]] name = "bstr" version = "0.2.17" @@ -272,13 +250,10 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#32de2201d4a607c2d4b910cc2a33d052fe852bd8" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#9733784cbb8af4fe0accc634463ff58424c88e62" dependencies = [ - "bicycle", - "bossy", "cocoa", "colored 1.9.3", - "const-utf16", "core-foundation 0.7.0", "deunicode", "dunce", @@ -286,20 +261,20 @@ dependencies = [ "english-numbers", "env_logger 0.7.1", "freedesktop_entry_parser", + "handlebars 3.5.5", "heck 0.4.0", - "hit", "home", "ignore", "indexmap", "java-properties", "lexical-core", + "libc", "log", "objc", "objc_id", "once-cell-regex", "openssl", "path_abs", - "reserved-names", "serde", "serde_json", "structopt", @@ -307,8 +282,8 @@ dependencies = [ "thiserror", "toml", "ureq", - "windows 0.26.0", - "yes-or-no", + "winapi 0.3.9", + "windows 0.39.0", ] [[package]] @@ -483,12 +458,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "const-utf16" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90feefab165fe011746e3be2f0708b7b180fcbd9f5054ff81a454d7bd93d8285" - [[package]] name = "constant_time_eq" version = "0.1.5" @@ -1334,18 +1303,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hit" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a8b280c3c6e2a2a51b3dde2f1071b28afff0ebce59e29443b1b391e7efe32fd" -dependencies = [ - "bossy", - "log", - "once-cell-regex", - "thiserror", -] - [[package]] name = "hmac" version = "0.12.1" @@ -2740,14 +2697,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "reserved-names" -version = "0.1.0" -source = "git+https://github.com/BrainiumLLC/reserved-names#cc46545db485b13851e9136280b7d8e5a95a9d18" -dependencies = [ - "thiserror", -] - [[package]] name = "ring" version = "0.16.20" @@ -3350,12 +3299,12 @@ version = "1.0.5" dependencies = [ "anyhow", "base64", - "bossy", "cargo-mobile", "clap 3.2.7", "colored 2.0.0", "ctrlc", "dialoguer", + "dirs-next", "env_logger 0.9.0", "glob", "handlebars 4.3.1", @@ -4009,19 +3958,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "347cdcaae1addebdff584aea1f9fbc0426dedbe1315f1dcf30c7a9876401cd25" -dependencies = [ - "windows_aarch64_msvc 0.26.0", - "windows_i686_gnu 0.26.0", - "windows_i686_msvc 0.26.0", - "windows_x86_64_gnu 0.26.0", - "windows_x86_64_msvc 0.26.0", -] - [[package]] name = "windows" version = "0.37.0" @@ -4036,6 +3972,19 @@ dependencies = [ "windows_x86_64_msvc 0.37.0", ] +[[package]] +name = "windows" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" +dependencies = [ + "windows_aarch64_msvc 0.39.0", + "windows_i686_gnu 0.39.0", + "windows_i686_msvc 0.39.0", + "windows_x86_64_gnu 0.39.0", + "windows_x86_64_msvc 0.39.0", +] + [[package]] name = "windows-implement" version = "0.37.0" @@ -4065,12 +4014,6 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169" -[[package]] -name = "windows_aarch64_msvc" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7758986b022add546ae53ccad31f4852ce6bd2e2c2d3cc2b1d7d06dea0b90da" - [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -4084,10 +4027,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" [[package]] -name = "windows_i686_gnu" -version = "0.26.0" +name = "windows_aarch64_msvc" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29261214caab8e589f61031ba1ccd5c3c25e61db2118a3aec4459f58ff798726" +checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" [[package]] name = "windows_i686_gnu" @@ -4102,10 +4045,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" [[package]] -name = "windows_i686_msvc" -version = "0.26.0" +name = "windows_i686_gnu" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43984fb3b944743142112ae926e7adeccb60f35bb81d43114f4d0fe2871f60ba" +checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" [[package]] name = "windows_i686_msvc" @@ -4120,10 +4063,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" [[package]] -name = "windows_x86_64_gnu" -version = "0.26.0" +name = "windows_i686_msvc" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a80fc90e1ad19769e596a3f58d0776319059e21cac9069a5a2a791362ce7190" +checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" [[package]] name = "windows_x86_64_gnu" @@ -4138,10 +4081,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" [[package]] -name = "windows_x86_64_msvc" -version = "0.26.0" +name = "windows_x86_64_gnu" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc24ddac19a0cf02ad2b32d8897f202fc1a13ef285e2d4774e6610783cc8398f" +checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" [[package]] name = "windows_x86_64_msvc" @@ -4155,6 +4098,12 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" + [[package]] name = "winreg" version = "0.10.1" @@ -4189,16 +4138,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" -[[package]] -name = "yes-or-no" -version = "0.1.0" -source = "git+https://github.com/BrainiumLLC/yes-or-no#71d932693601fcf93ff906b90ad61f85b52117c5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zeroize" version = "1.5.5" diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 9c2b987ab36..aec9d268ed9 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -26,14 +26,11 @@ include = [ name = "cargo-tauri" path = "src/main.rs" -[patch.crates-io] -bossy = { git = "https://github.com/lucasfernog/bossy", branch = "fix/winapi-features" } - [dependencies] # cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } -bossy = "0.2" textwrap = { version = "0.11.0", features = ["term_size"] } +dirs-next = "2.0" thiserror = "1" clap = { version = "3.2", features = [ "derive" ] } anyhow = "1.0" diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index 91ec0a3995f..a8b1bd06bc2 100644 --- a/tooling/cli/schema.json +++ b/tooling/cli/schema.json @@ -2433,7 +2433,7 @@ "type": "object", "properties": { "developmentTeam": { - "description": "The development team. This value is required for iOS development because code signing is enforced. The `APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.", + "description": "The development team. This value is required for iOS development because code signing is enforced. The `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.", "type": [ "string", "null" diff --git a/tooling/cli/src/build.rs b/tooling/cli/src/build.rs index 3320588531b..b0f2ecd5f8a 100644 --- a/tooling/cli/src/build.rs +++ b/tooling/cli/src/build.rs @@ -83,6 +83,8 @@ pub fn command(mut options: Options) -> Result<()> { let config_guard = config.lock().unwrap(); let config_ = config_guard.as_ref().unwrap(); + let mut interface = AppInterface::new(config_)?; + let bundle_identifier_source = match config_.find_bundle_identifier_overwriter() { Some(source) if source == MERGE_CONFIG_EXTENSION_NAME => merge_config_path.unwrap_or(source), Some(source) => source, @@ -154,7 +156,6 @@ pub fn command(mut options: Options) -> Result<()> { list.extend(config_.build.features.clone().unwrap_or_default()); } - let mut interface = AppInterface::new(config_)?; let app_settings = interface.app_settings(); let interface_options = options.clone().into(); diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index 7db4a57a4eb..53502e2d63f 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -49,7 +49,7 @@ pub struct Options { pub features: Option>, /// Exit on panic #[clap(short, long)] - exit_on_panic: bool, + pub exit_on_panic: bool, /// JSON string or path to JSON file to merge with tauri.conf.json #[clap(short, long)] pub config: Option, @@ -74,6 +74,15 @@ pub fn command(options: Options) -> Result<()> { } fn command_internal(mut options: Options) -> Result<()> { + let mut interface = setup(&mut options)?; + let exit_on_panic = options.exit_on_panic; + let no_watch = options.no_watch; + interface.dev(options.into(), move |status, reason| { + on_dev_exit(status, reason, exit_on_panic, no_watch) + }) +} + +pub fn setup(options: &mut Options) -> Result { let tauri_path = tauri_dir(); options.config = if let Some(config) = &options.config { Some(if config.starts_with('{') { @@ -89,6 +98,8 @@ fn command_internal(mut options: Options) -> Result<()> { let config = get_config(options.config.as_deref())?; + let interface = AppInterface::new(config.lock().unwrap().as_ref().unwrap())?; + if let Some(before_dev) = config .lock() .unwrap() @@ -261,13 +272,7 @@ fn command_internal(mut options: Options) -> Result<()> { } } - let mut interface = AppInterface::new(config.lock().unwrap().as_ref().unwrap())?; - - let exit_on_panic = options.exit_on_panic; - let no_watch = options.no_watch; - interface.dev(options.into(), move |status, reason| { - on_dev_exit(status, reason, exit_on_panic, no_watch) - }) + Ok(interface) } fn on_dev_exit(status: ExitStatus, reason: ExitReason, exit_on_panic: bool, no_watch: bool) { diff --git a/tooling/cli/src/helpers/flock.rs b/tooling/cli/src/helpers/flock.rs new file mode 100644 index 00000000000..0ab6ae36a44 --- /dev/null +++ b/tooling/cli/src/helpers/flock.rs @@ -0,0 +1,363 @@ +// Copyright 2019-2021 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +// taken from https://github.com/rust-lang/cargo/blob/b0c9586f4cbf426914df47c65de38ea323772c74/src/cargo/util/flock.rs +#![allow(dead_code)] + +use std::fs::{create_dir_all, File, OpenOptions}; +use std::io; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::path::{Path, PathBuf}; + +use crate::Result; +use anyhow::Context as _; +use sys::*; + +#[derive(Debug)] +pub struct FileLock { + f: Option, + path: PathBuf, + state: State, +} + +#[derive(PartialEq, Debug)] +enum State { + Unlocked, + Shared, + Exclusive, +} + +impl FileLock { + /// Returns the underlying file handle of this lock. + pub fn file(&self) -> &File { + self.f.as_ref().unwrap() + } + + /// Returns the underlying path that this lock points to. + /// + /// Note that special care must be taken to ensure that the path is not + /// referenced outside the lifetime of this lock. + pub fn path(&self) -> &Path { + assert_ne!(self.state, State::Unlocked); + &self.path + } + + /// Returns the parent path containing this file + pub fn parent(&self) -> &Path { + assert_ne!(self.state, State::Unlocked); + self.path.parent().unwrap() + } +} + +impl Read for FileLock { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.file().read(buf) + } +} + +impl Seek for FileLock { + fn seek(&mut self, to: SeekFrom) -> io::Result { + self.file().seek(to) + } +} + +impl Write for FileLock { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.file().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.file().flush() + } +} + +impl Drop for FileLock { + fn drop(&mut self) { + if self.state != State::Unlocked { + if let Some(f) = self.f.take() { + let _ = unlock(&f); + } + } + } +} + +/// Opens exclusive access to a file, returning the locked version of a +/// file. +/// +/// This function will create a file at `path` if it doesn't already exist +/// (including intermediate directories), and then it will acquire an +/// exclusive lock on `path`. If the process must block waiting for the +/// lock, the `msg` is logged. +/// +/// The returned file can be accessed to look at the path and also has +/// read/write access to the underlying file. +pub fn open_rw

(path: P, msg: &str) -> Result +where + P: AsRef, +{ + open( + path.as_ref(), + OpenOptions::new().read(true).write(true).create(true), + State::Exclusive, + msg, + ) +} + +/// Opens shared access to a file, returning the locked version of a file. +/// +/// This function will fail if `path` doesn't already exist, but if it does +/// then it will acquire a shared lock on `path`. If the process must block +/// waiting for the lock, the `msg` is logged. +/// +/// The returned file can be accessed to look at the path and also has read +/// access to the underlying file. Any writes to the file will return an +/// error. +pub fn open_ro

(path: P, msg: &str) -> Result +where + P: AsRef, +{ + open( + path.as_ref(), + OpenOptions::new().read(true), + State::Shared, + msg, + ) +} + +fn open(path: &Path, opts: &OpenOptions, state: State, msg: &str) -> Result { + // If we want an exclusive lock then if we fail because of NotFound it's + // likely because an intermediate directory didn't exist, so try to + // create the directory and then continue. + let f = opts + .open(&path) + .or_else(|e| { + if e.kind() == io::ErrorKind::NotFound && state == State::Exclusive { + create_dir_all(path.parent().unwrap())?; + Ok(opts.open(&path)?) + } else { + Err(anyhow::Error::from(e)) + } + }) + .with_context(|| format!("failed to open: {}", path.display()))?; + match state { + State::Exclusive => { + acquire(msg, path, &|| try_lock_exclusive(&f), &|| { + lock_exclusive(&f) + })?; + } + State::Shared => { + acquire(msg, path, &|| try_lock_shared(&f), &|| lock_shared(&f))?; + } + State::Unlocked => {} + } + Ok(FileLock { + f: Some(f), + path: path.to_path_buf(), + state, + }) +} + +/// Acquires a lock on a file in a "nice" manner. +/// +/// Almost all long-running blocking actions in Cargo have a status message +/// associated with them as we're not sure how long they'll take. Whenever a +/// conflicted file lock happens, this is the case (we're not sure when the lock +/// will be released). +/// +/// This function will acquire the lock on a `path`, printing out a nice message +/// to the console if we have to wait for it. It will first attempt to use `try` +/// to acquire a lock on the crate, and in the case of contention it will emit a +/// status message based on `msg` to `config`'s shell, and then use `block` to +/// block waiting to acquire a lock. +/// +/// Returns an error if the lock could not be acquired or if any error other +/// than a contention error happens. +fn acquire( + msg: &str, + path: &Path, + lock_try: &dyn Fn() -> io::Result<()>, + lock_block: &dyn Fn() -> io::Result<()>, +) -> Result<()> { + // File locking on Unix is currently implemented via `flock`, which is known + // to be broken on NFS. We could in theory just ignore errors that happen on + // NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking + // forever**, even if the "non-blocking" flag is passed! + // + // As a result, we just skip all file locks entirely on NFS mounts. That + // should avoid calling any `flock` functions at all, and it wouldn't work + // there anyway. + // + // [1]: https://github.com/rust-lang/cargo/issues/2615 + if is_on_nfs_mount(path) { + return Ok(()); + } + + match lock_try() { + Ok(()) => return Ok(()), + + // In addition to ignoring NFS which is commonly not working we also + // just ignore locking on filesystems that look like they don't + // implement file locking. + Err(e) if error_unsupported(&e) => return Ok(()), + + Err(e) => { + if !error_contended(&e) { + let e = anyhow::Error::from(e); + let cx = format!("failed to lock file: {}", path.display()); + return Err(e.context(cx)); + } + } + } + let msg = format!("waiting for file lock on {}", msg); + log::info!(action = "Blocking"; "{}", &msg); + + lock_block().with_context(|| format!("failed to lock file: {}", path.display()))?; + return Ok(()); + + #[cfg(all(target_os = "linux", not(target_env = "musl")))] + fn is_on_nfs_mount(path: &Path) -> bool { + use std::ffi::CString; + use std::mem; + use std::os::unix::prelude::*; + + let path = match CString::new(path.as_os_str().as_bytes()) { + Ok(path) => path, + Err(_) => return false, + }; + + unsafe { + let mut buf: libc::statfs = mem::zeroed(); + let r = libc::statfs(path.as_ptr(), &mut buf); + + r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32 + } + } + + #[cfg(any(not(target_os = "linux"), target_env = "musl"))] + fn is_on_nfs_mount(_path: &Path) -> bool { + false + } +} + +#[cfg(unix)] +mod sys { + use std::fs::File; + use std::io::{Error, Result}; + use std::os::unix::io::AsRawFd; + + pub(super) fn lock_shared(file: &File) -> Result<()> { + flock(file, libc::LOCK_SH) + } + + pub(super) fn lock_exclusive(file: &File) -> Result<()> { + flock(file, libc::LOCK_EX) + } + + pub(super) fn try_lock_shared(file: &File) -> Result<()> { + flock(file, libc::LOCK_SH | libc::LOCK_NB) + } + + pub(super) fn try_lock_exclusive(file: &File) -> Result<()> { + flock(file, libc::LOCK_EX | libc::LOCK_NB) + } + + pub(super) fn unlock(file: &File) -> Result<()> { + flock(file, libc::LOCK_UN) + } + + pub(super) fn error_contended(err: &Error) -> bool { + err.raw_os_error().map_or(false, |x| x == libc::EWOULDBLOCK) + } + + pub(super) fn error_unsupported(err: &Error) -> bool { + match err.raw_os_error() { + // Unfortunately, depending on the target, these may or may not be the same. + // For targets in which they are the same, the duplicate pattern causes a warning. + #[allow(unreachable_patterns)] + Some(libc::ENOTSUP | libc::EOPNOTSUPP) => true, + Some(libc::ENOSYS) => true, + _ => false, + } + } + + #[cfg(not(target_os = "solaris"))] + fn flock(file: &File, flag: libc::c_int) -> Result<()> { + let ret = unsafe { libc::flock(file.as_raw_fd(), flag) }; + if ret < 0 { + Err(Error::last_os_error()) + } else { + Ok(()) + } + } + + #[cfg(target_os = "solaris")] + fn flock(file: &File, flag: libc::c_int) -> Result<()> { + // Solaris lacks flock(), so simply succeed with a no-op + Ok(()) + } +} + +#[cfg(windows)] +mod sys { + use std::fs::File; + use std::io::{Error, Result}; + use std::mem; + use std::os::windows::io::AsRawHandle; + + use winapi::shared::minwindef::DWORD; + use winapi::shared::winerror::{ERROR_INVALID_FUNCTION, ERROR_LOCK_VIOLATION}; + use winapi::um::fileapi::{LockFileEx, UnlockFile}; + use winapi::um::minwinbase::{LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY}; + + pub(super) fn lock_shared(file: &File) -> Result<()> { + lock_file(file, 0) + } + + pub(super) fn lock_exclusive(file: &File) -> Result<()> { + lock_file(file, LOCKFILE_EXCLUSIVE_LOCK) + } + + pub(super) fn try_lock_shared(file: &File) -> Result<()> { + lock_file(file, LOCKFILE_FAIL_IMMEDIATELY) + } + + pub(super) fn try_lock_exclusive(file: &File) -> Result<()> { + lock_file(file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY) + } + + pub(super) fn error_contended(err: &Error) -> bool { + err + .raw_os_error() + .map_or(false, |x| x == ERROR_LOCK_VIOLATION as i32) + } + + pub(super) fn error_unsupported(err: &Error) -> bool { + err + .raw_os_error() + .map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32) + } + + pub(super) fn unlock(file: &File) -> Result<()> { + unsafe { + let ret = UnlockFile(file.as_raw_handle(), 0, 0, !0, !0); + if ret == 0 { + Err(Error::last_os_error()) + } else { + Ok(()) + } + } + } + + fn lock_file(file: &File, flags: DWORD) -> Result<()> { + unsafe { + let mut overlapped = mem::zeroed(); + let ret = LockFileEx(file.as_raw_handle(), flags, 0, !0, !0, &mut overlapped); + if ret == 0 { + Err(Error::last_os_error()) + } else { + Ok(()) + } + } + } +} diff --git a/tooling/cli/src/helpers/mod.rs b/tooling/cli/src/helpers/mod.rs index 5284a6ec54a..a3cb56a0191 100644 --- a/tooling/cli/src/helpers/mod.rs +++ b/tooling/cli/src/helpers/mod.rs @@ -4,6 +4,7 @@ pub mod app_paths; pub mod config; +pub mod flock; pub mod framework; pub mod template; pub mod updater_signature; diff --git a/tooling/cli/src/interface/mod.rs b/tooling/cli/src/interface/mod.rs index 6133531befa..e946f071d9b 100644 --- a/tooling/cli/src/interface/mod.rs +++ b/tooling/cli/src/interface/mod.rs @@ -12,7 +12,12 @@ use std::{ use crate::helpers::config::Config; use tauri_bundler::bundle::{PackageType, Settings, SettingsBuilder}; -pub use rust::{Options, Rust as AppInterface}; +pub use rust::{MobileOptions, Options, Rust as AppInterface}; + +pub trait DevProcess { + fn kill(&mut self) -> std::io::Result<()>; + fn try_wait(&mut self) -> std::io::Result>; +} pub trait AppSettings { fn get_package_settings(&self) -> tauri_bundler::PackageSettings; @@ -83,4 +88,9 @@ pub trait Interface: Sized { options: Options, on_exit: F, ) -> crate::Result<()>; + fn mobile_dev crate::Result>>( + &mut self, + options: MobileOptions, + runner: R, + ) -> crate::Result<()>; } diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 228c5bb5cbf..4e787e05e0e 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT use std::{ + collections::HashMap, ffi::OsStr, fs::{File, FileType}, io::{Read, Write}, @@ -10,7 +11,6 @@ use std::{ process::ExitStatus, str::FromStr, sync::{ - atomic::{AtomicBool, Ordering}, mpsc::{channel, sync_channel}, Arc, Mutex, }, @@ -24,13 +24,12 @@ use log::warn; use log::{debug, info}; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use serde::Deserialize; -use shared_child::SharedChild; use tauri_bundler::{ AppCategory, BundleBinary, BundleSettings, DebianSettings, MacOsSettings, PackageSettings, UpdaterSettings, WindowsSettings, }; -use super::{AppSettings, ExitReason, Interface}; +use super::{AppSettings, DevProcess, ExitReason, Interface}; use crate::helpers::{ app_paths::tauri_dir, config::{reload as reload_config, wix_settings, Config}, @@ -40,7 +39,7 @@ mod desktop; mod manifest; use manifest::{rewrite_manifest, Manifest}; -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone)] pub struct Options { pub runner: Option, pub debug: bool, @@ -79,30 +78,13 @@ impl From for Options { } } -pub struct DevChild { - manually_killed_app: Arc, - build_child: Arc, - app_child: Arc>>>, -} - -impl DevChild { - fn kill(&self) -> std::io::Result<()> { - if let Some(child) = &*self.app_child.lock().unwrap() { - child.kill()?; - } else { - self.build_child.kill()?; - } - self.manually_killed_app.store(true, Ordering::Relaxed); - Ok(()) - } - - fn try_wait(&self) -> std::io::Result> { - if let Some(child) = &*self.app_child.lock().unwrap() { - child.try_wait() - } else { - self.build_child.try_wait() - } - } +#[derive(Debug, Clone)] +pub struct MobileOptions { + pub debug: bool, + pub features: Option>, + pub args: Vec, + pub config: Option, + pub no_watch: bool, } #[derive(Debug)] @@ -144,8 +126,10 @@ impl Interface for Rust { std::env::set_var("MACOSX_DEPLOYMENT_TARGET", minimum_system_version); } + let app_settings = RustAppSettings::new(config, manifest)?; + Ok(Self { - app_settings: RustAppSettings::new(config, manifest)?, + app_settings, config_features: config.build.features.clone().unwrap_or_default(), product_name: config.package.product_name.clone(), available_targets: None, @@ -173,28 +157,56 @@ impl Interface for Rust { fn dev( &mut self, - options: Options, + mut options: Options, on_exit: F, ) -> crate::Result<()> { let on_exit = Arc::new(on_exit); - let on_exit_ = on_exit.clone(); + let run_args = dev_options( + &mut options.args, + &mut options.features, + self.app_settings.manifest.features(), + ); if options.no_watch { let (tx, rx) = sync_channel(1); - self.run_dev(options, move |status, reason| { + self.run_dev(options, run_args, move |status, reason| { tx.send(()).unwrap(); - on_exit_(status, reason) + on_exit(status, reason) })?; rx.recv().unwrap(); Ok(()) } else { - let child = self.run_dev(options.clone(), move |status, reason| { - on_exit_(status, reason) - })?; + let config = options.config.clone(); + let run = Arc::new(|rust: &mut Rust| { + let on_exit = on_exit.clone(); + rust.run_dev(options.clone(), run_args.clone(), move |status, reason| { + on_exit(status, reason) + }) + }); + self.run_dev_watcher(config, run) + } + } + + fn mobile_dev crate::Result>>( + &mut self, + mut options: MobileOptions, + runner: R, + ) -> crate::Result<()> { + dev_options( + &mut options.args, + &mut options.features, + self.app_settings.manifest.features(), + ); - self.run_dev_watcher(child, options, on_exit) + if options.no_watch { + runner(options)?; + Ok(()) + } else { + let config = options.config.clone(); + let run = Arc::new(|_rust: &mut Rust| runner(options.clone())); + self.run_dev_watcher(config, run) } } } @@ -223,50 +235,54 @@ fn lookup(dir: &Path, mut f: F) { } } -impl Rust { - fn run_dev( - &mut self, - mut options: Options, - on_exit: F, - ) -> crate::Result { - if !options.args.contains(&"--no-default-features".into()) { - let manifest_features = self.app_settings.manifest.features(); - let enable_features: Vec = manifest_features - .get("default") - .cloned() - .unwrap_or_default() - .into_iter() - .filter(|feature| { - if let Some(manifest_feature) = manifest_features.get(feature) { - !manifest_feature.contains(&"tauri/custom-protocol".into()) - } else { - feature != "tauri/custom-protocol" - } - }) - .collect(); - options.args.push("--no-default-features".into()); - if !enable_features.is_empty() { - options - .features - .get_or_insert(Vec::new()) - .extend(enable_features); - } +fn dev_options( + args: &mut Vec, + features: &mut Option>, + manifest_features: HashMap>, +) -> Vec { + if !args.contains(&"--no-default-features".into()) { + let enable_features: Vec = manifest_features + .get("default") + .cloned() + .unwrap_or_default() + .into_iter() + .filter(|feature| { + if let Some(manifest_feature) = manifest_features.get(feature) { + !manifest_feature.contains(&"tauri/custom-protocol".into()) + } else { + feature != "tauri/custom-protocol" + } + }) + .collect(); + args.push("--no-default-features".into()); + if !enable_features.is_empty() { + features.get_or_insert(Vec::new()).extend(enable_features); } + } - let mut args = Vec::new(); - let mut run_args = Vec::new(); - let mut reached_run_args = false; - for arg in options.args.clone() { - if reached_run_args { - run_args.push(arg); - } else if arg == "--" { - reached_run_args = true; - } else { - args.push(arg); - } + let mut dev_args = Vec::new(); + let mut run_args = Vec::new(); + let mut reached_run_args = false; + for arg in args.clone() { + if reached_run_args { + run_args.push(arg); + } else if arg == "--" { + reached_run_args = true; + } else { + dev_args.push(arg); } - options.args = args; + } + *args = dev_args; + run_args +} +impl Rust { + fn run_dev( + &mut self, + options: Options, + run_args: Vec, + on_exit: F, + ) -> crate::Result> { desktop::run_dev( options, run_args, @@ -276,14 +292,16 @@ impl Rust { self.product_name.clone(), on_exit, ) + .map(|c| Box::new(c) as Box) } - fn run_dev_watcher( + fn run_dev_watcher crate::Result>>( &mut self, - child: DevChild, - options: Options, - on_exit: Arc, + config: Option, + run: Arc, ) -> crate::Result<()> { + let child = run(self)?; + let process = Arc::new(Mutex::new(child)); let (tx, rx) = channel(); let tauri_path = tauri_dir(); @@ -326,7 +344,7 @@ impl Rust { } loop { - let on_exit = on_exit.clone(); + let run = run.clone(); if let Ok(event) = rx.recv() { let event_path = match event { DebouncedEvent::Create(path) => Some(path), @@ -338,7 +356,7 @@ impl Rust { if let Some(event_path) = event_path { if event_path.file_name() == Some(OsStr::new("tauri.conf.json")) { - let config = reload_config(options.config.as_deref())?; + let config = reload_config(config.as_deref())?; self.app_settings.manifest = rewrite_manifest(config.lock().unwrap().as_ref().unwrap())?; } else { @@ -353,9 +371,7 @@ impl Rust { break; } } - *p = self.run_dev(options.clone(), move |status, reason| { - on_exit(status, reason) - })?; + *p = run(self)?; } } } diff --git a/tooling/cli/src/interface/rust/desktop.rs b/tooling/cli/src/interface/rust/desktop.rs index b93a3d82610..66a785a8747 100644 --- a/tooling/cli/src/interface/rust/desktop.rs +++ b/tooling/cli/src/interface/rust/desktop.rs @@ -1,7 +1,8 @@ -use super::{AppSettings, DevChild, ExitReason, Options, RustAppSettings, Target}; +use super::{AppSettings, DevProcess, ExitReason, Options, RustAppSettings, Target}; use crate::CommandExt; use anyhow::Context; +#[cfg(target_os = "linux")] use heck::ToKebabCase; use shared_child::SharedChild; use std::{ @@ -15,6 +16,34 @@ use std::{ }, }; +pub struct DevChild { + manually_killed_app: Arc, + build_child: Option>, + app_child: Arc>>>, +} + +impl DevProcess for DevChild { + fn kill(&mut self) -> std::io::Result<()> { + if let Some(child) = &*self.app_child.lock().unwrap() { + child.kill()?; + } else if let Some(child) = &self.build_child { + child.kill()?; + } + self.manually_killed_app.store(true, Ordering::Relaxed); + Ok(()) + } + + fn try_wait(&mut self) -> std::io::Result> { + if let Some(child) = &*self.app_child.lock().unwrap() { + child.try_wait() + } else if let Some(child) = &self.build_child { + child.try_wait() + } else { + unreachable!() + } + } +} + pub fn run_dev( options: Options, run_args: Vec, @@ -23,7 +52,7 @@ pub fn run_dev( app_settings: &RustAppSettings, product_name: Option, on_exit: F, -) -> crate::Result { +) -> crate::Result { let bin_path = app_settings.app_binary_path(&options)?; let manually_killed_app = Arc::new(AtomicBool::default()); @@ -73,7 +102,7 @@ pub fn run_dev( Ok(DevChild { manually_killed_app, - build_child, + build_child: Some(build_child), app_child, }) } diff --git a/tooling/cli/src/lib.rs b/tooling/cli/src/lib.rs index 5fc4b6433e8..30748532458 100644 --- a/tooling/cli/src/lib.rs +++ b/tooling/cli/src/lib.rs @@ -14,7 +14,7 @@ mod mobile; mod plugin; mod signer; -use clap::{FromArgMatches, IntoApp, Parser, Subcommand}; +use clap::{FromArgMatches, IntoApp, Parser, Subcommand, ValueEnum}; use env_logger::fmt::Color; use env_logger::Builder; use log::{debug, log_enabled, Level}; @@ -23,9 +23,33 @@ use std::io::{BufReader, Write}; use std::process::{exit, Command, ExitStatus, Output, Stdio}; use std::{ ffi::OsString, + fmt::Display, sync::{Arc, Mutex}, }; +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +pub enum RunMode { + Desktop, + #[cfg(target_os = "macos")] + Ios, + Android, +} + +impl Display for RunMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Desktop => "desktop", + #[cfg(target_os = "macos")] + Self::Ios => "iOS", + Self::Android => "android", + } + ) + } +} + #[derive(Deserialize)] pub struct VersionMetadata { tauri: String, diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index ba3f7f0304f..290ce1fe2c8 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -6,10 +6,11 @@ use cargo_mobile::{ android::{ adb, config::{Config as AndroidConfig, Metadata as AndroidMetadata}, - device::Device, + device::{Device, RunError}, env::{Env, Error as EnvError}, target::{BuildError, Target}, }, + config::Config, device::PromptError, opts::{NoiseLevel, Profile}, os, @@ -19,12 +20,15 @@ use cargo_mobile::{ use clap::{Parser, Subcommand}; use super::{ - ensure_init, get_config, get_metadata, + ensure_init, get_config, init::{command as init_command, Options as InitOptions}, - Target as MobileTarget, + write_options, CliOptions, DevChild, Target as MobileTarget, +}; +use crate::{ + helpers::{config::get as get_tauri_config, flock}, + interface::{AppSettings, DevProcess, Interface, MobileOptions, Options as InterfaceOptions}, + Result, }; -use crate::helpers::config::get as get_tauri_config; -use crate::Result; pub(crate) mod project; @@ -32,16 +36,24 @@ pub(crate) mod project; enum Error { #[error(transparent)] EnvInitFailed(EnvError), + #[error(transparent)] + InitDotCargo(super::init::Error), #[error("invalid tauri configuration: {0}")] InvalidTauriConfig(String), #[error("{0}")] ProjectNotInitialized(String), #[error(transparent)] OpenFailed(os::OpenFileError), + #[error("{0}")] + DevFailed(String), #[error(transparent)] BuildFailed(BuildError), + #[error(transparent)] + RunFailed(RunError), #[error("{0}")] TargetInvalid(String), + #[error(transparent)] + FailedToPromptForDevice(PromptError), } #[derive(Parser)] @@ -68,9 +80,44 @@ pub struct BuildOptions { value_parser(clap::builder::PossibleValuesParser::new(["aarch64", "armv7", "i686", "x86_64"])) )] targets: Option>, - /// Builds with the debug flag + /// Builds with the release flag #[clap(short, long)] - debug: bool, + release: bool, +} + +#[derive(Debug, Clone, Parser)] +#[clap(about = "Android dev")] +pub struct DevOptions { + /// List of cargo features to activate + #[clap(short, long, multiple_occurrences(true), multiple_values(true))] + pub features: Option>, + /// Exit on panic + #[clap(short, long)] + exit_on_panic: bool, + /// JSON string or path to JSON file to merge with tauri.conf.json + #[clap(short, long)] + pub config: Option, + /// Disable the file watcher + #[clap(long)] + pub no_watch: bool, + /// Open Android Studio instead of trying to run on a connected device + #[clap(short, long)] + pub open: bool, +} + +impl From for crate::dev::Options { + fn from(options: DevOptions) -> Self { + Self { + runner: None, + target: None, + features: options.features, + exit_on_panic: options.exit_on_panic, + config: options.config, + release_mode: false, + args: Vec::new(), + no_watch: options.no_watch, + } + } } #[derive(Subcommand)] @@ -78,6 +125,7 @@ enum Commands { Init(InitOptions), /// Open project in Android Studio Open, + Dev(DevOptions), #[clap(hide(true))] Build(BuildOptions), } @@ -87,76 +135,182 @@ pub fn command(cli: Cli) -> Result<()> { Commands::Init(options) => init_command(options, MobileTarget::Android)?, Commands::Open => open()?, Commands::Build(options) => build(options)?, + Commands::Dev(options) => dev(options)?, } Ok(()) } -fn with_config( - f: impl FnOnce(&AndroidConfig, &AndroidMetadata) -> Result<(), Error>, -) -> Result<(), Error> { - let tauri_config = - get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - let config = get_config(tauri_config_); - let metadata = get_metadata(tauri_config_); - f(config.android(), metadata.android()) +fn with_config( + f: impl FnOnce(&Config, &AndroidConfig, &AndroidMetadata) -> Result, +) -> Result { + let (config, metadata) = { + let tauri_config = + get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + get_config(tauri_config_) + }; + f(&config, config.android(), metadata.android()) +} + +fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { + let device_list = + adb::device_list(env).map_err(|cause| PromptError::detection_failed("Android", cause))?; + if !device_list.is_empty() { + let index = if device_list.len() > 1 { + prompt::list( + concat!("Detected ", "Android", " devices"), + device_list.iter(), + "device", + None, + "Device", + ) + .map_err(|cause| PromptError::prompt_failed("Android", cause))? + } else { + 0 + }; + let device = device_list.into_iter().nth(index).unwrap(); + println!( + "Detected connected device: {} with target {:?}", + device, + device.target().triple, + ); + Ok(device) + } else { + Err(PromptError::none_detected("Android")) + } +} + +fn dev(options: DevOptions) -> Result<()> { + with_config(|_, config, _metadata| { + run_dev(options, config).map_err(|e| Error::DevFailed(e.to_string())) + }) + .map_err(Into::into) +} + +fn run_dev(options: DevOptions, config: &AndroidConfig) -> Result<()> { + let mut dev_options = options.clone().into(); + let mut interface = crate::dev::setup(&mut dev_options)?; + + let bundle_identifier = { + let tauri_config = + get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + tauri_config_.tauri.bundle.identifier.clone() + }; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&InterfaceOptions { + debug: !dev_options.release_mode, + ..Default::default() + })?; + let out_dir = bin_path.parent().unwrap(); + let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; + + let open = options.open; + interface.mobile_dev( + MobileOptions { + debug: true, + features: options.features, + args: Vec::new(), + config: options.config, + no_watch: options.no_watch, + }, + |options| { + let cli_options = CliOptions { + features: options.features.clone(), + args: options.args.clone(), + vars: Default::default(), + }; + write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; + + if open { + open_dev(config) + } else { + match run(options) { + Ok(c) => Ok(Box::new(c) as Box), + Err(Error::FailedToPromptForDevice(e)) => { + log::error!("{}", e); + open_dev(config) + } + Err(e) => Err(e.into()), + } + } + }, + ) +} + +fn open_dev(config: &AndroidConfig) -> ! { + log::info!("Opening Android Studio"); + if let Err(e) = os::open_file_with("Android Studio", config.project_dir()) { + log::error!("{}", e); + } + loop { + std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); + } } fn open() -> Result<()> { - with_config(|config, _metadata| { + with_config(|_, config, _metadata| { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) - })?; - Ok(()) + }) + .map_err(Into::into) } -fn build(options: BuildOptions) -> Result<()> { +fn run(options: MobileOptions) -> Result { let profile = if options.debug { Profile::Debug } else { Profile::Release }; - fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { - let device_list = - adb::device_list(env).map_err(|cause| PromptError::detection_failed("Android", cause))?; - if !device_list.is_empty() { - let index = if device_list.len() > 1 { - prompt::list( - concat!("Detected ", "Android", " devices"), - device_list.iter(), - "device", - None, - "Device", - ) - .map_err(|cause| PromptError::prompt_failed("Android", cause))? - } else { - 0 - }; - let device = device_list.into_iter().nth(index).unwrap(); - println!( - "Detected connected device: {} with target {:?}", - device, - device.target().triple, - ); - Ok(device) - } else { - Err(PromptError::none_detected("Android")) - } - } + with_config(|root_conf, config, metadata| { + let build_app_bundle = metadata.asset_packs().is_some(); + + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + + let env = Env::new().map_err(Error::EnvInitFailed)?; + super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + + device_prompt(&env) + .map_err(Error::FailedToPromptForDevice)? + .run( + config, + &env, + NoiseLevel::Polite, + profile, + None, + build_app_bundle, + false, + ".MainActivity".into(), + ) + .map_err(Error::RunFailed) + }) + .map(|c| DevChild(Some(c))) +} + +fn build(options: BuildOptions) -> Result<()> { + let profile = if options.release { + Profile::Release + } else { + Profile::Debug + }; fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { device_prompt(env).map(|device| device.target()).ok() } - with_config(|config, metadata| { + with_config(|root_conf, config, metadata| { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; let env = Env::new().map_err(Error::EnvInitFailed)?; + super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; call_for_targets_with_fallback( options.targets.unwrap_or_default().iter(), @@ -164,18 +318,11 @@ fn build(options: BuildOptions) -> Result<()> { &env, |target: &Target| { target - .build( - config, - metadata, - &env, - NoiseLevel::Polite, - true.into(), - profile, - ) + .build(config, metadata, &env, NoiseLevel::Polite, true, profile) .map_err(Error::BuildFailed) }, ) .map_err(|e| Error::TargetInvalid(e.to_string()))? - })?; - Ok(()) + }) + .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs index 0d4c504f72b..c40c54a84bc 100644 --- a/tooling/cli/src/mobile/android/project.rs +++ b/tooling/cli/src/mobile/android/project.rs @@ -6,11 +6,9 @@ use crate::helpers::template; use cargo_mobile::{ android::{ config::{Config, Metadata}, - env::Env, - ndk, target::Target, }, - dot_cargo, os, + bossy, os, target::TargetTrait as _, util::{ self, @@ -42,8 +40,6 @@ pub enum Error { }, #[error("failed to symlink asset directory")] AssetDirSymlinkFailed, - #[error(transparent)] - DotCargoGenFailed(ndk::MissingToolError), #[error("failed to copy {src} to {dest}: {cause}")] FileCopyFailed { src: PathBuf, @@ -57,10 +53,8 @@ pub enum Error { pub fn gen( config: &Config, metadata: &Metadata, - env: &Env, (handlebars, mut map): (Handlebars, template::JsonMap), wrapper: &TextWrapper, - dot_cargo: &mut dot_cargo::DotCargo, ) -> Result<(), Error> { println!("Installing Android toolchains..."); Target::install_all().map_err(Error::RustupFailed)?; @@ -138,7 +132,15 @@ pub fn gen( created_dirs.push(parent); } - fs::File::create(path) + let mut options = fs::OpenOptions::new(); + + #[cfg(unix)] + if path.file_name().unwrap() == OsStr::new("gradlew") { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o755); + } + + options.create_new(true).write(true).open(path) }, ) .map_err(|e| Error::TemplateProcessingFailed(e.to_string()))?; @@ -173,16 +175,5 @@ pub fn gen( os::ln::force_symlink_relative(config.app().asset_dir(), dest, ln::TargetStyle::Directory) .map_err(|_| Error::AssetDirSymlinkFailed)?; - { - for target in Target::all().values() { - dot_cargo.insert_target( - target.triple.to_owned(), - target - .generate_cargo_config(config, env) - .map_err(Error::DotCargoGenFailed)?, - ); - } - } - Ok(()) } diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 54bb1ff0117..34a0d184717 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -2,14 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use super::{get_config, get_metadata, Target}; -use crate::helpers::{app_paths::tauri_dir, config::get as get_tauri_config, template::JsonMap}; +use super::{get_config, Target}; +use crate::helpers::{config::get as get_tauri_config, template::JsonMap}; use crate::Result; use cargo_mobile::{ - android, + android::{self, env::Env as AndroidEnv, ndk, target::Target as AndroidTarget}, + bossy, config::Config, - dot_cargo, opts, + dot_cargo, os::code_command, + target::TargetTrait as _, util::{ self, cli::{Report, TextWrapper}, @@ -18,12 +20,7 @@ use cargo_mobile::{ use clap::Parser; use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError}; -use std::{ - fs, io, - path::{Path, PathBuf}, -}; - -use opts::{NonInteractive, OpenInEditor, ReinstallDeps, SkipDevTools}; +use std::{fs, io, path::PathBuf}; #[derive(Debug, Parser)] #[clap(about = "Initializes a Tauri Android project")] @@ -37,16 +34,7 @@ pub fn command(mut options: Options, target: Target) -> Result<()> { options.ci = options.ci || std::env::var("CI").is_ok(); let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); - exec( - target, - &wrapper, - options.ci.into(), - SkipDevTools::Yes, - ReinstallDeps::Yes, - OpenInEditor::No, - tauri_dir(), - ) - .map_err(|e| anyhow::anyhow!("{:#}", e))?; + exec(target, &wrapper, options.ci, true, true).map_err(|e| anyhow::anyhow!("{:#}", e))?; Ok(()) } @@ -64,6 +52,8 @@ pub enum Error { #[error(transparent)] DotCargoLoad(dot_cargo::LoadError), #[error(transparent)] + DotCargoGenFailed(ndk::MissingToolError), + #[error(transparent)] HostTargetTripleDetection(util::HostTargetTripleError), #[cfg(target_os = "macos")] #[error(transparent)] @@ -74,55 +64,65 @@ pub enum Error { AndroidInit(super::android::project::Error), #[error(transparent)] DotCargoWrite(dot_cargo::WriteError), - #[error(transparent)] - OpenInEditor(util::OpenInEditorError), +} + +pub fn init_dot_cargo(config: &Config, android_env: Option<&AndroidEnv>) -> Result<(), Error> { + let mut dot_cargo = dot_cargo::DotCargo::load(config.app()).map_err(Error::DotCargoLoad)?; + // Mysteriously, builds that don't specify `--target` seem to fight over + // the build cache with builds that use `--target`! This means that + // alternating between i.e. `cargo run` and `cargo apple run` would + // result in clean builds being made each time you switched... which is + // pretty nightmarish. Specifying `build.target` in `.cargo/config` + // fortunately has the same effect as specifying `--target`, so now we can + // `cargo run` with peace of mind! + // + // This behavior could be explained here: + // https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags + dot_cargo + .set_default_target(util::host_target_triple().map_err(Error::HostTargetTripleDetection)?); + + if let Some(env) = android_env { + for target in AndroidTarget::all().values() { + dot_cargo.insert_target( + target.triple.to_owned(), + target + .generate_cargo_config(config.android(), env) + .map_err(Error::DotCargoGenFailed)?, + ); + } + } + + dot_cargo.write(config.app()).map_err(Error::DotCargoWrite) } pub fn exec( target: Target, wrapper: &TextWrapper, - non_interactive: NonInteractive, - skip_dev_tools: SkipDevTools, - #[allow(unused_variables)] reinstall_deps: ReinstallDeps, - open_in_editor: OpenInEditor, - cwd: impl AsRef, + non_interactive: bool, + skip_dev_tools: bool, + #[allow(unused_variables)] reinstall_deps: bool, ) -> Result { - let cwd = cwd.as_ref(); let tauri_config = get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - let config = get_config(tauri_config_); - let metadata = get_metadata(tauri_config_); + let (config, metadata) = get_config(tauri_config_); let asset_dir = config.app().asset_dir(); if !asset_dir.is_dir() { fs::create_dir_all(&asset_dir).map_err(|cause| Error::AssetDirCreation { asset_dir, cause })?; } - if skip_dev_tools.no() && util::command_present("code").unwrap_or_default() { + if !skip_dev_tools && util::command_present("code").unwrap_or_default() { let mut command = code_command(); command.add_args(&["--install-extension", "vadimcn.vscode-lldb"]); - if non_interactive.yes() { + if non_interactive { command.add_arg("--force"); } command .run_and_wait() .map_err(Error::LldbExtensionInstall)?; } - let mut dot_cargo = dot_cargo::DotCargo::load(config.app()).map_err(Error::DotCargoLoad)?; - // Mysteriously, builds that don't specify `--target` seem to fight over - // the build cache with builds that use `--target`! This means that - // alternating between i.e. `cargo run` and `cargo apple run` would - // result in clean builds being made each time you switched... which is - // pretty nightmarish. Specifying `build.target` in `.cargo/config` - // fortunately has the same effect as specifying `--target`, so now we can - // `cargo run` with peace of mind! - // - // This behavior could be explained here: - // https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags - dot_cargo - .set_default_target(util::host_target_triple().map_err(Error::HostTargetTripleDetection)?); let (handlebars, mut map) = handlebars(&config); // TODO: make this a relative path @@ -135,24 +135,26 @@ pub fn exec( ); // Generate Android Studio project - if target == Target::Android { - match android::env::Env::new() { - Ok(env) => super::android::project::gen( - config.android(), - metadata.android(), - &env, - (handlebars, map), - wrapper, - &mut dot_cargo, - ) - .map_err(Error::AndroidInit)?, + let android_env = if target == Target::Android { + match AndroidEnv::new() { + Ok(env) => { + super::android::project::gen( + config.android(), + metadata.android(), + (handlebars, map), + wrapper, + ) + .map_err(Error::AndroidInit)?; + Some(env) + } Err(err) => { if err.sdk_or_ndk_issue() { Report::action_request( - " to initialize Android environment; Android support won't be usable until you fix the issue below and re-run `cargo mobile init`!", + " to initialize Android environment; Android support won't be usable until you fix the issue below and re-run `tauri android init`!", err, ) .print(wrapper); + None } else { return Err(Error::AndroidEnv(err)); } @@ -173,20 +175,16 @@ pub fn exec( ) .map_err(Error::IosInit)?; } - } + None + }; - dot_cargo - .write(config.app()) - .map_err(Error::DotCargoWrite)?; + init_dot_cargo(&config, android_env.as_ref())?; Report::victory( "Project generated successfully!", "Make cool apps! 🌻 🐕 🎉", ) .print(wrapper); - if open_in_editor.yes() { - util::open_in_editor(cwd).map_err(Error::OpenInEditor)?; - } Ok(config) } diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index fcd56851241..22fe55fd56b 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -2,24 +2,33 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::helpers::config::get as get_tauri_config; use cargo_mobile::{ apple::{ config::{Config as AppleConfig, Metadata as AppleMetadata}, + device::{Device, RunError}, + ios_deploy, target::{CompileLibError, Target}, }, + config::Config, + device::PromptError, env::{Env, Error as EnvError}, opts::{NoiseLevel, Profile}, os, util, + util::prompt, }; use clap::{Parser, Subcommand}; use super::{ - ensure_init, get_config, get_metadata, + ensure_init, env_vars, get_config, init::{command as init_command, Options as InitOptions}, - Target as MobileTarget, + write_options, CliOptions, DevChild, Target as MobileTarget, }; -use crate::Result; +use crate::{ + helpers::{config::get as get_tauri_config, flock}, + interface::{AppSettings, DevProcess, Interface, MobileOptions, Options as InterfaceOptions}, + Result, +}; + use std::{collections::HashMap, ffi::OsStr, path::PathBuf}; pub(crate) mod project; @@ -28,12 +37,16 @@ pub(crate) mod project; enum Error { #[error(transparent)] EnvInitFailed(EnvError), + #[error(transparent)] + InitDotCargo(super::init::Error), #[error("invalid tauri configuration: {0}")] InvalidTauriConfig(String), #[error("{0}")] ProjectNotInitialized(String), #[error(transparent)] OpenFailed(os::OpenFileError), + #[error("{0}")] + DevFailed(String), #[error(transparent)] NoHomeDir(util::NoHomeDir), #[error("SDK root provided by Xcode was invalid. {sdk_root} doesn't exist or isn't a directory")] @@ -46,6 +59,10 @@ enum Error { ArchInvalid { arch: String }, #[error(transparent)] CompileLibFailed(CompileLibError), + #[error(transparent)] + FailedToPromptForDevice(PromptError), + #[error(transparent)] + RunFailed(RunError), } #[derive(Parser)] @@ -80,10 +97,49 @@ pub struct XcodeScriptOptions { arches: Vec, } +#[derive(Debug, Clone, Parser)] +#[clap(about = "iOS dev")] +pub struct DevOptions { + /// List of cargo features to activate + #[clap(short, long, multiple_occurrences(true), multiple_values(true))] + pub features: Option>, + /// Exit on panic + #[clap(short, long)] + exit_on_panic: bool, + /// JSON string or path to JSON file to merge with tauri.conf.json + #[clap(short, long)] + pub config: Option, + /// Run the code in release mode + #[clap(long = "release")] + pub release_mode: bool, + /// Disable the file watcher + #[clap(long)] + pub no_watch: bool, + /// Open Xcode instead of trying to run on a connected device + #[clap(short, long)] + pub open: bool, +} + +impl From for crate::dev::Options { + fn from(options: DevOptions) -> Self { + Self { + runner: None, + target: None, + features: options.features, + exit_on_panic: options.exit_on_panic, + config: options.config, + release_mode: options.release_mode, + args: Vec::new(), + no_watch: options.no_watch, + } + } +} + #[derive(Subcommand)] enum Commands { Init(InitOptions), Open, + Dev(DevOptions), #[clap(hide(true))] XcodeScript(XcodeScriptOptions), } @@ -92,31 +148,159 @@ pub fn command(cli: Cli) -> Result<()> { match cli.command { Commands::Init(options) => init_command(options, MobileTarget::Ios)?, Commands::Open => open()?, + Commands::Dev(options) => dev(options)?, Commands::XcodeScript(options) => xcode_script(options)?, } Ok(()) } -fn with_config( - f: impl FnOnce(&AppleConfig, &AppleMetadata) -> Result<(), Error>, -) -> Result<(), Error> { - let tauri_config = - get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - let config = get_config(tauri_config_); - let metadata = get_metadata(tauri_config_); - f(config.apple(), metadata.apple()) +fn with_config( + f: impl FnOnce(&Config, &AppleConfig, &AppleMetadata) -> Result, +) -> Result { + let (config, metadata) = { + let tauri_config = + get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + get_config(tauri_config_) + }; + f(&config, config.apple(), metadata.apple()) +} + +fn env() -> Result { + let env = Env::new() + .map_err(Error::EnvInitFailed)? + .explicit_env_vars(env_vars()); + Ok(env) +} + +fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { + let device_list = + ios_deploy::device_list(env).map_err(|cause| PromptError::detection_failed("iOS", cause))?; + if !device_list.is_empty() { + let index = if device_list.len() > 1 { + prompt::list( + concat!("Detected ", "iOS", " devices"), + device_list.iter(), + "device", + None, + "Device", + ) + .map_err(|cause| PromptError::prompt_failed("iOS", cause))? + } else { + 0 + }; + let device = device_list.into_iter().nth(index).unwrap(); + println!( + "Detected connected device: {} with target {:?}", + device, + device.target().triple, + ); + Ok(device) + } else { + Err(PromptError::none_detected("iOS")) + } +} + +fn dev(options: DevOptions) -> Result<()> { + with_config(|_, config, _metadata| { + run_dev(options, config).map_err(|e| Error::DevFailed(e.to_string())) + }) + .map_err(Into::into) +} + +fn run_dev(options: DevOptions, config: &AppleConfig) -> Result<()> { + let mut dev_options = options.clone().into(); + let mut interface = crate::dev::setup(&mut dev_options)?; + + let bundle_identifier = { + let tauri_config = + get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + tauri_config_.tauri.bundle.identifier.clone() + }; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&InterfaceOptions { + debug: !dev_options.release_mode, + ..Default::default() + })?; + let out_dir = bin_path.parent().unwrap(); + let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; + + let open = options.open; + interface.mobile_dev( + MobileOptions { + debug: true, + features: options.features, + args: Vec::new(), + config: options.config, + no_watch: options.no_watch, + }, + |options| { + let cli_options = CliOptions { + features: options.features.clone(), + args: options.args.clone(), + vars: Default::default(), + }; + write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; + if open { + open_dev(config) + } else { + match run(options) { + Ok(c) => Ok(Box::new(c) as Box), + Err(Error::FailedToPromptForDevice(e)) => { + log::error!("{}", e); + open_dev(config) + } + Err(e) => Err(e.into()), + } + } + }, + ) +} + +fn open_dev(config: &AppleConfig) -> ! { + log::info!("Opening Xcode"); + if let Err(e) = os::open_file_with("Xcode", config.project_dir()) { + log::error!("{}", e); + } + loop { + std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); + } } fn open() -> Result<()> { - with_config(|config, _metadata| { + with_config(|_, config, _metadata| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) - })?; - Ok(()) + }) + .map_err(Into::into) +} + +fn run(options: MobileOptions) -> Result { + let profile = if options.debug { + Profile::Debug + } else { + Profile::Release + }; + + with_config(|root_conf, config, _| { + ensure_init(config.project_dir(), MobileTarget::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + + let env = env()?; + super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + + device_prompt(&env) + .map_err(Error::FailedToPromptForDevice)? + .run(config, &env, NoiseLevel::Polite, false, profile) + .map_err(Error::RunFailed) + }) + .map(|c| DevChild(Some(c))) } fn xcode_script(options: XcodeScriptOptions) -> Result<()> { @@ -135,8 +319,9 @@ fn xcode_script(options: XcodeScriptOptions) -> Result<()> { let profile = profile_from_configuration(&options.configuration); let macos = macos_from_platform(&options.platform); - with_config(|config, metadata| { - let env = Env::new().map_err(Error::EnvInitFailed)?; + with_config(|root_conf, config, metadata| { + let env = env()?; + super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; // The `PATH` env var Xcode gives us is missing any additions // made by the user's profile, so we'll manually add cargo's // `PATH`. @@ -216,7 +401,7 @@ fn xcode_script(options: XcodeScriptOptions) -> Result<()> { config, metadata, NoiseLevel::Polite, - true.into(), + true, profile, &env, target_env, @@ -224,7 +409,6 @@ fn xcode_script(options: XcodeScriptOptions) -> Result<()> { .map_err(Error::CompileLibFailed)?; } Ok(()) - })?; - - Ok(()) + }) + .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index b743151ada6..a0e5a8f35f5 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -9,7 +9,7 @@ use cargo_mobile::{ deps, rust_version_check, target::Target, }, - opts, + bossy, target::TargetTrait as _, util::{self, cli::TextWrapper, ln}, }; @@ -53,9 +53,9 @@ pub fn gen( metadata: &Metadata, (handlebars, mut map): (Handlebars, template::JsonMap), wrapper: &TextWrapper, - non_interactive: opts::NonInteractive, - skip_dev_tools: opts::SkipDevTools, - reinstall_deps: opts::ReinstallDeps, + non_interactive: bool, + skip_dev_tools: bool, + reinstall_deps: bool, ) -> Result<(), Error> { println!("Installing iOS toolchains..."); Target::install_all().map_err(Error::Rustup)?; diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 6aced9de5d1..aa6bf58936c 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::helpers::{app_paths::tauri_dir, config::Config as TauriConfig}; +use crate::{ + helpers::{app_paths::tauri_dir, config::Config as TauriConfig}, + interface::DevProcess, +}; use anyhow::{bail, Result}; #[cfg(target_os = "macos")] use cargo_mobile::apple::config::{ @@ -10,15 +13,48 @@ use cargo_mobile::apple::config::{ }; use cargo_mobile::{ android::config::{Metadata as AndroidMetadata, Raw as RawAndroidConfig}, + bossy, config::{app::Raw as RawAppConfig, metadata::Metadata, Config, Raw}, }; -use std::path::PathBuf; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, ffi::OsString, path::PathBuf, process::ExitStatus}; pub mod android; mod init; #[cfg(target_os = "macos")] pub mod ios; +pub struct DevChild(Option); + +impl Drop for DevChild { + fn drop(&mut self) { + // consume the handle since we're not waiting on it + // just to prevent a log error + // note that this doesn't leak any memory + self.0.take().unwrap().leak(); + } +} + +impl DevProcess for DevChild { + fn kill(&mut self) -> std::io::Result<()> { + self + .0 + .as_mut() + .unwrap() + .kill() + .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "failed to kill")) + } + + fn try_wait(&mut self) -> std::io::Result> { + self + .0 + .as_mut() + .unwrap() + .try_wait() + .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "failed to wait")) + } +} + #[derive(PartialEq, Eq)] pub enum Target { Android, @@ -44,41 +80,56 @@ impl Target { } } -fn get_metadata(_config: &TauriConfig) -> Metadata { - Metadata { - #[cfg(target_os = "macos")] - apple: AppleMetadata { - supported: true, - ios: ApplePlatform { - features: None, - frameworks: None, - valid_archs: None, - vendor_frameworks: None, - vendor_sdks: None, - asset_catalogs: None, - pods: None, - additional_targets: None, - pre_build_scripts: None, - post_compile_scripts: None, - post_build_scripts: None, - command_line_arguments: None, - }, - macos: Default::default(), - }, - android: AndroidMetadata { - supported: true, - features: None, - app_sources: None, - app_plugins: None, - project_dependencies: None, - app_dependencies: None, - app_dependencies_platform: None, - asset_packs: None, - }, +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct CliOptions { + pub features: Option>, + pub args: Vec, + pub vars: HashMap, +} + +fn options_path(bundle_identifier: &str, target: Target) -> PathBuf { + let out_dir = dirs_next::cache_dir() + .or_else(dirs_next::home_dir) + .unwrap_or_else(std::env::temp_dir); + let out_dir = out_dir.join(".tauri").join(bundle_identifier); + let _ = std::fs::create_dir_all(&out_dir); + out_dir + .join("cli-options") + .with_extension(target.command_name()) +} + +fn env_vars() -> HashMap { + let mut vars = HashMap::new(); + for (k, v) in std::env::vars_os() { + let k = k.to_string_lossy(); + if k.starts_with("TAURI") && k != "TAURI_PRIVATE_KEY" && k != "TAURI_KEY_PASSWORD" { + vars.insert(k.into_owned(), v); + } } + vars +} + +/// Writes CLI options to be used later on the Xcode and Android Studio build commands +pub fn write_options( + mut options: CliOptions, + bundle_identifier: &str, + target: Target, +) -> crate::Result<()> { + options.vars.extend(env_vars()); + std::fs::write( + options_path(bundle_identifier, target), + &serde_json::to_string(&options)?, + )?; + Ok(()) +} + +fn read_options(config: &TauriConfig, target: Target) -> crate::Result { + let data = std::fs::read_to_string(options_path(&config.tauri.bundle.identifier, target))?; + let options = serde_json::from_str(&data)?; + Ok(options) } -fn get_config(config: &TauriConfig) -> Config { +fn get_config(config: &TauriConfig) -> (Config, Metadata) { let mut s = config.tauri.bundle.identifier.rsplit('.'); let app_name = s.next().unwrap_or("app").to_string(); let mut domain = String::new(); @@ -87,6 +138,11 @@ fn get_config(config: &TauriConfig) -> Config { domain.push('.'); } domain.pop(); + + #[cfg(target_os = "macos")] + let ios_options = read_options(config, Target::Ios).unwrap_or_default(); + let android_options = read_options(config, Target::Android).unwrap_or_default(); + let raw = Raw { app: RawAppConfig { name: app_name, @@ -97,13 +153,13 @@ fn get_config(config: &TauriConfig) -> Config { }, #[cfg(target_os = "macos")] apple: Some(RawAppleConfig { - development_team: std::env::var("APPLE_DEVELOPMENT_TEAM") + development_team: std::env::var("TAURI_APPLE_DEVELOPMENT_TEAM") .ok() .or_else(|| config.tauri.ios.development_team.clone()) - .expect("you must set `tauri > iOS > developmentTeam` config value or the `APPLE_DEVELOPMENT_TEAM` environment variable"), + .expect("you must set `tauri > iOS > developmentTeam` config value or the `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable"), project_dir: None, ios_no_default_features: None, - ios_features: None, + ios_features: ios_options.features.clone(), macos_no_default_features: None, macos_features: None, bundle_version: None, @@ -118,10 +174,48 @@ fn get_config(config: &TauriConfig) -> Config { vulkan_validation: None, project_dir: None, no_default_features: None, - features: None, + features: android_options.features.clone(), }), }; - Config::from_raw(tauri_dir(), raw).unwrap() + let config = Config::from_raw(tauri_dir(), raw).unwrap(); + + let metadata = Metadata { + #[cfg(target_os = "macos")] + apple: AppleMetadata { + supported: true, + ios: ApplePlatform { + no_default_features: false, + cargo_args: Some(ios_options.args), + features: ios_options.features, + frameworks: None, + valid_archs: None, + vendor_frameworks: None, + vendor_sdks: None, + asset_catalogs: None, + pods: None, + additional_targets: None, + pre_build_scripts: None, + post_compile_scripts: None, + post_build_scripts: None, + command_line_arguments: None, + }, + macos: Default::default(), + }, + android: AndroidMetadata { + supported: true, + no_default_features: false, + cargo_args: Some(android_options.args), + features: android_options.features, + app_sources: None, + app_plugins: None, + project_dependencies: None, + app_dependencies: None, + app_dependencies_platform: None, + asset_packs: None, + }, + }; + + (config, metadata) } fn ensure_init(project_dir: PathBuf, target: Target) -> Result<()> { diff --git a/tooling/cli/templates/mobile/android/app/build.gradle.kts b/tooling/cli/templates/mobile/android/app/build.gradle.kts index cc33f0d8b57..5696afc5c09 100644 --- a/tooling/cli/templates/mobile/android/app/build.gradle.kts +++ b/tooling/cli/templates/mobile/android/app/build.gradle.kts @@ -40,6 +40,16 @@ android { } flavorDimensions.add("abi") productFlavors { + create("universal") { + dimension = "abi" + ndk { + abiFilters += listOf( + {{~#each targets}} + "{{this.abi}}",{{/each}} + ) + } + } + {{~#each targets}} create("{{this.arch}}") { @@ -75,7 +85,9 @@ dependencies { afterEvaluate { android.applicationVariants.all { - productFlavors.forEach { _ -> + tasks["mergeUniversalReleaseJniLibFolders"].dependsOn(tasks["rustBuildRelease"]) + tasks["mergeUniversalDebugJniLibFolders"].dependsOn(tasks["rustBuildDebug"]) + productFlavors.filter{ it.name != "universal" }.forEach { _ -> val archAndBuildType = name.capitalize() tasks["merge${archAndBuildType}JniLibFolders"].dependsOn(tasks["rustBuild${archAndBuildType}"]) } diff --git a/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml b/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml index cf42dcd2cb4..657c7e775c0 100644 --- a/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml +++ b/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml @@ -3,11 +3,8 @@ package="{{reverse-domain app.domain}}.{{snake-case app.name}}"> = 20) { loadedUrl = url runInitializationScripts() } diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cfe52e..00000000000 --- a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2898 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 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611da081676d42f6c3f78a2c91e7bcedddedb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1772 zcmVQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/values/strings.xml b/tooling/cli/templates/mobile/android/app/src/main/res/values/strings.xml index d7c29c74f3e..1a1f245744c 100644 --- a/tooling/cli/templates/mobile/android/app/src/main/res/values/strings.xml +++ b/tooling/cli/templates/mobile/android/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - qoo + {{app.stylized-name}} \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts b/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts index f3753b20689..86a264bddfe 100644 --- a/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts +++ b/tooling/cli/templates/mobile/android/buildSrc/build.gradle.kts @@ -2,10 +2,6 @@ plugins { `kotlin-dsl` } -kotlinDslPluginOptions { - experimentalWarning.set(false) -} - gradlePlugin { plugins { create("pluginsForCoolKids") { diff --git a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt index 9796cc8c350..381080bfe87 100644 --- a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt +++ b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt @@ -1,10 +1,8 @@ package {{reverse-domain app.domain}} -import com.android.build.gradle.* import java.io.File import org.gradle.api.DefaultTask import org.gradle.api.GradleException -import org.gradle.api.Project import org.gradle.api.logging.LogLevel import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory @@ -23,20 +21,11 @@ open class BuildTask : DefaultTask() { @TaskAction fun build() { - val rootDirRel = rootDirRel - if (rootDirRel == null) { - throw GradleException("rootDirRel cannot be null") - } - val target = target - if (target == null) { - throw GradleException("target cannot be null") - } - val release = release - if (release == null) { - throw GradleException("release cannot be null") - } + val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") + val target = target ?: throw GradleException("target cannot be null") + val release = release ?: throw GradleException("release cannot be null") project.exec { - workingDir(File(project.getProjectDir(), rootDirRel.getPath())) + workingDir(File(project.projectDir, rootDirRel.path)) executable("{{ tauri-binary }}") args(listOf("tauri", "android", "build")) if (project.logger.isEnabled(LogLevel.DEBUG)) { @@ -47,7 +36,7 @@ open class BuildTask : DefaultTask() { if (release) { args("--release") } - args(listOf("--target", "${target}")) + args(listOf("--target", target)) }.assertNormalExitValue() } } diff --git a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt index 86b86cd4fa8..7028af56d40 100644 --- a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt +++ b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/RustPlugin.kt @@ -1,11 +1,11 @@ package {{reverse-domain app.domain}} -import com.android.build.gradle.* -import java.io.File import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project +import java.io.File +import java.util.* const val TASK_GROUP = "rust" @@ -16,7 +16,7 @@ open class Config { } open class RustPlugin : Plugin { - internal lateinit var config: Config + private lateinit var config: Config override fun apply(project: Project) { config = project.extensions.create("rust", Config::class.java) @@ -28,17 +28,25 @@ open class RustPlugin : Plugin { throw GradleException("arches cannot be null") } for (profile in listOf("debug", "release")) { - val buildTask = project.tasks.maybeCreate("rustBuild${profile.capitalize()}", DefaultTask::class.java).apply { + val profileCapitalized = profile.capitalize(Locale.ROOT) + val buildTask = project.tasks.maybeCreate( + "rustBuild$profileCapitalized", + DefaultTask::class.java + ).apply { group = TASK_GROUP - description = "Build dynamic library in ${profile} mode for all targets" + description = "Build dynamic library in $profile mode for all targets" } for (targetPair in config.targets!!.withIndex()) { val targetName = targetPair.value val targetArch = config.arches!![targetPair.index] - val targetBuildTask = project.tasks.maybeCreate("rustBuild${targetArch.capitalize()}${profile.capitalize()}", BuildTask::class.java).apply { + val targetArchCapitalized = targetArch.capitalize(Locale.ROOT) + val targetBuildTask = project.tasks.maybeCreate( + "rustBuild$targetArchCapitalized$profileCapitalized", + BuildTask::class.java + ).apply { group = TASK_GROUP - description = "Build dynamic library in ${profile} mode for $targetArch" - rootDirRel = File(config.rootDirRel) + description = "Build dynamic library in $profile mode for $targetArch" + rootDirRel = config.rootDirRel?.let { File(it) } target = targetName release = profile == "release" } From 8aad710064afd0a67261748fce3ecfd6e0af882e Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 21 Aug 2022 09:58:27 -0300 Subject: [PATCH 013/423] feat(cli): enhance android build task arguments (#4986) --- tooling/cli/src/mobile/init.rs | 33 ++++++++++++++----- .../buildSrc/src/main/kotlin/BuildTask.kt | 2 +- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 34a0d184717..eeecf08c15b 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -20,7 +20,7 @@ use cargo_mobile::{ use clap::Parser; use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError}; -use std::{fs, io, path::PathBuf}; +use std::{env::current_dir, fs, io, path::PathBuf}; #[derive(Debug, Parser)] #[clap(about = "Initializes a Tauri Android project")] @@ -125,14 +125,31 @@ pub fn exec( } let (handlebars, mut map) = handlebars(&config); + + let mut args = std::env::args_os(); // TODO: make this a relative path - map.insert( - "tauri-binary", - std::env::args_os() - .next() - .unwrap_or_else(|| std::ffi::OsString::from("cargo")) - .to_string_lossy(), - ); + let tauri_binary = args + .next() + .unwrap_or_else(|| std::ffi::OsString::from("cargo")); + map.insert("tauri-binary", tauri_binary.to_string_lossy()); + let mut build_args = Vec::new(); + for arg in args { + if arg == "android" { + break; + } + let path = PathBuf::from(&arg); + if path.exists() { + if let Ok(dir) = current_dir() { + let absolute_path = util::prefix_path(dir, path); + build_args.push(absolute_path.to_string_lossy().into_owned()); + continue; + } + } + build_args.push(arg.to_string_lossy().into_owned()); + } + build_args.push("android".into()); + build_args.push("build".into()); + map.insert("tauri-binary-args", build_args); // Generate Android Studio project let android_env = if target == Target::Android { diff --git a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt index 381080bfe87..ea09602f464 100644 --- a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt +++ b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt @@ -27,7 +27,7 @@ open class BuildTask : DefaultTask() { project.exec { workingDir(File(project.projectDir, rootDirRel.path)) executable("{{ tauri-binary }}") - args(listOf("tauri", "android", "build")) + args(listOf({{quote-and-join tauri-binary-args}})) if (project.logger.isEnabled(LogLevel.DEBUG)) { args("-vv") } else if (project.logger.isEnabled(LogLevel.INFO)) { From e55d96acc1dc5de50a6e1380b880ba5279858508 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 21 Aug 2022 10:34:38 -0300 Subject: [PATCH 014/423] feat(cli): enhance iOS build task arguments (#4987) --- tooling/cli/src/mobile/init.rs | 12 ++++++------ tooling/cli/src/mobile/mod.rs | 8 ++++++++ tooling/cli/templates/mobile/ios/project.yml | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index eeecf08c15b..d95fd9294d3 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -134,9 +134,6 @@ pub fn exec( map.insert("tauri-binary", tauri_binary.to_string_lossy()); let mut build_args = Vec::new(); for arg in args { - if arg == "android" { - break; - } let path = PathBuf::from(&arg); if path.exists() { if let Ok(dir) = current_dir() { @@ -146,10 +143,13 @@ pub fn exec( } } build_args.push(arg.to_string_lossy().into_owned()); + if arg == "android" || arg == "ios" { + break; + } } - build_args.push("android".into()); - build_args.push("build".into()); - map.insert("tauri-binary-args", build_args); + build_args.push(target.ide_build_script_name().into()); + map.insert("tauri-binary-args", &build_args); + map.insert("tauri-binary-args-str", build_args.join(" ")); // Generate Android Studio project let android_env = if target == Target::Android { diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index aa6bf58936c..6a9a4bbd5d6 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -78,6 +78,14 @@ impl Target { Self::Ios => "ios", } } + + fn ide_build_script_name(&self) -> &'static str { + match self { + Self::Android => "build", + #[cfg(target_os = "macos")] + Self::Ios => "xcode-script", + } + } } #[derive(Debug, Default, Serialize, Deserialize)] diff --git a/tooling/cli/templates/mobile/ios/project.yml b/tooling/cli/templates/mobile/ios/project.yml index 54bd3bb23c7..9f9c8742d89 100644 --- a/tooling/cli/templates/mobile/ios/project.yml +++ b/tooling/cli/templates/mobile/ios/project.yml @@ -267,7 +267,7 @@ targets: VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} legacy: toolPath: {{ tauri-binary }} - arguments: ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + arguments: {{ tauri-binary-args-str }} -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} passSettings: false # prevents evil linker errors workingDirectory: $(SRCROOT)/../.. lib_{{app.name}}_macOS: @@ -275,6 +275,6 @@ targets: platform: macOS legacy: toolPath: {{ tauri-binary }} - arguments: ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + arguments: {{ tauri-binary-args-str }} -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} passSettings: false workingDirectory: $(SRCROOT)/../.. From 16360aed33c7a93d33771a454138d20636370240 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 21 Aug 2022 10:35:11 -0300 Subject: [PATCH 015/423] chore(cli): update cargo-mobile to fix cli.js mobile script running --- tooling/cli/Cargo.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 0ca964b023a..bfc1e301259 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -250,7 +250,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#9733784cbb8af4fe0accc634463ff58424c88e62" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#c30e0f1338632a9d904e0aca602408fca9d70650" dependencies = [ "cocoa", "colored 1.9.3", @@ -274,6 +274,7 @@ dependencies = [ "objc_id", "once-cell-regex", "openssl", + "os_pipe", "path_abs", "serde", "serde_json", From 9890486321c9c79ccfb7c547fafee85b5c3ffa71 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 21 Aug 2022 10:35:34 -0300 Subject: [PATCH 016/423] feat(core): add `mobile_entry_point` macro (#4983) --- .changes/build-android-env-vars.md | 5 ++ .changes/mobile-entry-point-macro.md | 5 ++ .changes/tauri-mobile-entry-point.md | 5 ++ core/tauri-build/src/lib.rs | 16 +++++++ core/tauri-macros/src/lib.rs | 6 +++ core/tauri-macros/src/mobile.rs | 71 ++++++++++++++++++++++++++++ core/tauri/Cargo.toml | 3 ++ core/tauri/src/lib.rs | 13 +++++ examples/api/src-tauri/Cargo.lock | 3 +- examples/api/src-tauri/Cargo.toml | 2 - examples/api/src-tauri/src/mobile.rs | 26 +--------- 11 files changed, 126 insertions(+), 29 deletions(-) create mode 100644 .changes/build-android-env-vars.md create mode 100644 .changes/mobile-entry-point-macro.md create mode 100644 .changes/tauri-mobile-entry-point.md create mode 100644 core/tauri-macros/src/mobile.rs diff --git a/.changes/build-android-env-vars.md b/.changes/build-android-env-vars.md new file mode 100644 index 00000000000..7091ba1597d --- /dev/null +++ b/.changes/build-android-env-vars.md @@ -0,0 +1,5 @@ +--- +"tauri-build": patch +--- + +Set environment variables used by `tauri::mobile_entry_point`. diff --git a/.changes/mobile-entry-point-macro.md b/.changes/mobile-entry-point-macro.md new file mode 100644 index 00000000000..8b39085e817 --- /dev/null +++ b/.changes/mobile-entry-point-macro.md @@ -0,0 +1,5 @@ +--- +"tauri-macros": minor +--- + +Added the `mobile_entry_point` macro. diff --git a/.changes/tauri-mobile-entry-point.md b/.changes/tauri-mobile-entry-point.md new file mode 100644 index 00000000000..6e57b976761 --- /dev/null +++ b/.changes/tauri-mobile-entry-point.md @@ -0,0 +1,5 @@ +--- +"tauri": minor +--- + +Export types required by the `mobile_entry_point` macro. diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 5ddbb3730c2..98ad640aef8 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -219,6 +219,22 @@ pub fn try_build(attributes: Attributes) -> Result<()> { } let config: Config = serde_json::from_value(config)?; + let s = config.tauri.bundle.identifier.split('.'); + let last = s.clone().count() - 1; + let mut app_name = String::new(); + let mut domain = String::new(); + for (i, w) in s.enumerate() { + if i == last { + app_name.push_str(w); + } else { + domain.push_str(w); + domain.push('_'); + } + } + domain.pop(); + println!("cargo:rustc-env=TAURI_ANDROID_DOMAIN={}", domain); + println!("cargo:rustc-env=TAURI_ANDROID_APP_NAME={}", app_name); + cfg_alias("dev", !has_feature("custom-protocol")); let mut manifest = Manifest::from_path("Cargo.toml")?; diff --git a/core/tauri-macros/src/lib.rs b/core/tauri-macros/src/lib.rs index 3af378078fc..77300d45d0d 100644 --- a/core/tauri-macros/src/lib.rs +++ b/core/tauri-macros/src/lib.rs @@ -8,6 +8,7 @@ use syn::{parse_macro_input, DeriveInput, ItemFn}; mod command; mod command_module; +mod mobile; mod runtime; #[macro_use] @@ -24,6 +25,11 @@ pub fn command(attributes: TokenStream, item: TokenStream) -> TokenStream { command::wrapper(attributes, item) } +#[proc_macro_attribute] +pub fn mobile_entry_point(attributes: TokenStream, item: TokenStream) -> TokenStream { + mobile::entry_point(attributes, item) +} + /// Accepts a list of commands functions. Creates a handler that allows commands to be called from JS with invoke(). /// /// # Examples diff --git a/core/tauri-macros/src/mobile.rs b/core/tauri-macros/src/mobile.rs new file mode 100644 index 00000000000..e98b647de51 --- /dev/null +++ b/core/tauri-macros/src/mobile.rs @@ -0,0 +1,71 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; +use std::env::var; +use syn::{parse_macro_input, spanned::Spanned, ItemFn}; + +fn get_env_var(name: &str, error: &mut Option, function: &ItemFn) -> TokenStream2 { + match var(name) { + Ok(value) => { + let ident = format_ident!("{}", value); + quote!(#ident) + } + Err(_) => { + error.replace( + syn::Error::new( + function.span(), + format!( + "`{}` env var not set, do you have a build script with tauri-build?", + name, + ), + ) + .into_compile_error(), + ); + quote!() + } + } +} + +pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { + let function = parse_macro_input!(item as ItemFn); + let function_name = function.sig.ident.clone(); + + let mut error = None; + let domain = get_env_var("TAURI_ANDROID_DOMAIN", &mut error, &function); + let app_name = get_env_var("TAURI_ANDROID_APP_NAME", &mut error, &function); + + if let Some(e) = error { + quote!(#e).into() + } else { + quote!( + fn stop_unwind T, T>(f: F) -> T { + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { + Ok(t) => t, + Err(err) => { + eprintln!("attempt to unwind out of `rust` with err: {:?}", err); + std::process::abort() + } + } + } + + #function + + fn _start_app() { + #[cfg(target_os = "android")] + { + use ::tauri::paste; + ::tauri::wry_android_binding!(#domain, #app_name, _start_app, ::tauri::wry); + } + stop_unwind(#function_name); + } + + #[cfg(not(target_os = "android"))] + #[no_mangle] + #[inline(never)] + pub extern "C" fn start_app() { + _start_app() + } + ) + .into() + } +} diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 879245f6585..459b64c9ce1 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -111,6 +111,9 @@ objc = "0.2" webview2-com = "0.16.0" win7-notifications = { version = "0.3.0", optional = true } +[target.'cfg(target_os = "android")'.dependencies] +paste = "1.0" + [target."cfg(windows)".dependencies.windows] version = "0.37.0" features = [ "Win32_Foundation" ] diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 332df1662a2..42a321a128a 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -158,6 +158,8 @@ pub use error::Error; #[cfg(shell_scope)] #[doc(hidden)] pub use regex; +#[cfg(mobile)] +pub use tauri_macros::mobile_entry_point; pub use tauri_macros::{command, generate_handler}; pub mod api; @@ -188,6 +190,17 @@ pub use tauri_utils as utils; #[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))] pub type Wry = tauri_runtime_wry::Wry; +#[cfg(all(feature = "wry", target_os = "android"))] +#[cfg_attr(doc_cfg, doc(cfg(all(feature = "wry", target_os = "android"))))] +pub use tauri_runtime_wry::wry::android_binding as wry_android_binding; + +#[cfg(all(feature = "wry", target_os = "android"))] +#[doc(hidden)] +pub use paste; +#[cfg(all(feature = "wry", target_os = "android"))] +#[doc(hidden)] +pub use tauri_runtime_wry::wry; + /// `Result` pub type Result = std::result::Result; diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index e868532a72a..73bd19c32ea 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -113,12 +113,10 @@ dependencies = [ "android_logger", "env_logger 0.9.0", "log", - "paste", "serde", "serde_json", "tauri", "tauri-build", - "tauri-runtime-wry", "tiny_http", "window-shadows", "window-vibrancy", @@ -3196,6 +3194,7 @@ dependencies = [ "open", "os_info", "os_pipe", + "paste", "percent-encoding", "png 0.17.5", "rand 0.8.5", diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index cbcd8c1aafc..92d75446cf0 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -41,11 +41,9 @@ window-shadows= "0.2" [target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] log = "0.4" -tauri-runtime-wry = { path = "../../../core/tauri-runtime-wry/" } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.9.0" -paste = "1.0" [target.'cfg(target_os = "ios")'.dependencies] env_logger = "0.9.0" diff --git a/examples/api/src-tauri/src/mobile.rs b/examples/api/src-tauri/src/mobile.rs index 8d47ad0ed5b..958fadd2d5c 100644 --- a/examples/api/src-tauri/src/mobile.rs +++ b/examples/api/src-tauri/src/mobile.rs @@ -1,6 +1,3 @@ -#[cfg(target_os = "android")] -use tauri_runtime_wry::wry::android_binding; - #[cfg(target_os = "android")] fn init_logging(app_name: &str) { android_logger::init_once( @@ -15,28 +12,7 @@ fn init_logging(_app_name: &str) { env_logger::init(); } -fn stop_unwind T, T>(f: F) -> T { - match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { - Ok(t) => t, - Err(err) => { - eprintln!("attempt to unwind out of `rust` with err: {:?}", err); - std::process::abort() - } - } -} - -fn _start_app() { - stop_unwind(main); -} - -#[no_mangle] -#[inline(never)] -pub extern "C" fn start_app() { - #[cfg(target_os = "android")] - android_binding!(com_tauri, api, _start_app, tauri_runtime_wry::wry); - _start_app() -} - +#[tauri::mobile_entry_point] fn main() { super::AppBuilder::new() .setup(|app| { From 9bb81bc3286438ed2f8d37acd68099a5c5f5f1a8 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 21 Aug 2022 17:48:38 -0300 Subject: [PATCH 017/423] refactor(android): rename BuildTask.kt command to android-studio-script (#4991) --- tooling/cli/src/mobile/android.rs | 8 ++++---- tooling/cli/src/mobile/mod.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 290ce1fe2c8..24214ccce05 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -70,7 +70,7 @@ pub struct Cli { } #[derive(Debug, Parser)] -pub struct BuildOptions { +pub struct AndroidStudioScriptOptions { /// Targets to build. #[clap( short, @@ -127,14 +127,14 @@ enum Commands { Open, Dev(DevOptions), #[clap(hide(true))] - Build(BuildOptions), + AndroidStudioScript(AndroidStudioScriptOptions), } pub fn command(cli: Cli) -> Result<()> { match cli.command { Commands::Init(options) => init_command(options, MobileTarget::Android)?, Commands::Open => open()?, - Commands::Build(options) => build(options)?, + Commands::AndroidStudioScript(options) => android_studio_script(options)?, Commands::Dev(options) => dev(options)?, } @@ -294,7 +294,7 @@ fn run(options: MobileOptions) -> Result { .map(|c| DevChild(Some(c))) } -fn build(options: BuildOptions) -> Result<()> { +fn android_studio_script(options: AndroidStudioScriptOptions) -> Result<()> { let profile = if options.release { Profile::Release } else { diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 6a9a4bbd5d6..250391c7b3d 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -81,7 +81,7 @@ impl Target { fn ide_build_script_name(&self) -> &'static str { match self { - Self::Android => "build", + Self::Android => "android-studio-script", #[cfg(target_os = "macos")] Self::Ios => "xcode-script", } From b3a3afc7de8de4021d73559288f5192732a706cf Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 22 Aug 2022 10:48:06 -0300 Subject: [PATCH 018/423] feat(core): detect android and ios platform configuration files (#4997) --- .changes/mobile-config.md | 5 +++++ core/tauri-utils/src/config.rs | 4 ++-- core/tauri-utils/src/config/parse.rs | 22 ++++++++++++++++++---- tooling/cli/schema.json | 2 +- 4 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 .changes/mobile-config.md diff --git a/.changes/mobile-config.md b/.changes/mobile-config.md new file mode 100644 index 00000000000..97417e2496f --- /dev/null +++ b/.changes/mobile-config.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": minor +--- + +Parse `android` and `ios` Tauri configuration files. diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 0545b04ce5d..36298eb600d 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -2616,8 +2616,8 @@ impl PackageConfig { /// /// In addition to the default configuration file, Tauri can /// read a platform-specific configuration from `tauri.linux.conf.json`, -/// `tauri.windows.conf.json`, and `tauri.macos.conf.json` -/// (or `Tauri.linux.toml`, `Tauri.windows.toml` and `Tauri.macos.toml` if the `Tauri.toml` format is used), +/// `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json` +/// (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used), /// which gets merged with the main configuration object. /// /// ## Configuration Structure diff --git a/core/tauri-utils/src/config/parse.rs b/core/tauri-utils/src/config/parse.rs index 6cf710faad5..369b793ce62 100644 --- a/core/tauri-utils/src/config/parse.rs +++ b/core/tauri-utils/src/config/parse.rs @@ -54,6 +54,10 @@ impl ConfigFormat { "tauri.macos.conf.json" } else if cfg!(windows) { "tauri.windows.conf.json" + } else if cfg!(target_os = "android") { + "tauri.android.conf.json" + } else if cfg!(target_os = "ios") { + "tauri.ios.conf.json" } else { "tauri.linux.conf.json" } @@ -63,6 +67,10 @@ impl ConfigFormat { "tauri.macos.conf.json5" } else if cfg!(windows) { "tauri.windows.conf.json5" + } else if cfg!(target_os = "android") { + "tauri.android.conf.json" + } else if cfg!(target_os = "ios") { + "tauri.ios.conf.json" } else { "tauri.linux.conf.json5" } @@ -72,6 +80,10 @@ impl ConfigFormat { "Tauri.macos.toml" } else if cfg!(windows) { "Tauri.windows.toml" + } else if cfg!(target_os = "android") { + "tauri.android.toml" + } else if cfg!(target_os = "ios") { + "tauri.ios.toml" } else { "Tauri.linux.toml" } @@ -143,11 +155,13 @@ pub enum ConfigError { /// Reads the configuration from the given root directory. /// -/// It first looks for a `tauri.conf.json[5]` file on the given directory. The file must exist. +/// It first looks for a `tauri.conf.json[5]` or `Tauri.toml` file on the given directory. The file must exist. /// Then it looks for a platform-specific configuration file: -/// - `tauri.macos.conf.json[5]` on macOS -/// - `tauri.linux.conf.json[5]` on Linux -/// - `tauri.windows.conf.json[5]` on Windows +/// - `tauri.macos.conf.json[5]` or `Tauri.macos.toml` on macOS +/// - `tauri.linux.conf.json[5]` or `Tauri.linux.toml` on Linux +/// - `tauri.windows.conf.json[5]` or `Tauri.windows.toml` on Windows +/// - `tauri.android.conf.json[5]` or `Tauri.android.toml` on Android +/// - `tauri.ios.conf.json[5]` or `Tauri.ios.toml` on iOS /// Merging the configurations using [JSON Merge Patch (RFC 7396)]. /// /// [JSON Merge Patch (RFC 7396)]: https://datatracker.ietf.org/doc/html/rfc7396. diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index a8b1bd06bc2..6322c3c7c6c 100644 --- a/tooling/cli/schema.json +++ b/tooling/cli/schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Config", - "description": "The Tauri configuration object. It is read from a file where you can define your frontend assets, configure the bundler, enable the app updater, define a system tray, enable APIs via the allowlist and more.\n\nThe configuration file is generated by the [`tauri init`](https://tauri.app/v1/api/cli#init) command that lives in your Tauri application source directory (src-tauri).\n\nOnce generated, you may modify it at will to customize your Tauri application.\n\n## File Formats\n\nBy default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\nTauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively. The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`. The TOML file name is `Tauri.toml`.\n\n## Platform-Specific Configuration\n\nIn addition to the default configuration file, Tauri can read a platform-specific configuration from `tauri.linux.conf.json`, `tauri.windows.conf.json`, and `tauri.macos.conf.json` (or `Tauri.linux.toml`, `Tauri.windows.toml` and `Tauri.macos.toml` if the `Tauri.toml` format is used), which gets merged with the main configuration object.\n\n## Configuration Structure\n\nThe configuration is composed of the following objects:\n\n- [`package`](#packageconfig): Package settings - [`tauri`](#tauriconfig): The Tauri config - [`build`](#buildconfig): The build configuration - [`plugins`](#pluginconfig): The plugins config\n\n```json title=\"Example tauri.config.json file\" { \"build\": { \"beforeBuildCommand\": \"\", \"beforeDevCommand\": \"\", \"devPath\": \"../dist\", \"distDir\": \"../dist\" }, \"package\": { \"productName\": \"tauri-app\", \"version\": \"0.1.0\" }, \"tauri\": { \"allowlist\": { \"all\": true }, \"bundle\": {}, \"security\": { \"csp\": null }, \"updater\": { \"active\": false }, \"windows\": [ { \"fullscreen\": false, \"height\": 600, \"resizable\": true, \"title\": \"Tauri App\", \"width\": 800 } ] } } ```", + "description": "The Tauri configuration object. It is read from a file where you can define your frontend assets, configure the bundler, enable the app updater, define a system tray, enable APIs via the allowlist and more.\n\nThe configuration file is generated by the [`tauri init`](https://tauri.app/v1/api/cli#init) command that lives in your Tauri application source directory (src-tauri).\n\nOnce generated, you may modify it at will to customize your Tauri application.\n\n## File Formats\n\nBy default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\nTauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively. The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`. The TOML file name is `Tauri.toml`.\n\n## Platform-Specific Configuration\n\nIn addition to the default configuration file, Tauri can read a platform-specific configuration from `tauri.linux.conf.json`, `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json` (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used), which gets merged with the main configuration object.\n\n## Configuration Structure\n\nThe configuration is composed of the following objects:\n\n- [`package`](#packageconfig): Package settings - [`tauri`](#tauriconfig): The Tauri config - [`build`](#buildconfig): The build configuration - [`plugins`](#pluginconfig): The plugins config\n\n```json title=\"Example tauri.config.json file\" { \"build\": { \"beforeBuildCommand\": \"\", \"beforeDevCommand\": \"\", \"devPath\": \"../dist\", \"distDir\": \"../dist\" }, \"package\": { \"productName\": \"tauri-app\", \"version\": \"0.1.0\" }, \"tauri\": { \"allowlist\": { \"all\": true }, \"bundle\": {}, \"security\": { \"csp\": null }, \"updater\": { \"active\": false }, \"windows\": [ { \"fullscreen\": false, \"height\": 600, \"resizable\": true, \"title\": \"Tauri App\", \"width\": 800 } ] } } ```", "type": "object", "properties": { "$schema": { From 4c9ea450c3b47c6b8c825ba32e9837909945ccd7 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 22 Aug 2022 12:49:58 -0300 Subject: [PATCH 019/423] feat(cli): add `android build` command (#4999) --- .changes/cli-android-build.md | 6 + tooling/cli/Cargo.lock | 2 +- tooling/cli/src/build.rs | 196 +++++++++--------- tooling/cli/src/mobile/android.rs | 194 ++++++++++++++++- .../mobile/android/app/build.gradle.kts | 4 +- 5 files changed, 299 insertions(+), 103 deletions(-) create mode 100644 .changes/cli-android-build.md diff --git a/.changes/cli-android-build.md b/.changes/cli-android-build.md new file mode 100644 index 00000000000..b1eb65a83e7 --- /dev/null +++ b/.changes/cli-android-build.md @@ -0,0 +1,6 @@ +--- +"cli.rs": minor +"cli.js": minor +--- + +Added `android build` command. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index bfc1e301259..dae6c0291e3 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -250,7 +250,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#c30e0f1338632a9d904e0aca602408fca9d70650" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#76732050ab72f308a202aeb580bcdfa6936c4e28" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/src/build.rs b/tooling/cli/src/build.rs index b0f2ecd5f8a..e212220c61c 100644 --- a/tooling/cli/src/build.rs +++ b/tooling/cli/src/build.rs @@ -58,104 +58,12 @@ pub struct Options { } pub fn command(mut options: Options) -> Result<()> { - let (merge_config, merge_config_path) = if let Some(config) = &options.config { - if config.starts_with('{') { - (Some(config.to_string()), None) - } else { - ( - Some( - std::fs::read_to_string(&config) - .with_context(|| "failed to read custom configuration")?, - ), - Some(config.clone()), - ) - } - } else { - (None, None) - }; - options.config = merge_config; - - let tauri_path = tauri_dir(); - set_current_dir(&tauri_path).with_context(|| "failed to change current working directory")?; + let mut interface = setup(&mut options)?; let config = get_config(options.config.as_deref())?; - let config_guard = config.lock().unwrap(); let config_ = config_guard.as_ref().unwrap(); - let mut interface = AppInterface::new(config_)?; - - let bundle_identifier_source = match config_.find_bundle_identifier_overwriter() { - Some(source) if source == MERGE_CONFIG_EXTENSION_NAME => merge_config_path.unwrap_or(source), - Some(source) => source, - None => "tauri.conf.json".into(), - }; - - if config_.tauri.bundle.identifier == "com.tauri.dev" { - error!( - "You must change the bundle identifier in `{} > tauri > bundle > identifier`. The default value `com.tauri.dev` is not allowed as it must be unique across applications.", - bundle_identifier_source - ); - std::process::exit(1); - } - - if config_ - .tauri - .bundle - .identifier - .chars() - .any(|ch| !(ch.is_alphanumeric() || ch == '-' || ch == '.')) - { - error!( - "The bundle identifier \"{}\" set in `{} > tauri > bundle > identifier`. The bundle identifier string must contain only alphanumeric characters (A–Z, a–z, and 0–9), hyphens (-), and periods (.).", - config_.tauri.bundle.identifier, - bundle_identifier_source - ); - std::process::exit(1); - } - - if let Some(before_build) = config_.build.before_build_command.clone() { - run_hook("beforeBuildCommand", before_build, options.debug)?; - } - - if let AppUrl::Url(WindowUrl::App(web_asset_path)) = &config_.build.dist_dir { - if !web_asset_path.exists() { - return Err(anyhow::anyhow!( - "Unable to find your web assets, did you forget to build your web app? Your distDir is set to \"{:?}\".", - web_asset_path - )); - } - if web_asset_path.canonicalize()?.file_name() == Some(std::ffi::OsStr::new("src-tauri")) { - return Err(anyhow::anyhow!( - "The configured distDir is the `src-tauri` folder. - Please isolate your web assets on a separate folder and update `tauri.conf.json > build > distDir`.", - )); - } - - let mut out_folders = Vec::new(); - for folder in &["node_modules", "src-tauri", "target"] { - if web_asset_path.join(folder).is_dir() { - out_folders.push(folder.to_string()); - } - } - if !out_folders.is_empty() { - return Err(anyhow::anyhow!( - "The configured distDir includes the `{:?}` {}. Please isolate your web assets on a separate folder and update `tauri.conf.json > build > distDir`.", - out_folders, - if out_folders.len() == 1 { "folder" }else { "folders" } - ) - ); - } - } - - if options.runner.is_none() { - options.runner = config_.build.runner.clone(); - } - - if let Some(list) = options.features.as_mut() { - list.extend(config_.build.features.clone().unwrap_or_default()); - } - let app_settings = interface.app_settings(); let interface_options = options.clone().into(); @@ -310,6 +218,108 @@ pub fn command(mut options: Options) -> Result<()> { Ok(()) } +pub fn setup(options: &mut Options) -> Result { + let (merge_config, merge_config_path) = if let Some(config) = &options.config { + if config.starts_with('{') { + (Some(config.to_string()), None) + } else { + ( + Some( + std::fs::read_to_string(&config) + .with_context(|| "failed to read custom configuration")?, + ), + Some(config.clone()), + ) + } + } else { + (None, None) + }; + options.config = merge_config; + + let tauri_path = tauri_dir(); + set_current_dir(&tauri_path).with_context(|| "failed to change current working directory")?; + + let config = get_config(options.config.as_deref())?; + + let config_guard = config.lock().unwrap(); + let config_ = config_guard.as_ref().unwrap(); + + let interface = AppInterface::new(config_)?; + + let bundle_identifier_source = match config_.find_bundle_identifier_overwriter() { + Some(source) if source == MERGE_CONFIG_EXTENSION_NAME => merge_config_path.unwrap_or(source), + Some(source) => source, + None => "tauri.conf.json".into(), + }; + + if config_.tauri.bundle.identifier == "com.tauri.dev" { + error!( + "You must change the bundle identifier in `{} > tauri > bundle > identifier`. The default value `com.tauri.dev` is not allowed as it must be unique across applications.", + bundle_identifier_source + ); + std::process::exit(1); + } + + if config_ + .tauri + .bundle + .identifier + .chars() + .any(|ch| !(ch.is_alphanumeric() || ch == '-' || ch == '.')) + { + error!( + "The bundle identifier \"{}\" set in `{} > tauri > bundle > identifier`. The bundle identifier string must contain only alphanumeric characters (A–Z, a–z, and 0–9), hyphens (-), and periods (.).", + config_.tauri.bundle.identifier, + bundle_identifier_source + ); + std::process::exit(1); + } + + if let Some(before_build) = config_.build.before_build_command.clone() { + run_hook("beforeBuildCommand", before_build, options.debug)?; + } + + if let AppUrl::Url(WindowUrl::App(web_asset_path)) = &config_.build.dist_dir { + if !web_asset_path.exists() { + return Err(anyhow::anyhow!( + "Unable to find your web assets, did you forget to build your web app? Your distDir is set to \"{:?}\".", + web_asset_path + )); + } + if web_asset_path.canonicalize()?.file_name() == Some(std::ffi::OsStr::new("src-tauri")) { + return Err(anyhow::anyhow!( + "The configured distDir is the `src-tauri` folder. + Please isolate your web assets on a separate folder and update `tauri.conf.json > build > distDir`.", + )); + } + + let mut out_folders = Vec::new(); + for folder in &["node_modules", "src-tauri", "target"] { + if web_asset_path.join(folder).is_dir() { + out_folders.push(folder.to_string()); + } + } + if !out_folders.is_empty() { + return Err(anyhow::anyhow!( + "The configured distDir includes the `{:?}` {}. Please isolate your web assets on a separate folder and update `tauri.conf.json > build > distDir`.", + out_folders, + if out_folders.len() == 1 { "folder" }else { "folders" } + ) + ); + } + } + + if options.runner.is_none() { + options.runner = config_.build.runner.clone(); + } + + if let Some(list) = options.features.as_mut() { + list.extend(config_.build.features.clone().unwrap_or_default()); + } + + Ok(interface) +} + fn run_hook(name: &str, hook: HookCommand, debug: bool) -> Result<()> { let (script, script_cwd) = match hook { HookCommand::Script(s) if s.is_empty() => (None, None), diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 24214ccce05..5d10ba6b158 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -4,7 +4,7 @@ use cargo_mobile::{ android::{ - adb, + aab, adb, apk, config::{Config as AndroidConfig, Metadata as AndroidMetadata}, device::{Device, RunError}, env::{Env, Error as EnvError}, @@ -14,7 +14,7 @@ use cargo_mobile::{ device::PromptError, opts::{NoiseLevel, Profile}, os, - target::call_for_targets_with_fallback, + target::{call_for_targets_with_fallback, TargetTrait}, util::prompt, }; use clap::{Parser, Subcommand}; @@ -30,6 +30,8 @@ use crate::{ Result, }; +use std::{fmt::Write, path::PathBuf}; + pub(crate) mod project; #[derive(Debug, thiserror::Error)] @@ -46,8 +48,10 @@ enum Error { OpenFailed(os::OpenFileError), #[error("{0}")] DevFailed(String), + #[error("{0}")] + BuildFailed(String), #[error(transparent)] - BuildFailed(BuildError), + AndroidStudioScriptFailed(BuildError), #[error(transparent)] RunFailed(RunError), #[error("{0}")] @@ -77,7 +81,8 @@ pub struct AndroidStudioScriptOptions { long = "target", multiple_occurrences(true), multiple_values(true), - value_parser(clap::builder::PossibleValuesParser::new(["aarch64", "armv7", "i686", "x86_64"])) + default_value = Target::DEFAULT_KEY, + value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) )] targets: Option>, /// Builds with the release flag @@ -120,12 +125,59 @@ impl From for crate::dev::Options { } } +#[derive(Debug, Clone, Parser)] +#[clap(about = "Android build")] +pub struct BuildOptions { + /// Builds with the debug flag + #[clap(short, long)] + pub debug: bool, + /// Which targets to build (all by default). + #[clap( + short, + long = "target", + multiple_occurrences(true), + multiple_values(true), + value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) + )] + targets: Option>, + /// List of cargo features to activate + #[clap(short, long, multiple_occurrences(true), multiple_values(true))] + pub features: Option>, + /// JSON string or path to JSON file to merge with tauri.conf.json + #[clap(short, long)] + pub config: Option, + /// Whether to split the APKs and AABs per ABIs. + #[clap(long)] + pub split_per_abi: bool, + /// Build APKs. + #[clap(long)] + pub apk: bool, + /// Build AABs. + #[clap(long)] + pub aab: bool, +} + +impl From for crate::build::Options { + fn from(options: BuildOptions) -> Self { + Self { + runner: None, + debug: options.debug, + target: None, + features: options.features, + bundles: None, + config: options.config, + args: Vec::new(), + } + } +} + #[derive(Subcommand)] enum Commands { Init(InitOptions), /// Open project in Android Studio Open, Dev(DevOptions), + Build(BuildOptions), #[clap(hide(true))] AndroidStudioScript(AndroidStudioScriptOptions), } @@ -134,8 +186,9 @@ pub fn command(cli: Cli) -> Result<()> { match cli.command { Commands::Init(options) => init_command(options, MobileTarget::Android)?, Commands::Open => open()?, - Commands::AndroidStudioScript(options) => android_studio_script(options)?, Commands::Dev(options) => dev(options)?, + Commands::Build(options) => build(options)?, + Commands::AndroidStudioScript(options) => android_studio_script(options)?, } Ok(()) @@ -182,8 +235,134 @@ fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError(targets: Vec) -> Result>, Error> { + if targets.is_empty() { + Ok(Target::all().iter().map(|t| t.1).collect()) + } else { + let mut outs = Vec::new(); + + let possible_targets = Target::all() + .keys() + .map(|key| key.to_string()) + .collect::>() + .join(","); + + for t in targets { + let target = Target::for_name(&t).ok_or_else(|| { + Error::TargetInvalid(format!( + "Target {} is invalid; the possible targets are {}", + t, possible_targets + )) + })?; + outs.push(target); + } + Ok(outs) + } +} + +fn build(options: BuildOptions) -> Result<()> { + with_config(|root_conf, config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + + let env = Env::new().map_err(Error::EnvInitFailed)?; + super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + + run_build(options, config, env).map_err(|e| Error::BuildFailed(e.to_string())) + }) + .map_err(Into::into) +} + +fn run_build(mut options: BuildOptions, config: &AndroidConfig, env: Env) -> Result<()> { + let profile = if options.debug { + Profile::Debug + } else { + Profile::Release + }; + + if !(options.apk || options.aab) { + // if the user didn't specify the format to build, we'll do both + options.apk = true; + options.aab = true; + } + + let bundle_identifier = { + let tauri_config = get_tauri_config(None)?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + tauri_config_.tauri.bundle.identifier.clone() + }; + + let mut build_options = options.clone().into(); + let interface = crate::build::setup(&mut build_options)?; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&InterfaceOptions { + debug: build_options.debug, + ..Default::default() + })?; + let out_dir = bin_path.parent().unwrap(); + let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; + + let cli_options = CliOptions { + features: build_options.features.clone(), + args: build_options.args.clone(), + vars: Default::default(), + }; + write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; + + options + .features + .get_or_insert(Vec::new()) + .push("custom-protocol".into()); + + let apk_outputs = if options.apk { + apk::build( + config, + &env, + NoiseLevel::Polite, + profile, + get_targets_or_all(Vec::new())?, + options.split_per_abi, + )? + } else { + Vec::new() + }; + + let aab_outputs = if options.aab { + aab::build( + config, + &env, + NoiseLevel::Polite, + profile, + get_targets_or_all(Vec::new())?, + options.split_per_abi, + )? + } else { + Vec::new() + }; + + log_finished(apk_outputs, "APK"); + log_finished(aab_outputs, "AAB"); + + Ok(()) +} + +fn log_finished(outputs: Vec, kind: &str) { + if !outputs.is_empty() { + let mut printable_paths = String::new(); + for path in &outputs { + writeln!(printable_paths, " {}", path.display()).unwrap(); + } + + log::info!(action = "Finished"; "{} {}{} at:\n{}", outputs.len(), kind, if outputs.len() == 1 { "" } else { "s" }, printable_paths); + } +} + fn dev(options: DevOptions) -> Result<()> { with_config(|_, config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; run_dev(options, config).map_err(|e| Error::DevFailed(e.to_string())) }) .map_err(Into::into) @@ -194,8 +373,7 @@ fn run_dev(options: DevOptions, config: &AndroidConfig) -> Result<()> { let mut interface = crate::dev::setup(&mut dev_options)?; let bundle_identifier = { - let tauri_config = - get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config = get_tauri_config(None)?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); tauri_config_.tauri.bundle.identifier.clone() @@ -319,7 +497,7 @@ fn android_studio_script(options: AndroidStudioScriptOptions) -> Result<()> { |target: &Target| { target .build(config, metadata, &env, NoiseLevel::Polite, true, profile) - .map_err(Error::BuildFailed) + .map_err(Error::AndroidStudioScriptFailed) }, ) .map_err(|e| Error::TargetInvalid(e.to_string()))? diff --git a/tooling/cli/templates/mobile/android/app/build.gradle.kts b/tooling/cli/templates/mobile/android/app/build.gradle.kts index 5696afc5c09..9031fd8ed2b 100644 --- a/tooling/cli/templates/mobile/android/app/build.gradle.kts +++ b/tooling/cli/templates/mobile/android/app/build.gradle.kts @@ -41,9 +41,11 @@ android { flavorDimensions.add("abi") productFlavors { create("universal") { + val abiList = findProperty("abiList") as? String + dimension = "abi" ndk { - abiFilters += listOf( + abiFilters += abiList?.split(",")?.map { it.trim() } ?: listOf( {{~#each targets}} "{{this.abi}}",{{/each}} ) From 403859d47e1a9bf978b353fa58e4b971e66337a3 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 22 Aug 2022 17:47:52 -0300 Subject: [PATCH 020/423] feat(cli): add `ios build` command (#5002) --- .changes/cli-ios-build.md | 6 + tooling/cli/Cargo.lock | 2 +- tooling/cli/src/mobile/android.rs | 76 ++++++------ tooling/cli/src/mobile/ios.rs | 193 ++++++++++++++++++++++++++---- tooling/cli/src/mobile/mod.rs | 4 +- 5 files changed, 224 insertions(+), 57 deletions(-) create mode 100644 .changes/cli-ios-build.md diff --git a/.changes/cli-ios-build.md b/.changes/cli-ios-build.md new file mode 100644 index 00000000000..5a611ecb744 --- /dev/null +++ b/.changes/cli-ios-build.md @@ -0,0 +1,6 @@ +--- +"cli.rs": patch +"cli.js": patch +--- + +Added `ios build` command. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index dae6c0291e3..1437c9675ae 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -250,7 +250,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#76732050ab72f308a202aeb580bcdfa6936c4e28" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#5444888b3e9bc5a596bc2668315c8805a865c5f6" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 5d10ba6b158..dd027262446 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -139,7 +139,7 @@ pub struct BuildOptions { multiple_values(true), value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) )] - targets: Option>, + pub targets: Option>, /// List of cargo features to activate #[clap(short, long, multiple_occurrences(true), multiple_values(true))] pub features: Option>, @@ -279,6 +279,7 @@ fn run_build(mut options: BuildOptions, config: &AndroidConfig, env: Env) -> Res } else { Profile::Release }; + let noise_level = NoiseLevel::Polite; if !(options.apk || options.aab) { // if the user didn't specify the format to build, we'll do both @@ -320,7 +321,7 @@ fn run_build(mut options: BuildOptions, config: &AndroidConfig, env: Env) -> Res apk::build( config, &env, - NoiseLevel::Polite, + noise_level, profile, get_targets_or_all(Vec::new())?, options.split_per_abi, @@ -333,7 +334,7 @@ fn run_build(mut options: BuildOptions, config: &AndroidConfig, env: Env) -> Res aab::build( config, &env, - NoiseLevel::Polite, + noise_level, profile, get_targets_or_all(Vec::new())?, options.split_per_abi, @@ -360,15 +361,20 @@ fn log_finished(outputs: Vec, kind: &str) { } fn dev(options: DevOptions) -> Result<()> { - with_config(|_, config, _metadata| { + with_config(|root_conf, config, metadata| { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, config).map_err(|e| Error::DevFailed(e.to_string())) + run_dev(options, root_conf, config, metadata).map_err(|e| Error::DevFailed(e.to_string())) }) .map_err(Into::into) } -fn run_dev(options: DevOptions, config: &AndroidConfig) -> Result<()> { +fn run_dev( + options: DevOptions, + root_conf: &Config, + config: &AndroidConfig, + metadata: &AndroidMetadata, +) -> Result<()> { let mut dev_options = options.clone().into(); let mut interface = crate::dev::setup(&mut dev_options)?; @@ -407,7 +413,7 @@ fn run_dev(options: DevOptions, config: &AndroidConfig) -> Result<()> { if open { open_dev(config) } else { - match run(options) { + match run(options, root_conf, config, metadata) { Ok(c) => Ok(Box::new(c) as Box), Err(Error::FailedToPromptForDevice(e)) => { log::error!("{}", e); @@ -439,37 +445,42 @@ fn open() -> Result<()> { .map_err(Into::into) } -fn run(options: MobileOptions) -> Result { +fn run( + options: MobileOptions, + root_conf: &Config, + config: &AndroidConfig, + metadata: &AndroidMetadata, +) -> Result { let profile = if options.debug { Profile::Debug } else { Profile::Release }; + let noise_level = NoiseLevel::Polite; - with_config(|root_conf, config, metadata| { - let build_app_bundle = metadata.asset_packs().is_some(); + let build_app_bundle = metadata.asset_packs().is_some(); - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + let env = Env::new().map_err(Error::EnvInitFailed)?; + super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; - let env = Env::new().map_err(Error::EnvInitFailed)?; - super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + device_prompt(&env) + .map_err(Error::FailedToPromptForDevice)? + .run( + config, + &env, + noise_level, + profile, + None, + build_app_bundle, + false, + ".MainActivity".into(), + ) + .map(|c| DevChild(Some(c))) + .map_err(Error::RunFailed) +} - device_prompt(&env) - .map_err(Error::FailedToPromptForDevice)? - .run( - config, - &env, - NoiseLevel::Polite, - profile, - None, - build_app_bundle, - false, - ".MainActivity".into(), - ) - .map_err(Error::RunFailed) - }) - .map(|c| DevChild(Some(c))) +fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { + device_prompt(env).map(|device| device.target()).ok() } fn android_studio_script(options: AndroidStudioScriptOptions) -> Result<()> { @@ -478,10 +489,7 @@ fn android_studio_script(options: AndroidStudioScriptOptions) -> Result<()> { } else { Profile::Debug }; - - fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { - device_prompt(env).map(|device| device.target()).ok() - } + let noise_level = NoiseLevel::Polite; with_config(|root_conf, config, metadata| { ensure_init(config.project_dir(), MobileTarget::Android) @@ -496,7 +504,7 @@ fn android_studio_script(options: AndroidStudioScriptOptions) -> Result<()> { &env, |target: &Target| { target - .build(config, metadata, &env, NoiseLevel::Polite, true, profile) + .build(config, metadata, &env, noise_level, true, profile) .map_err(Error::AndroidStudioScriptFailed) }, ) diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 22fe55fd56b..a8ab6c8ff02 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -13,7 +13,9 @@ use cargo_mobile::{ device::PromptError, env::{Env, Error as EnvError}, opts::{NoiseLevel, Profile}, - os, util, + os, + target::{call_for_targets_with_fallback, TargetInvalid, TargetTrait}, + util, util::prompt, }; use clap::{Parser, Subcommand}; @@ -29,7 +31,7 @@ use crate::{ Result, }; -use std::{collections::HashMap, ffi::OsStr, path::PathBuf}; +use std::{collections::HashMap, ffi::OsStr, fmt::Write, fs, path::PathBuf}; pub(crate) mod project; @@ -47,6 +49,8 @@ enum Error { OpenFailed(os::OpenFileError), #[error("{0}")] DevFailed(String), + #[error("{0}")] + BuildFailed(String), #[error(transparent)] NoHomeDir(util::NoHomeDir), #[error("SDK root provided by Xcode was invalid. {sdk_root} doesn't exist or isn't a directory")] @@ -63,6 +67,8 @@ enum Error { FailedToPromptForDevice(PromptError), #[error(transparent)] RunFailed(RunError), + #[error("{0}")] + TargetInvalid(String), } #[derive(Parser)] @@ -135,11 +141,53 @@ impl From for crate::dev::Options { } } +#[derive(Debug, Clone, Parser)] +#[clap(about = "Android build")] +pub struct BuildOptions { + /// Builds with the debug flag + #[clap(short, long)] + pub debug: bool, + /// Which targets to build. + #[clap( + short, + long = "target", + multiple_occurrences(true), + multiple_values(true), + default_value = Target::DEFAULT_KEY, + value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) + )] + pub targets: Vec, + /// List of cargo features to activate + #[clap(short, long, multiple_occurrences(true), multiple_values(true))] + pub features: Option>, + /// JSON string or path to JSON file to merge with tauri.conf.json + #[clap(short, long)] + pub config: Option, + /// Build number to append to the app version. + #[clap(long)] + pub build_number: Option, +} + +impl From for crate::build::Options { + fn from(options: BuildOptions) -> Self { + Self { + runner: None, + debug: options.debug, + target: None, + features: options.features, + bundles: None, + config: options.config, + args: Vec::new(), + } + } +} + #[derive(Subcommand)] enum Commands { Init(InitOptions), Open, Dev(DevOptions), + Build(BuildOptions), #[clap(hide(true))] XcodeScript(XcodeScriptOptions), } @@ -149,6 +197,7 @@ pub fn command(cli: Cli) -> Result<()> { Commands::Init(options) => init_command(options, MobileTarget::Ios)?, Commands::Open => open()?, Commands::Dev(options) => dev(options)?, + Commands::Build(options) => build(options)?, Commands::XcodeScript(options) => xcode_script(options)?, } @@ -203,14 +252,117 @@ fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError(env: &Env) -> Option<&'a Target<'a>> { + device_prompt(env).map(|device| device.target()).ok() +} + +fn build(options: BuildOptions) -> Result<()> { + with_config(|root_conf, config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + + let env = env()?; + super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + + run_build(options, config, env).map_err(|e| Error::BuildFailed(e.to_string())) + }) + .map_err(Into::into) +} + +fn run_build(mut options: BuildOptions, config: &AppleConfig, env: Env) -> Result<()> { + let profile = if options.debug { + Profile::Debug + } else { + Profile::Release + }; + let noise_level = NoiseLevel::Polite; + + let bundle_identifier = { + let tauri_config = get_tauri_config(None)?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + tauri_config_.tauri.bundle.identifier.clone() + }; + + let mut build_options = options.clone().into(); + let interface = crate::build::setup(&mut build_options)?; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&InterfaceOptions { + debug: build_options.debug, + ..Default::default() + })?; + let out_dir = bin_path.parent().unwrap(); + let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; + + let cli_options = CliOptions { + features: build_options.features.clone(), + args: build_options.args.clone(), + vars: Default::default(), + }; + write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; + + options + .features + .get_or_insert(Vec::new()) + .push("custom-protocol".into()); + + let mut out_files = Vec::new(); + + call_for_targets_with_fallback( + options.targets.iter(), + &detect_target_ok, + &env, + |target: &Target| { + let mut app_version = config.bundle_version().clone(); + if let Some(build_number) = options.build_number { + app_version.push_extra(build_number); + } + + target.build(config, &env, noise_level, profile)?; + target.archive(config, &env, noise_level, profile, Some(app_version))?; + target.export(config, &env, noise_level)?; + + if let Ok(ipa_path) = config.ipa_path() { + let out_dir = config.export_dir().join(target.arch); + fs::create_dir_all(&out_dir)?; + let path = out_dir.join(ipa_path.file_name().unwrap()); + fs::rename(&ipa_path, &path)?; + out_files.push(path); + } + + anyhow::Result::Ok(()) + }, + ) + .map_err(|e: TargetInvalid| Error::TargetInvalid(e.to_string()))? + .map_err(|e: anyhow::Error| e)?; + + log_finished(out_files, "IPA"); + + Ok(()) +} + +fn log_finished(outputs: Vec, kind: &str) { + if !outputs.is_empty() { + let mut printable_paths = String::new(); + for path in &outputs { + writeln!(printable_paths, " {}", path.display()).unwrap(); + } + + log::info!(action = "Finished"; "{} {}{} at:\n{}", outputs.len(), kind, if outputs.len() == 1 { "" } else { "s" }, printable_paths); + } +} + fn dev(options: DevOptions) -> Result<()> { - with_config(|_, config, _metadata| { - run_dev(options, config).map_err(|e| Error::DevFailed(e.to_string())) + with_config(|root_conf, config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + run_dev(options, root_conf, config).map_err(|e| Error::DevFailed(e.to_string())) }) .map_err(Into::into) } -fn run_dev(options: DevOptions, config: &AppleConfig) -> Result<()> { +fn run_dev(options: DevOptions, root_conf: &Config, config: &AppleConfig) -> Result<()> { let mut dev_options = options.clone().into(); let mut interface = crate::dev::setup(&mut dev_options)?; @@ -249,7 +401,7 @@ fn run_dev(options: DevOptions, config: &AppleConfig) -> Result<()> { if open { open_dev(config) } else { - match run(options) { + match run(options, root_conf, config) { Ok(c) => Ok(Box::new(c) as Box), Err(Error::FailedToPromptForDevice(e)) => { log::error!("{}", e); @@ -281,26 +433,26 @@ fn open() -> Result<()> { .map_err(Into::into) } -fn run(options: MobileOptions) -> Result { +fn run( + options: MobileOptions, + root_conf: &Config, + config: &AppleConfig, +) -> Result { let profile = if options.debug { Profile::Debug } else { Profile::Release }; + let noise_level = NoiseLevel::Polite; - with_config(|root_conf, config, _| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - - let env = env()?; - super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + let env = env()?; + super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; - device_prompt(&env) - .map_err(Error::FailedToPromptForDevice)? - .run(config, &env, NoiseLevel::Polite, false, profile) - .map_err(Error::RunFailed) - }) - .map(|c| DevChild(Some(c))) + device_prompt(&env) + .map_err(Error::FailedToPromptForDevice)? + .run(config, &env, noise_level, false, profile) + .map(|c| DevChild(Some(c))) + .map_err(Error::RunFailed) } fn xcode_script(options: XcodeScriptOptions) -> Result<()> { @@ -318,6 +470,7 @@ fn xcode_script(options: XcodeScriptOptions) -> Result<()> { let profile = profile_from_configuration(&options.configuration); let macos = macos_from_platform(&options.platform); + let noise_level = NoiseLevel::Polite; with_config(|root_conf, config, metadata| { let env = env()?; @@ -400,7 +553,7 @@ fn xcode_script(options: XcodeScriptOptions) -> Result<()> { .compile_lib( config, metadata, - NoiseLevel::Polite, + noise_level, true, profile, &env, diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 250391c7b3d..db6696db733 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -170,8 +170,8 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) { ios_features: ios_options.features.clone(), macos_no_default_features: None, macos_features: None, - bundle_version: None, - bundle_version_short: None, + bundle_version: config.package.version.clone(), + bundle_version_short: config.package.version.clone(), ios_version: None, macos_version: None, use_legacy_build_system: None, From e56a9dd729d5b761bd7d7c4c95aa4403acc3e841 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 22 Aug 2022 21:59:17 -0300 Subject: [PATCH 021/423] refactor(cli): move mobile commands to their own module (#5005) --- tooling/cli/src/mobile/android.rs | 406 +--------------- .../mobile/android/android_studio_script.rs | 56 +++ tooling/cli/src/mobile/android/build.rs | 174 +++++++ tooling/cli/src/mobile/android/dev.rs | 163 +++++++ tooling/cli/src/mobile/android/open.rs | 12 + tooling/cli/src/mobile/ios.rs | 443 +----------------- tooling/cli/src/mobile/ios/build.rs | 147 ++++++ tooling/cli/src/mobile/ios/dev.rs | 146 ++++++ tooling/cli/src/mobile/ios/open.rs | 12 + tooling/cli/src/mobile/ios/xcode_script.rs | 141 ++++++ tooling/cli/src/mobile/mod.rs | 13 +- 11 files changed, 894 insertions(+), 819 deletions(-) create mode 100644 tooling/cli/src/mobile/android/android_studio_script.rs create mode 100644 tooling/cli/src/mobile/android/build.rs create mode 100644 tooling/cli/src/mobile/android/dev.rs create mode 100644 tooling/cli/src/mobile/android/open.rs create mode 100644 tooling/cli/src/mobile/ios/build.rs create mode 100644 tooling/cli/src/mobile/ios/dev.rs create mode 100644 tooling/cli/src/mobile/ios/open.rs create mode 100644 tooling/cli/src/mobile/ios/xcode_script.rs diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index dd027262446..2d9aba649af 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -4,7 +4,7 @@ use cargo_mobile::{ android::{ - aab, adb, apk, + adb, config::{Config as AndroidConfig, Metadata as AndroidMetadata}, device::{Device, RunError}, env::{Env, Error as EnvError}, @@ -12,26 +12,22 @@ use cargo_mobile::{ }, config::Config, device::PromptError, - opts::{NoiseLevel, Profile}, os, - target::{call_for_targets_with_fallback, TargetTrait}, util::prompt, }; use clap::{Parser, Subcommand}; use super::{ ensure_init, get_config, - init::{command as init_command, Options as InitOptions}, - write_options, CliOptions, DevChild, Target as MobileTarget, + init::{command as init_command, init_dot_cargo, Options as InitOptions}, + log_finished, Target as MobileTarget, }; -use crate::{ - helpers::{config::get as get_tauri_config, flock}, - interface::{AppSettings, DevProcess, Interface, MobileOptions, Options as InterfaceOptions}, - Result, -}; - -use std::{fmt::Write, path::PathBuf}; +use crate::{helpers::config::get as get_tauri_config, Result}; +mod android_studio_script; +mod build; +mod dev; +mod open; pub(crate) mod project; #[derive(Debug, thiserror::Error)] @@ -73,122 +69,24 @@ pub struct Cli { command: Commands, } -#[derive(Debug, Parser)] -pub struct AndroidStudioScriptOptions { - /// Targets to build. - #[clap( - short, - long = "target", - multiple_occurrences(true), - multiple_values(true), - default_value = Target::DEFAULT_KEY, - value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) - )] - targets: Option>, - /// Builds with the release flag - #[clap(short, long)] - release: bool, -} - -#[derive(Debug, Clone, Parser)] -#[clap(about = "Android dev")] -pub struct DevOptions { - /// List of cargo features to activate - #[clap(short, long, multiple_occurrences(true), multiple_values(true))] - pub features: Option>, - /// Exit on panic - #[clap(short, long)] - exit_on_panic: bool, - /// JSON string or path to JSON file to merge with tauri.conf.json - #[clap(short, long)] - pub config: Option, - /// Disable the file watcher - #[clap(long)] - pub no_watch: bool, - /// Open Android Studio instead of trying to run on a connected device - #[clap(short, long)] - pub open: bool, -} - -impl From for crate::dev::Options { - fn from(options: DevOptions) -> Self { - Self { - runner: None, - target: None, - features: options.features, - exit_on_panic: options.exit_on_panic, - config: options.config, - release_mode: false, - args: Vec::new(), - no_watch: options.no_watch, - } - } -} - -#[derive(Debug, Clone, Parser)] -#[clap(about = "Android build")] -pub struct BuildOptions { - /// Builds with the debug flag - #[clap(short, long)] - pub debug: bool, - /// Which targets to build (all by default). - #[clap( - short, - long = "target", - multiple_occurrences(true), - multiple_values(true), - value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) - )] - pub targets: Option>, - /// List of cargo features to activate - #[clap(short, long, multiple_occurrences(true), multiple_values(true))] - pub features: Option>, - /// JSON string or path to JSON file to merge with tauri.conf.json - #[clap(short, long)] - pub config: Option, - /// Whether to split the APKs and AABs per ABIs. - #[clap(long)] - pub split_per_abi: bool, - /// Build APKs. - #[clap(long)] - pub apk: bool, - /// Build AABs. - #[clap(long)] - pub aab: bool, -} - -impl From for crate::build::Options { - fn from(options: BuildOptions) -> Self { - Self { - runner: None, - debug: options.debug, - target: None, - features: options.features, - bundles: None, - config: options.config, - args: Vec::new(), - } - } -} - #[derive(Subcommand)] enum Commands { Init(InitOptions), /// Open project in Android Studio Open, - Dev(DevOptions), - Build(BuildOptions), + Dev(dev::Options), + Build(build::Options), #[clap(hide(true))] - AndroidStudioScript(AndroidStudioScriptOptions), + AndroidStudioScript(android_studio_script::Options), } pub fn command(cli: Cli) -> Result<()> { match cli.command { Commands::Init(options) => init_command(options, MobileTarget::Android)?, - Commands::Open => open()?, - Commands::Dev(options) => dev(options)?, - Commands::Build(options) => build(options)?, - Commands::AndroidStudioScript(options) => android_studio_script(options)?, + Commands::Open => open::command()?, + Commands::Dev(options) => dev::command(options)?, + Commands::Build(options) => build::command(options)?, + Commands::AndroidStudioScript(options) => android_studio_script::command(options)?, } Ok(()) @@ -235,280 +133,6 @@ fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError(targets: Vec) -> Result>, Error> { - if targets.is_empty() { - Ok(Target::all().iter().map(|t| t.1).collect()) - } else { - let mut outs = Vec::new(); - - let possible_targets = Target::all() - .keys() - .map(|key| key.to_string()) - .collect::>() - .join(","); - - for t in targets { - let target = Target::for_name(&t).ok_or_else(|| { - Error::TargetInvalid(format!( - "Target {} is invalid; the possible targets are {}", - t, possible_targets - )) - })?; - outs.push(target); - } - Ok(outs) - } -} - -fn build(options: BuildOptions) -> Result<()> { - with_config(|root_conf, config, _metadata| { - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - - let env = Env::new().map_err(Error::EnvInitFailed)?; - super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; - - run_build(options, config, env).map_err(|e| Error::BuildFailed(e.to_string())) - }) - .map_err(Into::into) -} - -fn run_build(mut options: BuildOptions, config: &AndroidConfig, env: Env) -> Result<()> { - let profile = if options.debug { - Profile::Debug - } else { - Profile::Release - }; - let noise_level = NoiseLevel::Polite; - - if !(options.apk || options.aab) { - // if the user didn't specify the format to build, we'll do both - options.apk = true; - options.aab = true; - } - - let bundle_identifier = { - let tauri_config = get_tauri_config(None)?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - tauri_config_.tauri.bundle.identifier.clone() - }; - - let mut build_options = options.clone().into(); - let interface = crate::build::setup(&mut build_options)?; - - let app_settings = interface.app_settings(); - let bin_path = app_settings.app_binary_path(&InterfaceOptions { - debug: build_options.debug, - ..Default::default() - })?; - let out_dir = bin_path.parent().unwrap(); - let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; - - let cli_options = CliOptions { - features: build_options.features.clone(), - args: build_options.args.clone(), - vars: Default::default(), - }; - write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; - - options - .features - .get_or_insert(Vec::new()) - .push("custom-protocol".into()); - - let apk_outputs = if options.apk { - apk::build( - config, - &env, - noise_level, - profile, - get_targets_or_all(Vec::new())?, - options.split_per_abi, - )? - } else { - Vec::new() - }; - - let aab_outputs = if options.aab { - aab::build( - config, - &env, - noise_level, - profile, - get_targets_or_all(Vec::new())?, - options.split_per_abi, - )? - } else { - Vec::new() - }; - - log_finished(apk_outputs, "APK"); - log_finished(aab_outputs, "AAB"); - - Ok(()) -} - -fn log_finished(outputs: Vec, kind: &str) { - if !outputs.is_empty() { - let mut printable_paths = String::new(); - for path in &outputs { - writeln!(printable_paths, " {}", path.display()).unwrap(); - } - - log::info!(action = "Finished"; "{} {}{} at:\n{}", outputs.len(), kind, if outputs.len() == 1 { "" } else { "s" }, printable_paths); - } -} - -fn dev(options: DevOptions) -> Result<()> { - with_config(|root_conf, config, metadata| { - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config, metadata).map_err(|e| Error::DevFailed(e.to_string())) - }) - .map_err(Into::into) -} - -fn run_dev( - options: DevOptions, - root_conf: &Config, - config: &AndroidConfig, - metadata: &AndroidMetadata, -) -> Result<()> { - let mut dev_options = options.clone().into(); - let mut interface = crate::dev::setup(&mut dev_options)?; - - let bundle_identifier = { - let tauri_config = get_tauri_config(None)?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - tauri_config_.tauri.bundle.identifier.clone() - }; - - let app_settings = interface.app_settings(); - let bin_path = app_settings.app_binary_path(&InterfaceOptions { - debug: !dev_options.release_mode, - ..Default::default() - })?; - let out_dir = bin_path.parent().unwrap(); - let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; - - let open = options.open; - interface.mobile_dev( - MobileOptions { - debug: true, - features: options.features, - args: Vec::new(), - config: options.config, - no_watch: options.no_watch, - }, - |options| { - let cli_options = CliOptions { - features: options.features.clone(), - args: options.args.clone(), - vars: Default::default(), - }; - write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; - - if open { - open_dev(config) - } else { - match run(options, root_conf, config, metadata) { - Ok(c) => Ok(Box::new(c) as Box), - Err(Error::FailedToPromptForDevice(e)) => { - log::error!("{}", e); - open_dev(config) - } - Err(e) => Err(e.into()), - } - } - }, - ) -} - -fn open_dev(config: &AndroidConfig) -> ! { - log::info!("Opening Android Studio"); - if let Err(e) = os::open_file_with("Android Studio", config.project_dir()) { - log::error!("{}", e); - } - loop { - std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); - } -} - -fn open() -> Result<()> { - with_config(|_, config, _metadata| { - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) - }) - .map_err(Into::into) -} - -fn run( - options: MobileOptions, - root_conf: &Config, - config: &AndroidConfig, - metadata: &AndroidMetadata, -) -> Result { - let profile = if options.debug { - Profile::Debug - } else { - Profile::Release - }; - let noise_level = NoiseLevel::Polite; - - let build_app_bundle = metadata.asset_packs().is_some(); - - let env = Env::new().map_err(Error::EnvInitFailed)?; - super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; - - device_prompt(&env) - .map_err(Error::FailedToPromptForDevice)? - .run( - config, - &env, - noise_level, - profile, - None, - build_app_bundle, - false, - ".MainActivity".into(), - ) - .map(|c| DevChild(Some(c))) - .map_err(Error::RunFailed) -} - fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { device_prompt(env).map(|device| device.target()).ok() } - -fn android_studio_script(options: AndroidStudioScriptOptions) -> Result<()> { - let profile = if options.release { - Profile::Release - } else { - Profile::Debug - }; - let noise_level = NoiseLevel::Polite; - - with_config(|root_conf, config, metadata| { - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - - let env = Env::new().map_err(Error::EnvInitFailed)?; - super::init::init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; - - call_for_targets_with_fallback( - options.targets.unwrap_or_default().iter(), - &detect_target_ok, - &env, - |target: &Target| { - target - .build(config, metadata, &env, noise_level, true, profile) - .map_err(Error::AndroidStudioScriptFailed) - }, - ) - .map_err(|e| Error::TargetInvalid(e.to_string()))? - }) - .map_err(Into::into) -} diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs new file mode 100644 index 00000000000..d7821394163 --- /dev/null +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -0,0 +1,56 @@ +use super::{detect_target_ok, ensure_init, init_dot_cargo, with_config, Error, MobileTarget}; +use crate::Result; +use clap::Parser; + +use cargo_mobile::{ + android::{env::Env, target::Target}, + opts::{NoiseLevel, Profile}, + target::{call_for_targets_with_fallback, TargetTrait}, +}; + +#[derive(Debug, Parser)] +pub struct Options { + /// Targets to build. + #[clap( + short, + long = "target", + multiple_occurrences(true), + multiple_values(true), + default_value = Target::DEFAULT_KEY, + value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) + )] + targets: Option>, + /// Builds with the release flag + #[clap(short, long)] + release: bool, +} + +pub fn command(options: Options) -> Result<()> { + let profile = if options.release { + Profile::Release + } else { + Profile::Debug + }; + let noise_level = NoiseLevel::Polite; + + with_config(|root_conf, config, metadata| { + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + + let env = Env::new().map_err(Error::EnvInitFailed)?; + init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + + call_for_targets_with_fallback( + options.targets.unwrap_or_default().iter(), + &detect_target_ok, + &env, + |target: &Target| { + target + .build(config, metadata, &env, noise_level, true, profile) + .map_err(Error::AndroidStudioScriptFailed) + }, + ) + .map_err(|e| Error::TargetInvalid(e.to_string()))? + }) + .map_err(Into::into) +} diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs new file mode 100644 index 00000000000..a6fbeb1ebb0 --- /dev/null +++ b/tooling/cli/src/mobile/android/build.rs @@ -0,0 +1,174 @@ +use super::{ensure_init, init_dot_cargo, log_finished, with_config, Error, MobileTarget}; +use crate::{ + helpers::{config::get as get_tauri_config, flock}, + interface::{AppSettings, Interface, Options as InterfaceOptions}, + mobile::{write_options, CliOptions}, + Result, +}; +use clap::Parser; + +use cargo_mobile::{ + android::{aab, apk, config::Config as AndroidConfig, env::Env, target::Target}, + opts::{NoiseLevel, Profile}, + target::TargetTrait, +}; + +#[derive(Debug, Clone, Parser)] +#[clap(about = "Android build")] +pub struct Options { + /// Builds with the debug flag + #[clap(short, long)] + pub debug: bool, + /// Which targets to build (all by default). + #[clap( + short, + long = "target", + multiple_occurrences(true), + multiple_values(true), + value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) + )] + pub targets: Option>, + /// List of cargo features to activate + #[clap(short, long, multiple_occurrences(true), multiple_values(true))] + pub features: Option>, + /// JSON string or path to JSON file to merge with tauri.conf.json + #[clap(short, long)] + pub config: Option, + /// Whether to split the APKs and AABs per ABIs. + #[clap(long)] + pub split_per_abi: bool, + /// Build APKs. + #[clap(long)] + pub apk: bool, + /// Build AABs. + #[clap(long)] + pub aab: bool, +} + +impl From for crate::build::Options { + fn from(options: Options) -> Self { + Self { + runner: None, + debug: options.debug, + target: None, + features: options.features, + bundles: None, + config: options.config, + args: Vec::new(), + } + } +} + +pub fn command(options: Options) -> Result<()> { + with_config(|root_conf, config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + + let env = Env::new().map_err(Error::EnvInitFailed)?; + init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + + run_build(options, config, env).map_err(|e| Error::BuildFailed(e.to_string())) + }) + .map_err(Into::into) +} + +fn run_build(mut options: Options, config: &AndroidConfig, env: Env) -> Result<()> { + let profile = if options.debug { + Profile::Debug + } else { + Profile::Release + }; + let noise_level = NoiseLevel::Polite; + + if !(options.apk || options.aab) { + // if the user didn't specify the format to build, we'll do both + options.apk = true; + options.aab = true; + } + + let bundle_identifier = { + let tauri_config = get_tauri_config(None)?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + tauri_config_.tauri.bundle.identifier.clone() + }; + + let mut build_options = options.clone().into(); + let interface = crate::build::setup(&mut build_options)?; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&InterfaceOptions { + debug: build_options.debug, + ..Default::default() + })?; + let out_dir = bin_path.parent().unwrap(); + let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; + + let cli_options = CliOptions { + features: build_options.features.clone(), + args: build_options.args.clone(), + vars: Default::default(), + }; + write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; + + options + .features + .get_or_insert(Vec::new()) + .push("custom-protocol".into()); + + let apk_outputs = if options.apk { + apk::build( + config, + &env, + noise_level, + profile, + get_targets_or_all(Vec::new())?, + options.split_per_abi, + )? + } else { + Vec::new() + }; + + let aab_outputs = if options.aab { + aab::build( + config, + &env, + noise_level, + profile, + get_targets_or_all(Vec::new())?, + options.split_per_abi, + )? + } else { + Vec::new() + }; + + log_finished(apk_outputs, "APK"); + log_finished(aab_outputs, "AAB"); + + Ok(()) +} + +fn get_targets_or_all<'a>(targets: Vec) -> Result>, Error> { + if targets.is_empty() { + Ok(Target::all().iter().map(|t| t.1).collect()) + } else { + let mut outs = Vec::new(); + + let possible_targets = Target::all() + .keys() + .map(|key| key.to_string()) + .collect::>() + .join(","); + + for t in targets { + let target = Target::for_name(&t).ok_or_else(|| { + Error::TargetInvalid(format!( + "Target {} is invalid; the possible targets are {}", + t, possible_targets + )) + })?; + outs.push(target); + } + Ok(outs) + } +} diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs new file mode 100644 index 00000000000..f6e1033d2b9 --- /dev/null +++ b/tooling/cli/src/mobile/android/dev.rs @@ -0,0 +1,163 @@ +use super::{device_prompt, ensure_init, init_dot_cargo, with_config, Error, MobileTarget}; +use crate::{ + helpers::{config::get as get_tauri_config, flock}, + interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions}, + mobile::{write_options, CliOptions, DevChild, DevProcess}, + Result, +}; +use clap::Parser; + +use cargo_mobile::{ + android::{ + config::{Config as AndroidConfig, Metadata as AndroidMetadata}, + env::Env, + }, + config::Config, + opts::{NoiseLevel, Profile}, + os, +}; + +#[derive(Debug, Clone, Parser)] +#[clap(about = "Android dev")] +pub struct Options { + /// List of cargo features to activate + #[clap(short, long, multiple_occurrences(true), multiple_values(true))] + pub features: Option>, + /// Exit on panic + #[clap(short, long)] + exit_on_panic: bool, + /// JSON string or path to JSON file to merge with tauri.conf.json + #[clap(short, long)] + pub config: Option, + /// Disable the file watcher + #[clap(long)] + pub no_watch: bool, + /// Open Android Studio instead of trying to run on a connected device + #[clap(short, long)] + pub open: bool, +} + +impl From for crate::dev::Options { + fn from(options: Options) -> Self { + Self { + runner: None, + target: None, + features: options.features, + exit_on_panic: options.exit_on_panic, + config: options.config, + release_mode: false, + args: Vec::new(), + no_watch: options.no_watch, + } + } +} + +pub fn command(options: Options) -> Result<()> { + with_config(|root_conf, config, metadata| { + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + run_dev(options, root_conf, config, metadata).map_err(|e| Error::DevFailed(e.to_string())) + }) + .map_err(Into::into) +} + +fn run_dev( + options: Options, + root_conf: &Config, + config: &AndroidConfig, + metadata: &AndroidMetadata, +) -> Result<()> { + let mut dev_options = options.clone().into(); + let mut interface = crate::dev::setup(&mut dev_options)?; + + let bundle_identifier = { + let tauri_config = get_tauri_config(None)?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + tauri_config_.tauri.bundle.identifier.clone() + }; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&InterfaceOptions { + debug: !dev_options.release_mode, + ..Default::default() + })?; + let out_dir = bin_path.parent().unwrap(); + let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; + + let open = options.open; + interface.mobile_dev( + MobileOptions { + debug: true, + features: options.features, + args: Vec::new(), + config: options.config, + no_watch: options.no_watch, + }, + |options| { + let cli_options = CliOptions { + features: options.features.clone(), + args: options.args.clone(), + vars: Default::default(), + }; + write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; + + if open { + open_dev(config) + } else { + match run(options, root_conf, config, metadata) { + Ok(c) => Ok(Box::new(c) as Box), + Err(Error::FailedToPromptForDevice(e)) => { + log::error!("{}", e); + open_dev(config) + } + Err(e) => Err(e.into()), + } + } + }, + ) +} + +fn open_dev(config: &AndroidConfig) -> ! { + log::info!("Opening Android Studio"); + if let Err(e) = os::open_file_with("Android Studio", config.project_dir()) { + log::error!("{}", e); + } + loop { + std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); + } +} + +fn run( + options: MobileOptions, + root_conf: &Config, + config: &AndroidConfig, + metadata: &AndroidMetadata, +) -> Result { + let profile = if options.debug { + Profile::Debug + } else { + Profile::Release + }; + let noise_level = NoiseLevel::Polite; + + let build_app_bundle = metadata.asset_packs().is_some(); + + let env = Env::new().map_err(Error::EnvInitFailed)?; + init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + + device_prompt(&env) + .map_err(Error::FailedToPromptForDevice)? + .run( + config, + &env, + noise_level, + profile, + None, + build_app_bundle, + false, + ".MainActivity".into(), + ) + .map(|c| DevChild(Some(c))) + .map_err(Error::RunFailed) +} diff --git a/tooling/cli/src/mobile/android/open.rs b/tooling/cli/src/mobile/android/open.rs new file mode 100644 index 00000000000..e58fba14a9b --- /dev/null +++ b/tooling/cli/src/mobile/android/open.rs @@ -0,0 +1,12 @@ +use super::{ensure_init, with_config, Error, MobileTarget}; +use crate::Result; +use cargo_mobile::os; + +pub fn command() -> Result<()> { + with_config(|_, config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) + }) + .map_err(Into::into) +} diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index a8ab6c8ff02..388e9b31830 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -12,28 +12,25 @@ use cargo_mobile::{ config::Config, device::PromptError, env::{Env, Error as EnvError}, - opts::{NoiseLevel, Profile}, - os, - target::{call_for_targets_with_fallback, TargetInvalid, TargetTrait}, - util, + os, util, util::prompt, }; use clap::{Parser, Subcommand}; use super::{ ensure_init, env_vars, get_config, - init::{command as init_command, Options as InitOptions}, - write_options, CliOptions, DevChild, Target as MobileTarget, -}; -use crate::{ - helpers::{config::get as get_tauri_config, flock}, - interface::{AppSettings, DevProcess, Interface, MobileOptions, Options as InterfaceOptions}, - Result, + init::{command as init_command, init_dot_cargo, Options as InitOptions}, + log_finished, Target as MobileTarget, }; +use crate::{helpers::config::get as get_tauri_config, Result}; -use std::{collections::HashMap, ffi::OsStr, fmt::Write, fs, path::PathBuf}; +use std::path::PathBuf; +mod build; +mod dev; +mod open; pub(crate) mod project; +mod xcode_script; #[derive(Debug, thiserror::Error)] enum Error { @@ -84,121 +81,23 @@ pub struct Cli { command: Commands, } -#[derive(Debug, Parser)] -pub struct XcodeScriptOptions { - /// Value of `PLATFORM_DISPLAY_NAME` env var - #[clap(long)] - platform: String, - /// Value of `SDKROOT` env var - #[clap(long)] - sdk_root: PathBuf, - /// Value of `CONFIGURATION` env var - #[clap(long)] - configuration: String, - /// Value of `FORCE_COLOR` env var - #[clap(long)] - force_color: bool, - /// Value of `ARCHS` env var - #[clap(index = 1, required = true)] - arches: Vec, -} - -#[derive(Debug, Clone, Parser)] -#[clap(about = "iOS dev")] -pub struct DevOptions { - /// List of cargo features to activate - #[clap(short, long, multiple_occurrences(true), multiple_values(true))] - pub features: Option>, - /// Exit on panic - #[clap(short, long)] - exit_on_panic: bool, - /// JSON string or path to JSON file to merge with tauri.conf.json - #[clap(short, long)] - pub config: Option, - /// Run the code in release mode - #[clap(long = "release")] - pub release_mode: bool, - /// Disable the file watcher - #[clap(long)] - pub no_watch: bool, - /// Open Xcode instead of trying to run on a connected device - #[clap(short, long)] - pub open: bool, -} - -impl From for crate::dev::Options { - fn from(options: DevOptions) -> Self { - Self { - runner: None, - target: None, - features: options.features, - exit_on_panic: options.exit_on_panic, - config: options.config, - release_mode: options.release_mode, - args: Vec::new(), - no_watch: options.no_watch, - } - } -} - -#[derive(Debug, Clone, Parser)] -#[clap(about = "Android build")] -pub struct BuildOptions { - /// Builds with the debug flag - #[clap(short, long)] - pub debug: bool, - /// Which targets to build. - #[clap( - short, - long = "target", - multiple_occurrences(true), - multiple_values(true), - default_value = Target::DEFAULT_KEY, - value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) - )] - pub targets: Vec, - /// List of cargo features to activate - #[clap(short, long, multiple_occurrences(true), multiple_values(true))] - pub features: Option>, - /// JSON string or path to JSON file to merge with tauri.conf.json - #[clap(short, long)] - pub config: Option, - /// Build number to append to the app version. - #[clap(long)] - pub build_number: Option, -} - -impl From for crate::build::Options { - fn from(options: BuildOptions) -> Self { - Self { - runner: None, - debug: options.debug, - target: None, - features: options.features, - bundles: None, - config: options.config, - args: Vec::new(), - } - } -} - #[derive(Subcommand)] enum Commands { Init(InitOptions), Open, - Dev(DevOptions), - Build(BuildOptions), + Dev(dev::Options), + Build(build::Options), #[clap(hide(true))] - XcodeScript(XcodeScriptOptions), + XcodeScript(xcode_script::Options), } pub fn command(cli: Cli) -> Result<()> { match cli.command { Commands::Init(options) => init_command(options, MobileTarget::Ios)?, - Commands::Open => open()?, - Commands::Dev(options) => dev(options)?, - Commands::Build(options) => build(options)?, - Commands::XcodeScript(options) => xcode_script(options)?, + Commands::Open => open::command()?, + Commands::Dev(options) => dev::command(options)?, + Commands::Build(options) => build::command(options)?, + Commands::XcodeScript(options) => xcode_script::command(options)?, } Ok(()) @@ -255,313 +154,3 @@ fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError(env: &Env) -> Option<&'a Target<'a>> { device_prompt(env).map(|device| device.target()).ok() } - -fn build(options: BuildOptions) -> Result<()> { - with_config(|root_conf, config, _metadata| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - - let env = env()?; - super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; - - run_build(options, config, env).map_err(|e| Error::BuildFailed(e.to_string())) - }) - .map_err(Into::into) -} - -fn run_build(mut options: BuildOptions, config: &AppleConfig, env: Env) -> Result<()> { - let profile = if options.debug { - Profile::Debug - } else { - Profile::Release - }; - let noise_level = NoiseLevel::Polite; - - let bundle_identifier = { - let tauri_config = get_tauri_config(None)?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - tauri_config_.tauri.bundle.identifier.clone() - }; - - let mut build_options = options.clone().into(); - let interface = crate::build::setup(&mut build_options)?; - - let app_settings = interface.app_settings(); - let bin_path = app_settings.app_binary_path(&InterfaceOptions { - debug: build_options.debug, - ..Default::default() - })?; - let out_dir = bin_path.parent().unwrap(); - let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; - - let cli_options = CliOptions { - features: build_options.features.clone(), - args: build_options.args.clone(), - vars: Default::default(), - }; - write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; - - options - .features - .get_or_insert(Vec::new()) - .push("custom-protocol".into()); - - let mut out_files = Vec::new(); - - call_for_targets_with_fallback( - options.targets.iter(), - &detect_target_ok, - &env, - |target: &Target| { - let mut app_version = config.bundle_version().clone(); - if let Some(build_number) = options.build_number { - app_version.push_extra(build_number); - } - - target.build(config, &env, noise_level, profile)?; - target.archive(config, &env, noise_level, profile, Some(app_version))?; - target.export(config, &env, noise_level)?; - - if let Ok(ipa_path) = config.ipa_path() { - let out_dir = config.export_dir().join(target.arch); - fs::create_dir_all(&out_dir)?; - let path = out_dir.join(ipa_path.file_name().unwrap()); - fs::rename(&ipa_path, &path)?; - out_files.push(path); - } - - anyhow::Result::Ok(()) - }, - ) - .map_err(|e: TargetInvalid| Error::TargetInvalid(e.to_string()))? - .map_err(|e: anyhow::Error| e)?; - - log_finished(out_files, "IPA"); - - Ok(()) -} - -fn log_finished(outputs: Vec, kind: &str) { - if !outputs.is_empty() { - let mut printable_paths = String::new(); - for path in &outputs { - writeln!(printable_paths, " {}", path.display()).unwrap(); - } - - log::info!(action = "Finished"; "{} {}{} at:\n{}", outputs.len(), kind, if outputs.len() == 1 { "" } else { "s" }, printable_paths); - } -} - -fn dev(options: DevOptions) -> Result<()> { - with_config(|root_conf, config, _metadata| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config).map_err(|e| Error::DevFailed(e.to_string())) - }) - .map_err(Into::into) -} - -fn run_dev(options: DevOptions, root_conf: &Config, config: &AppleConfig) -> Result<()> { - let mut dev_options = options.clone().into(); - let mut interface = crate::dev::setup(&mut dev_options)?; - - let bundle_identifier = { - let tauri_config = - get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - tauri_config_.tauri.bundle.identifier.clone() - }; - - let app_settings = interface.app_settings(); - let bin_path = app_settings.app_binary_path(&InterfaceOptions { - debug: !dev_options.release_mode, - ..Default::default() - })?; - let out_dir = bin_path.parent().unwrap(); - let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; - - let open = options.open; - interface.mobile_dev( - MobileOptions { - debug: true, - features: options.features, - args: Vec::new(), - config: options.config, - no_watch: options.no_watch, - }, - |options| { - let cli_options = CliOptions { - features: options.features.clone(), - args: options.args.clone(), - vars: Default::default(), - }; - write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; - if open { - open_dev(config) - } else { - match run(options, root_conf, config) { - Ok(c) => Ok(Box::new(c) as Box), - Err(Error::FailedToPromptForDevice(e)) => { - log::error!("{}", e); - open_dev(config) - } - Err(e) => Err(e.into()), - } - } - }, - ) -} - -fn open_dev(config: &AppleConfig) -> ! { - log::info!("Opening Xcode"); - if let Err(e) = os::open_file_with("Xcode", config.project_dir()) { - log::error!("{}", e); - } - loop { - std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); - } -} - -fn open() -> Result<()> { - with_config(|_, config, _metadata| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) - }) - .map_err(Into::into) -} - -fn run( - options: MobileOptions, - root_conf: &Config, - config: &AppleConfig, -) -> Result { - let profile = if options.debug { - Profile::Debug - } else { - Profile::Release - }; - let noise_level = NoiseLevel::Polite; - - let env = env()?; - super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; - - device_prompt(&env) - .map_err(Error::FailedToPromptForDevice)? - .run(config, &env, noise_level, false, profile) - .map(|c| DevChild(Some(c))) - .map_err(Error::RunFailed) -} - -fn xcode_script(options: XcodeScriptOptions) -> Result<()> { - fn macos_from_platform(platform: &str) -> bool { - platform == "macOS" - } - - fn profile_from_configuration(configuration: &str) -> Profile { - if configuration == "release" { - Profile::Release - } else { - Profile::Debug - } - } - - let profile = profile_from_configuration(&options.configuration); - let macos = macos_from_platform(&options.platform); - let noise_level = NoiseLevel::Polite; - - with_config(|root_conf, config, metadata| { - let env = env()?; - super::init::init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; - // The `PATH` env var Xcode gives us is missing any additions - // made by the user's profile, so we'll manually add cargo's - // `PATH`. - let env = env.prepend_to_path( - util::home_dir() - .map_err(Error::NoHomeDir)? - .join(".cargo/bin"), - ); - - if !options.sdk_root.is_dir() { - return Err(Error::SdkRootInvalid { - sdk_root: options.sdk_root, - }); - } - let include_dir = options.sdk_root.join("usr/include"); - if !include_dir.is_dir() { - return Err(Error::IncludeDirInvalid { include_dir }); - } - - let mut host_env = HashMap::<&str, &OsStr>::new(); - - // Host flags that are used by build scripts - let (macos_isysroot, library_path) = { - let macos_sdk_root = options - .sdk_root - .join("../../../../MacOSX.platform/Developer/SDKs/MacOSX.sdk"); - if !macos_sdk_root.is_dir() { - return Err(Error::MacosSdkRootInvalid { macos_sdk_root }); - } - ( - format!("-isysroot {}", macos_sdk_root.display()), - format!("{}/usr/lib", macos_sdk_root.display()), - ) - }; - host_env.insert("MAC_FLAGS", macos_isysroot.as_ref()); - host_env.insert("CFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); - host_env.insert("CXXFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); - - host_env.insert( - "OBJC_INCLUDE_PATH_x86_64_apple_darwin", - include_dir.as_os_str(), - ); - - host_env.insert("RUST_BACKTRACE", "1".as_ref()); - - let macos_target = Target::macos(); - - let isysroot = format!("-isysroot {}", options.sdk_root.display()); - - for arch in options.arches { - // Set target-specific flags - let triple = match arch.as_str() { - "arm64" => "aarch64_apple_ios", - "x86_64" => "x86_64_apple_ios", - _ => return Err(Error::ArchInvalid { arch }), - }; - let cflags = format!("CFLAGS_{}", triple); - let cxxflags = format!("CFLAGS_{}", triple); - let objc_include_path = format!("OBJC_INCLUDE_PATH_{}", triple); - let mut target_env = host_env.clone(); - target_env.insert(cflags.as_ref(), isysroot.as_ref()); - target_env.insert(cxxflags.as_ref(), isysroot.as_ref()); - target_env.insert(objc_include_path.as_ref(), include_dir.as_ref()); - // Prevents linker errors in build scripts and proc macros: - // https://github.com/signalapp/libsignal-client/commit/02899cac643a14b2ced7c058cc15a836a2165b6d - target_env.insert("LIBRARY_PATH", library_path.as_ref()); - - let target = if macos { - &macos_target - } else { - Target::for_arch(&arch).ok_or_else(|| Error::ArchInvalid { - arch: arch.to_owned(), - })? - }; - target - .compile_lib( - config, - metadata, - noise_level, - true, - profile, - &env, - target_env, - ) - .map_err(Error::CompileLibFailed)?; - } - Ok(()) - }) - .map_err(Into::into) -} diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs new file mode 100644 index 00000000000..9eb126ff3b6 --- /dev/null +++ b/tooling/cli/src/mobile/ios/build.rs @@ -0,0 +1,147 @@ +use super::{ + detect_target_ok, ensure_init, env, init_dot_cargo, log_finished, with_config, Error, + MobileTarget, +}; +use crate::{ + helpers::{config::get as get_tauri_config, flock}, + interface::{AppSettings, Interface, Options as InterfaceOptions}, + mobile::{write_options, CliOptions}, + Result, +}; +use clap::Parser; + +use cargo_mobile::{ + apple::{config::Config as AppleConfig, target::Target}, + env::Env, + opts::{NoiseLevel, Profile}, + target::{call_for_targets_with_fallback, TargetInvalid, TargetTrait}, +}; + +use std::fs; + +#[derive(Debug, Clone, Parser)] +#[clap(about = "Android build")] +pub struct Options { + /// Builds with the debug flag + #[clap(short, long)] + pub debug: bool, + /// Which targets to build. + #[clap( + short, + long = "target", + multiple_occurrences(true), + multiple_values(true), + default_value = Target::DEFAULT_KEY, + value_parser(clap::builder::PossibleValuesParser::new(Target::name_list())) + )] + pub targets: Vec, + /// List of cargo features to activate + #[clap(short, long, multiple_occurrences(true), multiple_values(true))] + pub features: Option>, + /// JSON string or path to JSON file to merge with tauri.conf.json + #[clap(short, long)] + pub config: Option, + /// Build number to append to the app version. + #[clap(long)] + pub build_number: Option, +} + +impl From for crate::build::Options { + fn from(options: Options) -> Self { + Self { + runner: None, + debug: options.debug, + target: None, + features: options.features, + bundles: None, + config: options.config, + args: Vec::new(), + } + } +} + +pub fn command(options: Options) -> Result<()> { + with_config(|root_conf, config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + + let env = env()?; + init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + + run_build(options, config, env).map_err(|e| Error::BuildFailed(e.to_string())) + }) + .map_err(Into::into) +} + +fn run_build(mut options: Options, config: &AppleConfig, env: Env) -> Result<()> { + let profile = if options.debug { + Profile::Debug + } else { + Profile::Release + }; + let noise_level = NoiseLevel::Polite; + + let bundle_identifier = { + let tauri_config = get_tauri_config(None)?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + tauri_config_.tauri.bundle.identifier.clone() + }; + + let mut build_options = options.clone().into(); + let interface = crate::build::setup(&mut build_options)?; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&InterfaceOptions { + debug: build_options.debug, + ..Default::default() + })?; + let out_dir = bin_path.parent().unwrap(); + let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; + + let cli_options = CliOptions { + features: build_options.features.clone(), + args: build_options.args.clone(), + vars: Default::default(), + }; + write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; + + options + .features + .get_or_insert(Vec::new()) + .push("custom-protocol".into()); + + let mut out_files = Vec::new(); + + call_for_targets_with_fallback( + options.targets.iter(), + &detect_target_ok, + &env, + |target: &Target| { + let mut app_version = config.bundle_version().clone(); + if let Some(build_number) = options.build_number { + app_version.push_extra(build_number); + } + + target.build(config, &env, noise_level, profile)?; + target.archive(config, &env, noise_level, profile, Some(app_version))?; + target.export(config, &env, noise_level)?; + + if let Ok(ipa_path) = config.ipa_path() { + let out_dir = config.export_dir().join(target.arch); + fs::create_dir_all(&out_dir)?; + let path = out_dir.join(ipa_path.file_name().unwrap()); + fs::rename(&ipa_path, &path)?; + out_files.push(path); + } + + anyhow::Result::Ok(()) + }, + ) + .map_err(|e: TargetInvalid| Error::TargetInvalid(e.to_string()))? + .map_err(|e: anyhow::Error| e)?; + + log_finished(out_files, "IPA"); + + Ok(()) +} diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs new file mode 100644 index 00000000000..654e5a6b5e7 --- /dev/null +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -0,0 +1,146 @@ +use super::{device_prompt, ensure_init, env, init_dot_cargo, with_config, Error, MobileTarget}; +use crate::{ + helpers::{config::get as get_tauri_config, flock}, + interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions}, + mobile::{write_options, CliOptions, DevChild, DevProcess}, + Result, +}; +use clap::Parser; + +use cargo_mobile::{ + apple::config::Config as AppleConfig, + config::Config, + opts::{NoiseLevel, Profile}, + os, +}; + +#[derive(Debug, Clone, Parser)] +#[clap(about = "iOS dev")] +pub struct Options { + /// List of cargo features to activate + #[clap(short, long, multiple_occurrences(true), multiple_values(true))] + pub features: Option>, + /// Exit on panic + #[clap(short, long)] + exit_on_panic: bool, + /// JSON string or path to JSON file to merge with tauri.conf.json + #[clap(short, long)] + pub config: Option, + /// Run the code in release mode + #[clap(long = "release")] + pub release_mode: bool, + /// Disable the file watcher + #[clap(long)] + pub no_watch: bool, + /// Open Xcode instead of trying to run on a connected device + #[clap(short, long)] + pub open: bool, +} + +impl From for crate::dev::Options { + fn from(options: Options) -> Self { + Self { + runner: None, + target: None, + features: options.features, + exit_on_panic: options.exit_on_panic, + config: options.config, + release_mode: options.release_mode, + args: Vec::new(), + no_watch: options.no_watch, + } + } +} + +pub fn command(options: Options) -> Result<()> { + with_config(|root_conf, config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + run_dev(options, root_conf, config).map_err(|e| Error::DevFailed(e.to_string())) + }) + .map_err(Into::into) +} + +fn run_dev(options: Options, root_conf: &Config, config: &AppleConfig) -> Result<()> { + let mut dev_options = options.clone().into(); + let mut interface = crate::dev::setup(&mut dev_options)?; + + let bundle_identifier = { + let tauri_config = + get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config_guard = tauri_config.lock().unwrap(); + let tauri_config_ = tauri_config_guard.as_ref().unwrap(); + tauri_config_.tauri.bundle.identifier.clone() + }; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&InterfaceOptions { + debug: !dev_options.release_mode, + ..Default::default() + })?; + let out_dir = bin_path.parent().unwrap(); + let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; + + let open = options.open; + interface.mobile_dev( + MobileOptions { + debug: true, + features: options.features, + args: Vec::new(), + config: options.config, + no_watch: options.no_watch, + }, + |options| { + let cli_options = CliOptions { + features: options.features.clone(), + args: options.args.clone(), + vars: Default::default(), + }; + write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; + if open { + open_dev(config) + } else { + match run(options, root_conf, config) { + Ok(c) => Ok(Box::new(c) as Box), + Err(Error::FailedToPromptForDevice(e)) => { + log::error!("{}", e); + open_dev(config) + } + Err(e) => Err(e.into()), + } + } + }, + ) +} + +fn open_dev(config: &AppleConfig) -> ! { + log::info!("Opening Xcode"); + if let Err(e) = os::open_file_with("Xcode", config.project_dir()) { + log::error!("{}", e); + } + loop { + std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); + } +} + +fn run( + options: MobileOptions, + root_conf: &Config, + config: &AppleConfig, +) -> Result { + let profile = if options.debug { + Profile::Debug + } else { + Profile::Release + }; + let noise_level = NoiseLevel::Polite; + + let env = env()?; + init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + + device_prompt(&env) + .map_err(Error::FailedToPromptForDevice)? + .run(config, &env, noise_level, false, profile) + .map(|c| DevChild(Some(c))) + .map_err(Error::RunFailed) +} diff --git a/tooling/cli/src/mobile/ios/open.rs b/tooling/cli/src/mobile/ios/open.rs new file mode 100644 index 00000000000..331a073aef4 --- /dev/null +++ b/tooling/cli/src/mobile/ios/open.rs @@ -0,0 +1,12 @@ +use super::{ensure_init, with_config, Error, MobileTarget}; +use crate::Result; +use cargo_mobile::os; + +pub fn command() -> Result<()> { + with_config(|_, config, _metadata| { + ensure_init(config.project_dir(), MobileTarget::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) + }) + .map_err(Into::into) +} diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs new file mode 100644 index 00000000000..bf5df1d40c5 --- /dev/null +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -0,0 +1,141 @@ +use super::{env, init_dot_cargo, with_config, Error}; +use crate::Result; +use clap::Parser; + +use cargo_mobile::{ + apple::target::Target, + opts::{NoiseLevel, Profile}, + util, +}; + +use std::{collections::HashMap, ffi::OsStr, path::PathBuf}; + +#[derive(Debug, Parser)] +pub struct Options { + /// Value of `PLATFORM_DISPLAY_NAME` env var + #[clap(long)] + platform: String, + /// Value of `SDKROOT` env var + #[clap(long)] + sdk_root: PathBuf, + /// Value of `CONFIGURATION` env var + #[clap(long)] + configuration: String, + /// Value of `FORCE_COLOR` env var + #[clap(long)] + force_color: bool, + /// Value of `ARCHS` env var + #[clap(index = 1, required = true)] + arches: Vec, +} + +pub fn command(options: Options) -> Result<()> { + fn macos_from_platform(platform: &str) -> bool { + platform == "macOS" + } + + fn profile_from_configuration(configuration: &str) -> Profile { + if configuration == "release" { + Profile::Release + } else { + Profile::Debug + } + } + + let profile = profile_from_configuration(&options.configuration); + let macos = macos_from_platform(&options.platform); + let noise_level = NoiseLevel::Polite; + + with_config(|root_conf, config, metadata| { + let env = env()?; + init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + // The `PATH` env var Xcode gives us is missing any additions + // made by the user's profile, so we'll manually add cargo's + // `PATH`. + let env = env.prepend_to_path( + util::home_dir() + .map_err(Error::NoHomeDir)? + .join(".cargo/bin"), + ); + + if !options.sdk_root.is_dir() { + return Err(Error::SdkRootInvalid { + sdk_root: options.sdk_root, + }); + } + let include_dir = options.sdk_root.join("usr/include"); + if !include_dir.is_dir() { + return Err(Error::IncludeDirInvalid { include_dir }); + } + + let mut host_env = HashMap::<&str, &OsStr>::new(); + + // Host flags that are used by build scripts + let (macos_isysroot, library_path) = { + let macos_sdk_root = options + .sdk_root + .join("../../../../MacOSX.platform/Developer/SDKs/MacOSX.sdk"); + if !macos_sdk_root.is_dir() { + return Err(Error::MacosSdkRootInvalid { macos_sdk_root }); + } + ( + format!("-isysroot {}", macos_sdk_root.display()), + format!("{}/usr/lib", macos_sdk_root.display()), + ) + }; + host_env.insert("MAC_FLAGS", macos_isysroot.as_ref()); + host_env.insert("CFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); + host_env.insert("CXXFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); + + host_env.insert( + "OBJC_INCLUDE_PATH_x86_64_apple_darwin", + include_dir.as_os_str(), + ); + + host_env.insert("RUST_BACKTRACE", "1".as_ref()); + + let macos_target = Target::macos(); + + let isysroot = format!("-isysroot {}", options.sdk_root.display()); + + for arch in options.arches { + // Set target-specific flags + let triple = match arch.as_str() { + "arm64" => "aarch64_apple_ios", + "x86_64" => "x86_64_apple_ios", + _ => return Err(Error::ArchInvalid { arch }), + }; + let cflags = format!("CFLAGS_{}", triple); + let cxxflags = format!("CFLAGS_{}", triple); + let objc_include_path = format!("OBJC_INCLUDE_PATH_{}", triple); + let mut target_env = host_env.clone(); + target_env.insert(cflags.as_ref(), isysroot.as_ref()); + target_env.insert(cxxflags.as_ref(), isysroot.as_ref()); + target_env.insert(objc_include_path.as_ref(), include_dir.as_ref()); + // Prevents linker errors in build scripts and proc macros: + // https://github.com/signalapp/libsignal-client/commit/02899cac643a14b2ced7c058cc15a836a2165b6d + target_env.insert("LIBRARY_PATH", library_path.as_ref()); + + let target = if macos { + &macos_target + } else { + Target::for_arch(&arch).ok_or_else(|| Error::ArchInvalid { + arch: arch.to_owned(), + })? + }; + target + .compile_lib( + config, + metadata, + noise_level, + true, + profile, + &env, + target_env, + ) + .map_err(Error::CompileLibFailed)?; + } + Ok(()) + }) + .map_err(Into::into) +} diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index db6696db733..71e8a606405 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -17,7 +17,7 @@ use cargo_mobile::{ config::{app::Raw as RawAppConfig, metadata::Metadata, Config, Raw}, }; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, ffi::OsString, path::PathBuf, process::ExitStatus}; +use std::{collections::HashMap, ffi::OsString, fmt::Write, path::PathBuf, process::ExitStatus}; pub mod android; mod init; @@ -238,3 +238,14 @@ fn ensure_init(project_dir: PathBuf, target: Target) -> Result<()> { Ok(()) } } + +fn log_finished(outputs: Vec, kind: &str) { + if !outputs.is_empty() { + let mut printable_paths = String::new(); + for path in &outputs { + writeln!(printable_paths, " {}", path.display()).unwrap(); + } + + log::info!(action = "Finished"; "{} {}{} at:\n{}", outputs.len(), kind, if outputs.len() == 1 { "" } else { "s" }, printable_paths); + } +} From a70f7b26bc8f2bdc651842a3805ca8c3c5f38f4f Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 23 Aug 2022 11:34:16 -0300 Subject: [PATCH 022/423] feat(cli): improve error messages in mobile commands --- tooling/cli/src/mobile/android/build.rs | 2 +- tooling/cli/src/mobile/android/dev.rs | 2 +- tooling/cli/src/mobile/ios/build.rs | 2 +- tooling/cli/src/mobile/ios/dev.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index a6fbeb1ebb0..cca10d33748 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -67,7 +67,7 @@ pub fn command(options: Options) -> Result<()> { let env = Env::new().map_err(Error::EnvInitFailed)?; init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; - run_build(options, config, env).map_err(|e| Error::BuildFailed(e.to_string())) + run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e))) }) .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index f6e1033d2b9..636bce92660 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -56,7 +56,7 @@ pub fn command(options: Options) -> Result<()> { with_config(|root_conf, config, metadata| { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config, metadata).map_err(|e| Error::DevFailed(e.to_string())) + run_dev(options, root_conf, config, metadata).map_err(|e| Error::DevFailed(format!("{:#}", e))) }) .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index 9eb126ff3b6..e6b7cb7b522 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -68,7 +68,7 @@ pub fn command(options: Options) -> Result<()> { let env = env()?; init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; - run_build(options, config, env).map_err(|e| Error::BuildFailed(e.to_string())) + run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e))) }) .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 654e5a6b5e7..7bf4aa311ce 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -56,7 +56,7 @@ pub fn command(options: Options) -> Result<()> { with_config(|root_conf, config, _metadata| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config).map_err(|e| Error::DevFailed(e.to_string())) + run_dev(options, root_conf, config).map_err(|e| Error::DevFailed(format!("{:#}", e))) }) .map_err(Into::into) } From 4e26b05d20d1db0ab682352a8d9c52b10e3c81e9 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 23 Aug 2022 17:39:26 +0200 Subject: [PATCH 023/423] fix(cli/template/android): use raw string for executable (#5012) --- .../mobile/android/buildSrc/src/main/kotlin/BuildTask.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt index ea09602f464..500781d55b9 100644 --- a/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt +++ b/tooling/cli/templates/mobile/android/buildSrc/src/main/kotlin/BuildTask.kt @@ -26,7 +26,7 @@ open class BuildTask : DefaultTask() { val release = release ?: throw GradleException("release cannot be null") project.exec { workingDir(File(project.projectDir, rootDirRel.path)) - executable("{{ tauri-binary }}") + executable("""{{ tauri-binary }}""") args(listOf({{quote-and-join tauri-binary-args}})) if (project.logger.isEnabled(LogLevel.DEBUG)) { args("-vv") From 2b846f413cea59fd95e4731302888308db059e0a Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 23 Aug 2022 17:40:02 +0200 Subject: [PATCH 024/423] feat(examples/api): use strict port (#5013) --- examples/api/vite.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/api/vite.config.js b/examples/api/vite.config.js index 9157d89bbd6..c5d66fbbc07 100644 --- a/examples/api/vite.config.js +++ b/examples/api/vite.config.js @@ -18,6 +18,8 @@ export default defineConfig(async ({ command, mode }) => { } }, server: { + port: 5173, + strictPort: true, hmr: { host, port: 5183 From 3668a1fdc8cccf8c267d617a727db59f0365e378 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Tue, 23 Aug 2022 13:54:41 -0300 Subject: [PATCH 025/423] fix(cli): resolve absolute tauri binary path for the android template (#5015) --- tooling/cli/src/mobile/init.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index d95fd9294d3..4ad743dc48d 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -127,11 +127,19 @@ pub fn exec( let (handlebars, mut map) = handlebars(&config); let mut args = std::env::args_os(); - // TODO: make this a relative path let tauri_binary = args .next() + .map(|bin| { + let path = PathBuf::from(&bin); + if path.exists() { + if let Ok(dir) = current_dir() { + let absolute_path = util::prefix_path(dir, path); + return absolute_path.into(); + } + } + bin + }) .unwrap_or_else(|| std::ffi::OsString::from("cargo")); - map.insert("tauri-binary", tauri_binary.to_string_lossy()); let mut build_args = Vec::new(); for arg in args { let path = PathBuf::from(&arg); @@ -148,6 +156,7 @@ pub fn exec( } } build_args.push(target.ide_build_script_name().into()); + map.insert("tauri-binary", tauri_binary.to_string_lossy()); map.insert("tauri-binary-args", &build_args); map.insert("tauri-binary-args-str", build_args.join(" ")); From 1f84385e8a76f5dd97c32611f38b15e41585ef9c Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 24 Aug 2022 15:01:32 +0200 Subject: [PATCH 026/423] chore: update cargo-mobile to latest (#5025) Co-authored-by: Lucas Nogueira --- tooling/cli/Cargo.lock | 2 +- tooling/cli/src/mobile/mod.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 1437c9675ae..e7b6a9261b0 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -250,7 +250,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#5444888b3e9bc5a596bc2668315c8805a865c5f6" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#d5b4c4dd5ac23e700f07e362b8788d80a33d21d3" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 71e8a606405..ee2de83b948 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -176,6 +176,7 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) { macos_version: None, use_legacy_build_system: None, plist_pairs: None, + enable_bitcode: None, }), android: Some(RawAndroidConfig { min_sdk_version: None, @@ -195,12 +196,14 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) { no_default_features: false, cargo_args: Some(ios_options.args), features: ios_options.features, + libraries: None, frameworks: None, valid_archs: None, vendor_frameworks: None, vendor_sdks: None, asset_catalogs: None, pods: None, + pod_options: None, additional_targets: None, pre_build_scripts: None, post_compile_scripts: None, @@ -220,6 +223,9 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) { app_dependencies: None, app_dependencies_platform: None, asset_packs: None, + app_activity_name: None, + app_permissions: None, + app_theme_parent: None, }, }; From 0500d3b4b1ba03804981e74be28488fd21572b27 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Wed, 24 Aug 2022 12:41:57 -0300 Subject: [PATCH 027/423] fix(core): mobile app name must match the crate name (#5027) --- core/tauri-build/src/lib.rs | 6 +----- core/tauri-macros/src/mobile.rs | 2 +- tooling/cli/src/interface/mod.rs | 2 +- tooling/cli/src/interface/rust.rs | 2 +- tooling/cli/src/interface/rust/manifest.rs | 2 +- tooling/cli/src/mobile/mod.rs | 14 ++++++++++++++ 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 98ad640aef8..7b972366842 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -221,19 +221,15 @@ pub fn try_build(attributes: Attributes) -> Result<()> { let s = config.tauri.bundle.identifier.split('.'); let last = s.clone().count() - 1; - let mut app_name = String::new(); let mut domain = String::new(); for (i, w) in s.enumerate() { - if i == last { - app_name.push_str(w); - } else { + if i != last { domain.push_str(w); domain.push('_'); } } domain.pop(); println!("cargo:rustc-env=TAURI_ANDROID_DOMAIN={}", domain); - println!("cargo:rustc-env=TAURI_ANDROID_APP_NAME={}", app_name); cfg_alias("dev", !has_feature("custom-protocol")); diff --git a/core/tauri-macros/src/mobile.rs b/core/tauri-macros/src/mobile.rs index e98b647de51..3bd170075ca 100644 --- a/core/tauri-macros/src/mobile.rs +++ b/core/tauri-macros/src/mobile.rs @@ -32,7 +32,7 @@ pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { let mut error = None; let domain = get_env_var("TAURI_ANDROID_DOMAIN", &mut error, &function); - let app_name = get_env_var("TAURI_ANDROID_APP_NAME", &mut error, &function); + let app_name = get_env_var("CARGO_PKG_NAME", &mut error, &function); if let Some(e) = error { quote!(#e).into() diff --git a/tooling/cli/src/interface/mod.rs b/tooling/cli/src/interface/mod.rs index e946f071d9b..178eead72ae 100644 --- a/tooling/cli/src/interface/mod.rs +++ b/tooling/cli/src/interface/mod.rs @@ -12,7 +12,7 @@ use std::{ use crate::helpers::config::Config; use tauri_bundler::bundle::{PackageType, Settings, SettingsBuilder}; -pub use rust::{MobileOptions, Options, Rust as AppInterface}; +pub use rust::{manifest, MobileOptions, Options, Rust as AppInterface}; pub trait DevProcess { fn kill(&mut self) -> std::io::Result<()>; diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 0357934fc11..bd4692658d8 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -36,7 +36,7 @@ use crate::helpers::{ mod cargo_config; mod desktop; -mod manifest; +pub mod manifest; use cargo_config::Config as CargoConfig; use manifest::{rewrite_manifest, Manifest}; diff --git a/tooling/cli/src/interface/rust/manifest.rs b/tooling/cli/src/interface/rust/manifest.rs index d19ad837013..3b257edba56 100644 --- a/tooling/cli/src/interface/rust/manifest.rs +++ b/tooling/cli/src/interface/rust/manifest.rs @@ -82,7 +82,7 @@ fn get_enabled_features(list: &HashMap>, feature: &str) -> V f } -fn read_manifest(manifest_path: &Path) -> crate::Result { +pub fn read_manifest(manifest_path: &Path) -> crate::Result { let mut manifest_str = String::new(); let mut manifest_file = File::open(manifest_path) diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index ee2de83b948..f3ddea06795 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -147,6 +147,20 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) { } domain.pop(); + let manifest_path = tauri_dir().join("Cargo.toml"); + let app_name = if let Ok(manifest) = crate::interface::manifest::read_manifest(&manifest_path) { + manifest + .as_table() + .get("package") + .and_then(|p| p.as_table()) + .and_then(|p| p.get("name")) + .and_then(|n| n.as_str()) + .map(|n| n.to_string()) + .unwrap_or(app_name) + } else { + app_name + }; + #[cfg(target_os = "macos")] let ios_options = read_options(config, Target::Ios).unwrap_or_default(); let android_options = read_options(config, Target::Android).unwrap_or_default(); From 641d56dcb3f47732df2b8ca407e9ff56dbe502f3 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Wed, 24 Aug 2022 13:06:45 -0300 Subject: [PATCH 028/423] feat(android): improve initialization scripts (#5028) --- examples/api/src-tauri/Cargo.lock | 3 +-- examples/api/src-tauri/Cargo.toml | 3 +++ .../app/src/main/RustWebChromeClient.kt | 26 ------------------- .../android/app/src/main/RustWebViewClient.kt | 16 +++++++++++- 4 files changed, 19 insertions(+), 29 deletions(-) delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/RustWebChromeClient.kt diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 73bd19c32ea..bf5d42d2e18 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -4223,8 +4223,7 @@ dependencies = [ [[package]] name = "wry" version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea48fb3b68ab76f62837bfcea63baed9a185dbec9c14d4e5d70033e22fefffd2" +source = "git+https://github.com/tauri-apps/wry?branch=dev#1b26d605d6e33f5417eb6566a7381d8feb239c8b" dependencies = [ "block", "cocoa", diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 92d75446cf0..19c491ff50c 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -6,6 +6,9 @@ edition = "2021" rust-version = "1.57" license = "Apache-2.0 OR MIT" +[patch.crates-io] +wry = { git = "https://github.com/tauri-apps/wry", branch = "dev" } + [lib] crate-type = ["staticlib", "cdylib", "rlib"] diff --git a/tooling/cli/templates/mobile/android/app/src/main/RustWebChromeClient.kt b/tooling/cli/templates/mobile/android/app/src/main/RustWebChromeClient.kt deleted file mode 100644 index eda111cc7be..00000000000 --- a/tooling/cli/templates/mobile/android/app/src/main/RustWebChromeClient.kt +++ /dev/null @@ -1,26 +0,0 @@ -package {{reverse-domain app.domain}}.{{snake-case app.name}} - -import android.webkit.* - -class RustWebChromeClient: WebChromeClient() { - private var loadedUrl: String? = null - - override fun onProgressChanged(view: WebView, progress: Int) { - var url = view.url ?: "" - if (url.endsWith("##")) { - url = url.dropLast(2) - } - if (loadedUrl != url && progress >= 20) { - loadedUrl = url - runInitializationScripts() - } - } - - companion object { - init { - System.loadLibrary("{{snake-case app.name}}") - } - } - - private external fun runInitializationScripts() -} diff --git a/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt b/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt index c6c65fa951e..b854a32de86 100644 --- a/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt +++ b/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt @@ -1,8 +1,22 @@ package {{reverse-domain app.domain}}.{{snake-case app.name}} +import android.graphics.Bitmap import android.webkit.* -class RustWebViewClient: WebViewClient() { +class RustWebViewClient(initScripts: Array): WebViewClient() { + private val initializationScripts: Array + + init { + initializationScripts = initScripts + } + + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + for (script in initializationScripts) { + view?.evaluateJavascript(script, null) + } + super.onPageStarted(view, url, favicon) + } + override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { return false } From ff4cb56b2ed9b86f8f684f1e03d272269f61aa67 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Wed, 24 Aug 2022 14:02:08 -0300 Subject: [PATCH 029/423] fix(tauri-macros): escape `_` in mobile entry point's app name (#5029) --- core/tauri-macros/src/mobile.rs | 18 ++++++++++++++---- examples/api/src-tauri/Cargo.lock | 3 +-- examples/api/src-tauri/Cargo.toml | 1 + 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/core/tauri-macros/src/mobile.rs b/core/tauri-macros/src/mobile.rs index 3bd170075ca..ca593ea3b12 100644 --- a/core/tauri-macros/src/mobile.rs +++ b/core/tauri-macros/src/mobile.rs @@ -4,10 +4,15 @@ use quote::{format_ident, quote}; use std::env::var; use syn::{parse_macro_input, spanned::Spanned, ItemFn}; -fn get_env_var(name: &str, error: &mut Option, function: &ItemFn) -> TokenStream2 { +fn get_env_var String>( + name: &str, + replacer: R, + error: &mut Option, + function: &ItemFn, +) -> TokenStream2 { match var(name) { Ok(value) => { - let ident = format_ident!("{}", value); + let ident = format_ident!("{}", replacer(value)); quote!(#ident) } Err(_) => { @@ -31,8 +36,13 @@ pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { let function_name = function.sig.ident.clone(); let mut error = None; - let domain = get_env_var("TAURI_ANDROID_DOMAIN", &mut error, &function); - let app_name = get_env_var("CARGO_PKG_NAME", &mut error, &function); + let domain = get_env_var("TAURI_ANDROID_DOMAIN", |r| r, &mut error, &function); + let app_name = get_env_var( + "CARGO_PKG_NAME", + |r| r.replace('_', "_1"), + &mut error, + &function, + ); if let Some(e) = error { quote!(#e).into() diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index bf5d42d2e18..7966d0fbae2 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -3107,8 +3107,7 @@ dependencies = [ [[package]] name = "tao" version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2093fa6bba3cc0c185b21c900de1b757e66637e78848cbcdda967b836d8c0ec" +source = "git+https://github.com/tauri-apps/tao?branch=dev#816ca49dc648a5286eab943c6fc936629fd3a596" dependencies = [ "bitflags", "cairo-rs", diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 19c491ff50c..a8c0e3a9edf 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -8,6 +8,7 @@ license = "Apache-2.0 OR MIT" [patch.crates-io] wry = { git = "https://github.com/tauri-apps/wry", branch = "dev" } +tao = { git = "https://github.com/tauri-apps/tao", branch = "dev" } [lib] crate-type = ["staticlib", "cdylib", "rlib"] From 752ad3b20343d03128073d666ae002f2f8f2289e Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Wed, 24 Aug 2022 16:06:14 -0300 Subject: [PATCH 030/423] feat(cli): use templates from wry (#5030) --- examples/api/src-tauri/Cargo.lock | 2 +- tooling/cli/Cargo.lock | 2 +- tooling/cli/src/mobile/android.rs | 10 ++- .../mobile/android/android_studio_script.rs | 6 +- tooling/cli/src/mobile/android/build.rs | 4 +- tooling/cli/src/mobile/android/dev.rs | 9 +-- tooling/cli/src/mobile/ios.rs | 11 +-- tooling/cli/src/mobile/mod.rs | 52 ++++++++++++-- .../mobile/android/app/src/main/Ipc.kt | 18 ----- .../android/app/src/main/RustWebViewClient.kt | 38 ---------- .../android/app/src/main/TauriActivity.kt | 69 +------------------ 11 files changed, 71 insertions(+), 150 deletions(-) delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/Ipc.kt delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 7966d0fbae2..86333c28ca9 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -4222,7 +4222,7 @@ dependencies = [ [[package]] name = "wry" version = "0.20.2" -source = "git+https://github.com/tauri-apps/wry?branch=dev#1b26d605d6e33f5417eb6566a7381d8feb239c8b" +source = "git+https://github.com/tauri-apps/wry?branch=dev#b4789034dc4d10ab83f6acce6b4152d79f702940" dependencies = [ "block", "cocoa", diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index e7b6a9261b0..81d4a74255a 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -250,7 +250,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#d5b4c4dd5ac23e700f07e362b8788d80a33d21d3" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#5b866af17df4e2310e084efd3550ec31f62ff984" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 2d9aba649af..440938ba52d 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -7,11 +7,12 @@ use cargo_mobile::{ adb, config::{Config as AndroidConfig, Metadata as AndroidMetadata}, device::{Device, RunError}, - env::{Env, Error as EnvError}, + env::{Env, Error as AndroidEnvError}, target::{BuildError, Target}, }, config::Config, device::PromptError, + env::Error as EnvError, os, util::prompt, }; @@ -35,6 +36,8 @@ enum Error { #[error(transparent)] EnvInitFailed(EnvError), #[error(transparent)] + AndroidEnvInitFailed(AndroidEnvError), + #[error(transparent)] InitDotCargo(super::init::Error), #[error("invalid tauri configuration: {0}")] InvalidTauriConfig(String), @@ -105,6 +108,11 @@ fn with_config( f(&config, config.android(), metadata.android()) } +fn env() -> Result { + let env = super::env().map_err(Error::EnvInitFailed)?; + cargo_mobile::android::env::Env::from_env(env).map_err(Error::AndroidEnvInitFailed) +} + fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { let device_list = adb::device_list(env).map_err(|cause| PromptError::detection_failed("Android", cause))?; diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index d7821394163..8c37af04939 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -1,9 +1,9 @@ -use super::{detect_target_ok, ensure_init, init_dot_cargo, with_config, Error, MobileTarget}; +use super::{detect_target_ok, ensure_init, env, init_dot_cargo, with_config, Error, MobileTarget}; use crate::Result; use clap::Parser; use cargo_mobile::{ - android::{env::Env, target::Target}, + android::target::Target, opts::{NoiseLevel, Profile}, target::{call_for_targets_with_fallback, TargetTrait}, }; @@ -37,7 +37,7 @@ pub fn command(options: Options) -> Result<()> { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - let env = Env::new().map_err(Error::EnvInitFailed)?; + let env = env()?; init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; call_for_targets_with_fallback( diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index cca10d33748..5766f5f68bf 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -1,4 +1,4 @@ -use super::{ensure_init, init_dot_cargo, log_finished, with_config, Error, MobileTarget}; +use super::{ensure_init, env, init_dot_cargo, log_finished, with_config, Error, MobileTarget}; use crate::{ helpers::{config::get as get_tauri_config, flock}, interface::{AppSettings, Interface, Options as InterfaceOptions}, @@ -64,7 +64,7 @@ pub fn command(options: Options) -> Result<()> { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - let env = Env::new().map_err(Error::EnvInitFailed)?; + let env = env()?; init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e))) diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 636bce92660..073d1af7c3a 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -1,4 +1,4 @@ -use super::{device_prompt, ensure_init, init_dot_cargo, with_config, Error, MobileTarget}; +use super::{device_prompt, ensure_init, env, init_dot_cargo, with_config, Error, MobileTarget}; use crate::{ helpers::{config::get as get_tauri_config, flock}, interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions}, @@ -8,10 +8,7 @@ use crate::{ use clap::Parser; use cargo_mobile::{ - android::{ - config::{Config as AndroidConfig, Metadata as AndroidMetadata}, - env::Env, - }, + android::config::{Config as AndroidConfig, Metadata as AndroidMetadata}, config::Config, opts::{NoiseLevel, Profile}, os, @@ -143,7 +140,7 @@ fn run( let build_app_bundle = metadata.asset_packs().is_some(); - let env = Env::new().map_err(Error::EnvInitFailed)?; + let env = env()?; init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; device_prompt(&env) diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 388e9b31830..5a38dcf12c8 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -18,7 +18,7 @@ use cargo_mobile::{ use clap::{Parser, Subcommand}; use super::{ - ensure_init, env_vars, get_config, + ensure_init, env, get_config, init::{command as init_command, init_dot_cargo, Options as InitOptions}, log_finished, Target as MobileTarget, }; @@ -35,7 +35,7 @@ mod xcode_script; #[derive(Debug, thiserror::Error)] enum Error { #[error(transparent)] - EnvInitFailed(EnvError), + EnvInitFailed(#[from] EnvError), #[error(transparent)] InitDotCargo(super::init::Error), #[error("invalid tauri configuration: {0}")] @@ -116,13 +116,6 @@ fn with_config( f(&config, config.apple(), metadata.apple()) } -fn env() -> Result { - let env = Env::new() - .map_err(Error::EnvInitFailed)? - .explicit_env_vars(env_vars()); - Ok(env) -} - fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { let device_list = ios_deploy::device_list(env).map_err(|cause| PromptError::detection_failed("iOS", cause))?; diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index f3ddea06795..7e996ee91e2 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -15,9 +15,17 @@ use cargo_mobile::{ android::config::{Metadata as AndroidMetadata, Raw as RawAndroidConfig}, bossy, config::{app::Raw as RawAppConfig, metadata::Metadata, Config, Raw}, + env::Error as EnvError, }; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, ffi::OsString, fmt::Write, path::PathBuf, process::ExitStatus}; +use std::{ + collections::HashMap, env::set_var, ffi::OsString, fmt::Write, path::PathBuf, process::ExitStatus, +}; + +#[cfg(not(windows))] +use cargo_mobile::env::Env; +#[cfg(windows)] +use cargo_mobile::os::Env; pub mod android; mod init; @@ -110,13 +118,20 @@ fn env_vars() -> HashMap { let mut vars = HashMap::new(); for (k, v) in std::env::vars_os() { let k = k.to_string_lossy(); - if k.starts_with("TAURI") && k != "TAURI_PRIVATE_KEY" && k != "TAURI_KEY_PASSWORD" { + if (k.starts_with("TAURI") && k != "TAURI_PRIVATE_KEY" && k != "TAURI_KEY_PASSWORD") + || k.starts_with("WRY") + { vars.insert(k.into_owned(), v); } } vars } +fn env() -> Result { + let env = Env::new()?.explicit_env_vars(env_vars()); + Ok(env) +} + /// Writes CLI options to be used later on the Xcode and Android Studio build commands pub fn write_options( mut options: CliOptions, @@ -133,7 +148,10 @@ pub fn write_options( fn read_options(config: &TauriConfig, target: Target) -> crate::Result { let data = std::fs::read_to_string(options_path(&config.tauri.bundle.identifier, target))?; - let options = serde_json::from_str(&data)?; + let options: CliOptions = serde_json::from_str(&data)?; + for (k, v) in &options.vars { + set_var(k, v); + } Ok(options) } @@ -147,6 +165,17 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) { } domain.pop(); + let s = config.tauri.bundle.identifier.split('.'); + let last = s.clone().count() - 1; + let mut reverse_domain = String::new(); + for (i, w) in s.enumerate() { + if i != last { + reverse_domain.push_str(w); + reverse_domain.push('.'); + } + } + reverse_domain.pop(); + let manifest_path = tauri_dir().join("Cargo.toml"); let app_name = if let Ok(manifest) = crate::interface::manifest::read_manifest(&manifest_path) { manifest @@ -167,7 +196,7 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) { let raw = Raw { app: RawAppConfig { - name: app_name, + name: app_name.clone(), stylized_name: config.package.product_name.clone(), domain, asset_dir: None, @@ -243,6 +272,21 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) { }, }; + set_var("WRY_ANDROID_REVERSED_DOMAIN", &reverse_domain); + set_var("WRY_ANDROID_APP_NAME_SNAKE_CASE", &app_name); + set_var( + "WRY_ANDROID_KOTLIN_FILES_OUT_DIR", + config + .android() + .project_dir() + .join("app/src/main") + .join(format!( + "java/{}/{}", + config.app().reverse_domain().replace('.', "/"), + config.app().name() + )), + ); + (config, metadata) } diff --git a/tooling/cli/templates/mobile/android/app/src/main/Ipc.kt b/tooling/cli/templates/mobile/android/app/src/main/Ipc.kt deleted file mode 100644 index f2bb26f1575..00000000000 --- a/tooling/cli/templates/mobile/android/app/src/main/Ipc.kt +++ /dev/null @@ -1,18 +0,0 @@ -package {{reverse-domain app.domain}}.{{snake-case app.name}} - -import android.webkit.* - -class Ipc { - @JavascriptInterface - fun postMessage(message: String) { - this.ipc(message) - } - - companion object { - init { - System.loadLibrary("{{snake-case app.name}}") - } - } - - private external fun ipc(message: String) -} diff --git a/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt b/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt deleted file mode 100644 index b854a32de86..00000000000 --- a/tooling/cli/templates/mobile/android/app/src/main/RustWebViewClient.kt +++ /dev/null @@ -1,38 +0,0 @@ -package {{reverse-domain app.domain}}.{{snake-case app.name}} - -import android.graphics.Bitmap -import android.webkit.* - -class RustWebViewClient(initScripts: Array): WebViewClient() { - private val initializationScripts: Array - - init { - initializationScripts = initScripts - } - - override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { - for (script in initializationScripts) { - view?.evaluateJavascript(script, null) - } - super.onPageStarted(view, url, favicon) - } - - override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { - return false - } - - override fun shouldInterceptRequest( - view: WebView, - request: WebResourceRequest - ): WebResourceResponse? { - return handleRequest(request) - } - - companion object { - init { - System.loadLibrary("{{snake-case app.name}}") - } - } - - private external fun handleRequest(request: WebResourceRequest): WebResourceResponse? -} diff --git a/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt b/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt index 84b13f949a8..2d1ffa69a32 100644 --- a/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt +++ b/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt @@ -1,71 +1,6 @@ -package {{reverse-domain app.domain}}.{{snake-case app.name}} +package {{app-domain-reversed}}.{{app-name-snake-case}} import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -abstract class TauriActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - create(this) - } - - override fun onStart() { - super.onStart() - start() - } - - override fun onResume() { - super.onResume() - resume() - } - - override fun onPause() { - super.onPause() - pause() - } - - override fun onStop() { - super.onStop() - stop() - } - - override fun onWindowFocusChanged(hasFocus: Boolean) { - super.onWindowFocusChanged(hasFocus) - focus(hasFocus) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - save() - } - - override fun onDestroy() { - super.onDestroy() - destroy() - } - - override fun onLowMemory() { - super.onLowMemory() - memory() - } - - fun getAppClass(name: String): Class<*> { - return Class.forName(name) - } - - companion object { - init { - System.loadLibrary("{{snake-case app.name}}") - } - } - - private external fun create(activity: TauriActivity) - private external fun start() - private external fun resume() - private external fun pause() - private external fun stop() - private external fun save() - private external fun destroy() - private external fun memory() - private external fun focus(focus: Boolean) -} +abstract class TauriActivity : AppCompatActivity() From 4a5f2ec1aee0d1af026bf74b1cfad049fc537872 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 25 Aug 2022 00:19:47 -0300 Subject: [PATCH 031/423] feat(android): enable dev HMR in both HTTP and HTTPS dev servers (#5033) --- core/tauri/src/manager.rs | 5 ++++- examples/api/src-tauri/Cargo.lock | 2 +- examples/api/vite.config.js | 1 + tooling/cli/src/mobile/android.rs | 8 +++++++ tooling/cli/src/mobile/android/build.rs | 11 +++++++++- tooling/cli/src/mobile/android/dev.rs | 22 ++++++++++++++++++- .../mobile/android/app/build.gradle.kts | 2 ++ .../android/app/src/main/AndroidManifest.xml | 3 ++- 8 files changed, 49 insertions(+), 5 deletions(-) diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 1f670d2352e..35083fb82c7 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -847,7 +847,10 @@ impl WindowManager { let mut response = { let mut url = url.clone(); url.set_path(&path); - match attohttpc::get(url.as_str()).send() { + match attohttpc::get(url.as_str()) + .danger_accept_invalid_certs(true) + .send() + { Ok(r) => { for (name, value) in r.headers() { builder = builder.header(name, value); diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 86333c28ca9..ce1d02a8c4c 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -4222,7 +4222,7 @@ dependencies = [ [[package]] name = "wry" version = "0.20.2" -source = "git+https://github.com/tauri-apps/wry?branch=dev#b4789034dc4d10ab83f6acce6b4152d79f702940" +source = "git+https://github.com/tauri-apps/wry?branch=dev#b1e8560c3f13f2674528f6ca440ba476ddbef7c2" dependencies = [ "block", "cocoa", diff --git a/examples/api/vite.config.js b/examples/api/vite.config.js index c5d66fbbc07..6ccdda26aa3 100644 --- a/examples/api/vite.config.js +++ b/examples/api/vite.config.js @@ -21,6 +21,7 @@ export default defineConfig(async ({ command, mode }) => { port: 5173, strictPort: true, hmr: { + protocol: 'ws', host, port: 5183 }, diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 440938ba52d..4e580932158 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -113,6 +113,14 @@ fn env() -> Result { cargo_mobile::android::env::Env::from_env(env).map_err(Error::AndroidEnvInitFailed) } +fn delete_codegen_vars() { + for (k, _) in std::env::vars() { + if k.starts_with("WRY_") && (k.ends_with("CLASS_EXTENSION") || k.ends_with("CLASS_INIT")) { + std::env::remove_var(k); + } + } +} + fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { let device_list = adb::device_list(env).map_err(|cause| PromptError::detection_failed("Android", cause))?; diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index 5766f5f68bf..fdc8be9ef1a 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -1,4 +1,7 @@ -use super::{ensure_init, env, init_dot_cargo, log_finished, with_config, Error, MobileTarget}; +use super::{ + delete_codegen_vars, ensure_init, env, init_dot_cargo, log_finished, with_config, Error, + MobileTarget, +}; use crate::{ helpers::{config::get as get_tauri_config, flock}, interface::{AppSettings, Interface, Options as InterfaceOptions}, @@ -13,6 +16,8 @@ use cargo_mobile::{ target::TargetTrait, }; +use std::env::set_var; + #[derive(Debug, Clone, Parser)] #[clap(about = "Android build")] pub struct Options { @@ -60,7 +65,11 @@ impl From for crate::build::Options { } pub fn command(options: Options) -> Result<()> { + delete_codegen_vars(); with_config(|root_conf, config, _metadata| { + set_var("WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", ""); + set_var("WRY_RUSTWEBVIEW_CLASS_INIT", ""); + ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 073d1af7c3a..3c2e17aac1e 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -1,4 +1,7 @@ -use super::{device_prompt, ensure_init, env, init_dot_cargo, with_config, Error, MobileTarget}; +use super::{ + delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, with_config, Error, + MobileTarget, +}; use crate::{ helpers::{config::get as get_tauri_config, flock}, interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions}, @@ -14,6 +17,17 @@ use cargo_mobile::{ os, }; +use std::env::set_var; + +const WEBVIEW_CLIENT_CLASS_EXTENSION: &str = " + @android.annotation.SuppressLint(\"WebViewClientOnReceivedSslError\") + override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler, error: android.net.http.SslError) { + handler.proceed() + } +"; +const WEBVIEW_CLASS_INIT: &str = + "this.settings.mixedContentMode = android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW"; + #[derive(Debug, Clone, Parser)] #[clap(about = "Android dev")] pub struct Options { @@ -50,7 +64,13 @@ impl From for crate::dev::Options { } pub fn command(options: Options) -> Result<()> { + delete_codegen_vars(); with_config(|root_conf, config, metadata| { + set_var( + "WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", + WEBVIEW_CLIENT_CLASS_EXTENSION, + ); + set_var("WRY_RUSTWEBVIEW_CLASS_INIT", WEBVIEW_CLASS_INIT); ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; run_dev(options, root_conf, config, metadata).map_err(|e| Error::DevFailed(format!("{:#}", e))) diff --git a/tooling/cli/templates/mobile/android/app/build.gradle.kts b/tooling/cli/templates/mobile/android/app/build.gradle.kts index 9031fd8ed2b..7ebfc04805e 100644 --- a/tooling/cli/templates/mobile/android/app/build.gradle.kts +++ b/tooling/cli/templates/mobile/android/app/build.gradle.kts @@ -9,6 +9,7 @@ plugins { android { compileSdk = 33 defaultConfig { + manifestPlaceholders["usesCleartextTraffic"] = "false" applicationId = "{{reverse-domain app.domain}}.{{snake-case app.name}}" minSdk = {{android.min-sdk-version}} targetSdk = 33 @@ -23,6 +24,7 @@ android { } buildTypes { getByName("debug") { + manifestPlaceholders["usesCleartextTraffic"] = "true" isDebuggable = true isJniDebuggable = true isMinifyEnabled = false diff --git a/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml b/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml index 657c7e775c0..de5a85690b1 100644 --- a/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml +++ b/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml @@ -5,7 +5,8 @@ + android:theme="@style/Theme.{{snake-case app.name}}" + android:usesCleartextTraffic="${usesCleartextTraffic}"> From 927ccc465d0e21b7c8cd5c373349c3957bb9fe77 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 25 Aug 2022 15:30:05 -0300 Subject: [PATCH 032/423] refactor(cli): improve security of android dev/build (#5043) --- examples/api/src-tauri/Cargo.lock | 2 +- tooling/cli/Cargo.lock | 216 +++++++++++++++++- tooling/cli/Cargo.toml | 2 +- tooling/cli/src/mobile/android.rs | 18 +- .../mobile/android/android_studio_script.rs | 2 +- tooling/cli/src/mobile/android/build.rs | 18 +- tooling/cli/src/mobile/android/dev.rs | 21 +- tooling/cli/src/mobile/android/open.rs | 2 +- tooling/cli/src/mobile/init.rs | 2 +- tooling/cli/src/mobile/ios.rs | 18 +- tooling/cli/src/mobile/ios/build.rs | 18 +- tooling/cli/src/mobile/ios/dev.rs | 21 +- tooling/cli/src/mobile/ios/open.rs | 2 +- tooling/cli/src/mobile/ios/xcode_script.rs | 2 +- tooling/cli/src/mobile/mod.rs | 69 ++++-- 15 files changed, 340 insertions(+), 73 deletions(-) diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index ce1d02a8c4c..cc1d00ca41c 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -4222,7 +4222,7 @@ dependencies = [ [[package]] name = "wry" version = "0.20.2" -source = "git+https://github.com/tauri-apps/wry?branch=dev#b1e8560c3f13f2674528f6ca440ba476ddbef7c2" +source = "git+https://github.com/tauri-apps/wry?branch=dev#83b6a8d06be8b1687296d2dfb4daeff3cc587714" dependencies = [ "block", "cocoa", diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 81d4a74255a..3f0b1a19046 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -85,6 +85,29 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "async-channel" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-task" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + [[package]] name = "attohttpc" version = "0.22.0" @@ -187,6 +210,20 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "blocking" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + [[package]] name = "bstr" version = "0.2.17" @@ -247,6 +284,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + [[package]] name = "cargo-mobile" version = "0.1.0" @@ -444,6 +487,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "concurrent-queue" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +dependencies = [ + "cache-padded", +] + [[package]] name = "console" version = "0.15.0" @@ -979,6 +1031,12 @@ dependencies = [ "libc", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "exr" version = "1.4.2" @@ -1131,17 +1189,109 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" + +[[package]] +name = "futures-executor" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765" + +[[package]] +name = "futures-task" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306" + +[[package]] +name = "futures-util" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] [[package]] name = "fxhash" @@ -1494,6 +1644,29 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "interprocess" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c58ec7fbda1df9a93f587b780659db3c99f61f4be27f9c82c9b37684ffd0366" +dependencies = [ + "blocking", + "cfg-if 1.0.0", + "futures", + "intmap", + "libc", + "once_cell", + "spinning", + "thiserror", + "winapi 0.3.9", +] + +[[package]] +name = "intmap" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" + [[package]] name = "io-lifetimes" version = "0.7.2" @@ -2184,6 +2357,12 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + [[package]] name = "parking_lot" version = "0.12.1" @@ -2409,6 +2588,18 @@ dependencies = [ "syn", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.25" @@ -3120,6 +3311,15 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spinning" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" +dependencies = [ + "lock_api", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3305,13 +3505,13 @@ dependencies = [ "colored 2.0.0", "ctrlc", "dialoguer", - "dirs-next", "env_logger 0.9.0", "glob", "handlebars 4.3.1", "heck 0.4.0", "ignore", "include_dir", + "interprocess", "json-patch", "lazy_static", "libc", @@ -3798,6 +3998,12 @@ dependencies = [ "libc", ] +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "walkdir" version = "2.3.2" diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index aec9d268ed9..2aa2976e9b0 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -30,7 +30,7 @@ path = "src/main.rs" # cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } textwrap = { version = "0.11.0", features = ["term_size"] } -dirs-next = "2.0" +interprocess = "1" thiserror = "1" clap = { version = "3.2", features = [ "derive" ] } anyhow = "1.0" diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 4e580932158..2220cc0fdcf 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -21,7 +21,7 @@ use clap::{Parser, Subcommand}; use super::{ ensure_init, get_config, init::{command as init_command, init_dot_cargo, Options as InitOptions}, - log_finished, Target as MobileTarget, + log_finished, read_options, CliOptions, Target as MobileTarget, }; use crate::{helpers::config::get as get_tauri_config, Result}; @@ -96,6 +96,7 @@ pub fn command(cli: Cli) -> Result<()> { } fn with_config( + cli_options: Option, f: impl FnOnce(&Config, &AndroidConfig, &AndroidMetadata) -> Result, ) -> Result { let (config, metadata) = { @@ -103,7 +104,10 @@ fn with_config( get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - get_config(tauri_config_) + get_config( + tauri_config_, + cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Android)), + ) }; f(&config, config.android(), metadata.android()) } @@ -152,3 +156,13 @@ fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError(env: &Env) -> Option<&'a Target<'a>> { device_prompt(env).map(|device| device.target()).ok() } + +fn open_and_wait(config: &AndroidConfig) -> ! { + log::info!("Opening Android Studio"); + if let Err(e) = os::open_file_with("Android Studio", config.project_dir()) { + log::error!("{}", e); + } + loop { + std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); + } +} diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index 8c37af04939..b4c2417c2f8 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -33,7 +33,7 @@ pub fn command(options: Options) -> Result<()> { }; let noise_level = NoiseLevel::Polite; - with_config(|root_conf, config, metadata| { + with_config(None, |root_conf, config, metadata| { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index fdc8be9ef1a..4872ba4e814 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -1,6 +1,6 @@ use super::{ - delete_codegen_vars, ensure_init, env, init_dot_cargo, log_finished, with_config, Error, - MobileTarget, + delete_codegen_vars, ensure_init, env, init_dot_cargo, log_finished, open_and_wait, with_config, + Error, MobileTarget, }; use crate::{ helpers::{config::get as get_tauri_config, flock}, @@ -48,6 +48,9 @@ pub struct Options { /// Build AABs. #[clap(long)] pub aab: bool, + /// Open Android Studio + #[clap(short, long)] + pub open: bool, } impl From for crate::build::Options { @@ -66,7 +69,7 @@ impl From for crate::build::Options { pub fn command(options: Options) -> Result<()> { delete_codegen_vars(); - with_config(|root_conf, config, _metadata| { + with_config(Some(Default::default()), |root_conf, config, _metadata| { set_var("WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", ""); set_var("WRY_RUSTWEBVIEW_CLASS_INIT", ""); @@ -76,7 +79,14 @@ pub fn command(options: Options) -> Result<()> { let env = env()?; init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; - run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e))) + let open = options.open; + run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; + + if open { + open_and_wait(config); + } + + Ok(()) }) .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 3c2e17aac1e..cba5f7c8f0d 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -1,6 +1,6 @@ use super::{ - delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, with_config, Error, - MobileTarget, + delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, + Error, MobileTarget, }; use crate::{ helpers::{config::get as get_tauri_config, flock}, @@ -14,7 +14,6 @@ use cargo_mobile::{ android::config::{Config as AndroidConfig, Metadata as AndroidMetadata}, config::Config, opts::{NoiseLevel, Profile}, - os, }; use std::env::set_var; @@ -65,7 +64,7 @@ impl From for crate::dev::Options { pub fn command(options: Options) -> Result<()> { delete_codegen_vars(); - with_config(|root_conf, config, metadata| { + with_config(Some(Default::default()), |root_conf, config, metadata| { set_var( "WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", WEBVIEW_CLIENT_CLASS_EXTENSION, @@ -120,13 +119,13 @@ fn run_dev( write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; if open { - open_dev(config) + open_and_wait(config) } else { match run(options, root_conf, config, metadata) { Ok(c) => Ok(Box::new(c) as Box), Err(Error::FailedToPromptForDevice(e)) => { log::error!("{}", e); - open_dev(config) + open_and_wait(config) } Err(e) => Err(e.into()), } @@ -135,16 +134,6 @@ fn run_dev( ) } -fn open_dev(config: &AndroidConfig) -> ! { - log::info!("Opening Android Studio"); - if let Err(e) = os::open_file_with("Android Studio", config.project_dir()) { - log::error!("{}", e); - } - loop { - std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); - } -} - fn run( options: MobileOptions, root_conf: &Config, diff --git a/tooling/cli/src/mobile/android/open.rs b/tooling/cli/src/mobile/android/open.rs index e58fba14a9b..c6ba84967d3 100644 --- a/tooling/cli/src/mobile/android/open.rs +++ b/tooling/cli/src/mobile/android/open.rs @@ -3,7 +3,7 @@ use crate::Result; use cargo_mobile::os; pub fn command() -> Result<()> { - with_config(|_, config, _metadata| { + with_config(Some(Default::default()), |_, config, _metadata| { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 4ad743dc48d..ea87100ff41 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -107,7 +107,7 @@ pub fn exec( let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - let (config, metadata) = get_config(tauri_config_); + let (config, metadata) = get_config(tauri_config_, Default::default()); let asset_dir = config.app().asset_dir(); if !asset_dir.is_dir() { diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 5a38dcf12c8..8b067c40bff 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -20,7 +20,7 @@ use clap::{Parser, Subcommand}; use super::{ ensure_init, env, get_config, init::{command as init_command, init_dot_cargo, Options as InitOptions}, - log_finished, Target as MobileTarget, + log_finished, read_options, CliOptions, Target as MobileTarget, }; use crate::{helpers::config::get as get_tauri_config, Result}; @@ -104,6 +104,7 @@ pub fn command(cli: Cli) -> Result<()> { } fn with_config( + cli_options: Option, f: impl FnOnce(&Config, &AppleConfig, &AppleMetadata) -> Result, ) -> Result { let (config, metadata) = { @@ -111,7 +112,10 @@ fn with_config( get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - get_config(tauri_config_) + get_config( + tauri_config_, + cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Ios)), + ) }; f(&config, config.apple(), metadata.apple()) } @@ -147,3 +151,13 @@ fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError(env: &Env) -> Option<&'a Target<'a>> { device_prompt(env).map(|device| device.target()).ok() } + +fn open_and_wait(config: &AppleConfig) -> ! { + log::info!("Opening Xcode"); + if let Err(e) = os::open_file_with("Xcode", config.project_dir()) { + log::error!("{}", e); + } + loop { + std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); + } +} diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index e6b7cb7b522..c00aaea0106 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -1,6 +1,6 @@ use super::{ - detect_target_ok, ensure_init, env, init_dot_cargo, log_finished, with_config, Error, - MobileTarget, + detect_target_ok, ensure_init, env, init_dot_cargo, log_finished, open_and_wait, with_config, + Error, MobileTarget, }; use crate::{ helpers::{config::get as get_tauri_config, flock}, @@ -44,6 +44,9 @@ pub struct Options { /// Build number to append to the app version. #[clap(long)] pub build_number: Option, + /// Open Xcode + #[clap(short, long)] + pub open: bool, } impl From for crate::build::Options { @@ -61,14 +64,21 @@ impl From for crate::build::Options { } pub fn command(options: Options) -> Result<()> { - with_config(|root_conf, config, _metadata| { + with_config(Some(Default::default()), |root_conf, config, _metadata| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; let env = env()?; init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; - run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e))) + let open = options.open; + run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; + + if open { + open_and_wait(config); + } + + Ok(()) }) .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 7bf4aa311ce..5daa148b797 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -1,4 +1,6 @@ -use super::{device_prompt, ensure_init, env, init_dot_cargo, with_config, Error, MobileTarget}; +use super::{ + device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, Error, MobileTarget, +}; use crate::{ helpers::{config::get as get_tauri_config, flock}, interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions}, @@ -11,7 +13,6 @@ use cargo_mobile::{ apple::config::Config as AppleConfig, config::Config, opts::{NoiseLevel, Profile}, - os, }; #[derive(Debug, Clone, Parser)] @@ -53,7 +54,7 @@ impl From for crate::dev::Options { } pub fn command(options: Options) -> Result<()> { - with_config(|root_conf, config, _metadata| { + with_config(Some(Default::default()), |root_conf, config, _metadata| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; run_dev(options, root_conf, config).map_err(|e| Error::DevFailed(format!("{:#}", e))) @@ -98,13 +99,13 @@ fn run_dev(options: Options, root_conf: &Config, config: &AppleConfig) -> Result }; write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; if open { - open_dev(config) + open_and_wait(config) } else { match run(options, root_conf, config) { Ok(c) => Ok(Box::new(c) as Box), Err(Error::FailedToPromptForDevice(e)) => { log::error!("{}", e); - open_dev(config) + open_and_wait(config) } Err(e) => Err(e.into()), } @@ -113,16 +114,6 @@ fn run_dev(options: Options, root_conf: &Config, config: &AppleConfig) -> Result ) } -fn open_dev(config: &AppleConfig) -> ! { - log::info!("Opening Xcode"); - if let Err(e) = os::open_file_with("Xcode", config.project_dir()) { - log::error!("{}", e); - } - loop { - std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); - } -} - fn run( options: MobileOptions, root_conf: &Config, diff --git a/tooling/cli/src/mobile/ios/open.rs b/tooling/cli/src/mobile/ios/open.rs index 331a073aef4..2173498792c 100644 --- a/tooling/cli/src/mobile/ios/open.rs +++ b/tooling/cli/src/mobile/ios/open.rs @@ -3,7 +3,7 @@ use crate::Result; use cargo_mobile::os; pub fn command() -> Result<()> { - with_config(|_, config, _metadata| { + with_config(Some(Default::default()), |_, config, _metadata| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index bf5df1d40c5..b2421a0ecec 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -46,7 +46,7 @@ pub fn command(options: Options) -> Result<()> { let macos = macos_from_platform(&options.platform); let noise_level = NoiseLevel::Polite; - with_config(|root_conf, config, metadata| { + with_config(None, |root_conf, config, metadata| { let env = env()?; init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; // The `PATH` env var Xcode gives us is missing any additions diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 7e996ee91e2..bf1558a7d98 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -17,9 +17,16 @@ use cargo_mobile::{ config::{app::Raw as RawAppConfig, metadata::Metadata, Config, Raw}, env::Error as EnvError, }; +use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; use serde::{Deserialize, Serialize}; use std::{ - collections::HashMap, env::set_var, ffi::OsString, fmt::Write, path::PathBuf, process::ExitStatus, + collections::HashMap, + env::set_var, + ffi::OsString, + fmt::Write, + io::{BufRead, BufReader, Write as _}, + path::PathBuf, + process::ExitStatus, }; #[cfg(not(windows))] @@ -63,7 +70,7 @@ impl DevProcess for DevChild { } } -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Copy, Clone)] pub enum Target { Android, #[cfg(target_os = "macos")] @@ -96,17 +103,15 @@ impl Target { } } -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct CliOptions { pub features: Option>, pub args: Vec, pub vars: HashMap, } -fn options_path(bundle_identifier: &str, target: Target) -> PathBuf { - let out_dir = dirs_next::cache_dir() - .or_else(dirs_next::home_dir) - .unwrap_or_else(std::env::temp_dir); +fn options_local_socket_name(bundle_identifier: &str, target: Target) -> PathBuf { + let out_dir = std::env::temp_dir(); let out_dir = out_dir.join(".tauri").join(bundle_identifier); let _ = std::fs::create_dir_all(&out_dir); out_dir @@ -120,6 +125,7 @@ fn env_vars() -> HashMap { let k = k.to_string_lossy(); if (k.starts_with("TAURI") && k != "TAURI_PRIVATE_KEY" && k != "TAURI_KEY_PASSWORD") || k.starts_with("WRY") + || k == "TMPDIR" { vars.insert(k.into_owned(), v); } @@ -139,23 +145,50 @@ pub fn write_options( target: Target, ) -> crate::Result<()> { options.vars.extend(env_vars()); - std::fs::write( - options_path(bundle_identifier, target), - &serde_json::to_string(&options)?, - )?; + let name = options_local_socket_name(bundle_identifier, target); + let _ = std::fs::remove_file(&name); + let value = serde_json::to_string(&options)?; + + std::thread::spawn(move || { + let listener = LocalSocketListener::bind(name).expect("failed to start local socket"); + for mut conn in listener.incoming().flatten() { + let _ = conn.write_all(value.as_bytes()); + let _ = conn.write_all(b"\n"); + } + }); + Ok(()) } -fn read_options(config: &TauriConfig, target: Target) -> crate::Result { - let data = std::fs::read_to_string(options_path(&config.tauri.bundle.identifier, target))?; - let options: CliOptions = serde_json::from_str(&data)?; +fn read_options(config: &TauriConfig, target: Target) -> CliOptions { + let name = options_local_socket_name(&config.tauri.bundle.identifier, target); + let conn = LocalSocketStream::connect(name).unwrap_or_else(|_| { + log::error!( + "failed to connect to local socket. You must keep the Tauri CLI alive with the `{cmd} dev` or `{cmd} build --open` commands.", + cmd = target.command_name() + ); + std::process::exit(1); + }); + conn + .set_nonblocking(true) + .expect("failed to set local socket stream to nonblocking"); + let mut conn = BufReader::new(conn); + let mut buffer = String::new(); + conn.read_line(&mut buffer).unwrap_or_else(|_| { + log::error!( + "failed to connect to local socket. You must keep the Tauri CLI alive with the `{cmd} dev` or `{cmd} build --open` commands.", + cmd = target.command_name() + ); + std::process::exit(1); + }); + let options: CliOptions = serde_json::from_str(&buffer).expect("invalid CLI options"); for (k, v) in &options.vars { set_var(k, v); } - Ok(options) + options } -fn get_config(config: &TauriConfig) -> (Config, Metadata) { +fn get_config(config: &TauriConfig, cli_options: CliOptions) -> (Config, Metadata) { let mut s = config.tauri.bundle.identifier.rsplit('.'); let app_name = s.next().unwrap_or("app").to_string(); let mut domain = String::new(); @@ -191,8 +224,8 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) { }; #[cfg(target_os = "macos")] - let ios_options = read_options(config, Target::Ios).unwrap_or_default(); - let android_options = read_options(config, Target::Android).unwrap_or_default(); + let ios_options = cli_options.clone(); + let android_options = cli_options; let raw = Raw { app: RawAppConfig { From badad2b9a17a5c9d751559b330e89c3127014083 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 25 Aug 2022 16:06:00 -0300 Subject: [PATCH 033/423] feat(cli): implement verbosity on mobile commands (#5044) --- tooling/cli/src/lib.rs | 4 ++-- tooling/cli/src/mobile/android.rs | 7 ++++--- .../src/mobile/android/android_studio_script.rs | 2 +- tooling/cli/src/mobile/android/build.rs | 13 +++++++++---- tooling/cli/src/mobile/android/dev.rs | 10 ++++++---- tooling/cli/src/mobile/ios.rs | 7 ++++--- tooling/cli/src/mobile/ios/build.rs | 13 +++++++++---- tooling/cli/src/mobile/ios/dev.rs | 16 +++++++++++----- tooling/cli/src/mobile/ios/xcode_script.rs | 2 +- tooling/cli/src/mobile/mod.rs | 10 ++++++++++ 10 files changed, 57 insertions(+), 27 deletions(-) diff --git a/tooling/cli/src/lib.rs b/tooling/cli/src/lib.rs index 30748532458..c64e6100862 100644 --- a/tooling/cli/src/lib.rs +++ b/tooling/cli/src/lib.rs @@ -192,9 +192,9 @@ where Commands::Init(options) => init::command(options)?, Commands::Plugin(cli) => plugin::command(cli)?, Commands::Signer(cli) => signer::command(cli)?, - Commands::Android(cli) => mobile::android::command(cli)?, + Commands::Android(c) => mobile::android::command(c, cli.verbose)?, #[cfg(target_os = "macos")] - Commands::Ios(cli) => mobile::ios::command(cli)?, + Commands::Ios(c) => mobile::ios::command(c, cli.verbose)?, } Ok(()) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 2220cc0fdcf..0c10d8b9dfd 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -83,12 +83,13 @@ enum Commands { AndroidStudioScript(android_studio_script::Options), } -pub fn command(cli: Cli) -> Result<()> { +pub fn command(cli: Cli, verbosity: usize) -> Result<()> { + let noise_level = super::verbosity_to_noise_level(verbosity); match cli.command { Commands::Init(options) => init_command(options, MobileTarget::Android)?, Commands::Open => open::command()?, - Commands::Dev(options) => dev::command(options)?, - Commands::Build(options) => build::command(options)?, + Commands::Dev(options) => dev::command(options, noise_level)?, + Commands::Build(options) => build::command(options, noise_level)?, Commands::AndroidStudioScript(options) => android_studio_script::command(options)?, } diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index b4c2417c2f8..018ee155961 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -31,7 +31,7 @@ pub fn command(options: Options) -> Result<()> { } else { Profile::Debug }; - let noise_level = NoiseLevel::Polite; + let noise_level = NoiseLevel::FranklyQuitePedantic; with_config(None, |root_conf, config, metadata| { ensure_init(config.project_dir(), MobileTarget::Android) diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index 4872ba4e814..4388b98caad 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -67,7 +67,7 @@ impl From for crate::build::Options { } } -pub fn command(options: Options) -> Result<()> { +pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { delete_codegen_vars(); with_config(Some(Default::default()), |root_conf, config, _metadata| { set_var("WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", ""); @@ -80,7 +80,8 @@ pub fn command(options: Options) -> Result<()> { init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; let open = options.open; - run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; + run_build(options, config, env, noise_level) + .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; if open { open_and_wait(config); @@ -91,13 +92,17 @@ pub fn command(options: Options) -> Result<()> { .map_err(Into::into) } -fn run_build(mut options: Options, config: &AndroidConfig, env: Env) -> Result<()> { +fn run_build( + mut options: Options, + config: &AndroidConfig, + env: Env, + noise_level: NoiseLevel, +) -> Result<()> { let profile = if options.debug { Profile::Debug } else { Profile::Release }; - let noise_level = NoiseLevel::Polite; if !(options.apk || options.aab) { // if the user didn't specify the format to build, we'll do both diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index cba5f7c8f0d..6fa94000c0e 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -62,7 +62,7 @@ impl From for crate::dev::Options { } } -pub fn command(options: Options) -> Result<()> { +pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { delete_codegen_vars(); with_config(Some(Default::default()), |root_conf, config, metadata| { set_var( @@ -72,7 +72,8 @@ pub fn command(options: Options) -> Result<()> { set_var("WRY_RUSTWEBVIEW_CLASS_INIT", WEBVIEW_CLASS_INIT); ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config, metadata).map_err(|e| Error::DevFailed(format!("{:#}", e))) + run_dev(options, root_conf, config, metadata, noise_level) + .map_err(|e| Error::DevFailed(format!("{:#}", e))) }) .map_err(Into::into) } @@ -82,6 +83,7 @@ fn run_dev( root_conf: &Config, config: &AndroidConfig, metadata: &AndroidMetadata, + noise_level: NoiseLevel, ) -> Result<()> { let mut dev_options = options.clone().into(); let mut interface = crate::dev::setup(&mut dev_options)?; @@ -121,7 +123,7 @@ fn run_dev( if open { open_and_wait(config) } else { - match run(options, root_conf, config, metadata) { + match run(options, root_conf, config, metadata, noise_level) { Ok(c) => Ok(Box::new(c) as Box), Err(Error::FailedToPromptForDevice(e)) => { log::error!("{}", e); @@ -139,13 +141,13 @@ fn run( root_conf: &Config, config: &AndroidConfig, metadata: &AndroidMetadata, + noise_level: NoiseLevel, ) -> Result { let profile = if options.debug { Profile::Debug } else { Profile::Release }; - let noise_level = NoiseLevel::Polite; let build_app_bundle = metadata.asset_packs().is_some(); diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 8b067c40bff..f98b3ea3834 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -91,12 +91,13 @@ enum Commands { XcodeScript(xcode_script::Options), } -pub fn command(cli: Cli) -> Result<()> { +pub fn command(cli: Cli, verbosity: usize) -> Result<()> { + let noise_level = super::verbosity_to_noise_level(verbosity); match cli.command { Commands::Init(options) => init_command(options, MobileTarget::Ios)?, Commands::Open => open::command()?, - Commands::Dev(options) => dev::command(options)?, - Commands::Build(options) => build::command(options)?, + Commands::Dev(options) => dev::command(options, noise_level)?, + Commands::Build(options) => build::command(options, noise_level)?, Commands::XcodeScript(options) => xcode_script::command(options)?, } diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index c00aaea0106..55671443066 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -63,7 +63,7 @@ impl From for crate::build::Options { } } -pub fn command(options: Options) -> Result<()> { +pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { with_config(Some(Default::default()), |root_conf, config, _metadata| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; @@ -72,7 +72,8 @@ pub fn command(options: Options) -> Result<()> { init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; let open = options.open; - run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; + run_build(options, config, env, noise_level) + .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; if open { open_and_wait(config); @@ -83,13 +84,17 @@ pub fn command(options: Options) -> Result<()> { .map_err(Into::into) } -fn run_build(mut options: Options, config: &AppleConfig, env: Env) -> Result<()> { +fn run_build( + mut options: Options, + config: &AppleConfig, + env: Env, + noise_level: NoiseLevel, +) -> Result<()> { let profile = if options.debug { Profile::Debug } else { Profile::Release }; - let noise_level = NoiseLevel::Polite; let bundle_identifier = { let tauri_config = get_tauri_config(None)?; diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 5daa148b797..4cbb8b30a3b 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -53,16 +53,22 @@ impl From for crate::dev::Options { } } -pub fn command(options: Options) -> Result<()> { +pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { with_config(Some(Default::default()), |root_conf, config, _metadata| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config).map_err(|e| Error::DevFailed(format!("{:#}", e))) + run_dev(options, root_conf, config, noise_level) + .map_err(|e| Error::DevFailed(format!("{:#}", e))) }) .map_err(Into::into) } -fn run_dev(options: Options, root_conf: &Config, config: &AppleConfig) -> Result<()> { +fn run_dev( + options: Options, + root_conf: &Config, + config: &AppleConfig, + noise_level: NoiseLevel, +) -> Result<()> { let mut dev_options = options.clone().into(); let mut interface = crate::dev::setup(&mut dev_options)?; @@ -101,7 +107,7 @@ fn run_dev(options: Options, root_conf: &Config, config: &AppleConfig) -> Result if open { open_and_wait(config) } else { - match run(options, root_conf, config) { + match run(options, root_conf, config, noise_level) { Ok(c) => Ok(Box::new(c) as Box), Err(Error::FailedToPromptForDevice(e)) => { log::error!("{}", e); @@ -118,13 +124,13 @@ fn run( options: MobileOptions, root_conf: &Config, config: &AppleConfig, + noise_level: NoiseLevel, ) -> Result { let profile = if options.debug { Profile::Debug } else { Profile::Release }; - let noise_level = NoiseLevel::Polite; let env = env()?; init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index b2421a0ecec..90576b152d5 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -44,7 +44,7 @@ pub fn command(options: Options) -> Result<()> { let profile = profile_from_configuration(&options.configuration); let macos = macos_from_platform(&options.platform); - let noise_level = NoiseLevel::Polite; + let noise_level = NoiseLevel::FranklyQuitePedantic; with_config(None, |root_conf, config, metadata| { let env = env()?; diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index bf1558a7d98..8ff69d19d77 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -16,6 +16,7 @@ use cargo_mobile::{ bossy, config::{app::Raw as RawAppConfig, metadata::Metadata, Config, Raw}, env::Error as EnvError, + opts::NoiseLevel, }; use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; use serde::{Deserialize, Serialize}; @@ -346,3 +347,12 @@ fn log_finished(outputs: Vec, kind: &str) { log::info!(action = "Finished"; "{} {}{} at:\n{}", outputs.len(), kind, if outputs.len() == 1 { "" } else { "s" }, printable_paths); } } + +fn verbosity_to_noise_level(verbosity: usize) -> NoiseLevel { + match verbosity { + 0 => NoiseLevel::Polite, + 1 => NoiseLevel::LoudAndProud, + 2.. => NoiseLevel::FranklyQuitePedantic, + _ => panic!(), + } +} From 80a301ea63585e3123b5f79eae697ef1fe3bab06 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 25 Aug 2022 16:43:29 -0300 Subject: [PATCH 034/423] feat(cli): add mobile support to the app template (#5046) --- .../mobile/android/android_studio_script.rs | 2 +- tooling/cli/src/mobile/ios/xcode_script.rs | 2 +- .../app/src-tauri/Cargo.crate-manifest | 13 +++++- .../templates/app/src-tauri/src/desktop.rs | 8 ++++ .../cli/templates/app/src-tauri/src/lib.rs | 41 +++++++++++++++++++ .../cli/templates/app/src-tauri/src/main.rs | 11 ++--- .../cli/templates/app/src-tauri/src/mobile.rs | 23 +++++++++++ .../android/app/src/main/TauriActivity.kt | 3 +- 8 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 tooling/cli/templates/app/src-tauri/src/desktop.rs create mode 100644 tooling/cli/templates/app/src-tauri/src/lib.rs create mode 100644 tooling/cli/templates/app/src-tauri/src/mobile.rs diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index 018ee155961..f2397016b03 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -31,7 +31,7 @@ pub fn command(options: Options) -> Result<()> { } else { Profile::Debug }; - let noise_level = NoiseLevel::FranklyQuitePedantic; + let noise_level = NoiseLevel::LoudAndProud; with_config(None, |root_conf, config, metadata| { ensure_init(config.project_dir(), MobileTarget::Android) diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 90576b152d5..f04ec025a6c 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -44,7 +44,7 @@ pub fn command(options: Options) -> Result<()> { let profile = profile_from_configuration(&options.configuration); let macos = macos_from_platform(&options.platform); - let noise_level = NoiseLevel::FranklyQuitePedantic; + let noise_level = NoiseLevel::LoudAndProud; with_config(None, |root_conf, config, metadata| { let env = env()?; diff --git a/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest b/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest index 1249d3cc6b3..08e75dfa331 100755 --- a/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest +++ b/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest @@ -5,12 +5,14 @@ description = "A Tauri App" authors = ["you"] license = "" repository = "" -default-run = "app" edition = "2021" rust-version = "1.57" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["staticlib", "cdylib", "rlib"] + [build-dependencies] tauri-build = {{{ tauri_build_dep }}} @@ -19,6 +21,15 @@ serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } tauri = {{{ tauri_dep }}} +[target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] +log = "0.4" + +[target.'cfg(target_os = "android")'.dependencies] +android_logger = "0.9.0" + +[target.'cfg(target_os = "ios")'.dependencies] +env_logger = "0.9.0" + [features] # by default Tauri runs in production mode # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL diff --git a/tooling/cli/templates/app/src-tauri/src/desktop.rs b/tooling/cli/templates/app/src-tauri/src/desktop.rs new file mode 100644 index 00000000000..ef900d6533f --- /dev/null +++ b/tooling/cli/templates/app/src-tauri/src/desktop.rs @@ -0,0 +1,8 @@ +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +fn main() { + app::AppBuilder::new() +} diff --git a/tooling/cli/templates/app/src-tauri/src/lib.rs b/tooling/cli/templates/app/src-tauri/src/lib.rs new file mode 100644 index 00000000000..d8c1e87726f --- /dev/null +++ b/tooling/cli/templates/app/src-tauri/src/lib.rs @@ -0,0 +1,41 @@ +use tauri::App; + +#[cfg(mobile)] +mod mobile; +#[cfg(mobile)] +pub use mobile::*; + +pub type SetupHook = Box Result<(), Box> + Send>; + +#[derive(Default)] +pub struct AppBuilder { + setup: Option, +} + +impl AppBuilder { + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn setup(mut self, setup: F) -> Self + where + F: FnOnce(&mut App) -> Result<(), Box> + Send + 'static, + { + self.setup.replace(Box::new(setup)); + self + } + + pub fn run(self) { + let setup = self.setup; + tauri::Builder::default() + .setup(move |app| { + if let Some(setup) = setup { + (setup)(app)?; + } + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); + } +} diff --git a/tooling/cli/templates/app/src-tauri/src/main.rs b/tooling/cli/templates/app/src-tauri/src/main.rs index e994ea4d199..1ade16f98e3 100644 --- a/tooling/cli/templates/app/src-tauri/src/main.rs +++ b/tooling/cli/templates/app/src-tauri/src/main.rs @@ -1,10 +1,7 @@ -#![cfg_attr( - all(not(debug_assertions), target_os = "windows"), - windows_subsystem = "windows" -)] +#[cfg(desktop)] +mod desktop; fn main() { - tauri::Builder::default() - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + #[cfg(desktop)] + desktop::main(); } diff --git a/tooling/cli/templates/app/src-tauri/src/mobile.rs b/tooling/cli/templates/app/src-tauri/src/mobile.rs new file mode 100644 index 00000000000..3e22469b256 --- /dev/null +++ b/tooling/cli/templates/app/src-tauri/src/mobile.rs @@ -0,0 +1,23 @@ +#[cfg(target_os = "android")] +fn init_logging(app_name: &str) { + android_logger::init_once( + android_logger::Config::default() + .with_min_level(log::Level::Trace) + .with_tag(app_name), + ); +} + +#[cfg(not(target_os = "android"))] +fn init_logging(_app_name: &str) { + env_logger::init(); +} + +#[tauri::mobile_entry_point] +fn main() { + super::AppBuilder::new() + .setup(|app| { + init_logging(&app.package_info().name); + Ok(()) + }) + .run() +} diff --git a/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt b/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt index 2d1ffa69a32..b630718275e 100644 --- a/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt +++ b/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt @@ -1,6 +1,5 @@ -package {{app-domain-reversed}}.{{app-name-snake-case}} +package {{reverse-domain app.domain}}.{{snake-case app.name}} -import android.os.Bundle import androidx.appcompat.app.AppCompatActivity abstract class TauriActivity : AppCompatActivity() From 69aaff55070337836037f16e2620eec4cdacd3f3 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Fri, 26 Aug 2022 09:24:23 -0300 Subject: [PATCH 035/423] feat(cli): persist verbosity for the IDE scripts (#5047) --- tooling/cli/src/mobile/android.rs | 17 +++++----- .../mobile/android/android_studio_script.rs | 14 +++++--- tooling/cli/src/mobile/android/build.rs | 34 +++++++++++-------- tooling/cli/src/mobile/android/dev.rs | 26 ++++++++------ tooling/cli/src/mobile/android/open.rs | 13 ++++--- tooling/cli/src/mobile/init.rs | 2 +- tooling/cli/src/mobile/ios.rs | 16 ++++----- tooling/cli/src/mobile/ios/build.rs | 30 +++++++++------- tooling/cli/src/mobile/ios/dev.rs | 16 +++++---- tooling/cli/src/mobile/ios/open.rs | 13 ++++--- tooling/cli/src/mobile/ios/xcode_script.rs | 11 ++---- tooling/cli/src/mobile/mod.rs | 14 ++------ 12 files changed, 111 insertions(+), 95 deletions(-) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 0c10d8b9dfd..73aea69d4f4 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -13,6 +13,7 @@ use cargo_mobile::{ config::Config, device::PromptError, env::Error as EnvError, + opts::NoiseLevel, os, util::prompt, }; @@ -84,7 +85,7 @@ enum Commands { } pub fn command(cli: Cli, verbosity: usize) -> Result<()> { - let noise_level = super::verbosity_to_noise_level(verbosity); + let noise_level = NoiseLevel::from_occurrences(verbosity as u64); match cli.command { Commands::Init(options) => init_command(options, MobileTarget::Android)?, Commands::Open => open::command()?, @@ -98,19 +99,19 @@ pub fn command(cli: Cli, verbosity: usize) -> Result<()> { fn with_config( cli_options: Option, - f: impl FnOnce(&Config, &AndroidConfig, &AndroidMetadata) -> Result, + f: impl FnOnce(&Config, &AndroidConfig, &AndroidMetadata, CliOptions) -> Result, ) -> Result { - let (config, metadata) = { + let (config, metadata, cli_options) = { let tauri_config = get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - get_config( - tauri_config_, - cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Android)), - ) + let cli_options = + cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Android)); + let (config, metadata) = get_config(tauri_config_, &cli_options); + (config, metadata, cli_options) }; - f(&config, config.android(), metadata.android()) + f(&config, config.android(), metadata.android(), cli_options) } fn env() -> Result { diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index f2397016b03..eb37490e604 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -4,7 +4,7 @@ use clap::Parser; use cargo_mobile::{ android::target::Target, - opts::{NoiseLevel, Profile}, + opts::Profile, target::{call_for_targets_with_fallback, TargetTrait}, }; @@ -31,9 +31,8 @@ pub fn command(options: Options) -> Result<()> { } else { Profile::Debug }; - let noise_level = NoiseLevel::LoudAndProud; - with_config(None, |root_conf, config, metadata| { + with_config(None, |root_conf, config, metadata, cli_options| { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; @@ -46,7 +45,14 @@ pub fn command(options: Options) -> Result<()> { &env, |target: &Target| { target - .build(config, metadata, &env, noise_level, true, profile) + .build( + config, + metadata, + &env, + cli_options.noise_level, + true, + profile, + ) .map_err(Error::AndroidStudioScriptFailed) }, ) diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index 4388b98caad..f1ed0939b4f 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -69,26 +69,29 @@ impl From for crate::build::Options { pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { delete_codegen_vars(); - with_config(Some(Default::default()), |root_conf, config, _metadata| { - set_var("WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", ""); - set_var("WRY_RUSTWEBVIEW_CLASS_INIT", ""); + with_config( + Some(Default::default()), + |root_conf, config, _metadata, _cli_options| { + set_var("WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", ""); + set_var("WRY_RUSTWEBVIEW_CLASS_INIT", ""); - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - let env = env()?; - init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + let env = env()?; + init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; - let open = options.open; - run_build(options, config, env, noise_level) - .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; + let open = options.open; + run_build(options, config, env, noise_level) + .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; - if open { - open_and_wait(config); - } + if open { + open_and_wait(config); + } - Ok(()) - }) + Ok(()) + }, + ) .map_err(Into::into) } @@ -131,6 +134,7 @@ fn run_build( let cli_options = CliOptions { features: build_options.features.clone(), args: build_options.args.clone(), + noise_level, vars: Default::default(), }; write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 6fa94000c0e..a91f9383308 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -64,17 +64,20 @@ impl From for crate::dev::Options { pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { delete_codegen_vars(); - with_config(Some(Default::default()), |root_conf, config, metadata| { - set_var( - "WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", - WEBVIEW_CLIENT_CLASS_EXTENSION, - ); - set_var("WRY_RUSTWEBVIEW_CLASS_INIT", WEBVIEW_CLASS_INIT); - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config, metadata, noise_level) - .map_err(|e| Error::DevFailed(format!("{:#}", e))) - }) + with_config( + Some(Default::default()), + |root_conf, config, metadata, _cli_options| { + set_var( + "WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", + WEBVIEW_CLIENT_CLASS_EXTENSION, + ); + set_var("WRY_RUSTWEBVIEW_CLASS_INIT", WEBVIEW_CLASS_INIT); + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + run_dev(options, root_conf, config, metadata, noise_level) + .map_err(|e| Error::DevFailed(format!("{:#}", e))) + }, + ) .map_err(Into::into) } @@ -116,6 +119,7 @@ fn run_dev( let cli_options = CliOptions { features: options.features.clone(), args: options.args.clone(), + noise_level, vars: Default::default(), }; write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; diff --git a/tooling/cli/src/mobile/android/open.rs b/tooling/cli/src/mobile/android/open.rs index c6ba84967d3..97db6129a03 100644 --- a/tooling/cli/src/mobile/android/open.rs +++ b/tooling/cli/src/mobile/android/open.rs @@ -3,10 +3,13 @@ use crate::Result; use cargo_mobile::os; pub fn command() -> Result<()> { - with_config(Some(Default::default()), |_, config, _metadata| { - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) - }) + with_config( + Some(Default::default()), + |_root_conf, config, _metadata, _cli_options| { + ensure_init(config.project_dir(), MobileTarget::Android) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) + }, + ) .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index ea87100ff41..7bcd965a3ec 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -107,7 +107,7 @@ pub fn exec( let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - let (config, metadata) = get_config(tauri_config_, Default::default()); + let (config, metadata) = get_config(tauri_config_, &Default::default()); let asset_dir = config.app().asset_dir(); if !asset_dir.is_dir() { diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index f98b3ea3834..5fbcfe3dd1c 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -12,6 +12,7 @@ use cargo_mobile::{ config::Config, device::PromptError, env::{Env, Error as EnvError}, + opts::NoiseLevel, os, util, util::prompt, }; @@ -92,7 +93,7 @@ enum Commands { } pub fn command(cli: Cli, verbosity: usize) -> Result<()> { - let noise_level = super::verbosity_to_noise_level(verbosity); + let noise_level = NoiseLevel::from_occurrences(verbosity as u64); match cli.command { Commands::Init(options) => init_command(options, MobileTarget::Ios)?, Commands::Open => open::command()?, @@ -106,19 +107,18 @@ pub fn command(cli: Cli, verbosity: usize) -> Result<()> { fn with_config( cli_options: Option, - f: impl FnOnce(&Config, &AppleConfig, &AppleMetadata) -> Result, + f: impl FnOnce(&Config, &AppleConfig, &AppleMetadata, CliOptions) -> Result, ) -> Result { - let (config, metadata) = { + let (config, metadata, cli_options) = { let tauri_config = get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - get_config( - tauri_config_, - cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Ios)), - ) + let cli_options = cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Ios)); + let (config, metadata) = get_config(tauri_config_, &cli_options); + (config, metadata, cli_options) }; - f(&config, config.apple(), metadata.apple()) + f(&config, config.apple(), metadata.apple(), cli_options) } fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index 55671443066..99f5f6d9a19 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -64,23 +64,26 @@ impl From for crate::build::Options { } pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { - with_config(Some(Default::default()), |root_conf, config, _metadata| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + with_config( + Some(Default::default()), + |root_conf, config, _metadata, _cli_options| { + ensure_init(config.project_dir(), MobileTarget::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - let env = env()?; - init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + let env = env()?; + init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; - let open = options.open; - run_build(options, config, env, noise_level) - .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; + let open = options.open; + run_build(options, config, env, noise_level) + .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; - if open { - open_and_wait(config); - } + if open { + open_and_wait(config); + } - Ok(()) - }) + Ok(()) + }, + ) .map_err(Into::into) } @@ -117,6 +120,7 @@ fn run_build( let cli_options = CliOptions { features: build_options.features.clone(), args: build_options.args.clone(), + noise_level, vars: Default::default(), }; write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 4cbb8b30a3b..5e29575c686 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -54,12 +54,15 @@ impl From for crate::dev::Options { } pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { - with_config(Some(Default::default()), |root_conf, config, _metadata| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config, noise_level) - .map_err(|e| Error::DevFailed(format!("{:#}", e))) - }) + with_config( + Some(Default::default()), + |root_conf, config, _metadata, _cli_options| { + ensure_init(config.project_dir(), MobileTarget::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + run_dev(options, root_conf, config, noise_level) + .map_err(|e| Error::DevFailed(format!("{:#}", e))) + }, + ) .map_err(Into::into) } @@ -101,6 +104,7 @@ fn run_dev( let cli_options = CliOptions { features: options.features.clone(), args: options.args.clone(), + noise_level, vars: Default::default(), }; write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; diff --git a/tooling/cli/src/mobile/ios/open.rs b/tooling/cli/src/mobile/ios/open.rs index 2173498792c..9d28d232280 100644 --- a/tooling/cli/src/mobile/ios/open.rs +++ b/tooling/cli/src/mobile/ios/open.rs @@ -3,10 +3,13 @@ use crate::Result; use cargo_mobile::os; pub fn command() -> Result<()> { - with_config(Some(Default::default()), |_, config, _metadata| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) - }) + with_config( + Some(Default::default()), + |_root_conf, config, _metadata, _cli_options| { + ensure_init(config.project_dir(), MobileTarget::Ios) + .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) + }, + ) .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index f04ec025a6c..4ff2e5bffe5 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -2,11 +2,7 @@ use super::{env, init_dot_cargo, with_config, Error}; use crate::Result; use clap::Parser; -use cargo_mobile::{ - apple::target::Target, - opts::{NoiseLevel, Profile}, - util, -}; +use cargo_mobile::{apple::target::Target, opts::Profile, util}; use std::{collections::HashMap, ffi::OsStr, path::PathBuf}; @@ -44,9 +40,8 @@ pub fn command(options: Options) -> Result<()> { let profile = profile_from_configuration(&options.configuration); let macos = macos_from_platform(&options.platform); - let noise_level = NoiseLevel::LoudAndProud; - with_config(None, |root_conf, config, metadata| { + with_config(None, |root_conf, config, metadata, cli_options| { let env = env()?; init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; // The `PATH` env var Xcode gives us is missing any additions @@ -127,7 +122,7 @@ pub fn command(options: Options) -> Result<()> { .compile_lib( config, metadata, - noise_level, + cli_options.noise_level, true, profile, &env, diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 8ff69d19d77..9ac0ce9a6e2 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -108,6 +108,7 @@ impl Target { pub struct CliOptions { pub features: Option>, pub args: Vec, + pub noise_level: NoiseLevel, pub vars: HashMap, } @@ -189,7 +190,7 @@ fn read_options(config: &TauriConfig, target: Target) -> CliOptions { options } -fn get_config(config: &TauriConfig, cli_options: CliOptions) -> (Config, Metadata) { +fn get_config(config: &TauriConfig, cli_options: &CliOptions) -> (Config, Metadata) { let mut s = config.tauri.bundle.identifier.rsplit('.'); let app_name = s.next().unwrap_or("app").to_string(); let mut domain = String::new(); @@ -226,7 +227,7 @@ fn get_config(config: &TauriConfig, cli_options: CliOptions) -> (Config, Metadat #[cfg(target_os = "macos")] let ios_options = cli_options.clone(); - let android_options = cli_options; + let android_options = cli_options.clone(); let raw = Raw { app: RawAppConfig { @@ -347,12 +348,3 @@ fn log_finished(outputs: Vec, kind: &str) { log::info!(action = "Finished"; "{} {}{} at:\n{}", outputs.len(), kind, if outputs.len() == 1 { "" } else { "s" }, printable_paths); } } - -fn verbosity_to_noise_level(verbosity: usize) -> NoiseLevel { - match verbosity { - 0 => NoiseLevel::Polite, - 1 => NoiseLevel::LoudAndProud, - 2.. => NoiseLevel::FranklyQuitePedantic, - _ => panic!(), - } -} From 53a3398beb7d947af3f082f5e1682b9cc83c00d3 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Fri, 26 Aug 2022 16:08:44 -0300 Subject: [PATCH 036/423] feat(cli): improve DX on android dev and ios dev commands (#5059) --- tooling/cli/Cargo.lock | 2 +- tooling/cli/src/dev.rs | 26 ++++++- tooling/cli/src/interface/mod.rs | 6 +- tooling/cli/src/interface/rust/desktop.rs | 38 +++++---- tooling/cli/src/mobile/android.rs | 4 +- tooling/cli/src/mobile/android/build.rs | 10 +-- tooling/cli/src/mobile/android/dev.rs | 34 ++++++--- tooling/cli/src/mobile/android/open.rs | 6 +- tooling/cli/src/mobile/ios.rs | 4 +- tooling/cli/src/mobile/ios/build.rs | 14 ++-- tooling/cli/src/mobile/ios/dev.rs | 31 +++++--- tooling/cli/src/mobile/ios/open.rs | 5 +- tooling/cli/src/mobile/mod.rs | 93 +++++++++-------------- 13 files changed, 153 insertions(+), 120 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 3f0b1a19046..73d03d2acee 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -293,7 +293,7 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#5b866af17df4e2310e084efd3550ec31f62ff984" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#228f0eb83ccbc7bfeb0103d2f68b6664691a289d" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index 53502e2d63f..f989f150157 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -8,7 +8,7 @@ use crate::{ command_env, config::{get as get_config, AppUrl, BeforeDevCommand, WindowUrl}, }, - interface::{AppInterface, ExitReason, Interface}, + interface::{AppInterface, DevProcess, ExitReason, Interface}, CommandExt, Result, }; use clap::Parser; @@ -78,7 +78,7 @@ fn command_internal(mut options: Options) -> Result<()> { let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; interface.dev(options.into(), move |status, reason| { - on_dev_exit(status, reason, exit_on_panic, no_watch) + on_app_exit(status, reason, exit_on_panic, no_watch) }) } @@ -275,7 +275,27 @@ pub fn setup(options: &mut Options) -> Result { Ok(interface) } -fn on_dev_exit(status: ExitStatus, reason: ExitReason, exit_on_panic: bool, no_watch: bool) { +pub fn wait_dev_process< + C: DevProcess + Send + 'static, + F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static, +>( + child: C, + on_exit: F, +) { + std::thread::spawn(move || { + let status = child.wait().expect("failed to wait on app"); + on_exit( + status, + if child.manually_killed_process() { + ExitReason::TriggeredKill + } else { + ExitReason::NormalExit + }, + ); + }); +} + +pub fn on_app_exit(status: ExitStatus, reason: ExitReason, exit_on_panic: bool, no_watch: bool) { if no_watch || (!matches!(reason, ExitReason::TriggeredKill) && (exit_on_panic || matches!(reason, ExitReason::NormalExit))) diff --git a/tooling/cli/src/interface/mod.rs b/tooling/cli/src/interface/mod.rs index 178eead72ae..c35e48a5d40 100644 --- a/tooling/cli/src/interface/mod.rs +++ b/tooling/cli/src/interface/mod.rs @@ -15,8 +15,10 @@ use tauri_bundler::bundle::{PackageType, Settings, SettingsBuilder}; pub use rust::{manifest, MobileOptions, Options, Rust as AppInterface}; pub trait DevProcess { - fn kill(&mut self) -> std::io::Result<()>; - fn try_wait(&mut self) -> std::io::Result>; + fn kill(&self) -> std::io::Result<()>; + fn try_wait(&self) -> std::io::Result>; + fn wait(&self) -> std::io::Result; + fn manually_killed_process(&self) -> bool; } pub trait AppSettings { diff --git a/tooling/cli/src/interface/rust/desktop.rs b/tooling/cli/src/interface/rust/desktop.rs index 66a785a8747..8de0a90b111 100644 --- a/tooling/cli/src/interface/rust/desktop.rs +++ b/tooling/cli/src/interface/rust/desktop.rs @@ -23,7 +23,7 @@ pub struct DevChild { } impl DevProcess for DevChild { - fn kill(&mut self) -> std::io::Result<()> { + fn kill(&self) -> std::io::Result<()> { if let Some(child) = &*self.app_child.lock().unwrap() { child.kill()?; } else if let Some(child) = &self.build_child { @@ -33,7 +33,7 @@ impl DevProcess for DevChild { Ok(()) } - fn try_wait(&mut self) -> std::io::Result> { + fn try_wait(&self) -> std::io::Result> { if let Some(child) = &*self.app_child.lock().unwrap() { child.try_wait() } else if let Some(child) = &self.build_child { @@ -42,6 +42,20 @@ impl DevProcess for DevChild { unreachable!() } } + + fn wait(&self) -> std::io::Result { + if let Some(child) = &*self.app_child.lock().unwrap() { + child.wait() + } else if let Some(child) = &self.build_child { + child.wait() + } else { + unreachable!() + } + } + + fn manually_killed_process(&self) -> bool { + self.manually_killed_app.load(Ordering::Relaxed) + } } pub fn run_dev( @@ -73,18 +87,14 @@ pub fn run_dev( app.stderr(os_pipe::dup_stderr().unwrap()); app.args(run_args); let app_child = Arc::new(SharedChild::spawn(&mut app).unwrap()); - let app_child_t = app_child.clone(); - std::thread::spawn(move || { - let status = app_child_t.wait().expect("failed to wait on app"); - on_exit( - status, - if manually_killed_app_.load(Ordering::Relaxed) { - ExitReason::TriggeredKill - } else { - ExitReason::NormalExit - }, - ); - }); + crate::dev::wait_dev_process( + DevChild { + manually_killed_app: manually_killed_app_, + build_child: None, + app_child: Arc::new(Mutex::new(Some(app_child.clone()))), + }, + on_exit, + ); app_child_.lock().unwrap().replace(app_child); } else { diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 73aea69d4f4..9a8563ea48b 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -159,9 +159,9 @@ fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { device_prompt(env).map(|device| device.target()).ok() } -fn open_and_wait(config: &AndroidConfig) -> ! { +fn open_and_wait(config: &AndroidConfig, env: &Env) -> ! { log::info!("Opening Android Studio"); - if let Err(e) = os::open_file_with("Android Studio", config.project_dir()) { + if let Err(e) = os::open_file_with("Android Studio", config.project_dir(), &env.base) { log::error!("{}", e); } loop { diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index f1ed0939b4f..833c1c8175b 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -82,11 +82,11 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; let open = options.open; - run_build(options, config, env, noise_level) + run_build(options, config, &env, noise_level) .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; if open { - open_and_wait(config); + open_and_wait(config, &env); } Ok(()) @@ -98,7 +98,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { fn run_build( mut options: Options, config: &AndroidConfig, - env: Env, + env: &Env, noise_level: NoiseLevel, ) -> Result<()> { let profile = if options.debug { @@ -147,7 +147,7 @@ fn run_build( let apk_outputs = if options.apk { apk::build( config, - &env, + env, noise_level, profile, get_targets_or_all(Vec::new())?, @@ -160,7 +160,7 @@ fn run_build( let aab_outputs = if options.aab { aab::build( config, - &env, + env, noise_level, profile, get_targets_or_all(Vec::new())?, diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index a91f9383308..bde02562448 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -11,7 +11,10 @@ use crate::{ use clap::Parser; use cargo_mobile::{ - android::config::{Config as AndroidConfig, Metadata as AndroidMetadata}, + android::{ + config::{Config as AndroidConfig, Metadata as AndroidMetadata}, + env::Env, + }, config::Config, opts::{NoiseLevel, Profile}, }; @@ -106,7 +109,12 @@ fn run_dev( let out_dir = bin_path.parent().unwrap(); let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; + let env = env()?; + init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + let open = options.open; + let exit_on_panic = options.exit_on_panic; + let no_watch = options.no_watch; interface.mobile_dev( MobileOptions { debug: true, @@ -125,13 +133,18 @@ fn run_dev( write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; if open { - open_and_wait(config) + open_and_wait(config, &env) } else { - match run(options, root_conf, config, metadata, noise_level) { - Ok(c) => Ok(Box::new(c) as Box), + match run(options, config, &env, metadata, noise_level) { + Ok(c) => { + crate::dev::wait_dev_process(c.clone(), move |status, reason| { + crate::dev::on_app_exit(status, reason, exit_on_panic, no_watch) + }); + Ok(Box::new(c) as Box) + } Err(Error::FailedToPromptForDevice(e)) => { log::error!("{}", e); - open_and_wait(config) + open_and_wait(config, &env) } Err(e) => Err(e.into()), } @@ -142,8 +155,8 @@ fn run_dev( fn run( options: MobileOptions, - root_conf: &Config, config: &AndroidConfig, + env: &Env, metadata: &AndroidMetadata, noise_level: NoiseLevel, ) -> Result { @@ -155,14 +168,11 @@ fn run( let build_app_bundle = metadata.asset_packs().is_some(); - let env = env()?; - init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; - - device_prompt(&env) + device_prompt(env) .map_err(Error::FailedToPromptForDevice)? .run( config, - &env, + env, noise_level, profile, None, @@ -170,6 +180,6 @@ fn run( false, ".MainActivity".into(), ) - .map(|c| DevChild(Some(c))) + .map(DevChild::new) .map_err(Error::RunFailed) } diff --git a/tooling/cli/src/mobile/android/open.rs b/tooling/cli/src/mobile/android/open.rs index 97db6129a03..f4f147ad9f8 100644 --- a/tooling/cli/src/mobile/android/open.rs +++ b/tooling/cli/src/mobile/android/open.rs @@ -1,4 +1,4 @@ -use super::{ensure_init, with_config, Error, MobileTarget}; +use super::{ensure_init, env, with_config, Error, MobileTarget}; use crate::Result; use cargo_mobile::os; @@ -8,7 +8,9 @@ pub fn command() -> Result<()> { |_root_conf, config, _metadata, _cli_options| { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed) + let env = env()?; + os::open_file_with("Android Studio", config.project_dir(), &env.base) + .map_err(Error::OpenFailed) }, ) .map_err(Into::into) diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 5fbcfe3dd1c..4382188244c 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -153,9 +153,9 @@ fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { device_prompt(env).map(|device| device.target()).ok() } -fn open_and_wait(config: &AppleConfig) -> ! { +fn open_and_wait(config: &AppleConfig, env: &Env) -> ! { log::info!("Opening Xcode"); - if let Err(e) = os::open_file_with("Xcode", config.project_dir()) { + if let Err(e) = os::open_file_with("Xcode", config.project_dir(), env) { log::error!("{}", e); } loop { diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index 99f5f6d9a19..0577ab3b6e6 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -74,11 +74,11 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; let open = options.open; - run_build(options, config, env, noise_level) + run_build(options, config, &env, noise_level) .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; if open { - open_and_wait(config); + open_and_wait(config, &env); } Ok(()) @@ -90,7 +90,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { fn run_build( mut options: Options, config: &AppleConfig, - env: Env, + env: &Env, noise_level: NoiseLevel, ) -> Result<()> { let profile = if options.debug { @@ -135,16 +135,16 @@ fn run_build( call_for_targets_with_fallback( options.targets.iter(), &detect_target_ok, - &env, + env, |target: &Target| { let mut app_version = config.bundle_version().clone(); if let Some(build_number) = options.build_number { app_version.push_extra(build_number); } - target.build(config, &env, noise_level, profile)?; - target.archive(config, &env, noise_level, profile, Some(app_version))?; - target.export(config, &env, noise_level)?; + target.build(config, env, noise_level, profile)?; + target.archive(config, env, noise_level, profile, Some(app_version))?; + target.export(config, env, noise_level)?; if let Ok(ipa_path) = config.ipa_path() { let out_dir = config.export_dir().join(target.arch); diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 5e29575c686..1914c0f655a 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -12,6 +12,7 @@ use clap::Parser; use cargo_mobile::{ apple::config::Config as AppleConfig, config::Config, + env::Env, opts::{NoiseLevel, Profile}, }; @@ -91,7 +92,12 @@ fn run_dev( let out_dir = bin_path.parent().unwrap(); let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; + let env = env()?; + init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + let open = options.open; + let exit_on_panic = options.exit_on_panic; + let no_watch = options.no_watch; interface.mobile_dev( MobileOptions { debug: true, @@ -108,14 +114,20 @@ fn run_dev( vars: Default::default(), }; write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; + if open { - open_and_wait(config) + open_and_wait(config, &env) } else { - match run(options, root_conf, config, noise_level) { - Ok(c) => Ok(Box::new(c) as Box), + match run(options, config, &env, noise_level) { + Ok(c) => { + crate::dev::wait_dev_process(c.clone(), move |status, reason| { + crate::dev::on_app_exit(status, reason, exit_on_panic, no_watch) + }); + Ok(Box::new(c) as Box) + } Err(Error::FailedToPromptForDevice(e)) => { log::error!("{}", e); - open_and_wait(config) + open_and_wait(config, &env) } Err(e) => Err(e.into()), } @@ -126,8 +138,8 @@ fn run_dev( fn run( options: MobileOptions, - root_conf: &Config, config: &AppleConfig, + env: &Env, noise_level: NoiseLevel, ) -> Result { let profile = if options.debug { @@ -136,12 +148,11 @@ fn run( Profile::Release }; - let env = env()?; - init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + let non_interactive = true; // ios-deploy --noninteractive (quit when app crashes or exits) - device_prompt(&env) + device_prompt(env) .map_err(Error::FailedToPromptForDevice)? - .run(config, &env, noise_level, false, profile) - .map(|c| DevChild(Some(c))) + .run(config, env, noise_level, non_interactive, profile) + .map(DevChild::new) .map_err(Error::RunFailed) } diff --git a/tooling/cli/src/mobile/ios/open.rs b/tooling/cli/src/mobile/ios/open.rs index 9d28d232280..0304516ddc7 100644 --- a/tooling/cli/src/mobile/ios/open.rs +++ b/tooling/cli/src/mobile/ios/open.rs @@ -1,4 +1,4 @@ -use super::{ensure_init, with_config, Error, MobileTarget}; +use super::{ensure_init, env, with_config, Error, MobileTarget}; use crate::Result; use cargo_mobile::os; @@ -8,7 +8,8 @@ pub fn command() -> Result<()> { |_root_conf, config, _metadata, _cli_options| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed) + let env = env()?; + os::open_file_with("Xcode", config.project_dir(), &env).map_err(Error::OpenFailed) }, ) .map_err(Into::into) diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 9ac0ce9a6e2..f0b4646858e 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -20,6 +20,7 @@ use cargo_mobile::{ }; use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; use serde::{Deserialize, Serialize}; +use shared_child::SharedChild; use std::{ collections::HashMap, env::set_var, @@ -28,6 +29,10 @@ use std::{ io::{BufRead, BufReader, Write as _}, path::PathBuf, process::ExitStatus, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, }; #[cfg(not(windows))] @@ -40,34 +45,38 @@ mod init; #[cfg(target_os = "macos")] pub mod ios; -pub struct DevChild(Option); +#[derive(Clone)] +pub struct DevChild { + child: Arc, + manually_killed_process: Arc, +} -impl Drop for DevChild { - fn drop(&mut self) { - // consume the handle since we're not waiting on it - // just to prevent a log error - // note that this doesn't leak any memory - self.0.take().unwrap().leak(); +impl DevChild { + fn new(handle: bossy::Handle) -> Self { + Self { + child: Arc::new(SharedChild::new(handle.into()).unwrap()), + manually_killed_process: Default::default(), + } } } impl DevProcess for DevChild { - fn kill(&mut self) -> std::io::Result<()> { - self - .0 - .as_mut() - .unwrap() - .kill() - .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "failed to kill")) + fn kill(&self) -> std::io::Result<()> { + self.child.kill()?; + self.manually_killed_process.store(true, Ordering::Relaxed); + Ok(()) + } + + fn try_wait(&self) -> std::io::Result> { + self.child.try_wait() + } + + fn wait(&self) -> std::io::Result { + self.child.wait() } - fn try_wait(&mut self) -> std::io::Result> { - self - .0 - .as_mut() - .unwrap() - .try_wait() - .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "failed to wait")) + fn manually_killed_process(&self) -> bool { + self.manually_killed_process.load(Ordering::Relaxed) } } @@ -243,25 +252,15 @@ fn get_config(config: &TauriConfig, cli_options: &CliOptions) -> (Config, Metada .ok() .or_else(|| config.tauri.ios.development_team.clone()) .expect("you must set `tauri > iOS > developmentTeam` config value or the `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable"), - project_dir: None, - ios_no_default_features: None, + ios_features: ios_options.features.clone(), - macos_no_default_features: None, - macos_features: None, bundle_version: config.package.version.clone(), bundle_version_short: config.package.version.clone(), - ios_version: None, - macos_version: None, - use_legacy_build_system: None, - plist_pairs: None, - enable_bitcode: None, + ..Default::default() }), android: Some(RawAndroidConfig { - min_sdk_version: None, - vulkan_validation: None, - project_dir: None, - no_default_features: None, features: android_options.features.clone(), + ..Default::default() }), }; let config = Config::from_raw(tauri_dir(), raw).unwrap(); @@ -271,39 +270,17 @@ fn get_config(config: &TauriConfig, cli_options: &CliOptions) -> (Config, Metada apple: AppleMetadata { supported: true, ios: ApplePlatform { - no_default_features: false, cargo_args: Some(ios_options.args), features: ios_options.features, - libraries: None, - frameworks: None, - valid_archs: None, - vendor_frameworks: None, - vendor_sdks: None, - asset_catalogs: None, - pods: None, - pod_options: None, - additional_targets: None, - pre_build_scripts: None, - post_compile_scripts: None, - post_build_scripts: None, - command_line_arguments: None, + ..Default::default() }, macos: Default::default(), }, android: AndroidMetadata { supported: true, - no_default_features: false, cargo_args: Some(android_options.args), features: android_options.features, - app_sources: None, - app_plugins: None, - project_dependencies: None, - app_dependencies: None, - app_dependencies_platform: None, - asset_packs: None, - app_activity_name: None, - app_permissions: None, - app_theme_parent: None, + ..Default::default() }, }; From dee19784f9ab6731aae2ae376c828d61427f20a2 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sat, 27 Aug 2022 16:35:57 -0300 Subject: [PATCH 037/423] fix(cli): only require development team when running iOS commands (#5071) --- tooling/cli/src/mobile/android.rs | 2 +- tooling/cli/src/mobile/init.rs | 2 +- tooling/cli/src/mobile/ios.rs | 2 +- tooling/cli/src/mobile/mod.rs | 15 +++++++++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 9a8563ea48b..bee6da19cfa 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -108,7 +108,7 @@ fn with_config( let tauri_config_ = tauri_config_guard.as_ref().unwrap(); let cli_options = cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Android)); - let (config, metadata) = get_config(tauri_config_, &cli_options); + let (config, metadata) = get_config(tauri_config_, &cli_options, MobileTarget::Android); (config, metadata, cli_options) }; f(&config, config.android(), metadata.android(), cli_options) diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 7bcd965a3ec..2e08bd4bc90 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -107,7 +107,7 @@ pub fn exec( let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - let (config, metadata) = get_config(tauri_config_, &Default::default()); + let (config, metadata) = get_config(tauri_config_, &Default::default(), target); let asset_dir = config.app().asset_dir(); if !asset_dir.is_dir() { diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 4382188244c..50094bf0169 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -115,7 +115,7 @@ fn with_config( let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); let cli_options = cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Ios)); - let (config, metadata) = get_config(tauri_config_, &cli_options); + let (config, metadata) = get_config(tauri_config_, &cli_options, MobileTarget::Ios); (config, metadata, cli_options) }; f(&config, config.apple(), metadata.apple(), cli_options) diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index f0b4646858e..f3a59023f3f 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -199,7 +199,11 @@ fn read_options(config: &TauriConfig, target: Target) -> CliOptions { options } -fn get_config(config: &TauriConfig, cli_options: &CliOptions) -> (Config, Metadata) { +fn get_config( + config: &TauriConfig, + cli_options: &CliOptions, + #[allow(unused_variables)] target: Target, +) -> (Config, Metadata) { let mut s = config.tauri.bundle.identifier.rsplit('.'); let app_name = s.next().unwrap_or("app").to_string(); let mut domain = String::new(); @@ -248,11 +252,14 @@ fn get_config(config: &TauriConfig, cli_options: &CliOptions) -> (Config, Metada }, #[cfg(target_os = "macos")] apple: Some(RawAppleConfig { - development_team: std::env::var("TAURI_APPLE_DEVELOPMENT_TEAM") + development_team: if target == Target::Ios { + std::env::var("TAURI_APPLE_DEVELOPMENT_TEAM") .ok() .or_else(|| config.tauri.ios.development_team.clone()) - .expect("you must set `tauri > iOS > developmentTeam` config value or the `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable"), - + .expect("you must set `tauri > iOS > developmentTeam` config value or the `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable") + } else { + Default::default() + }, ios_features: ios_options.features.clone(), bundle_version: config.package.version.clone(), bundle_version_short: config.package.version.clone(), From 0a203a13ae8d2634c4eaead41cad33d5e5b7c190 Mon Sep 17 00:00:00 2001 From: Jeffrey Hutchins Date: Sun, 28 Aug 2022 11:48:15 -0600 Subject: [PATCH 038/423] fix(cli): config issues on macos (#5075) Co-authored-by: Lucas Nogueira --- tooling/cli/src/mobile/android.rs | 57 ++++++++++-- .../mobile/android/android_studio_script.rs | 4 +- tooling/cli/src/mobile/android/build.rs | 4 +- tooling/cli/src/mobile/android/dev.rs | 10 +-- tooling/cli/src/mobile/init.rs | 82 +++++++++-------- tooling/cli/src/mobile/ios.rs | 57 ++++++++++-- tooling/cli/src/mobile/ios/build.rs | 4 +- tooling/cli/src/mobile/ios/dev.rs | 11 ++- tooling/cli/src/mobile/mod.rs | 88 ++----------------- 9 files changed, 164 insertions(+), 153 deletions(-) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index bee6da19cfa..407f623a608 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -5,12 +5,12 @@ use cargo_mobile::{ android::{ adb, - config::{Config as AndroidConfig, Metadata as AndroidMetadata}, + config::{Config as AndroidConfig, Metadata as AndroidMetadata, Raw as RawAndroidConfig}, device::{Device, RunError}, env::{Env, Error as AndroidEnvError}, target::{BuildError, Target}, }, - config::Config, + config::app::App, device::PromptError, env::Error as EnvError, opts::NoiseLevel, @@ -18,13 +18,17 @@ use cargo_mobile::{ util::prompt, }; use clap::{Parser, Subcommand}; +use std::env::set_var; use super::{ - ensure_init, get_config, + ensure_init, get_app, init::{command as init_command, init_dot_cargo, Options as InitOptions}, log_finished, read_options, CliOptions, Target as MobileTarget, }; -use crate::{helpers::config::get as get_tauri_config, Result}; +use crate::{ + helpers::config::{get as get_tauri_config, Config as TauriConfig}, + Result, +}; mod android_studio_script; mod build; @@ -97,21 +101,56 @@ pub fn command(cli: Cli, verbosity: usize) -> Result<()> { Ok(()) } +pub fn get_config( + app: Option, + config: &TauriConfig, + cli_options: &CliOptions, +) -> (App, AndroidConfig, AndroidMetadata) { + let app = app.unwrap_or_else(|| get_app(config)); + let android_options = cli_options.clone(); + + let raw = RawAndroidConfig { + features: android_options.features.clone(), + ..Default::default() + }; + let config = AndroidConfig::from_raw(app.clone(), Some(raw)).unwrap(); + + let metadata = AndroidMetadata { + supported: true, + cargo_args: Some(android_options.args), + features: android_options.features, + ..Default::default() + }; + + set_var("WRY_ANDROID_REVERSED_DOMAIN", app.reverse_domain()); + set_var("WRY_ANDROID_APP_NAME_SNAKE_CASE", app.name()); + set_var( + "WRY_ANDROID_KOTLIN_FILES_OUT_DIR", + config.project_dir().join("app/src/main").join(format!( + "java/{}/{}", + app.reverse_domain().replace('.', "/"), + app.name() + )), + ); + + (app, config, metadata) +} + fn with_config( cli_options: Option, - f: impl FnOnce(&Config, &AndroidConfig, &AndroidMetadata, CliOptions) -> Result, + f: impl FnOnce(&App, &AndroidConfig, &AndroidMetadata, CliOptions) -> Result, ) -> Result { - let (config, metadata, cli_options) = { + let (app, config, metadata, cli_options) = { let tauri_config = get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); let cli_options = cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Android)); - let (config, metadata) = get_config(tauri_config_, &cli_options, MobileTarget::Android); - (config, metadata, cli_options) + let (app, config, metadata) = get_config(None, tauri_config_, &cli_options); + (app, config, metadata, cli_options) }; - f(&config, config.android(), metadata.android(), cli_options) + f(&app, &config, &metadata, cli_options) } fn env() -> Result { diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index eb37490e604..1c91f2f9f74 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -32,12 +32,12 @@ pub fn command(options: Options) -> Result<()> { Profile::Debug }; - with_config(None, |root_conf, config, metadata, cli_options| { + with_config(None, |app, config, metadata, cli_options| { ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; let env = env()?; - init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, Some((&env, config))).map_err(Error::InitDotCargo)?; call_for_targets_with_fallback( options.targets.unwrap_or_default().iter(), diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index 833c1c8175b..4b37173cc79 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -71,7 +71,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { delete_codegen_vars(); with_config( Some(Default::default()), - |root_conf, config, _metadata, _cli_options| { + |app, config, _metadata, _cli_options| { set_var("WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", ""); set_var("WRY_RUSTWEBVIEW_CLASS_INIT", ""); @@ -79,7 +79,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; let env = env()?; - init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, Some((&env, config))).map_err(Error::InitDotCargo)?; let open = options.open; run_build(options, config, &env, noise_level) diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index bde02562448..66eaf1c1279 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -15,7 +15,7 @@ use cargo_mobile::{ config::{Config as AndroidConfig, Metadata as AndroidMetadata}, env::Env, }, - config::Config, + config::app::App, opts::{NoiseLevel, Profile}, }; @@ -69,7 +69,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { delete_codegen_vars(); with_config( Some(Default::default()), - |root_conf, config, metadata, _cli_options| { + |app, config, metadata, _cli_options| { set_var( "WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", WEBVIEW_CLIENT_CLASS_EXTENSION, @@ -77,7 +77,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { set_var("WRY_RUSTWEBVIEW_CLASS_INIT", WEBVIEW_CLASS_INIT); ensure_init(config.project_dir(), MobileTarget::Android) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config, metadata, noise_level) + run_dev(options, app, config, metadata, noise_level) .map_err(|e| Error::DevFailed(format!("{:#}", e))) }, ) @@ -86,7 +86,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { fn run_dev( options: Options, - root_conf: &Config, + app: &App, config: &AndroidConfig, metadata: &AndroidMetadata, noise_level: NoiseLevel, @@ -110,7 +110,7 @@ fn run_dev( let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; let env = env()?; - init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, Some((&env, config))).map_err(Error::InitDotCargo)?; let open = options.open; let exit_on_panic = options.exit_on_panic; diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 2e08bd4bc90..13204def507 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -2,13 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use super::{get_config, Target}; +use super::{get_app, Target}; use crate::helpers::{config::get as get_tauri_config, template::JsonMap}; use crate::Result; use cargo_mobile::{ - android::{self, env::Env as AndroidEnv, ndk, target::Target as AndroidTarget}, + android::{ + self, config::Config as AndroidConfig, env::Env as AndroidEnv, ndk, + target::Target as AndroidTarget, + }, bossy, - config::Config, + config::app::App, dot_cargo, os::code_command, target::TargetTrait as _, @@ -66,8 +69,11 @@ pub enum Error { DotCargoWrite(dot_cargo::WriteError), } -pub fn init_dot_cargo(config: &Config, android_env: Option<&AndroidEnv>) -> Result<(), Error> { - let mut dot_cargo = dot_cargo::DotCargo::load(config.app()).map_err(Error::DotCargoLoad)?; +pub fn init_dot_cargo( + app: &App, + android: Option<(&AndroidEnv, &AndroidConfig)>, +) -> Result<(), Error> { + let mut dot_cargo = dot_cargo::DotCargo::load(app).map_err(Error::DotCargoLoad)?; // Mysteriously, builds that don't specify `--target` seem to fight over // the build cache with builds that use `--target`! This means that // alternating between i.e. `cargo run` and `cargo apple run` would @@ -81,18 +87,18 @@ pub fn init_dot_cargo(config: &Config, android_env: Option<&AndroidEnv>) -> Resu dot_cargo .set_default_target(util::host_target_triple().map_err(Error::HostTargetTripleDetection)?); - if let Some(env) = android_env { + if let Some((env, config)) = android { for target in AndroidTarget::all().values() { dot_cargo.insert_target( target.triple.to_owned(), target - .generate_cargo_config(config.android(), env) + .generate_cargo_config(config, env) .map_err(Error::DotCargoGenFailed)?, ); } } - dot_cargo.write(config.app()).map_err(Error::DotCargoWrite) + dot_cargo.write(app).map_err(Error::DotCargoWrite) } pub fn exec( @@ -101,15 +107,15 @@ pub fn exec( non_interactive: bool, skip_dev_tools: bool, #[allow(unused_variables)] reinstall_deps: bool, -) -> Result { +) -> Result { let tauri_config = get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - let (config, metadata) = get_config(tauri_config_, &Default::default(), target); + let app = get_app(tauri_config_); - let asset_dir = config.app().asset_dir(); + let asset_dir = app.asset_dir(); if !asset_dir.is_dir() { fs::create_dir_all(&asset_dir).map_err(|cause| Error::AssetDirCreation { asset_dir, cause })?; } @@ -124,7 +130,7 @@ pub fn exec( .map_err(Error::LldbExtensionInstall)?; } - let (handlebars, mut map) = handlebars(&config); + let (handlebars, mut map) = handlebars(&app); let mut args = std::env::args_os(); let tauri_binary = args @@ -160,18 +166,17 @@ pub fn exec( map.insert("tauri-binary-args", &build_args); map.insert("tauri-binary-args-str", build_args.join(" ")); - // Generate Android Studio project - let android_env = if target == Target::Android { - match AndroidEnv::new() { + let app = match target { + // Generate Android Studio project + Target::Android => match AndroidEnv::new() { Ok(env) => { - super::android::project::gen( - config.android(), - metadata.android(), - (handlebars, map), - wrapper, - ) - .map_err(Error::AndroidInit)?; - Some(env) + let (app, config, metadata) = + super::android::get_config(Some(app), tauri_config_, &Default::default()); + map.insert("android", &config); + super::android::project::gen(&config, &metadata, (handlebars, map), wrapper) + .map_err(Error::AndroidInit)?; + init_dot_cargo(&app, Some((&env, &config)))?; + app } Err(err) => { if err.sdk_or_ndk_issue() { @@ -180,19 +185,22 @@ pub fn exec( err, ) .print(wrapper); - None + init_dot_cargo(&app, None)?; + app } else { return Err(Error::AndroidEnv(err)); } } - } - } else { - // Generate Xcode project + }, #[cfg(target_os = "macos")] - if target == Target::Ios { + // Generate Xcode project + Target::Ios => { + let (app, config, metadata) = + super::ios::get_config(Some(app), tauri_config_, &Default::default()); + map.insert("apple", &config); super::ios::project::gen( - config.apple(), - metadata.apple(), + &config, + &metadata, (handlebars, map), wrapper, non_interactive, @@ -200,21 +208,20 @@ pub fn exec( reinstall_deps, ) .map_err(Error::IosInit)?; + init_dot_cargo(&app, None)?; + app } - None }; - init_dot_cargo(&config, android_env.as_ref())?; - Report::victory( "Project generated successfully!", "Make cool apps! 🌻 🐕 🎉", ) .print(wrapper); - Ok(config) + Ok(app) } -fn handlebars(config: &Config) -> (Handlebars<'static>, JsonMap) { +fn handlebars(app: &App) -> (Handlebars<'static>, JsonMap) { let mut h = Handlebars::new(); h.register_escape_fn(handlebars::no_escape); @@ -236,10 +243,7 @@ fn handlebars(config: &Config) -> (Handlebars<'static>, JsonMap) { h.register_helper("unprefix-path", Box::new(unprefix_path)); let mut map = JsonMap::default(); - map.insert("app", config.app()); - #[cfg(target_os = "macos")] - map.insert("apple", config.apple()); - map.insert("android", config.android()); + map.insert("app", app); (h, map) } diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 50094bf0169..e37de65ddef 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -4,12 +4,15 @@ use cargo_mobile::{ apple::{ - config::{Config as AppleConfig, Metadata as AppleMetadata}, + config::{ + Config as AppleConfig, Metadata as AppleMetadata, Platform as ApplePlatform, + Raw as RawAppleConfig, + }, device::{Device, RunError}, ios_deploy, target::{CompileLibError, Target}, }, - config::Config, + config::app::App, device::PromptError, env::{Env, Error as EnvError}, opts::NoiseLevel, @@ -19,11 +22,14 @@ use cargo_mobile::{ use clap::{Parser, Subcommand}; use super::{ - ensure_init, env, get_config, + ensure_init, env, get_app, init::{command as init_command, init_dot_cargo, Options as InitOptions}, log_finished, read_options, CliOptions, Target as MobileTarget, }; -use crate::{helpers::config::get as get_tauri_config, Result}; +use crate::{ + helpers::config::{get as get_tauri_config, Config as TauriConfig}, + Result, +}; use std::path::PathBuf; @@ -105,20 +111,53 @@ pub fn command(cli: Cli, verbosity: usize) -> Result<()> { Ok(()) } +pub fn get_config( + app: Option, + config: &TauriConfig, + cli_options: &CliOptions, +) -> (App, AppleConfig, AppleMetadata) { + let app = app.unwrap_or_else(|| get_app(config)); + let ios_options = cli_options.clone(); + + let raw = RawAppleConfig { + development_team: std::env::var("TAURI_APPLE_DEVELOPMENT_TEAM") + .ok() + .or_else(|| config.tauri.ios.development_team.clone()) + .expect("you must set `tauri > iOS > developmentTeam` config value or the `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable"), + ios_features: ios_options.features.clone(), + bundle_version: config.package.version.clone(), + bundle_version_short: config.package.version.clone(), + ..Default::default() + }; + let config = AppleConfig::from_raw(app.clone(), Some(raw)).unwrap(); + + let metadata = AppleMetadata { + supported: true, + ios: ApplePlatform { + cargo_args: Some(ios_options.args), + features: ios_options.features, + ..Default::default() + }, + macos: Default::default(), + }; + + (app, config, metadata) +} + fn with_config( cli_options: Option, - f: impl FnOnce(&Config, &AppleConfig, &AppleMetadata, CliOptions) -> Result, + f: impl FnOnce(&App, &AppleConfig, &AppleMetadata, CliOptions) -> Result, ) -> Result { - let (config, metadata, cli_options) = { + let (app, config, metadata, cli_options) = { let tauri_config = get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); let cli_options = cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Ios)); - let (config, metadata) = get_config(tauri_config_, &cli_options, MobileTarget::Ios); - (config, metadata, cli_options) + let (app, config, metadata) = get_config(None, tauri_config_, &cli_options); + (app, config, metadata, cli_options) }; - f(&config, config.apple(), metadata.apple(), cli_options) + f(&app, &config, &metadata, cli_options) } fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index 0577ab3b6e6..be6a2143701 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -66,12 +66,12 @@ impl From for crate::build::Options { pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { with_config( Some(Default::default()), - |root_conf, config, _metadata, _cli_options| { + |app, config, _metadata, _cli_options| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; let env = env()?; - init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, None).map_err(Error::InitDotCargo)?; let open = options.open; run_build(options, config, &env, noise_level) diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 1914c0f655a..c1e2621982a 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -11,7 +11,7 @@ use clap::Parser; use cargo_mobile::{ apple::config::Config as AppleConfig, - config::Config, + config::app::App, env::Env, opts::{NoiseLevel, Profile}, }; @@ -57,11 +57,10 @@ impl From for crate::dev::Options { pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { with_config( Some(Default::default()), - |root_conf, config, _metadata, _cli_options| { + |app, config, _metadata, _cli_options| { ensure_init(config.project_dir(), MobileTarget::Ios) .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, root_conf, config, noise_level) - .map_err(|e| Error::DevFailed(format!("{:#}", e))) + run_dev(options, app, config, noise_level).map_err(|e| Error::DevFailed(format!("{:#}", e))) }, ) .map_err(Into::into) @@ -69,7 +68,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { fn run_dev( options: Options, - root_conf: &Config, + app: &App, config: &AppleConfig, noise_level: NoiseLevel, ) -> Result<()> { @@ -93,7 +92,7 @@ fn run_dev( let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; let env = env()?; - init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, None).map_err(Error::InitDotCargo)?; let open = options.open; let exit_on_panic = options.exit_on_panic; diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index f3a59023f3f..9ad44e0428a 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -7,14 +7,9 @@ use crate::{ interface::DevProcess, }; use anyhow::{bail, Result}; -#[cfg(target_os = "macos")] -use cargo_mobile::apple::config::{ - Metadata as AppleMetadata, Platform as ApplePlatform, Raw as RawAppleConfig, -}; use cargo_mobile::{ - android::config::{Metadata as AndroidMetadata, Raw as RawAndroidConfig}, bossy, - config::{app::Raw as RawAppConfig, metadata::Metadata, Config, Raw}, + config::app::{App, Raw as RawAppConfig}, env::Error as EnvError, opts::NoiseLevel, }; @@ -199,11 +194,7 @@ fn read_options(config: &TauriConfig, target: Target) -> CliOptions { options } -fn get_config( - config: &TauriConfig, - cli_options: &CliOptions, - #[allow(unused_variables)] target: Target, -) -> (Config, Metadata) { +fn get_app(config: &TauriConfig) -> App { let mut s = config.tauri.bundle.identifier.rsplit('.'); let app_name = s.next().unwrap_or("app").to_string(); let mut domain = String::new(); @@ -238,75 +229,14 @@ fn get_config( app_name }; - #[cfg(target_os = "macos")] - let ios_options = cli_options.clone(); - let android_options = cli_options.clone(); - - let raw = Raw { - app: RawAppConfig { - name: app_name.clone(), - stylized_name: config.package.product_name.clone(), - domain, - asset_dir: None, - template_pack: None, - }, - #[cfg(target_os = "macos")] - apple: Some(RawAppleConfig { - development_team: if target == Target::Ios { - std::env::var("TAURI_APPLE_DEVELOPMENT_TEAM") - .ok() - .or_else(|| config.tauri.ios.development_team.clone()) - .expect("you must set `tauri > iOS > developmentTeam` config value or the `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable") - } else { - Default::default() - }, - ios_features: ios_options.features.clone(), - bundle_version: config.package.version.clone(), - bundle_version_short: config.package.version.clone(), - ..Default::default() - }), - android: Some(RawAndroidConfig { - features: android_options.features.clone(), - ..Default::default() - }), - }; - let config = Config::from_raw(tauri_dir(), raw).unwrap(); - - let metadata = Metadata { - #[cfg(target_os = "macos")] - apple: AppleMetadata { - supported: true, - ios: ApplePlatform { - cargo_args: Some(ios_options.args), - features: ios_options.features, - ..Default::default() - }, - macos: Default::default(), - }, - android: AndroidMetadata { - supported: true, - cargo_args: Some(android_options.args), - features: android_options.features, - ..Default::default() - }, + let raw = RawAppConfig { + name: app_name, + stylized_name: config.package.product_name.clone(), + domain, + asset_dir: None, + template_pack: None, }; - - set_var("WRY_ANDROID_REVERSED_DOMAIN", &reverse_domain); - set_var("WRY_ANDROID_APP_NAME_SNAKE_CASE", &app_name); - set_var( - "WRY_ANDROID_KOTLIN_FILES_OUT_DIR", - config - .android() - .project_dir() - .join("app/src/main") - .join(format!( - "java/{}/{}", - config.app().reverse_domain().replace('.', "/"), - config.app().name() - )), - ); - - (config, metadata) + App::from_raw(tauri_dir(), raw).unwrap() } fn ensure_init(project_dir: PathBuf, target: Target) -> Result<()> { From 8cf9af93ebeeb5a2c1a35e2ffd2be77c90e78e64 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 28 Aug 2022 15:32:50 -0300 Subject: [PATCH 039/423] refactor(cli): remove mobile Error enum, use anyhow instead (#5076) --- tooling/cli/src/mobile/android.rs | 48 +++--------- .../mobile/android/android_studio_script.rs | 12 ++- tooling/cli/src/mobile/android/build.rs | 19 +++-- tooling/cli/src/mobile/android/dev.rs | 28 ++++--- tooling/cli/src/mobile/android/open.rs | 9 +-- tooling/cli/src/mobile/android/project.rs | 64 ++++++---------- tooling/cli/src/mobile/init.rs | 74 +++++-------------- tooling/cli/src/mobile/ios.rs | 53 ++----------- tooling/cli/src/mobile/ios/build.rs | 12 ++- tooling/cli/src/mobile/ios/dev.rs | 31 ++++---- tooling/cli/src/mobile/ios/open.rs | 8 +- tooling/cli/src/mobile/ios/project.rs | 50 ++++--------- tooling/cli/src/mobile/ios/xcode_script.rs | 61 ++++++++------- 13 files changed, 167 insertions(+), 302 deletions(-) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 407f623a608..6c1b1779229 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -6,13 +6,12 @@ use cargo_mobile::{ android::{ adb, config::{Config as AndroidConfig, Metadata as AndroidMetadata, Raw as RawAndroidConfig}, - device::{Device, RunError}, - env::{Env, Error as AndroidEnvError}, - target::{BuildError, Target}, + device::Device, + env::Env, + target::Target, }, config::app::App, device::PromptError, - env::Error as EnvError, opts::NoiseLevel, os, util::prompt, @@ -36,34 +35,6 @@ mod dev; mod open; pub(crate) mod project; -#[derive(Debug, thiserror::Error)] -enum Error { - #[error(transparent)] - EnvInitFailed(EnvError), - #[error(transparent)] - AndroidEnvInitFailed(AndroidEnvError), - #[error(transparent)] - InitDotCargo(super::init::Error), - #[error("invalid tauri configuration: {0}")] - InvalidTauriConfig(String), - #[error("{0}")] - ProjectNotInitialized(String), - #[error(transparent)] - OpenFailed(os::OpenFileError), - #[error("{0}")] - DevFailed(String), - #[error("{0}")] - BuildFailed(String), - #[error(transparent)] - AndroidStudioScriptFailed(BuildError), - #[error(transparent)] - RunFailed(RunError), - #[error("{0}")] - TargetInvalid(String), - #[error(transparent)] - FailedToPromptForDevice(PromptError), -} - #[derive(Parser)] #[clap( author, @@ -138,11 +109,10 @@ pub fn get_config( fn with_config( cli_options: Option, - f: impl FnOnce(&App, &AndroidConfig, &AndroidMetadata, CliOptions) -> Result, -) -> Result { + f: impl FnOnce(&App, &AndroidConfig, &AndroidMetadata, CliOptions) -> Result, +) -> Result { let (app, config, metadata, cli_options) = { - let tauri_config = - get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config = get_tauri_config(None)?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); let cli_options = @@ -153,9 +123,9 @@ fn with_config( f(&app, &config, &metadata, cli_options) } -fn env() -> Result { - let env = super::env().map_err(Error::EnvInitFailed)?; - cargo_mobile::android::env::Env::from_env(env).map_err(Error::AndroidEnvInitFailed) +fn env() -> Result { + let env = super::env()?; + cargo_mobile::android::env::Env::from_env(env).map_err(Into::into) } fn delete_codegen_vars() { diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index 1c91f2f9f74..cf3bc974a8f 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -1,4 +1,4 @@ -use super::{detect_target_ok, ensure_init, env, init_dot_cargo, with_config, Error, MobileTarget}; +use super::{detect_target_ok, ensure_init, env, init_dot_cargo, with_config, MobileTarget}; use crate::Result; use clap::Parser; @@ -33,11 +33,10 @@ pub fn command(options: Options) -> Result<()> { }; with_config(None, |app, config, metadata, cli_options| { - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + ensure_init(config.project_dir(), MobileTarget::Android)?; let env = env()?; - init_dot_cargo(app, Some((&env, config))).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, Some((&env, config)))?; call_for_targets_with_fallback( options.targets.unwrap_or_default().iter(), @@ -53,10 +52,9 @@ pub fn command(options: Options) -> Result<()> { true, profile, ) - .map_err(Error::AndroidStudioScriptFailed) + .map_err(Into::into) }, ) - .map_err(|e| Error::TargetInvalid(e.to_string()))? + .map_err(|e| anyhow::anyhow!(e.to_string()))? }) - .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index 4b37173cc79..4827557200c 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -1,6 +1,6 @@ use super::{ delete_codegen_vars, ensure_init, env, init_dot_cargo, log_finished, open_and_wait, with_config, - Error, MobileTarget, + MobileTarget, }; use crate::{ helpers::{config::get as get_tauri_config, flock}, @@ -75,15 +75,13 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { set_var("WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", ""); set_var("WRY_RUSTWEBVIEW_CLASS_INIT", ""); - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + ensure_init(config.project_dir(), MobileTarget::Android)?; let env = env()?; - init_dot_cargo(app, Some((&env, config))).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, Some((&env, config)))?; let open = options.open; - run_build(options, config, &env, noise_level) - .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; + run_build(options, config, &env, noise_level)?; if open { open_and_wait(config, &env); @@ -176,7 +174,7 @@ fn run_build( Ok(()) } -fn get_targets_or_all<'a>(targets: Vec) -> Result>, Error> { +fn get_targets_or_all<'a>(targets: Vec) -> Result>> { if targets.is_empty() { Ok(Target::all().iter().map(|t| t.1).collect()) } else { @@ -190,10 +188,11 @@ fn get_targets_or_all<'a>(targets: Vec) -> Result>, E for t in targets { let target = Target::for_name(&t).ok_or_else(|| { - Error::TargetInvalid(format!( + anyhow::anyhow!( "Target {} is invalid; the possible targets are {}", - t, possible_targets - )) + t, + possible_targets + ) })?; outs.push(target); } diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 66eaf1c1279..d308886efee 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -1,6 +1,6 @@ use super::{ delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, - Error, MobileTarget, + MobileTarget, PromptError, }; use crate::{ helpers::{config::get as get_tauri_config, flock}, @@ -12,7 +12,9 @@ use clap::Parser; use cargo_mobile::{ android::{ + adb, config::{Config as AndroidConfig, Metadata as AndroidMetadata}, + device::RunError as DeviceRunError, env::Env, }, config::app::App, @@ -75,10 +77,8 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { WEBVIEW_CLIENT_CLASS_EXTENSION, ); set_var("WRY_RUSTWEBVIEW_CLASS_INIT", WEBVIEW_CLASS_INIT); - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, app, config, metadata, noise_level) - .map_err(|e| Error::DevFailed(format!("{:#}", e))) + ensure_init(config.project_dir(), MobileTarget::Android)?; + run_dev(options, app, config, metadata, noise_level).map_err(Into::into) }, ) .map_err(Into::into) @@ -110,7 +110,7 @@ fn run_dev( let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; let env = env()?; - init_dot_cargo(app, Some((&env, config))).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, Some((&env, config)))?; let open = options.open; let exit_on_panic = options.exit_on_panic; @@ -142,7 +142,7 @@ fn run_dev( }); Ok(Box::new(c) as Box) } - Err(Error::FailedToPromptForDevice(e)) => { + Err(RunError::FailedToPromptForDevice(e)) => { log::error!("{}", e); open_and_wait(config, &env) } @@ -153,13 +153,21 @@ fn run_dev( ) } +#[derive(Debug, thiserror::Error)] +enum RunError { + #[error(transparent)] + FailedToPromptForDevice(PromptError), + #[error(transparent)] + RunFailed(DeviceRunError), +} + fn run( options: MobileOptions, config: &AndroidConfig, env: &Env, metadata: &AndroidMetadata, noise_level: NoiseLevel, -) -> Result { +) -> Result { let profile = if options.debug { Profile::Debug } else { @@ -169,7 +177,7 @@ fn run( let build_app_bundle = metadata.asset_packs().is_some(); device_prompt(env) - .map_err(Error::FailedToPromptForDevice)? + .map_err(RunError::FailedToPromptForDevice)? .run( config, env, @@ -181,5 +189,5 @@ fn run( ".MainActivity".into(), ) .map(DevChild::new) - .map_err(Error::RunFailed) + .map_err(RunError::RunFailed) } diff --git a/tooling/cli/src/mobile/android/open.rs b/tooling/cli/src/mobile/android/open.rs index f4f147ad9f8..b67c2aaec3a 100644 --- a/tooling/cli/src/mobile/android/open.rs +++ b/tooling/cli/src/mobile/android/open.rs @@ -1,4 +1,4 @@ -use super::{ensure_init, env, with_config, Error, MobileTarget}; +use super::{ensure_init, env, with_config, MobileTarget}; use crate::Result; use cargo_mobile::os; @@ -6,12 +6,9 @@ pub fn command() -> Result<()> { with_config( Some(Default::default()), |_root_conf, config, _metadata, _cli_options| { - ensure_init(config.project_dir(), MobileTarget::Android) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + ensure_init(config.project_dir(), MobileTarget::Android)?; let env = env()?; - os::open_file_with("Android Studio", config.project_dir(), &env.base) - .map_err(Error::OpenFailed) + os::open_file_with("Android Studio", config.project_dir(), &env.base).map_err(Into::into) }, ) - .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs index c40c54a84bc..9182c7c8e02 100644 --- a/tooling/cli/src/mobile/android/project.rs +++ b/tooling/cli/src/mobile/android/project.rs @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::helpers::template; +use crate::{helpers::template, Result}; +use anyhow::Context; use cargo_mobile::{ android::{ config::{Config, Metadata}, target::Target, }, - bossy, os, + os, target::TargetTrait as _, util::{ self, @@ -19,45 +20,18 @@ use cargo_mobile::{ use handlebars::Handlebars; use include_dir::{include_dir, Dir}; -use std::{ - ffi::OsStr, - fs, - path::{Path, PathBuf}, -}; +use std::{ffi::OsStr, fs, path::Path}; const TEMPLATE_DIR: Dir<'_> = include_dir!("templates/mobile/android"); -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("failed to run rustup: {0}")] - RustupFailed(bossy::Error), - #[error("failed to process template: {0}")] - TemplateProcessingFailed(String), - #[error("failed to create directory at {path}: {cause}")] - DirectoryCreationFailed { - path: PathBuf, - cause: std::io::Error, - }, - #[error("failed to symlink asset directory")] - AssetDirSymlinkFailed, - #[error("failed to copy {src} to {dest}: {cause}")] - FileCopyFailed { - src: PathBuf, - dest: PathBuf, - cause: std::io::Error, - }, - #[error("asset source {0} is invalid")] - AssetSourceInvalid(PathBuf), -} - pub fn gen( config: &Config, metadata: &Metadata, (handlebars, mut map): (Handlebars, template::JsonMap), wrapper: &TextWrapper, -) -> Result<(), Error> { +) -> Result<()> { println!("Installing Android toolchains..."); - Target::install_all().map_err(Error::RustupFailed)?; + Target::install_all().with_context(|| "failed to run rustup")?; println!("Generating Android Studio project..."); let dest = config.project_dir(); let asset_packs = metadata.asset_packs().unwrap_or_default(); @@ -143,7 +117,7 @@ pub fn gen( options.create_new(true).write(true).open(path) }, ) - .map_err(|e| Error::TemplateProcessingFailed(e.to_string()))?; + .with_context(|| "failed to process template")?; if !asset_packs.is_empty() { Report::action_request( @@ -157,23 +131,27 @@ pub fn gen( let source_src = config.app().root_dir().join(&source); let source_file = source_src .file_name() - .ok_or_else(|| Error::AssetSourceInvalid(source_src.clone()))?; + .ok_or_else(|| anyhow::anyhow!("asset source {} is invalid", source_src.display()))?; fs::copy(&source_src, source_dest.join(source_file)).map_err(|cause| { - Error::FileCopyFailed { - src: source_src, - dest: source_dest.clone(), - cause, - } + anyhow::anyhow!( + "failed to copy {} to {}: {}", + source_src.display(), + source_dest.display(), + cause + ) })?; } let dest = prefix_path(dest, "app/src/main/"); - fs::create_dir_all(&dest).map_err(|cause| Error::DirectoryCreationFailed { - path: dest.clone(), - cause, + fs::create_dir_all(&dest).map_err(|cause| { + anyhow::anyhow!( + "failed to create directory at {}: {}", + dest.display(), + cause + ) })?; os::ln::force_symlink_relative(config.app().asset_dir(), dest, ln::TargetStyle::Directory) - .map_err(|_| Error::AssetDirSymlinkFailed)?; + .map_err(|_| anyhow::anyhow!("failed to symlink asset directory"))?; Ok(()) } diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 13204def507..26838e2b624 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -7,10 +7,8 @@ use crate::helpers::{config::get as get_tauri_config, template::JsonMap}; use crate::Result; use cargo_mobile::{ android::{ - self, config::Config as AndroidConfig, env::Env as AndroidEnv, ndk, - target::Target as AndroidTarget, + config::Config as AndroidConfig, env::Env as AndroidEnv, target::Target as AndroidTarget, }, - bossy, config::app::App, dot_cargo, os::code_command, @@ -23,7 +21,7 @@ use cargo_mobile::{ use clap::Parser; use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError}; -use std::{env::current_dir, fs, io, path::PathBuf}; +use std::{env::current_dir, fs, path::PathBuf}; #[derive(Debug, Parser)] #[clap(about = "Initializes a Tauri Android project")] @@ -41,39 +39,8 @@ pub fn command(mut options: Options, target: Target) -> Result<()> { Ok(()) } -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("invalid tauri configuration: {0}")] - InvalidTauriConfig(String), - #[error("failed to create asset dir {asset_dir}: {cause}")] - AssetDirCreation { - asset_dir: PathBuf, - cause: io::Error, - }, - #[error("failed to install LLDB VS Code extension: {0}")] - LldbExtensionInstall(bossy::Error), - #[error(transparent)] - DotCargoLoad(dot_cargo::LoadError), - #[error(transparent)] - DotCargoGenFailed(ndk::MissingToolError), - #[error(transparent)] - HostTargetTripleDetection(util::HostTargetTripleError), - #[cfg(target_os = "macos")] - #[error(transparent)] - IosInit(super::ios::project::Error), - #[error(transparent)] - AndroidEnv(android::env::Error), - #[error(transparent)] - AndroidInit(super::android::project::Error), - #[error(transparent)] - DotCargoWrite(dot_cargo::WriteError), -} - -pub fn init_dot_cargo( - app: &App, - android: Option<(&AndroidEnv, &AndroidConfig)>, -) -> Result<(), Error> { - let mut dot_cargo = dot_cargo::DotCargo::load(app).map_err(Error::DotCargoLoad)?; +pub fn init_dot_cargo(app: &App, android: Option<(&AndroidEnv, &AndroidConfig)>) -> Result<()> { + let mut dot_cargo = dot_cargo::DotCargo::load(app)?; // Mysteriously, builds that don't specify `--target` seem to fight over // the build cache with builds that use `--target`! This means that // alternating between i.e. `cargo run` and `cargo apple run` would @@ -84,21 +51,18 @@ pub fn init_dot_cargo( // // This behavior could be explained here: // https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags - dot_cargo - .set_default_target(util::host_target_triple().map_err(Error::HostTargetTripleDetection)?); + dot_cargo.set_default_target(util::host_target_triple()?); if let Some((env, config)) = android { for target in AndroidTarget::all().values() { dot_cargo.insert_target( target.triple.to_owned(), - target - .generate_cargo_config(config, env) - .map_err(Error::DotCargoGenFailed)?, + target.generate_cargo_config(config, env)?, ); } } - dot_cargo.write(app).map_err(Error::DotCargoWrite) + dot_cargo.write(app).map_err(Into::into) } pub fn exec( @@ -107,9 +71,8 @@ pub fn exec( non_interactive: bool, skip_dev_tools: bool, #[allow(unused_variables)] reinstall_deps: bool, -) -> Result { - let tauri_config = - get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; +) -> Result { + let tauri_config = get_tauri_config(None)?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); @@ -117,7 +80,12 @@ pub fn exec( let asset_dir = app.asset_dir(); if !asset_dir.is_dir() { - fs::create_dir_all(&asset_dir).map_err(|cause| Error::AssetDirCreation { asset_dir, cause })?; + fs::create_dir_all(&asset_dir).map_err(|cause| { + anyhow::anyhow!( + "failed to create asset dir {path}: {cause}", + path = asset_dir.display() + ) + })?; } if !skip_dev_tools && util::command_present("code").unwrap_or_default() { let mut command = code_command(); @@ -125,9 +93,7 @@ pub fn exec( if non_interactive { command.add_arg("--force"); } - command - .run_and_wait() - .map_err(Error::LldbExtensionInstall)?; + command.run_and_wait()?; } let (handlebars, mut map) = handlebars(&app); @@ -173,8 +139,7 @@ pub fn exec( let (app, config, metadata) = super::android::get_config(Some(app), tauri_config_, &Default::default()); map.insert("android", &config); - super::android::project::gen(&config, &metadata, (handlebars, map), wrapper) - .map_err(Error::AndroidInit)?; + super::android::project::gen(&config, &metadata, (handlebars, map), wrapper)?; init_dot_cargo(&app, Some((&env, &config)))?; app } @@ -188,7 +153,7 @@ pub fn exec( init_dot_cargo(&app, None)?; app } else { - return Err(Error::AndroidEnv(err)); + return Err(err.into()); } } }, @@ -206,8 +171,7 @@ pub fn exec( non_interactive, skip_dev_tools, reinstall_deps, - ) - .map_err(Error::IosInit)?; + )?; init_dot_cargo(&app, None)?; app } diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index e37de65ddef..907bfaf6bc4 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -8,15 +8,15 @@ use cargo_mobile::{ Config as AppleConfig, Metadata as AppleMetadata, Platform as ApplePlatform, Raw as RawAppleConfig, }, - device::{Device, RunError}, + device::Device, ios_deploy, - target::{CompileLibError, Target}, + target::Target, }, config::app::App, device::PromptError, - env::{Env, Error as EnvError}, + env::Env, opts::NoiseLevel, - os, util, + os, util::prompt, }; use clap::{Parser, Subcommand}; @@ -31,50 +31,12 @@ use crate::{ Result, }; -use std::path::PathBuf; - mod build; mod dev; mod open; pub(crate) mod project; mod xcode_script; -#[derive(Debug, thiserror::Error)] -enum Error { - #[error(transparent)] - EnvInitFailed(#[from] EnvError), - #[error(transparent)] - InitDotCargo(super::init::Error), - #[error("invalid tauri configuration: {0}")] - InvalidTauriConfig(String), - #[error("{0}")] - ProjectNotInitialized(String), - #[error(transparent)] - OpenFailed(os::OpenFileError), - #[error("{0}")] - DevFailed(String), - #[error("{0}")] - BuildFailed(String), - #[error(transparent)] - NoHomeDir(util::NoHomeDir), - #[error("SDK root provided by Xcode was invalid. {sdk_root} doesn't exist or isn't a directory")] - SdkRootInvalid { sdk_root: PathBuf }, - #[error("Include dir was invalid. {include_dir} doesn't exist or isn't a directory")] - IncludeDirInvalid { include_dir: PathBuf }, - #[error("macOS SDK root was invalid. {macos_sdk_root} doesn't exist or isn't a directory")] - MacosSdkRootInvalid { macos_sdk_root: PathBuf }, - #[error("Arch specified by Xcode was invalid. {arch} isn't a known arch")] - ArchInvalid { arch: String }, - #[error(transparent)] - CompileLibFailed(CompileLibError), - #[error(transparent)] - FailedToPromptForDevice(PromptError), - #[error(transparent)] - RunFailed(RunError), - #[error("{0}")] - TargetInvalid(String), -} - #[derive(Parser)] #[clap( author, @@ -146,11 +108,10 @@ pub fn get_config( fn with_config( cli_options: Option, - f: impl FnOnce(&App, &AppleConfig, &AppleMetadata, CliOptions) -> Result, -) -> Result { + f: impl FnOnce(&App, &AppleConfig, &AppleMetadata, CliOptions) -> Result, +) -> Result { let (app, config, metadata, cli_options) = { - let tauri_config = - get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config = get_tauri_config(None)?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); let cli_options = cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Ios)); diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index be6a2143701..9f771eed969 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -1,6 +1,6 @@ use super::{ detect_target_ok, ensure_init, env, init_dot_cargo, log_finished, open_and_wait, with_config, - Error, MobileTarget, + MobileTarget, }; use crate::{ helpers::{config::get as get_tauri_config, flock}, @@ -67,15 +67,13 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { with_config( Some(Default::default()), |app, config, _metadata, _cli_options| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + ensure_init(config.project_dir(), MobileTarget::Ios)?; let env = env()?; - init_dot_cargo(app, None).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, None)?; let open = options.open; - run_build(options, config, &env, noise_level) - .map_err(|e| Error::BuildFailed(format!("{:#}", e)))?; + run_build(options, config, &env, noise_level)?; if open { open_and_wait(config, &env); @@ -157,7 +155,7 @@ fn run_build( anyhow::Result::Ok(()) }, ) - .map_err(|e: TargetInvalid| Error::TargetInvalid(e.to_string()))? + .map_err(|e: TargetInvalid| anyhow::anyhow!(e.to_string()))? .map_err(|e: anyhow::Error| e)?; log_finished(out_files, "IPA"); diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index c1e2621982a..3f8001de1d2 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -1,5 +1,5 @@ use super::{ - device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, Error, MobileTarget, + device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, MobileTarget, }; use crate::{ helpers::{config::get as get_tauri_config, flock}, @@ -10,8 +10,9 @@ use crate::{ use clap::Parser; use cargo_mobile::{ - apple::config::Config as AppleConfig, + apple::{config::Config as AppleConfig, device::RunError as DeviceRunError, ios_deploy}, config::app::App, + device::PromptError, env::Env, opts::{NoiseLevel, Profile}, }; @@ -58,12 +59,10 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { with_config( Some(Default::default()), |app, config, _metadata, _cli_options| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; - run_dev(options, app, config, noise_level).map_err(|e| Error::DevFailed(format!("{:#}", e))) + ensure_init(config.project_dir(), MobileTarget::Ios)?; + run_dev(options, app, config, noise_level).map_err(Into::into) }, ) - .map_err(Into::into) } fn run_dev( @@ -76,8 +75,7 @@ fn run_dev( let mut interface = crate::dev::setup(&mut dev_options)?; let bundle_identifier = { - let tauri_config = - get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?; + let tauri_config = get_tauri_config(None)?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); tauri_config_.tauri.bundle.identifier.clone() @@ -92,7 +90,7 @@ fn run_dev( let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; let env = env()?; - init_dot_cargo(app, None).map_err(Error::InitDotCargo)?; + init_dot_cargo(app, None)?; let open = options.open; let exit_on_panic = options.exit_on_panic; @@ -124,7 +122,7 @@ fn run_dev( }); Ok(Box::new(c) as Box) } - Err(Error::FailedToPromptForDevice(e)) => { + Err(RunError::FailedToPromptForDevice(e)) => { log::error!("{}", e); open_and_wait(config, &env) } @@ -135,12 +133,19 @@ fn run_dev( ) } +#[derive(Debug, thiserror::Error)] +enum RunError { + #[error(transparent)] + FailedToPromptForDevice(PromptError), + #[error(transparent)] + RunFailed(DeviceRunError), +} fn run( options: MobileOptions, config: &AppleConfig, env: &Env, noise_level: NoiseLevel, -) -> Result { +) -> Result { let profile = if options.debug { Profile::Debug } else { @@ -150,8 +155,8 @@ fn run( let non_interactive = true; // ios-deploy --noninteractive (quit when app crashes or exits) device_prompt(env) - .map_err(Error::FailedToPromptForDevice)? + .map_err(RunError::FailedToPromptForDevice)? .run(config, env, noise_level, non_interactive, profile) .map(DevChild::new) - .map_err(Error::RunFailed) + .map_err(RunError::RunFailed) } diff --git a/tooling/cli/src/mobile/ios/open.rs b/tooling/cli/src/mobile/ios/open.rs index 0304516ddc7..9d3f52b4232 100644 --- a/tooling/cli/src/mobile/ios/open.rs +++ b/tooling/cli/src/mobile/ios/open.rs @@ -1,4 +1,4 @@ -use super::{ensure_init, env, with_config, Error, MobileTarget}; +use super::{ensure_init, env, with_config, MobileTarget}; use crate::Result; use cargo_mobile::os; @@ -6,11 +6,9 @@ pub fn command() -> Result<()> { with_config( Some(Default::default()), |_root_conf, config, _metadata, _cli_options| { - ensure_init(config.project_dir(), MobileTarget::Ios) - .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?; + ensure_init(config.project_dir(), MobileTarget::Ios)?; let env = env()?; - os::open_file_with("Xcode", config.project_dir(), &env).map_err(Error::OpenFailed) + os::open_file_with("Xcode", config.project_dir(), &env).map_err(Into::into) }, ) - .map_err(Into::into) } diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index a0e5a8f35f5..e1ae7213ce6 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::helpers::template; +use crate::{helpers::template, Result}; +use anyhow::Context; use cargo_mobile::{ apple::{ config::{Config, Metadata}, @@ -23,29 +24,6 @@ use std::{ const TEMPLATE_DIR: Dir<'_> = include_dir!("templates/mobile/ios"); -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error(transparent)] - Rustup(bossy::Error), - #[error(transparent)] - RustVersionCheck(util::RustVersionError), - #[error("failed to install Apple dependencies: {0}")] - DepsInstall(deps::Error), - #[error("failed to process template: {0}")] - TemplateProcessing(String), - #[error("failed to symlink asset directory")] - AssetDirSymlink, - #[error("failed to create directory at {path}: {cause}")] - DirectoryCreation { - path: PathBuf, - cause: std::io::Error, - }, - #[error("failed to run `xcodegen`: {0}")] - Xcodegen(bossy::Error), - #[error("failed to run `pod install`: {0}")] - PodInstall(bossy::Error), -} - // unprefixed app_root seems pretty dangerous!! // TODO: figure out what cargo-mobile meant by that pub fn gen( @@ -56,13 +34,13 @@ pub fn gen( non_interactive: bool, skip_dev_tools: bool, reinstall_deps: bool, -) -> Result<(), Error> { +) -> Result<()> { println!("Installing iOS toolchains..."); - Target::install_all().map_err(Error::Rustup)?; - rust_version_check(wrapper).map_err(Error::RustVersionCheck)?; + Target::install_all()?; + rust_version_check(wrapper)?; deps::install_all(wrapper, non_interactive, skip_dev_tools, reinstall_deps) - .map_err(Error::DepsInstall)?; + .with_context(|| "failed to install Apple dependencies")?; let dest = config.project_dir(); let rel_prefix = util::relativize_path(config.app().root_dir(), &dest); @@ -161,16 +139,18 @@ pub fn gen( File::create(path) }, ) - .map_err(|e| Error::TemplateProcessing(e.to_string()))?; + .with_context(|| "failed to process template")?; ln::force_symlink_relative(config.app().asset_dir(), &dest, ln::TargetStyle::Directory) - .map_err(|_| Error::AssetDirSymlink)?; + .map_err(|_| anyhow::anyhow!("failed to symlink asset directory"))?; // Create all asset catalog directories if they don't already exist for dir in asset_catalogs { - std::fs::create_dir_all(dir).map_err(|cause| Error::DirectoryCreation { - path: dest.clone(), - cause, + std::fs::create_dir_all(dir).map_err(|cause| { + anyhow::anyhow!( + "failed to create directory at {path}: {cause}", + path = dir.display() + ) })?; } @@ -181,13 +161,13 @@ pub fn gen( .with_args(&["generate", "--spec"]) .with_arg(dest.join("project.yml")) .run_and_wait() - .map_err(Error::Xcodegen)?; + .with_context(|| "failed to run `xcodegen`")?; if !ios_pods.is_empty() || !macos_pods.is_empty() { bossy::Command::impure_parse("pod install") .with_arg(format!("--project-directory={}", dest.display())) .run_and_wait() - .map_err(Error::PodInstall)?; + .with_context(|| "failed to run `pod install`")?; } Ok(()) } diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 4ff2e5bffe5..870edf52da8 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -1,4 +1,4 @@ -use super::{env, init_dot_cargo, with_config, Error}; +use super::{env, init_dot_cargo, with_config}; use crate::Result; use clap::Parser; @@ -43,24 +43,24 @@ pub fn command(options: Options) -> Result<()> { with_config(None, |root_conf, config, metadata, cli_options| { let env = env()?; - init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?; + init_dot_cargo(root_conf, None)?; // The `PATH` env var Xcode gives us is missing any additions // made by the user's profile, so we'll manually add cargo's // `PATH`. - let env = env.prepend_to_path( - util::home_dir() - .map_err(Error::NoHomeDir)? - .join(".cargo/bin"), - ); + let env = env.prepend_to_path(util::home_dir()?.join(".cargo/bin")); if !options.sdk_root.is_dir() { - return Err(Error::SdkRootInvalid { - sdk_root: options.sdk_root, - }); + return Err(anyhow::anyhow!( + "SDK root provided by Xcode was invalid. {} doesn't exist or isn't a directory", + options.sdk_root.display(), + )); } let include_dir = options.sdk_root.join("usr/include"); if !include_dir.is_dir() { - return Err(Error::IncludeDirInvalid { include_dir }); + return Err(anyhow::anyhow!( + "Include dir was invalid. {} doesn't exist or isn't a directory", + include_dir.display() + )); } let mut host_env = HashMap::<&str, &OsStr>::new(); @@ -71,7 +71,10 @@ pub fn command(options: Options) -> Result<()> { .sdk_root .join("../../../../MacOSX.platform/Developer/SDKs/MacOSX.sdk"); if !macos_sdk_root.is_dir() { - return Err(Error::MacosSdkRootInvalid { macos_sdk_root }); + return Err(anyhow::anyhow!( + "macOS SDK root was invalid. {0} doesn't exist or isn't a directory", + macos_sdk_root.display() + )); } ( format!("-isysroot {}", macos_sdk_root.display()), @@ -98,7 +101,12 @@ pub fn command(options: Options) -> Result<()> { let triple = match arch.as_str() { "arm64" => "aarch64_apple_ios", "x86_64" => "x86_64_apple_ios", - _ => return Err(Error::ArchInvalid { arch }), + _ => { + return Err(anyhow::anyhow!( + "Arch specified by Xcode was invalid. {} isn't a known arch", + arch + )) + } }; let cflags = format!("CFLAGS_{}", triple); let cxxflags = format!("CFLAGS_{}", triple); @@ -114,21 +122,22 @@ pub fn command(options: Options) -> Result<()> { let target = if macos { &macos_target } else { - Target::for_arch(&arch).ok_or_else(|| Error::ArchInvalid { - arch: arch.to_owned(), + Target::for_arch(&arch).ok_or_else(|| { + anyhow::anyhow!( + "Arch specified by Xcode was invalid. {} isn't a known arch", + arch + ) })? }; - target - .compile_lib( - config, - metadata, - cli_options.noise_level, - true, - profile, - &env, - target_env, - ) - .map_err(Error::CompileLibFailed)?; + target.compile_lib( + config, + metadata, + cli_options.noise_level, + true, + profile, + &env, + target_env, + )?; } Ok(()) }) From 1d9226b28cc06f670ca248e3fcff9e9ffcc11a2b Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 28 Aug 2022 15:34:41 -0300 Subject: [PATCH 040/423] refactor:move iOS configuration to the bundle object (#5072) --- core/tauri-utils/src/config.rs | 16 +++++------ core/tauri/src/test/mod.rs | 1 - tooling/cli/schema.json | 49 +++++++++++++++++----------------- tooling/cli/src/mobile/ios.rs | 2 +- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 36298eb600d..6f0c13fd768 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -594,6 +594,9 @@ pub struct BundleConfig { /// Configuration for the Windows bundle. #[serde(default)] pub windows: WindowsConfig, + /// iOS configuration. + #[serde(rename = "iOS", default)] + pub ios: IosConfig, } /// A CLI argument definition. @@ -2085,9 +2088,6 @@ pub struct TauriConfig { /// MacOS private API configuration. Enables the transparent background API and sets the `fullScreenEnabled` preference to `true`. #[serde(rename = "macOSPrivateApi", alias = "macos-private-api", default)] pub macos_private_api: bool, - /// iOS configuration. - #[serde(rename = "iOS", default)] - pub ios: IosConfig, } impl TauriConfig { @@ -3120,6 +3120,7 @@ mod build { let macos = quote!(Default::default()); let external_bin = opt_vec_str_lit(self.external_bin.as_ref()); let windows = &self.windows; + let ios = quote!(Default::default()); literal_struct!( tokens, @@ -3137,7 +3138,8 @@ mod build { deb, macos, external_bin, - windows + windows, + ios ); } } @@ -3458,7 +3460,6 @@ mod build { let system_tray = opt_lit(self.system_tray.as_ref()); let allowlist = &self.allowlist; let macos_private_api = self.macos_private_api; - let ios = quote!(Default::default()); literal_struct!( tokens, @@ -3471,8 +3472,7 @@ mod build { security, system_tray, allowlist, - macos_private_api, - ios + macos_private_api ); } } @@ -3552,6 +3552,7 @@ mod test { macos: Default::default(), external_bin: None, windows: Default::default(), + ios: Default::default(), }, cli: None, updater: UpdaterConfig { @@ -3570,7 +3571,6 @@ mod test { allowlist: AllowlistConfig::default(), system_tray: None, macos_private_api: false, - ios: Default::default(), }; // create a build config diff --git a/core/tauri/src/test/mod.rs b/core/tauri/src/test/mod.rs index 9f4357b36ef..bf0bb33e074 100644 --- a/core/tauri/src/test/mod.rs +++ b/core/tauri/src/test/mod.rs @@ -61,7 +61,6 @@ pub fn mock_context(assets: A) -> crate::Context { updater: Default::default(), system_tray: None, macos_private_api: false, - ios: Default::default(), }, build: Default::default(), plugins: Default::default(), diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index 6322c3c7c6c..141dba7de1a 100644 --- a/tooling/cli/schema.json +++ b/tooling/cli/schema.json @@ -124,6 +124,7 @@ "deb": { "files": {} }, + "iOS": {}, "icon": [], "identifier": "", "macOS": { @@ -144,7 +145,6 @@ "wix": null } }, - "iOS": {}, "macOSPrivateApi": false, "pattern": { "use": "brownfield" @@ -260,6 +260,7 @@ "deb": { "files": {} }, + "iOS": {}, "icon": [], "identifier": "", "macOS": { @@ -427,15 +428,6 @@ "description": "MacOS private API configuration. Enables the transparent background API and sets the `fullScreenEnabled` preference to `true`.", "default": false, "type": "boolean" - }, - "iOS": { - "description": "iOS configuration.", - "default": {}, - "allOf": [ - { - "$ref": "#/definitions/IosConfig" - } - ] } }, "additionalProperties": false @@ -1042,6 +1034,15 @@ "$ref": "#/definitions/WindowsConfig" } ] + }, + "iOS": { + "description": "iOS configuration.", + "default": {}, + "allOf": [ + { + "$ref": "#/definitions/IosConfig" + } + ] } }, "additionalProperties": false @@ -1493,6 +1494,20 @@ }, "additionalProperties": false }, + "IosConfig": { + "description": "General configuration for the iOS target.", + "type": "object", + "properties": { + "developmentTeam": { + "description": "The development team. This value is required for iOS development because code signing is enforced. The `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, "AllowlistConfig": { "description": "Allowlist configuration.", "type": "object", @@ -2428,20 +2443,6 @@ }, "additionalProperties": false }, - "IosConfig": { - "description": "General configuration for the iOS target.", - "type": "object", - "properties": { - "developmentTeam": { - "description": "The development team. This value is required for iOS development because code signing is enforced. The `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable can be set to overwrite it.", - "type": [ - "string", - "null" - ] - } - }, - "additionalProperties": false - }, "BuildConfig": { "description": "The Build configuration object.", "type": "object", diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 907bfaf6bc4..f9c8d3cc154 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -84,7 +84,7 @@ pub fn get_config( let raw = RawAppleConfig { development_team: std::env::var("TAURI_APPLE_DEVELOPMENT_TEAM") .ok() - .or_else(|| config.tauri.ios.development_team.clone()) + .or_else(|| config.tauri.bundle.ios.development_team.clone()) .expect("you must set `tauri > iOS > developmentTeam` config value or the `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable"), ios_features: ios_options.features.clone(), bundle_version: config.package.version.clone(), From c4d323b70fa9fe02098be76ef7e8728b01a4d7b4 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 28 Aug 2022 17:50:05 -0300 Subject: [PATCH 041/423] fix(ci): set target on test-core.yml --- .github/workflows/test-core.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index a89eb8690a5..5735c3bb9c8 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -101,6 +101,6 @@ jobs: - name: test run: | - cargo test - cargo test --features api-all - cargo test --features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart + cargo test --target ${{ matrix.platform.target }} + cargo test --target ${{ matrix.platform.target }} --features api-all + cargo test --target ${{ matrix.platform.target }} --features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart From 65e7085d2f6aecbe69500a9d9fa3f1e9bb186dc1 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 28 Aug 2022 17:52:40 -0300 Subject: [PATCH 042/423] fix(ci): checkout repo in udeps.yml --- .github/workflows/udeps.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/udeps.yml b/.github/workflows/udeps.yml index 8a98eafe620..1bd56bcc983 100644 --- a/.github/workflows/udeps.yml +++ b/.github/workflows/udeps.yml @@ -33,6 +33,7 @@ jobs: bundler: ${{ steps.filter.outputs.bundler }} cli: ${{ steps.filter.outputs.cli }} steps: + - uses: actions/checkout@v2 - uses: dorny/paths-filter@v2 id: filter with: From b36ccb7e991997efe45ec68e6c264974c4018066 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 28 Aug 2022 23:02:32 -0300 Subject: [PATCH 043/423] feat(ci): test mobile targets (#5078) --- .github/workflows/test-core.yml | 61 +++++++++++++++++++++++--- Cargo.toml | 1 - core/tauri-codegen/Cargo.toml | 4 +- core/tauri-codegen/src/context.rs | 1 + core/tests/app-updater/tests/update.rs | 7 ++- 5 files changed, 64 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index 5735c3bb9c8..855de09eeb5 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -18,6 +18,9 @@ env: RUST_BACKTRACE: 1 CARGO_INCREMENTAL: 0 # This is set to 0 by the https://github.com/Swatinem/rust-cache CARGO_PROFILE_DEV_DEBUG: 0 # This would add unnecessary bloat to the target folder, decreasing cache efficiency. + WRY_ANDROID_REVERSED_DOMAIN: 'app.tauri' + WRY_ANDROID_APP_NAME_SNAKE_CASE: 'dev' + WRY_ANDROID_KOTLIN_FILES_OUT_DIR: 'out' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -34,17 +37,37 @@ jobs: - { target: x86_64-pc-windows-msvc, os: windows-latest, - toolchain: '1.61.0' + toolchain: '1.61.0', + cross: false, + command: 'test' } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, - toolchain: '1.57.0' + toolchain: '1.57.0', + cross: false, + command: 'test' } - { target: x86_64-apple-darwin, os: macos-latest, - toolchain: '1.57.0' + toolchain: '1.57.0', + cross: false, + command: 'test' + } + - { + target: aarch64-apple-ios, + os: macos-latest, + toolchain: '1.57.0', + cross: false, + acommand: 'build' + } + - { + target: aarch64-linux-android, + os: ubuntu-latest, + toolchain: '1.57.0', + cross: true, + command: 'build' } steps: @@ -54,6 +77,7 @@ jobs: with: toolchain: ${{ matrix.platform.toolchain }} target: ${{ matrix.platform.target }} + override: true - name: install Linux dependencies if: contains(matrix.platform.target, 'unknown-linux') run: | @@ -99,8 +123,31 @@ jobs: ${{ matrix.platform.os }}-${{ matrix.platform.toolchain }}- ${{ matrix.platform.os }}- - - name: test + - name: create kotlin out dir + if: contains(matrix.platform.target, 'android') + run: mkdir out + + - name: pin time run: | - cargo test --target ${{ matrix.platform.target }} - cargo test --target ${{ matrix.platform.target }} --features api-all - cargo test --target ${{ matrix.platform.target }} --features compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart + cargo update -p time --precise 0.3.13 + + - name: test + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ matrix.platform.cross }} + command: ${{ matrix.platform.command }} + args: --target ${{ matrix.platform.target }} --features native-tls-vendored + + - name: test api-all + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ matrix.platform.cross }} + command: ${{ matrix.platform.command }} + args: --target ${{ matrix.platform.target }} --features native-tls-vendored,api-all + + - name: test all features + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ matrix.platform.cross }} + command: ${{ matrix.platform.command }} + args: --target ${{ matrix.platform.target }} --features native-tls-vendored,compression,wry,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart diff --git a/Cargo.toml b/Cargo.toml index 4d739f1489f..53de27cd06d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ exclude = [ # default to small, optimized workspace release binaries [profile.release] -strip = true panic = "abort" codegen-units = 1 lto = true diff --git a/core/tauri-codegen/Cargo.toml b/core/tauri-codegen/Cargo.toml index 0c90dff3ef8..f8c17181d0f 100644 --- a/core/tauri-codegen/Cargo.toml +++ b/core/tauri-codegen/Cargo.toml @@ -29,9 +29,11 @@ semver = "1" ico = "0.1" png = "0.17" json-patch = "0.2" -local-ip-address = "0.4" url = "2" +[target."cfg(any(windows, target_os = \"linux\", target_os = \"macos\"))".dependencies] +local-ip-address = "0.4" + [target."cfg(target_os = \"macos\")".dependencies] plist = "1" time = { version = "0.3", features = [ "parsing", "formatting" ] } diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index 977c41513ad..b454b4e39d0 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -155,6 +155,7 @@ pub fn context_codegen(data: ContextData) -> Result PathBuf { #[cfg(target_os = "ios")] fn bundle_path(root_dir: &Path, _version: &str) -> PathBuf { - root_dir.join(format!("target/debug/bundle/ios/app-updater.app")) + root_dir.join(format!("target/debug/bundle/ios/app-updater.ipa")) +} + +#[cfg(target_os = "android")] +fn bundle_path(root_dir: &Path, _version: &str) -> PathBuf { + root_dir.join(format!("target/debug/bundle/android/app-updater.apk")) } #[cfg(windows)] From e22d21beaf2ec20ac8e8a6def2c2c45f8f98c436 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 29 Aug 2022 14:46:37 -0300 Subject: [PATCH 044/423] fix(cli): add timeout on interprocess communication (#5090) --- tooling/cli/src/mobile/mod.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 9ad44e0428a..5da42b40b11 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -179,14 +179,24 @@ fn read_options(config: &TauriConfig, target: Target) -> CliOptions { .set_nonblocking(true) .expect("failed to set local socket stream to nonblocking"); let mut conn = BufReader::new(conn); - let mut buffer = String::new(); - conn.read_line(&mut buffer).unwrap_or_else(|_| { - log::error!( + + let mut attempt = 0; + let max_tries = 5; + let buffer = loop { + let mut buffer = String::new(); + if conn.read_line(&mut buffer).is_ok() { + break buffer; + } + std::thread::sleep(std::time::Duration::from_secs(1)); + attempt += 1; + if attempt == max_tries { + log::error!( "failed to connect to local socket. You must keep the Tauri CLI alive with the `{cmd} dev` or `{cmd} build --open` commands.", cmd = target.command_name() ); - std::process::exit(1); - }); + std::process::exit(1); + } + }; let options: CliOptions = serde_json::from_str(&buffer).expect("invalid CLI options"); for (k, v) in &options.vars { set_var(k, v); From 82e8751ae8ad93d1ccae75e0ef474403ed6fc8fa Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Tue, 30 Aug 2022 10:27:53 -0300 Subject: [PATCH 045/423] feat(cli): add option to run on specific Android emulator/device (#5093) --- tooling/cli/Cargo.lock | 2 +- tooling/cli/Cargo.toml | 2 +- tooling/cli/src/mobile/android.rs | 99 +++++++++++++++++++++++---- tooling/cli/src/mobile/android/dev.rs | 41 ++++++++++- 4 files changed, 127 insertions(+), 17 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 3fe7a070c3c..20e2d86e578 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -293,7 +293,7 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#228f0eb83ccbc7bfeb0103d2f68b6664691a289d" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#909edf9337ec0cb42c76e0a34af61e99b15ccfe0" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index f10c14a6cee..a7c40d28ccd 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -28,7 +28,7 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -# cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } +#cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } textwrap = { version = "0.11.0", features = ["term_size"] } interprocess = "1" diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 6c1b1779229..a65ee2995a8 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -7,6 +7,7 @@ use cargo_mobile::{ adb, config::{Config as AndroidConfig, Metadata as AndroidMetadata, Raw as RawAndroidConfig}, device::Device, + emulator, env::Env, target::Target, }, @@ -17,7 +18,11 @@ use cargo_mobile::{ util::prompt, }; use clap::{Parser, Subcommand}; -use std::env::set_var; +use std::{ + env::set_var, + thread::{sleep, spawn}, + time::Duration, +}; use super::{ ensure_init, get_app, @@ -136,19 +141,30 @@ fn delete_codegen_vars() { } } -fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { +fn adb_device_prompt<'a>( + env: &'_ Env, + target: Option<&str>, +) -> Result, PromptError> { let device_list = adb::device_list(env).map_err(|cause| PromptError::detection_failed("Android", cause))?; if !device_list.is_empty() { let index = if device_list.len() > 1 { - prompt::list( - concat!("Detected ", "Android", " devices"), - device_list.iter(), - "device", - None, - "Device", - ) - .map_err(|cause| PromptError::prompt_failed("Android", cause))? + if let Some(t) = target { + let t = t.to_lowercase(); + device_list + .iter() + .position(|d| d.name().to_lowercase().starts_with(&t)) + .unwrap_or_default() + } else { + prompt::list( + concat!("Detected ", "Android", " devices"), + device_list.iter(), + "device", + None, + "Device", + ) + .map_err(|cause| PromptError::prompt_failed("Android", cause))? + } } else { 0 }; @@ -164,8 +180,67 @@ fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError, +) -> Result> { + let emulator_list = emulator::avd_list(env).unwrap_or_default(); + if emulator_list.is_empty() { + Err(PromptError::none_detected("Android emulator")) + } else { + let index = if emulator_list.len() > 1 { + if let Some(t) = target { + let t = t.to_lowercase(); + emulator_list + .iter() + .position(|d| d.name().to_lowercase().starts_with(&t)) + .unwrap_or_default() + } else { + prompt::list( + concat!("Detected ", "Android", " emulators"), + emulator_list.iter(), + "emulator", + None, + "Emulator", + ) + .map_err(|cause| PromptError::prompt_failed("Android emulator", cause))? + } + } else { + 0 + }; + + Ok(emulator_list.iter().nth(index).cloned().unwrap()) + } +} + +fn device_prompt<'a>( + env: &'_ Env, + target: Option<&str>, +) -> Result, PromptError> { + if let Ok(device) = adb_device_prompt(env, target) { + Ok(device) + } else { + let emulator = emulator_prompt(env, target)?; + let handle = emulator.start(env).map_err(|e| { + PromptError::prompt_failed( + "Android emulator", + std::io::Error::new(std::io::ErrorKind::Other, e.to_string()), + ) + })?; + spawn(move || { + let _ = handle.wait(); + }); + loop { + sleep(Duration::from_secs(2)); + if let Ok(device) = adb_device_prompt(env, Some(emulator.name())) { + return Ok(device); + } + } + } +} + fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { - device_prompt(env).map(|device| device.target()).ok() + device_prompt(env, None).map(|device| device.target()).ok() } fn open_and_wait(config: &AndroidConfig, env: &Env) -> ! { @@ -174,6 +249,6 @@ fn open_and_wait(config: &AndroidConfig, env: &Env) -> ! { log::error!("{}", e); } loop { - std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); + sleep(Duration::from_secs(24 * 60 * 60)); } } diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index d308886efee..52fb6a2c035 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -15,13 +15,18 @@ use cargo_mobile::{ adb, config::{Config as AndroidConfig, Metadata as AndroidMetadata}, device::RunError as DeviceRunError, + emulator, env::Env, }, config::app::App, opts::{NoiseLevel, Profile}, }; -use std::env::set_var; +use std::{ + env::set_var, + thread::{sleep, spawn}, + time::Duration, +}; const WEBVIEW_CLIENT_CLASS_EXTENSION: &str = " @android.annotation.SuppressLint(\"WebViewClientOnReceivedSslError\") @@ -50,6 +55,8 @@ pub struct Options { /// Open Android Studio instead of trying to run on a connected device #[clap(short, long)] pub open: bool, + /// Runs on the given device name + pub device: Option, } impl From for crate::dev::Options { @@ -112,9 +119,29 @@ fn run_dev( let env = env()?; init_dot_cargo(app, Some((&env, config)))?; + if let Some(device) = &options.device { + let emulators = emulator::avd_list(&env).unwrap_or_default(); + for emulator in emulators { + if emulator + .name() + .to_lowercase() + .starts_with(&device.to_lowercase()) + { + log::info!("Starting emulator {}", emulator.name()); + let handle = emulator.start(&env)?; + spawn(move || { + let _ = handle.wait(); + }); + sleep(Duration::from_secs(3)); + break; + } + } + } + let open = options.open; let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; + let device = options.device; interface.mobile_dev( MobileOptions { debug: true, @@ -135,7 +162,14 @@ fn run_dev( if open { open_and_wait(config, &env) } else { - match run(options, config, &env, metadata, noise_level) { + match run( + device.as_deref(), + options, + config, + &env, + metadata, + noise_level, + ) { Ok(c) => { crate::dev::wait_dev_process(c.clone(), move |status, reason| { crate::dev::on_app_exit(status, reason, exit_on_panic, no_watch) @@ -162,6 +196,7 @@ enum RunError { } fn run( + device: Option<&str>, options: MobileOptions, config: &AndroidConfig, env: &Env, @@ -176,7 +211,7 @@ fn run( let build_app_bundle = metadata.asset_packs().is_some(); - device_prompt(env) + device_prompt(env, device) .map_err(RunError::FailedToPromptForDevice)? .run( config, From 9f9e3ae54d084dd50b5efffba7c43f28ef811905 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 30 Aug 2022 13:22:26 -0300 Subject: [PATCH 046/423] fix: improve target check on context codegen --- core/tauri-build/src/lib.rs | 3 +++ core/tauri-codegen/src/context.rs | 29 +++++++++-------------------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 0841dfcebab..ac0d7bf1ae6 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -286,6 +286,9 @@ pub fn try_build(attributes: Attributes) -> Result<()> { } let target_triple = std::env::var("TARGET").unwrap(); + + println!("cargo:rustc-env=TAURI_TARGET_TRIPLE={}", target_triple); + let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); // TODO: far from ideal, but there's no other way to get the target dir, see let target_dir = out_dir diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index b454b4e39d0..1faf2899bdc 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -127,32 +127,21 @@ pub fn context_codegen(data: ContextData) -> Result Date: Tue, 30 Aug 2022 16:09:06 -0300 Subject: [PATCH 047/423] feat(cli): add option to run on specific iOS simulator/device (#5098) --- tooling/cli/Cargo.lock | 2 +- tooling/cli/Cargo.toml | 2 +- tooling/cli/src/mobile/android.rs | 2 +- tooling/cli/src/mobile/ios.rs | 87 +++++++++++++++++--- tooling/cli/src/mobile/ios/dev.rs | 41 +++++++-- tooling/cli/src/mobile/ios/project.rs | 6 +- tooling/cli/src/mobile/ios/xcode_script.rs | 1 + tooling/cli/templates/mobile/ios/project.yml | 2 +- 8 files changed, 119 insertions(+), 24 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 20e2d86e578..2d0cec6b2bc 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -293,7 +293,7 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#909edf9337ec0cb42c76e0a34af61e99b15ccfe0" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#ff88cc91fbf008ee0b9b33c8e4bcb5c9b5b743e6" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index a7c40d28ccd..f10c14a6cee 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -28,7 +28,7 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -#cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } +# cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } textwrap = { version = "0.11.0", features = ["term_size"] } interprocess = "1" diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index a65ee2995a8..9b6a0063283 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -209,7 +209,7 @@ fn emulator_prompt( 0 }; - Ok(emulator_list.iter().nth(index).cloned().unwrap()) + Ok(emulator_list.into_iter().nth(index).unwrap()) } } diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index f9c8d3cc154..0cc0e1a712f 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -9,7 +9,7 @@ use cargo_mobile::{ Raw as RawAppleConfig, }, device::Device, - ios_deploy, + ios_deploy, simctl, target::Target, }, config::app::App, @@ -31,6 +31,11 @@ use crate::{ Result, }; +use std::{ + thread::{sleep, spawn}, + time::Duration, +}; + mod build; mod dev; mod open; @@ -121,19 +126,30 @@ fn with_config( f(&app, &config, &metadata, cli_options) } -fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { +fn ios_deploy_device_prompt<'a>( + env: &'_ Env, + target: Option<&str>, +) -> Result, PromptError> { let device_list = ios_deploy::device_list(env).map_err(|cause| PromptError::detection_failed("iOS", cause))?; if !device_list.is_empty() { let index = if device_list.len() > 1 { - prompt::list( - concat!("Detected ", "iOS", " devices"), - device_list.iter(), - "device", - None, - "Device", - ) - .map_err(|cause| PromptError::prompt_failed("iOS", cause))? + if let Some(t) = target { + let t = t.to_lowercase(); + device_list + .iter() + .position(|d| d.name().to_lowercase().starts_with(&t)) + .unwrap_or_default() + } else { + prompt::list( + concat!("Detected ", "iOS", " devices"), + device_list.iter(), + "device", + None, + "Device", + ) + .map_err(|cause| PromptError::prompt_failed("iOS", cause))? + } } else { 0 }; @@ -149,8 +165,55 @@ fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError, +) -> Result> { + let simulator_list = simctl::device_list(env) + .map_err(|cause| PromptError::detection_failed("iOS Simulator", cause))?; + if !simulator_list.is_empty() { + let index = if simulator_list.len() > 1 { + if let Some(t) = target { + let t = t.to_lowercase(); + simulator_list + .iter() + .position(|d| d.name().to_lowercase().starts_with(&t)) + .unwrap_or_default() + } else { + prompt::list( + concat!("Detected ", "iOS", " simulators"), + simulator_list.iter(), + "simulator", + None, + "Simulator", + ) + .map_err(|cause| PromptError::prompt_failed("iOS Simulator", cause))? + } + } else { + 0 + }; + let device = simulator_list.into_iter().nth(index).unwrap(); + Ok(device) + } else { + Err(PromptError::none_detected("iOS Simulator")) + } +} + +fn device_prompt<'a>(env: &'_ Env, target: Option<&str>) -> Result> { + if let Ok(device) = ios_deploy_device_prompt(env, target) { + Ok(device) + } else { + let simulator = simulator_prompt(env, target)?; + let handle = simulator.start(env)?; + spawn(move || { + let _ = handle.wait(); + }); + Ok(simulator.into()) + } +} + fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { - device_prompt(env).map(|device| device.target()).ok() + device_prompt(env, None).map(|device| device.target()).ok() } fn open_and_wait(config: &AppleConfig, env: &Env) -> ! { @@ -159,6 +222,6 @@ fn open_and_wait(config: &AppleConfig, env: &Env) -> ! { log::error!("{}", e); } loop { - std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); + sleep(Duration::from_secs(24 * 60 * 60)); } } diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 3f8001de1d2..4ab79826c71 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -10,13 +10,17 @@ use crate::{ use clap::Parser; use cargo_mobile::{ - apple::{config::Config as AppleConfig, device::RunError as DeviceRunError, ios_deploy}, + apple::{config::Config as AppleConfig, device::RunError as DeviceRunError, simctl}, config::app::App, - device::PromptError, env::Env, opts::{NoiseLevel, Profile}, }; +use std::{ + thread::{sleep, spawn}, + time::Duration, +}; + #[derive(Debug, Clone, Parser)] #[clap(about = "iOS dev")] pub struct Options { @@ -38,6 +42,8 @@ pub struct Options { /// Open Xcode instead of trying to run on a connected device #[clap(short, long)] pub open: bool, + /// Runs on the given device name + pub device: Option, } impl From for crate::dev::Options { @@ -92,9 +98,29 @@ fn run_dev( let env = env()?; init_dot_cargo(app, None)?; + if let Some(device) = &options.device { + let simulators = simctl::device_list(&env).unwrap_or_default(); + for simulator in simulators { + if simulator + .name() + .to_lowercase() + .starts_with(&device.to_lowercase()) + { + log::info!("Starting simulator {}", simulator.name()); + let handle = simulator.start(&env)?; + spawn(move || { + let _ = handle.wait(); + }); + sleep(Duration::from_secs(3)); + break; + } + } + } + let open = options.open; let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; + let device = options.device; interface.mobile_dev( MobileOptions { debug: true, @@ -115,7 +141,7 @@ fn run_dev( if open { open_and_wait(config, &env) } else { - match run(options, config, &env, noise_level) { + match run(device.as_deref(), options, config, &env, noise_level) { Ok(c) => { crate::dev::wait_dev_process(c.clone(), move |status, reason| { crate::dev::on_app_exit(status, reason, exit_on_panic, no_watch) @@ -135,12 +161,13 @@ fn run_dev( #[derive(Debug, thiserror::Error)] enum RunError { - #[error(transparent)] - FailedToPromptForDevice(PromptError), + #[error("{0}")] + FailedToPromptForDevice(String), #[error(transparent)] RunFailed(DeviceRunError), } fn run( + device: Option<&str>, options: MobileOptions, config: &AppleConfig, env: &Env, @@ -154,8 +181,8 @@ fn run( let non_interactive = true; // ios-deploy --noninteractive (quit when app crashes or exits) - device_prompt(env) - .map_err(RunError::FailedToPromptForDevice)? + device_prompt(env, device) + .map_err(|e| RunError::FailedToPromptForDevice(e.to_string()))? .run(config, env, noise_level, non_interactive, profile) .map(DevChild::new) .map_err(RunError::RunFailed) diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index e1ae7213ce6..5a44bb11bd8 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -50,7 +50,11 @@ pub fn gen( let ios_pods = metadata.ios().pods().unwrap_or_default(); let macos_pods = metadata.macos().pods().unwrap_or_default(); - let default_archs = [String::from("arm64"), String::from("x86_64")]; + let default_archs = [ + String::from("arm64"), + String::from("arm64-sim"), + String::from("x86_64"), + ]; map.insert("file-groups", &source_dirs); map.insert("ios-frameworks", metadata.ios().frameworks()); diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 870edf52da8..ded473c650a 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -100,6 +100,7 @@ pub fn command(options: Options) -> Result<()> { // Set target-specific flags let triple = match arch.as_str() { "arm64" => "aarch64_apple_ios", + "arm64-sim" => "aarch64_apple_ios_sim", "x86_64" => "x86_64_apple_ios", _ => { return Err(anyhow::anyhow!( diff --git a/tooling/cli/templates/mobile/ios/project.yml b/tooling/cli/templates/mobile/ios/project.yml index 9f9c8742d89..fb72413cb71 100644 --- a/tooling/cli/templates/mobile/ios/project.yml +++ b/tooling/cli/templates/mobile/ios/project.yml @@ -72,7 +72,7 @@ targets: ARCHS: [{{join ios-valid-archs}}] VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} LIBRARY_SEARCH_PATHS[sdk=iphoneos*]: $(inherited) "{{prefix-path "target/aarch64-apple-ios/$(CONFIGURATION)"}}" - LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]: $(inherited) "{{prefix-path "target/x86_64-apple-ios/$(CONFIGURATION)"}}" + LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]: $(inherited) "{{prefix-path "target/aarch64-apple-ios-sim/$(CONFIGURATION)"}}" ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true groups: [app] dependencies: From 5d3242c496baa84217d8f7b1e1fcc3aab0ff922c Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 31 Aug 2022 20:53:00 -0300 Subject: [PATCH 048/423] fix(examples): keep the target fallback --- core/tauri-codegen/src/context.rs | 44 ++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index 1faf2899bdc..45b2e88c685 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -127,22 +127,34 @@ pub fn context_codegen(data: ContextData) -> Result Date: Wed, 31 Aug 2022 21:40:51 -0300 Subject: [PATCH 049/423] feat(cli): iOS simulator support on Intel based devices (#5112) --- core/tauri/src/manager.rs | 2 +- tooling/cli/Cargo.lock | 2 +- .../mobile/android/android_studio_script.rs | 5 +- tooling/cli/src/mobile/ios/project.rs | 14 +-- tooling/cli/src/mobile/ios/xcode_script.rs | 33 +---- tooling/cli/templates/mobile/ios/project.yml | 114 +----------------- 6 files changed, 14 insertions(+), 156 deletions(-) diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 7cf82e59a5b..ef59d5453c3 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -858,7 +858,7 @@ impl WindowManager { builder.status(r.status()).body(r.bytes()?)? } Err(e) => { - debug_eprintln!("Failed to request {}: {}", url.path(), e); + debug_eprintln!("Failed to request {}: {}", url.as_str(), e); return Err(Box::new(e)); } } diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 2d0cec6b2bc..1692bb078ec 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -293,7 +293,7 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#ff88cc91fbf008ee0b9b33c8e4bcb5c9b5b743e6" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#4a28c9e2370c07b4cb83458e58da0d7bfe4e9b1e" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index cf3bc974a8f..be7bd52acc8 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -1,4 +1,4 @@ -use super::{detect_target_ok, ensure_init, env, init_dot_cargo, with_config, MobileTarget}; +use super::{detect_target_ok, ensure_init, env, with_config, MobileTarget}; use crate::Result; use clap::Parser; @@ -32,11 +32,10 @@ pub fn command(options: Options) -> Result<()> { Profile::Debug }; - with_config(None, |app, config, metadata, cli_options| { + with_config(None, |_app, config, metadata, cli_options| { ensure_init(config.project_dir(), MobileTarget::Android)?; let env = env()?; - init_dot_cargo(app, Some((&env, config)))?; call_for_targets_with_fallback( options.targets.unwrap_or_default().iter(), diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index 5a44bb11bd8..841f8488ed4 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -50,18 +50,14 @@ pub fn gen( let ios_pods = metadata.ios().pods().unwrap_or_default(); let macos_pods = metadata.macos().pods().unwrap_or_default(); - let default_archs = [ - String::from("arm64"), - String::from("arm64-sim"), - String::from("x86_64"), - ]; + #[cfg(target_arch = "aarch64")] + let default_archs = ["arm64", "arm64-sim"]; + #[cfg(not(target_arch = "aarch64"))] + let default_archs = ["arm64", "x86_64"]; map.insert("file-groups", &source_dirs); map.insert("ios-frameworks", metadata.ios().frameworks()); - map.insert( - "ios-valid-archs", - metadata.ios().valid_archs().unwrap_or(&default_archs), - ); + map.insert("ios-valid-archs", default_archs); map.insert("ios-vendor-frameworks", metadata.ios().vendor_frameworks()); map.insert("ios-vendor-sdks", metadata.ios().vendor_sdks()); map.insert("macos-frameworks", metadata.macos().frameworks()); diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index ded473c650a..1f2da48e3c6 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -1,4 +1,4 @@ -use super::{env, init_dot_cargo, with_config}; +use super::{env, with_config}; use crate::Result; use clap::Parser; @@ -41,9 +41,8 @@ pub fn command(options: Options) -> Result<()> { let profile = profile_from_configuration(&options.configuration); let macos = macos_from_platform(&options.platform); - with_config(None, |root_conf, config, metadata, cli_options| { + with_config(None, |_root_conf, config, metadata, cli_options| { let env = env()?; - init_dot_cargo(root_conf, None)?; // The `PATH` env var Xcode gives us is missing any additions // made by the user's profile, so we'll manually add cargo's // `PATH`. @@ -65,31 +64,6 @@ pub fn command(options: Options) -> Result<()> { let mut host_env = HashMap::<&str, &OsStr>::new(); - // Host flags that are used by build scripts - let (macos_isysroot, library_path) = { - let macos_sdk_root = options - .sdk_root - .join("../../../../MacOSX.platform/Developer/SDKs/MacOSX.sdk"); - if !macos_sdk_root.is_dir() { - return Err(anyhow::anyhow!( - "macOS SDK root was invalid. {0} doesn't exist or isn't a directory", - macos_sdk_root.display() - )); - } - ( - format!("-isysroot {}", macos_sdk_root.display()), - format!("{}/usr/lib", macos_sdk_root.display()), - ) - }; - host_env.insert("MAC_FLAGS", macos_isysroot.as_ref()); - host_env.insert("CFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); - host_env.insert("CXXFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); - - host_env.insert( - "OBJC_INCLUDE_PATH_x86_64_apple_darwin", - include_dir.as_os_str(), - ); - host_env.insert("RUST_BACKTRACE", "1".as_ref()); let macos_target = Target::macos(); @@ -116,9 +90,6 @@ pub fn command(options: Options) -> Result<()> { target_env.insert(cflags.as_ref(), isysroot.as_ref()); target_env.insert(cxxflags.as_ref(), isysroot.as_ref()); target_env.insert(objc_include_path.as_ref(), include_dir.as_ref()); - // Prevents linker errors in build scripts and proc macros: - // https://github.com/signalapp/libsignal-client/commit/02899cac643a14b2ced7c058cc15a836a2165b6d - target_env.insert("LIBRARY_PATH", library_path.as_ref()); let target = if macos { &macos_target diff --git a/tooling/cli/templates/mobile/ios/project.yml b/tooling/cli/templates/mobile/ios/project.yml index fb72413cb71..af7263b22a7 100644 --- a/tooling/cli/templates/mobile/ios/project.yml +++ b/tooling/cli/templates/mobile/ios/project.yml @@ -3,7 +3,6 @@ options: bundleIdPrefix: {{reverse-domain app.domain}} deploymentTarget: iOS: {{apple.ios-version}} - macOS: {{apple.macos-version}} fileGroups: [{{join file-groups}}] configs: debug: debug @@ -71,8 +70,9 @@ targets: ENABLE_BITCODE: false ARCHS: [{{join ios-valid-archs}}] VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} - LIBRARY_SEARCH_PATHS[sdk=iphoneos*]: $(inherited) "{{prefix-path "target/aarch64-apple-ios/$(CONFIGURATION)"}}" - LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]: $(inherited) "{{prefix-path "target/aarch64-apple-ios-sim/$(CONFIGURATION)"}}" + LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) "{{prefix-path "target/x86_64-apple-ios/$(CONFIGURATION)"}}" + LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) "{{prefix-path "target/aarch64-apple-ios/$(CONFIGURATION)"}}" + LIBRARY_SEARCH_PATHS[arch=arm64-sim]: $(inherited) "{{prefix-path "target/aarch64-apple-ios-sim/$(CONFIGURATION)"}}" ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true groups: [app] dependencies: @@ -158,106 +158,6 @@ targets: {{~/each~}} {{~/if}} - {{app.name}}_macOS: - type: application - platform: macOS - sources: Sources - {{~#each macos-additional-targets}} - - path: {{prefix-path this}}{{/each}} - info: - path: {{app.name}}_macOS/Info.plist - properties: - NSHighResolutionCapable: true - scheme: - environmentVariables: - RUST_BACKTRACE: full - RUST_LOG: info - {{~#if ios-command-line-arguments}} - commandLineArguments: - {{~#each ios-command-line-arguments}} - "{{this}}": true - {{/each}}{{~/if}} - settings: - base: - LIBRARY_SEARCH_PATHS: $(inherited) "{{prefix-path "target/x86_64-apple-darwin/$(CONFIGURATION)"}}" - groups: [app] - dependencies: - - target: lib_{{app.name}}_macOS - embed: false - link: false - - framework: lib{{snake-case app.name}}.a - embed: false - {{~#each macos-vendor-frameworks}} - - framework: {{prefix-path this}}{{/each}} - {{~#each macos-vendor-sdks}} - - sdk: {{prefix-path this}}{{/each}} - - sdk: Metal.framework - {{~#each macos-frameworks}} - - sdk: {{this}}.framework{{/each}} - {{~#if macos-pre-build-scripts}} - preBuildScripts: - {{~#each macos-pre-build-scripts}}{{#if this.path}} - - path {{this.path}}{{/if}}{{#if this.script}} - - script: {{this.script}}{{/if}}{{#if this.name}} - name: {{this.name}}{{/if}}{{#if this.input-files}} - inputFiles: {{~#each this.input-files}} - - {{this}}{{/each}}{{/if}}{{#if this.output-files}} - outputFiles: {{~#each this.output-files}} - - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} - inputFileLists: {{~#each this.output-files}} - - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} - outputFileLists: {{~#each this.output-files}} - - {{this}}{{/each}}{{/if}}{{#if this.shell}} - shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} - showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} - runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} - basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} - discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} - {{~/each~}} - {{~/if~}} - {{#if macos-post-compile-scripts}} - postCompileScripts: - {{~#each macos-post-compile-scripts}}{{#if this.path}} - - path {{this.path}}{{/if}}{{#if this.script}} - - script: {{this.script}}{{/if}}{{#if this.name}} - name: {{this.name}}{{/if}}{{#if this.input-files}} - inputFiles: {{~#each this.input-files}} - - {{this}}{{/each}}{{/if}}{{#if this.output-files}} - outputFiles: {{~#each this.output-files}} - - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} - inputFileLists: {{~#each this.output-files}} - - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} - outputFileLists: {{~#each this.output-files}} - - {{this}}{{/each}}{{/if}}{{#if this.shell}} - shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} - showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} - runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} - basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} - discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} - {{~/each~}} - {{~/if~}} - {{#if macos-post-build-scripts}} - postBuildScripts: - {{~#each macos-post-build-scripts}}{{#if this.path}} - - path {{this.path}}{{/if}}{{#if this.script}} - - script: {{this.script}}{{/if}}{{#if this.name}} - name: {{this.name}}{{/if}}{{#if this.input-files}} - inputFiles: {{~#each this.input-files}} - - {{this}}{{/each}}{{/if}}{{#if this.output-files}} - outputFiles: {{~#each this.output-files}} - - {{this}}{{/each}}{{/if}}{{#if this.input-file-lists}} - inputFileLists: {{~#each this.output-files}} - - {{this}}{{/each}}{{/if}}{{#if this.output-file-lists}} - outputFileLists: {{~#each this.output-files}} - - {{this}}{{/each}}{{/if}}{{#if this.shell}} - shell: {{this.shell}}{{/if}}{{#if this.show-env-vars}} - showEnvVars: {{this.show_env_vars}}{{/if}}{{#if this.run-only-when-installing}} - runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} - basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} - discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} - {{~/each~}} - {{~/if}} - lib_{{app.name}}_iOS: type: "" platform: iOS @@ -270,11 +170,3 @@ targets: arguments: {{ tauri-binary-args-str }} -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} passSettings: false # prevents evil linker errors workingDirectory: $(SRCROOT)/../.. - lib_{{app.name}}_macOS: - type: "" - platform: macOS - legacy: - toolPath: {{ tauri-binary }} - arguments: {{ tauri-binary-args-str }} -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} - passSettings: false - workingDirectory: $(SRCROOT)/../.. From 8f3a9c5cf623c0d138a0b4734bd54102b77455e6 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 1 Sep 2022 12:00:17 -0300 Subject: [PATCH 050/423] feat(cli): improve device/simulator prompt logic (#5114) --- tooling/cli/Cargo.lock | 9 +- tooling/cli/Cargo.toml | 1 + tooling/cli/src/mobile/android.rs | 128 ++++++++++++++------------ tooling/cli/src/mobile/android/dev.rs | 42 ++------- tooling/cli/src/mobile/ios.rs | 110 +++++++++++++--------- tooling/cli/src/mobile/ios/dev.rs | 32 +------ tooling/cli/src/mobile/mod.rs | 2 + 7 files changed, 156 insertions(+), 168 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 1692bb078ec..5b1dea645b5 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -293,7 +293,7 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#4a28c9e2370c07b4cb83458e58da0d7bfe4e9b1e" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#123b8cf58e9a894375bb9d9e7a098419a7d67b5f" dependencies = [ "cocoa", "colored 1.9.3", @@ -3410,6 +3410,12 @@ dependencies = [ "syn", ] +[[package]] +name = "sublime_fuzzy" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7986063f7c0ab374407e586d7048a3d5aac94f103f751088bf398e07cd5400" + [[package]] name = "subtle" version = "2.4.1" @@ -3528,6 +3534,7 @@ dependencies = [ "serde_json", "serde_with 2.0.0", "shared_child", + "sublime_fuzzy", "tauri-bundler", "tauri-utils", "tempfile", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index f10c14a6cee..d1cf49764bb 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -33,6 +33,7 @@ cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "d textwrap = { version = "0.11.0", features = ["term_size"] } interprocess = "1" thiserror = "1" +sublime_fuzzy = "0.7" clap = { version = "3.2", features = [ "derive" ] } anyhow = "1.0" tauri-bundler = { version = "1.0.5", path = "../bundler" } diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 9b6a0063283..697ba1470bb 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -12,7 +12,6 @@ use cargo_mobile::{ target::Target, }, config::app::App, - device::PromptError, opts::NoiseLevel, os, util::prompt, @@ -23,11 +22,12 @@ use std::{ thread::{sleep, spawn}, time::Duration, }; +use sublime_fuzzy::best_match; use super::{ ensure_init, get_app, init::{command as init_command, init_dot_cargo, Options as InitOptions}, - log_finished, read_options, CliOptions, Target as MobileTarget, + log_finished, read_options, CliOptions, Target as MobileTarget, MIN_DEVICE_MATCH_SCORE, }; use crate::{ helpers::config::{get as get_tauri_config, Config as TauriConfig}, @@ -141,34 +141,39 @@ fn delete_codegen_vars() { } } -fn adb_device_prompt<'a>( - env: &'_ Env, - target: Option<&str>, -) -> Result, PromptError> { - let device_list = - adb::device_list(env).map_err(|cause| PromptError::detection_failed("Android", cause))?; +fn adb_device_prompt<'a>(env: &'_ Env, target: Option<&str>) -> Result> { + let device_list = adb::device_list(env) + .map_err(|cause| anyhow::anyhow!("Failed to detect connected Android devices: {cause}"))?; if !device_list.is_empty() { - let index = if device_list.len() > 1 { - if let Some(t) = target { - let t = t.to_lowercase(); - device_list - .iter() - .position(|d| d.name().to_lowercase().starts_with(&t)) - .unwrap_or_default() + let device = if let Some(t) = target { + let (device, score) = device_list + .into_iter() + .rev() + .map(|d| { + let score = best_match(t, d.name()).map_or(0, |m| m.score()); + (d, score) + }) + .max_by_key(|(_, score)| *score) + // we already checked the list is not empty + .unwrap(); + if score > MIN_DEVICE_MATCH_SCORE { + device } else { - prompt::list( - concat!("Detected ", "Android", " devices"), - device_list.iter(), - "device", - None, - "Device", - ) - .map_err(|cause| PromptError::prompt_failed("Android", cause))? + anyhow::bail!("Could not find an Android device matching {t}") } + } else if device_list.len() > 1 { + let index = prompt::list( + concat!("Detected ", "Android", " devices"), + device_list.iter(), + "device", + None, + "Device", + ) + .map_err(|cause| anyhow::anyhow!("Failed to prompt for Android device: {cause}"))?; + device_list.into_iter().nth(index).unwrap() } else { - 0 + device_list.into_iter().next().unwrap() }; - let device = device_list.into_iter().nth(index).unwrap(); println!( "Detected connected device: {} with target {:?}", device, @@ -176,57 +181,60 @@ fn adb_device_prompt<'a>( ); Ok(device) } else { - Err(PromptError::none_detected("Android")) + Err(anyhow::anyhow!("No connected Android devices detected")) } } -fn emulator_prompt( - env: &'_ Env, - target: Option<&str>, -) -> Result> { +fn emulator_prompt(env: &'_ Env, target: Option<&str>) -> Result { let emulator_list = emulator::avd_list(env).unwrap_or_default(); - if emulator_list.is_empty() { - Err(PromptError::none_detected("Android emulator")) - } else { - let index = if emulator_list.len() > 1 { - if let Some(t) = target { - let t = t.to_lowercase(); - emulator_list - .iter() - .position(|d| d.name().to_lowercase().starts_with(&t)) - .unwrap_or_default() + if !emulator_list.is_empty() { + let emulator = if let Some(t) = target { + let (device, score) = emulator_list + .into_iter() + .rev() + .map(|d| { + let score = best_match(t, d.name()).map_or(0, |m| m.score()); + (d, score) + }) + .max_by_key(|(_, score)| *score) + // we already checked the list is not empty + .unwrap(); + if score > MIN_DEVICE_MATCH_SCORE { + device } else { - prompt::list( - concat!("Detected ", "Android", " emulators"), - emulator_list.iter(), - "emulator", - None, - "Emulator", - ) - .map_err(|cause| PromptError::prompt_failed("Android emulator", cause))? + anyhow::bail!("Could not find an Android Emulator matching {t}") } + } else if emulator_list.len() > 1 { + let index = prompt::list( + concat!("Detected ", "Android", " emulators"), + emulator_list.iter(), + "emulator", + None, + "Emulator", + ) + .map_err(|cause| anyhow::anyhow!("Failed to prompt for Android Emulator device: {cause}"))?; + emulator_list.into_iter().nth(index).unwrap() } else { - 0 + emulator_list.into_iter().next().unwrap() }; - Ok(emulator_list.into_iter().nth(index).unwrap()) + let handle = emulator.start(env)?; + spawn(move || { + let _ = handle.wait(); + }); + + Ok(emulator) + } else { + Err(anyhow::anyhow!("No available Android Emulator detected")) } } -fn device_prompt<'a>( - env: &'_ Env, - target: Option<&str>, -) -> Result, PromptError> { +fn device_prompt<'a>(env: &'_ Env, target: Option<&str>) -> Result> { if let Ok(device) = adb_device_prompt(env, target) { Ok(device) } else { let emulator = emulator_prompt(env, target)?; - let handle = emulator.start(env).map_err(|e| { - PromptError::prompt_failed( - "Android emulator", - std::io::Error::new(std::io::ErrorKind::Other, e.to_string()), - ) - })?; + let handle = emulator.start(env)?; spawn(move || { let _ = handle.wait(); }); diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 52fb6a2c035..1fac4ce4d7e 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -1,6 +1,6 @@ use super::{ delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, - MobileTarget, PromptError, + MobileTarget, }; use crate::{ helpers::{config::get as get_tauri_config, flock}, @@ -12,21 +12,14 @@ use clap::Parser; use cargo_mobile::{ android::{ - adb, config::{Config as AndroidConfig, Metadata as AndroidMetadata}, - device::RunError as DeviceRunError, - emulator, env::Env, }, config::app::App, opts::{NoiseLevel, Profile}, }; -use std::{ - env::set_var, - thread::{sleep, spawn}, - time::Duration, -}; +use std::env::set_var; const WEBVIEW_CLIENT_CLASS_EXTENSION: &str = " @android.annotation.SuppressLint(\"WebViewClientOnReceivedSslError\") @@ -119,25 +112,6 @@ fn run_dev( let env = env()?; init_dot_cargo(app, Some((&env, config)))?; - if let Some(device) = &options.device { - let emulators = emulator::avd_list(&env).unwrap_or_default(); - for emulator in emulators { - if emulator - .name() - .to_lowercase() - .starts_with(&device.to_lowercase()) - { - log::info!("Starting emulator {}", emulator.name()); - let handle = emulator.start(&env)?; - spawn(move || { - let _ = handle.wait(); - }); - sleep(Duration::from_secs(3)); - break; - } - } - } - let open = options.open; let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; @@ -189,10 +163,10 @@ fn run_dev( #[derive(Debug, thiserror::Error)] enum RunError { - #[error(transparent)] - FailedToPromptForDevice(PromptError), - #[error(transparent)] - RunFailed(DeviceRunError), + #[error("{0}")] + FailedToPromptForDevice(String), + #[error("{0}")] + RunFailed(String), } fn run( @@ -212,7 +186,7 @@ fn run( let build_app_bundle = metadata.asset_packs().is_some(); device_prompt(env, device) - .map_err(RunError::FailedToPromptForDevice)? + .map_err(|e| RunError::FailedToPromptForDevice(e.to_string()))? .run( config, env, @@ -224,5 +198,5 @@ fn run( ".MainActivity".into(), ) .map(DevChild::new) - .map_err(RunError::RunFailed) + .map_err(|e| RunError::RunFailed(e.to_string())) } diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 0cc0e1a712f..ade5c71cbff 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -13,18 +13,18 @@ use cargo_mobile::{ target::Target, }, config::app::App, - device::PromptError, env::Env, opts::NoiseLevel, os, util::prompt, }; use clap::{Parser, Subcommand}; +use sublime_fuzzy::best_match; use super::{ ensure_init, env, get_app, init::{command as init_command, init_dot_cargo, Options as InitOptions}, - log_finished, read_options, CliOptions, Target as MobileTarget, + log_finished, read_options, CliOptions, Target as MobileTarget, MIN_DEVICE_MATCH_SCORE, }; use crate::{ helpers::config::{get as get_tauri_config, Config as TauriConfig}, @@ -126,21 +126,28 @@ fn with_config( f(&app, &config, &metadata, cli_options) } -fn ios_deploy_device_prompt<'a>( - env: &'_ Env, - target: Option<&str>, -) -> Result, PromptError> { - let device_list = - ios_deploy::device_list(env).map_err(|cause| PromptError::detection_failed("iOS", cause))?; +fn ios_deploy_device_prompt<'a>(env: &'_ Env, target: Option<&str>) -> Result> { + let device_list = ios_deploy::device_list(env) + .map_err(|cause| anyhow::anyhow!("Failed to detect connected iOS devices: {cause}"))?; if !device_list.is_empty() { - let index = if device_list.len() > 1 { - if let Some(t) = target { - let t = t.to_lowercase(); - device_list - .iter() - .position(|d| d.name().to_lowercase().starts_with(&t)) - .unwrap_or_default() + let device = if let Some(t) = target { + let (device, score) = device_list + .into_iter() + .rev() + .map(|d| { + let score = best_match(t, d.name()).map_or(0, |m| m.score()); + (d, score) + }) + .max_by_key(|(_, score)| *score) + // we already checked the list is not empty + .unwrap(); + if score > MIN_DEVICE_MATCH_SCORE { + device } else { + anyhow::bail!("Could not find an iOS device matching {t}") + } + } else { + let index = if device_list.len() > 1 { prompt::list( concat!("Detected ", "iOS", " devices"), device_list.iter(), @@ -148,12 +155,12 @@ fn ios_deploy_device_prompt<'a>( None, "Device", ) - .map_err(|cause| PromptError::prompt_failed("iOS", cause))? - } - } else { - 0 + .map_err(|cause| anyhow::anyhow!("Failed to prompt for iOS device: {cause}"))? + } else { + 0 + }; + device_list.into_iter().nth(index).unwrap() }; - let device = device_list.into_iter().nth(index).unwrap(); println!( "Detected connected device: {} with target {:?}", device, @@ -161,41 +168,54 @@ fn ios_deploy_device_prompt<'a>( ); Ok(device) } else { - Err(PromptError::none_detected("iOS")) + Err(anyhow::anyhow!("No connected iOS devices detected")) } } -fn simulator_prompt( - env: &'_ Env, - target: Option<&str>, -) -> Result> { - let simulator_list = simctl::device_list(env) - .map_err(|cause| PromptError::detection_failed("iOS Simulator", cause))?; +fn simulator_prompt(env: &'_ Env, target: Option<&str>) -> Result { + let simulator_list = simctl::device_list(env).map_err(|cause| { + anyhow::anyhow!("Failed to detect connected iOS Simulator devices: {cause}") + })?; if !simulator_list.is_empty() { - let index = if simulator_list.len() > 1 { - if let Some(t) = target { - let t = t.to_lowercase(); - simulator_list - .iter() - .position(|d| d.name().to_lowercase().starts_with(&t)) - .unwrap_or_default() + let device = if let Some(t) = target { + let (device, score) = simulator_list + .into_iter() + .rev() + .map(|d| { + let score = best_match(t, d.name()).map_or(0, |m| m.score()); + (d, score) + }) + .max_by_key(|(_, score)| *score) + // we already checked the list is not empty + .unwrap(); + if score > MIN_DEVICE_MATCH_SCORE { + device } else { - prompt::list( - concat!("Detected ", "iOS", " simulators"), - simulator_list.iter(), - "simulator", - None, - "Simulator", - ) - .map_err(|cause| PromptError::prompt_failed("iOS Simulator", cause))? + anyhow::bail!("Could not find an iOS Simulator matching {t}") } + } else if simulator_list.len() > 1 { + let index = prompt::list( + concat!("Detected ", "iOS", " simulators"), + simulator_list.iter(), + "simulator", + None, + "Simulator", + ) + .map_err(|cause| anyhow::anyhow!("Failed to prompt for iOS Simulator device: {cause}"))?; + simulator_list.into_iter().nth(index).unwrap() } else { - 0 + simulator_list.into_iter().next().unwrap() }; - let device = simulator_list.into_iter().nth(index).unwrap(); + + log::info!("Starting simulator {}", device.name()); + let handle = device.start(env)?; + spawn(move || { + let _ = handle.wait(); + }); + Ok(device) } else { - Err(PromptError::none_detected("iOS Simulator")) + Err(anyhow::anyhow!("No available iOS Simulator detected")) } } diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 4ab79826c71..14f6b21062b 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -10,17 +10,12 @@ use crate::{ use clap::Parser; use cargo_mobile::{ - apple::{config::Config as AppleConfig, device::RunError as DeviceRunError, simctl}, + apple::config::Config as AppleConfig, config::app::App, env::Env, opts::{NoiseLevel, Profile}, }; -use std::{ - thread::{sleep, spawn}, - time::Duration, -}; - #[derive(Debug, Clone, Parser)] #[clap(about = "iOS dev")] pub struct Options { @@ -98,25 +93,6 @@ fn run_dev( let env = env()?; init_dot_cargo(app, None)?; - if let Some(device) = &options.device { - let simulators = simctl::device_list(&env).unwrap_or_default(); - for simulator in simulators { - if simulator - .name() - .to_lowercase() - .starts_with(&device.to_lowercase()) - { - log::info!("Starting simulator {}", simulator.name()); - let handle = simulator.start(&env)?; - spawn(move || { - let _ = handle.wait(); - }); - sleep(Duration::from_secs(3)); - break; - } - } - } - let open = options.open; let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; @@ -163,8 +139,8 @@ fn run_dev( enum RunError { #[error("{0}")] FailedToPromptForDevice(String), - #[error(transparent)] - RunFailed(DeviceRunError), + #[error("{0}")] + RunFailed(String), } fn run( device: Option<&str>, @@ -185,5 +161,5 @@ fn run( .map_err(|e| RunError::FailedToPromptForDevice(e.to_string()))? .run(config, env, noise_level, non_interactive, profile) .map(DevChild::new) - .map_err(RunError::RunFailed) + .map_err(|e| RunError::RunFailed(e.to_string())) } diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 5da42b40b11..dba5eda472b 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -40,6 +40,8 @@ mod init; #[cfg(target_os = "macos")] pub mod ios; +const MIN_DEVICE_MATCH_SCORE: isize = 0; + #[derive(Clone)] pub struct DevChild { child: Arc, From 5d82357166b163fde92129d9ebe87b5c6d196a43 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 1 Sep 2022 12:07:06 -0300 Subject: [PATCH 051/423] feat(cli): add `--reinstall-deps` option to `ios init` --- tooling/cli/src/mobile/android.rs | 12 ++++++++-- tooling/cli/src/mobile/init.rs | 32 +++++++-------------------- tooling/cli/src/mobile/ios.rs | 15 +++++++++++-- tooling/cli/src/mobile/ios/project.rs | 3 +-- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 697ba1470bb..35ed4a306e8 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -26,7 +26,7 @@ use sublime_fuzzy::best_match; use super::{ ensure_init, get_app, - init::{command as init_command, init_dot_cargo, Options as InitOptions}, + init::{command as init_command, init_dot_cargo}, log_finished, read_options, CliOptions, Target as MobileTarget, MIN_DEVICE_MATCH_SCORE, }; use crate::{ @@ -53,6 +53,14 @@ pub struct Cli { command: Commands, } +#[derive(Debug, Parser)] +#[clap(about = "Initializes a Tauri Android project")] +pub struct InitOptions { + /// Skip prompting for values + #[clap(long)] + ci: bool, +} + #[derive(Subcommand)] enum Commands { Init(InitOptions), @@ -67,7 +75,7 @@ enum Commands { pub fn command(cli: Cli, verbosity: usize) -> Result<()> { let noise_level = NoiseLevel::from_occurrences(verbosity as u64); match cli.command { - Commands::Init(options) => init_command(options, MobileTarget::Android)?, + Commands::Init(options) => init_command(MobileTarget::Android, options.ci, false)?, Commands::Open => open::command()?, Commands::Dev(options) => dev::command(options, noise_level)?, Commands::Build(options) => build::command(options, noise_level)?, diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 26838e2b624..07094e9d01a 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -11,31 +11,25 @@ use cargo_mobile::{ }, config::app::App, dot_cargo, - os::code_command, target::TargetTrait as _, util::{ self, cli::{Report, TextWrapper}, }, }; -use clap::Parser; use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError}; use std::{env::current_dir, fs, path::PathBuf}; -#[derive(Debug, Parser)] -#[clap(about = "Initializes a Tauri Android project")] -pub struct Options { - /// Skip prompting for values - #[clap(long)] - ci: bool, -} - -pub fn command(mut options: Options, target: Target) -> Result<()> { - options.ci = options.ci || std::env::var("CI").is_ok(); - +pub fn command(target: Target, ci: bool, reinstall_deps: bool) -> Result<()> { let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); - exec(target, &wrapper, options.ci, true, true).map_err(|e| anyhow::anyhow!("{:#}", e))?; + exec( + target, + &wrapper, + ci || std::env::var("CI").is_ok(), + reinstall_deps, + ) + .map_err(|e| anyhow::anyhow!("{:#}", e))?; Ok(()) } @@ -69,7 +63,6 @@ pub fn exec( target: Target, wrapper: &TextWrapper, non_interactive: bool, - skip_dev_tools: bool, #[allow(unused_variables)] reinstall_deps: bool, ) -> Result { let tauri_config = get_tauri_config(None)?; @@ -87,14 +80,6 @@ pub fn exec( ) })?; } - if !skip_dev_tools && util::command_present("code").unwrap_or_default() { - let mut command = code_command(); - command.add_args(&["--install-extension", "vadimcn.vscode-lldb"]); - if non_interactive { - command.add_arg("--force"); - } - command.run_and_wait()?; - } let (handlebars, mut map) = handlebars(&app); @@ -169,7 +154,6 @@ pub fn exec( (handlebars, map), wrapper, non_interactive, - skip_dev_tools, reinstall_deps, )?; init_dot_cargo(&app, None)?; diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index ade5c71cbff..4a3bae8aeea 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -23,7 +23,7 @@ use sublime_fuzzy::best_match; use super::{ ensure_init, env, get_app, - init::{command as init_command, init_dot_cargo, Options as InitOptions}, + init::{command as init_command, init_dot_cargo}, log_finished, read_options, CliOptions, Target as MobileTarget, MIN_DEVICE_MATCH_SCORE, }; use crate::{ @@ -55,6 +55,17 @@ pub struct Cli { command: Commands, } +#[derive(Debug, Parser)] +#[clap(about = "Initializes a Tauri iOS project")] +pub struct InitOptions { + /// Skip prompting for values + #[clap(long)] + ci: bool, + /// Reinstall dependencies + #[clap(short, long)] + reinstall_deps: bool, +} + #[derive(Subcommand)] enum Commands { Init(InitOptions), @@ -68,7 +79,7 @@ enum Commands { pub fn command(cli: Cli, verbosity: usize) -> Result<()> { let noise_level = NoiseLevel::from_occurrences(verbosity as u64); match cli.command { - Commands::Init(options) => init_command(options, MobileTarget::Ios)?, + Commands::Init(options) => init_command(MobileTarget::Ios, options.ci, options.reinstall_deps)?, Commands::Open => open::command()?, Commands::Dev(options) => dev::command(options, noise_level)?, Commands::Build(options) => build::command(options, noise_level)?, diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index 841f8488ed4..533488ec124 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -32,14 +32,13 @@ pub fn gen( (handlebars, mut map): (Handlebars, template::JsonMap), wrapper: &TextWrapper, non_interactive: bool, - skip_dev_tools: bool, reinstall_deps: bool, ) -> Result<()> { println!("Installing iOS toolchains..."); Target::install_all()?; rust_version_check(wrapper)?; - deps::install_all(wrapper, non_interactive, skip_dev_tools, reinstall_deps) + deps::install_all(wrapper, non_interactive, true, reinstall_deps) .with_context(|| "failed to install Apple dependencies")?; let dest = config.project_dir(); From 349895b296502f70ecfde57789526e90e77d214e Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 1 Sep 2022 12:25:08 -0300 Subject: [PATCH 052/423] refactor(cli): remove mobile assets folder symlink --- tooling/cli/src/mobile/android/project.rs | 15 ++++++++++++--- tooling/cli/src/mobile/init.rs | 12 +----------- tooling/cli/src/mobile/ios/project.rs | 14 +++++++++++--- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs index 9182c7c8e02..39160b2d0a1 100644 --- a/tooling/cli/src/mobile/android/project.rs +++ b/tooling/cli/src/mobile/android/project.rs @@ -9,12 +9,13 @@ use cargo_mobile::{ config::{Config, Metadata}, target::Target, }, + config::app::DEFAULT_ASSET_DIR, os, target::TargetTrait as _, util::{ self, cli::{Report, TextWrapper}, - ln, prefix_path, + prefix_path, }, }; use handlebars::Handlebars; @@ -150,8 +151,16 @@ pub fn gen( cause ) })?; - os::ln::force_symlink_relative(config.app().asset_dir(), dest, ln::TargetStyle::Directory) - .map_err(|_| anyhow::anyhow!("failed to symlink asset directory"))?; + + let asset_dir = dest.join(DEFAULT_ASSET_DIR); + if !asset_dir.is_dir() { + fs::create_dir_all(&asset_dir).map_err(|cause| { + anyhow::anyhow!( + "failed to create asset dir {path}: {cause}", + path = asset_dir.display() + ) + })?; + } Ok(()) } diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 07094e9d01a..c1a9fcdb33d 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -19,7 +19,7 @@ use cargo_mobile::{ }; use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError}; -use std::{env::current_dir, fs, path::PathBuf}; +use std::{env::current_dir, path::PathBuf}; pub fn command(target: Target, ci: bool, reinstall_deps: bool) -> Result<()> { let wrapper = TextWrapper::with_splitter(textwrap::termwidth(), textwrap::NoHyphenation); @@ -71,16 +71,6 @@ pub fn exec( let app = get_app(tauri_config_); - let asset_dir = app.asset_dir(); - if !asset_dir.is_dir() { - fs::create_dir_all(&asset_dir).map_err(|cause| { - anyhow::anyhow!( - "failed to create asset dir {path}: {cause}", - path = asset_dir.display() - ) - })?; - } - let (handlebars, mut map) = handlebars(&app); let mut args = std::env::args_os(); diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index 533488ec124..a2ddc1a3a94 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -11,8 +11,9 @@ use cargo_mobile::{ target::Target, }, bossy, + config::app::DEFAULT_ASSET_DIR, target::TargetTrait as _, - util::{self, cli::TextWrapper, ln}, + util::{self, cli::TextWrapper}, }; use handlebars::Handlebars; use include_dir::{include_dir, Dir}; @@ -140,8 +141,15 @@ pub fn gen( ) .with_context(|| "failed to process template")?; - ln::force_symlink_relative(config.app().asset_dir(), &dest, ln::TargetStyle::Directory) - .map_err(|_| anyhow::anyhow!("failed to symlink asset directory"))?; + let asset_dir = dest.join(DEFAULT_ASSET_DIR); + if !asset_dir.is_dir() { + create_dir_all(&asset_dir).map_err(|cause| { + anyhow::anyhow!( + "failed to create asset dir {path}: {cause}", + path = asset_dir.display() + ) + })?; + } // Create all asset catalog directories if they don't already exist for dir in asset_catalogs { From aae91a9b53ea8e6d673a258e5fc9f871f40e08fd Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 1 Sep 2022 16:58:41 -0300 Subject: [PATCH 053/423] refactor(tauri-codegen): panic if local IP address cannot be resolved --- core/tauri-codegen/src/context.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index 45b2e88c685..1ace8c10ab3 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -167,9 +167,8 @@ pub fn context_codegen(data: ContextData) -> Result false, }; if localhost { - if let Ok(ip) = local_ip_address::local_ip() { - url.set_host(Some(&ip.to_string())).unwrap(); - } + let ip = local_ip_address::local_ip().expect("failed to resolve local IP address"); + url.set_host(Some(&ip.to_string())).unwrap(); } } } From 5643ece77c327bad2bca260ca1c24645bf3f05d9 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Fri, 2 Sep 2022 15:30:42 +0200 Subject: [PATCH 054/423] fix(cli/mobile): strip `\n` before parsing json (#5126) --- tooling/cli/src/mobile/init.rs | 2 +- tooling/cli/src/mobile/mod.rs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index c1a9fcdb33d..991e85d1243 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -62,7 +62,7 @@ pub fn init_dot_cargo(app: &App, android: Option<(&AndroidEnv, &AndroidConfig)>) pub fn exec( target: Target, wrapper: &TextWrapper, - non_interactive: bool, + #[allow(unused_variables)] non_interactive: bool, #[allow(unused_variables)] reinstall_deps: bool, ) -> Result { let tauri_config = get_tauri_config(None)?; diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index dba5eda472b..4e62c6d7e62 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -184,10 +184,10 @@ fn read_options(config: &TauriConfig, target: Target) -> CliOptions { let mut attempt = 0; let max_tries = 5; - let buffer = loop { + let (buffer, len) = loop { let mut buffer = String::new(); - if conn.read_line(&mut buffer).is_ok() { - break buffer; + if let Ok(len) = conn.read_line(&mut buffer) { + break (buffer, len); } std::thread::sleep(std::time::Duration::from_secs(1)); attempt += 1; @@ -199,7 +199,8 @@ fn read_options(config: &TauriConfig, target: Target) -> CliOptions { std::process::exit(1); } }; - let options: CliOptions = serde_json::from_str(&buffer).expect("invalid CLI options"); + + let options: CliOptions = serde_json::from_str(&buffer[..len - 1]).expect("invalid CLI options"); for (k, v) in &options.vars { set_var(k, v); } From 30d0e190bb22f261c32b5041e9587fcc76fa6a03 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Fri, 2 Sep 2022 17:21:15 +0200 Subject: [PATCH 055/423] fix(cli/templates): fix desktop entrypoint template (#5127) --- tooling/cli/templates/app/src-tauri/src/desktop.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tooling/cli/templates/app/src-tauri/src/desktop.rs b/tooling/cli/templates/app/src-tauri/src/desktop.rs index ef900d6533f..c43e6eaf38f 100644 --- a/tooling/cli/templates/app/src-tauri/src/desktop.rs +++ b/tooling/cli/templates/app/src-tauri/src/desktop.rs @@ -3,6 +3,6 @@ windows_subsystem = "windows" )] -fn main() { - app::AppBuilder::new() +pub fn main() { + app::AppBuilder::new().run(); } From 4c1c78ebf004cdc0f64c2b7787a2220e4292fe2f Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 4 Sep 2022 13:49:49 -0300 Subject: [PATCH 056/423] chore(cli): update lockfile --- tooling/cli/Cargo.lock | 57 ++++++------------------------------------ 1 file changed, 7 insertions(+), 50 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 3d1f042eb98..c7b86d41aac 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -327,7 +327,7 @@ dependencies = [ "toml", "ureq", "winapi 0.3.9", - "windows 0.39.0", + "windows", ] [[package]] @@ -3558,7 +3558,7 @@ dependencies = [ "toml", "url", "walkdir", - "windows 0.37.0", + "windows", ] [[package]] @@ -4143,26 +4143,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" -dependencies = [ - "windows-implement", - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", -] - [[package]] name = "windows" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" dependencies = [ + "windows-implement", "windows_aarch64_msvc 0.39.0", "windows_i686_gnu 0.39.0", "windows_i686_msvc 0.39.0", @@ -4172,9 +4159,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a1062e555f7d9d66fd1130ed4f7c6ec41a47529ee0850cd0e926d95b26bb14" +checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" dependencies = [ "syn", "windows-tokens", @@ -4195,9 +4182,9 @@ dependencies = [ [[package]] name = "windows-tokens" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169" +checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" [[package]] name = "windows_aarch64_msvc" @@ -4205,12 +4192,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - [[package]] name = "windows_aarch64_msvc" version = "0.39.0" @@ -4223,12 +4204,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - [[package]] name = "windows_i686_gnu" version = "0.39.0" @@ -4241,12 +4216,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - [[package]] name = "windows_i686_msvc" version = "0.39.0" @@ -4259,12 +4228,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - [[package]] name = "windows_x86_64_gnu" version = "0.39.0" @@ -4277,12 +4240,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - [[package]] name = "windows_x86_64_msvc" version = "0.39.0" From f33365687649bc75741d3c094b6081419a1b9c8b Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 4 Sep 2022 14:16:52 -0300 Subject: [PATCH 057/423] fix(core): set response mimetype on dev --- core/tauri/src/manager.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 2efe16af42d..a3048890b6f 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -856,6 +856,9 @@ impl WindowManager { { Ok(r) => { for (name, value) in r.headers() { + if name == "Content-Type" { + builder = builder.mimetype(value.to_str().unwrap()); + } builder = builder.header(name, value); } builder.status(r.status()).body(r.bytes()?)? From e1b8ee2b7affb74fb1cb7a1a85e3bd7ea84449a5 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sun, 4 Sep 2022 22:06:56 -0300 Subject: [PATCH 058/423] refactor(cli): move generated Kotlin files to the `generated` folder --- tooling/cli/src/mobile/android.rs | 14 ++++++---- tooling/cli/src/mobile/android/project.rs | 28 ++++++++++++++----- .../templates/mobile/android/app/.gitignore | 3 +- .../src/main/{ => generated}/TauriActivity.kt | 0 4 files changed, 32 insertions(+), 13 deletions(-) rename tooling/cli/templates/mobile/android/app/src/main/{ => generated}/TauriActivity.kt (100%) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 35ed4a306e8..d76d86c676b 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -110,11 +110,15 @@ pub fn get_config( set_var("WRY_ANDROID_APP_NAME_SNAKE_CASE", app.name()); set_var( "WRY_ANDROID_KOTLIN_FILES_OUT_DIR", - config.project_dir().join("app/src/main").join(format!( - "java/{}/{}", - app.reverse_domain().replace('.', "/"), - app.name() - )), + config + .project_dir() + .join("app/src/main") + .join(format!( + "java/{}/{}", + app.reverse_domain().replace('.', "/"), + app.name() + )) + .join("generated"), ); (app, config, metadata) diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs index 39160b2d0a1..8a06701f66f 100644 --- a/tooling/cli/src/mobile/android/project.rs +++ b/tooling/cli/src/mobile/android/project.rs @@ -85,6 +85,8 @@ pub fn gen( let domain = config.app().reverse_domain().replace('.', "/"); let package_path = format!("java/{}/{}", domain, config.app().name()); + map.insert("package-path", &package_path); + let mut created_dirs = Vec::new(); template::render_with_generator( &handlebars, @@ -92,13 +94,25 @@ pub fn gen( &TEMPLATE_DIR, &dest, &mut |path| { - let path = if path.extension() == Some(OsStr::new("kt")) { - let parent = path.parent().unwrap(); - let file_name = path.file_name().unwrap(); - let out_dir = dest.join(parent).join(&package_path); - out_dir.join(file_name) - } else { - dest.join(path) + let mut iter = path.iter(); + let root = iter.next().unwrap().to_str().unwrap(); + let path_without_root: std::path::PathBuf = iter.collect(); + let path = match ( + root, + path.extension().and_then(|o| o.to_str()), + path_without_root.strip_prefix("src/main"), + ) { + ("app" | "buildSrc", Some("kt"), Ok(path)) => { + let parent = path.parent().unwrap(); + let file_name = path.file_name().unwrap(); + let out_dir = dest + .join(root) + .join("src/main") + .join(&package_path) + .join(parent); + out_dir.join(file_name) + } + _ => dest.join(path), }; let parent = path.parent().unwrap().to_path_buf(); diff --git a/tooling/cli/templates/mobile/android/app/.gitignore b/tooling/cli/templates/mobile/android/app/.gitignore index 42afabfd2ab..357fc705cec 100644 --- a/tooling/cli/templates/mobile/android/app/.gitignore +++ b/tooling/cli/templates/mobile/android/app/.gitignore @@ -1 +1,2 @@ -/build \ No newline at end of file +/build +/src/main/{{package-path}}/generated diff --git a/tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt b/tooling/cli/templates/mobile/android/app/src/main/generated/TauriActivity.kt similarity index 100% rename from tooling/cli/templates/mobile/android/app/src/main/TauriActivity.kt rename to tooling/cli/templates/mobile/android/app/src/main/generated/TauriActivity.kt From bc1622c5abd3ac2e1d85b55ac456a379641dc611 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 5 Sep 2022 16:16:59 -0300 Subject: [PATCH 059/423] feat(core): use native window dialogs on Android (#5137) --- .github/workflows/test-core.yml | 7 ----- core/tauri/scripts/core.js | 37 -------------------------- core/tauri/scripts/init.js | 4 +++ core/tauri/scripts/window_dialogs.js | 19 ++++++++++++++ core/tauri/scripts/window_print.js | 13 ++++++++++ core/tauri/src/manager.rs | 17 ++++++++++++ examples/api/src-tauri/Cargo.lock | 39 ++++++++-------------------- 7 files changed, 64 insertions(+), 72 deletions(-) create mode 100644 core/tauri/scripts/window_dialogs.js create mode 100644 core/tauri/scripts/window_print.js diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index 855de09eeb5..fa5b6fb784e 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -18,9 +18,6 @@ env: RUST_BACKTRACE: 1 CARGO_INCREMENTAL: 0 # This is set to 0 by the https://github.com/Swatinem/rust-cache CARGO_PROFILE_DEV_DEBUG: 0 # This would add unnecessary bloat to the target folder, decreasing cache efficiency. - WRY_ANDROID_REVERSED_DOMAIN: 'app.tauri' - WRY_ANDROID_APP_NAME_SNAKE_CASE: 'dev' - WRY_ANDROID_KOTLIN_FILES_OUT_DIR: 'out' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -123,10 +120,6 @@ jobs: ${{ matrix.platform.os }}-${{ matrix.platform.toolchain }}- ${{ matrix.platform.os }}- - - name: create kotlin out dir - if: contains(matrix.platform.target, 'android') - run: mkdir out - - name: pin time run: | cargo update -p time --precise 0.3.13 diff --git a/core/tauri/scripts/core.js b/core/tauri/scripts/core.js index bc1887495ed..5fa5bbd8e87 100644 --- a/core/tauri/scripts/core.js +++ b/core/tauri/scripts/core.js @@ -245,41 +245,4 @@ setNotificationPermission(response ? 'granted' : 'denied') } }) - - window.alert = function (message) { - window.__TAURI_INVOKE__('tauri', { - __tauriModule: 'Dialog', - message: { - cmd: 'messageDialog', - message: message.toString() - } - }) - } - - window.confirm = function (message) { - return window.__TAURI_INVOKE__('tauri', { - __tauriModule: 'Dialog', - message: { - cmd: 'confirmDialog', - message: message.toString() - } - }) - } - - // window.print works on Linux/Windows; need to use the API on macOS - if (navigator.userAgent.includes('Mac')) { - window.print = function () { - return window.__TAURI_INVOKE__('tauri', { - __tauriModule: 'Window', - message: { - cmd: 'manage', - data: { - cmd: { - type: 'print' - } - } - } - }) - } - } })() diff --git a/core/tauri/scripts/init.js b/core/tauri/scripts/init.js index 581b4f27df3..8c820a11c91 100644 --- a/core/tauri/scripts/init.js +++ b/core/tauri/scripts/init.js @@ -17,6 +17,10 @@ __RAW_core_script__ + __RAW_window_dialogs_script__ + + __RAW_window_print_script__ + __RAW_event_initialization_script__ if (window.ipc) { diff --git a/core/tauri/scripts/window_dialogs.js b/core/tauri/scripts/window_dialogs.js new file mode 100644 index 00000000000..7bdf1bafb82 --- /dev/null +++ b/core/tauri/scripts/window_dialogs.js @@ -0,0 +1,19 @@ +window.alert = function (message) { + window.__TAURI_INVOKE__('tauri', { + __tauriModule: 'Dialog', + message: { + cmd: 'messageDialog', + message: message.toString() + } + }) +} + +window.confirm = function (message) { + return window.__TAURI_INVOKE__('tauri', { + __tauriModule: 'Dialog', + message: { + cmd: 'confirmDialog', + message: message.toString() + } + }) +} diff --git a/core/tauri/scripts/window_print.js b/core/tauri/scripts/window_print.js new file mode 100644 index 00000000000..bffd4867157 --- /dev/null +++ b/core/tauri/scripts/window_print.js @@ -0,0 +1,13 @@ +window.print = function () { + return window.__TAURI_INVOKE__('tauri', { + __tauriModule: 'Window', + message: { + cmd: 'manage', + data: { + cmd: { + type: 'print' + } + } + } + }) +} diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index a3048890b6f..8410154a2dc 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -917,6 +917,10 @@ impl WindowManager { #[raw] core_script: &'a str, #[raw] + window_dialogs_script: &'a str, + #[raw] + window_print_script: &'a str, + #[raw] event_initialization_script: &'a str, #[raw] plugin_initialization_script: &'a str, @@ -982,6 +986,19 @@ impl WindowManager { ) ), core_script: include_str!("../scripts/core.js"), + + // window.print works on Linux/Windows; need to use the API on macOS + #[cfg(any(target_os = "macos", target_os = "ios"))] + window_print_script: include_str!("../scripts/window_print.js"), + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + window_print_script: "", + + // dialogs are implemented natively on Android + #[cfg(not(target_os = "android"))] + window_dialogs_script: include_str!("../scripts/window_dialogs.js"), + #[cfg(target_os = "android")] + window_dialogs_script: "", + event_initialization_script: &self.event_initialization_script(), plugin_initialization_script, freeze_prototype, diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index e6a5f212c47..6dfe2d18f6f 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -2030,9 +2030,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" [[package]] name = "opaque-debug" @@ -3107,7 +3107,7 @@ dependencies = [ [[package]] name = "tao" version = "0.13.3" -source = "git+https://github.com/tauri-apps/tao?branch=dev#816ca49dc648a5286eab943c6fc936629fd3a596" +source = "git+https://github.com/tauri-apps/tao?branch=dev#0ae71fc887f0cbc498f7a2f5053201b4546f5a4d" dependencies = [ "bitflags", "cairo-rs", @@ -3146,8 +3146,8 @@ dependencies = [ "serde", "unicode-segmentation", "uuid 1.1.2", - "windows 0.37.0", - "windows-implement 0.37.0", + "windows 0.39.0", + "windows-implement", "x11-dl", ] @@ -3893,7 +3893,7 @@ dependencies = [ "webview2-com-macros", "webview2-com-sys", "windows 0.39.0", - "windows-implement 0.39.0", + "windows-implement", ] [[package]] @@ -4024,7 +4024,6 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" dependencies = [ - "windows-implement 0.37.0", "windows_aarch64_msvc 0.37.0", "windows_i686_gnu 0.37.0", "windows_i686_msvc 0.37.0", @@ -4038,7 +4037,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" dependencies = [ - "windows-implement 0.39.0", + "windows-implement", "windows_aarch64_msvc 0.39.0", "windows_i686_gnu 0.39.0", "windows_i686_msvc 0.39.0", @@ -4053,17 +4052,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" dependencies = [ "windows-metadata", - "windows-tokens 0.39.0", -] - -[[package]] -name = "windows-implement" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a1062e555f7d9d66fd1130ed4f7c6ec41a47529ee0850cd0e926d95b26bb14" -dependencies = [ - "syn", - "windows-tokens 0.37.0", + "windows-tokens", ] [[package]] @@ -4073,7 +4062,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" dependencies = [ "syn", - "windows-tokens 0.39.0", + "windows-tokens", ] [[package]] @@ -4095,12 +4084,6 @@ dependencies = [ "windows_x86_64_msvc 0.36.1", ] -[[package]] -name = "windows-tokens" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169" - [[package]] name = "windows-tokens" version = "0.39.0" @@ -4283,7 +4266,7 @@ dependencies = [ [[package]] name = "wry" version = "0.20.2" -source = "git+https://github.com/tauri-apps/wry?branch=dev#854d2226f4161d4fc783f64e79b6a8add31ac4eb" +source = "git+https://github.com/tauri-apps/wry?branch=dev#3624414faa53386660b9d066c00e91e240b7681d" dependencies = [ "base64", "block", @@ -4312,7 +4295,7 @@ dependencies = [ "webkit2gtk-sys", "webview2-com", "windows 0.39.0", - "windows-implement 0.39.0", + "windows-implement", ] [[package]] From c9ad2a73af5b2473e58464bf20c5984e244d4957 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 7 Sep 2022 17:05:11 +0200 Subject: [PATCH 060/423] fix(cli/mobile): use one `write_all` call (#5155) Co-authored-by: Lucas Nogueira --- tooling/cli/src/mobile/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 4e62c6d7e62..dd07b14849b 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -155,13 +155,13 @@ pub fn write_options( options.vars.extend(env_vars()); let name = options_local_socket_name(bundle_identifier, target); let _ = std::fs::remove_file(&name); - let value = serde_json::to_string(&options)?; + let mut value = serde_json::to_string(&options)?; + value.push('\n'); std::thread::spawn(move || { let listener = LocalSocketListener::bind(name).expect("failed to start local socket"); for mut conn in listener.incoming().flatten() { let _ = conn.write_all(value.as_bytes()); - let _ = conn.write_all(b"\n"); } }); @@ -184,10 +184,10 @@ fn read_options(config: &TauriConfig, target: Target) -> CliOptions { let mut attempt = 0; let max_tries = 5; - let (buffer, len) = loop { + let buffer = loop { let mut buffer = String::new(); - if let Ok(len) = conn.read_line(&mut buffer) { - break (buffer, len); + if conn.read_line(&mut buffer).is_ok() { + break buffer; } std::thread::sleep(std::time::Duration::from_secs(1)); attempt += 1; @@ -200,7 +200,7 @@ fn read_options(config: &TauriConfig, target: Target) -> CliOptions { } }; - let options: CliOptions = serde_json::from_str(&buffer[..len - 1]).expect("invalid CLI options"); + let options: CliOptions = serde_json::from_str(&buffer).expect("invalid CLI options"); for (k, v) in &options.vars { set_var(k, v); } From 8ea87e9c9ca8ba4c7017c8281f78aacd08f45785 Mon Sep 17 00:00:00 2001 From: Jeffrey Hutchins Date: Thu, 8 Sep 2022 07:57:10 -0600 Subject: [PATCH 061/423] feat(android): with_webview access for jni execution (#5148) --- .changes/mobile-webview-access.md | 6 ++++++ core/tauri-runtime-wry/src/lib.rs | 16 ++++++++------ core/tauri-runtime-wry/src/webview.rs | 6 ++++++ core/tauri/src/window.rs | 30 ++++++++++++++++++++++----- 4 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 .changes/mobile-webview-access.md diff --git a/.changes/mobile-webview-access.md b/.changes/mobile-webview-access.md new file mode 100644 index 00000000000..9d7fd65ad74 --- /dev/null +++ b/.changes/mobile-webview-access.md @@ -0,0 +1,6 @@ +--- +"tauri-runtime-wry": minor +"tauri": minor +--- + +Support `with_webview` for Android platform alowing execution of JNI code in context. diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 2ebc42de5ca..f054e4fb76c 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -102,9 +102,9 @@ type FileDropHandler = dyn Fn(&Window, WryFileDropEvent) -> bool + 'static; #[cfg(all(desktop, feature = "system-tray"))] pub use tauri_runtime::TrayId; -#[cfg(desktop)] +#[cfg(any(desktop, target_os = "android"))] mod webview; -#[cfg(desktop)] +#[cfg(any(desktop, target_os = "android"))] pub use webview::Webview; #[cfg(all(desktop, feature = "system-tray"))] @@ -987,7 +987,7 @@ pub struct RawWindowHandle(pub raw_window_handle::RawWindowHandle); unsafe impl Send for RawWindowHandle {} pub enum WindowMessage { - #[cfg(desktop)] + #[cfg(any(desktop, target_os = "android"))] WithWebview(Box), AddEventListener(Uuid, Box), AddMenuEventListener(Uuid, Box), @@ -1133,7 +1133,7 @@ pub struct WryDispatcher { unsafe impl Sync for WryDispatcher {} impl WryDispatcher { - #[cfg(desktop)] + #[cfg(any(desktop, target_os = "android"))] pub fn with_webview(&self, f: F) -> Result<()> { send_user_message( &self.context, @@ -2166,7 +2166,7 @@ fn handle_user_message( }); if let Some((Some(window), window_event_listeners, menu_event_listeners)) = w { match window_message { - #[cfg(desktop)] + #[cfg(any(target_os = "android", desktop))] WindowMessage::WithWebview(f) => { if let WindowHandle::Webview(w) = window { #[cfg(any( @@ -2189,13 +2189,17 @@ fn handle_user_message( ns_window: w.ns_window(), }); } - #[cfg(windows)] { f(Webview { controller: w.controller(), }); } + #[cfg(target_os = "android")] + { + use wry::webview::WebviewExtAndroid; + f(w.handle()) + } } } diff --git a/core/tauri-runtime-wry/src/webview.rs b/core/tauri-runtime-wry/src/webview.rs index 8d200fdf30a..f95b19a3d09 100644 --- a/core/tauri-runtime-wry/src/webview.rs +++ b/core/tauri-runtime-wry/src/webview.rs @@ -34,4 +34,10 @@ mod imp { } } +#[cfg(target_os = "android")] +mod imp { + use wry::webview::JniHandle; + pub type Webview = JniHandle; +} + pub use imp::*; diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index fb987564a1d..002bfc85b5d 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -587,11 +587,14 @@ impl<'de, R: Runtime> CommandArg<'de, R> for Window { } /// The platform webview handle. Accessed with [`Window#method.with_webview`]; -#[cfg(all(desktop, feature = "wry"))] -#[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "wry"))))] +#[cfg(all(any(desktop, target_os = "android"), feature = "wry"))] +#[cfg_attr( + doc_cfg, + doc(cfg(all(any(desktop, target_os = "android"), feature = "wry"))) +)] pub struct PlatformWebview(tauri_runtime_wry::Webview); -#[cfg(all(desktop, feature = "wry"))] +#[cfg(all(any(desktop, target_os = "android"), feature = "wry"))] impl PlatformWebview { /// Returns [`webkit2gtk::WebView`] handle. #[cfg(any( @@ -650,6 +653,12 @@ impl PlatformWebview { pub fn ns_window(&self) -> cocoa::base::id { self.0.ns_window } + + /// Returns handle for JNI execution. + #[cfg(target_os = "android")] + pub fn jni_handle(&self) -> tauri_runtime_wry::wry::webview::JniHandle { + self.0 + } } /// APIs specific to the wry runtime. @@ -693,13 +702,24 @@ impl Window { /// let bg_color: cocoa::base::id = msg_send![class!(NSColor), colorWithDeviceRed:0.5 green:0.2 blue:0.4 alpha:1.]; /// let () = msg_send![webview.ns_window(), setBackgroundColor: bg_color]; /// } + /// + /// #[cfg(target_os = "android")] + /// { + /// use jni::objects::JValue; + /// webview.jni_handle().exec(|env, _, webview| { + /// env.call_method(webview, "zoomBy", "(F)V", &[JValue::Float(4.)]).unwrap(); + /// }) + /// } /// }); /// Ok(()) /// }); /// } /// ``` - #[cfg(desktop)] - #[cfg_attr(doc_cfg, doc(cfg(all(feature = "wry", desktop))))] + #[cfg(any(desktop, target_os = "android"))] + #[cfg_attr( + doc_cfg, + doc(cfg(all(feature = "wry", any(desktop, target_os = "android")))) + )] pub fn with_webview( &self, f: F, From 10ab3e2f5ef3f24f67755339587bd7ced13ff2d5 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Mon, 12 Sep 2022 21:46:09 +0200 Subject: [PATCH 062/423] feat(cli/mobile): allow checking `gen` folder into source control (#5146) Co-authored-by: Lucas Nogueira --- tooling/cli/src/helpers/template.rs | 19 ++++++++++--------- tooling/cli/src/info.rs | 2 +- tooling/cli/src/mobile/android/project.rs | 7 ++++++- tooling/cli/src/mobile/ios/project.rs | 13 ++++++++++--- .../cli/templates/mobile/android/.gitignore | 4 +++- .../templates/mobile/android/app/.gitignore | 1 - 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/tooling/cli/src/helpers/template.rs b/tooling/cli/src/helpers/template.rs index 20e64cf01e1..3bc03eeb259 100644 --- a/tooling/cli/src/helpers/template.rs +++ b/tooling/cli/src/helpers/template.rs @@ -54,7 +54,7 @@ pub fn render, D: Serialize>( create_dir_all(&parent)?; created_dirs.push(parent); } - File::create(path) + File::create(path).map(Some) }, ) } @@ -62,7 +62,7 @@ pub fn render, D: Serialize>( pub fn render_with_generator< P: AsRef, D: Serialize, - F: FnMut(&PathBuf) -> std::io::Result, + F: FnMut(&PathBuf) -> std::io::Result>, >( handlebars: &Handlebars<'_>, data: &D, @@ -80,13 +80,14 @@ pub fn render_with_generator< file_path.set_extension("toml"); } } - let mut output_file = out_file_generator(&file_path)?; - if let Some(utf8) = file.contents_utf8() { - handlebars - .render_template_to_write(utf8, &data, &mut output_file) - .expect("Failed to render template"); - } else { - output_file.write_all(file.contents())?; + if let Some(mut output_file) = out_file_generator(&file_path)? { + if let Some(utf8) = file.contents_utf8() { + handlebars + .render_template_to_write(utf8, &data, &mut output_file) + .expect("Failed to render template"); + } else { + output_file.write_all(file.contents())?; + } } } for dir in dir.dirs() { diff --git a/tooling/cli/src/info.rs b/tooling/cli/src/info.rs index 1f5d78f56ab..b4b4b3c90d2 100644 --- a/tooling/cli/src/info.rs +++ b/tooling/cli/src/info.rs @@ -641,7 +641,7 @@ pub fn command(_options: Options) -> Result<()> { InfoBlock::new("MSVC", "").display(); for i in build_tools { indent(6); - println!("{}", format!("{} {}", "-".cyan(), i)); + println!("{} {}", "-".cyan(), i); } } } diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs index 8a06701f66f..16b5cacfe7d 100644 --- a/tooling/cli/src/mobile/android/project.rs +++ b/tooling/cli/src/mobile/android/project.rs @@ -122,6 +122,7 @@ pub fn gen( } let mut options = fs::OpenOptions::new(); + options.write(true); #[cfg(unix)] if path.file_name().unwrap() == OsStr::new("gradlew") { @@ -129,7 +130,11 @@ pub fn gen( options.mode(0o755); } - options.create_new(true).write(true).open(path) + if path.file_name().unwrap() == OsStr::new("BuildTask.kt") || !path.exists() { + options.create(true).open(path).map(Some) + } else { + Ok(None) + } }, ) .with_context(|| "failed to process template")?; diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index a2ddc1a3a94..ea7bf3035da 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -18,8 +18,8 @@ use cargo_mobile::{ use handlebars::Handlebars; use include_dir::{include_dir, Dir}; use std::{ - ffi::OsString, - fs::{create_dir_all, File}, + ffi::{OsStr, OsString}, + fs::{create_dir_all, OpenOptions}, path::{Component, PathBuf}, }; @@ -136,7 +136,14 @@ pub fn gen( created_dirs.push(parent); } - File::create(path) + let mut options = OpenOptions::new(); + options.write(true); + + if path.file_name().unwrap() == OsStr::new("BuildTask.kt") || !path.exists() { + options.create(true).open(path).map(Some) + } else { + Ok(None) + } }, ) .with_context(|| "failed to process template")?; diff --git a/tooling/cli/templates/mobile/android/.gitignore b/tooling/cli/templates/mobile/android/.gitignore index aa724b77071..09f56a44322 100644 --- a/tooling/cli/templates/mobile/android/.gitignore +++ b/tooling/cli/templates/mobile/android/.gitignore @@ -8,7 +8,9 @@ /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store -/build +build +/buildSrc/src/main/{{package-path}}/kotlin/BuildTask.kt +/buildSrc/src/main/{{package-path}}/kotlin/RustPlugin.kt /captures .externalNativeBuild .cxx diff --git a/tooling/cli/templates/mobile/android/app/.gitignore b/tooling/cli/templates/mobile/android/app/.gitignore index 357fc705cec..5da46064800 100644 --- a/tooling/cli/templates/mobile/android/app/.gitignore +++ b/tooling/cli/templates/mobile/android/app/.gitignore @@ -1,2 +1 @@ -/build /src/main/{{package-path}}/generated From 348b26ffed6c0b031df5350a57b238f1c71c1c03 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 31 Oct 2022 13:27:22 -0300 Subject: [PATCH 063/423] fix(ci): minimum rustc is now 1.59 --- .github/workflows/test-core.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index b79063bd5c7..e1bff7f5a7e 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -56,14 +56,14 @@ jobs: - { target: aarch64-apple-ios, os: macos-latest, - toolchain: '1.57.0', + toolchain: '1.59.0', cross: false, acommand: 'build' } - { target: aarch64-linux-android, os: ubuntu-latest, - toolchain: '1.57.0', + toolchain: '1.59.0', cross: true, command: 'build' } From 69414a487ff680875cff5924c39efc01e2f501c8 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 9 Nov 2022 11:38:08 -0300 Subject: [PATCH 064/423] chore(cli): lint --- examples/api/src-tauri/Cargo.lock | 19 ++----------------- examples/api/src-tauri/Cargo.toml | 3 --- tooling/cli/src/interface/rust/desktop.rs | 2 +- tooling/cli/src/mobile/ios/build.rs | 7 +++---- tooling/cli/src/mobile/ios/project.rs | 2 +- 5 files changed, 7 insertions(+), 26 deletions(-) diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index e604447b0db..bd54e02d6f1 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -103,7 +103,6 @@ version = "0.1.0" dependencies = [ "android_logger", "env_logger 0.9.3", - "jni 0.19.0", "log", "serde", "serde_json", @@ -1501,20 +1500,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "jni" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - [[package]] name = "jni-sys" version = "0.3.0" @@ -1626,7 +1611,7 @@ dependencies = [ "libc", "neli", "thiserror", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -3110,7 +3095,7 @@ dependencies = [ "gtk", "image", "instant", - "jni 0.20.0", + "jni", "lazy_static", "libappindicator", "libc", diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 6dad7c673e2..8bec982b331 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -6,9 +6,6 @@ edition = "2021" rust-version = "1.59" license = "Apache-2.0 OR MIT" -[patch.crates-io] -tao = { git = "https://github.com/tauri-apps/tao", branch = "dev" } - [lib] crate-type = ["staticlib", "cdylib", "rlib"] diff --git a/tooling/cli/src/interface/rust/desktop.rs b/tooling/cli/src/interface/rust/desktop.rs index e477e6eea7b..b84c6e5526c 100644 --- a/tooling/cli/src/interface/rust/desktop.rs +++ b/tooling/cli/src/interface/rust/desktop.rs @@ -383,7 +383,7 @@ fn rename_app(bin_path: &Path, product_name: Option<&str>) -> crate::Result Result<()> { let mut app_version = config.bundle_version().clone(); if let Some(build_number) = options.build_number { app_version.push_extra(build_number); @@ -152,11 +152,10 @@ fn run_build( out_files.push(path); } - anyhow::Result::Ok(()) + Ok(()) }, ) - .map_err(|e: TargetInvalid| anyhow::anyhow!(e.to_string()))? - .map_err(|e: anyhow::Error| e)?; + .map_err(|e: TargetInvalid| anyhow::anyhow!(e.to_string()))??; log_finished(out_files, "IPA"); diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index ea7bf3035da..051af0eeedd 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -172,7 +172,7 @@ pub fn gen( // often necessary. println!("Generating Xcode project..."); bossy::Command::impure("xcodegen") - .with_args(&["generate", "--spec"]) + .with_args(["generate", "--spec"]) .with_arg(dest.join("project.yml")) .run_and_wait() .with_context(|| "failed to run `xcodegen`")?; From 7c26514340f0637ed9052cb0f534436a78b60284 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Fri, 11 Nov 2022 11:41:20 -0300 Subject: [PATCH 065/423] fix(cli): kill beforeDevCommand process when mobile fails to compile --- tooling/cli/src/dev.rs | 2 +- tooling/cli/src/mobile/android/dev.rs | 5 ++++- tooling/cli/src/mobile/ios/dev.rs | 5 ++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index ca4bc4269aa..af04cdda5e5 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -357,7 +357,7 @@ fn check_for_updates() -> Result<()> { Ok(()) } -fn kill_before_dev_process() { +pub fn kill_before_dev_process() { if let Some(child) = BEFORE_DEV.get() { let child = child.lock().unwrap(); KILL_BEFORE_DEV_FLAG diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index d9814bbf123..67617e5e16a 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -154,7 +154,10 @@ fn run_dev( log::error!("{}", e); open_and_wait(config, &env) } - Err(e) => Err(e.into()), + Err(e) => { + crate::dev::kill_before_dev_process(); + Err(e.into()) + } } } }, diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 5b205abdb0d..6fd4e66184f 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -128,7 +128,10 @@ fn run_dev( log::error!("{}", e); open_and_wait(config, &env) } - Err(e) => Err(e.into()), + Err(e) => { + crate::dev::kill_before_dev_process(); + Err(e.into()) + } } } }, From f9b529a96eadf7b7b9bc6de50063c285d45f93d9 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Fri, 11 Nov 2022 11:43:25 -0300 Subject: [PATCH 066/423] feat(cli): update project.yml syntax --- tooling/cli/src/mobile/ios/xcode_script.rs | 57 +++++++++++++++++++- tooling/cli/src/mobile/mod.rs | 1 + tooling/cli/templates/mobile/ios/project.yml | 30 ++++------- 3 files changed, 68 insertions(+), 20 deletions(-) diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 1f2da48e3c6..59e509a2b7a 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -14,6 +14,15 @@ pub struct Options { /// Value of `SDKROOT` env var #[clap(long)] sdk_root: PathBuf, + /// Value of `FRAMEWORK_SEARCH_PATHS` env var + #[clap(long)] + framework_search_paths: String, + /// Value of `GCC_PREPROCESSOR_DEFINITIONS` env var + #[clap(long)] + gcc_preprocessor_definitions: String, + /// Value of `HEADER_SEARCH_PATHS` env var + #[clap(long)] + header_search_paths: String, /// Value of `CONFIGURATION` env var #[clap(long)] configuration: String, @@ -38,6 +47,17 @@ pub fn command(options: Options) -> Result<()> { } } + // `xcode-script` is ran from the `gen/apple` folder. + std::env::set_current_dir( + std::env::current_dir() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap(), + ) + .unwrap(); + let profile = profile_from_configuration(&options.configuration); let macos = macos_from_platform(&options.platform); @@ -46,7 +66,9 @@ pub fn command(options: Options) -> Result<()> { // The `PATH` env var Xcode gives us is missing any additions // made by the user's profile, so we'll manually add cargo's // `PATH`. - let env = env.prepend_to_path(util::home_dir()?.join(".cargo/bin")); + let env = env + .explicit_env_vars(cli_options.vars) + .prepend_to_path(util::home_dir()?.join(".cargo/bin")); if !options.sdk_root.is_dir() { return Err(anyhow::anyhow!( @@ -62,10 +84,42 @@ pub fn command(options: Options) -> Result<()> { )); } + // Host flags that are used by build scripts + let macos_isysroot = { + let macos_sdk_root = options + .sdk_root + .join("../../../../MacOSX.platform/Developer/SDKs/MacOSX.sdk"); + if !macos_sdk_root.is_dir() { + return Err(anyhow::anyhow!( + "Invalid SDK root {}", + macos_sdk_root.display() + )); + } + format!("-isysroot {}", macos_sdk_root.display()) + }; + let mut host_env = HashMap::<&str, &OsStr>::new(); host_env.insert("RUST_BACKTRACE", "1".as_ref()); + host_env.insert("CFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); + host_env.insert("CXXFLAGS_x86_64_apple_darwin", macos_isysroot.as_ref()); + + host_env.insert( + "OBJC_INCLUDE_PATH_x86_64_apple_darwin", + include_dir.as_os_str(), + ); + + host_env.insert( + "FRAMEWORK_SEARCH_PATHS", + options.framework_search_paths.as_ref(), + ); + host_env.insert( + "GCC_PREPROCESSOR_DEFINITIONS", + options.gcc_preprocessor_definitions.as_ref(), + ); + host_env.insert("HEADER_SEARCH_PATHS", options.header_search_paths.as_ref()); + let macos_target = Target::macos(); let isysroot = format!("-isysroot {}", options.sdk_root.display()); @@ -76,6 +130,7 @@ pub fn command(options: Options) -> Result<()> { "arm64" => "aarch64_apple_ios", "arm64-sim" => "aarch64_apple_ios_sim", "x86_64" => "x86_64_apple_ios", + "Simulator" => continue, _ => { return Err(anyhow::anyhow!( "Arch specified by Xcode was invalid. {} isn't a known arch", diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index c8cdbe3ab57..67adff88069 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -138,6 +138,7 @@ fn env_vars() -> HashMap { if (k.starts_with("TAURI") && k != "TAURI_PRIVATE_KEY" && k != "TAURI_KEY_PASSWORD") || k.starts_with("WRY") || k == "TMPDIR" + || k == "PATH" { vars.insert(k.into_owned(), v); } diff --git a/tooling/cli/templates/mobile/ios/project.yml b/tooling/cli/templates/mobile/ios/project.yml index af7263b22a7..7f190c73cef 100644 --- a/tooling/cli/templates/mobile/ios/project.yml +++ b/tooling/cli/templates/mobile/ios/project.yml @@ -76,11 +76,11 @@ targets: ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true groups: [app] dependencies: - - target: lib_{{app.name}}_iOS - embed: false - link: false - framework: lib{{snake-case app.name}}.a embed: false + {{~#each ios-libraries}} + - framework: {{this}} + embed: false{{/each}} {{~#each ios-vendor-frameworks}} - framework: {{prefix-path this}}{{/each}} {{~#each ios-vendor-sdks}} @@ -94,7 +94,6 @@ targets: {{~#each ios-frameworks}} - sdk: {{this}}.framework{{/each}} - sdk: WebKit.framework - {{~#if ios-pre-build-scripts}} preBuildScripts: {{~#each ios-pre-build-scripts}}{{#if this.path}} - path {{this.path}}{{/if}}{{#if this.script}} @@ -113,8 +112,14 @@ targets: runOnlyWhenInstalling: {{this.run-only-when-installing}}{{/if}}{{#if this.based-on-dependency-analysis}} basedOnDependencyAnalysis: {{this.based-on-dependency-analysis}}{{/if}}{{#if this.discovered-dependency-file}} discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} - {{~/each~}} - {{~/if~}} + {{~/each}} + + - script: {{ tauri-binary }} {{ tauri-binary-args-str }} -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths "${FRAMEWORK_SEARCH_PATHS:?}" --header-search-paths "${HEADER_SEARCH_PATHS:?}" --gcc-preprocessor-definitions "${GCC_PREPROCESSOR_DEFINITIONS:?}" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} + name: Build Rust Code + basedOnDependencyAnalysis: false + outputFiles: + - $(SRCROOT)/target/aarch64-apple-ios/${CONFIGURATION}/deps/lib{{snake-case app.name}}.a + - $(SRCROOT)/target/x86_64-apple-ios/${CONFIGURATION}/deps/lib{{snake-case app.name}}.a {{~#if ios-post-compile-scripts}} postCompileScripts: {{~#each ios-post-compile-scripts}}{{#if this.path}} @@ -157,16 +162,3 @@ targets: discoveredDependencyFile: {{this.discovered-dependency-file}}{{/if}} {{~/each~}} {{~/if}} - - lib_{{app.name}}_iOS: - type: "" - platform: iOS - settings: - ENABLE_BITCODE: false - ARCHS: [{{join ios-valid-archs}}] - VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} - legacy: - toolPath: {{ tauri-binary }} - arguments: {{ tauri-binary-args-str }} -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?} - passSettings: false # prevents evil linker errors - workingDirectory: $(SRCROOT)/../.. From 8c576222ba072481f471116f30e011a1cd0808f9 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 14 Nov 2022 18:31:51 -0300 Subject: [PATCH 067/423] feat(cli): find development teams for iOS development (#5627) --- tooling/cli/Cargo.lock | 2 +- tooling/cli/src/info.rs | 23 ++++++++++++++++++++ tooling/cli/src/mobile/init.rs | 2 ++ tooling/cli/src/mobile/ios.rs | 21 ++++++++++++++++-- tooling/cli/src/mobile/ios/dev.rs | 36 ++++++++++++++++++++++++++++++- 5 files changed, 80 insertions(+), 4 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 073c81bceb6..dde6e91dc07 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -387,7 +387,7 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#bf876273afc0b3e5e1e2cdcc232cdfe81ff4428d" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#22337b1fbde028e8c66b00e9f9ac97f60146bbb5" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/src/info.rs b/tooling/cli/src/info.rs index c1b3fa1b7b4..4db28bf6ed5 100644 --- a/tooling/cli/src/info.rs +++ b/tooling/cli/src/info.rs @@ -885,6 +885,29 @@ pub fn command(_options: Options) -> Result<()> { } } + #[cfg(target_os = "macos")] + if tauri_dir.is_some() { + let p = tauri_dir.as_ref().unwrap(); + if p.join("gen/apple").exists() { + let teams = cargo_mobile::apple::teams::find_development_teams().unwrap_or_default(); + Section("iOS").display(); + InfoBlock::new( + "Teams", + if teams.is_empty() { + "None".red().to_string() + } else { + teams + .iter() + .map(|t| format!("{} (ID: {})", t.name, t.id)) + .collect::>() + .join(", ") + .to_string() + }, + ) + .display(); + } + } + Ok(()) } diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 991e85d1243..99fedd9fc0d 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -135,6 +135,8 @@ pub fn exec( #[cfg(target_os = "macos")] // Generate Xcode project Target::Ios => { + // the apple development team is not required on init + std::env::set_var(super::ios::APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME, ""); let (app, config, metadata) = super::ios::get_config(Some(app), tauri_config_, &Default::default()); map.insert("apple", &config); diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index a7f5e1275d8..c2783ba92b0 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -11,6 +11,7 @@ use cargo_mobile::{ device::Device, ios_deploy, simctl, target::Target, + teams::find_development_teams, }, config::app::App, env::Env, @@ -32,6 +33,7 @@ use crate::{ }; use std::{ + process::exit, thread::{sleep, spawn}, time::Duration, }; @@ -42,6 +44,8 @@ mod open; pub(crate) mod project; mod xcode_script; +pub const APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME: &str = "TAURI_APPLE_DEVELOPMENT_TEAM"; + #[derive(Parser)] #[clap( author, @@ -98,10 +102,23 @@ pub fn get_config( let ios_options = cli_options.clone(); let raw = RawAppleConfig { - development_team: std::env::var("TAURI_APPLE_DEVELOPMENT_TEAM") + development_team: std::env::var(APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME) .ok() .or_else(|| config.tauri.bundle.ios.development_team.clone()) - .expect("you must set `tauri > iOS > developmentTeam` config value or the `TAURI_APPLE_DEVELOPMENT_TEAM` environment variable"), + .unwrap_or_else(|| { + let teams = find_development_teams().unwrap_or_default(); + match teams.len() { + 0 => { + log::error!("No code signing certificates found. You must add one and set the certificate development team ID on the `tauri > iOS > developmentTeam` config value or the `{APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME}` environment variable. To list the available certificates, run `tauri info`."); + exit(1); + } + 1 => teams.first().unwrap().id.clone(), + _ => { + log::error!("You must set the code signing certificate development team ID on the `tauri > iOS > developmentTeam` config value or the `{APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME}` environment variable. Available certificates: {}", teams.iter().map(|t| format!("{} (ID: {})", t.name, t.id)).collect::>().join(", ")); + exit(1); + } + } + }), ios_features: ios_options.features.clone(), bundle_version: config.package.version.clone(), bundle_version_short: config.package.version.clone(), diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 6fd4e66184f..732010e7b69 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -1,5 +1,6 @@ use super::{ device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, MobileTarget, + APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME, }; use crate::{ helpers::{config::get as get_tauri_config, flock}, @@ -10,11 +11,14 @@ use crate::{ use clap::{ArgAction, Parser}; use cargo_mobile::{ - apple::config::Config as AppleConfig, + apple::{config::Config as AppleConfig, teams::find_development_teams}, config::app::App, env::Env, opts::{NoiseLevel, Profile}, }; +use dialoguer::{theme::ColorfulTheme, Select}; + +use std::env::{set_var, var_os}; #[derive(Debug, Clone, Parser)] #[clap(about = "iOS dev")] @@ -57,6 +61,36 @@ impl From for crate::dev::Options { } pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { + if var_os(APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME).is_none() { + if let Ok(teams) = find_development_teams() { + let index = match teams.len() { + 0 => None, + 1 => Some(0), + _ => { + let index = Select::with_theme(&ColorfulTheme::default()) + .items( + &teams + .iter() + .map(|t| format!("{} (ID: {})", t.name, t.id)) + .collect::>(), + ) + .default(0) + .interact()?; + Some(index) + } + }; + if let Some(index) = index { + let team = teams.get(index).unwrap(); + log::info!( + "Using development team `{}`. To make this permanent, set the `{}` environment variable to `{}`", + team.name, + APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME, + team.id + ); + set_var(APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME, &team.id); + } + } + } with_config( Some(Default::default()), |app, config, _metadata, _cli_options| { From 634c6b832ca2629651ff25761f45b2f8f43c36a8 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 14 Nov 2022 19:59:46 -0300 Subject: [PATCH 068/423] feat(cli): update cargo-mobile including Android env var cleanup --- tooling/cli/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index dde6e91dc07..8a6d5322990 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -387,7 +387,7 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#22337b1fbde028e8c66b00e9f9ac97f60146bbb5" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#76e6e8e7ba289ff5e2c74f72d003f19572c9dba9" dependencies = [ "cocoa", "colored 1.9.3", From 658bb1165e34a718a8777aa172553a6fd092c06a Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Fri, 18 Nov 2022 13:10:20 -0300 Subject: [PATCH 069/423] feat(cli): automatically enable native-tls-vendored feature on mobile (#5651) --- examples/api/src-tauri/Cargo.lock | 10 ----- examples/api/src-tauri/Cargo.toml | 1 - tooling/cli/src/build.rs | 22 ++++++----- tooling/cli/src/interface/rust.rs | 50 +++++++++++++++++++++---- tooling/cli/src/mobile/android/build.rs | 2 +- tooling/cli/src/mobile/ios/build.rs | 2 +- 6 files changed, 56 insertions(+), 31 deletions(-) diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index bd54e02d6f1..ac88c602827 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -2039,15 +2039,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "openssl-src" -version = "111.22.0+1.1.1q" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853" -dependencies = [ - "cc", -] - [[package]] name = "openssl-sys" version = "0.9.77" @@ -2057,7 +2048,6 @@ dependencies = [ "autocfg", "cc", "libc", - "openssl-src", "pkg-config", "vcpkg", ] diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 8bec982b331..5deaab6e6f7 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -30,7 +30,6 @@ features = [ "macos-private-api", "windows7-compat", "reqwest-client", - "reqwest-native-tls-vendored", "system-tray", "updater" ] diff --git a/tooling/cli/src/build.rs b/tooling/cli/src/build.rs index 22624f23877..6bf24ac5e6b 100644 --- a/tooling/cli/src/build.rs +++ b/tooling/cli/src/build.rs @@ -57,7 +57,7 @@ pub struct Options { } pub fn command(mut options: Options) -> Result<()> { - let mut interface = setup(&mut options)?; + let mut interface = setup(&mut options, false)?; let config = get_config(options.config.as_deref())?; let config_guard = config.lock().unwrap(); @@ -222,7 +222,7 @@ pub fn command(mut options: Options) -> Result<()> { Ok(()) } -pub fn setup(options: &mut Options) -> Result { +pub fn setup(options: &mut Options, mobile: bool) -> Result { let (merge_config, merge_config_path) = if let Some(config) = &options.config { if config.starts_with('{') { (Some(config.to_string()), None) @@ -309,11 +309,11 @@ pub fn setup(options: &mut Options) -> Result { } if !out_folders.is_empty() { return Err(anyhow::anyhow!( - "The configured distDir includes the `{:?}` {}. Please isolate your web assets on a separate folder and update `tauri.conf.json > build > distDir`.", - out_folders, - if out_folders.len() == 1 { "folder" }else { "folders" } - ) - ); + "The configured distDir includes the `{:?}` {}. Please isolate your web assets on a separate folder and update `tauri.conf.json > build > distDir`.", + out_folders, + if out_folders.len() == 1 { "folder" }else { "folders" } + ) + ); } } @@ -321,9 +321,11 @@ pub fn setup(options: &mut Options) -> Result { options.runner = config_.build.runner.clone(); } - if let Some(list) = options.features.as_mut() { - list.extend(config_.build.features.clone().unwrap_or_default()); - } + options + .features + .get_or_insert(Vec::new()) + .extend(config_.build.features.clone().unwrap_or_default()); + interface.build_options(&mut options.features, mobile); Ok(interface) } diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 27c5b2577cb..59513829332 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -148,11 +148,7 @@ impl Interface for Rust { &self.app_settings } - fn build(&mut self, mut options: Options) -> crate::Result<()> { - options - .features - .get_or_insert(Vec::new()) - .push("custom-protocol".into()); + fn build(&mut self, options: Options) -> crate::Result<()> { desktop::build( options, &self.app_settings, @@ -172,10 +168,11 @@ impl Interface for Rust { let mut run_args = Vec::new(); dev_options( + false, &mut options.args, &mut run_args, &mut options.features, - self.app_settings.manifest.features(), + &self.app_settings, ); if options.no_watch { @@ -206,10 +203,11 @@ impl Interface for Rust { ) -> crate::Result<()> { let mut run_args = Vec::new(); dev_options( + true, &mut options.args, &mut run_args, &mut options.features, - self.app_settings.manifest.features(), + &self.app_settings, ); if options.no_watch { @@ -346,11 +344,37 @@ fn lookup(dir: &Path, mut f: F) { } } +fn shared_options( + mobile: bool, + features: &mut Option>, + app_settings: &RustAppSettings, +) { + if mobile { + let all_features = app_settings + .manifest + .all_enabled_features(if let Some(f) = features { f } else { &[] }); + if !all_features.contains(&"tauri/native-tls-vendored".into()) + && !all_features.contains(&"tauri/reqwest-native-tls-vendored".into()) + { + if all_features.contains(&"tauri/reqwest-client".into()) { + features + .get_or_insert(Vec::new()) + .push("tauri/reqwest-native-tls-vendored".into()); + } else { + features + .get_or_insert(Vec::new()) + .push("tauri/native-tls-vendored".into()); + } + } + } +} + fn dev_options( + mobile: bool, args: &mut Vec, run_args: &mut Vec, features: &mut Option>, - manifest_features: HashMap>, + app_settings: &RustAppSettings, ) { let mut dev_args = Vec::new(); let mut reached_run_args = false; @@ -365,7 +389,10 @@ fn dev_options( } *args = dev_args; + shared_options(mobile, features, app_settings); + if !args.contains(&"--no-default-features".into()) { + let manifest_features = app_settings.manifest.features(); let enable_features: Vec = manifest_features .get("default") .cloned() @@ -387,6 +414,13 @@ fn dev_options( } impl Rust { + pub fn build_options(&self, features: &mut Option>, mobile: bool) { + features + .get_or_insert(Vec::new()) + .push("custom-protocol".into()); + shared_options(mobile, features, &self.app_settings); + } + fn run_dev( &mut self, options: Options, diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index 398510a24e2..d46aa2fb0c0 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -119,7 +119,7 @@ fn run_build( }; let mut build_options = options.clone().into(); - let interface = crate::build::setup(&mut build_options)?; + let interface = crate::build::setup(&mut build_options, true)?; let app_settings = interface.app_settings(); let bin_path = app_settings.app_binary_path(&InterfaceOptions { diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index ce0269cacf4..47f1fcc46da 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -105,7 +105,7 @@ fn run_build( }; let mut build_options = options.clone().into(); - let interface = crate::build::setup(&mut build_options)?; + let interface = crate::build::setup(&mut build_options, true)?; let app_settings = interface.app_settings(); let bin_path = app_settings.app_binary_path(&InterfaceOptions { From 538e21e2e7102426f894d77f18b9fdafca0dc50a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sat, 19 Nov 2022 14:25:23 -0300 Subject: [PATCH 070/423] fix(cli): iOS development team is required on init --- tooling/cli/src/mobile/init.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 99fedd9fc0d..991e85d1243 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -135,8 +135,6 @@ pub fn exec( #[cfg(target_os = "macos")] // Generate Xcode project Target::Ios => { - // the apple development team is not required on init - std::env::set_var(super::ios::APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME, ""); let (app, config, metadata) = super::ios::get_config(Some(app), tauri_config_, &Default::default()); map.insert("apple", &config); From 03d6c6a68faadc6fb5b8ca7953826dbf6b0746cc Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 20 Nov 2022 09:48:02 -0300 Subject: [PATCH 071/423] refactor(cli): use jsonrpsee for mobile CLI options communication (#5657) --- tooling/cli/Cargo.lock | 450 +++++++++++++++++++----- tooling/cli/Cargo.toml | 2 +- tooling/cli/src/mobile/android.rs | 3 +- tooling/cli/src/mobile/android/build.rs | 17 +- tooling/cli/src/mobile/android/dev.rs | 12 +- tooling/cli/src/mobile/ios.rs | 2 +- tooling/cli/src/mobile/ios/build.rs | 17 +- tooling/cli/src/mobile/ios/dev.rs | 12 +- tooling/cli/src/mobile/mod.rs | 101 +++--- 9 files changed, 420 insertions(+), 196 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 8a6d5322990..265b0fe21af 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -98,21 +98,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] -name = "async-channel" -version = "1.7.1" +name = "arrayvec" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] -name = "async-task" -version = "4.3.0" +name = "async-lock" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +dependencies = [ + "event-listener", + "futures-lite", +] [[package]] name = "async-trait" @@ -125,12 +124,6 @@ dependencies = [ "syn", ] -[[package]] -name = "atomic-waker" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" - [[package]] name = "attohttpc" version = "0.23.1" @@ -223,6 +216,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -280,6 +282,15 @@ dependencies = [ "generic-array 0.12.4", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.5", +] + [[package]] name = "block-buffer" version = "0.10.2" @@ -298,20 +309,6 @@ dependencies = [ "byte-tools", ] -[[package]] -name = "blocking" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" -dependencies = [ - "async-channel", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", -] - [[package]] name = "bstr" version = "0.2.17" @@ -378,16 +375,10 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#76e6e8e7ba289ff5e2c74f72d003f19572c9dba9" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#068cda074b0ff0c42f8d5297d8a2d5c29fd75c03" dependencies = [ "cocoa", "colored 1.9.3", @@ -575,15 +566,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" -[[package]] -name = "concurrent-queue" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" -dependencies = [ - "cache-padded", -] - [[package]] name = "console" version = "0.15.0" @@ -873,6 +855,15 @@ dependencies = [ "generic-array 0.12.4", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.5", +] + [[package]] name = "digest" version = "0.10.3" @@ -1221,7 +1212,6 @@ checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", - "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -1244,17 +1234,6 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" -[[package]] -name = "futures-executor" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - [[package]] name = "futures-io" version = "0.3.24" @@ -1299,6 +1278,16 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper", +] + [[package]] name = "futures-util" version = "0.3.24" @@ -1408,6 +1397,51 @@ dependencies = [ "regex", ] +[[package]] +name = "gloo-net" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec897194fb9ac576c708f63d35604bc58f2a262b8cec0fabfed26f3991255f21" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40913a05c8297adca04392f707b1e73b12ba7b8eab7244a4961580b1fd34063c" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "h2" version = "0.3.14" @@ -1608,6 +1642,22 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d" +dependencies = [ + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "webpki-roots", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1729,29 +1779,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "interprocess" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c58ec7fbda1df9a93f587b780659db3c99f61f4be27f9c82c9b37684ffd0366" -dependencies = [ - "blocking", - "cfg-if", - "futures", - "intmap", - "libc", - "once_cell", - "spinning", - "thiserror", - "winapi", -] - -[[package]] -name = "intmap" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9" - [[package]] name = "ipnet" version = "2.5.0" @@ -1848,6 +1875,153 @@ dependencies = [ "serde", ] +[[package]] +name = "jsonrpsee" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5af9646e616e37c61093ef85e25bd883ae0c22e2fa1e6eedfe590048247116e3" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-wasm-client", + "jsonrpsee-ws-client", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e85cfc9c2f17eab237fdfa2efe5c1608fd06a90e1e0d7fd7b10f2d0e153f375" +dependencies = [ + "anyhow", + "futures-channel", + "futures-timer", + "futures-util", + "gloo-net", + "http", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "webpki-roots", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673d68136e2f0f67323bab95b3a7177df26ac21ddbf395fc32d60f30fe5a1364" +dependencies = [ + "anyhow", + "arrayvec 0.7.2", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "globset", + "hyper", + "jsonrpsee-types", + "parking_lot", + "rand 0.8.5", + "rustc-hash", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", + "wasm-bindgen-futures", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42007820863ab29f3adeacf43886ef54abaedb35bc33dada25771db4e1f94de4" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core", + "jsonrpsee-types", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78f34520019321bd466d00620606db2f40827362d0185b3b95040328eb502f6" +dependencies = [ + "futures-channel", + "futures-util", + "http", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7985a27ee315c7c8c5c5033ac133e9472aec881edfd947780f5a9970efb7cbbf" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46811fcec615d8e58228e7e281b3238693b26da1eb2469ac208af40a217bc8d9" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480fc9922f10b8fca3f07c07c51e137ddcf13fd60a304f117cfaa9e9bf41c60b" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + [[package]] name = "jsonschema" version = "0.16.0" @@ -1927,7 +2101,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", "bitflags", "cfg-if", "ryu", @@ -3060,6 +3234,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -3081,6 +3261,27 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +dependencies = [ + "base64", +] + [[package]] name = "ryu" version = "1.0.10" @@ -3229,6 +3430,12 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "serde" version = "1.0.137" @@ -3349,6 +3556,19 @@ dependencies = [ "opaque-debug 0.2.3", ] +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "sha-1" version = "0.10.0" @@ -3429,6 +3649,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64", + "bytes", + "futures", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.9.8", +] + [[package]] name = "spin" version = "0.5.2" @@ -3444,15 +3680,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "spinning" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b" -dependencies = [ - "lock_api", -] - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3659,8 +3886,8 @@ dependencies = [ "ignore", "image", "include_dir", - "interprocess", "json-patch", + "jsonrpsee", "jsonschema", "kuchiki", "libc", @@ -3923,6 +4150,28 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-tungstenite" version = "0.17.2" @@ -3943,6 +4192,7 @@ checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -4026,9 +4276,21 @@ dependencies = [ "cfg-if", "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.29" @@ -4288,6 +4550,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" dependencies = [ "cfg-if", + "serde", + "serde_json", "wasm-bindgen-macro", ] diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 9afe8dd7582..47e67c0f41a 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -42,7 +42,7 @@ path = "src/main.rs" # cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } textwrap = { version = "0.11.0", features = ["term_size"] } -interprocess = "1" +jsonrpsee = { version = "0.16", features = [ "client", "server" ]} thiserror = "1" sublime_fuzzy = "0.7" clap = { version = "4.0", features = [ "derive" ] } diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 915cebba2e3..68170d7ff33 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -132,8 +132,7 @@ fn with_config( let tauri_config = get_tauri_config(None)?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - let cli_options = - cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Android)); + let cli_options = cli_options.unwrap_or_else(read_options); let (app, config, metadata) = get_config(None, tauri_config_, &cli_options); (app, config, metadata, cli_options) }; diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index d46aa2fb0c0..02b6268079b 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -3,7 +3,7 @@ use super::{ MobileTarget, }; use crate::{ - helpers::{config::get as get_tauri_config, flock}, + helpers::flock, interface::{AppSettings, Interface, Options as InterfaceOptions}, mobile::{write_options, CliOptions}, Result, @@ -77,11 +77,11 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { ensure_init(config.project_dir(), MobileTarget::Android)?; - let env = env()?; + let mut env = env()?; init_dot_cargo(app, Some((&env, config)))?; let open = options.open; - run_build(options, config, &env, noise_level)?; + run_build(options, config, &mut env, noise_level)?; if open { open_and_wait(config, &env); @@ -96,7 +96,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { fn run_build( mut options: Options, config: &AndroidConfig, - env: &Env, + env: &mut Env, noise_level: NoiseLevel, ) -> Result<()> { let profile = if options.debug { @@ -111,13 +111,6 @@ fn run_build( options.aab = true; } - let bundle_identifier = { - let tauri_config = get_tauri_config(None)?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - tauri_config_.tauri.bundle.identifier.clone() - }; - let mut build_options = options.clone().into(); let interface = crate::build::setup(&mut build_options, true)?; @@ -135,7 +128,7 @@ fn run_build( noise_level, vars: Default::default(), }; - write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; + let _handle = write_options(cli_options, &mut env.base)?; options .features diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 67617e5e16a..083a814456f 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -3,7 +3,7 @@ use super::{ MobileTarget, }; use crate::{ - helpers::{config::get as get_tauri_config, flock}, + helpers::flock, interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions}, mobile::{write_options, CliOptions, DevChild, DevProcess}, Result, @@ -94,13 +94,6 @@ fn run_dev( let mut dev_options = options.clone().into(); let mut interface = crate::dev::setup(&mut dev_options)?; - let bundle_identifier = { - let tauri_config = get_tauri_config(None)?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - tauri_config_.tauri.bundle.identifier.clone() - }; - let app_settings = interface.app_settings(); let bin_path = app_settings.app_binary_path(&InterfaceOptions { debug: !dev_options.release_mode, @@ -125,13 +118,14 @@ fn run_dev( no_watch: options.no_watch, }, |options| { + let mut env = env.clone(); let cli_options = CliOptions { features: options.features.clone(), args: options.args.clone(), noise_level, vars: Default::default(), }; - write_options(cli_options, &bundle_identifier, MobileTarget::Android)?; + let _handle = write_options(cli_options, &mut env.base)?; if open { open_and_wait(config, &env) diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index c2783ba92b0..2edda014b37 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -147,7 +147,7 @@ fn with_config( let tauri_config = get_tauri_config(None)?; let tauri_config_guard = tauri_config.lock().unwrap(); let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - let cli_options = cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Ios)); + let cli_options = cli_options.unwrap_or_else(read_options); let (app, config, metadata) = get_config(None, tauri_config_, &cli_options); (app, config, metadata, cli_options) }; diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index 47f1fcc46da..679b0c06305 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -3,7 +3,7 @@ use super::{ MobileTarget, }; use crate::{ - helpers::{config::get as get_tauri_config, flock}, + helpers::flock, interface::{AppSettings, Interface, Options as InterfaceOptions}, mobile::{write_options, CliOptions}, Result, @@ -69,11 +69,11 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { |app, config, _metadata, _cli_options| { ensure_init(config.project_dir(), MobileTarget::Ios)?; - let env = env()?; + let mut env = env()?; init_dot_cargo(app, None)?; let open = options.open; - run_build(options, config, &env, noise_level)?; + run_build(options, config, &mut env, noise_level)?; if open { open_and_wait(config, &env); @@ -88,7 +88,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { fn run_build( mut options: Options, config: &AppleConfig, - env: &Env, + env: &mut Env, noise_level: NoiseLevel, ) -> Result<()> { let profile = if options.debug { @@ -97,13 +97,6 @@ fn run_build( Profile::Release }; - let bundle_identifier = { - let tauri_config = get_tauri_config(None)?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - tauri_config_.tauri.bundle.identifier.clone() - }; - let mut build_options = options.clone().into(); let interface = crate::build::setup(&mut build_options, true)?; @@ -121,7 +114,7 @@ fn run_build( noise_level, vars: Default::default(), }; - write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; + let _handle = write_options(cli_options, env)?; options .features diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 732010e7b69..a80bf1e37ad 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -3,7 +3,7 @@ use super::{ APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME, }; use crate::{ - helpers::{config::get as get_tauri_config, flock}, + helpers::flock, interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions}, mobile::{write_options, CliOptions, DevChild, DevProcess}, Result, @@ -109,13 +109,6 @@ fn run_dev( let mut dev_options = options.clone().into(); let mut interface = crate::dev::setup(&mut dev_options)?; - let bundle_identifier = { - let tauri_config = get_tauri_config(None)?; - let tauri_config_guard = tauri_config.lock().unwrap(); - let tauri_config_ = tauri_config_guard.as_ref().unwrap(); - tauri_config_.tauri.bundle.identifier.clone() - }; - let app_settings = interface.app_settings(); let bin_path = app_settings.app_binary_path(&InterfaceOptions { debug: !dev_options.release_mode, @@ -140,13 +133,14 @@ fn run_dev( no_watch: options.no_watch, }, |options| { + let mut env = env.clone(); let cli_options = CliOptions { features: options.features.clone(), args: options.args.clone(), noise_level, vars: Default::default(), }; - write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?; + let _handle = write_options(cli_options, &mut env)?; if open { open_and_wait(config, &env) diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 67adff88069..3a7ab5c247b 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -13,15 +13,19 @@ use cargo_mobile::{ env::Error as EnvError, opts::NoiseLevel, }; -use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; +use jsonrpsee::client_transport::ws::WsTransportClientBuilder; +use jsonrpsee::core::client::{Client, ClientBuilder, ClientT}; +use jsonrpsee::rpc_params; +use jsonrpsee::server::{RpcModule, ServerBuilder, ServerHandle}; use serde::{Deserialize, Serialize}; use shared_child::SharedChild; use std::{ collections::HashMap, env::set_var, + env::var, ffi::OsString, fmt::Write, - io::{BufRead, BufReader, Write as _}, + net::SocketAddr, path::PathBuf, process::ExitStatus, sync::{ @@ -29,6 +33,7 @@ use std::{ Arc, }, }; +use tokio::runtime::Runtime; #[cfg(not(windows))] use cargo_mobile::env::Env; @@ -122,15 +127,6 @@ pub struct CliOptions { pub vars: HashMap, } -fn options_local_socket_name(bundle_identifier: &str, target: Target) -> PathBuf { - let out_dir = std::env::temp_dir(); - let out_dir = out_dir.join(".tauri").join(bundle_identifier); - let _ = std::fs::create_dir_all(&out_dir); - out_dir - .join("cli-options") - .with_extension(target.command_name()) -} - fn env_vars() -> HashMap { let mut vars = HashMap::new(); for (k, v) in std::env::vars_os() { @@ -154,58 +150,49 @@ fn env() -> Result { /// Writes CLI options to be used later on the Xcode and Android Studio build commands pub fn write_options( mut options: CliOptions, - bundle_identifier: &str, - target: Target, -) -> crate::Result<()> { + env: &mut Env, +) -> crate::Result<(Runtime, ServerHandle)> { options.vars.extend(env_vars()); - let name = options_local_socket_name(bundle_identifier, target); - let _ = std::fs::remove_file(&name); - let mut value = serde_json::to_string(&options)?; - value.push('\n'); - - std::thread::spawn(move || { - let listener = LocalSocketListener::bind(name).expect("failed to start local socket"); - for mut conn in listener.incoming().flatten() { - let _ = conn.write_all(value.as_bytes()); - } + + let runtime = Runtime::new().unwrap(); + let r: anyhow::Result<(ServerHandle, SocketAddr)> = runtime.block_on(async move { + let server = ServerBuilder::default().build("127.0.0.1:0").await?; + let addr = server.local_addr()?; + + let mut module = RpcModule::new(()); + module.register_method("options", move |_, _| Ok(options.clone()))?; + + let handle = server.start(module)?; + + Ok((handle, addr)) }); + let (handle, addr) = r?; + + env.insert_env_var("TAURI_OPTIONS_SERVER_ADDR".into(), addr.to_string().into()); - Ok(()) + Ok((runtime, handle)) } -fn read_options(config: &TauriConfig, target: Target) -> CliOptions { - let name = options_local_socket_name(&config.tauri.bundle.identifier, target); - let conn = LocalSocketStream::connect(name).unwrap_or_else(|_| { - log::error!( - "failed to connect to local socket. You must keep the Tauri CLI alive with the `{cmd} dev` or `{cmd} build --open` commands.", - cmd = target.command_name() - ); - std::process::exit(1); - }); - conn - .set_nonblocking(true) - .expect("failed to set local socket stream to nonblocking"); - let mut conn = BufReader::new(conn); - - let mut attempt = 0; - let max_tries = 5; - let buffer = loop { - let mut buffer = String::new(); - if conn.read_line(&mut buffer).is_ok() { - break buffer; - } - std::thread::sleep(std::time::Duration::from_secs(1)); - attempt += 1; - if attempt == max_tries { - log::error!( - "failed to connect to local socket. You must keep the Tauri CLI alive with the `{cmd} dev` or `{cmd} build --open` commands.", - cmd = target.command_name() - ); - std::process::exit(1); - } - }; +fn read_options() -> CliOptions { + let runtime = tokio::runtime::Runtime::new().unwrap(); + let options = runtime + .block_on(async move { + let (tx, rx) = WsTransportClientBuilder::default() + .build( + format!( + "ws://{}", + var("TAURI_OPTIONS_SERVER_ADDR").expect("missing addr environment variable") + ) + .parse() + .unwrap(), + ) + .await?; + let client: Client = ClientBuilder::default().build_with_tokio(tx, rx); + let options: CliOptions = client.request("options", rpc_params![]).await?; + Ok::(options) + }) + .expect("failed to read CLI options"); - let options: CliOptions = serde_json::from_str(&buffer).expect("invalid CLI options"); for (k, v) in &options.vars { set_var(k, v); } From f6f9192aa51bd842df8aa1d1aa538b12aa6c2d29 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 20 Nov 2022 09:49:23 -0300 Subject: [PATCH 072/423] fix(core): Android compilation on Windows (#5658) --- .changes/default-tls-features.md | 5 ++ core/tauri-build/src/lib.rs | 76 +++++++++--------- core/tauri/Cargo.toml | 6 +- core/tauri/src/lib.rs | 2 + examples/api/src-tauri/Cargo.lock | 129 ------------------------------ tooling/cli/src/interface/rust.rs | 4 +- 6 files changed, 52 insertions(+), 170 deletions(-) create mode 100644 .changes/default-tls-features.md diff --git a/.changes/default-tls-features.md b/.changes/default-tls-features.md new file mode 100644 index 00000000000..549ead9ea37 --- /dev/null +++ b/.changes/default-tls-features.md @@ -0,0 +1,5 @@ +--- +"tauri": major +--- + +Added the `default-tls` and `reqwest-default-tls` Cargo features for enabling TLS suppport to connect over HTTPS. diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 826eb4945c7..40b2dd370df 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -348,11 +348,12 @@ pub fn try_build(attributes: Attributes) -> Result<()> { .window_icon_path .unwrap_or_else(|| find_icon(&config, |i| i.ends_with(".ico"), "icons/icon.ico")); - if window_icon_path.exists() { - let mut res = WindowsResource::new(); + if target_triple.contains("windows") { + if window_icon_path.exists() { + let mut res = WindowsResource::new(); - res.set_manifest( - r#" + res.set_manifest( + r#" @@ -368,42 +369,43 @@ pub fn try_build(attributes: Attributes) -> Result<()> { "#, - ); - - if let Some(sdk_dir) = &attributes.windows_attributes.sdk_dir { - if let Some(sdk_dir_str) = sdk_dir.to_str() { - res.set_toolkit_path(sdk_dir_str); - } else { - return Err(anyhow!( - "sdk_dir path is not valid; only UTF-8 characters are allowed" - )); + ); + + if let Some(sdk_dir) = &attributes.windows_attributes.sdk_dir { + if let Some(sdk_dir_str) = sdk_dir.to_str() { + res.set_toolkit_path(sdk_dir_str); + } else { + return Err(anyhow!( + "sdk_dir path is not valid; only UTF-8 characters are allowed" + )); + } } - } - if let Some(version) = &config.package.version { - if let Ok(v) = Version::parse(version) { - let version = v.major << 48 | v.minor << 32 | v.patch << 16; - res.set_version_info(VersionInfo::FILEVERSION, version); - res.set_version_info(VersionInfo::PRODUCTVERSION, version); + if let Some(version) = &config.package.version { + if let Ok(v) = Version::parse(version) { + let version = v.major << 48 | v.minor << 32 | v.patch << 16; + res.set_version_info(VersionInfo::FILEVERSION, version); + res.set_version_info(VersionInfo::PRODUCTVERSION, version); + } + res.set("FileVersion", version); + res.set("ProductVersion", version); } - res.set("FileVersion", version); - res.set("ProductVersion", version); - } - if let Some(product_name) = &config.package.product_name { - res.set("ProductName", product_name); - res.set("FileDescription", product_name); - } - res.set_icon_with_id(&window_icon_path.display().to_string(), "32512"); - res.compile().with_context(|| { - format!( - "failed to compile `{}` into a Windows Resource file during tauri-build", + if let Some(product_name) = &config.package.product_name { + res.set("ProductName", product_name); + res.set("FileDescription", product_name); + } + res.set_icon_with_id(&window_icon_path.display().to_string(), "32512"); + res.compile().with_context(|| { + format!( + "failed to compile `{}` into a Windows Resource file during tauri-build", + window_icon_path.display() + ) + })?; + } else { + return Err(anyhow!(format!( + "`{}` not found; required for generating a Windows Resource file during tauri-build", window_icon_path.display() - ) - })?; - } else { - return Err(anyhow!(format!( - "`{}` not found; required for generating a Windows Resource file during tauri-build", - window_icon_path.display() - ))); + ))); + } } let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap(); diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 3ca59c87a8f..b23e4f1d229 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -67,9 +67,9 @@ dirs-next = "2.0" percent-encoding = "2.2" base64 = { version = "0.13", optional = true } clap = { version = "3", optional = true } -reqwest = { version = "0.11", features = [ "json", "stream" ], optional = true } +reqwest = { version = "0.11", default-features = false, features = [ "json", "stream" ], optional = true } bytes = { version = "1", features = [ "serde" ], optional = true } -attohttpc = { version = "0.22", features = [ "compress", "json", "form" ] } +attohttpc = { version = "0.24", default-features = false, features = [ "compress", "json", "form" ] } open = { version = "3.0", optional = true } shared_child = { version = "1.0", optional = true } os_pipe = { version = "1.0", optional = true } @@ -147,6 +147,8 @@ http-multipart = [ "attohttpc/multipart-form", "reqwest/multipart" ] shell-open-api = [ "open", "regex", "tauri-macros/shell-scope" ] fs-extract-api = [ "zip" ] reqwest-client = [ "reqwest", "bytes" ] +reqwest-default-tls = [ "reqwest-client", "reqwest/default-tls" ] +default-tls = [ "attohttpc/tls-native" ] reqwest-native-tls-vendored = [ "reqwest-client", "reqwest/native-tls-vendored" ] native-tls-vendored = [ "attohttpc/tls-vendored" ] process-command-api = [ "shared_child", "os_pipe" ] diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 5a6ab16e544..2ceeca538f0 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -22,6 +22,8 @@ //! - **http-api**: Enables the [`api::http`] module. //! - **http-multipart**: Adds support to `multipart/form-data` requests. //! - **reqwest-client**: Uses `reqwest` as HTTP client on the `http` APIs. Improves performance, but increases the bundle size. +//! - **default-tls**: Provides TLS support to connect over HTTPS (applies to the default HTTP client). +//! - **reqwest-default-tls**: Provides TLS support to connect over HTTPS (applies to the `reqwest` HTTP client). //! - **native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL (applies to the default HTTP client). //! - **reqwest-native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL (applies to the `reqwest` HTTP client). //! - **process-command-api**: Enables the [`api::process::Command`] APIs. diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index ac88c602827..53e65cbb858 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -154,7 +154,6 @@ dependencies = [ "log", "mime", "multipart", - "native-tls", "serde", "serde_json", "serde_urlencoded", @@ -1329,19 +1328,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "ico" version = "0.1.0" @@ -1785,24 +1771,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "ndk" version = "0.6.0" @@ -2007,51 +1975,6 @@ dependencies = [ "windows-sys 0.36.1", ] -[[package]] -name = "openssl" -version = "0.10.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "os_info" version = "3.5.1" @@ -2567,13 +2490,11 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-tls", "ipnet", "js-sys", "log", "mime", "mime_guess", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -2581,7 +2502,6 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", - "tokio-native-tls", "tokio-util", "tower-service", "url", @@ -2660,16 +2580,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" -dependencies = [ - "lazy_static", - "windows-sys 0.36.1", -] - [[package]] name = "scoped-tls" version = "1.0.1" @@ -2682,29 +2592,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "security-framework" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "selectors" version = "0.22.0" @@ -3459,16 +3346,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.4" @@ -3672,12 +3549,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.0.11" diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 59513829332..5e470282257 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -353,8 +353,8 @@ fn shared_options( let all_features = app_settings .manifest .all_enabled_features(if let Some(f) = features { f } else { &[] }); - if !all_features.contains(&"tauri/native-tls-vendored".into()) - && !all_features.contains(&"tauri/reqwest-native-tls-vendored".into()) + if all_features.contains(&"tauri/default-tls".into()) + || all_features.contains(&"tauri/reqwest-default-tls".into()) { if all_features.contains(&"tauri/reqwest-client".into()) { features From 6dcb7fbb816f35f34bbdd643af740a809a0ebd22 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 20 Nov 2022 10:55:38 -0300 Subject: [PATCH 073/423] fix(core): manage mobile logs, fix Android logcat filtering (#5659) --- core/tauri-macros/src/mobile.rs | 2 ++ core/tauri/Cargo.toml | 5 +++++ core/tauri/src/lib.rs | 16 ++++++++++++++ examples/api/src-tauri/Cargo.lock | 9 ++++---- examples/api/src-tauri/Cargo.toml | 9 -------- examples/api/src-tauri/src/mobile.rs | 21 +------------------ tooling/cli/Cargo.lock | 2 +- tooling/cli/src/mobile/android.rs | 1 + tooling/cli/src/mobile/android/dev.rs | 8 +++++-- .../app/src-tauri/Cargo.crate-manifest | 9 -------- .../cli/templates/app/src-tauri/src/mobile.rs | 21 +------------------ 11 files changed, 38 insertions(+), 65 deletions(-) diff --git a/core/tauri-macros/src/mobile.rs b/core/tauri-macros/src/mobile.rs index ca593ea3b12..99d4efdb07b 100644 --- a/core/tauri-macros/src/mobile.rs +++ b/core/tauri-macros/src/mobile.rs @@ -43,6 +43,7 @@ pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { &mut error, &function, ); + let app_name_str = var("CARGO_PKG_NAME").unwrap(); if let Some(e) = error { quote!(#e).into() @@ -61,6 +62,7 @@ pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { #function fn _start_app() { + ::tauri::init_logging(#app_name_str); #[cfg(target_os = "android")] { use ::tauri::paste; diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index b23e4f1d229..5c110aa30d3 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -106,6 +106,11 @@ win7-notifications = { version = "0.3.1", optional = true } [target.'cfg(target_os = "android")'.dependencies] paste = "1.0" +android_logger = "0.9" +log = "0.4" + +[target.'cfg(target_os = "ios")'.dependencies] +env_logger = "0.9.0" [target."cfg(windows)".dependencies.windows] version = "0.39.0" diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 2ceeca538f0..639f172500d 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -277,6 +277,22 @@ pub use self::runtime::ClipboardManager; #[cfg_attr(doc_cfg, doc(cfg(feature = "global-shortcut")))] pub use self::runtime::GlobalShortcutManager; +#[cfg(target_os = "android")] +#[doc(hidden)] +pub fn init_logging(tag: &str) { + android_logger::init_once( + android_logger::Config::default() + .with_min_level(log::Level::Trace) + .with_tag(tag), + ); +} + +#[cfg(target_os = "ios")] +#[doc(hidden)] +pub fn init_logging(_tag: &str) { + env_logger::init(); +} + /// Updater events. #[cfg(updater)] #[cfg_attr(doc_cfg, doc(cfg(feature = "updater")))] diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 53e65cbb858..4f89159f5e4 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -101,8 +101,6 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" name = "api" version = "0.1.0" dependencies = [ - "android_logger", - "env_logger 0.9.3", "log", "serde", "serde_json", @@ -145,9 +143,9 @@ dependencies = [ [[package]] name = "attohttpc" -version = "0.22.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" +checksum = "b85f766c20e6ae766956f7a2fcc4e0931e79a7e1f48b29132b5d647021114914" dependencies = [ "flate2", "http", @@ -3010,6 +3008,7 @@ dependencies = [ name = "tauri" version = "1.2.0" dependencies = [ + "android_logger", "anyhow", "attohttpc", "base64", @@ -3019,6 +3018,7 @@ dependencies = [ "dirs-next", "embed_plist", "encoding_rs", + "env_logger 0.9.3", "flate2", "futures-util", "glib", @@ -3029,6 +3029,7 @@ dependencies = [ "ico", "ignore", "infer 0.9.0", + "log", "minisign-verify", "notify-rust", "objc", diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 5deaab6e6f7..4f936ca8361 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -38,15 +38,6 @@ features = [ window-vibrancy = "0.2" window-shadows= "0.2" -[target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] -log = "0.4" - -[target.'cfg(target_os = "android")'.dependencies] -android_logger = "0.9.0" - -[target.'cfg(target_os = "ios")'.dependencies] -env_logger = "0.9.0" - [features] default = [ "custom-protocol" ] custom-protocol = [ "tauri/custom-protocol" ] diff --git a/examples/api/src-tauri/src/mobile.rs b/examples/api/src-tauri/src/mobile.rs index 958fadd2d5c..ffb9ab03030 100644 --- a/examples/api/src-tauri/src/mobile.rs +++ b/examples/api/src-tauri/src/mobile.rs @@ -1,23 +1,4 @@ -#[cfg(target_os = "android")] -fn init_logging(app_name: &str) { - android_logger::init_once( - android_logger::Config::default() - .with_min_level(log::Level::Trace) - .with_tag(app_name), - ); -} - -#[cfg(not(target_os = "android"))] -fn init_logging(_app_name: &str) { - env_logger::init(); -} - #[tauri::mobile_entry_point] fn main() { - super::AppBuilder::new() - .setup(|app| { - init_logging(&app.package_info().name); - Ok(()) - }) - .run(); + super::AppBuilder::new().run(); } diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 265b0fe21af..175bff3ce4d 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -378,7 +378,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#068cda074b0ff0c42f8d5297d8a2d5c29fd75c03" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#51d0e9a6c9b37e3287ebd5210d28798c534cb971" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 68170d7ff33..d603327f219 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -95,6 +95,7 @@ pub fn get_config( let raw = RawAndroidConfig { features: android_options.features.clone(), + logcat_filter_specs: vec!["RustStdoutStderr".into()], ..Default::default() }; let config = AndroidConfig::from_raw(app.clone(), Some(raw)).unwrap(); diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 083a814456f..e963b68ebd7 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -16,7 +16,7 @@ use cargo_mobile::{ env::Env, }, config::app::App, - opts::{NoiseLevel, Profile}, + opts::{FilterLevel, NoiseLevel, Profile}, }; use std::env::set_var; @@ -189,7 +189,11 @@ fn run( env, noise_level, profile, - None, + Some(match noise_level { + NoiseLevel::Polite => FilterLevel::Info, + NoiseLevel::LoudAndProud => FilterLevel::Debug, + NoiseLevel::FranklyQuitePedantic => FilterLevel::Verbose, + }), build_app_bundle, false, ".MainActivity".into(), diff --git a/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest b/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest index 53a184ba792..8ff358f73a1 100755 --- a/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest +++ b/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest @@ -21,15 +21,6 @@ serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } tauri = {{{ tauri_dep }}} -[target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] -log = "0.4" - -[target.'cfg(target_os = "android")'.dependencies] -android_logger = "0.9.0" - -[target.'cfg(target_os = "ios")'.dependencies] -env_logger = "0.9.0" - [features] # by default Tauri runs in production mode # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL diff --git a/tooling/cli/templates/app/src-tauri/src/mobile.rs b/tooling/cli/templates/app/src-tauri/src/mobile.rs index 3e22469b256..cacaf5f11d2 100644 --- a/tooling/cli/templates/app/src-tauri/src/mobile.rs +++ b/tooling/cli/templates/app/src-tauri/src/mobile.rs @@ -1,23 +1,4 @@ -#[cfg(target_os = "android")] -fn init_logging(app_name: &str) { - android_logger::init_once( - android_logger::Config::default() - .with_min_level(log::Level::Trace) - .with_tag(app_name), - ); -} - -#[cfg(not(target_os = "android"))] -fn init_logging(_app_name: &str) { - env_logger::init(); -} - #[tauri::mobile_entry_point] fn main() { - super::AppBuilder::new() - .setup(|app| { - init_logging(&app.package_info().name); - Ok(()) - }) - .run() + super::AppBuilder::new().run() } From be808a9f5c50a8cbc9ac5349397a8c9a85c8abc2 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 21 Nov 2022 11:47:12 -0300 Subject: [PATCH 074/423] feat(ci): add Android test workflow (#5661) --- .github/workflows/test-mobile.yml | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 .github/workflows/test-mobile.yml diff --git a/.github/workflows/test-mobile.yml b/.github/workflows/test-mobile.yml new file mode 100644 index 00000000000..6ebdf91eb30 --- /dev/null +++ b/.github/workflows/test-mobile.yml @@ -0,0 +1,90 @@ +name: test mobile + +on: + pull_request: + paths: + - '.github/workflows/test-mobile.yml' + - 'tooling/cli/templates/mobile/**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test-android: + runs-on: ${{ matrix.platform }} + + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + + steps: + - uses: actions/checkout@v3 + + - name: install Rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: install Linux dependencies + if: matrix.platform == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 + + - name: setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: yarn + cache-dependency-path: | + tooling/api/yarn.lock + examples/api/yarn.lock + + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + cache: gradle + + - name: Setup NDK + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r25b + local-cache: true + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + tooling/cli + examples/api/src-tauri + + - name: build CLI + uses: actions-rs/cargo@v1 + with: + command: build + args: --manifest-path ./tooling/cli/Cargo.toml + + - name: build Tauri API + working-directory: ./tooling/api + run: yarn && yarn build + + - name: install API example dependencies + working-directory: ./examples/api + run: yarn + + - name: Init Android Studio Project + working-directory: ./examples/api + run: ../../tooling/cli/target/debug/cargo-tauri android init + env: + NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + + - name: Build apk + working-directory: ./examples/api + run: ../../tooling/cli/target/debug/cargo-tauri android build + env: + NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} From 8baf20dd340a9c404fff69f02774836ff78e9eab Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 21 Nov 2022 12:50:41 -0300 Subject: [PATCH 075/423] feat(cli): update Android project dependencies (#5663) --- examples/api/src-tauri/Cargo.lock | 1 - .../cli/templates/mobile/android/app/build.gradle.kts | 11 ++++++----- .../mobile/android/app/src/main/AndroidManifest.xml | 3 +-- tooling/cli/templates/mobile/android/build.gradle.kts | 2 +- .../mobile/android/buildSrc/build.gradle.kts | 2 +- .../android/gradle/wrapper/gradle-wrapper.properties | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 4f89159f5e4..1e59701c28c 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -101,7 +101,6 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" name = "api" version = "0.1.0" dependencies = [ - "log", "serde", "serde_json", "tauri", diff --git a/tooling/cli/templates/mobile/android/app/build.gradle.kts b/tooling/cli/templates/mobile/android/app/build.gradle.kts index 7ebfc04805e..48978e8d617 100644 --- a/tooling/cli/templates/mobile/android/app/build.gradle.kts +++ b/tooling/cli/templates/mobile/android/app/build.gradle.kts @@ -66,6 +66,7 @@ android { } assetPacks += mutableSetOf({{quote-and-join-colon-prefix asset-packs}}) + namespace = "{{reverse-domain app.domain}}.{{snake-case app.name}}" } rust { @@ -79,12 +80,12 @@ dependencies { implementation(platform("{{this}}")){{/each}} {{~#each android-app-dependencies}} implementation("{{this}}"){{/each}} - implementation("androidx.webkit:webkit:1.4.0") - implementation("androidx.appcompat:appcompat:1.5.0") - implementation("com.google.android.material:material:1.6.1") + implementation("androidx.webkit:webkit:1.5.0") + implementation("androidx.appcompat:appcompat:1.5.1") + implementation("com.google.android.material:material:1.7.0") testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.3") - androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") + androidTestImplementation("androidx.test.ext:junit:1.1.4") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") } afterEvaluate { diff --git a/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml b/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml index de5a85690b1..660f1b3b35e 100644 --- a/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml +++ b/tooling/cli/templates/mobile/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + Date: Mon, 21 Nov 2022 17:56:22 -0300 Subject: [PATCH 076/423] feat(cli): generate icons for Android (#5665) --- tooling/cli/src/icon.rs | 123 ++++++++++++++++-- tooling/cli/src/mobile/android.rs | 2 +- .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3524 bytes .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 1404 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 14102 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3524 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 3377 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 982 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 9081 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 3377 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 7971 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 1900 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 18900 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7971 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 12392 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 2884 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 29506 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 12392 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 16751 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 3844 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 40510 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 16751 bytes 23 files changed, 114 insertions(+), 16 deletions(-) delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/tooling/cli/src/icon.rs b/tooling/cli/src/icon.rs index 76a1530bc66..319a15e8fde 100644 --- a/tooling/cli/src/icon.rs +++ b/tooling/cli/src/icon.rs @@ -30,6 +30,12 @@ struct IcnsEntry { ostype: String, } +struct PngEntry { + name: String, + size: u32, + out_path: PathBuf, +} + #[derive(Debug, Parser)] #[clap(about = "Generates various icons for all major platforms")] pub struct Options { @@ -153,17 +159,114 @@ fn ico(source: &DynamicImage, out_dir: &Path) -> Result<()> { } // Generate .png files in 32x32, 128x128, 256x256, 512x512 (icon.png) -// Main target: Linux +// Main target: Linux & Android fn png(source: &DynamicImage, out_dir: &Path) -> Result<()> { - for size in [32, 128, 256, 512] { - let file_name = match size { - 256 => "128x128@2x.png".to_string(), - 512 => "icon.png".to_string(), - _ => format!("{}x{}.png", size, size), - }; - - log::info!(action = "PNG"; "Creating {}", file_name); - resize_and_save_png(source, size, &out_dir.join(&file_name))?; + fn desktop_entries(out_dir: &Path) -> Vec { + let mut entries = Vec::new(); + + for size in [32, 128, 256, 512] { + let file_name = match size { + 256 => "128x128@2x.png".to_string(), + 512 => "icon.png".to_string(), + _ => format!("{}x{}.png", size, size), + }; + + entries.push(PngEntry { + out_path: out_dir.join(&file_name), + name: file_name, + size, + }); + } + + entries + } + + fn android_entries(out_dir: &Path) -> Result> { + struct AndroidEntry { + name: &'static str, + size: u32, + foreground_size: u32, + } + + let mut entries = Vec::new(); + + let targets = vec![ + AndroidEntry { + name: "hdpi", + size: 49, + foreground_size: 162, + }, + AndroidEntry { + name: "mdpi", + size: 48, + foreground_size: 108, + }, + AndroidEntry { + name: "xhdpi", + size: 96, + foreground_size: 216, + }, + AndroidEntry { + name: "xxhdpi", + size: 144, + foreground_size: 324, + }, + AndroidEntry { + name: "xxxhdpi", + size: 192, + foreground_size: 432, + }, + ]; + + for target in targets { + let folder_name = format!("mipmap-{}", target.name); + let out_folder = out_dir.join(&folder_name); + + create_dir_all(&out_folder).context("Can't create Android mipmap output directory")?; + + entries.push(PngEntry { + name: format!("{}/{}", folder_name, "ic_launcher_foreground.png"), + out_path: out_folder.join("ic_launcher_foreground.png"), + size: target.foreground_size, + }); + entries.push(PngEntry { + name: format!("{}/{}", folder_name, "ic_launcher_round.png"), + out_path: out_folder.join("ic_launcher_round.png"), + size: target.size, + }); + entries.push(PngEntry { + name: format!("{}/{}", folder_name, "ic_launcher.png"), + out_path: out_folder.join("ic_launcher.png"), + size: target.size, + }); + } + + Ok(entries) + } + + let mut entries = desktop_entries(out_dir); + let _ = crate::mobile::android::with_config( + Some(Default::default()), + |_app, config, _metadata, _cli_options| { + let android_out = out_dir.parent().unwrap().join(format!( + "gen/android/{}/app/src/main/res/", + config.app().name() + )); + let out = if android_out.exists() { + android_out + } else { + let out = out_dir.join("android"); + create_dir_all(&out).context("Can't create Android output directory")?; + out + }; + entries.extend(android_entries(&out)?); + Ok(()) + }, + ); + + for entry in entries { + log::info!(action = "PNG"; "Creating {}", entry.name); + resize_and_save_png(source, entry.size, &entry.out_path)?; } Ok(()) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index d603327f219..17756f4a47e 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -125,7 +125,7 @@ pub fn get_config( (app, config, metadata) } -fn with_config( +pub fn with_config( cli_options: Option, f: impl FnOnce(&App, &AndroidConfig, &AndroidMetadata, CliOptions) -> Result, ) -> Result { diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cfe52e..00000000000 --- a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..28f1aa119119336781390c2531c381499576ed59 GIT binary patch literal 3524 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 Kmw_-CKmY()leWbG diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..85d0c88af65bcef008faba29bfc02688883dda8c GIT binary patch literal 14102 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 literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..28f1aa119119336781390c2531c381499576ed59 GIT binary patch literal 3524 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 Hu0mjfi}+A- literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TSA42T}4*}5d>5a1yqn4 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 literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..73e48dbfb7dc31ed1e951b77820af88b93fcc418 GIT binary patch literal 3377 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 Hu0mjfi}+A- literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..1d98044f13bdd3afc7cb426644ae118688c03c14 GIT binary patch literal 7971 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 Z{{b+Ide7&7`HKJm002ovPDHLkV1iT#V`TsU literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a3070fe34c611c42c0d3ad3013a0dce358be0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..a888b336b5bc84b42c0c266ce9b8ec5d9eafed0b GIT binary patch literal 18900 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 I*#%+#2dtK;n*aa+ literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..1d98044f13bdd3afc7cb426644ae118688c03c14 GIT binary patch literal 7971 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 Z{{b+Ide7&7`HKJm002ovPDHLkV1iT#V`TsU literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..081832466b4a17180fe9255fa0d3fcba2010592c GIT binary patch literal 12392 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 K@hVZnfd2y&>ZwQo literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77f9f036a47549d47db79c16788749dca10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2884 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 K@hVZnfd2y&>ZwQo literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..b18bceb64d257ed31d50e65d7f2b98abcfb21158 GIT binary patch literal 16751 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 XrJNsHnTG=pQeg0O^>bP0l+XkK^)z{o literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d6427e6fa1074b79ccd52ef67ac15c5637e85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3844 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

C z(CX6fIh8mroxe4Fy?l{bj%8R307X*fDQ#(v@y+x%4>XY?ax zC^p*eaCfrcq#aa-hT`A*7=V1wKdlx2BoX`y?{jb4s1j@)b7-etD6E@cKXx@8e&Vf zWehdcr8jm&_uNAX*$0NY%B&gn)>)@jQLcO5F3UZEr zds`B64=#T7qE6myk{5rNg+99@E{Zf&GEI$b?&PagH-fv#5k*V6EEmp!A*tE}RWk_SsM5#wN>{l7X5DBVl)bv~bNp*#NXY*I)h$Ng literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/tooling/cli/templates/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..b18bceb64d257ed31d50e65d7f2b98abcfb21158 GIT binary patch literal 16751 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 XrJNsHnTG=pQeg0O^>bP0l+XkK^)z{o literal 0 HcmV?d00001 From d33fcbc81c3481a354cda3755ad4866fc2261bc5 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Tue, 22 Nov 2022 11:18:56 -0300 Subject: [PATCH 077/423] feat(cli): generate icons for iOS (#5667) --- tooling/cli/src/icon.rs | 91 ++++++++++++++ .../AppIcon.appiconset/AppIcon-20x20@1x.png | Bin 0 -> 975 bytes .../AppIcon.appiconset/AppIcon-20x20@2x-1.png | Bin 0 -> 2590 bytes .../AppIcon.appiconset/AppIcon-20x20@2x.png | Bin 0 -> 2590 bytes .../AppIcon.appiconset/AppIcon-20x20@3x.png | Bin 0 -> 4624 bytes .../AppIcon.appiconset/AppIcon-29x29@1x.png | Bin 0 -> 1641 bytes .../AppIcon.appiconset/AppIcon-29x29@2x-1.png | Bin 0 -> 4437 bytes .../AppIcon.appiconset/AppIcon-29x29@2x.png | Bin 0 -> 4437 bytes .../AppIcon.appiconset/AppIcon-29x29@3x.png | Bin 0 -> 7243 bytes .../AppIcon.appiconset/AppIcon-40x40@1x.png | Bin 0 -> 2590 bytes .../AppIcon.appiconset/AppIcon-40x40@2x-1.png | Bin 0 -> 6524 bytes .../AppIcon.appiconset/AppIcon-40x40@2x.png | Bin 0 -> 6524 bytes .../AppIcon.appiconset/AppIcon-40x40@3x.png | Bin 0 -> 10085 bytes .../AppIcon.appiconset/AppIcon-512@2x.png | Bin 0 -> 149064 bytes .../AppIcon.appiconset/AppIcon-512x512@2x.png | Bin 0 -> 15001 bytes .../AppIcon.appiconset/AppIcon-60x60@2x.png | Bin 0 -> 10085 bytes .../AppIcon.appiconset/AppIcon-60x60@3x.png | Bin 0 -> 15512 bytes .../AppIcon.appiconset/AppIcon-76x76@1x.png | Bin 0 -> 6132 bytes .../AppIcon.appiconset/AppIcon-76x76@2x.png | Bin 0 -> 12970 bytes .../AppIcon-83.5x83.5@2x.png | Bin 0 -> 14550 bytes .../AppIcon.appiconset/Contents.json | 116 ++++++++++++++++++ .../mobile/ios/Assets.xcassets/Contents.json | 6 + tooling/cli/templates/mobile/ios/project.yml | 1 + 23 files changed, 214 insertions(+) create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-512x512@2x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 tooling/cli/templates/mobile/ios/Assets.xcassets/Contents.json diff --git a/tooling/cli/src/icon.rs b/tooling/cli/src/icon.rs index 319a15e8fde..6535349d179 100644 --- a/tooling/cli/src/icon.rs +++ b/tooling/cli/src/icon.rs @@ -30,6 +30,7 @@ struct IcnsEntry { ostype: String, } +#[derive(Debug)] struct PngEntry { name: String, size: u32, @@ -244,6 +245,83 @@ fn png(source: &DynamicImage, out_dir: &Path) -> Result<()> { Ok(entries) } + fn ios_entries(out_dir: &Path) -> Result> { + struct IosEntry { + size: f32, + multipliers: Vec, + has_extra: bool, + } + + let mut entries = Vec::new(); + + let targets = vec![ + IosEntry { + size: 20., + multipliers: vec![1, 2, 3], + has_extra: true, + }, + IosEntry { + size: 29., + multipliers: vec![1, 2, 3], + has_extra: true, + }, + IosEntry { + size: 40., + multipliers: vec![1, 2, 3], + has_extra: true, + }, + IosEntry { + size: 60., + multipliers: vec![2, 3], + has_extra: false, + }, + IosEntry { + size: 76., + multipliers: vec![1, 2], + has_extra: false, + }, + IosEntry { + size: 83.5, + multipliers: vec![2], + has_extra: false, + }, + IosEntry { + size: 512., + multipliers: vec![2], + has_extra: false, + }, + ]; + + for target in targets { + let size_str = if target.size == 512. { + "512".to_string() + } else { + format!("{size}x{size}", size = target.size) + }; + if target.has_extra { + let name = format!("AppIcon-{size_str}@2x-1.png"); + entries.push(PngEntry { + out_path: out_dir.join(&name), + name, + size: (target.size * 2.) as u32, + }); + } + for multiplier in target.multipliers { + let name = format!( + "AppIcon-{size_str}@{multiplier}x.png", + multiplier = multiplier + ); + entries.push(PngEntry { + out_path: out_dir.join(&name), + name, + size: (target.size * multiplier as f32) as u32, + }); + } + } + + Ok(entries) + } + let mut entries = desktop_entries(out_dir); let _ = crate::mobile::android::with_config( Some(Default::default()), @@ -264,6 +342,19 @@ fn png(source: &DynamicImage, out_dir: &Path) -> Result<()> { }, ); + let ios_out = out_dir + .parent() + .unwrap() + .join("gen/apple/Assets.xcassets/AppIcon.appiconset"); + let out = if ios_out.exists() { + ios_out + } else { + let out = out_dir.join("ios"); + create_dir_all(&out).context("Can't create iOS output directory")?; + out + }; + entries.extend(ios_entries(&out)?); + for entry in entries { log::info!(action = "PNG"; "Creating {}", entry.name); resize_and_save_png(source, entry.size, &entry.out_path)?; diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..8a08f3c83f4f00b4f6c772c1ed32e0f6b09424ff GIT binary patch literal 975 zcmV;=12FuFP)xy;VqBso#sp2&7%|4( zC~^BiaR1?g#$rGbjfuNek>G++g8^+3pI$PRhELDKljd=Gv9o3&UX$P%`iq- z!eGfTKN9>*s58t+V1yt7fPe$Ug)50NGf|Oh(z8i&DVDmr{2ja>7=jrZgMMWcjDDpO zOv;z&Hg`KRI-xNKpm`F&qbzV9w>+Hr*O=)-EDpqu$dC`f^md*J)386ee%`|9(%w(L zo>SGX6SX(LUhD1hd-8I}OerbSmUg>+?mLY=cSmI^va8u`z6u}zhN`lJV=4ua4~)r6 zRb{y@rF{ZtHcYdI=L^h8h@4V(^gF@8n$N5qx4*=uDntR-Fw0L5%Y`S|aQlGiMyRI; zsz(^%7NM+DXU{_8^D=`QXxFmX@}|m^X9d=9n;6f7)x({M5T}(!4+kYA0H%r5>MBmx zrF8;mi{~z;zJIdRHu^lusJUwVbMeT5RdIXZSq@AzDKo`qlc3-*;y6EH6>RLGlQ+ly zUpUouxZ+%Aecc7R-O}o9aliMrAsI%uo_ZQYKy?_SMt(rve(oDx^K}Un1~ZKCO3jM( z+frJ6pk2LT$Ix^n=~-j(l(Dm@4(Xbe5oyQebeF3-t)2dhjSyf*7d$&-?IX9rakmXi zqylp+lSx(f{|e>t35i25s92-l3vY6P>QqS8kB%ZlI4n=LOfWZhK;(w)N5n&sW#tu? zmToZA+;5ZXbb9KG(a55%Y)?E7FDJo^$`t2dc8FFb$WYF*SRz53r|YW-sK~J9Lw_HV zB^aY*y0|~7Z?;`CXU2k4TB9ps1KF+jmfA zNJ_?{b9jpc1g*@OY%{;?0E6RRJDZwm9TcJpQ$%gqQr@l literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c39a345126ae20cf2fd40dc4de691165756eb48b GIT binary patch literal 2590 zcmV+(3gPvMP)Z1F{!4g)f!Drtvxo!o|vUEX^pn^ zGdrmm#o%fizbLad2 z@BhC4pF#icf6TMo3^NF0%rG7Il?jn$8Oi^%C^?ql02b~p8R|Ob8_OVz87x71;3xf{ z_z3-glz3BeR0uyX(1-JGvPo^#b*}2d>up!AR0$RB0%AppNdHxUG#hXek^X*dqTkfT z(}I3EYLF?7F|)jqOQP;hzuRO~ep9%-?eL?&t^4PLEe;ov+h7080cx8Mjy6Q^czeL# zQ&KG7MIu3xO_D_sxiE(0VuT^LM3Rw2f9s4ahm06iSGHBo|6}#sV;5S^0Rt>B&}Ams zzJi;a=JUNX>#X_xBMdQ)M$v&>gyFn7mh-|KOAwYN;^1@)2gf+pfH8qAI$A^_pdTNS zJ~?o1V}t8nUYYF@3;RJJ@-xYx2JZP3%jlf-);yXuyC4Y$3umNMW}j``zvo25-l8g@ zwAm)Nm<-sM5UG!wGS=sZGbi|`V>4@TG>HzLWAuyD96n%)9bE?oR_(Cj}fjCdD(F68m^Zj;7O zFk!5o=TsJk#Tes&0a-jbTqGAplAJZM2i0|bry01?wg2BRXy{;_*<@xq)Tn`H@bdJK zU+w=eI&<~H{vQPSa=x8`JiIDqFRfo0YSDHysXin?=dUr)`-aW3%BGCb#P$2xg2e+5 z=2k@=I9n?L+iVUlFvudew#fGK;ymr9+OSY` zLnuMOx+o!80^@bMh?BOrtGDc>T6VMq`B^wK0(;UB%Q9)}IY5?GXf-CMrUorru}P|b zgkYi*S^ofi>#1W0ZcQ2++O%iqHn<(8P_%x!^Jx9Pv$;)24E=dMYhgKKC~v%XO{o0% zA9ZU~5Rx{Q4H{rbL_FgL2p#F}16NJEfO|GSMD9LX7GMFP;0rpv+-7q_1SE0*YD?Cmt!@?=*n@Ndkop-E8y6fJtWOv^27nRl?ZY%5{B*bT_KiCX z%jt+Dx-@WXK%l>0`s=^e^^b@H+d1EuxqTgRIc1Sys9s6?PNYrH`&LA6O&w(!@2D5; zdLPzMbfqMQqYjVmS)jaTjTJ0ZsLoU7k@Mrxv6GZ)tKLz z6|$sq@8Ifjsg@TU4Wa|+<`JC-S5>FyU$vFc+I-SvjXP5?_Oh7V50Ai==n{$0=J6<^ zQm2cEN`&Kx(eXTTiv+`3#7D;tX_rD}!?8oX(vTjb)_Sp3Z)Ww338N(+-JT1>Y6b++ z<*?cTpu23qh;8m?HA0}@a?pdl+LfttQ&-LH5;%^a=A4R9AO#>O|6zq3w)HnlYFVKQJyq1&2XH3KjL+ zH~h&_QF@&V4L7nDZ%)@goNqn;MOMqvtk2w0!a|_Uz+*Nk#*|o9+IDrx>U#_0W|Sr7 zTx`i#Y3=2huL&S1G$)nu*Yj^s- zHt!9FB1d-F;pGV{nqEA7R(WdilX;P+Oq_blMxsh0w! zH)52>Buv2~H9&0g;v;q)%uBf5NMgULBJt%9Nx$EIxTbv)p5_rw$&fK=WQw)uE`jJt z%-M3qw8Pf#%F=~Jgm-nhaa7vqsLyk+DQ17FxHaz59TJ^ga0gqwO^?@T4>R>7s;PbW zzA|fW@bCV-O=@bOL#l)I_181@ubFy4_ZdA&GU}t@Ny?JY5l4Zh6ub9S; zpM+=wZfO><*=!`DDBb<}^b~tZVG-jV<)o(usAiHw6y#6lybhz*W@^N8bF-b{c?0xl z#gw35BP2SVj2@njjqrj2Ud})OBJJs*a$ag|XsKO1dxCJepn#SD^zvR=OV>(*)<)F^ zkzW)FFB~@xOCHBZ4h*KqAUhIivx#(Ep;7_EmSEy`AyH((7g_9OX4}r6J_g_>I<8%< z=nJ?WznjtMhPH-%rl-&Mo;+y5)GhRI^lkb355KjYL`?=cv;Y7A07*qoM6N<$f=gWf Af&c&j literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c39a345126ae20cf2fd40dc4de691165756eb48b GIT binary patch literal 2590 zcmV+(3gPvMP)Z1F{!4g)f!Drtvxo!o|vUEX^pn^ zGdrmm#o%fizbLad2 z@BhC4pF#icf6TMo3^NF0%rG7Il?jn$8Oi^%C^?ql02b~p8R|Ob8_OVz87x71;3xf{ z_z3-glz3BeR0uyX(1-JGvPo^#b*}2d>up!AR0$RB0%AppNdHxUG#hXek^X*dqTkfT z(}I3EYLF?7F|)jqOQP;hzuRO~ep9%-?eL?&t^4PLEe;ov+h7080cx8Mjy6Q^czeL# zQ&KG7MIu3xO_D_sxiE(0VuT^LM3Rw2f9s4ahm06iSGHBo|6}#sV;5S^0Rt>B&}Ams zzJi;a=JUNX>#X_xBMdQ)M$v&>gyFn7mh-|KOAwYN;^1@)2gf+pfH8qAI$A^_pdTNS zJ~?o1V}t8nUYYF@3;RJJ@-xYx2JZP3%jlf-);yXuyC4Y$3umNMW}j``zvo25-l8g@ zwAm)Nm<-sM5UG!wGS=sZGbi|`V>4@TG>HzLWAuyD96n%)9bE?oR_(Cj}fjCdD(F68m^Zj;7O zFk!5o=TsJk#Tes&0a-jbTqGAplAJZM2i0|bry01?wg2BRXy{;_*<@xq)Tn`H@bdJK zU+w=eI&<~H{vQPSa=x8`JiIDqFRfo0YSDHysXin?=dUr)`-aW3%BGCb#P$2xg2e+5 z=2k@=I9n?L+iVUlFvudew#fGK;ymr9+OSY` zLnuMOx+o!80^@bMh?BOrtGDc>T6VMq`B^wK0(;UB%Q9)}IY5?GXf-CMrUorru}P|b zgkYi*S^ofi>#1W0ZcQ2++O%iqHn<(8P_%x!^Jx9Pv$;)24E=dMYhgKKC~v%XO{o0% zA9ZU~5Rx{Q4H{rbL_FgL2p#F}16NJEfO|GSMD9LX7GMFP;0rpv+-7q_1SE0*YD?Cmt!@?=*n@Ndkop-E8y6fJtWOv^27nRl?ZY%5{B*bT_KiCX z%jt+Dx-@WXK%l>0`s=^e^^b@H+d1EuxqTgRIc1Sys9s6?PNYrH`&LA6O&w(!@2D5; zdLPzMbfqMQqYjVmS)jaTjTJ0ZsLoU7k@Mrxv6GZ)tKLz z6|$sq@8Ifjsg@TU4Wa|+<`JC-S5>FyU$vFc+I-SvjXP5?_Oh7V50Ai==n{$0=J6<^ zQm2cEN`&Kx(eXTTiv+`3#7D;tX_rD}!?8oX(vTjb)_Sp3Z)Ww338N(+-JT1>Y6b++ z<*?cTpu23qh;8m?HA0}@a?pdl+LfttQ&-LH5;%^a=A4R9AO#>O|6zq3w)HnlYFVKQJyq1&2XH3KjL+ zH~h&_QF@&V4L7nDZ%)@goNqn;MOMqvtk2w0!a|_Uz+*Nk#*|o9+IDrx>U#_0W|Sr7 zTx`i#Y3=2huL&S1G$)nu*Yj^s- zHt!9FB1d-F;pGV{nqEA7R(WdilX;P+Oq_blMxsh0w! zH)52>Buv2~H9&0g;v;q)%uBf5NMgULBJt%9Nx$EIxTbv)p5_rw$&fK=WQw)uE`jJt z%-M3qw8Pf#%F=~Jgm-nhaa7vqsLyk+DQ17FxHaz59TJ^ga0gqwO^?@T4>R>7s;PbW zzA|fW@bCV-O=@bOL#l)I_181@ubFy4_ZdA&GU}t@Ny?JY5l4Zh6ub9S; zpM+=wZfO><*=!`DDBb<}^b~tZVG-jV<)o(usAiHw6y#6lybhz*W@^N8bF-b{c?0xl z#gw35BP2SVj2@njjqrj2Ud})OBJJs*a$ag|XsKO1dxCJepn#SD^zvR=OV>(*)<)F^ zkzW)FFB~@xOCHBZ4h*KqAUhIivx#(Ep;7_EmSEy`AyH((7g_9OX4}r6J_g_>I<8%< z=nJ?WznjtMhPH-%rl-&Mo;+y5)GhRI^lkb355KjYL`?=cv;Y7A07*qoM6N<$f=gWf Af&c&j literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..47f66cd3b79734cef7e45a33d5fd199f7f407327 GIT binary patch literal 4624 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 literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000000000000000000000000000000000..7971ff3d68074df304ecb4555df2e1351d60f074 GIT binary patch literal 4437 zcmV-b5vuNqP)|v)0I~^zge?RT_ADeJduBb$-|zd+nK(=)5Ry=6pPuKLWai9S{`Xtn@BO|r z%)hpOvW>l|H)nnBPZ&aO;Jx_2Coqm zz_L-Hg2`s##6ZyCJOPb!%ZDy{M8iozu@7$;-x;3Awo=;jkmbm zn%dyd8p^6X7Ya^0^S9?WZ{J?fyt~n^%3i>#MpXX@xamOgqC6~4QHg?|Cyxx9@a#h| zvxf}`8q3&tiNaQ3%^JK^?+L8_5*WI#y3BoU?Y6p4SFEr8LrIzYlGktu_?7?)AU{z`IbMRGwepNwFWcW-TeWo2 z`xURlxzxMfYuq%Tm`|Ffhw0K8adQ{W=)N2?OlekCLz3=HM7G z7Kfh!kSdC-$b<^asiXD-lNO!%$?iKq1Xf@yz)J$Fhc5Bqe|+yIjOtPmG{I2C^JMU#WaCB6t~F)PD;b-c-*n)5 z!FCBy^cE}#*-`uY^^^}M-W&Ot=dvt|01!Q<8ekzm8LeKie|yB9^X-x5T_-QNPc=H! zM)*!lj27bt_qGnY`?jF5DQVVzXatM|i~_s{cvluooCtnbM$bN#1%!3zdcwF%fO>DZ zVs^sIznl^Gil&A&9qt!`c>%o9{LY4|U$0ny`TaBH?#ecq8VKp0tdLQQrgnQV zD=Q?MX;dN98I|K1UQys$W=`-pwA%DX3p37Ccq-8lU`XwT1H~GNx%88fVWa=NJT*tD zS2Wm%*F`U=LYTnkw9rXOf20Zv!Mn;Tqe$U`_?JyJ zE3=<1nNr)R+A*(C>OoX$#*4Bn@!97!9@=B?aYsSU!KVM2GA8V&l9{(?3Zr85$WBd; zPqU;t8`SdM$Lt4jpZDB0Ii$0K0RPXQ67y8|9_BPyfGP&@k`N*Yrg)RId0XA*lNOzw z@i{Yt4>Wr1IsdztuazqZM9ss>!>}iJzni_ZsPwZ?u(pMzM+31c=hp& z9+pIg`ECeNatA3YyvV*G%z#v=p1zR`)&hL>JBgOczE+2^1@&7}k!^V??nMBSRsE{3lG;RAJ zLiP+ML({G4dMdS@iXF~TU8=Ecaj9J4a;pR{#GB1r(1@|4Y?B|EqUfYV{6Tv6y*{)1 zf1F*{uefMU#>wK(GfMWn+k5knKGt+qrJ>GW8NxafZ`|9srTo0B7)lIp2oMFu$B5g4 zf1qEV?RubKdY&p%NDDDOF)Cut!bOSe{{L9w>aTMxgNKfxH|BXEP&xb4;+`vCn3M2k zRD{?A2u3nVtnIF>kRRr)>QgYRf6xF_P`+UCF6dQetDm!p2X2AZn(k z!Xs-6T`Vmxo;t-1-jP0dE@`FV#el?epvaMj3Eh$gAoP32Nj5EPKqGrFa4EBX8HJ4>ZK~*X|()(q|iwo6`qmDVM8c zEcL7)pcnuJ4yDrNviqN_X}Wkxj1l8-p@uTLGVgJzBFl6O7h;T@V|~LHx0*)PNrCF$ zu}1VtzQ*Vj&=!zG?i`Zv-(T!yMKMHeZj@O;q?(qfs=@^Y^L$7s_<`eEw{ZHRFI38< z*tuvVKwU*A0fkl6DiuaV5Z{u78;oA8c)sx+F(jq?S|z4^Kw;qsQcOsW?f&*hTOmJr zB6}1HQqxp4FCP{mIP>@CmOcH*I8RB@3B!$=f9QR_rS?;tBw)9LVx1T{>XwO%3*p(k z#{WN@LlZE73;ubVVcJ!GX^2C zk_3DxC{;_If9&F8lg2dXe6tn9x0T9)vVjA#uXZ);SiP=lnHVeaoQXyG4_0DWQ-okv z8kG8(%P!2+1w>%y4DM~YgVC3xBL(lmsHMb^q-}asxOT|$G-Si4&U(t4HGBl-B=w{L zQgb=$%V$p?M}>v@gT&JSBwhT%M`9Pz(gZsZ5NqL> zqo*AE5F}!03*Ej#zmtXA+rQnGYt&D*6-d};O6}V#{@qW%N?yP1bn=&5ij)4lwJ_}G zGoCbb0b#E4ctzBwhMX7V`d_DPVLTc`g*7Y<8Jr2?nI)?)Eoz2ogJ4V)!NH$eKG_I<#wx}fz|LS)8_|` z%pSp@^?0851&JqHV5Irz_=pDzp`m{1-{apVZpZ)_1R7I7S+sIeL#3ze!I#fI;;WcC z22`J`X{;zQP+GzXrWVaCU|baKgB0lrKei%fQh1lfrR28M<>vSij0Rejh}ydOht{{ zK*uLq`^G2S)#@vh4W2cbBwdN+%U<-$y3=u?olUh0anNvf*E3}`2iQ;uVgJ}yWT>t=5Y3=0LmR+<`LOsj4V97&}nlN#1@Fr-rDtq$p85SkSaJW9o^1dyd(Q5F~te zT@RYLHkgQ-AA~!jHq_O!m`ao@p>Tux6XOyRS{mP${G>&lbsfz=?kO(X#|4M5U?{g? zAwtDJkT!@Arg__@FZ@H_8^*B_LL9D2G;x9$DT;Q7TJ_SK7iVQYd2-x^N>2@)>nhj( zG>yB8ETZ2jRhP~(7>YhElL?qOU=Y9@58qY3_QFif>2UMm5k`)}U&+La-4lfRbsxWN z-?4Q&y}o~Gf^Fik&4msV1mhT7hdM~2tzKk%U?LHx=?meld9lAi0H{={;bi!l1Pop_cmg*K8n7Os5onj40S_CMhK?;cCX|T<97T0N2ri0?)aU zBOny0VnE_Wu@FA}sfE76!ms&Msi8mzE7g^?^2Kvy#N+V_4ASZ(JQf*1QDpsR0UQG* zDlPnpU(QY1u)WCCJH5ZJ@jDcu*9u=0_K~yZ&5N4zpTETc7+M`=AJkXA^3~_2HhlTX zns#+BUU47D`*^+3@Zivgr~N`o>e&}SW{Sxv{e{SA!BuqZ^tp+{Qw@S-I{Is*_VuF? zks-82rPD_i4>d40x8&>`e-e zXDVNNerm%yFs9G39ohKyEJkzKoj`fc;4u^K#Raf>ccP+qLFG(w9^AJ{sVJ*J^}`wp z>00^kom`-ox-Wl!ZP4iPKUOYPxEZh&ggFe_Y@(<5#G%Sp7d)eTk#y<-Qahfe>Gjc> ze_(&mog>D9IPU@D6AWRf{E7;qJt*Y<$urct+OzKChYx$h6M?W;Za6Cc@Fy5N{ft5a z^B>wc$i1HN?|kyV*5RY>QR{0Jj0#FhKua7n4;9Zp^&_S9LWR-xb?O&%?PD=qpr&e4 za!P#C+HVSR)Cr17VFY7h4-3JGgJaM3t#zy4`<3InUAe^LZu4@$qRB9|m=!wt=kq`h zOL~;ha)yMY3Nf)l^>1IDTf6eDw{_2}ovxki_2!c$^Uk61?|!xyLsWCSK%#+E(w|r3 zc!kHN+;g_%1WX`u+$ARql#0>{TqqwA6%K~#ZN6>r-R1#O>*4V&M#iUOnpmkO? zAu2|wTeEU`)zXFY=zNfL><3gG21*Y`#)T4K$-FBo{`af5v1Ut(1cGLG@Vn72lsj@rHF}9 zPgy;$InHe1?7MR}RxJ3%efFHqxj1^n!@3e=hdkgp=nPCXW#oM$i1HW zAWWh4(6_+TE(kdXqrAS(?#SEwO~d+6SAfXh>0ej7dh47}rcQ$g!p7n_t5twu+FkG( zC8hQqD0GWWVntIZfW@-nQ0wceb{yEVLoPjk$+!-NJn?&0a|>?xsLbeg z0|v)0I~^zge?RT_ADeJduBb$-|zd+nK(=)5Ry=6pPuKLWai9S{`Xtn@BO|r z%)hpOvW>l|H)nnBPZ&aO;Jx_2Coqm zz_L-Hg2`s##6ZyCJOPb!%ZDy{M8iozu@7$;-x;3Awo=;jkmbm zn%dyd8p^6X7Ya^0^S9?WZ{J?fyt~n^%3i>#MpXX@xamOgqC6~4QHg?|Cyxx9@a#h| zvxf}`8q3&tiNaQ3%^JK^?+L8_5*WI#y3BoU?Y6p4SFEr8LrIzYlGktu_?7?)AU{z`IbMRGwepNwFWcW-TeWo2 z`xURlxzxMfYuq%Tm`|Ffhw0K8adQ{W=)N2?OlekCLz3=HM7G z7Kfh!kSdC-$b<^asiXD-lNO!%$?iKq1Xf@yz)J$Fhc5Bqe|+yIjOtPmG{I2C^JMU#WaCB6t~F)PD;b-c-*n)5 z!FCBy^cE}#*-`uY^^^}M-W&Ot=dvt|01!Q<8ekzm8LeKie|yB9^X-x5T_-QNPc=H! zM)*!lj27bt_qGnY`?jF5DQVVzXatM|i~_s{cvluooCtnbM$bN#1%!3zdcwF%fO>DZ zVs^sIznl^Gil&A&9qt!`c>%o9{LY4|U$0ny`TaBH?#ecq8VKp0tdLQQrgnQV zD=Q?MX;dN98I|K1UQys$W=`-pwA%DX3p37Ccq-8lU`XwT1H~GNx%88fVWa=NJT*tD zS2Wm%*F`U=LYTnkw9rXOf20Zv!Mn;Tqe$U`_?JyJ zE3=<1nNr)R+A*(C>OoX$#*4Bn@!97!9@=B?aYsSU!KVM2GA8V&l9{(?3Zr85$WBd; zPqU;t8`SdM$Lt4jpZDB0Ii$0K0RPXQ67y8|9_BPyfGP&@k`N*Yrg)RId0XA*lNOzw z@i{Yt4>Wr1IsdztuazqZM9ss>!>}iJzni_ZsPwZ?u(pMzM+31c=hp& z9+pIg`ECeNatA3YyvV*G%z#v=p1zR`)&hL>JBgOczE+2^1@&7}k!^V??nMBSRsE{3lG;RAJ zLiP+ML({G4dMdS@iXF~TU8=Ecaj9J4a;pR{#GB1r(1@|4Y?B|EqUfYV{6Tv6y*{)1 zf1F*{uefMU#>wK(GfMWn+k5knKGt+qrJ>GW8NxafZ`|9srTo0B7)lIp2oMFu$B5g4 zf1qEV?RubKdY&p%NDDDOF)Cut!bOSe{{L9w>aTMxgNKfxH|BXEP&xb4;+`vCn3M2k zRD{?A2u3nVtnIF>kRRr)>QgYRf6xF_P`+UCF6dQetDm!p2X2AZn(k z!Xs-6T`Vmxo;t-1-jP0dE@`FV#el?epvaMj3Eh$gAoP32Nj5EPKqGrFa4EBX8HJ4>ZK~*X|()(q|iwo6`qmDVM8c zEcL7)pcnuJ4yDrNviqN_X}Wkxj1l8-p@uTLGVgJzBFl6O7h;T@V|~LHx0*)PNrCF$ zu}1VtzQ*Vj&=!zG?i`Zv-(T!yMKMHeZj@O;q?(qfs=@^Y^L$7s_<`eEw{ZHRFI38< z*tuvVKwU*A0fkl6DiuaV5Z{u78;oA8c)sx+F(jq?S|z4^Kw;qsQcOsW?f&*hTOmJr zB6}1HQqxp4FCP{mIP>@CmOcH*I8RB@3B!$=f9QR_rS?;tBw)9LVx1T{>XwO%3*p(k z#{WN@LlZE73;ubVVcJ!GX^2C zk_3DxC{;_If9&F8lg2dXe6tn9x0T9)vVjA#uXZ);SiP=lnHVeaoQXyG4_0DWQ-okv z8kG8(%P!2+1w>%y4DM~YgVC3xBL(lmsHMb^q-}asxOT|$G-Si4&U(t4HGBl-B=w{L zQgb=$%V$p?M}>v@gT&JSBwhT%M`9Pz(gZsZ5NqL> zqo*AE5F}!03*Ej#zmtXA+rQnGYt&D*6-d};O6}V#{@qW%N?yP1bn=&5ij)4lwJ_}G zGoCbb0b#E4ctzBwhMX7V`d_DPVLTc`g*7Y<8Jr2?nI)?)Eoz2ogJ4V)!NH$eKG_I<#wx}fz|LS)8_|` z%pSp@^?0851&JqHV5Irz_=pDzp`m{1-{apVZpZ)_1R7I7S+sIeL#3ze!I#fI;;WcC z22`J`X{;zQP+GzXrWVaCU|baKgB0lrKei%fQh1lfrR28M<>vSij0Rejh}ydOht{{ zK*uLq`^G2S)#@vh4W2cbBwdN+%U<-$y3=u?olUh0anNvf*E3}`2iQ;uVgJ}yWT>t=5Y3=0LmR+<`LOsj4V97&}nlN#1@Fr-rDtq$p85SkSaJW9o^1dyd(Q5F~te zT@RYLHkgQ-AA~!jHq_O!m`ao@p>Tux6XOyRS{mP${G>&lbsfz=?kO(X#|4M5U?{g? zAwtDJkT!@Arg__@FZ@H_8^*B_LL9D2G;x9$DT;Q7TJ_SK7iVQYd2-x^N>2@)>nhj( zG>yB8ETZ2jRhP~(7>YhElL?qOU=Y9@58qY3_QFif>2UMm5k`)}U&+La-4lfRbsxWN z-?4Q&y}o~Gf^Fik&4msV1mhT7hdM~2tzKk%U?LHx=?meld9lAi0H{={;bi!l1Pop_cmg*K8n7Os5onj40S_CMhK?;cCX|T<97T0N2ri0?)aU zBOny0VnE_Wu@FA}sfE76!ms&Msi8mzE7g^?^2Kvy#N+V_4ASZ(JQf*1QDpsR0UQG* zDlPnpU(QY1u)WCCJH5ZJ@jDcu*9u=0_K~yZ&5N4zpTETc7+M`=AJkXA^3~_2HhlTX zns#+BUU47D`*^+3@Zivgr~N`o>e&}SW{Sxv{e{SA!BuqZ^tp+{Qw@S-I{Is*_VuF? zks-82rPD_i4>d40x8&>`e-e zXDVNNerm%yFs9G39ohKyEJkzKoj`fc;4u^K#Raf>ccP+qLFG(w9^AJ{sVJ*J^}`wp z>00^kom`-ox-Wl!ZP4iPKUOYPxEZh&ggFe_Y@(<5#G%Sp7d)eTk#y<-Qahfe>Gjc> ze_(&mog>D9IPU@D6AWRf{E7;qJt*Y<$urct+OzKChYx$h6M?W;Za6Cc@Fy5N{ft5a z^B>wc$i1HN?|kyV*5RY>QR{0Jj0#FhKua7n4;9Zp^&_S9LWR-xb?O&%?PD=qpr&e4 za!P#C+HVSR)Cr17VFY7h4-3JGgJaM3t#zy4`<3InUAe^LZu4@$qRB9|m=!wt=kq`h zOL~;ha)yMY3Nf)l^>1IDTf6eDw{_2}ovxki_2!c$^Uk61?|!xyLsWCSK%#+E(w|r3 zc!kHN+;g_%1WX`u+$ARql#0>{TqqwA6%K~#ZN6>r-R1#O>*4V&M#iUOnpmkO? zAu2|wTeEU`)zXFY=zNfL><3gG21*Y`#)T4K$-FBo{`af5v1Ut(1cGLG@Vn72lsj@rHF}9 zPgy;$InHe1?7MR}RxJ3%efFHqxj1^n!@3e=hdkgp=nPCXW#oM$i1HW zAWWh4(6_+TE(kdXqrAS(?#SEwO~d+6SAfXh>0ej7dh47}rcQ$g!p7n_t5twu+FkG( zC8hQqD0GWWVntIZfW@-nQ0wceb{yEVLoPjk$+!-NJn?&0a|>?xsLbeg z0BK|}#rQLHE~f>^L2uE_cxtGjmDhs&xD1$Gq`#1$14 zMGyp}i}bp55|)-mdNMPacJDp+ec!ov!ela&Odth)+@B+1GWX88|MQ*y_y5YdFqe)? z$ED-aap|~p{1y)OS2tjmVL5o=5n}wRs^Ux4{=5Wk1YmNAC&S8$O5iOL>8S$G@(4H~ z_#dNQ0#`pc4OAWgl>^^zn;=@#lf?A)Z49Xv6Q7uwYOn|b%bhIrxg0K~qNrRh%*pfS z7L-ZlaBj_cJkJUMFHwj}{@;Sjak%Wyfoe5z%#gm(*WBDc=H@|HSO#@SHFmdH`4lD! z;nz%CCj{+JNo9Nvr6RZ3cYOO1*S2+g9IIAtw{OTVm8$iXz{B;`mH_#p0hiUS%MWVj zbYsfody~fBJt*#hwi!{~8BPO^@xl5Ai^Qc4e?vsoT64pK!6O-naDq|0Jx`f*cG(vD zcmG&iGH1g+=YD(-C_{j2D9uS17X`RrN*vxL@@c13qwR&UDKCt_HQ^~PPOvgAg;Bf& z?NZ@1H^%A48g}fuGb4na2HYzG_nHB5n3V0i9p6uRzu@If`<(}Mka3kLze#X~0Be2a zp*D}bHoon2Hk!9FPKA+WRYu?u&DO*$K0Oygyd3A|Gd9?4_Y?M{>s7i1N+D=wjp1?mH7*=GBL7Skwk$N z!+=7UH6e`3gY06XIF7UM9A^r8;rk-|US(KCq54)~HJ%Q>CQcwvWH4kO;cgmcyUQkW z@`~;DjZFzK+ls&rp7%z_b?C4FG#MnA5ONs<9|K$hfJsdOJb&q-Q=Wfk=Xwuj=Xy>% z-J}YZv|F$OXU!{oWzx*N zsW28E#-#kJ!1X(?oBBofTK&&1dl(Pl;l4lx)LlXNKCFL^j|POmv8~-&wP@kmiUq57 z*ni0_ma6Le9$O4==-0*EXY4TBsL?~?#~E!xGUHGv-y-FNS{V{Ve`2D@Pn(%H;l+;& z=QM?rYcX(jxkWNk3^B(RbUPen;?oqgs1O#56CfBk0RjbP^@hs1Z+u!f?Uw`9Cu#-_ z>H)78>U&Ur;l%j=4iR0 z6iJ>yOYTs|#rnayqLZoZycaS)>f0}Rgi@*cNR*!{sHT_L2l14VE^ z5$6@nwTMF#F&ct_KR+hr!G}gC|5>V#WprL4fsGf0z5AR?2TaVlamNwYDd0*Gfl%~l zDpIASSt3I)NQT^l2)Tx;4}U28A~l*P{rkrZQN1di^FV-cMW75c#EY@L(v4}Kt}6c? z9USuVMF}q6oB}hSk!*k_A{l?1qgC-pxZLd}ogP<)-^#Vdv zl~M&m(4oO~I0%C`_^Oq{n+C<*PQ1h)?$wNiCX3vJbh_N!`*5~vR8$mv+X)kEUdN zrg}&e-Xwy{nslvdgggtp@=ken4R|_xxZA6e=8X3-$iQf9^)AQO0hy+DT`srulRZS{ z0suyOkbFc*%!tZ3dCIeMZ;m_Xe5_Y!UP^!gF{=FF&9(>FSU!&Rz%q;y1hq=GA39RK zYs$>ZHDH^jyXnM6{NpSggs zGYrtiQ+Fm#n)hSbk9uP1;!q37sdyu1BgYmk>6vG>38`ClI=)-Hsq(8IHdk#pmg_AJ z!Ot9Ng!7;d-_PBe1B(~|aHs-0(8oq3jQHq{4$I^!S1ryhtAo3yzR*ns@s>rTQ^R_(A zC8$hIy$pj9j@Q1op=%)LqY~JY?LKzbRWUcDrW-pFAI+Tkt(V21gNqh;d%2XqYNul} z^%aen{AL5!e}j1?Qt9WbD^{H?moZG`0Z>qqJa}&OXsS$;-fE3wkHkUp$m1xFlHvqo zW}2}>icL&5KyIL_T5Is#e~1_ZBJ!F(>PmS5g^g$AsQ0{g%) z+t7WVb<4hUxNRZ;+YaCp;KdpZY{GT@qi3T3sj=%j~jm<38sq z2I_SrfKw4zNE{JJ-bcfPX6cSDV$7NqTnLQ>02CH6kb{qyXo z9=(U7={VOcU##DVS|1f)Ffsh9m@7V;)^QCaa;%X11%{+R&^j6dg?R(HLRP0tOr1XU z(Y8+^z+nyoniw7biS(XhQJGV)l_36Cy zkH0=(9X$LI_^m}QE0t8Amr*J!s=V1pkLa$Mgu8jP9`t&%C#QdabubK3!P^`3Wn^}O=;9AapCXNAKLE8aUEdXM5D0aiG~kB!YQ-SFf_Tfg0sTX zAPh*cJu-28#+NH|tT*2L2yxmOVy{BJRq->d!N4dLW%+VRQE|;3gc_jU*m3K%u>(7G zF<&FQ6eU!`CxAyenkqDM#)pO{jBjWu)7~67P8azKz15@?zb$YiG%^F#-7Z;dtEr60 z3qeg9L{J(4Yvto@LYx1Y^_g|Z@X<+^c`96>eP?BZjqQWB8>%lwV@VzjW zOPAr0zScpywrTL@XgKR5%#FR_9EPSa!^$WB#j4%J;czRJMzffRndE@w&+1bjtN$%x zH7ztXuwq(9Qu@c=tQ9lb_moSEWR@2=9oRZ1rb?1S|CtS!@AAK$nQBZ$3yO&FFt7mK z6l>*^fIcuOgbE3A#(}FR-pwyuu;AGw#)c(#a$tiur6Pa@E{z{c?#Opkb@)^?G#-l0~le5K?1rVdWT=g(AzCj2utL#D=_} z((>S9K!6sTnx2q4WA0`yCMJ!zt0e&35b~}SMMg{J<7^CPwM5s)w}HpbqprQWH*fHu zya-?&J>xluIRSK14V`C;7EH<5gd&H+7OnxpM*p#e)B;AXilT643zPQYS3d~}N$p6r z6ZlmSL##iPWGWvQFQ}5Qto*|nZxl~^@nL|FG1t(cb6>@2_k{$OC^7`T z9A}MY?cXn)d3w~a%;Gm+dewX6;2B-wh6h)rm0bZ!E>&-RS@arX6B(bZDq#Rty^`w1 zYR2}R_NCu!s9dY7hpYp|Y)k1GOri^8B2FSyy&7 z_Z)JQ?Qy0;Vk9VV2Hq_EEEp3+e{JrG!G*+3`2(^4eB`mM*GIve`O z<4^qkA9<4j*mBV(3PPMv^WsfBS+=U|!(NXZy$Z_jPJhLt-igy?8kGBQ!468U_OjQK zKyIS$KIS^8k0tVy==oVKFCcI!ufOw7?5J_iDdlD8z{FrGU`-Z5$~&_^Z~VxsAXxCS zq9}s=I`3WptjGB?QmlNyk**q`Z3)DITz;YNSm!Z^yScqY^(T9phZZ2TMPLn`y0nkJY`FC z*m<#nK_Cu$QT|h7hEiZbmR$(2e*Q(f!h8WlR8^1$Tm)|~3Yq|(PHhB9ZC3y6bN3}b ziRyy5AXMhh9DiGXuzpq|Y(vt^NmAb~=5`~8#y_Dt6eg$=QM0nY{kd{+L#pG# z!9_j-tA!vT8j>*Mh||gFecZKqHa1?UTKvTf=em`fP}4RQu)$VLE}$Dm_c!FNr&9mM zh+Gu$LfFopvs$DC0fLI+TyM_epDWk+ExFko8q4t%Nb3#18v&X&S(SY<6X*cIbdVw- zmrzpn&$nOG--;Xn4sB7uN=40!Vc8~6=tDGSz~%kyy-D{B85B20cF1*7lQQaSGtYd! zx_qwFO|T*@G`4@DQQ*=pEI2GT{_Y+XoPBU)i_(gVj$!OezM3f)=G&<V;S=0()%_OWXDDX0FGtgb^y(GFr=Ofo{Y& zQT$+T;dGF6<>%Xw7dg0ST>t_An&$$SR6nA8t0 zEmXoaQ7@;w_Vx#sfkW7+WyOOT~x63>TONZDNvwHXgBZMt%eSno#pZ}tO=e=ECR@oRF6%Z^upVQ zu323RCO+`E1~3~*u7}Y#tMGcCEtzyr(#ZEFr!Ro>O`(9*m`!Cl6VFVVnKz-rsgMh4 zBaN7I{ag-t+U_c;6@L2_nn0j*w^A zKf{WU3i5r?!$v%o@br{tDE)9;Bu0UK2Lyrb+0m4-c1Grc_g-rME!>8QNU9R5wgx#1 zSx6AM`Ck`L`+Rl9di>0V^yfzI(DbzmOE?2NDr#kp@OR*IAZRF)a!;MoZ6JImN|SGD z%!rY1g-6YrGnVZFfJm63s~$smw!+!&p!QfC69AauS5DU|kqH0T)vPBGRr z^*88H>FQyif?;MFK7-YKCQFb2LXH-^Em*8#NDvK&4^?mI``FQIPM!5RbqCfYU6zdj zm(|-Zq?*3MS~o+D)z~0TnniO+Enwv(n&)pAUHk|*ImUPLcz*C4h59&cCIds)2U;tQ zh)xa^ItM9B{<|MaXZ3mX=wQ&Z9=$JEW}4`T3|v~Y0}cXyluOQ5A-~os?#nV-RRYw| zjG7sTH(`J7*K=^+2G(d~)Clsr7(qaeg7F;K^K%1ggnn6@jcZ)c7<+01-WEKMg03gcPrF#z@ zTecqML9p@8d-Pyl*$4moET52s){EDW^hwiR7@5eDk26LY;B8;=UtgG@jXM0W{gs41 zolQL$D{th@JT8C1;~^zqEh(MR?~$Wj?tAUjlru%XYVDZ|n4@W)XD&1%<)~Sz??mU~ZR(!vbX{gFUwJOV%&zbQ7CyJJYiBsMNAVp9d)N#=X5=9nPgF!AY zEj|14fKEIxW^`IdWb|ec?c{ncgV&oWhGAE@uMdp-I#~K(p)Z9iz5Y=rOyKa z7m2%vd68y_C2(t!Op`aJepmNS`*7i0W7i(-YgS~dgUi&^^cm;672l%tVuN>^g9RcW zroPwSZXJ5-jW8i{o#JJ94#p=WC=?vLVJ(5q(mVo*FvW=HphJXfDU$31nINHpGhW`K zqwUGbPh~7vwm#$YANPuF)2{OD+jX3-+-7`AQ{!rA3Y9+`q!ssivkz~E3h;HR+pREx zaXvO5d)-XpA77g7+Oi>wxLq>M`$h_o{!F{Zn+f&>LkcswO)M%F$h37bcI$a%RM)Hl zh7O&s03{>B#3OYvl?&%j*QiF|vGURSoa;Y9?^%c)HSWGPufOw^Tv3L30wGA3$`DWp zj^*FYD*V$^lK@@gI)D_s8)u|n}N8h&z!z@ z=;e@P^60Ir&GzW4^Vupzmys{~$RX>n5qCqOG8K4M33P0@$%MG86;tmkuYstp$h~XZ z4y}i*>2E}Dt5UF;I{m|is|}ZRyaLPppjK1>m0*v*rgT&y#3%3-^WT4^dgGeS)F2}4 z_-RRSb?cQ0lzMVwZn=950Er3gTKtVo1*+S{S_a*88+`7Rd*9AIcqWR>rm_|QRK!&R z5|9BWwZe48l|vMJm4tl~I38BxZ4SAdi;5CJCgz;|^B*39Fj{3=;5nM-x5*DRBGe4L z#||Ge_3V=c+BXZtzZ8^X#dS$wS+dq4!|&qE77uXnCcXSFUI@T+-vy4HD6-hDy)Aw2 zqBSs%!BEnd>$FvA5>e%1;<%DGUwi-qk(RL9^WxlEo*4!xreBtL+1IO%F}!F)hZ0l~ z_B$wskGHYazpP(UH1+u>r86gs{3}44D!M^MZAh?!5%MbCDX1k_V(%GyTjJyw=W)@| zN#LyNa6kQeS(c!hEIL*$nN#@O;}aMx5d~~Cy!b3v2aPs>R8}}4dfFFr$Eb1OELc77 z&DPSLSe17<6=TOP-DB_k{ga@PRq15TegMRMuGGu&be9i(dJU()SdJ#r{R%=W-l?Yk z1A4dl>$~sAk9+(L0LsF6J)waGL53;;N>2w0{_x=KG?9;5yoH{ud*PiH92Uw7xO)*Rh9!=9D%!7CFDJ- z0N6nQ?0(wCF3S?r!bj6R5`b<^CP$mQhz`3kHvf>ky%;x?Bt_MyYX1qLWfS8(|39>8+a|E?Z zbMQ0vH+Vfh$iC%6FtFdfcgIGk0RQCPv*S=rtqpS>m~dBH{omba1#kmE##Fc(R0jhq zjujG<(}cEZDG;Gy{VWRqO$SKwIAN)m%0-2RQgLAc{4CXSgUG8yx#&P`9dLtXm|l`+ zBHfIRAu<#gRc#+JVVH{&(^dvI1lZcYw!^YPZ1F{!4g)f!Drtvxo!o|vUEX^pn^ zGdrmm#o%fizbLad2 z@BhC4pF#icf6TMo3^NF0%rG7Il?jn$8Oi^%C^?ql02b~p8R|Ob8_OVz87x71;3xf{ z_z3-glz3BeR0uyX(1-JGvPo^#b*}2d>up!AR0$RB0%AppNdHxUG#hXek^X*dqTkfT z(}I3EYLF?7F|)jqOQP;hzuRO~ep9%-?eL?&t^4PLEe;ov+h7080cx8Mjy6Q^czeL# zQ&KG7MIu3xO_D_sxiE(0VuT^LM3Rw2f9s4ahm06iSGHBo|6}#sV;5S^0Rt>B&}Ams zzJi;a=JUNX>#X_xBMdQ)M$v&>gyFn7mh-|KOAwYN;^1@)2gf+pfH8qAI$A^_pdTNS zJ~?o1V}t8nUYYF@3;RJJ@-xYx2JZP3%jlf-);yXuyC4Y$3umNMW}j``zvo25-l8g@ zwAm)Nm<-sM5UG!wGS=sZGbi|`V>4@TG>HzLWAuyD96n%)9bE?oR_(Cj}fjCdD(F68m^Zj;7O zFk!5o=TsJk#Tes&0a-jbTqGAplAJZM2i0|bry01?wg2BRXy{;_*<@xq)Tn`H@bdJK zU+w=eI&<~H{vQPSa=x8`JiIDqFRfo0YSDHysXin?=dUr)`-aW3%BGCb#P$2xg2e+5 z=2k@=I9n?L+iVUlFvudew#fGK;ymr9+OSY` zLnuMOx+o!80^@bMh?BOrtGDc>T6VMq`B^wK0(;UB%Q9)}IY5?GXf-CMrUorru}P|b zgkYi*S^ofi>#1W0ZcQ2++O%iqHn<(8P_%x!^Jx9Pv$;)24E=dMYhgKKC~v%XO{o0% zA9ZU~5Rx{Q4H{rbL_FgL2p#F}16NJEfO|GSMD9LX7GMFP;0rpv+-7q_1SE0*YD?Cmt!@?=*n@Ndkop-E8y6fJtWOv^27nRl?ZY%5{B*bT_KiCX z%jt+Dx-@WXK%l>0`s=^e^^b@H+d1EuxqTgRIc1Sys9s6?PNYrH`&LA6O&w(!@2D5; zdLPzMbfqMQqYjVmS)jaTjTJ0ZsLoU7k@Mrxv6GZ)tKLz z6|$sq@8Ifjsg@TU4Wa|+<`JC-S5>FyU$vFc+I-SvjXP5?_Oh7V50Ai==n{$0=J6<^ zQm2cEN`&Kx(eXTTiv+`3#7D;tX_rD}!?8oX(vTjb)_Sp3Z)Ww338N(+-JT1>Y6b++ z<*?cTpu23qh;8m?HA0}@a?pdl+LfttQ&-LH5;%^a=A4R9AO#>O|6zq3w)HnlYFVKQJyq1&2XH3KjL+ zH~h&_QF@&V4L7nDZ%)@goNqn;MOMqvtk2w0!a|_Uz+*Nk#*|o9+IDrx>U#_0W|Sr7 zTx`i#Y3=2huL&S1G$)nu*Yj^s- zHt!9FB1d-F;pGV{nqEA7R(WdilX;P+Oq_blMxsh0w! zH)52>Buv2~H9&0g;v;q)%uBf5NMgULBJt%9Nx$EIxTbv)p5_rw$&fK=WQw)uE`jJt z%-M3qw8Pf#%F=~Jgm-nhaa7vqsLyk+DQ17FxHaz59TJ^ga0gqwO^?@T4>R>7s;PbW zzA|fW@bCV-O=@bOL#l)I_181@ubFy4_ZdA&GU}t@Ny?JY5l4Zh6ub9S; zpM+=wZfO><*=!`DDBb<}^b~tZVG-jV<)o(usAiHw6y#6lybhz*W@^N8bF-b{c?0xl z#gw35BP2SVj2@njjqrj2Ud})OBJJs*a$ag|XsKO1dxCJepn#SD^zvR=OV>(*)<)F^ zkzW)FFB~@xOCHBZ4h*KqAUhIivx#(Ep;7_EmSEy`AyH((7g_9OX4}r6J_g_>I<8%< z=nJ?WznjtMhPH-%rl-&Mo;+y5)GhRI^lkb355KjYL`?=cv;Y7A07*qoM6N<$f=gWf Af&c&j literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b5a2e0222344edafab14d522e9393b3a8d3f8f GIT binary patch literal 6524 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�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 Hu0mjf2n(UY literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d4ceb760374f5fa0d56fdcde8bd5765156d095d7 GIT binary patch literal 149064 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

|7oSf6`Fio7GD-P+Sjib1uHT+%` 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

CS>NYA`CMQ*&z|d{!GcJYvM*Wml6))KU1a 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 literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-512x512@2x.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-512x512@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5e9add73161e79c11efd1e2c4e4ee64f243c99e7 GIT binary patch literal 15001 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�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 Hu0mjf2n(UY literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..4bfc2d971a5a7feb92353f579e87257fbdeaa35e GIT binary patch literal 15512 zcma)@LtrFKfQ7qb+x{oEZBJ~gW83D$wv$OFwylY6+qS*4r@ii>aHvBaO7DI3A{6B% z5#jLQ0001@)K4+xe{JLc78t01HUA_O3jn}pASEWO>XCh^2MtnHTk5WnoBo3hgCb%d z^lM8@T&wm!s%pK9sLe<0Sv|eMrVckHrLss2Oj@{~DM&%dijZI-@gWPR_iO%BzV;+E zS1d^xqhy}$oT0KT_FEm#9ozh$D8@MbI73XK%+ZDj|G#edASjx;w5N|H#1I6j>Px5@ z`V}siG~=#T^gV1)@sb%NSTVGL8&EwhWk#i`l}e5fkdT0h*;fX}l@iV{jDppNysUx= z39R00LP*CCh$RfzjS@yV2c)|G{hYLzpBN}6zqd@RpuzQtEfkr7w8aJ5V5lNX+beyXk`&uAVRy6G zS@BLhKWX#ByJ;-Hn?Jh|Y5i{Lw7NFCoJOawd>3=k-y<-&LI+g}sKd#j^3MS#L;-YT z$={_2Gi52{hg(Xx*amZgo-P$;N5TVBW{SBIJJK`>rfPxD)Ks`6e_V^=xnr7OfJ#Xz zYN|+%fGw@iLyryd-YYPl(Q-{cwpzPgsAcTDn$kD3FNH<#2lQY-ttX^?A*ypT@MHuh z13F%jqd730$<9P#n^{!Z{(N!OjoQ&kYoJ6CD^(sWoyLhd(;k9j3=>Ah%bOqwe-Jyq z{~5yrd(uFQ{c&=7{D_zuJ@^Hx)88X18m**`9B7l$fVynq21@8m1zu zjE7o@D3mt@fP}~7&!*KfKcEGUpaVwC(eq@*dJ4b-Od))NCaE(YQ8vVw>IfVisJ>Jg zv{XKad~1k3A?YX>2v@SH%Ig2ksdo%uMuep!B7WUHn;}Zn&U20nxC0Q5z!+Jv$UKu0LHA!+_>^c`nG)bHj1_~KRam3ELG_up3O+xUlmLhXT( zK;t3BEBvy>i1I+;gaLFEQ`MdZaz+uQ*XMsw=2V?RaR4?oHF5hY8EIBhMrrr8b-#Nb zSM7Tlm4ofHfHHw&Y_cTZfptp60HqLU@l+d4o%<#FX>5Vc&j7)IlEeZw+0`|GOI4qh zBPQQkQea9BG5^|Guv^ZhM%f}-<|tVs5?ix*sCf!-r80hkwO(e;b-v*jr%z|86RA9O zt_W2z+n;008(H)!U?l{D6%X+pxQ6Lt-ltDik!6{P;4Jy6I()Ix6lNwmur>NXLyRk< zUQ#dHZ+O)+^$gP5dg&!7G7`>Ml;s*1K~8>TJUQZsKj*t;R>P#5l(>XK2Ng@lg62NI zft}I>&SZG{Dg*p#)mgMjr#RAaL&Y$kwKtCgX`{_+Gq+XDNm+l3$u>s&5y`zK>pEB@>A zfOfz!WqNPEU19*z=mZ|CyLM(KH9`VkJxt9o!~~Aa9}B~50-8Lw_>n*0>87fCX`6?` zgETK{&0)FQKdmPZ4hQ+WZ=ZZ*b~E$sThphDNXm^Vz8h8D(uTkvvd1& zdT8~?pCc#M>)KQSe@0Op9i_vH9)b@qKaKW@s`8a_#sX0Li)|ms)7OXO^7F1u9p(6# zd&vpnqlEihc4D# zYHu=S+QpH792wcbSn&!ma1owN6eT5EbAJkQWj{<83(%JYpLx8^UrTbb$b7Rg`f4Z) zEm1(JAct*wwqW4V%{xXN;oIM=oqc>=fIKAb+hY_|B&b9*lzW&7EeP}fdX@M?ttb0a z-waUuvg}l)683OycFnchesTWILh=e_WOyI0@n2%;Pp>05^rB;QZgg5IVN=n2rS71PTR|gEo@kNN6=BZhDn*SV>%$y~|O5D%*bsj!GMa_<}U}Y1)D| z*|olD=IYxWPrB!~w59Vj5qlK&Xl_e0vmX1kjaLd=n|t1E+JCco%M%y1I(uKt(Z?I4 z2v$F$-Z#As`1p?g9UezlbPvlQldODJW-||3C4kfA?^5j}uJW|@@V*qa(Z0bfUHq;0 zav$Xi)$WUE){CwkP9*A5svAAiv(`YZi8)v*<%XvvqBf*0_V1Fj#q3nrBow;M6WmN= z=JU)_&@9s@PsKSS!nBKWuP*klpWFynhMv%px`%sj{?k-#*S{2`mrgGtey2yOB1m&xLy2kBAMd(R%OxTk1(%DFwE%BPx&^fuPf??9*rkdvvXmz!}jYbh~e!F0{hM%L4_yag5HU5lt zV3;s&Eq>I0cTgXLd$6nAjZx_=!!A&n1@iw=F>y86?zueeHcVR%^9m$ew&!El*+wVU zaVw6Z=;C8=7YnO?;uVjpT)>=wK}iK?;%%Lau*b|1+{x|2m^djF&T{w>z)cpbI1V8> z>G45&P4rp5UDeTXCAgylb|j!btr&shL$&pD2V_X-(@} zA68RKc9PjvYxGv?H=2<_Kj}-B7sTQ?K*>HfFI-b_(AeHWsAzQ@qF0@YfaezKcF1(Dx@*QX9tk3(9< z++cl8N(_5t3!H6m`qAy*S}a{oc2}d5*|o*7JVA0#nt_8rI2jgXjMe2r3Qg|r{m^X2 zYSI#|9uuzD4`arpcB|v=aish3?3-r4daNRc$fO3#NtF3E1Nr@UmZ`SNQC+XlbFj<8 zW&Fd`iVQXzTBf*6t39La)kpjh599G{{A~6F5W_-1M*yzf(*Ba-mPF`Qjb7@Y{KrzI zp2(HLD}{Kf&3Ad^b5IxzbQFZhCWE({xj04rwa(6N?PQ~?cAY!5Tc99ZD+yuME(HcR z-<8vJ+)`m(OhApk`8E{sZsGDHOkY~)b=?#f-07|=4T!S%GY77gnQUqTj>Nk9T+nWM zaG2%~6ynjEY>YSN&G`4FV3>hHYvtXxNNDfU?_^*wclf|qanHBI`0WF$YLos*y%MvF zty}JA&4-jc?Rub+WveL{ep;%jL>x}o?>{u$aAsxBoL5inv%;hlninEDE73*bmMhoG zZGHrc@7_p7i9C=;}0XI!e$((lnO%%GC!(uP#X+J4vLC3-Fk5bSGkE|bzi3TrVsOHY&ILFJACdYj5)ixDqQH>-TL5DXEN|y(^;b}cijmd zqKqktI#bCUxdaI!dhhy-NHD0zs4RAq&6uY;P^taz*&Im=3=R^rbHSJPJRN z$n!HbCC;xDX?t^z>mcZdC<;~S%c~<3K|oFVrJC*2%}&=I`}HIKZczeBX?KE~Fbn9o zNvM=D;@>a-1+-H5J=$c2F?`Cb(JH54Byc8ZhbTk(y=}~S+j%wLTzU-4ac1(Mw`W&I z6XyDn);UlNQbwM9h#x|LUvNV!sEL*-(`Yu=pAECG^GBuHx!JxQYHZI&;=Lv>&wFRG zc`=36hJUx+r}Tg0F7!LfNwaLLKr++mo}fw@;bgWLth8BWi){eP42vw#>4ro9p%2S-j&<;sy^QL8TUlU4Ly(}4Zy+a ztBpUZq!fegYm=Jvc^+g|0vY)-pOZXvRUc8-9kjF|Q_ZfjJREwn^3tke&3_0Lf-Gcl z)Ifh3u8s+8CfQs`!ZpOJs8=pOKdKnViWxL@8o-1qmw5CoZJbK0S)lL@h3|?5f_O-S zw`$MI|0{F$%00vZmrtuLyx+lNNl^WYbWR#3$!{W(f)Wj54P-+%9d4X*?s#xO$oihV zEzwU`n#Z^t`=uIn(Y_Y*u_zT&XJXa#JCBB9w`N=vM4(TWK`|{qPDbn$V%3yQpVun)9=(MVwS-Wt85_esvrZwhlhVpm_ z%N?{P*L*yQD@D*86jDHoRjUJFRZ)m>PzhF*-|FsJEhZ=d%uYcla|hy_oK zaTvkR1!LtTuE(1VyNU~>YX4R&WL;>eN|_Y3y|?CNjE6FpQH0Gn=B?P6a=r^o>p5*= zb@>$X1K~C|DE0`)u@XDRlBYGI%gU*+nyL1q0{y8TGgH{E@R)}0Ks%qvy>blq>W#D4 zrx?#7;;-7iCdx)nV^eOSncvHpLR7wW^;S!dg)Hymi)fEDp>@mlVtFr(##01z4&8f|PFTu!$Z1#Ui~aH7aHKSs9o!GxBnC0=@R zNMJ-tVn~yStUTA7`sXRoXtRPRLsb*TB!6Q$W{SdSqQ&`R1)3DnuP3LRTWxh;{t`tO z8hRXY&#>j*zmOgF>}LQ&F`#3!XAY2aYTip!kvbes`j#rp>ZOVCC!Sn~S)4mx95pj| zk?v4X&dgv?(PK&IBQ2r!4_U<2yh09vZG=LQdM`vc%ksIt($bUIdXU{snfEW%B(5mW zk5oU&hD<1_Ev2fZX=$@8#rwmr{dcy5in`I(@CtrgAPzc^Lt6-(N~@NsK@7b9?{PB* z!SSc5*xkvw9aEE?Gtj!Om`Z?{$rwdQO<3py#kxD2Rm0#{DNz(yNno3~q|VftI}hEV ztuAF-1(2w}*x%rmbSQH~$;?X4{4~FmM)3NU zVQR7kA?0&7f>1T#f%cDqhBv28I|j;WXn0xb)(b#R&rdqs5c9nan?nK5>z4irK6HDc z=uflSr(XeBJb8^|Xn*z-mDvQAes%Q*=1Pxwi@bsoI9okqXm${Rr8a9>|8wWBOES+x zl7Ll=QGr1`pGYyXCDQwOie@dCk@GOw7&B(>vEjC*o78w*c-mA0FGD=oWkh-FfL=mV zu2*@GI(s2xk9q;+%rPGDEX!5kWz7rk_VP+Phgr|-w~()F#B5AtjY1TeX{ou6jc zk{YsRLOv^9@H!i*nGAw;SYp|%>I80Mi!>1IrU>ht!;w7t*qlrep$b=ID*r0>0=<$@ z28Rvq7HosEaj%|Cs`=~KtSS9%P^RsAg041a^H{j3eT-T|O&TXRu$~$4#M!i^$_ELF zerv1Qm7pILv*mNI?bG}bhwmKfezn`mttl5(mQ_&iK%!vmh0|Mc*f^}+6;NZiV$L>O zu->`K9|q12jST5#y#K+JL;Bz08k}EHYLh-I;M2Sk;ftN9FzYq@i0pkd?P}UL3+&af zt$nP(V*@Rmq<5&X>phR37Vjg%qtT*5U|R{5Gd@gl;it3HK{qXH-ruiKFyhT7u~pmw z?760}{im$ZBG-U45m&APNQP#HSU+gbG@ii#y3O`cp2twbEkMThsji42; zNBbO3?_HYVTA6R^->ZlNNFW`7N^>v{vk9{wL?2DEG_Z05wEP_8T z5TwdRGCKY_fcd7jxXA`YRLx&z`_X1Uolb3Hf~?YcbrOsl%+jYOf2q&my^h1grlU=+ zMof)pM-GNQ{zgQ@4hyBWTyC3d&?p&Yb%QM4*gs~5N>9QwXT8kEFJ!%lrInnZ(cY)#<6npl99jYUcgV zoic9kgrx0d-+!Wk*$JakSU{y0hvQgKFyt0NFH#s z%v^Z`BsOSS4n~#@?mavLYRR>}wl3!^GH*mZr5eikKWKQQ!UQ+;4>kFil9{Cc3-tqY zj-+1RU>V`wi)AlW+-2o@v?~rIVJ+wwQ6g9UQxdpYo=+ls{HL5#qr^GCNR3u?c75{ zY!ynZUk(-2#9a+vLI-E)!eY@!H|k`xhd$AdKoqdGO|oZpn2NK?f7g;cR+uo=oJ+Q} zJO(|xXiB=GKudci%x@AgO1b!jbq|+}gnp?WhruvMFfxm|cdHiu82B=|->nCR81@g9 z&r~!aY&A+#;~aIblM{Nv^2p2kW$c`XIJjc56krxKoZ}Y7q>~0d?mWhIM<~ox;s;}2 zgvrdSDKb*WKD^BnJTCo3Inv*cBkz&Rq6*+al&mzrm?v)38{fu1rbK#<6QsnArP>1y zn=NpI+V%0PNi*gk+%7XSj}5*WU6^efupXl^CER!Ks|d?{1X5+-p=Y!=utkFojkrDV zkS$9h*II#-7732hL*Q}SmR8%ZMhKr|+8<7O@npNdv^w}T`R&iD!fLu+)_SKdO;y_l zI)6YoWp@&KRTF>Dl@yJ4f84#$AJG8~j31A3Q27bnY}ku#{^k;1t&$FX^9b`I6xfTy zx(c+GmNal`T1`Z{CI0H4Q9XHjO#z3FgS2*uA9mS0R|{RboJ_pT?ygd?Z{1guRfmL^ z+)UR>6(6#}3Lo^@S=mR{Rlh&XzI>5_5P(BB-2aR&cXKJU8;tSs`A&}Wt@pV+#GU^s zbXaH3p)D%kU<&nwnYL2{sZ!i~wRV=f@m$UCuIhb&P7Ynrjq5r-F(N#QjXE|Zs|gzD zr9hHR^^OAL?er7x*EzuF?!(2)P)@a!P+bzt^(UMO-QtN@5Vn&jN^~WIRX(j-3>(sQ zp$Nj|h!HZ?s-jWuLJ%MS=p7UNaGl1G?|B>z=Eqc8{qg5-U?rxcn5X7nbr-6J^2SQW z&qUEpD2o^*Xv$iw=Cc^&DEm{tdj>eO4U{?nYvK4bw`FIWsLVLZN&uTTdF5m8Cm;ao z{e|K~YbQN@nYn^fh*g^-BGGAbg?56lLm#TXs?t1N49+YqUmi`9sN2F=xT64xtzg`1z zr65TxRrFRD9BzPkICJvn%1BoTv2&+j0rB3iPlz4o|cnV zsxFQi8%#e#5qSxgs)2x}kvq|x4jAL!wBHl zc}_q4c>18}b7-w!{k_I8s>*b=rLOtOBxW*aOYLUl!KDxCgbnLigEYK8LN9cBZS!a} z!nTEAoN2UfqN>)e^nV5xV{Sz7cb*03-12I&UqRqIa=5`nD=zekAHy)d$mC`?M0gy; z!#l&_gQfc;+o-<9yVfxvYWpeuH{T=Qm>;DP?4!pZ>DH|0D163M*)tqwfCv2ZGyY9{ zu*0L!y6h|4QE6-+{v_$b#z|k`NPbm~keRoirDA7{0+YDgv*Y~S6A~vzl2a0#sN79e zMW^Zu^BI+<32@;<%iK`qAo?o`9fHx^?5C6x2($HT_BYyMKv@WotK02l4Bj)_Hv{^gKsj!A*H$SG2^X3UwN&AS=Dmx&eXa)O&nAB5XNH~ z9I|#ELY~Ro)cU6qjEG&naw4Y-H4fp3ryS@j1vPY896vtSq#a;4JPLq0C|>=q%U8Cy zWV;yzbk$fu2~}+q%2HLI8@bnIJmcY-B9{$7jIG0sVNz%u-+J=4UG3g;!p}YS+B1Q) zY!Mm(4`(0Hd#bb*VVPu!lrio~AXRji|qHk z6PmH8VYi9j`*0R3pAQX*Q}`UAUNE~ZQ9D0atm_^63MJJwxil`u>9J0A~!&0dc;9!gyOWtobfuzq@I5t$5nk=`l7C@I&S$RQc=<54=5y z4!qj@Do1t$wIGnVkwwZcg`bKTNafQ(UJr1VoosGD?Ks>N?CP#<^x;;l+wlpZc&r z2Iq?2QB?fA8}rP2$0-1vI*uKYpX)6jd!L@0Kc*9}(|($g1ASQ?9)FSzdPhf8JCAlP-lv-MDP-bV<$_eFJynVzuxIb+-0s70h@2B z0RXUO9gt1xr$2aH>C884O>v#${nN2D8bQqmk3UsEdZkNWe(gRRDW+D?l9%1JPH0xy z&RzT5#4O#X*~@jm4ShkNW6cEhF8QAo>qDh2n;sDXjvkc&te4^AA zEk4zpJwi6-+DUn3?lT*8I>?wg)VopmG}aBM*KcYD6$L-7w+4f5A6%GlO?;~l!}5GD z2|OQJl?NRq#8JZD8bM%SJi#*}&UaDs$s)F_z20IHC_KlqKDKI4QP;8LnjKn7t#6H? zpm_z^5|Nv34~_eZt#8Tm7!uawe~cx4R)lqR^X{s{fd7uoH>UKF#%vI8rt#LMqnrc#WH96neyFW;i$sNOcxf3i*^0iQTTHDh=}v> zM(J#4BDCnKvZbDN6Fi@;GW0kQaMM3PwX&*!6IVM>NI~e%L56q~O`h-RyfPWSe(Iu^ zMvyW6#%X|}PS8ofu(QXN>P4Ut@bz^j(AH?zkFjgpVtIDP8K3nk&!MVFa{63J9V#_O zf?e6fT1!A*g5?TVaB!i$icmb$LA=_50xt=OGBT0~(>mpunmqVz`Mj2;(6~jGzLKjP zLzdxjHd8*|zE80$SE;CirOSOZ3La-&0!%;`evE<7zdY7o;c=1ur-Mqy@cQP=<6L8|5u!?J6#}QF|c5R z?IofJrz3XP3d%Lc;d=GOFLM=TwUnIg1upOJcq!kF^lVM6*wCxxX%wiX6hyEo;>m zGn;YfOrnhI^72^trx}el$m*sXWpRX9#VY#qp}@FNX~Zg<|C@mRVf^wvmfMI)kd`QY zAu4_r6#@3;L z=t7@IE}!{oHC=z1dJyhHK_`M^)KOD^hz!;%Ukk_|;@ zwiM>b^(wFBYN{n1Uavc6DuMO6&usZ(4ni-Q1#;p-{h;0v&49Uo7OqU6ddz)+SpObs zdyp2W()OONP_@P=q1@7yN)4N_;E|7JrAEz}XSz~LiGMf2JU~!3VpGrB)R}C=At`IoV(M&=L=6FfjHw;&D1tokWe9_7OJ@OK=c3; zQM|RU5;ph4ema%%Mym31>X_rD>MGCT?@jMq z2$Vvc!iAQuHUU7DyI;Y7{XP&LuwB9w%o9@J>mFtVN4O1}uvytg^^e5a<(KoNvR&He z9T<~xakIZP{nwJoYpvCnKb-$j(65M?+g%7nQ*uXASYud=M};p!?*2gtE62O7GSyUy z%1Oh>S#;*vOxi})V9JC;LV}F%#ar*`F8^y*X_yHoqp-oDxv{bglQN zzJ+ECs$T~W~25HTOU!=SdU?}%4O*iKeqcmYr&XB|LF&;iZz-I z5YM#g6AT-LMqH=GELJXJRTF0XHs!iRN@~*hW_uICIMh>Pl*6Q2d285vBoNkN{;#gA zCv)s&Ms(#ODqND4N9$w%ybD156jQIB=Iae5bE3jSmwv}&-ua7AR2%EU z4<%x;RZv*wTk?T2Qs|Ww87-`+3p#ewxGi*#gCfW9Cb(-{Hr(tb7>Ly+vHDQ_@OPJ+ zF^J97ja-fP=!&JMe|oB#Fi!+r>d1n>qh_z{Kx6O|;bahS<7s~Fb%-zLIo?k(aPsTC zj9rNWIU}*tR}0)%tk_7KO%Ys_W?MW(BXs>f0JUoFbgD$5PQ$$!^sJqry4?Aa7W>aL za2)ay2`$gUM^>&_ci(5oC1d=xi(l?o)B-gDMd%NaQYHR$z0@Lyeh$tu)nmkQTmIuk zR!k1{d?JnMFdWTb?noCKDe0@Jfp{6|RdmzZTvhPqO^x=4nVF0Rc^W*L~<2g}nop1Ypf#Bo6A7AmSF_<1})3vi;uMql1 zUqG*v!b(ZXtvsjwoSNC}t{ZJ)>*(roN%$IqH7P#j6S$^dQKO%Ky9@`(RK}N%Du=&6 z|5R^{jLn`(r=5A83kg@!=dj zlq{*UslBf>aJ{@tXP~n~UFkB~q?sL{*_Th3)4MB%h%jTZC?-C_{IIt*X^>YhUtO-) zKZzi$zpFCeHiw#)Bt5lX-7(h4k>X)`|2Y%b(o@a{#nc_cKZ(`c+@z=f{yfmMLeszx`$Z}cuyjla zgbDZ>*r+>aPmjhk!#BaSHm~LA@G#kU-0ZAKyNvjy>tzLUwom}MIgH6OFD*YseW%qseL^>^Ka#t!Vi$0f&>c?}G90myxn z{1cnsO2I4dnDi}g*m2vaH_4NhEqx)7tZ$=A?n-K*Ta*E-+;8m-SzTX!Inj#+A-#5h zHKv3RZW_u@QM4V8>3p1dTs>mBu4Vf6gkbXLQhX$r|n zGV216Nh4Z~jbgyfa2Y+R6LGs2(De96Hsh_kGdSrRrDF`?_Lk1G#?l;|yww@AY%gb> z8xL*+?l36&LN(i;ccA}?^v~W7vsZb(w@1>((`dy!Y^!2(+(v(uImHvH(onv}R6BFC zl`sU}ZGn>yrK6{uu)x-|g5FN!SA!*^L+}oGEH90k6@^ALYpDEw-nu4OXW#qPmNJcP zADB-6Di?7w0##CQsdAhhR$|Yy6j5UaJm40en*HH|SO7xFn$)056=5qSo4qBo`#-eq zrG5YPw~iRfp6%D|IdBA9W0Ape(AT zE}H~&Udp~+dZqKaac50ixpAbWCJYByp@K0#c?;XqPIT$lh>9yA1L%k#L{7ktB=W9W ztuUf{hdq@VU-nm31Z$DGt_>C8>rATfu7*Uevvk#@l&f*yGu`_Y1iexj__<@+X<_)%yRBz(NdM;3nn^-TS!-EMK5EU6@oIB_y~p@dKU4F+xUq*CJ({ksTb zqOMF%#+jh?TZ4p03d2-0#QPZnYP1f@mvg_2xMC(T{opdjwV{GzQTheiNf4W?& z;fm&>K{Y26d=TJAYP#F}_FKt@%dtsaDT=|;#oo=YJI2{HQ-|;QcettlE{ONFnp_g? z`!fsicsZ*DPIWf~QBB!FmbW`K)b49!#2LKj%9?MXKc#&M4OqycE_F;|69NOn#i`;*Y-0_04Q`_q!nKh+ z4%`2sx~G<0#XWdV&*ODH>94j?O@gU~_q!~qqD5h;4{EEwiQ3q%6L#f?xcTBw6A(lb z82sQ0bgnyOtNS=`!FqN=+;k#@?!#+rA1XKL&!y%)tcG)j0n%wdG3T6k=I2~57s&6vfzN+H;NWisARcZhI}QU0#tJii zZOOS8W}LfgG8a?EPqmAv^0JSzo$}wcH83O%>XPcY{0f}Z_c70)8J~=xl6D2MUN+d( z-S!m(!^g$+9Dn!n{ADY4m>mE%3T%2HZy{E+tV1K5QXDj&Nq_;%kR}5yaKJ|n%8QI! z`kuaypPsNk>Vyf3;@`}iT%)`l#94tD+JdpUocXHP9o(s`Q*=?M58d#+^QYKVftic{7p%hF^i zSg`oO*b;pC@AW{`%PbS8L&3a9Hj5jAgw8gu<^3dQ%Wl3~V%f5F-;6MYx{}T^DNr;1 zV1%6UfS~-iIuXV{j!if-H;xw;#jAvy$wCn%OGEt>kXb)3a1vxKY^I7E^M@3YNYuMD zTo}T!-gy9%Gn3bS9Fd5p6>4RziukrO6&uIAUPNLkFj^f7;aIlkdiK0N&*PMqfxHPh zKx+t5%e)@QO(2H&p9ARnc$lagTr7s9qG{7&gPHLW*@EYlWc%+d+*ZHdk{!S7QLG+x zutaRel52-x6?RJyw_j?k-n3M=&jfwt>K^>XdKaWVI(7Q4bJIV)OUjD@iYw@(n6CKe z%^})6eXOfofHS^yoH=r{d2&zG!?^RkZo(1~(Vq*JJ*&=h(equaK=&@ls(#Smcixm*N@s3`mSTD7qw zgXmYMmv44o)2&iFM0l>RK?C4ynBZJktU@-vP+Z!b_qR|tV{SiobW0139(b=3O_ofK z>bA1??lCwb}AHn{^_=U`TC~o&|Ue~S$6uf>166uM77U923~t*s`wkJtW(l0 z$ar3L|LBMvYm*%-%z7sT|B)q=VQeYZ>#O$@i-s2$Vk3!AB*>cX6+@w7}TTuq_ffs*Hqx|_*My9mOTHkiYDByXp65?$z zr+1*76ppE&DrB9%w9(z{7d$=UYyE}?XyQN1MHUw4$<8Aa5 zz{vVyF+Gmq1Z9GWnA=9@AHzx*Xjg`u*!Eb)IVwGXMHz!DV&mW^beTfEXO>3hj9VRn zCe83W4ZOjh%pTq?Yz5hJX1P*S!ki^AC)n==eT!aZy@`tRg4GKoZ0zkE0t6sS)4)rU zj9|{zaDbWWr!z-C4@>(XG(V8l89!{9O0%ZQ&h-sNxc&Y0;Tf$c5ul!f)BDt?^B(z_ z?n869FXXwcti(JND+k~t#yZzB5Ir>$< zG6Wssi`8F{9aBItXZgCa9R0m4H>2zuj_#m_bP>75S(0(S3Hh1gEZD%B4cP9>e$jL^ zu^PYoku(HnfR^1EXlT|UAYhQP*Oczxud1u-o@`pSaYW6IJbsL1_C~!ZSr<7_1dM6r z(tuMLZnmm&M1T))==`4)W~e0hq)7Re%x%Gd9qTb z_Ppm||0xtNXE4IKb25ftBh*}zPC#u%%gdEgwrlm(9ZOkO0IAIDP%Wxi5T{yT$?mao zRLPYX33V?HF=4^qR%>%~8J(>wcxcp6v1zuI0p1{V@^o!fvYuiq!@h1&T7O@gO)Sx^ z>IF|BXAEgtuQEm4Z~SgW1L@B|mflk+wz~TrCQD7LP3P9>um5v}OlqA}z#5;}qL&^d z=S0Y>_}~8oP&p+T--z$O9MSWDLbBAak3L#4D2mb6{umuI{V9Et{=J z-IJ`kUtGG*?$NtEqSrLhwf{xqu3q%~F$8GC6mXsUO`ig>quE_ge1NshYL8wP{dwqv z2u&sc3JGVCsDk}(Tm*~1B9d76?GoXtx~eQn6l_IKQU$TKlhX*kmEMLU1Xn z;`2Jm2$d zmG^y@ydoD5ZyGzZpVhP;L0w}D|30$|US|M(wuI&Qi}F~9T|pfhj>A+d8Yr-Fa6Dk&k>wJ@1CKl;ZDoQ5g{7|&i}H&l<%9- zPnSYwa*}gU=yL=ui?p>H<=M}_5%!5fH$%JGy{pmi9;t!%tz|_I*D9|awIwX3u>$l2 zE31w_4F5w0Bzv9l*z1~M?Xo&2Mvw);f&r?Gu#EuhqQ^)oSu?M|05iahldHpZwV;gq z-a}{K$pIZ+Kj6U2kb@#5NO&F=s@|_| k^Z(56{$JKN#{NUFr15lpjIdhvU*Z@bB`z;kEdmPqA4n!I>i_@% literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..51ee10c227cc5c691e84343da45355dcd1b5c6d4 GIT binary patch literal 6132 zcmV)Ym>oA*k(qz%x5&hUPHO`E*D-1psczVn@P?hAEo zyS821u5EwfjsC+9jD{ateV}WMRZ$f33m;_s^*;+3!@v(sGk}63%ar8Ldsv3%5KviG zBw3-9{|UfYmS)jjyg>>HWVx7@e9U#NbkSxL8`ip&&M1hAa<0H#Wpm1fr*mAn*+oLG z!zI@M4<2MdPX4b141tnm;#=ebJGC{$4DTOu`z^hKZtEHs(4&Q!i=hG-9mT*faYqBf zBf*qpn{3Z35zZb;x9{J1pmOWRpUSsq<-1E&OJ%{8mF58MssM(>1lGWck|N^&0Meit zV_Hrf*wr+Q3gQfu2*9}ETH#Uh0%dWC&6bZe`dfCC;)-D-jH5FUFS@QoY$vU@NMKpyF?*~G%+ZeL12B2(Zl<&gB2kl z90%?g$Px?O`hD3aGnV8&e>~HfO$?4fj-%v19bh=)2#B7isfAC+&YgI7_&@M@$tDp; zLohFOOb8%YlLR2MY!H~9Q+Bl<-jx9b?# zPbw4eB~c~oIvAi;sPdty>)Ib~D z6~W=n9G{)x_^t2c^qbC?2<4XxTC)SD4G_TUkg}k|0RSLabjltJsCj`X2Ncf0u#^!z zvr=@WWjT+eopl{OmFvo~JLL*EinauCmJTucc1djvU0O%$J5fepD7#FFE*XJiRo<)K zV4~c7FssWw=Sus_V<-EZFBU4idGrqj7|uL~_PA($*zuJekLm-Ni2A@nKsg;lNc(KM z{owM=CCk6wQ@;6Vh9le8xse9}wD@~(w)}nk9W5q>$Liy#Dv6T7l3BKlJ$ssR?}GMt%Xw(TnW@WyGUZ~x)cHCX%0BF$XzUgf7tlep%MwHd##_z^c3Dj9 zZ0cdRil=@$W;>#d@v2*x#u30;CdwNu6w!7;=4zLJuX^Bk@Ip-0yKauib`qRnAG-=W|hIXXo$wj)wVF4 zS>=m4ZC6G`>N-enS-`78K9dQ#!v$J+=DpXLO5N)QfHEwIY< zKUg;>Md~9ucQyS5t%_AG4S^++!_1i?OswIH-PUg*ymHD#SXq->7!Dc+CEYd1JYd7y z?SFJy1-C&5`5;OJ9ZggLK7PKTc){JXGp7QVa7al?6IB|tK!blDNlx6+IVtcK5XRnG z58a%mb+o(N*b^NNpK+YSDv7_D0N2!j;b8D_@`$jhl!2iPp}Y=K9%tGq`=OtY+xGr? z!hSF-&wU2+z5VK2nhY#*G5|}Pw0mY}J&^iQ$8?CAApoJGrCJy;I0LJj{I{@4kI&D3 zfs~Y}=Bse{_Cv-a9(yGk6O;qJE5bKM38a=1VPhLCg&-=6h^YXZe zCzM0I6k%64LD}71!Cn5LyQKWoLn+zRIU~fx^8Svc$z4)2?b~_>^$54{mXEiWes{rF zSROAHgp#O6KBY0sYDkr9R@9iR2{bTF=4VOi;bD9ngcDiI2q3SNA?PsoOg}vsIB!>D z=W7XAKV$%~V*q6P6%s&7DwC(6pR z$3;whX~aK%_U#@D&44& zk3M|baVqni>u}ra4BcgytT3Ji3qwIh(Jo3CA05sI=N1VS{-jGx6A~B>gH-@5sKkdw zzOiIc>(v|6&11$-XM%!bG6^C#6manoP^3> zBYpfEbe)KK-C{vO#q&bp@HLCss}eeUdTWlzzD4>P0LBTB>QYHASv{JN}l#6AmBPzZR z_QcW(yIiaa+y<`&+|HmLq13|zZuL=V9>Aa?OraYBFGVk1vxAR_?j%)|BUrjxVG8|^ zW?O_A#M0st+rE^~H4N*~aM-BeoKSY&mugSJw5lN?DPhwWT5Q|*SL{?{PJ`9~#2#6X z0nw@^D{pXiE*~(@`&!L;cX>e9p8cgt1dFQy7Dh|j7>L&9C|NpxR@R*ZVoR62vw#q* z;v;WU-RiBi1@kF4sq6FJg+gruAl*6xqq7yb^Pmrzta6y-Lrjwm%G!yLwR%H_qKu(j zvXosY6sYsq^cF@)95j2#=&@sh?;JIOa8@nL0v8lPta7gO)IH}O7?x17VBS0k0uCZp z8yJ+DICJ|>*w4JMDCc216x&BB{H~86kUs67@s>I(tuXxoGn&HK@ncXisGp z%h_F^la7rOT2=Zu3L3g`l@(H9PLcb(mZ>x?VDM>ZIb5qKOW3qo3&FAwK&CcWTxb|q zwQbYNb7O`Mbe}$%4S@2*vg%Rx^;nOH<>P;?E?E8Wj59+E3f<{g!saX-NA@6=$;fo< z8$3C^`-T+j9-`fRxGtk|X^(hgr`EBCBpKXY<2n)~0Lb)gXIf#IXhR|E4`AFy;sr!c ziD6Gpe+43As8m{lWX^lLA!`ZcDzilC9Rf}X5Q4lWXb4Z@YQ0zzBO2367U*eG7 zK?4$78xqh652f3WeRr^GKdIO@(wtQ_ocz#jq4!dHm@fxRhFXEeh9?6;cF$4UkE*s~ z(0UtZBmb2=1Fk6R+b6V*U9%+}{^nGk^qd3oAto@0a~wUiEBnFWgFWwoY)+Ogxz5x; z1%Xxzg+9U3(i|ZBz|)^zf7)PRBH-u?)e3Nb=#Vpin|A7kjX#y|^|v$bw@cEhd4rOl z%$R~Ww5DpXke4urv{jYo|Nqo67u3IWSw%U~)B30JVzksq?IJ0S^%b>N+w%{$dd3i@ zix35}W<#q~$gG}Waj~)o6%ZMJ(E`l{L~bzz{&mRx01UV%Tm4=IxQPu8 zp-MkoI7>K}S%{p48m+N&qL#xTLIMc_Tu*9(ydrs#J_6NPEK@t}Lf`8HJ3M)J%h^hW zNUYU6xL~t*&Cb$QxV{W0!r!W|Ux3kM{R{N}`VP8@i-?L*+-|8_oPtDP^ajp-E;HS_ zcI5{emo+AOiB(Dj3XvR-0K;>{GB0C8U~pLi6b`BN|7^Pse3Mf=3-xGi0uWGliBPfl ztKt=^b@|z6|kY+s$8`$nGLis<^O(nsa#d5fa7o&DSgWlPliB4;57EXae%IE z`z|25|jCh?jUJvI1EVU3**0zX`}BZLQ~hf)kfA^gz)^bReAHX z^4-N3yVip92P@)UU}T1*uKg9at6Iw-Ug%CzWre$H=aw%$0LpShmWAgE?0;jw@L6+~ z>e_Ya0N8UMss1PiRO(FsLz$3`rI zbyIg!Qrhb4k96xASO+ZC^=K-9p{Bl<^B4$T_r-A6#R5ig5z(IW z`pgc%ouKX?w*0x)^L8ysI2skD>x6Dt8!R`}&$v*YS+%}s9_YJmXpxt)?1^a{FAsk~ zgp9z7L(M!;A*C(%*^IP?WF4*6wSq@I@F)n|P{rkR13-K|z6Ekc5?~T&^MezgRtLZ| z#H$}nkDZ&bHtFny`&vFHJ7rpSDiT*0EVnL{JyR%Qh1m|DvtGx2xx+( zsgZ-s_tF83(OoLKu%L<=LJhD)0+#M%s_lncpFKT7RrU0xHjn)mt}L|s>6#JJ>k3>A zzFD#uGgSOMFwp0@_wGfC8)+#9E4SbZ zL^zhnj>}n&?tk))WfsLJAUc0|5OPYZU%42@hw`!txuV>q0#v`{df3!JdFfqT9E<^g z;b?}l@a&q;i{|%y^yDxNEb%fI2Kxb+$0me=JS&EN+Iw{H8d3}w-l}B{XYnC?oyQOF zrgeIf8TkA(cnu@3EA7}`1rH;UQmM^eByt-KG-om}p_HeMu0VH4z#cFk%JX)+lr!nY zj5{BgoAs>fhK$$*tEd@2;3A`yt)=;tDApojnh17Xo45qZhqtQHlnm6m?xSUrt+I;d zA+*X;-3SF@6;97n@RD`i>i5-C%Js56Y^^MOcxW&eCPCQ->t?(KQZj|IC4W}%YWp#% z@yos`*{t4+L^su4A^uxf{`nk`0+lFyG-U8TGX;7J`oyH(k?a7{Yp!JkRlMlUC!N3VPo;b$@QfOD|Ju#emv3zdW{nJ$4|TKm zKFoWu<3p+KA9?-UKk`Zh2e}gjsH7=Z2XTJjMLtZ-&n*!0bI<77wC|?49177^;A)V{ zP`Y;Uy>!>Livf9&qm_Z`A##%PRBT-T6-2>X!)Ls_l#7aO=k3Gb_67O#UwOv5cGaiE zl6@rb8USG-T+o`2^56SpN9o#>!&SdlIXs`s={P+wzHqr~@_lLG)o8=Twj zPg>$tB=jN^*6uQJVISO8?eGsW~9j@P|jtTf7l2N~*3ZfMfun$q0G5 z$Igx(+*xrtF)k=n1LM@r=i@bwDE!8vE`xm{Qe(U8oaw(s5jyHAU6 zPFbNP)V7~fc8Ns=`A})N(9FQqdrpU_kBjdFjgfJbCn4t>JRgFE1n5-{qt$wtUtnpa zAplBri=gjW$ROn{Ly_8mBYkGZbBzGLT} z`ovECb#2;pLCJ!9`RA*bLV(Hh%UYX-!@LHozrIU{)~h$BLDhmGuBV*?1_r#uIS&2u zP4>8v!^!iwqNl0c!E-MBEIE4N>Q7C#55G@wx;*V_my5!53Y#sADVWchIJ%4D;J%}m z+f%5Qg8e>Hv$DecVsO70aDkq(!zR*k@047FuW04<YWp`kJl%Pz$*t?+0aHUJ>Llmk0|yNqJ0}9Cb%idHyAEhJo2YLa zmk>PSo=1V-%3UXq{|3A#R?oV}I#>)Ccswb4YZ1_MhmlMh0%*(FNfRPpUGTNOLt;;` zED?2HO|V2aYYx@fe@@w0@ZvN7=ZXAEvw(GRq+geS6*k1o#;)FU5OT?`p4B@w7o?C{ z7SD66DTs1sWt^$p@y&{=U0c6$r5-t<_-KYho=NHBJ0=9)Jak0x(7#WF=oc?lRpQz* zv3AKCM6zI(8l>rx7hHeNhWdYY~1K@Y!qgy92Mk5VYNyCy554T)08Fm|4TBsAV=%<#F8kl z-YxM|Y)wLw1*@`ftVncx-4eBe)&`|+9zaUZd1TZe6se7-WPb#}yg_4w&3xorOV>hY zd?auUMQZc))$K_+5P3>gSEVY|Lm}AF45Ok**OMWjwQ;Pa1!dotvgQ14 z)}@htJwX6n0n?+3O`or$M4{N&qt9Rvu{x+^pj}Bct$y{;1!QOh#zXcO_WHrXGzf(j z1nA%p9IsgV;ezS;v!*WQ%{v;F_^vPtAPIH297Ou*(YC3`pB8 zVS#~Sd|OZr3zW$^b_&oZ*^Q!`04^n8QI6}=0sX>Xk&hQn$r zd$3E?**tpeQ^x)`jsl(w#S%B+CybET-}FcfrH18!(!gZ|BroJxN-Qb1+J8y;viysW z7utXR@lZ7lV8!-Ie-{?jTsO&kYWTF4nt*^p=m297U8|@8 zJVYrDyA3KLS+3J3_S=8iv(3I|*AC%)PKhe;Sh8UK3R_k416BA*hYJOFpQvKx%SAn0dZP&JI+qLb#eftl%L*>{iYq&rF00005lRY@Nbq>@0000UwNaG70Su8Io6I2j~`5VKl?o=Unk3oOPGlLKMS46#RNWxzu?hajr3arfQ2gv z@!QOTz~t-fNJ#@jCe=TVHnwCY*H}Ci6Stx418Ei;EFC)-jD!_&YR4x67#R9se$9D; z6GRY_LNbVNdRSLGIp9hgBrBefMZQA@;6o_~kZ>?`YblZ`-_BLnT6Y$xt1xW;7X@3a zJ6V^`;*)enof~@CW1vOUTwVk{rdlY+%tHI>X762YYlmV7yUA7>K3}JeDb+=##iyVf&}?0Ep07*n#RWm0S_sjwj-CJw%W^Fo{%Gi>Q=T*(Pb zszemCnIPxMzLl~!A%zAI<1*mo?wQE(La%S7)A5pMV|3f;lBY!7m00?gVG3-bxJlg( z(qI>X0c(-?P}#pins=X&d@nragxDEEs%lbRx|0K(irWXS{tT$>|C_2qFc$|yB(tKl z{AqdZe#(KS3y_RsySCCjSOI|eHK4=!SI@Rb#(AdEbZI!4wWj6S8o!p!8WM`Qti+; z8Y2BIEYOIBtnT_aCOe_$bFex1YllJ!ajQx+J)Vkw>Wd4o&yxx9CM*W`w7$5UM3j~O zfwjuVTBxo9&{9zaU+$=<0-M!Jw!liQdFjyFis%fx0rpPR_)UO!%YJV+gAD01^yid^lCjJs4= zT@PZA%;7!y@ci3c#BKl2zA9vDFqi`UzpsFut8Top$f7Z@-eh-mn&@)M2)N1w^zCO2 zkkgglc_K{8x^-CmZT>)$UDmu7v5ZLwF9pI`q|yOnJRb%bB*NBT?8a6k^$5QswMa=; zNLc9{aUVvgzLjGxoBGbc4+gN1>y@>}DG?YX$71nQJj!g++-o;&1rA8f$pHvx?Xx?a z5SAT}!3uc~7q%UmcM2$y;bm_#_zDzzXvSk>9lKmB{=EdC$h?{OMMYZ!0!Y!1yb zBx0A>Q-DlLx&=~4O~i<-d|}wLmCfZ%c_@=9cmoC|Dh7%m>y7Z7-qYnx{yAOBNZwep z;QDk_9E#SsptCeCK^c*TlE+avauI(*8oVz-JMBfHiid47H443e=tVa%Op~>J{-WG=mD(lj z1V_>V-q_|R=|vXra-+_edQ9Mcwd%Us>-sM8o?Aq{DH%B#q8hHyZsp29H%GBmb#GaqcC|pc397UtciGML}dsVqIAB$zsExD4(V7 zFgdB8`-ZZf9H{|z z@BwRN8c{FYWc$>wvU^n=!pr@uVRBQK9cG%gdd8utgG2_oo1VwNLu3X0S~j84%3(d^ zZ;#g;43&Uvu+`ePf2ipv^fO)`Pd#5p?4*;D4dmu15Xj>B85wKBhj}@nb62G|*$`{= zGPCb)PSD^`gtero0TmNXU$z+SLJ44SOzj8RZu`g&G?3@B#zhi1#lV>IhI44Kwmet= ztY4R%>2)deb13~BqEZ;N)E@i=t2Y2OPwPtRfnOXjrVY8u3r}jYN z^BoL!FaC=?0fqSe0?&loFYY`?&|{zXxNx2~-iN?1T1@Jp<=oIPpTfx$M`5VHH92oM z-nY&et1xdGXnVTCZq$yr*z5Wt(2)aXB2HAx0$zV#K8mo8zyIvB%!92bBYZtBNrrg} z6DGe?5gIi~)NvYGWbe6XXzU3xm$j(x^Wc`MwL69OJB@Zj*f7VFgl|EWH1&nRy|b;3 zI7B{J#WLOWb3jy1!Jyydh@raXZ7Y)!qTV_8SEu*H06aMQ+ut{<%ZAd-9 zfAwTLw8#8mB+*59GH)D_&TI{XAbFbT`jB;v8RP%ZQ)u5GAi<^)pJlPG=NoDGdGWUc z;$8ClXL*8;O4{)2u4QXCXcfkwCm;R?cd>$Kju)W@5YaFMx8miqBZ9F^#3SrRycYy< z!SxG!Yz_fCWh-u{FzZJ<&0_|o2IEHwQ_uI!@CW}GykO?NbV`vq5_DgBeC74mQBO^o zMIz&Yf}R#wv^uVxRou&zeG*4I#!f?fJeTBA*qx~OG$4e>K0ovI= zD_W(JpwrAPcIm)>Cnz4rwQ9nyW#@{-Dcu=<;Z#Z#8hquRtS{&P3^4l52wv|s9~H{y zXC->KfHU-S>&8YdaExMi*f2t<5+EVjWP>=?P`{5v>!oWMXnpyT7AI6L-%fzMQ^`Ee zk@Z)=^C)7jf32ehHDm9PfbGOnl@;`^6F#!Nj;g|7W5W`6{NtjqytZoV+(o!C@+DJy zAn+G*0CnTgBTl>qv1}!ltU@Y62OkPgBcUCSw@z!#OXEt6H!1&vc=+z1<)bsBrUPWK*{@ShI(=w|#RvV|4VDLZf+^4+7$YoB>^2=Vm$1HWoYNYl#58Z@F1~lG=w0(IE zu^F`Sm{!`S9%f!$M&H)c(l`5b=G%8WGz|Rq`uFa6+<`@I?6>#1hmB=lzQt?_a;a}5 z2+AL&ecbN!q+|pWb^V!p0Sl0x-(n^yEKp)iwz|o4*G0<*5zGjFYYen&9_4DIR>DfO zLv~@Jw;%8!uW<`K>D`vCjpb0T8b#o=a{LlY#NWnA;ee&waKkRL51Sy+W2*W3V zr}QmDq#sH;RbUtlin4lU5{9f$BLA!oEdd&}|7hn%_aic>#0xrZeXGoU_yJd#EN+Lu zZa)UX+Bn?c=DkR>5|_BIooQ=rEX9TAlmBy-taH-nZL^s_t^9)&;Sy5(u;c3jDSd2= zCh<2L(l%vIOGXfzrx<3CwnT~%zFS6-S4>4X8Sr!m!seV?blw#_7z2htd41Dfid);tVGGC8)1suENqC_&X`u56(Nk~SQjOu&MxS1uh5&LR<3QHzSqw5il1 zO&^Fr=ZoHpTLX(#?=7skj9VI(>#O|>8l`8Q-{Fr`udB8Y7gue`g_8zxmz@EA!A?7- z_s^sqWx!6Nx{mmlm-d@4pn7dpQjP>cW?UPOqh@g%y)m;cE+Qm7Qhu#?PPQQvMc>&C0eR0ia`+~?C6@<;S{e`hDz}jrBrf-SqLWuLZRB0B|u!QVSqrwGkhJK46t zu1DnjGwL{m4GeoFX{kTkcm+tWgz!vDVW>3I{OiLje5#0OvI|kTl?P`F-)rXOR~HJO zLNLI22rMSy+$M()vKL~!I|?#AM?s<3(R`#@yya$iY;4PO?2i}|tbeyv2K4mFgMFG0XUF_siV#*wSjM-ED~ z<@*%MS_8Md4?e{&`ie@?;v08i5TJb;ql%1tG-%VlhKGqj$P45<<-dUtLe+8gAWgfl zn;U&og!<=SLpr=|+VFwfD1B~rJU{T7X|@`Arh>BzdBYjd9a&1$<7o!B4fPvYYHj_*)~|7%i{jfVLhOud5OznVXmWwpK`>`yNX^Jg*9jBrAmee%oqk0)-f7 zQXrN$!1}?E-D}H8;u{c()8Xhk?sPWF7Oa=y!yx`)Ym@Zxvk1a4l&IW#4)DLc+Xha1b18JZYrCU_(=oi`KA5i zm6I<34Gt=i-t6IJ_jdmpM3T!O+AmEpiUZ5ElL>Et0V>jptGz!Ljg(Pc{P2mbF3Iy$-|4JlmVeb4R3AhOH#U45uO7$bRB2$Rm+zxwhLH1@p8~c z%$;JP*rp{=hsNgC9@CLZ)y_o##8c=8tSN}_;*yVT7<$t(h)c`wfzUmfM2u13RG_nP z$DLCz-|K^brn=wlm(E$;CyJBt0eYW*TkV54eB-(wSC87wrP1(1I2UebrrWu52 zCH^sQ^?9v{>qxEf{-aQ~gWWFNC>)(=wh#Pa@Y0s&_4FkRe~rT)7=S=Qs)S)vPbY&Z zlVbEdO_zO2hkf@W_UO>-*yEiSQrXd&m7vG%=VSOJx4Qk<#R7>*d!py%+(3GXh}gr~ z9Lp(2>G)YP!Vr-Gg&XWTso}&>TPAa#SEJf5rdM(C{2Fm~VFgMhbp%2Gv(@qeU+YxA zF1Wx-Q#~mP6d|dnA2O^&B;Fs%8;?5|g>Y&Fad;W39{}8-C~h`h_mXz+yS}*Xy7Bmc zCMf~M%oM|mb4D?#Ocak%(G;y#>KWyH!75-WAUIUAOLm*K%Jd@{W{XlnVCH?RToVYj zZW2hst?qC)-u0f+{cs>B$Fw+KD*m?{cH|PF7&&22@T1peA`4IXdzF|NwPD{zbrn6?_!;P{#xNE;o0TEBc=#e0?zXALlp-Cn&qzc(r6LoDqTTs z4MwBIV2weQpn^(p*DxU}q3>&VQU#nE)3mpTS6HQG;1ojW7#D^@&udWTHMU)FKX)xh zOsiHo@lVyLfBVE3b={Y@i$2x-_{ ze#BkW>NC>$LZvoBMrk!fp}5@-9Fe_@oLKTZ#qIZZxbl?runW=Pt+*OZpY%u)Xe5I~ zw46mO-Jjw`^5u@O`xqH`RMKS3%^*bUus6djLS$kIUEB)ERpot2<* z?kx)PcCA|Ffl!En&^3hm^RC9sxfeu(bz2cQQF<`t4hcc;t?f}pmp=Njo4y0(F zt(-QB94R~Z2&@uc%OdbM`NYjNqmvE2&<(#_bERWn#b;fdM;L&bpG4fvr;a!fVV?2M zp-?V|-mLB3gQ1Rzx2}v}-9~*%)#SoWEmpQl6K44J6Y0#U2vVuaho;3AV7RKxCMCjT zRbx#HFU0R_efLvaR=G|%C`GC|*Hl=yuA*F>PuyrF$6!WT%9bTr?}e~<#g?Q;n?6YA zp`bh5fumW08cT@Rv{*^a;F*G)3(6q}i6br{&eB5rklW0_Bie@+L&Ql@c5Wk_`r+?| z5gz5b`p?a-9t3NYr zx9h23dc-*%vhdoc8yx&k{D6t4OU2l+{~-g#g+$VxOk#d%JtLYLOwa=4QD^_gJZnwXhBuS$9|z6IS>lmnu8cq@LX zDMhepQfcJ0xVcl@zBO%FOkB0qRjBW9wU-v9m9DNuKN3-%a}bLn@oeK1w+`Q^ok@%F z>JrX*l_i6dWif#~Hbuy;ZY{0~lkO3REx-_gvXBS{r7M;dk1>+LQ|By{2`#Y<6Em;x zx${ZJ;*x2JtgNMu3WY@vsTqNJJHcslXVQomttRpBo5LUh94DT|*&`ICy)`4uiPBjZ zDj-_o@wd8gvga)3iXjBhhIhZ^Au<;1^l_4UT6aTsDU6czM^i+-W-(s&P%5N$k?hCd z^cz7%7h?V*WAe`6)8Wv&xVz6)t;V;ql@z^u-8a!s&FlozI3g#TR};@y#pHEflHzsJ z7k-?^XcDK#OE#SoHeN&}kV4!9f8Rq86pVSl?(3R2v6U2T?Fp3tiuxY*Z`wx;o!~$p zwl!Q1Mp^}!F9lz|kS2_Sz07_`B<(6{!xM4kTl(#j`beM&dRPcRU$2fdAR_Dr^KpHp ztgHy8G6xe?4^X5&sLbYa?TcpHIy|0&*{FF?$uJqcHn{F?_v>`EM}T-!i9c1qqd(lc zpM5!=*K$dM*mbGpXH3r<@`C%tJSK%EFcmY%tj^__csh7S-}uw?5dL|tTLsgCAgFQf zLU?8X0Z@BFygdOzVKxnzBW)DSK8>NEvp4EM_l(6PW@grhVs21@$lfJ{r^@?dqtz|T zIivU_fth4wlY<=d4poT-(MBVc!CiibsuevhNBQr0_;ZeYOmKpdqE#G4-Ld2BF$7wA zj@LH8^v_Mit-GL-dY9E{IA&hYJM*rGUrnjPNZ5l}3-l0?10|q+d0fUmN_D~T-bwP25#e7=|+F$oQbPm`M$@thsIF4g(9)mjPN znIgH)OV`Kv%l3ugLRwEJx7GNT$l(B&+)8 z*F?Bo*4UcJQNirMer+;Le}^c$q4ZPs7kr9 zg^7D~ovp^>$?0&f=lXw?8WBLBL~h&hZ%#m+A(;49QUtrtWMU<5m()`Kzsi9U=;r1A_nN% zE>b=|>&8U5xY2MwErWoQ<>txZV^q4{HX?0P%mRx?*6AI~iR!=><2N5YYW^`0_K>3| z&yzU1Ie|Y?-5icN7t~trEEnfw%afOPl3N-<&8p3asEA0%leVyXfkilucnr=XixiVu zhe*RQ3-J{HNu{^zCHuA;F^>(U%3pt-goB93D~o7i0h*`Ih@rh}g(;55q1teomKy>x ze?D69cu0kro5PV>-=Vx8Y$__Fc?Un8D5O|Yft7R$6Lw`~&lTT5 zL>}%NlNev-@mcqCRi!%;WBv_Fw!t7`_MlTKp;6jY9=+-@ zR*xKvn%o~C@DiW|R74z_s|94BH$Tah3Vt%1dE_V*@ty4OlLb==nVxx{qRPKOJL%rF zag|*Z(}98P*5r{jODY2T#nXM!RXJ}nK4e#}T5}i405K9km>gsrkaDxoEYs>b$TUra-&#x~6hQaj|P=UApK~}B?6{y1+u7teQfR(aP;uB$e z&K)rsj+0+Ioiyw^T(@gQpT}QnjsS@Lf*;=vaDZxD6z!CHk^!7d(_l~aK6iGu&+O2{ zs>-Z<6gyjd)sx0s9+)qn8ElU-sR^mM4r-XWFe#G$d%A5$dvH7Age8oeJR#U`BqX+r^fj)Q?~7XsQ80yO+Cf zMyQ}lhPwLCw*@-afzQd8S!B+X4XX1KyVFsO3GvK-v;I&F#d!7GP%tXi=dtVVom-g; zuFHe-`mDNm!5WGGSk};Ds>wJDQ}!dfYl8+!D0T-CZC8s^8NoG@EJA*s!08|^`aDlD zP|-nKGeD#oc!ZF&9=lwueTBYD*>$*mnFy~Odh$xw73u7!@@jGu)bOEzVG++X8T_fs zd9e8s|Gb{|WjStN2pw=l{0^x+sD=ZV^Hmy~W~Am2oUFo%^%&*CC&VwY-)`s$>*#lh z%Y>8Foc_Gw)AYOg2Hf6I~%AKz)Tt3!ZyQXmnwJ;YHi=6RfT)OP9+d|zmPsHO6t#GTm_TXf zJD*^0ZLfXAKmfK9(DL;b5k|ynd2mjAtP z5efaDsu10ga4HBJyl`Baq7!BkT%JP*JQ5XG6d9Cr$k82K=x}G#_}x6wTjp#*LG!i} zJE-d6eTtn)*O$}QR8)pKmuWDkcZo7qC4narBob|$$jrF0s&a#m_Ln0lauQ!(U5A1>$s|n`h$}kZ;gHHN)C@rt5y>!Vqz<+>JfzGqn%J`z%%X3acy#0SweiSqJ95{oZ}6cjXpu7IkRka6)tjx zhV~l+m=*>+=r~xt6>%^6^*Nx6uj9^wabqnv(v&}>c2Y>F-Ie;QeLO4>WA!}@%r~0i zKW`wXtw^Oi$8llNp4v}9`wYploXJIKUbJu6OXWWKM~zIdMPPK~62qR9I6mcM9b+pH z-J=X%&=0U1HPm#JL4gJInJ&6=$Ii7~`(SRj>QavyL$a3= zf0?y%XfZRKaP~kFk>H$j5tlET5p#F1sG8}BZXl@m8;>v?s{eO$5bYpcZu9FTN?>HO zl9lV*t6-Fx#)5?LRK)Fyi<{{+AFhGucE?pr>0(eK?~W5=qE`J(A;twS92)GdOGDIM zvT!09ss!9f5ZRx4z5-Dbh6cTqzQh}o;1H4b%GoNZ%MiAX(n_kcvjolhTgG+eLK<%o z?*)$o1Ybxab{oRSU5)aJ>TG zq0Ex#fspQG_hLag8U_xi{%)PLwQ;HL7Nv*Z0q>+Mqpg79r#qbYgDyG06&$PI2lEA> z{l#3|)hW!Y(3lA0eY-3#-M2{j_rkiqS2sB?#8JoaUAX_UH79YINt`wrG>Nn~xZy$a z6UVuThx6*m`%3eee;8dlIjMT_eJehokW;vVGxMa|>Jo`$m=d4)A}`}(@O-9Cx08$d zcFuC!nGx7ji9P)-^S)5e^7WtAX3J>a@Xupby>Quvdx~p%s4=V?5$Jmx4gQX;{db|7 z%m41sB=ptpi!+K|GTYv}?Wm2z&G~SWWQptasB|ANP~>uY$y^j*CQ_k*_mGzT&*!7G z3YrEPM*d=*Wrs;L8KtyBI-lMumHC7JHsLrtLThuM@HX;`=W3^X*xamJ+2b7vJ^rG2 zacFoD4>X)zr#TD$L<>P8vt;UaYI^2;OX7QMmv&ciDuA1lJ7h%@EkZx zLq2v?p6jKB^Wg$Zo$L`H3~{o-^n$kei8wJSK?7Mtc4rh}b9&AkTk+yDO%`u)F#A#u ziOq{7oPg8$XhAICbg?9-rbZ|AI1MO`CEyhysG~-j(`AC+&_x5LlSaJdLZ}DkEu;|l zd1{sNT`QT+_MF#4;*>nDfM?O2zGf(nY_XlHS2ThspsU>>1gJ>_7}cSY>@SX18%o+6 z6rXSGk=682Db&?A#H&zPs8lOOD{T$yVD0X7l~DxLEs~wAnB&;bgSaeR5Y2U)&SLgp zs02z)9Yc_G(bhFsiD*+((H5TigboMkrIWFeZWWe{h+zq-m(=WeSZ_O{Mhd~zAB|u} zZsI}1ZYHD$$-MqhtA&Vf{b7Qg4|sZtwjzPDlM+NEUY>Ydz9pffB0nM{mB$*nhO z!8guyhrx^zRCtQoQBlV>_!@os4>LMfb83nUDJGT4q&n4yRnBI^ApiRXqtG~W4o?aJ zc}+|?yd^j?e9B!D?kcxtPyE-Osz9rJ6wJn3=c`*rS7(dd^{^6+f~@~FJC0= z2RrpHpE;+4>iUlCRI*JMHwNfabs2FmhaNn3tA7hdp17sRti3iC|I|P};5v_w)H90= zuh-wOKZfEfEz{k6iS*>cE&2PZp;6A9Y+BFh@ndo+rP!8xZTI~Z1fKgDjg(25XcX^S z@v;Vmo#C&=Q6c{$)iR!+Lntvl@A3&&l`L7oPnNZ;!B_!wzpUMfiTRq-)Vwm`HQrQ$ z5$>U1F&Kw1{%BO1jIz5g5Fogv?>V=iWIRm3&$-M6I9lGPUIR+`2!+FUjC^@V7*|(S zX5fxdL2t3T=o>zb4oLs@G3L1@G#$`9q#0~ml}hZpIiLo_)4y0CHHXUourHM2eL#)L ztgXJ_Ed)%~{%2c(mqh~#i_ed=`h!MJlnG(UW zv3Ym1uC(lC-@~J$ZYfq){9I{@+M?<)jH~PFvieMU0{8S&^(Y%j3V2WTft{#{3JO_$ zHW|In%PpwTXgBRR+hDToB(V_@V|af`Oq;$xe;dD-`p6eLL{1cihJ9*MV(sr131gNv zd%Ucz2wQE!w^EJoJ=5dFT=F1EC@|1&c^G|gLXbcAca*9R*@PR!)R#uY0@WK!n%R+gZ&`-AoEOnBlR(Fnc;QD{7K&WxnNUMvBdFA z616zt-@8zHp!QL7;is6D^||?S&@%he*#_%XYekZX(dIbL6kPtSXmZM!6XXm|K zRdL(f^Kr1w|M9Q#kKn2~K&dp_-sgy7@AKR6SQ&F53s4+<{cA<6&@uWkioiI6ti|98 zeQbHHty;%K_j8Euxc0IGNBHSn&(&*Uw#S@rGJaPxg9+ECNRZqa7FdE4O|_$;_*C-Q zYac)OA2%-DYg%hd>wVfwbFlT7y&wa;Z_e&n-MatdroC;z7KA`yRf0w~;{vPV#7|lR318jR&v|@1zoOTVepKHsX2W!B%*E8NwgKHk z!RmMC>rN^7H7F?po&nFH7KBqPS|5lpI7Bonp&}LKjHi>dftfZ5|GYzygJnAYC%x!z zzSR`K2iZFy!GHt0L}bZG#=VCjT=*DyiL4~>oCmPd*X+Yn!9&=){vx?t!N{FZA|dSt zBh{$*A1rCE=#f`?z_N+Vj1zwoCl$u2ypjv+tTjsCicteoKeEyND+uP&&;L8{ z%b){m93$Wy4zTiVIYSptzx`+)Y)keW2xc>VyRn=TIh2boaM77%l7`NX7DD3syR;>d zIV_}n_HoRLG@7kKKk})h7q>_&_Zb==lsOg+?K?u<_JlKjEP2SZ7!*Jqh;??%z=wA87qe{<}gC1vOdLUj-~N`eiTo(a^{qE z;N%QI+XGPLxgTBd?XLFT%BpV5GJ)@R3P5%y@Wclhq;W1g2g*RgaC5~%b=i-NmtA#q zyPdZ61ujYib*$k$$P*iwu$tiVkleggbIM}-RLII|M*k9E9=p(BX%)ZhuPOC!cOz`3 z0J7~F?OSXdaoS^S3+z&9K7G;oNYKgMok6Ig%Ri2)jpm^i&aG^%mW>3@_(0BAV{@hc z3=cTy;NE=Kl=?s&-!8Y1sbt{O`Wndku6{dXf8^*e>z@s;gXC*^##DiwoXtL0ax;7^ zd?jY1a&9TmiHq9pg3AUlBs^jqabf188G!K&Hm1@zP-%HzMusj#5vOx(HP|k&0+A7v z>4SblBFJSfaWd^LumS(;dw$}mKYIp1KQ>N5cc<>`mf@KXq4vrr?h^g5X_9uVHrej{ zx}VQr?mpo4$QSkv8x?rw`>$DWi#9A1cX{Ds%4dA%A>FZU)3(#0pU?&*3jPB;dCw`o zr+VFTfb{1#NX4=n&kBsh!IB>Z)VQq1=Vt%j>9yP4RwGoyUu=u4FyNAFc1FoX9N-D< zq?%4w1eNcKOSorb-)q}_x;II6wr=`FX--ZuTR)0A3?HGhfT%kI@pzmetrozcIfy47 zpWHkxOa|e>xjQ_!`E-eYq{*26oI>w?O?zoCOxWnuj%g_d(179C8SNJ>;)n=S-#?nH zy})#xDp!6}*bF;WS@0I|fYmXpG$axkj->FRuMl}K^kdP%LP?`88%(%CE4MDVc@fmq zo1R$DOJ!{M>UQeF%PU?ihrHAFC+Dm31iy$DBL|8%%`Gl3qq=iEoh|&A*YA-xCTzsn zl9ux+%9g(dg+)nc$bt7q<8w2BD@-;0P&JLzYe7GgGnI%(r43#*ucr0?XipJ)P3wGhSEOA~D*f)iQnSW#A@I7#F4wx+U!*e6(3Xva^z9uYqe(z!=)Q89W_!m^z;?>EXJ%fm>P}DulNOVL0~ZS4cyS4%FqPQ zrEek2g16*#gETON|DjEoY~LWkd7tuttU6eztMdO2dHml6blh*0ZCqe|JLcEKe={fm NX>kRyT2X_b{{a_G0iyr_ literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..edc9595ce72ba0d46fb31f96dd25f3edb90e4112 GIT binary patch literal 14550 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 H4TJv&Jtlf! literal 0 HcmV?d00001 diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/Contents.json b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..90eea7ec7e2 --- /dev/null +++ b/tooling/cli/templates/mobile/ios/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "AppIcon-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "AppIcon-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "AppIcon-29x29@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "AppIcon-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "AppIcon-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "AppIcon-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppIcon-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppIcon-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "AppIcon-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "AppIcon-20x20@2x-1.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "AppIcon-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "AppIcon-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "AppIcon-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "AppIcon-40x40@2x-1.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppIcon-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppIcon-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "AppIcon-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "AppIcon-512@2x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/tooling/cli/templates/mobile/ios/Assets.xcassets/Contents.json b/tooling/cli/templates/mobile/ios/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/tooling/cli/templates/mobile/ios/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/tooling/cli/templates/mobile/ios/project.yml b/tooling/cli/templates/mobile/ios/project.yml index 7f190c73cef..31fe4bf979b 100644 --- a/tooling/cli/templates/mobile/ios/project.yml +++ b/tooling/cli/templates/mobile/ios/project.yml @@ -30,6 +30,7 @@ targets: platform: iOS sources: - path: Sources + - path: Assets.xcassets - path: {{app.asset-dir}} buildPhase: resources type: folder From 87e47ce1dcd794517d1fb4b3f15bdce3595cf22c Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 28 Nov 2022 08:27:02 -0300 Subject: [PATCH 078/423] feat(core): proxy request headers, simplify response headers (#5704) --- core/tauri/src/manager.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 071fdb6ac0e..4ad7c548910 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -911,15 +911,13 @@ impl WindowManager { let mut response = { let mut url = url.clone(); url.set_path(&path); - match attohttpc::get(url.as_str()) - .danger_accept_invalid_certs(true) - .send() - { + let mut proxy_builder = attohttpc::get(url.as_str()).danger_accept_invalid_certs(true); + for (name, value) in request.headers() { + proxy_builder = proxy_builder.header(name, value); + } + match proxy_builder.send() { Ok(r) => { for (name, value) in r.headers() { - if name == "Content-Type" { - builder = builder.mimetype(value.to_str().unwrap()); - } builder = builder.header(name, value); } builder.status(r.status()).body(r.bytes()?)? From d38204907e23d8a9254e7361b68a8c88085ed3af Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 28 Nov 2022 08:30:48 -0300 Subject: [PATCH 079/423] feat(cli): run local dev server on public IP on mobile development (#5705) --- tooling/cli/Cargo.lock | 86 ++++++++++++++++++++++- tooling/cli/Cargo.toml | 1 + tooling/cli/src/dev.rs | 21 ++++-- tooling/cli/src/helpers/web_dev_server.rs | 6 +- tooling/cli/src/mobile/android/dev.rs | 2 +- tooling/cli/src/mobile/ios/dev.rs | 2 +- 6 files changed, 103 insertions(+), 15 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 175bff3ce4d..e96d13a4fe0 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -2153,6 +2153,18 @@ dependencies = [ "safemem", ] +[[package]] +name = "local-ip-address" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eab7f092fb08006040e656c2adce6670e6a516bea831a8b2b3d0fb24d4488f5" +dependencies = [ + "libc", + "neli", + "thiserror", + "windows-sys 0.42.0", +] + [[package]] name = "lock_api" version = "0.4.7" @@ -2282,7 +2294,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -2367,6 +2379,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "neli" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9053554eb5dcb7e10d9cdab1206965bde870eed5d0d341532ca035e3ba221508" +dependencies = [ + "byteorder", + "libc", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -2685,7 +2707,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -3319,7 +3341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -3891,6 +3913,7 @@ dependencies = [ "jsonschema", "kuchiki", "libc", + "local-ip-address", "log", "minisign", "notify", @@ -4714,12 +4737,33 @@ dependencies = [ "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + [[package]] name = "windows-tokens" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -4732,6 +4776,12 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -4744,6 +4794,12 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -4756,6 +4812,12 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -4768,6 +4830,18 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -4780,6 +4854,12 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + [[package]] name = "winreg" version = "0.10.1" diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 47e67c0f41a..8dc472482c3 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -85,6 +85,7 @@ html5ever = "0.25" kuchiki = "0.8" tokio = { version = "1", features = [ "macros", "sync" ] } common-path = "1" +local-ip-address = "0.4" [target."cfg(windows)".dependencies] winapi = { version = "0.3", features = [ "handleapi", "processenv", "winbase", "wincon", "winnt" ] } diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index af04cdda5e5..00647b92929 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -20,6 +20,7 @@ use shared_child::SharedChild; use std::{ env::set_current_dir, + net::{Ipv4Addr, SocketAddr}, process::{exit, Command, ExitStatus, Stdio}, sync::{ atomic::{AtomicBool, Ordering}, @@ -74,7 +75,7 @@ pub fn command(options: Options) -> Result<()> { } fn command_internal(mut options: Options) -> Result<()> { - let mut interface = setup(&mut options)?; + let mut interface = setup(&mut options, false)?; let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; interface.dev(options.into(), move |status, reason| { @@ -82,7 +83,7 @@ fn command_internal(mut options: Options) -> Result<()> { }) } -pub fn setup(options: &mut Options) -> Result { +pub fn setup(options: &mut Options, mobile: bool) -> Result { let tauri_path = tauri_dir(); options.config = if let Some(config) = &options.config { Some(if config.starts_with('{') { @@ -228,11 +229,19 @@ pub fn setup(options: &mut Options) -> Result { .dev_path .clone(); if let AppUrl::Url(WindowUrl::App(path)) = &dev_path { - use crate::helpers::web_dev_server::{start_dev_server, SERVER_URL}; + use crate::helpers::web_dev_server::start_dev_server; if path.exists() { + let ip = if mobile { + local_ip_address::local_ip().expect("failed to resolve local IP address") + } else { + Ipv4Addr::new(127, 0, 0, 1).into() + }; + let port = 1430; + let server_address = SocketAddr::new(ip, port); let path = path.canonicalize()?; - start_dev_server(path); - dev_path = AppUrl::Url(WindowUrl::External(SERVER_URL.parse().unwrap())); + start_dev_server(server_address, path); + let server_url = format!("http://{}", server_address); + dev_path = AppUrl::Url(WindowUrl::External(server_url.parse().unwrap())); // TODO: in v2, use an env var to pass the url to the app context // or better separate the config passed from the cli internally and @@ -245,7 +254,7 @@ pub fn setup(options: &mut Options) -> Result { } else { options.config = Some(format!( r#"{{ "build": {{ "devPath": "{}" }} }}"#, - SERVER_URL + server_url )) } } diff --git a/tooling/cli/src/helpers/web_dev_server.rs b/tooling/cli/src/helpers/web_dev_server.rs index 41061c1c857..3b01a524cb8 100644 --- a/tooling/cli/src/helpers/web_dev_server.rs +++ b/tooling/cli/src/helpers/web_dev_server.rs @@ -12,7 +12,6 @@ use notify_debouncer_mini::new_debouncer; use std::{ net::SocketAddr, path::{Path, PathBuf}, - str::FromStr, sync::{mpsc::sync_channel, Arc}, thread, time::Duration, @@ -21,14 +20,13 @@ use tauri_utils::mime_type::MimeType; use tokio::sync::broadcast::{channel, Sender}; const AUTO_RELOAD_SCRIPT: &str = include_str!("./auto-reload.js"); -pub const SERVER_URL: &str = "http://127.0.0.1:1430"; struct State { serve_dir: PathBuf, tx: Sender<()>, } -pub fn start_dev_server>(path: P) { +pub fn start_dev_server>(address: SocketAddr, path: P) { let serve_dir = path.as_ref().to_path_buf(); std::thread::spawn(move || { @@ -80,7 +78,7 @@ pub fn start_dev_server>(path: P) { ws.on_upgrade(|socket| async move { ws_handler(socket, state).await }) }), ); - Server::bind(&SocketAddr::from_str(SERVER_URL.split('/').nth(2).unwrap()).unwrap()) + Server::bind(&address) .serve(router.into_make_service()) .await .unwrap(); diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index e963b68ebd7..292c4ec0901 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -92,7 +92,7 @@ fn run_dev( noise_level: NoiseLevel, ) -> Result<()> { let mut dev_options = options.clone().into(); - let mut interface = crate::dev::setup(&mut dev_options)?; + let mut interface = crate::dev::setup(&mut dev_options, true)?; let app_settings = interface.app_settings(); let bin_path = app_settings.app_binary_path(&InterfaceOptions { diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index a80bf1e37ad..13d58cd8b4a 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -107,7 +107,7 @@ fn run_dev( noise_level: NoiseLevel, ) -> Result<()> { let mut dev_options = options.clone().into(); - let mut interface = crate::dev::setup(&mut dev_options)?; + let mut interface = crate::dev::setup(&mut dev_options, true)?; let app_settings = interface.app_settings(); let bin_path = app_settings.app_binary_path(&InterfaceOptions { From 4a72d5e0ca9f0137d12b674e29c52fcf84fd6d2b Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 28 Nov 2022 08:53:26 -0300 Subject: [PATCH 080/423] feat(cli): replace $HOST variable on beforeDevCommand - public IP usage (#5709) --- tooling/cli/src/dev.rs | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index 00647b92929..1824592cea4 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -104,6 +104,15 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { options.target.clone(), )?; + let mut dev_path = config + .lock() + .unwrap() + .as_ref() + .unwrap() + .build + .dev_path + .clone(); + if let Some(before_dev) = config .lock() .unwrap() @@ -121,7 +130,27 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { } }; let cwd = script_cwd.unwrap_or_else(|| app_dir().clone()); - if let Some(before_dev) = script { + if let Some(mut before_dev) = script { + if before_dev.contains("$HOST") { + if mobile { + let local_ip_address = local_ip_address::local_ip() + .expect("failed to resolve local IP address") + .to_string(); + before_dev = before_dev.replace("$HOST", &local_ip_address); + if let AppUrl::Url(WindowUrl::External(url)) = &mut dev_path { + url.set_host(Some(&local_ip_address))?; + } + } else { + before_dev = before_dev.replace( + "$HOST", + if let AppUrl::Url(WindowUrl::External(url)) = &dev_path { + url.host_str().unwrap_or("0.0.0.0") + } else { + "0.0.0.0" + }, + ); + } + } info!(action = "Running"; "BeforeDevCommand (`{}`)", before_dev); let mut env = command_env(true); env.extend(interface.env()); @@ -220,14 +249,6 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { cargo_features.extend(features.clone()); } - let mut dev_path = config - .lock() - .unwrap() - .as_ref() - .unwrap() - .build - .dev_path - .clone(); if let AppUrl::Url(WindowUrl::App(path)) = &dev_path { use crate::helpers::web_dev_server::start_dev_server; if path.exists() { From eca131dfbb0974a17fee450d244660fc26636871 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 30 Nov 2022 09:59:52 -0300 Subject: [PATCH 081/423] feat(cli): show all Android error logs --- tooling/cli/src/mobile/android.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 17756f4a47e..675868b0548 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -95,7 +95,7 @@ pub fn get_config( let raw = RawAndroidConfig { features: android_options.features.clone(), - logcat_filter_specs: vec!["RustStdoutStderr".into()], + logcat_filter_specs: vec!["RustStdoutStderr".into(), "*:E".into()], ..Default::default() }; let config = AndroidConfig::from_raw(app.clone(), Some(raw)).unwrap(); From e938c3dff5ab222285f33584cd12e3decc9f4e0f Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 30 Nov 2022 10:00:44 -0300 Subject: [PATCH 082/423] feat(core): map 304 status code to 200 on dev proxy Fixes an Android issue when reloading Vite pages --- core/tauri/src/manager.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 4ad7c548910..2fb4b3b18f9 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -909,6 +909,7 @@ impl WindowManager { #[cfg(dev)] let mut response = { + use attohttpc::StatusCode; let mut url = url.clone(); url.set_path(&path); let mut proxy_builder = attohttpc::get(url.as_str()).danger_accept_invalid_certs(true); @@ -920,7 +921,11 @@ impl WindowManager { for (name, value) in r.headers() { builder = builder.header(name, value); } - builder.status(r.status()).body(r.bytes()?)? + let mut status = r.status(); + if status == StatusCode::NOT_MODIFIED { + status = StatusCode::OK; + } + builder.status(status).body(r.bytes()?)? } Err(e) => { debug_eprintln!("Failed to request {}: {}", url.as_str(), e); From fa6d10e39c263a669852992e71917dae3b0a6315 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Wed, 30 Nov 2022 10:30:25 -0300 Subject: [PATCH 083/423] refactor(core): remove window APIs on mobile (#5713) --- core/tauri-runtime-wry/src/lib.rs | 49 +++++++++++++++++-------------- core/tauri/src/window.rs | 26 +++++++++------- examples/api/src-tauri/src/lib.rs | 15 ++++++---- 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 3e584294891..d377dfb8680 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -696,17 +696,7 @@ impl WindowBuilder for WindowBuilderWrapper { } fn with_config(config: WindowConfig) -> Self { - let mut window = WindowBuilderWrapper::new() - .title(config.title.to_string()) - .inner_size(config.width, config.height) - .visible(config.visible) - .resizable(config.resizable) - .fullscreen(config.fullscreen) - .decorations(config.decorations) - .maximized(config.maximized) - .always_on_top(config.always_on_top) - .skip_taskbar(config.skip_taskbar) - .theme(config.theme); + let mut window = WindowBuilderWrapper::new(); #[cfg(target_os = "macos")] { @@ -734,18 +724,33 @@ impl WindowBuilder for WindowBuilderWrapper { "); } - if let (Some(min_width), Some(min_height)) = (config.min_width, config.min_height) { - window = window.min_inner_size(min_width, min_height); - } - if let (Some(max_width), Some(max_height)) = (config.max_width, config.max_height) { - window = window.max_inner_size(max_width, max_height); - } - if let (Some(x), Some(y)) = (config.x, config.y) { - window = window.position(x, y); - } + #[cfg(desktop)] + { + window = window + .title(config.title.to_string()) + .inner_size(config.width, config.height) + .visible(config.visible) + .resizable(config.resizable) + .fullscreen(config.fullscreen) + .decorations(config.decorations) + .maximized(config.maximized) + .always_on_top(config.always_on_top) + .skip_taskbar(config.skip_taskbar) + .theme(config.theme); + + if let (Some(min_width), Some(min_height)) = (config.min_width, config.min_height) { + window = window.min_inner_size(min_width, min_height); + } + if let (Some(max_width), Some(max_height)) = (config.max_width, config.max_height) { + window = window.max_inner_size(max_width, max_height); + } + if let (Some(x), Some(y)) = (config.x, config.y) { + window = window.position(x, y); + } - if config.center { - window = window.center(); + if config.center { + window = window.center(); + } } window diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index 69058354770..3aa11cd16d0 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -262,9 +262,11 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { Ok(window) } +} - // --------------------------------------------- Window builder --------------------------------------------- - +/// Desktop APIs. +#[cfg(desktop)] +impl<'a, R: Runtime> WindowBuilder<'a, R> { /// Sets the menu for the window. #[must_use] pub fn menu(mut self, menu: Menu) -> Self { @@ -475,8 +477,16 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self } - // ------------------------------------------- Webview attributes ------------------------------------------- + /// Sets whether clicking an inactive window also clicks through to the webview. + #[must_use] + pub fn accept_first_mouse(mut self, accept: bool) -> Self { + self.webview_attributes.accept_first_mouse = accept; + self + } +} +/// Webview attributes. +impl<'a, R: Runtime> WindowBuilder<'a, R> { /// Adds the provided JavaScript to a list of scripts that should be run after the global object has been created, /// but before the HTML document has been parsed and before any other script included by the HTML document is run. /// @@ -548,13 +558,6 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self.webview_attributes.clipboard = true; self } - - /// Sets whether clicking an inactive window also clicks through to the webview. - #[must_use] - pub fn accept_first_mouse(mut self, accept: bool) -> Self { - self.webview_attributes.accept_first_mouse = accept; - self - } } // TODO: expand these docs since this is a pretty important type @@ -1016,7 +1019,8 @@ impl Window { } } -/// Window setters and actions. +/// Desktop window setters and actions. +#[cfg(desktop)] impl Window { /// Centers the window. pub fn center(&self) -> crate::Result<()> { diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index b6706674b1d..ca001726919 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -65,12 +65,15 @@ impl AppBuilder { (setup)(app)?; } - #[allow(unused_mut)] - let mut window_builder = WindowBuilder::new(app, "main", WindowUrl::default()) - .user_agent("Tauri API") - .title("Tauri API Validation") - .inner_size(1000., 800.) - .min_inner_size(600., 400.); + let mut window_builder = WindowBuilder::new(app, "main", WindowUrl::default()); + #[cfg(desktop)] + { + window_builder = window_builder + .user_agent("Tauri API") + .title("Tauri API Validation") + .inner_size(1000., 800.) + .min_inner_size(600., 400.); + } #[cfg(target_os = "windows")] { From debe73ab0ef03a75b7ed749e47b38bc6c7bf98b8 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 30 Nov 2022 11:45:22 -0300 Subject: [PATCH 084/423] fix(core): remove window setters command handlers on mobile --- core/tauri/src/endpoints/window.rs | 63 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/core/tauri/src/endpoints/window.rs b/core/tauri/src/endpoints/window.rs index 58ea4c747df..a72bbff6aa3 100644 --- a/core/tauri/src/endpoints/window.rs +++ b/core/tauri/src/endpoints/window.rs @@ -262,73 +262,73 @@ impl Cmd { WindowManagerCmd::AvailableMonitors => return Ok(window.available_monitors()?.into()), WindowManagerCmd::Theme => return Ok(window.theme()?.into()), // Setters - #[cfg(window_center)] + #[cfg(all(desktop, window_center))] WindowManagerCmd::Center => window.center()?, - #[cfg(window_request_user_attention)] + #[cfg(all(desktop, window_request_user_attention))] WindowManagerCmd::RequestUserAttention(request_type) => { window.request_user_attention(request_type)? } - #[cfg(window_set_resizable)] + #[cfg(all(desktop, window_set_resizable))] WindowManagerCmd::SetResizable(resizable) => window.set_resizable(resizable)?, - #[cfg(window_set_title)] + #[cfg(all(desktop, window_set_title))] WindowManagerCmd::SetTitle(title) => window.set_title(&title)?, - #[cfg(window_maximize)] + #[cfg(all(desktop, window_maximize))] WindowManagerCmd::Maximize => window.maximize()?, - #[cfg(window_unmaximize)] + #[cfg(all(desktop, window_unmaximize))] WindowManagerCmd::Unmaximize => window.unmaximize()?, - #[cfg(all(window_maximize, window_unmaximize))] + #[cfg(all(desktop, window_maximize, window_unmaximize))] WindowManagerCmd::ToggleMaximize => match window.is_maximized()? { true => window.unmaximize()?, false => window.maximize()?, }, - #[cfg(window_minimize)] + #[cfg(all(desktop, window_minimize))] WindowManagerCmd::Minimize => window.minimize()?, - #[cfg(window_unminimize)] + #[cfg(all(desktop, window_unminimize))] WindowManagerCmd::Unminimize => window.unminimize()?, - #[cfg(window_show)] + #[cfg(all(desktop, window_show))] WindowManagerCmd::Show => window.show()?, - #[cfg(window_hide)] + #[cfg(all(desktop, window_hide))] WindowManagerCmd::Hide => window.hide()?, - #[cfg(window_close)] + #[cfg(all(desktop, window_close))] WindowManagerCmd::Close => window.close()?, - #[cfg(window_set_decorations)] + #[cfg(all(desktop, window_set_decorations))] WindowManagerCmd::SetDecorations(decorations) => window.set_decorations(decorations)?, - #[cfg(window_set_always_on_top)] + #[cfg(all(desktop, window_set_always_on_top))] WindowManagerCmd::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top)?, - #[cfg(window_set_size)] + #[cfg(all(desktop, window_set_size))] WindowManagerCmd::SetSize(size) => window.set_size(size)?, - #[cfg(window_set_min_size)] + #[cfg(all(desktop, window_set_min_size))] WindowManagerCmd::SetMinSize(size) => window.set_min_size(size)?, - #[cfg(window_set_max_size)] + #[cfg(all(desktop, window_set_max_size))] WindowManagerCmd::SetMaxSize(size) => window.set_max_size(size)?, - #[cfg(window_set_position)] + #[cfg(all(desktop, window_set_position))] WindowManagerCmd::SetPosition(position) => window.set_position(position)?, - #[cfg(window_set_fullscreen)] + #[cfg(all(desktop, window_set_fullscreen))] WindowManagerCmd::SetFullscreen(fullscreen) => window.set_fullscreen(fullscreen)?, - #[cfg(window_set_focus)] + #[cfg(all(desktop, window_set_focus))] WindowManagerCmd::SetFocus => window.set_focus()?, - #[cfg(window_set_icon)] + #[cfg(all(desktop, window_set_icon))] WindowManagerCmd::SetIcon { icon } => window.set_icon(icon.into())?, - #[cfg(window_set_skip_taskbar)] + #[cfg(all(desktop, window_set_skip_taskbar))] WindowManagerCmd::SetSkipTaskbar(skip) => window.set_skip_taskbar(skip)?, - #[cfg(window_set_cursor_grab)] + #[cfg(all(desktop, window_set_cursor_grab))] WindowManagerCmd::SetCursorGrab(grab) => window.set_cursor_grab(grab)?, - #[cfg(window_set_cursor_visible)] + #[cfg(all(desktop, window_set_cursor_visible))] WindowManagerCmd::SetCursorVisible(visible) => window.set_cursor_visible(visible)?, - #[cfg(window_set_cursor_icon)] + #[cfg(all(desktop, window_set_cursor_icon))] WindowManagerCmd::SetCursorIcon(icon) => window.set_cursor_icon(icon)?, - #[cfg(window_set_cursor_position)] + #[cfg(all(desktop, window_set_cursor_position))] WindowManagerCmd::SetCursorPosition(position) => window.set_cursor_position(position)?, - #[cfg(window_set_ignore_cursor_events)] + #[cfg(all(desktop, window_set_ignore_cursor_events))] WindowManagerCmd::SetIgnoreCursorEvents(ignore_cursor) => { window.set_ignore_cursor_events(ignore_cursor)? } - #[cfg(window_start_dragging)] + #[cfg(all(desktop, window_start_dragging))] WindowManagerCmd::StartDragging => window.start_dragging()?, - #[cfg(window_print)] + #[cfg(all(desktop, window_print))] WindowManagerCmd::Print => window.print()?, // internals - #[cfg(all(window_maximize, window_unmaximize))] + #[cfg(all(desktop, window_maximize, window_unmaximize))] WindowManagerCmd::InternalToggleMaximize => { if window.is_resizable()? { match window.is_maximized()? { @@ -337,7 +337,7 @@ impl Cmd { } } } - #[cfg(any(debug_assertions, feature = "devtools"))] + #[cfg(all(desktop, any(debug_assertions, feature = "devtools")))] WindowManagerCmd::InternalToggleDevtools => { if window.is_devtools_open() { window.close_devtools(); @@ -345,6 +345,7 @@ impl Cmd { window.open_devtools(); } } + _ => (), } #[allow(unreachable_code)] Ok(().into()) From ad65b950703ca9385e26854a84e30af88c9f1df0 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 30 Nov 2022 11:52:59 -0300 Subject: [PATCH 085/423] refactor(core): move local ip address resolve to CLI --- core/tauri-codegen/Cargo.toml | 3 --- core/tauri-codegen/src/context.rs | 19 +-------------- core/tauri/src/endpoints/window.rs | 1 + examples/api/src-tauri/Cargo.lock | 23 ------------------- tooling/cli/src/dev.rs | 37 ++++++++++++++++++++++++++---- 5 files changed, 34 insertions(+), 49 deletions(-) diff --git a/core/tauri-codegen/Cargo.toml b/core/tauri-codegen/Cargo.toml index 5bc747a09d1..2a341b238c8 100644 --- a/core/tauri-codegen/Cargo.toml +++ b/core/tauri-codegen/Cargo.toml @@ -31,9 +31,6 @@ png = "0.17" json-patch = "0.2" url = "2" -[target."cfg(any(windows, target_os = \"linux\", target_os = \"macos\"))".dependencies] -local-ip-address = "0.4" - [target."cfg(target_os = \"macos\")".dependencies] plist = "1" time = { version = "0.3", features = [ "parsing", "formatting" ] } diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index e75a2c97630..eb4036f3842 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -124,7 +124,7 @@ enum Target { pub fn context_codegen(data: ContextData) -> Result { let ContextData { dev, - mut config, + config, config_parent, root, } = data; @@ -158,23 +158,6 @@ pub fn context_codegen(data: ContextData) -> Result d == "localhost", - Some(url::Host::Ipv4(i)) => { - i == std::net::Ipv4Addr::LOCALHOST || i == std::net::Ipv4Addr::UNSPECIFIED - } - _ => false, - }; - if localhost { - let ip = local_ip_address::local_ip().expect("failed to resolve local IP address"); - url.set_host(Some(&ip.to_string())).unwrap(); - } - } - } - let mut options = AssetOptions::new(config.tauri.pattern.clone()) .freeze_prototype(config.tauri.security.freeze_prototype) .dangerous_disable_asset_csp_modification( diff --git a/core/tauri/src/endpoints/window.rs b/core/tauri/src/endpoints/window.rs index a72bbff6aa3..f4a545cdae2 100644 --- a/core/tauri/src/endpoints/window.rs +++ b/core/tauri/src/endpoints/window.rs @@ -345,6 +345,7 @@ impl Cmd { window.open_devtools(); } } + #[allow(unreachable_patterns)] _ => (), } #[allow(unreachable_code)] diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 1e59701c28c..daed0f08e96 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -1585,18 +1585,6 @@ dependencies = [ "safemem", ] -[[package]] -name = "local-ip-address" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d6f43d42d775afa8073bfa6aba569b340a3635efa8c9f7702c9c6ed209692f" -dependencies = [ - "libc", - "neli", - "thiserror", - "windows-sys 0.36.1", -] - [[package]] name = "lock_api" version = "0.4.9" @@ -1796,16 +1784,6 @@ dependencies = [ "jni-sys", ] -[[package]] -name = "neli" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9053554eb5dcb7e10d9cdab1206965bde870eed5d0d341532ca035e3ba221508" -dependencies = [ - "byteorder", - "libc", -] - [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -3093,7 +3071,6 @@ dependencies = [ "brotli", "ico", "json-patch", - "local-ip-address", "plist", "png 0.17.7", "proc-macro2", diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index 1824592cea4..45e553b5d6d 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -20,7 +20,7 @@ use shared_child::SharedChild; use std::{ env::set_current_dir, - net::{Ipv4Addr, SocketAddr}, + net::{IpAddr, Ipv4Addr, SocketAddr}, process::{exit, Command, ExitStatus, Stdio}, sync::{ atomic::{AtomicBool, Ordering}, @@ -83,6 +83,11 @@ fn command_internal(mut options: Options) -> Result<()> { }) } +fn local_ip_address() -> &'static IpAddr { + static LOCAL_IP: OnceCell = OnceCell::new(); + LOCAL_IP.get_or_init(|| local_ip_address::local_ip().expect("failed to resolve local IP address")) +} + pub fn setup(options: &mut Options, mobile: bool) -> Result { let tauri_path = tauri_dir(); options.config = if let Some(config) = &options.config { @@ -113,6 +118,30 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { .dev_path .clone(); + if mobile { + if let AppUrl::Url(WindowUrl::External(url)) = &mut dev_path { + let localhost = match url.host() { + Some(url::Host::Domain(d)) => d == "localhost", + Some(url::Host::Ipv4(i)) => { + i == std::net::Ipv4Addr::LOCALHOST || i == std::net::Ipv4Addr::UNSPECIFIED + } + _ => false, + }; + if localhost { + let ip = local_ip_address(); + url.set_host(Some(&ip.to_string())).unwrap(); + if let Some(c) = &options.config { + let mut c: tauri_utils::config::Config = serde_json::from_str(c)?; + c.build.dev_path = dev_path.clone(); + options.config = Some(serde_json::to_string(&c).unwrap()); + } else { + options.config = Some(format!(r#"{{ "build": {{ "devPath": "{}" }} }}"#, url)) + } + reload_config(options.config.as_deref())?; + } + } + } + if let Some(before_dev) = config .lock() .unwrap() @@ -133,9 +162,7 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { if let Some(mut before_dev) = script { if before_dev.contains("$HOST") { if mobile { - let local_ip_address = local_ip_address::local_ip() - .expect("failed to resolve local IP address") - .to_string(); + let local_ip_address = local_ip_address().to_string(); before_dev = before_dev.replace("$HOST", &local_ip_address); if let AppUrl::Url(WindowUrl::External(url)) = &mut dev_path { url.set_host(Some(&local_ip_address))?; @@ -253,7 +280,7 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { use crate::helpers::web_dev_server::start_dev_server; if path.exists() { let ip = if mobile { - local_ip_address::local_ip().expect("failed to resolve local IP address") + *local_ip_address() } else { Ipv4Addr::new(127, 0, 0, 1).into() }; From 5024f7905cba0e45dafd92844a79f7a5c7cd24d6 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 30 Nov 2022 12:48:24 -0300 Subject: [PATCH 086/423] chore(cli): update cargo-mobile including logcat improvement --- tooling/cli/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index e96d13a4fe0..ec8063affac 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -378,7 +378,7 @@ dependencies = [ [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#51d0e9a6c9b37e3287ebd5210d28798c534cb971" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#f117968d675537bc753b53d608a3743815de9689" dependencies = [ "cocoa", "colored 1.9.3", From 626faf9ec17509bbfb1afd5b39556c733c1ca299 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Sat, 3 Dec 2022 10:20:04 -0300 Subject: [PATCH 087/423] chore(examples): move API dev server config from CLI to vite.config.js --- examples/api/src-tauri/tauri.conf.json | 2 +- examples/api/vite.config.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/api/src-tauri/tauri.conf.json b/examples/api/src-tauri/tauri.conf.json index e16677630c3..192781efb8d 100644 --- a/examples/api/src-tauri/tauri.conf.json +++ b/examples/api/src-tauri/tauri.conf.json @@ -3,7 +3,7 @@ "build": { "distDir": "../dist", "devPath": "http://localhost:5173", - "beforeDevCommand": "yarn dev --host", + "beforeDevCommand": "yarn dev", "beforeBuildCommand": "yarn build" }, "package": { diff --git a/examples/api/vite.config.js b/examples/api/vite.config.js index 6ccdda26aa3..26249285ea4 100644 --- a/examples/api/vite.config.js +++ b/examples/api/vite.config.js @@ -18,6 +18,7 @@ export default defineConfig(async ({ command, mode }) => { } }, server: { + host: '0.0.0.0', port: 5173, strictPort: true, hmr: { From c1cd0f5253b4ebed4eb258cb70906f8bfd1c4c97 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 6 Dec 2022 09:19:04 -0300 Subject: [PATCH 088/423] chore(deps): rename cargo-mobile to tauri-mobile --- tooling/cli/Cargo.lock | 82 +++++++++---------- tooling/cli/Cargo.toml | 4 +- tooling/cli/src/info.rs | 2 +- tooling/cli/src/mobile/android.rs | 18 ++-- .../mobile/android/android_studio_script.rs | 2 +- tooling/cli/src/mobile/android/build.rs | 2 +- tooling/cli/src/mobile/android/dev.rs | 2 +- tooling/cli/src/mobile/android/open.rs | 2 +- tooling/cli/src/mobile/android/project.rs | 6 +- tooling/cli/src/mobile/init.rs | 4 +- tooling/cli/src/mobile/ios.rs | 6 +- tooling/cli/src/mobile/ios/build.rs | 2 +- tooling/cli/src/mobile/ios/dev.rs | 4 +- tooling/cli/src/mobile/ios/open.rs | 2 +- tooling/cli/src/mobile/ios/project.rs | 16 ++-- tooling/cli/src/mobile/ios/xcode_script.rs | 2 +- tooling/cli/src/mobile/mod.rs | 16 ++-- 17 files changed, 86 insertions(+), 86 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index ec8063affac..e1b316b8ef2 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -375,46 +375,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cargo-mobile" -version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#f117968d675537bc753b53d608a3743815de9689" -dependencies = [ - "cocoa", - "colored 1.9.3", - "core-foundation 0.7.0", - "deunicode", - "dunce", - "embed-resource", - "english-numbers", - "env_logger 0.7.1", - "freedesktop_entry_parser", - "handlebars 3.5.5", - "heck 0.4.0", - "home", - "ignore", - "indexmap", - "java-properties", - "lexical-core", - "libc", - "log", - "objc", - "objc_id", - "once-cell-regex", - "openssl", - "os_pipe", - "path_abs", - "serde", - "serde_json", - "structopt", - "textwrap", - "thiserror", - "toml", - "ureq", - "winapi", - "windows", -] - [[package]] name = "cc" version = "1.0.73" @@ -3895,7 +3855,6 @@ dependencies = [ "anyhow", "axum", "base64", - "cargo-mobile", "clap 4.0.9", "colored 2.0.0", "common-path", @@ -3929,6 +3888,7 @@ dependencies = [ "sublime_fuzzy", "tauri-bundler", "tauri-icns", + "tauri-mobile", "tauri-utils", "textwrap", "thiserror", @@ -3963,6 +3923,46 @@ dependencies = [ "png", ] +[[package]] +name = "tauri-mobile" +version = "0.1.0" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#5318e9bbe5c42c155ce8a0810d79e6e7c699a6bb" +dependencies = [ + "cocoa", + "colored 1.9.3", + "core-foundation 0.7.0", + "deunicode", + "dunce", + "embed-resource", + "english-numbers", + "env_logger 0.7.1", + "freedesktop_entry_parser", + "handlebars 3.5.5", + "heck 0.4.0", + "home", + "ignore", + "indexmap", + "java-properties", + "lexical-core", + "libc", + "log", + "objc", + "objc_id", + "once-cell-regex", + "openssl", + "os_pipe", + "path_abs", + "serde", + "serde_json", + "structopt", + "textwrap", + "thiserror", + "toml", + "ureq", + "winapi", + "windows", +] + [[package]] name = "tauri-utils" version = "1.2.0" diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 8dc472482c3..09ccb234ccb 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -39,8 +39,8 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -# cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } -cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } +# tauri-mobile = { path = "../../../cargo-mobile/", default-features = false } +tauri-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } textwrap = { version = "0.11.0", features = ["term_size"] } jsonrpsee = { version = "0.16", features = [ "client", "server" ]} thiserror = "1" diff --git a/tooling/cli/src/info.rs b/tooling/cli/src/info.rs index 4db28bf6ed5..f63b620d800 100644 --- a/tooling/cli/src/info.rs +++ b/tooling/cli/src/info.rs @@ -889,7 +889,7 @@ pub fn command(_options: Options) -> Result<()> { if tauri_dir.is_some() { let p = tauri_dir.as_ref().unwrap(); if p.join("gen/apple").exists() { - let teams = cargo_mobile::apple::teams::find_development_teams().unwrap_or_default(); + let teams = tauri_mobile::apple::teams::find_development_teams().unwrap_or_default(); Section("iOS").display(); InfoBlock::new( "Teams", diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 675868b0548..a89ce7ba0bb 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -2,7 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use cargo_mobile::{ +use clap::{Parser, Subcommand}; +use std::{ + env::set_var, + thread::{sleep, spawn}, + time::Duration, +}; +use sublime_fuzzy::best_match; +use tauri_mobile::{ android::{ adb, config::{Config as AndroidConfig, Metadata as AndroidMetadata, Raw as RawAndroidConfig}, @@ -16,13 +23,6 @@ use cargo_mobile::{ os, util::prompt, }; -use clap::{Parser, Subcommand}; -use std::{ - env::set_var, - thread::{sleep, spawn}, - time::Duration, -}; -use sublime_fuzzy::best_match; use super::{ ensure_init, get_app, @@ -142,7 +142,7 @@ pub fn with_config( fn env() -> Result { let env = super::env()?; - cargo_mobile::android::env::Env::from_env(env).map_err(Into::into) + tauri_mobile::android::env::Env::from_env(env).map_err(Into::into) } fn delete_codegen_vars() { diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index 57677a0c955..b5996383ec5 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -2,7 +2,7 @@ use super::{detect_target_ok, ensure_init, env, with_config, MobileTarget}; use crate::Result; use clap::{ArgAction, Parser}; -use cargo_mobile::{ +use tauri_mobile::{ android::target::Target, opts::Profile, target::{call_for_targets_with_fallback, TargetTrait}, diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index 02b6268079b..fdb99bc9b7d 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -10,7 +10,7 @@ use crate::{ }; use clap::{ArgAction, Parser}; -use cargo_mobile::{ +use tauri_mobile::{ android::{aab, apk, config::Config as AndroidConfig, env::Env, target::Target}, opts::{NoiseLevel, Profile}, target::TargetTrait, diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 292c4ec0901..981bbd43e6b 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -10,7 +10,7 @@ use crate::{ }; use clap::{ArgAction, Parser}; -use cargo_mobile::{ +use tauri_mobile::{ android::{ config::{Config as AndroidConfig, Metadata as AndroidMetadata}, env::Env, diff --git a/tooling/cli/src/mobile/android/open.rs b/tooling/cli/src/mobile/android/open.rs index b67c2aaec3a..33d78ced776 100644 --- a/tooling/cli/src/mobile/android/open.rs +++ b/tooling/cli/src/mobile/android/open.rs @@ -1,6 +1,6 @@ use super::{ensure_init, env, with_config, MobileTarget}; use crate::Result; -use cargo_mobile::os; +use tauri_mobile::os; pub fn command() -> Result<()> { with_config( diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs index 2ede33a21e9..abfe5ad0e1c 100644 --- a/tooling/cli/src/mobile/android/project.rs +++ b/tooling/cli/src/mobile/android/project.rs @@ -4,7 +4,9 @@ use crate::{helpers::template, Result}; use anyhow::Context; -use cargo_mobile::{ +use handlebars::Handlebars; +use include_dir::{include_dir, Dir}; +use tauri_mobile::{ android::{ config::{Config, Metadata}, target::Target, @@ -18,8 +20,6 @@ use cargo_mobile::{ prefix_path, }, }; -use handlebars::Handlebars; -use include_dir::{include_dir, Dir}; use std::{ffi::OsStr, fs, path::Path}; diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 991e85d1243..4a07876c36d 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -5,7 +5,8 @@ use super::{get_app, Target}; use crate::helpers::{config::get as get_tauri_config, template::JsonMap}; use crate::Result; -use cargo_mobile::{ +use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError}; +use tauri_mobile::{ android::{ config::Config as AndroidConfig, env::Env as AndroidEnv, target::Target as AndroidTarget, }, @@ -17,7 +18,6 @@ use cargo_mobile::{ cli::{Report, TextWrapper}, }, }; -use handlebars::{Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError}; use std::{env::current_dir, path::PathBuf}; diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 2edda014b37..33ea44d91d6 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use cargo_mobile::{ +use clap::{Parser, Subcommand}; +use sublime_fuzzy::best_match; +use tauri_mobile::{ apple::{ config::{ Config as AppleConfig, Metadata as AppleMetadata, Platform as ApplePlatform, @@ -19,8 +21,6 @@ use cargo_mobile::{ os, util::prompt, }; -use clap::{Parser, Subcommand}; -use sublime_fuzzy::best_match; use super::{ ensure_init, env, get_app, diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index 679b0c06305..5efe53f6564 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -10,7 +10,7 @@ use crate::{ }; use clap::{ArgAction, Parser}; -use cargo_mobile::{ +use tauri_mobile::{ apple::{config::Config as AppleConfig, target::Target}, env::Env, opts::{NoiseLevel, Profile}, diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 13d58cd8b4a..b4bff556b63 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -10,13 +10,13 @@ use crate::{ }; use clap::{ArgAction, Parser}; -use cargo_mobile::{ +use dialoguer::{theme::ColorfulTheme, Select}; +use tauri_mobile::{ apple::{config::Config as AppleConfig, teams::find_development_teams}, config::app::App, env::Env, opts::{NoiseLevel, Profile}, }; -use dialoguer::{theme::ColorfulTheme, Select}; use std::env::{set_var, var_os}; diff --git a/tooling/cli/src/mobile/ios/open.rs b/tooling/cli/src/mobile/ios/open.rs index 9d3f52b4232..bff1f7ad9a1 100644 --- a/tooling/cli/src/mobile/ios/open.rs +++ b/tooling/cli/src/mobile/ios/open.rs @@ -1,6 +1,6 @@ use super::{ensure_init, env, with_config, MobileTarget}; use crate::Result; -use cargo_mobile::os; +use tauri_mobile::os; pub fn command() -> Result<()> { with_config( diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index 051af0eeedd..4067be50417 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -4,7 +4,14 @@ use crate::{helpers::template, Result}; use anyhow::Context; -use cargo_mobile::{ +use handlebars::Handlebars; +use include_dir::{include_dir, Dir}; +use std::{ + ffi::{OsStr, OsString}, + fs::{create_dir_all, OpenOptions}, + path::{Component, PathBuf}, +}; +use tauri_mobile::{ apple::{ config::{Config, Metadata}, deps, rust_version_check, @@ -15,13 +22,6 @@ use cargo_mobile::{ target::TargetTrait as _, util::{self, cli::TextWrapper}, }; -use handlebars::Handlebars; -use include_dir::{include_dir, Dir}; -use std::{ - ffi::{OsStr, OsString}, - fs::{create_dir_all, OpenOptions}, - path::{Component, PathBuf}, -}; const TEMPLATE_DIR: Dir<'_> = include_dir!("templates/mobile/ios"); diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 59e509a2b7a..7d2746f8cdb 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -2,7 +2,7 @@ use super::{env, with_config}; use crate::Result; use clap::Parser; -use cargo_mobile::{apple::target::Target, opts::Profile, util}; +use tauri_mobile::{apple::target::Target, opts::Profile, util}; use std::{collections::HashMap, ffi::OsStr, path::PathBuf}; diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 3a7ab5c247b..21425ea351e 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -7,12 +7,6 @@ use crate::{ interface::DevProcess, }; use anyhow::{bail, Result}; -use cargo_mobile::{ - bossy, - config::app::{App, Raw as RawAppConfig}, - env::Error as EnvError, - opts::NoiseLevel, -}; use jsonrpsee::client_transport::ws::WsTransportClientBuilder; use jsonrpsee::core::client::{Client, ClientBuilder, ClientT}; use jsonrpsee::rpc_params; @@ -33,12 +27,18 @@ use std::{ Arc, }, }; +use tauri_mobile::{ + bossy, + config::app::{App, Raw as RawAppConfig}, + env::Error as EnvError, + opts::NoiseLevel, +}; use tokio::runtime::Runtime; #[cfg(not(windows))] -use cargo_mobile::env::Env; +use tauri_mobile::env::Env; #[cfg(windows)] -use cargo_mobile::os::Env; +use tauri_mobile::os::Env; pub mod android; mod init; From b874d139f964f23ab79dfb16143ed37b16acaf11 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 6 Dec 2022 09:50:37 -0300 Subject: [PATCH 089/423] fix(cli): emulator.start called twice --- tooling/cli/src/mobile/android.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index a89ce7ba0bb..304abcf52dc 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -230,11 +230,6 @@ fn emulator_prompt(env: &'_ Env, target: Option<&str>) -> Result Date: Tue, 6 Dec 2022 09:58:59 -0300 Subject: [PATCH 090/423] fix(cli): update cargo-mobile fixing adb pidof check --- tooling/cli/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index e1b316b8ef2..27e262fd271 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3926,7 +3926,7 @@ dependencies = [ [[package]] name = "tauri-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#5318e9bbe5c42c155ce8a0810d79e6e7c699a6bb" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#0b10eec445391f459af694ae6646f9dda6c60fc1" dependencies = [ "cocoa", "colored 1.9.3", From 02706c92f63616128df65bdc5f7fcfd104f95e96 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 6 Dec 2022 14:04:30 -0300 Subject: [PATCH 091/423] chore(cli): force iOS cargo build stdout --- tooling/cli/src/mobile/ios/dev.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index b4bff556b63..dbb0a904b2d 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -145,7 +145,7 @@ fn run_dev( if open { open_and_wait(config, &env) } else { - match run(device.as_deref(), options, config, &env, noise_level) { + match run(device.as_deref(), options, config, &env) { Ok(c) => { crate::dev::wait_dev_process(c.clone(), move |status, reason| { crate::dev::on_app_exit(status, reason, exit_on_panic, no_watch) @@ -178,7 +178,6 @@ fn run( options: MobileOptions, config: &AppleConfig, env: &Env, - noise_level: NoiseLevel, ) -> Result { let profile = if options.debug { Profile::Debug @@ -190,7 +189,13 @@ fn run( device_prompt(env, device) .map_err(|e| RunError::FailedToPromptForDevice(e.to_string()))? - .run(config, env, noise_level, non_interactive, profile) + .run( + config, + env, + NoiseLevel::FranklyQuitePedantic, + non_interactive, + profile, + ) .map(DevChild::new) .map_err(|e| RunError::RunFailed(e.to_string())) } From aa18c7d671557386c67a39119f341b4e054c7d60 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 7 Dec 2022 15:53:41 -0300 Subject: [PATCH 092/423] chore(cli): pin tauri-mobile to v0.1.0 --- tooling/cli/Cargo.lock | 39 +++++---------------------------------- tooling/cli/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 27e262fd271..2a181283e31 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -987,19 +987,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4f5d6e192964d498b45abee72ca445e91909094bc8e8791259e82c2a0d1aa6" -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime 1.3.0", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.9.1" @@ -1007,7 +994,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" dependencies = [ "atty", - "humantime 2.1.0", + "humantime", "log", "regex", "termcolor", @@ -1436,7 +1423,7 @@ dependencies = [ "log", "pest", "pest_derive", - "quick-error 2.0.1", + "quick-error", "serde", "serde_json", ] @@ -1563,15 +1550,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error 1.2.3", -] - [[package]] name = "humantime" version = "2.1.0" @@ -2979,12 +2957,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quick-error" version = "2.0.1" @@ -3860,7 +3832,7 @@ dependencies = [ "common-path", "ctrlc", "dialoguer", - "env_logger 0.9.1", + "env_logger", "handlebars 4.3.1", "heck 0.4.0", "html5ever", @@ -3926,7 +3898,8 @@ dependencies = [ [[package]] name = "tauri-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#0b10eec445391f459af694ae6646f9dda6c60fc1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b23191ab3de2efc8d266ec26c16c1c90bd9e171f6f8ccdc56cd31cc9ad97130" dependencies = [ "cocoa", "colored 1.9.3", @@ -3935,7 +3908,6 @@ dependencies = [ "dunce", "embed-resource", "english-numbers", - "env_logger 0.7.1", "freedesktop_entry_parser", "handlebars 3.5.5", "heck 0.4.0", @@ -3954,7 +3926,6 @@ dependencies = [ "path_abs", "serde", "serde_json", - "structopt", "textwrap", "thiserror", "toml", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 09ccb234ccb..a0d8487ac89 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -40,7 +40,7 @@ path = "src/main.rs" [dependencies] # tauri-mobile = { path = "../../../cargo-mobile/", default-features = false } -tauri-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } +tauri-mobile = { version = "0.1", default-features = false } textwrap = { version = "0.11.0", features = ["term_size"] } jsonrpsee = { version = "0.16", features = [ "client", "server" ]} thiserror = "1" From fa3a10988a03aed1b66fb17d893b1a9adb90f7cd Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 8 Dec 2022 11:28:12 -0800 Subject: [PATCH 093/423] feat(ci): prepare 2.0.0-alpha.0 (#5786) --- .changes/config.json | 2 +- .changes/mobile.md | 13 ++ .changes/pre.json | 4 + .../covector-version-or-publish-next.yml | 137 ++++++++++++++++++ .scripts/covector/sync-cli-metadata.js | 9 ++ core/config-schema/Cargo.toml | 2 +- 6 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 .changes/mobile.md create mode 100644 .changes/pre.json create mode 100644 .github/workflows/covector-version-or-publish-next.yml diff --git a/.changes/config.json b/.changes/config.json index 8bc799a56b2..5df56a75d06 100644 --- a/.changes/config.json +++ b/.changes/config.json @@ -103,7 +103,7 @@ "pipe": true }, { - "command": "yarn publish --access public --loglevel silly", + "command": "yarn publish --access public --loglevel silly --tag next", "dryRunCommand": "npm publish --dry-run --access public", "pipe": true }, diff --git a/.changes/mobile.md b/.changes/mobile.md new file mode 100644 index 00000000000..caf32195e9c --- /dev/null +++ b/.changes/mobile.md @@ -0,0 +1,13 @@ +--- +"api": major +"tauri-utils": major +"tauri-bundler": major +"tauri-codegen": major +"tauri-macros": major +"tauri-build": major +"tauri": major +"cli.rs": major +"cli.js": major +--- + +First mobile alpha release! diff --git a/.changes/pre.json b/.changes/pre.json new file mode 100644 index 00000000000..5914ed91ace --- /dev/null +++ b/.changes/pre.json @@ -0,0 +1,4 @@ +{ + "tag": "alpha", + "changes": [] +} diff --git a/.github/workflows/covector-version-or-publish-next.yml b/.github/workflows/covector-version-or-publish-next.yml new file mode 100644 index 00000000000..c958ea36854 --- /dev/null +++ b/.github/workflows/covector-version-or-publish-next.yml @@ -0,0 +1,137 @@ +# Copyright 2019-2022 Tauri Programme within The Commons Conservancy +# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: MIT + +name: version or publish + +on: + push: + branches: + - next + +jobs: + run-integration-tests: + runs-on: ${{ matrix.platform }} + + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: install stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: install Linux dependencies + if: matrix.platform == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev libfuse2 + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + core -> ../target + tooling/cli + + - name: build CLI + uses: actions-rs/cargo@v1 + with: + command: build + args: --manifest-path ./tooling/cli/Cargo.toml + + - name: run integration tests + run: cargo test --test '*' -- --ignored + + - name: run CLI tests + timeout-minutes: 30 + run: | + cd ./tooling/cli/node + yarn + yarn build + yarn test + + version-or-publish: + runs-on: ubuntu-latest + timeout-minutes: 65 + outputs: + change: ${{ steps.covector.outputs.change }} + commandRan: ${{ steps.covector.outputs.commandRan }} + successfulPublish: ${{ steps.covector.outputs.successfulPublish }} + needs: + - run-integration-tests + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-node@v2 + with: + node-version: 14 + registry-url: 'https://registry.npmjs.org' + cache: yarn + cache-dependency-path: tooling/*/yarn.lock + + - name: cargo login + run: cargo login ${{ secrets.ORG_CRATES_IO_TOKEN }} + - name: git config + run: | + git config --global user.name "${{ github.event.pusher.name }}" + git config --global user.email "${{ github.event.pusher.email }}" + + - name: covector version or publish (publish when no change files present) + uses: jbolda/covector/packages/action@covector-v0 + id: covector + env: + NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }} + CARGO_AUDIT_OPTIONS: ${{ secrets.CARGO_AUDIT_OPTIONS }} + with: + token: ${{ secrets.GITHUB_TOKEN }} + command: 'version-or-publish' + createRelease: true + + - name: Create Pull Request With Versions Bumped + if: steps.covector.outputs.commandRan == 'version' + uses: tauri-apps/create-pull-request@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: release/version-updates-next + title: (NEXT) Apply Version Updates From Current Changes + commit-message: 'apply version updates' + labels: 'version updates' + body: ${{ steps.covector.outputs.change }} + + - name: Trigger doc update + if: | + steps.covector.outputs.successfulPublish == 'true' && + steps.covector.outputs.packagesPublished != '' + uses: peter-evans/repository-dispatch@v1 + with: + token: ${{ secrets.ORG_TAURI_BOT_PAT }} + repository: tauri-apps/tauri-docs + event-type: update-docs + + - name: Trigger cli.js publishing workflow + if: | + steps.covector.outputs.successfulPublish == 'true' && + contains(steps.covector.outputs.packagesPublished, 'cli.rs') + uses: peter-evans/repository-dispatch@v1 + with: + token: ${{ secrets.ORG_TAURI_BOT_PAT }} + repository: tauri-apps/tauri + event-type: publish-clijs + inputs: '{"releaseId": "${{ steps.covector.outputs.cli.js-releaseId }}" }' + + - name: Trigger cli.rs publishing workflow + if: | + steps.covector.outputs.successfulPublish == 'true' && + contains(steps.covector.outputs.packagesPublished, 'cli.rs') + uses: peter-evans/repository-dispatch@v1 + with: + token: ${{ secrets.ORG_TAURI_BOT_PAT }} + repository: tauri-apps/tauri + event-type: publish-clirs diff --git a/.scripts/covector/sync-cli-metadata.js b/.scripts/covector/sync-cli-metadata.js index f63cfe56ec2..de8ce26b4ac 100644 --- a/.scripts/covector/sync-cli-metadata.js +++ b/.scripts/covector/sync-cli-metadata.js @@ -11,6 +11,7 @@ rust binaries. */ const { readFileSync, writeFileSync } = require('fs') +const { resolve } = require('path') const packageNickname = process.argv[2] const filePath = @@ -22,6 +23,7 @@ let index = null switch (bump) { case 'major': + case 'premajor': index = 0 break case 'minor': @@ -30,6 +32,9 @@ switch (bump) { case 'patch': index = 2 break + case 'prerelease': + index = 3 + break default: throw new Error('unexpected bump ' + bump) } @@ -43,6 +48,10 @@ const inc = (version) => { v[i] = 0 } } + if (bump === 'premajor') { + const pre = JSON.parse(readFileSync(resolve(filePath, '../../../.changes/pre.json'), 'utf-8')) + return `${v.join('.')}-${pre.tag}.0` + } return v.join('.') } diff --git a/core/config-schema/Cargo.toml b/core/config-schema/Cargo.toml index a304bfa3822..9e5798e3039 100644 --- a/core/config-schema/Cargo.toml +++ b/core/config-schema/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" publish = false [build-dependencies] -tauri-utils = { version = "1.0.0", features = [ "schema" ], path = "../tauri-utils" } +tauri-utils = { path = "../tauri-utils", features = [ "schema" ] } schemars = { version = "0.8", features = [ "url", "preserve_order" ] } serde = { version = "1.0", features = [ "derive" ] } serde_json = "1.0" From 35040076eaa576bdcc973df18b6a7144d2816c48 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Fri, 9 Dec 2022 10:11:41 -0300 Subject: [PATCH 094/423] feat(ci): publish cli.js with --tag next --- .github/workflows/publish-cli-js.yml | 2 +- tooling/api/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-cli-js.yml b/.github/workflows/publish-cli-js.yml index 0a86624862b..f30248b1c58 100644 --- a/.github/workflows/publish-cli-js.yml +++ b/.github/workflows/publish-cli-js.yml @@ -393,7 +393,7 @@ jobs: - name: Publish run: | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc - npm publish + npm publish --tag next env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.ORG_NPM_TOKEN }} diff --git a/tooling/api/package.json b/tooling/api/package.json index bdca9a0bf7d..c7feaa9348b 100644 --- a/tooling/api/package.json +++ b/tooling/api/package.json @@ -13,7 +13,7 @@ "scripts": { "build": "yarn tsup && node ./scripts/after-build.cjs", "npm-pack": "yarn build && cd ./dist && npm pack", - "npm-publish": "yarn build && cd ./dist && yarn publish --access public --loglevel silly", + "npm-publish": "yarn build && cd ./dist && yarn publish --access public --loglevel silly --tag next", "lint": "eslint --ext ts \"./src/**/*.ts\"", "lint-fix": "eslint --fix --ext ts \"./src/**/*.ts\"", "format": "prettier --write --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path ../../.prettierignore", From 25416a64ba2d7a15d78b1d387c3b18fc00170ee2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Dec 2022 14:27:41 -0300 Subject: [PATCH 095/423] (NEXT) Apply Version Updates From Current Changes (#5787) Co-authored-by: lucasfernog Co-authored-by: Lucas Nogueira --- .changes/pre.json | 18 +++++++++++++++++- core/tauri-build/CHANGELOG.md | 7 +++++++ core/tauri-build/Cargo.toml | 6 +++--- core/tauri-codegen/CHANGELOG.md | 7 +++++++ core/tauri-codegen/Cargo.toml | 4 ++-- core/tauri-macros/CHANGELOG.md | 7 +++++++ core/tauri-macros/Cargo.toml | 6 +++--- core/tauri-runtime-wry/CHANGELOG.md | 5 +++++ core/tauri-runtime-wry/Cargo.toml | 6 +++--- core/tauri-runtime/CHANGELOG.md | 9 +++++++++ core/tauri-runtime/Cargo.toml | 4 ++-- core/tauri-utils/CHANGELOG.md | 7 +++++++ core/tauri-utils/Cargo.toml | 2 +- core/tauri/CHANGELOG.md | 15 +++++++++++++++ core/tauri/Cargo.toml | 22 +++++++++++----------- tooling/api/CHANGELOG.md | 5 +++++ tooling/api/package.json | 2 +- tooling/bundler/CHANGELOG.md | 5 +++++ tooling/bundler/Cargo.toml | 4 ++-- tooling/cli/CHANGELOG.md | 15 +++++++++++++++ tooling/cli/Cargo.lock | 6 +++--- tooling/cli/Cargo.toml | 11 +++++------ tooling/cli/metadata.json | 6 +++--- tooling/cli/node/CHANGELOG.md | 15 +++++++++++++++ tooling/cli/node/package.json | 2 +- 25 files changed, 154 insertions(+), 42 deletions(-) diff --git a/.changes/pre.json b/.changes/pre.json index 5914ed91ace..3ccd3733290 100644 --- a/.changes/pre.json +++ b/.changes/pre.json @@ -1,4 +1,20 @@ { "tag": "alpha", - "changes": [] + "changes": [ + ".changes/build-android-env-vars.md", + ".changes/cli-android-build.md", + ".changes/cli-ios-build.md", + ".changes/cli-mobile-dev.md", + ".changes/codegen-mobile-devurl.md", + ".changes/default-tls-features.md", + ".changes/dev-proxy.md", + ".changes/mobile-config.md", + ".changes/mobile-entry-point-macro.md", + ".changes/mobile-init.md", + ".changes/mobile-open.md", + ".changes/mobile-webview-access.md", + ".changes/mobile.md", + ".changes/refactor-setup.md", + ".changes/tauri-mobile-entry-point.md" + ] } diff --git a/core/tauri-build/CHANGELOG.md b/core/tauri-build/CHANGELOG.md index ff6e0289074..7a77a98ef0e 100644 --- a/core/tauri-build/CHANGELOG.md +++ b/core/tauri-build/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.0-alpha.0] + +- Set environment variables used by `tauri::mobile_entry_point`. + - [98904863](https://www.github.com/tauri-apps/tauri/commit/9890486321c9c79ccfb7c547fafee85b5c3ffa71) feat(core): add `mobile_entry_point` macro ([#4983](https://www.github.com/tauri-apps/tauri/pull/4983)) on 2022-08-21 +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + ## \[1.2.1] - Fix `allowlist > app > show/hide` always disabled when `allowlist > app > all: false`. diff --git a/core/tauri-build/Cargo.toml b/core/tauri-build/Cargo.toml index 05387dab684..1840ac2654a 100644 --- a/core/tauri-build/Cargo.toml +++ b/core/tauri-build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-build" -version = "1.2.1" +version = "2.0.0-alpha.0" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -19,8 +19,8 @@ rustdoc-args = [ "--cfg", "doc_cfg" ] [dependencies] anyhow = "1" quote = { version = "1", optional = true } -tauri-codegen = { version = "1.2.1", path = "../tauri-codegen", optional = true } -tauri-utils = { version = "1.2.1", path = "../tauri-utils", features = [ "build", "resources" ] } +tauri-codegen = { version = "2.0.0-alpha.0", path = "../tauri-codegen", optional = true } +tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils", features = [ "build", "resources" ] } cargo_toml = "0.13" serde_json = "1" heck = "0.4" diff --git a/core/tauri-codegen/CHANGELOG.md b/core/tauri-codegen/CHANGELOG.md index 69f338e4600..e32ce10b394 100644 --- a/core/tauri-codegen/CHANGELOG.md +++ b/core/tauri-codegen/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.0-alpha.0] + +- Change `devPath` URL to use the local IP address on iOS and Android. + - [6f061504](https://www.github.com/tauri-apps/tauri/commit/6f0615044d09ec58393a7ebca5e45bb175e20db3) feat(cli): add `android dev` and `ios dev` commands ([#4982](https://www.github.com/tauri-apps/tauri/pull/4982)) on 2022-08-20 +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + ## \[1.2.1] - Fix `allowlist > app > show/hide` always disabled when `allowlist > app > all: false`. diff --git a/core/tauri-codegen/Cargo.toml b/core/tauri-codegen/Cargo.toml index d9df517d01b..6c434d130a0 100644 --- a/core/tauri-codegen/Cargo.toml +++ b/core/tauri-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-codegen" -version = "1.2.1" +version = "2.0.0-alpha.0" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -19,7 +19,7 @@ proc-macro2 = "1" quote = "1" serde = { version = "1", features = [ "derive" ] } serde_json = "1" -tauri-utils = { version = "1.2.1", path = "../tauri-utils", features = [ "build" ] } +tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils", features = [ "build" ] } thiserror = "1" walkdir = "2" brotli = { version = "3", optional = true, default-features = false, features = [ "std" ] } diff --git a/core/tauri-macros/CHANGELOG.md b/core/tauri-macros/CHANGELOG.md index bfca33345ef..32d3e7e7067 100644 --- a/core/tauri-macros/CHANGELOG.md +++ b/core/tauri-macros/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.0-alpha.0] + +- Added the `mobile_entry_point` macro. + - [98904863](https://www.github.com/tauri-apps/tauri/commit/9890486321c9c79ccfb7c547fafee85b5c3ffa71) feat(core): add `mobile_entry_point` macro ([#4983](https://www.github.com/tauri-apps/tauri/pull/4983)) on 2022-08-21 +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + ## \[1.2.1] - Fix `allowlist > app > show/hide` always disabled when `allowlist > app > all: false`. diff --git a/core/tauri-macros/Cargo.toml b/core/tauri-macros/Cargo.toml index 5e18ca0eaf6..e79b037ef07 100644 --- a/core/tauri-macros/Cargo.toml +++ b/core/tauri-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-macros" -version = "1.2.1" +version = "2.0.0-alpha.0" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "os", "filesystem", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -20,8 +20,8 @@ proc-macro2 = "1" quote = "1" syn = { version = "1", features = [ "full" ] } heck = "0.4" -tauri-codegen = { version = "1.2.1", default-features = false, path = "../tauri-codegen" } -tauri-utils = { version = "1.2.1", path = "../tauri-utils" } +tauri-codegen = { version = "2.0.0-alpha.0", default-features = false, path = "../tauri-codegen" } +tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } [features] custom-protocol = [ ] diff --git a/core/tauri-runtime-wry/CHANGELOG.md b/core/tauri-runtime-wry/CHANGELOG.md index b67b9b265ec..84d2d789392 100644 --- a/core/tauri-runtime-wry/CHANGELOG.md +++ b/core/tauri-runtime-wry/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## \[0.13.0-alpha.0] + +- Support `with_webview` for Android platform alowing execution of JNI code in context. + - [8ea87e9c](https://www.github.com/tauri-apps/tauri/commit/8ea87e9c9ca8ba4c7017c8281f78aacd08f45785) feat(android): with_webview access for jni execution ([#5148](https://www.github.com/tauri-apps/tauri/pull/5148)) on 2022-09-08 + ## \[0.12.2] - Fix compatibility with older Linux distributions. diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 54c480e5747..485ae76c2bd 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-runtime-wry" -version = "0.12.2" +version = "0.13.0-alpha.0" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -14,8 +14,8 @@ readme = "README.md" [dependencies] wry = { version = "0.23", default-features = false, features = [ "file-drop", "protocol" ] } -tauri-runtime = { version = "0.12.1", path = "../tauri-runtime" } -tauri-utils = { version = "1.2.1", path = "../tauri-utils" } +tauri-runtime = { version = "0.13.0-alpha.0", path = "../tauri-runtime" } +tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } rand = "0.8" raw-window-handle = "0.5" diff --git a/core/tauri-runtime/CHANGELOG.md b/core/tauri-runtime/CHANGELOG.md index 084df72757b..c488a06e150 100644 --- a/core/tauri-runtime/CHANGELOG.md +++ b/core/tauri-runtime/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## \[0.13.0-alpha.0] + +- Parse `android` and `ios` Tauri configuration files. + - Bumped due to a bump in tauri-utils. + - [b3a3afc7](https://www.github.com/tauri-apps/tauri/commit/b3a3afc7de8de4021d73559288f5192732a706cf) feat(core): detect android and ios platform configuration files ([#4997](https://www.github.com/tauri-apps/tauri/pull/4997)) on 2022-08-22 +- First mobile alpha release! + - Bumped due to a bump in tauri-utils. + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + ## \[0.12.1] - Fix `allowlist > app > show/hide` always disabled when `allowlist > app > all: false`. diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml index b8a090f9093..9e670d96477 100644 --- a/core/tauri-runtime/Cargo.toml +++ b/core/tauri-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-runtime" -version = "0.12.1" +version = "0.13.0-alpha.0" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -26,7 +26,7 @@ targets = [ serde = { version = "1.0", features = [ "derive" ] } serde_json = "1.0" thiserror = "1.0" -tauri-utils = { version = "1.2.1", path = "../tauri-utils" } +tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } http = "0.2.4" http-range = "0.1.4" diff --git a/core/tauri-utils/CHANGELOG.md b/core/tauri-utils/CHANGELOG.md index 11b54526680..5e81091a6e4 100644 --- a/core/tauri-utils/CHANGELOG.md +++ b/core/tauri-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.0-alpha.0] + +- Parse `android` and `ios` Tauri configuration files. + - [b3a3afc7](https://www.github.com/tauri-apps/tauri/commit/b3a3afc7de8de4021d73559288f5192732a706cf) feat(core): detect android and ios platform configuration files ([#4997](https://www.github.com/tauri-apps/tauri/pull/4997)) on 2022-08-22 +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + ## \[1.2.1] - Fix `allowlist > app > show/hide` always disabled when `allowlist > app > all: false`. diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml index 94e7d8d510e..785b415defc 100644 --- a/core/tauri-utils/Cargo.toml +++ b/core/tauri-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-utils" -version = "1.2.1" +version = "2.0.0-alpha.0" authors = [ "Tauri Programme within The Commons Conservancy" ] license = "Apache-2.0 OR MIT" homepage = "https://tauri.app" diff --git a/core/tauri/CHANGELOG.md b/core/tauri/CHANGELOG.md index 8b29b7fba7e..731487e9f77 100644 --- a/core/tauri/CHANGELOG.md +++ b/core/tauri/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## \[2.0.0-alpha.0] + +- Added the `default-tls` and `reqwest-default-tls` Cargo features for enabling TLS suppport to connect over HTTPS. + - [f6f9192a](https://www.github.com/tauri-apps/tauri/commit/f6f9192aa51bd842df8aa1d1aa538b12aa6c2d29) fix(core): Android compilation on Windows ([#5658](https://www.github.com/tauri-apps/tauri/pull/5658)) on 2022-11-20 +- **Breaking change:** Use the custom protocol as a proxy to the development server on all platforms except Linux. + - [6f061504](https://www.github.com/tauri-apps/tauri/commit/6f0615044d09ec58393a7ebca5e45bb175e20db3) feat(cli): add `android dev` and `ios dev` commands ([#4982](https://www.github.com/tauri-apps/tauri/pull/4982)) on 2022-08-20 +- Support `with_webview` for Android platform alowing execution of JNI code in context. + - [8ea87e9c](https://www.github.com/tauri-apps/tauri/commit/8ea87e9c9ca8ba4c7017c8281f78aacd08f45785) feat(android): with_webview access for jni execution ([#5148](https://www.github.com/tauri-apps/tauri/pull/5148)) on 2022-09-08 +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 +- **Breaking change:** The window creation and setup hook are now called when the event loop is ready. + - [b4622ea4](https://www.github.com/tauri-apps/tauri/commit/b4622ea4d32720bc3bb2a8c740bb70cfe32fed93) refactor(app): run setup and window creation when event loop is ready ([#4914](https://www.github.com/tauri-apps/tauri/pull/4914)) on 2022-08-11 +- Export types required by the `mobile_entry_point` macro. + - [98904863](https://www.github.com/tauri-apps/tauri/commit/9890486321c9c79ccfb7c547fafee85b5c3ffa71) feat(core): add `mobile_entry_point` macro ([#4983](https://www.github.com/tauri-apps/tauri/pull/4983)) on 2022-08-21 + ## \[1.2.2] - Invoke event listener in windows safely to avoid causing uncaught errors in windows that have loaded external urls diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index eb383b2128a..457f207ea68 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR MIT" name = "tauri" readme = "README.md" repository = "https://github.com/tauri-apps/tauri" -version = "1.2.2" +version = "2.0.0-alpha.0" [package.metadata.docs.rs] no-default-features = true @@ -49,10 +49,10 @@ url = { version = "2.3" } anyhow = "1.0" thiserror = "1.0" once_cell = "1" -tauri-runtime = { version = "0.12.1", path = "../tauri-runtime" } -tauri-macros = { version = "1.2.1", path = "../tauri-macros" } -tauri-utils = { version = "1.2.1", features = [ "resources" ], path = "../tauri-utils" } -tauri-runtime-wry = { version = "0.12.2", path = "../tauri-runtime-wry", optional = true } +tauri-runtime = { version = "0.13.0-alpha.0", path = "../tauri-runtime" } +tauri-macros = { version = "2.0.0-alpha.0", path = "../tauri-macros" } +tauri-utils = { version = "2.0.0-alpha.0", features = [ "resources" ], path = "../tauri-utils" } +tauri-runtime-wry = { version = "0.13.0-alpha.0", path = "../tauri-runtime-wry", optional = true } rand = "0.8" semver = { version = "1.0", features = [ "serde" ] } serde_repr = "0.1" @@ -104,18 +104,18 @@ objc = "0.2" webview2-com = "0.19.1" win7-notifications = { version = "0.3.1", optional = true } -[target.'cfg(target_os = "android")'.dependencies] + [target."cfg(windows)".dependencies.windows] + version = "0.39.0" + features = [ "Win32_Foundation" ] + +[target."cfg(target_os = \"android\")".dependencies] paste = "1.0" android_logger = "0.9" log = "0.4" -[target.'cfg(target_os = "ios")'.dependencies] +[target."cfg(target_os = \"ios\")".dependencies] env_logger = "0.9.0" -[target."cfg(windows)".dependencies.windows] -version = "0.39.0" -features = [ "Win32_Foundation" ] - [build-dependencies] heck = "0.4" once_cell = "1" diff --git a/tooling/api/CHANGELOG.md b/tooling/api/CHANGELOG.md index d2a733f9f40..0bdd28f8c49 100644 --- a/tooling/api/CHANGELOG.md +++ b/tooling/api/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## \[2.0.0-alpha.0] + +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + ## \[1.2.0] - Added the `acceptFirstMouse` window option. diff --git a/tooling/api/package.json b/tooling/api/package.json index c7feaa9348b..18a348bcc99 100644 --- a/tooling/api/package.json +++ b/tooling/api/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/api", - "version": "1.2.0", + "version": "2.0.0-alpha.0", "description": "Tauri API definitions", "type": "module", "funding": { diff --git a/tooling/bundler/CHANGELOG.md b/tooling/bundler/CHANGELOG.md index 133611269c1..1011ffe01e7 100644 --- a/tooling/bundler/CHANGELOG.md +++ b/tooling/bundler/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## \[2.0.0-alpha.0] + +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + ## \[1.1.2] - Fixes blank taskbar icon on WiX updates. diff --git a/tooling/bundler/Cargo.toml b/tooling/bundler/Cargo.toml index e2ae6742266..5b12fc16d69 100644 --- a/tooling/bundler/Cargo.toml +++ b/tooling/bundler/Cargo.toml @@ -2,7 +2,7 @@ workspace = { } [package] name = "tauri-bundler" -version = "1.1.2" +version = "2.0.0-alpha.0" authors = [ "George Burton ", "Tauri Programme within The Commons Conservancy" @@ -17,7 +17,7 @@ rust-version = "1.59" exclude = [ "CHANGELOG.md", "/target", "rustfmt.toml" ] [dependencies] -tauri-utils = { version = "1.2.1", path = "../../core/tauri-utils", features = [ "resources" ] } +tauri-utils = { version = "2.0.0-alpha.0", path = "../../core/tauri-utils", features = [ "resources" ] } image = "0.24.5" libflate = "1.2" anyhow = "1.0" diff --git a/tooling/cli/CHANGELOG.md b/tooling/cli/CHANGELOG.md index 62864b72b99..d27d5860006 100644 --- a/tooling/cli/CHANGELOG.md +++ b/tooling/cli/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## \[2.0.0-alpha.0] + +- Added `android build` command. + - [4c9ea450](https://www.github.com/tauri-apps/tauri/commit/4c9ea450c3b47c6b8c825ba32e9837909945ccd7) feat(cli): add `android build` command ([#4999](https://www.github.com/tauri-apps/tauri/pull/4999)) on 2022-08-22 +- Added `ios build` command. + - [403859d4](https://www.github.com/tauri-apps/tauri/commit/403859d47e1a9bf978b353fa58e4b971e66337a3) feat(cli): add `ios build` command ([#5002](https://www.github.com/tauri-apps/tauri/pull/5002)) on 2022-08-22 +- Added `android dev` and `ios dev` commands. + - [6f061504](https://www.github.com/tauri-apps/tauri/commit/6f0615044d09ec58393a7ebca5e45bb175e20db3) feat(cli): add `android dev` and `ios dev` commands ([#4982](https://www.github.com/tauri-apps/tauri/pull/4982)) on 2022-08-20 +- Added `android init` and `ios init` commands. + - [d44f67f7](https://www.github.com/tauri-apps/tauri/commit/d44f67f7afd30a81d53a973ec603b2a253150bde) feat: add `android init` and `ios init` commands ([#4942](https://www.github.com/tauri-apps/tauri/pull/4942)) on 2022-08-15 +- Added `android open` and `ios open` commands. + - [a9c8e565](https://www.github.com/tauri-apps/tauri/commit/a9c8e565c6495961940877df7090f307be16b554) feat: add `android open` and `ios open` commands ([#4946](https://www.github.com/tauri-apps/tauri/pull/4946)) on 2022-08-15 +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + ## \[1.2.2] - Detect SvelteKit and Vite for the init and info commands. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index b5d16f236fe..9858c5269d8 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3785,7 +3785,7 @@ dependencies = [ [[package]] name = "tauri-bundler" -version = "1.1.2" +version = "2.0.0-alpha.0" dependencies = [ "anyhow", "ar", @@ -3822,7 +3822,7 @@ dependencies = [ [[package]] name = "tauri-cli" -version = "1.2.2" +version = "2.0.0-alpha.0" dependencies = [ "anyhow", "axum", @@ -3936,7 +3936,7 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "1.2.1" +version = "2.0.0-alpha.0" dependencies = [ "aes-gcm", "ctor", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index b5ede8850f1..ebd0ae86d53 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -3,7 +3,7 @@ members = [ "node" ] [package] name = "tauri-cli" -version = "1.2.2" +version = "2.0.0-alpha.0" authors = [ "Tauri Programme within The Commons Conservancy" ] edition = "2021" rust-version = "1.59" @@ -39,15 +39,14 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -# tauri-mobile = { path = "../../../cargo-mobile/", default-features = false } tauri-mobile = { version = "0.1", default-features = false } -textwrap = { version = "0.11.0", features = ["term_size"] } -jsonrpsee = { version = "0.16", features = [ "client", "server" ]} +textwrap = { version = "0.11.0", features = [ "term_size" ] } +jsonrpsee = { version = "0.16", features = [ "client", "server" ] } thiserror = "1" sublime_fuzzy = "0.7" clap = { version = "4.0", features = [ "derive" ] } anyhow = "1.0" -tauri-bundler = { version = "1.1.2", path = "../bundler" } +tauri-bundler = { version = "2.0.0-alpha.0", path = "../bundler" } colored = "2.0" once_cell = "1" serde = { version = "1.0", features = [ "derive" ] } @@ -57,7 +56,7 @@ notify-debouncer-mini = "0.2" shared_child = "1.0" toml_edit = "0.14" json-patch = "0.2" -tauri-utils = { version = "1.2.1", path = "../../core/tauri-utils", features = [ "isolation", "schema", "config-json5", "config-toml" ] } +tauri-utils = { version = "2.0.0-alpha.0", path = "../../core/tauri-utils", features = [ "isolation", "schema", "config-json5", "config-toml" ] } toml = "0.5" jsonschema = "0.16" handlebars = "4.3" diff --git a/tooling/cli/metadata.json b/tooling/cli/metadata.json index 71f5c9bca0b..f8933df5ada 100644 --- a/tooling/cli/metadata.json +++ b/tooling/cli/metadata.json @@ -1,8 +1,8 @@ { "cli.js": { - "version": "1.2.2", + "version": "2.0.0-alpha.0", "node": ">= 10.0.0" }, - "tauri": "1.2.2", - "tauri-build": "1.2.1" + "tauri": "2.0.0-alpha.0", + "tauri-build": "2.0.0-alpha.0" } diff --git a/tooling/cli/node/CHANGELOG.md b/tooling/cli/node/CHANGELOG.md index 93e79adcf61..0729cda1fa3 100644 --- a/tooling/cli/node/CHANGELOG.md +++ b/tooling/cli/node/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## \[2.0.0-alpha.0] + +- Added `android build` command. + - [4c9ea450](https://www.github.com/tauri-apps/tauri/commit/4c9ea450c3b47c6b8c825ba32e9837909945ccd7) feat(cli): add `android build` command ([#4999](https://www.github.com/tauri-apps/tauri/pull/4999)) on 2022-08-22 +- Added `ios build` command. + - [403859d4](https://www.github.com/tauri-apps/tauri/commit/403859d47e1a9bf978b353fa58e4b971e66337a3) feat(cli): add `ios build` command ([#5002](https://www.github.com/tauri-apps/tauri/pull/5002)) on 2022-08-22 +- Added `android dev` and `ios dev` commands. + - [6f061504](https://www.github.com/tauri-apps/tauri/commit/6f0615044d09ec58393a7ebca5e45bb175e20db3) feat(cli): add `android dev` and `ios dev` commands ([#4982](https://www.github.com/tauri-apps/tauri/pull/4982)) on 2022-08-20 +- Added `android init` and `ios init` commands. + - [d44f67f7](https://www.github.com/tauri-apps/tauri/commit/d44f67f7afd30a81d53a973ec603b2a253150bde) feat: add `android init` and `ios init` commands ([#4942](https://www.github.com/tauri-apps/tauri/pull/4942)) on 2022-08-15 +- Added `android open` and `ios open` commands. + - [a9c8e565](https://www.github.com/tauri-apps/tauri/commit/a9c8e565c6495961940877df7090f307be16b554) feat: add `android open` and `ios open` commands ([#4946](https://www.github.com/tauri-apps/tauri/pull/4946)) on 2022-08-15 +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + ## \[1.2.2] - Detect SvelteKit and Vite for the init and info commands. diff --git a/tooling/cli/node/package.json b/tooling/cli/node/package.json index a00832893ff..2ecf80c9caf 100644 --- a/tooling/cli/node/package.json +++ b/tooling/cli/node/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/cli", - "version": "1.2.2", + "version": "2.0.0-alpha.0", "description": "Command line interface for building Tauri apps", "funding": { "type": "opencollective", From 1d20db741010d90eb27b445872f13c9b621a0eed Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Fri, 9 Dec 2022 15:11:15 -0300 Subject: [PATCH 096/423] chore: update bundle.global.js --- core/tauri/scripts/bundle.global.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/tauri/scripts/bundle.global.js b/core/tauri/scripts/bundle.global.js index 9947ec6e4cd..711c6ef9e88 100644 --- a/core/tauri/scripts/bundle.global.js +++ b/core/tauri/scripts/bundle.global.js @@ -1,8 +1,8 @@ -"use strict";var __TAURI_IIFE__=(()=>{var L=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var me=Object.getOwnPropertyNames;var pe=Object.prototype.hasOwnProperty;var c=(n,e)=>{for(var t in e)L(n,t,{get:e[t],enumerable:!0})},ge=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of me(e))!pe.call(n,s)&&s!==t&&L(n,s,{get:()=>e[s],enumerable:!(r=de(e,s))||r.enumerable});return n};var he=n=>ge(L({},"__esModule",{value:!0}),n);var Jt={};c(Jt,{app:()=>U,cli:()=>k,clipboard:()=>I,dialog:()=>N,event:()=>V,fs:()=>j,globalShortcut:()=>q,http:()=>$,invoke:()=>$t,notification:()=>J,os:()=>ne,path:()=>K,process:()=>Q,shell:()=>Z,tauri:()=>R,updater:()=>X,window:()=>te});var U={};c(U,{getName:()=>be,getTauriVersion:()=>Pe,getVersion:()=>_e,hide:()=>ve,show:()=>we});var R={};c(R,{convertFileSrc:()=>fe,invoke:()=>f,transformCallback:()=>d});function ye(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function d(n,e=!1){let t=ye(),r=`_${t}`;return Object.defineProperty(window,r,{value:s=>(e&&Reflect.deleteProperty(window,r),n==null?void 0:n(s)),writable:!1,configurable:!0}),t}async function f(n,e={}){return new Promise((t,r)=>{let s=d(l=>{t(l),Reflect.deleteProperty(window,`_${a}`)},!0),a=d(l=>{r(l),Reflect.deleteProperty(window,`_${s}`)},!0);window.__TAURI_IPC__({cmd:n,callback:s,error:a,...e})})}function fe(n,e="asset"){let t=encodeURIComponent(n);return navigator.userAgent.includes("Windows")?`https://${e}.localhost/${t}`:`${e}://localhost/${t}`}async function i(n){return f("tauri",n)}async function _e(){return i({__tauriModule:"App",message:{cmd:"getAppVersion"}})}async function be(){return i({__tauriModule:"App",message:{cmd:"getAppName"}})}async function Pe(){return i({__tauriModule:"App",message:{cmd:"getTauriVersion"}})}async function we(){return i({__tauriModule:"App",message:{cmd:"show"}})}async function ve(){return i({__tauriModule:"App",message:{cmd:"hide"}})}var k={};c(k,{getMatches:()=>Me});async function Me(){return i({__tauriModule:"Cli",message:{cmd:"cliMatches"}})}var I={};c(I,{readText:()=>Oe,writeText:()=>Te});async function Te(n){return i({__tauriModule:"Clipboard",message:{cmd:"writeText",data:n}})}async function Oe(){return i({__tauriModule:"Clipboard",message:{cmd:"readText",data:null}})}var N={};c(N,{ask:()=>Ce,confirm:()=>De,message:()=>Ae,open:()=>Fe,save:()=>Ee});async function Fe(n={}){return typeof n=="object"&&Object.freeze(n),i({__tauriModule:"Dialog",message:{cmd:"openDialog",options:n}})}async function Ee(n={}){return typeof n=="object"&&Object.freeze(n),i({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:n}})}async function Ae(n,e){var r;let t=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:n.toString(),title:(r=t==null?void 0:t.title)==null?void 0:r.toString(),type:t==null?void 0:t.type}})}async function Ce(n,e){var r;let t=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"askDialog",message:n.toString(),title:(r=t==null?void 0:t.title)==null?void 0:r.toString(),type:t==null?void 0:t.type}})}async function De(n,e){var r;let t=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"confirmDialog",message:n.toString(),title:(r=t==null?void 0:t.title)==null?void 0:r.toString(),type:t==null?void 0:t.type}})}var V={};c(V,{TauriEvent:()=>M,emit:()=>T,listen:()=>z,once:()=>H});async function ie(n,e){return i({__tauriModule:"Event",message:{cmd:"unlisten",event:n,eventId:e}})}async function w(n,e,t){await i({__tauriModule:"Event",message:{cmd:"emit",event:n,windowLabel:e,payload:t}})}async function _(n,e,t){return i({__tauriModule:"Event",message:{cmd:"listen",event:n,windowLabel:e,handler:d(t)}}).then(r=>async()=>ie(n,r))}async function v(n,e,t){return _(n,e,r=>{t(r),ie(n,r.id).catch(()=>{})})}var M=(u=>(u.WINDOW_RESIZED="tauri://resize",u.WINDOW_MOVED="tauri://move",u.WINDOW_CLOSE_REQUESTED="tauri://close-requested",u.WINDOW_CREATED="tauri://window-created",u.WINDOW_DESTROYED="tauri://destroyed",u.WINDOW_FOCUS="tauri://focus",u.WINDOW_BLUR="tauri://blur",u.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",u.WINDOW_THEME_CHANGED="tauri://theme-changed",u.WINDOW_FILE_DROP="tauri://file-drop",u.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",u.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",u.MENU="tauri://menu",u.CHECK_UPDATE="tauri://update",u.UPDATE_AVAILABLE="tauri://update-available",u.INSTALL_UPDATE="tauri://update-install",u.STATUS_UPDATE="tauri://update-status",u.DOWNLOAD_PROGRESS="tauri://update-download-progress",u))(M||{});async function z(n,e){return _(n,null,e)}async function H(n,e){return v(n,null,e)}async function T(n,e){return w(n,void 0,e)}var j={};c(j,{BaseDirectory:()=>O,Dir:()=>O,copyFile:()=>Ne,createDir:()=>ke,exists:()=>Ve,readBinaryFile:()=>xe,readDir:()=>Ue,readTextFile:()=>We,removeDir:()=>Ie,removeFile:()=>ze,renameFile:()=>He,writeBinaryFile:()=>Re,writeFile:()=>Le,writeTextFile:()=>Le});var O=(o=>(o[o.Audio=1]="Audio",o[o.Cache=2]="Cache",o[o.Config=3]="Config",o[o.Data=4]="Data",o[o.LocalData=5]="LocalData",o[o.Desktop=6]="Desktop",o[o.Document=7]="Document",o[o.Download=8]="Download",o[o.Executable=9]="Executable",o[o.Font=10]="Font",o[o.Home=11]="Home",o[o.Picture=12]="Picture",o[o.Public=13]="Public",o[o.Runtime=14]="Runtime",o[o.Template=15]="Template",o[o.Video=16]="Video",o[o.Resource=17]="Resource",o[o.App=18]="App",o[o.Log=19]="Log",o[o.Temp=20]="Temp",o[o.AppConfig=21]="AppConfig",o[o.AppData=22]="AppData",o[o.AppLocalData=23]="AppLocalData",o[o.AppCache=24]="AppCache",o[o.AppLog=25]="AppLog",o))(O||{});async function We(n,e={}){return i({__tauriModule:"Fs",message:{cmd:"readTextFile",path:n,options:e}})}async function xe(n,e={}){let t=await i({__tauriModule:"Fs",message:{cmd:"readFile",path:n,options:e}});return Uint8Array.from(t)}async function Le(n,e,t){typeof t=="object"&&Object.freeze(t),typeof n=="object"&&Object.freeze(n);let r={path:"",contents:""},s=t;return typeof n=="string"?r.path=n:(r.path=n.path,r.contents=n.contents),typeof e=="string"?r.contents=e??"":s=e,i({__tauriModule:"Fs",message:{cmd:"writeFile",path:r.path,contents:Array.from(new TextEncoder().encode(r.contents)),options:s}})}async function Re(n,e,t){typeof t=="object"&&Object.freeze(t),typeof n=="object"&&Object.freeze(n);let r={path:"",contents:[]},s=t;return typeof n=="string"?r.path=n:(r.path=n.path,r.contents=n.contents),e&&"dir"in e?s=e:typeof n=="string"&&(r.contents=e??[]),i({__tauriModule:"Fs",message:{cmd:"writeFile",path:r.path,contents:Array.from(r.contents instanceof ArrayBuffer?new Uint8Array(r.contents):r.contents),options:s}})}async function Ue(n,e={}){return i({__tauriModule:"Fs",message:{cmd:"readDir",path:n,options:e}})}async function ke(n,e={}){return i({__tauriModule:"Fs",message:{cmd:"createDir",path:n,options:e}})}async function Ie(n,e={}){return i({__tauriModule:"Fs",message:{cmd:"removeDir",path:n,options:e}})}async function Ne(n,e,t={}){return i({__tauriModule:"Fs",message:{cmd:"copyFile",source:n,destination:e,options:t}})}async function ze(n,e={}){return i({__tauriModule:"Fs",message:{cmd:"removeFile",path:n,options:e}})}async function He(n,e,t={}){return i({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:n,newPath:e,options:t}})}async function Ve(n,e={}){return i({__tauriModule:"Fs",message:{cmd:"exists",path:n,options:e}})}var q={};c(q,{isRegistered:()=>Ge,register:()=>je,registerAll:()=>qe,unregister:()=>$e,unregisterAll:()=>Je});async function je(n,e){return i({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:n,handler:d(e)}})}async function qe(n,e){return i({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:n,handler:d(e)}})}async function Ge(n){return i({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:n}})}async function $e(n){return i({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:n}})}async function Je(){return i({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}})}var $={};c($,{Body:()=>p,Client:()=>E,Response:()=>F,ResponseType:()=>re,fetch:()=>Ke,getClient:()=>se});var re=(r=>(r[r.JSON=1]="JSON",r[r.Text=2]="Text",r[r.Binary=3]="Binary",r))(re||{}),p=class{constructor(e,t){this.type=e,this.payload=t}static form(e){let t={},r=(s,a)=>{if(a!==null){let l;typeof a=="string"?l=a:a instanceof Uint8Array||Array.isArray(a)?l=Array.from(a):a instanceof File?l={file:a.name,mime:a.type,fileName:a.name}:typeof a.file=="string"?l={file:a.file,mime:a.mime,fileName:a.fileName}:l={file:Array.from(a.file),mime:a.mime,fileName:a.fileName},t[String(s)]=l}};if(e instanceof FormData)for(let[s,a]of e)r(s,a);else for(let[s,a]of Object.entries(e))r(s,a);return new p("Form",t)}static json(e){return new p("Json",e)}static text(e){return new p("Text",e)}static bytes(e){return new p("Bytes",Array.from(e instanceof ArrayBuffer?new Uint8Array(e):e))}},F=class{constructor(e){this.url=e.url,this.status=e.status,this.ok=this.status>=200&&this.status<300,this.headers=e.headers,this.rawHeaders=e.rawHeaders,this.data=e.data}},E=class{constructor(e){this.id=e}async drop(){return i({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}})}async request(e){let t=!e.responseType||e.responseType===1;return t&&(e.responseType=2),i({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:e}}).then(r=>{let s=new F(r);if(t){try{s.data=JSON.parse(s.data)}catch(a){if(s.ok&&s.data==="")s.data={};else if(s.ok)throw Error(`Failed to parse response \`${s.data}\` as JSON: ${a}; - try setting the \`responseType\` option to \`ResponseType.Text\` or \`ResponseType.Binary\` if the API does not return a JSON response.`)}return s}return s})}async get(e,t){return this.request({method:"GET",url:e,...t})}async post(e,t,r){return this.request({method:"POST",url:e,body:t,...r})}async put(e,t,r){return this.request({method:"PUT",url:e,body:t,...r})}async patch(e,t){return this.request({method:"PATCH",url:e,...t})}async delete(e,t){return this.request({method:"DELETE",url:e,...t})}};async function se(n){return i({__tauriModule:"Http",message:{cmd:"createClient",options:n}}).then(e=>new E(e))}var G=null;async function Ke(n,e){return G===null&&(G=await se()),G.request({url:n,method:(e==null?void 0:e.method)??"GET",...e})}var J={};c(J,{isPermissionGranted:()=>Qe,requestPermission:()=>Ze,sendNotification:()=>Ye});async function Qe(){return window.Notification.permission!=="default"?Promise.resolve(window.Notification.permission==="granted"):i({__tauriModule:"Notification",message:{cmd:"isNotificationPermissionGranted"}})}async function Ze(){return window.Notification.requestPermission()}function Ye(n){typeof n=="string"?new window.Notification(n):new window.Notification(n.title,n)}var K={};c(K,{BaseDirectory:()=>O,appCacheDir:()=>tt,appConfigDir:()=>ae,appDataDir:()=>Be,appDir:()=>Xe,appLocalDataDir:()=>et,appLogDir:()=>oe,audioDir:()=>nt,basename:()=>At,cacheDir:()=>it,configDir:()=>rt,dataDir:()=>st,delimiter:()=>vt,desktopDir:()=>at,dirname:()=>Ft,documentDir:()=>ot,downloadDir:()=>lt,executableDir:()=>ut,extname:()=>Et,fontDir:()=>ct,homeDir:()=>dt,isAbsolute:()=>Ct,join:()=>Ot,localDataDir:()=>mt,logDir:()=>Pt,normalize:()=>Tt,pictureDir:()=>pt,publicDir:()=>gt,resolve:()=>Mt,resolveResource:()=>yt,resourceDir:()=>ht,runtimeDir:()=>ft,sep:()=>wt,templateDir:()=>_t,videoDir:()=>bt});function b(){return navigator.appVersion.includes("Win")}async function Xe(){return ae()}async function ae(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:21}})}async function Be(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:22}})}async function et(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:23}})}async function tt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:24}})}async function nt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:1}})}async function it(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:2}})}async function rt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:3}})}async function st(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:4}})}async function at(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:6}})}async function ot(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:7}})}async function lt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:8}})}async function ut(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:9}})}async function ct(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:10}})}async function dt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:11}})}async function mt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:5}})}async function pt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:12}})}async function gt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:13}})}async function ht(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:17}})}async function yt(n){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:n,directory:17}})}async function ft(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:14}})}async function _t(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:15}})}async function bt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:16}})}async function Pt(){return oe()}async function oe(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:25}})}var wt=b()?"\\":"/",vt=b()?";":":";async function Mt(...n){return i({__tauriModule:"Path",message:{cmd:"resolve",paths:n}})}async function Tt(n){return i({__tauriModule:"Path",message:{cmd:"normalize",path:n}})}async function Ot(...n){return i({__tauriModule:"Path",message:{cmd:"join",paths:n}})}async function Ft(n){return i({__tauriModule:"Path",message:{cmd:"dirname",path:n}})}async function Et(n){return i({__tauriModule:"Path",message:{cmd:"extname",path:n}})}async function At(n,e){return i({__tauriModule:"Path",message:{cmd:"basename",path:n,ext:e}})}async function Ct(n){return i({__tauriModule:"Path",message:{cmd:"isAbsolute",path:n}})}var Q={};c(Q,{exit:()=>Dt,relaunch:()=>St});async function Dt(n=0){return i({__tauriModule:"Process",message:{cmd:"exit",exitCode:n}})}async function St(){return i({__tauriModule:"Process",message:{cmd:"relaunch"}})}var Z={};c(Z,{Child:()=>A,Command:()=>P,EventEmitter:()=>g,open:()=>xt});async function Wt(n,e,t=[],r){return typeof t=="object"&&Object.freeze(t),i({__tauriModule:"Shell",message:{cmd:"execute",program:e,args:t,options:r,onEventFn:d(n)}})}var g=class{constructor(){this.eventListeners=Object.create(null)}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}on(e,t){return e in this.eventListeners?this.eventListeners[e].push(t):this.eventListeners[e]=[t],this}once(e,t){let r=(...s)=>{this.removeListener(e,r),t(...s)};return this.addListener(e,r)}off(e,t){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter(r=>r!==t)),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,...t){if(e in this.eventListeners){let r=this.eventListeners[e];for(let s of r)s(...t);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,t){return e in this.eventListeners?this.eventListeners[e].unshift(t):this.eventListeners[e]=[t],this}prependOnceListener(e,t){let r=(...s)=>{this.removeListener(e,r),t(...s)};return this.prependListener(e,r)}},A=class{constructor(e){this.pid=e}async write(e){return i({__tauriModule:"Shell",message:{cmd:"stdinWrite",pid:this.pid,buffer:typeof e=="string"?e:Array.from(e)}})}async kill(){return i({__tauriModule:"Shell",message:{cmd:"killChild",pid:this.pid}})}},P=class extends g{constructor(t,r=[],s){super();this.stdout=new g;this.stderr=new g;this.program=t,this.args=typeof r=="string"?[r]:r,this.options=s??{}}static sidecar(t,r=[],s){let a=new P(t,r,s);return a.options.sidecar=!0,a}async spawn(){return Wt(t=>{switch(t.event){case"Error":this.emit("error",t.payload);break;case"Terminated":this.emit("close",t.payload);break;case"Stdout":this.stdout.emit("data",t.payload);break;case"Stderr":this.stderr.emit("data",t.payload);break}},this.program,this.args,this.options).then(t=>new A(t))}async execute(){return new Promise((t,r)=>{this.on("error",r);let s=[],a=[];this.stdout.on("data",l=>{s.push(l)}),this.stderr.on("data",l=>{a.push(l)}),this.on("close",l=>{t({code:l.code,signal:l.signal,stdout:s.join(` +"use strict";var __TAURI_IIFE__=(()=>{var L=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var me=Object.getOwnPropertyNames;var pe=Object.prototype.hasOwnProperty;var c=(t,e)=>{for(var n in e)L(t,n,{get:e[n],enumerable:!0})},ge=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of me(e))!pe.call(t,s)&&s!==n&&L(t,s,{get:()=>e[s],enumerable:!(r=de(e,s))||r.enumerable});return t};var he=t=>ge(L({},"__esModule",{value:!0}),t);var Jt={};c(Jt,{app:()=>k,cli:()=>U,clipboard:()=>I,dialog:()=>N,event:()=>V,fs:()=>j,globalShortcut:()=>q,http:()=>$,invoke:()=>$t,notification:()=>J,os:()=>ne,path:()=>K,process:()=>Q,shell:()=>Z,tauri:()=>R,updater:()=>X,window:()=>te});var k={};c(k,{getName:()=>be,getTauriVersion:()=>Pe,getVersion:()=>_e,hide:()=>ve,show:()=>we});var R={};c(R,{convertFileSrc:()=>fe,invoke:()=>f,transformCallback:()=>d});function ye(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function d(t,e=!1){let n=ye(),r=`_${n}`;return Object.defineProperty(window,r,{value:s=>(e&&Reflect.deleteProperty(window,r),t?.(s)),writable:!1,configurable:!0}),n}async function f(t,e={}){return new Promise((n,r)=>{let s=d(l=>{n(l),Reflect.deleteProperty(window,`_${a}`)},!0),a=d(l=>{r(l),Reflect.deleteProperty(window,`_${s}`)},!0);window.__TAURI_IPC__({cmd:t,callback:s,error:a,...e})})}function fe(t,e="asset"){let n=encodeURIComponent(t);return navigator.userAgent.includes("Windows")?`https://${e}.localhost/${n}`:`${e}://localhost/${n}`}async function i(t){return f("tauri",t)}async function _e(){return i({__tauriModule:"App",message:{cmd:"getAppVersion"}})}async function be(){return i({__tauriModule:"App",message:{cmd:"getAppName"}})}async function Pe(){return i({__tauriModule:"App",message:{cmd:"getTauriVersion"}})}async function we(){return i({__tauriModule:"App",message:{cmd:"show"}})}async function ve(){return i({__tauriModule:"App",message:{cmd:"hide"}})}var U={};c(U,{getMatches:()=>Me});async function Me(){return i({__tauriModule:"Cli",message:{cmd:"cliMatches"}})}var I={};c(I,{readText:()=>Oe,writeText:()=>Te});async function Te(t){return i({__tauriModule:"Clipboard",message:{cmd:"writeText",data:t}})}async function Oe(){return i({__tauriModule:"Clipboard",message:{cmd:"readText",data:null}})}var N={};c(N,{ask:()=>Ce,confirm:()=>De,message:()=>Ae,open:()=>Fe,save:()=>Ee});async function Fe(t={}){return typeof t=="object"&&Object.freeze(t),i({__tauriModule:"Dialog",message:{cmd:"openDialog",options:t}})}async function Ee(t={}){return typeof t=="object"&&Object.freeze(t),i({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:t}})}async function Ae(t,e){let n=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:t.toString(),title:n?.title?.toString(),type:n?.type}})}async function Ce(t,e){let n=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"askDialog",message:t.toString(),title:n?.title?.toString(),type:n?.type}})}async function De(t,e){let n=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"confirmDialog",message:t.toString(),title:n?.title?.toString(),type:n?.type}})}var V={};c(V,{TauriEvent:()=>M,emit:()=>T,listen:()=>z,once:()=>H});async function ie(t,e){return i({__tauriModule:"Event",message:{cmd:"unlisten",event:t,eventId:e}})}async function w(t,e,n){await i({__tauriModule:"Event",message:{cmd:"emit",event:t,windowLabel:e,payload:n}})}async function _(t,e,n){return i({__tauriModule:"Event",message:{cmd:"listen",event:t,windowLabel:e,handler:d(n)}}).then(r=>async()=>ie(t,r))}async function v(t,e,n){return _(t,e,r=>{n(r),ie(t,r.id).catch(()=>{})})}var M=(u=>(u.WINDOW_RESIZED="tauri://resize",u.WINDOW_MOVED="tauri://move",u.WINDOW_CLOSE_REQUESTED="tauri://close-requested",u.WINDOW_CREATED="tauri://window-created",u.WINDOW_DESTROYED="tauri://destroyed",u.WINDOW_FOCUS="tauri://focus",u.WINDOW_BLUR="tauri://blur",u.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",u.WINDOW_THEME_CHANGED="tauri://theme-changed",u.WINDOW_FILE_DROP="tauri://file-drop",u.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",u.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",u.MENU="tauri://menu",u.CHECK_UPDATE="tauri://update",u.UPDATE_AVAILABLE="tauri://update-available",u.INSTALL_UPDATE="tauri://update-install",u.STATUS_UPDATE="tauri://update-status",u.DOWNLOAD_PROGRESS="tauri://update-download-progress",u))(M||{});async function z(t,e){return _(t,null,e)}async function H(t,e){return v(t,null,e)}async function T(t,e){return w(t,void 0,e)}var j={};c(j,{BaseDirectory:()=>O,Dir:()=>O,copyFile:()=>Ne,createDir:()=>Ue,exists:()=>Ve,readBinaryFile:()=>xe,readDir:()=>ke,readTextFile:()=>We,removeDir:()=>Ie,removeFile:()=>ze,renameFile:()=>He,writeBinaryFile:()=>Re,writeFile:()=>Le,writeTextFile:()=>Le});var O=(o=>(o[o.Audio=1]="Audio",o[o.Cache=2]="Cache",o[o.Config=3]="Config",o[o.Data=4]="Data",o[o.LocalData=5]="LocalData",o[o.Desktop=6]="Desktop",o[o.Document=7]="Document",o[o.Download=8]="Download",o[o.Executable=9]="Executable",o[o.Font=10]="Font",o[o.Home=11]="Home",o[o.Picture=12]="Picture",o[o.Public=13]="Public",o[o.Runtime=14]="Runtime",o[o.Template=15]="Template",o[o.Video=16]="Video",o[o.Resource=17]="Resource",o[o.App=18]="App",o[o.Log=19]="Log",o[o.Temp=20]="Temp",o[o.AppConfig=21]="AppConfig",o[o.AppData=22]="AppData",o[o.AppLocalData=23]="AppLocalData",o[o.AppCache=24]="AppCache",o[o.AppLog=25]="AppLog",o))(O||{});async function We(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"readTextFile",path:t,options:e}})}async function xe(t,e={}){let n=await i({__tauriModule:"Fs",message:{cmd:"readFile",path:t,options:e}});return Uint8Array.from(n)}async function Le(t,e,n){typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t);let r={path:"",contents:""},s=n;return typeof t=="string"?r.path=t:(r.path=t.path,r.contents=t.contents),typeof e=="string"?r.contents=e??"":s=e,i({__tauriModule:"Fs",message:{cmd:"writeFile",path:r.path,contents:Array.from(new TextEncoder().encode(r.contents)),options:s}})}async function Re(t,e,n){typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t);let r={path:"",contents:[]},s=n;return typeof t=="string"?r.path=t:(r.path=t.path,r.contents=t.contents),e&&"dir"in e?s=e:typeof t=="string"&&(r.contents=e??[]),i({__tauriModule:"Fs",message:{cmd:"writeFile",path:r.path,contents:Array.from(r.contents instanceof ArrayBuffer?new Uint8Array(r.contents):r.contents),options:s}})}async function ke(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"readDir",path:t,options:e}})}async function Ue(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"createDir",path:t,options:e}})}async function Ie(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"removeDir",path:t,options:e}})}async function Ne(t,e,n={}){return i({__tauriModule:"Fs",message:{cmd:"copyFile",source:t,destination:e,options:n}})}async function ze(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"removeFile",path:t,options:e}})}async function He(t,e,n={}){return i({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:t,newPath:e,options:n}})}async function Ve(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"exists",path:t,options:e}})}var q={};c(q,{isRegistered:()=>Ge,register:()=>je,registerAll:()=>qe,unregister:()=>$e,unregisterAll:()=>Je});async function je(t,e){return i({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:t,handler:d(e)}})}async function qe(t,e){return i({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:t,handler:d(e)}})}async function Ge(t){return i({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:t}})}async function $e(t){return i({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:t}})}async function Je(){return i({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}})}var $={};c($,{Body:()=>p,Client:()=>E,Response:()=>F,ResponseType:()=>re,fetch:()=>Ke,getClient:()=>se});var re=(r=>(r[r.JSON=1]="JSON",r[r.Text=2]="Text",r[r.Binary=3]="Binary",r))(re||{}),p=class{constructor(e,n){this.type=e,this.payload=n}static form(e){let n={},r=(s,a)=>{if(a!==null){let l;typeof a=="string"?l=a:a instanceof Uint8Array||Array.isArray(a)?l=Array.from(a):a instanceof File?l={file:a.name,mime:a.type,fileName:a.name}:typeof a.file=="string"?l={file:a.file,mime:a.mime,fileName:a.fileName}:l={file:Array.from(a.file),mime:a.mime,fileName:a.fileName},n[String(s)]=l}};if(e instanceof FormData)for(let[s,a]of e)r(s,a);else for(let[s,a]of Object.entries(e))r(s,a);return new p("Form",n)}static json(e){return new p("Json",e)}static text(e){return new p("Text",e)}static bytes(e){return new p("Bytes",Array.from(e instanceof ArrayBuffer?new Uint8Array(e):e))}},F=class{constructor(e){this.url=e.url,this.status=e.status,this.ok=this.status>=200&&this.status<300,this.headers=e.headers,this.rawHeaders=e.rawHeaders,this.data=e.data}},E=class{constructor(e){this.id=e}async drop(){return i({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}})}async request(e){let n=!e.responseType||e.responseType===1;return n&&(e.responseType=2),i({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:e}}).then(r=>{let s=new F(r);if(n){try{s.data=JSON.parse(s.data)}catch(a){if(s.ok&&s.data==="")s.data={};else if(s.ok)throw Error(`Failed to parse response \`${s.data}\` as JSON: ${a}; + try setting the \`responseType\` option to \`ResponseType.Text\` or \`ResponseType.Binary\` if the API does not return a JSON response.`)}return s}return s})}async get(e,n){return this.request({method:"GET",url:e,...n})}async post(e,n,r){return this.request({method:"POST",url:e,body:n,...r})}async put(e,n,r){return this.request({method:"PUT",url:e,body:n,...r})}async patch(e,n){return this.request({method:"PATCH",url:e,...n})}async delete(e,n){return this.request({method:"DELETE",url:e,...n})}};async function se(t){return i({__tauriModule:"Http",message:{cmd:"createClient",options:t}}).then(e=>new E(e))}var G=null;async function Ke(t,e){return G===null&&(G=await se()),G.request({url:t,method:e?.method??"GET",...e})}var J={};c(J,{isPermissionGranted:()=>Qe,requestPermission:()=>Ze,sendNotification:()=>Ye});async function Qe(){return window.Notification.permission!=="default"?Promise.resolve(window.Notification.permission==="granted"):i({__tauriModule:"Notification",message:{cmd:"isNotificationPermissionGranted"}})}async function Ze(){return window.Notification.requestPermission()}function Ye(t){typeof t=="string"?new window.Notification(t):new window.Notification(t.title,t)}var K={};c(K,{BaseDirectory:()=>O,appCacheDir:()=>tt,appConfigDir:()=>ae,appDataDir:()=>Be,appDir:()=>Xe,appLocalDataDir:()=>et,appLogDir:()=>oe,audioDir:()=>nt,basename:()=>At,cacheDir:()=>it,configDir:()=>rt,dataDir:()=>st,delimiter:()=>vt,desktopDir:()=>at,dirname:()=>Ft,documentDir:()=>ot,downloadDir:()=>lt,executableDir:()=>ut,extname:()=>Et,fontDir:()=>ct,homeDir:()=>dt,isAbsolute:()=>Ct,join:()=>Ot,localDataDir:()=>mt,logDir:()=>Pt,normalize:()=>Tt,pictureDir:()=>pt,publicDir:()=>gt,resolve:()=>Mt,resolveResource:()=>yt,resourceDir:()=>ht,runtimeDir:()=>ft,sep:()=>wt,templateDir:()=>_t,videoDir:()=>bt});function b(){return navigator.appVersion.includes("Win")}async function Xe(){return ae()}async function ae(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:21}})}async function Be(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:22}})}async function et(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:23}})}async function tt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:24}})}async function nt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:1}})}async function it(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:2}})}async function rt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:3}})}async function st(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:4}})}async function at(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:6}})}async function ot(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:7}})}async function lt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:8}})}async function ut(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:9}})}async function ct(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:10}})}async function dt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:11}})}async function mt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:5}})}async function pt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:12}})}async function gt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:13}})}async function ht(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:17}})}async function yt(t){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:t,directory:17}})}async function ft(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:14}})}async function _t(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:15}})}async function bt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:16}})}async function Pt(){return oe()}async function oe(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:25}})}var wt=b()?"\\":"/",vt=b()?";":":";async function Mt(...t){return i({__tauriModule:"Path",message:{cmd:"resolve",paths:t}})}async function Tt(t){return i({__tauriModule:"Path",message:{cmd:"normalize",path:t}})}async function Ot(...t){return i({__tauriModule:"Path",message:{cmd:"join",paths:t}})}async function Ft(t){return i({__tauriModule:"Path",message:{cmd:"dirname",path:t}})}async function Et(t){return i({__tauriModule:"Path",message:{cmd:"extname",path:t}})}async function At(t,e){return i({__tauriModule:"Path",message:{cmd:"basename",path:t,ext:e}})}async function Ct(t){return i({__tauriModule:"Path",message:{cmd:"isAbsolute",path:t}})}var Q={};c(Q,{exit:()=>Dt,relaunch:()=>St});async function Dt(t=0){return i({__tauriModule:"Process",message:{cmd:"exit",exitCode:t}})}async function St(){return i({__tauriModule:"Process",message:{cmd:"relaunch"}})}var Z={};c(Z,{Child:()=>A,Command:()=>P,EventEmitter:()=>g,open:()=>xt});async function Wt(t,e,n=[],r){return typeof n=="object"&&Object.freeze(n),i({__tauriModule:"Shell",message:{cmd:"execute",program:e,args:n,options:r,onEventFn:d(t)}})}var g=class{constructor(){this.eventListeners=Object.create(null)}addListener(e,n){return this.on(e,n)}removeListener(e,n){return this.off(e,n)}on(e,n){return e in this.eventListeners?this.eventListeners[e].push(n):this.eventListeners[e]=[n],this}once(e,n){let r=(...s)=>{this.removeListener(e,r),n(...s)};return this.addListener(e,r)}off(e,n){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter(r=>r!==n)),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,...n){if(e in this.eventListeners){let r=this.eventListeners[e];for(let s of r)s(...n);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,n){return e in this.eventListeners?this.eventListeners[e].unshift(n):this.eventListeners[e]=[n],this}prependOnceListener(e,n){let r=(...s)=>{this.removeListener(e,r),n(...s)};return this.prependListener(e,r)}},A=class{constructor(e){this.pid=e}async write(e){return i({__tauriModule:"Shell",message:{cmd:"stdinWrite",pid:this.pid,buffer:typeof e=="string"?e:Array.from(e)}})}async kill(){return i({__tauriModule:"Shell",message:{cmd:"killChild",pid:this.pid}})}},P=class extends g{constructor(n,r=[],s){super();this.stdout=new g;this.stderr=new g;this.program=n,this.args=typeof r=="string"?[r]:r,this.options=s??{}}static sidecar(n,r=[],s){let a=new P(n,r,s);return a.options.sidecar=!0,a}async spawn(){return Wt(n=>{switch(n.event){case"Error":this.emit("error",n.payload);break;case"Terminated":this.emit("close",n.payload);break;case"Stdout":this.stdout.emit("data",n.payload);break;case"Stderr":this.stderr.emit("data",n.payload);break}},this.program,this.args,this.options).then(n=>new A(n))}async execute(){return new Promise((n,r)=>{this.on("error",r);let s=[],a=[];this.stdout.on("data",l=>{s.push(l)}),this.stderr.on("data",l=>{a.push(l)}),this.on("close",l=>{n({code:l.code,signal:l.signal,stdout:s.join(` `),stderr:a.join(` -`)})}),this.spawn().catch(r)})}};async function xt(n,e){return i({__tauriModule:"Shell",message:{cmd:"open",path:n,with:e}})}var X={};c(X,{checkUpdate:()=>Rt,installUpdate:()=>Lt,onUpdaterEvent:()=>Y});async function Y(n){return z("tauri://update-status",e=>{n(e==null?void 0:e.payload)})}async function Lt(){let n;function e(){n&&n(),n=void 0}return new Promise((t,r)=>{function s(a){if(a.error)return e(),r(a.error);if(a.status==="DONE")return e(),t()}Y(s).then(a=>{n=a}).catch(a=>{throw e(),a}),T("tauri://update-install").catch(a=>{throw e(),a})})}async function Rt(){let n;function e(){n&&n(),n=void 0}return new Promise((t,r)=>{function s(l){return e(),t({manifest:l,shouldUpdate:!0})}function a(l){if(l.error)return e(),r(l.error);if(l.status==="UPTODATE")return e(),t({shouldUpdate:!1})}H("tauri://update-available",l=>{s(l==null?void 0:l.payload)}).catch(l=>{throw e(),l}),Y(a).then(l=>{n=l}).catch(l=>{throw e(),l}),T("tauri://update").catch(l=>{throw e(),l})})}var te={};c(te,{CloseRequestedEvent:()=>x,LogicalPosition:()=>D,LogicalSize:()=>C,PhysicalPosition:()=>y,PhysicalSize:()=>h,UserAttentionType:()=>ue,WebviewWindow:()=>m,WebviewWindowHandle:()=>S,WindowManager:()=>W,appWindow:()=>B,availableMonitors:()=>Nt,currentMonitor:()=>kt,getAll:()=>ce,getCurrent:()=>Ut,primaryMonitor:()=>It});var C=class{constructor(e,t){this.type="Logical";this.width=e,this.height=t}},h=class{constructor(e,t){this.type="Physical";this.width=e,this.height=t}toLogical(e){return new C(this.width/e,this.height/e)}},D=class{constructor(e,t){this.type="Logical";this.x=e,this.y=t}},y=class{constructor(e,t){this.type="Physical";this.x=e,this.y=t}toLogical(e){return new D(this.x/e,this.y/e)}},ue=(t=>(t[t.Critical=1]="Critical",t[t.Informational=2]="Informational",t))(ue||{});function Ut(){return new m(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0})}function ce(){return window.__TAURI_METADATA__.__windows.map(n=>new m(n.label,{skip:!0}))}var le=["tauri://created","tauri://error"],S=class{constructor(e){this.label=e,this.listeners=Object.create(null)}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):_(e,this.label,t)}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(t),1)}):v(e,this.label,t)}async emit(e,t){if(le.includes(e)){for(let r of this.listeners[e]||[])r({event:e,id:-1,windowLabel:this.label,payload:t});return Promise.resolve()}return w(e,this.label,t)}_handleTauriEvent(e,t){return le.includes(e)?(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0):!1}},W=class extends S{async scaleFactor(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"scaleFactor"}}}})}async innerPosition(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerPosition"}}}}).then(({x:e,y:t})=>new y(e,t))}async outerPosition(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerPosition"}}}}).then(({x:e,y:t})=>new y(e,t))}async innerSize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerSize"}}}}).then(({width:e,height:t})=>new h(e,t))}async outerSize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerSize"}}}}).then(({width:e,height:t})=>new h(e,t))}async isFullscreen(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isFullscreen"}}}})}async isMaximized(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMaximized"}}}})}async isDecorated(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isDecorated"}}}})}async isResizable(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isResizable"}}}})}async isVisible(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isVisible"}}}})}async theme(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"theme"}}}})}async center(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"center"}}}})}async requestUserAttention(e){let t=null;return e&&(e===1?t={type:"Critical"}:t={type:"Informational"}),i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"requestUserAttention",payload:t}}}})}async setResizable(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setResizable",payload:e}}}})}async setTitle(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setTitle",payload:e}}}})}async maximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"maximize"}}}})}async unmaximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unmaximize"}}}})}async toggleMaximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"toggleMaximize"}}}})}async minimize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"minimize"}}}})}async unminimize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unminimize"}}}})}async show(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"show"}}}})}async hide(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"hide"}}}})}async close(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"close"}}}})}async setDecorations(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setDecorations",payload:e}}}})}async setAlwaysOnTop(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setAlwaysOnTop",payload:e}}}})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSize",payload:{type:e.type,data:{width:e.width,height:e.height}}}}}})}async setMinSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMinSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setMaxSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMaxSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setFullscreen(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFullscreen",payload:e}}}})}async setFocus(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFocus"}}}})}async setIcon(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIcon",payload:{icon:typeof e=="string"?e:Array.from(e)}}}}})}async setSkipTaskbar(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSkipTaskbar",payload:e}}}})}async setCursorGrab(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorGrab",payload:e}}}})}async setCursorVisible(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorVisible",payload:e}}}})}async setCursorIcon(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorIcon",payload:e}}}})}async setCursorPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setIgnoreCursorEvents(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIgnoreCursorEvents",payload:e}}}})}async startDragging(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"startDragging"}}}})}async onResized(e){return this.listen("tauri://resize",e)}async onMoved(e){return this.listen("tauri://move",e)}async onCloseRequested(e){return this.listen("tauri://close-requested",t=>{let r=new x(t);Promise.resolve(e(r)).then(()=>{if(!r.isPreventDefault())return this.close()})})}async onFocusChanged(e){let t=await this.listen("tauri://focus",s=>{e({...s,payload:!0})}),r=await this.listen("tauri://blur",s=>{e({...s,payload:!1})});return()=>{t(),r()}}async onScaleChanged(e){return this.listen("tauri://scale-change",e)}async onMenuClicked(e){return this.listen("tauri://menu",e)}async onFileDropEvent(e){let t=await this.listen("tauri://file-drop",a=>{e({...a,payload:{type:"drop",paths:a.payload}})}),r=await this.listen("tauri://file-drop-hover",a=>{e({...a,payload:{type:"hover",paths:a.payload}})}),s=await this.listen("tauri://file-drop-cancelled",a=>{e({...a,payload:{type:"cancel"}})});return()=>{t(),r(),s()}}async onThemeChanged(e){return this.listen("tauri://theme-changed",e)}},x=class{constructor(e){this._preventDefault=!1;this.event=e.event,this.windowLabel=e.windowLabel,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}},m=class extends W{constructor(e,t={}){super(e),t!=null&&t.skip||i({__tauriModule:"Window",message:{cmd:"createWebview",data:{options:{label:e,...t}}}}).then(async()=>this.emit("tauri://created")).catch(async r=>this.emit("tauri://error",r))}static getByLabel(e){return ce().some(t=>t.label===e)?new m(e,{skip:!0}):null}},B;"__TAURI_METADATA__"in window?B=new m(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0}):(console.warn(`Could not find "window.__TAURI_METADATA__". The "appWindow" value will reference the "main" window label. -Note that this is not an issue if running this frontend on a browser instead of a Tauri window.`),B=new m("main",{skip:!0}));function ee(n){return n===null?null:{name:n.name,scaleFactor:n.scaleFactor,position:new y(n.position.x,n.position.y),size:new h(n.size.width,n.size.height)}}async function kt(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"currentMonitor"}}}}).then(ee)}async function It(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"primaryMonitor"}}}}).then(ee)}async function Nt(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"availableMonitors"}}}}).then(n=>n.map(ee))}var ne={};c(ne,{EOL:()=>zt,arch:()=>qt,platform:()=>Ht,tempdir:()=>Gt,type:()=>jt,version:()=>Vt});var zt=b()?`\r +`)})}),this.spawn().catch(r)})}};async function xt(t,e){return i({__tauriModule:"Shell",message:{cmd:"open",path:t,with:e}})}var X={};c(X,{checkUpdate:()=>Rt,installUpdate:()=>Lt,onUpdaterEvent:()=>Y});async function Y(t){return z("tauri://update-status",e=>{t(e?.payload)})}async function Lt(){let t;function e(){t&&t(),t=void 0}return new Promise((n,r)=>{function s(a){if(a.error)return e(),r(a.error);if(a.status==="DONE")return e(),n()}Y(s).then(a=>{t=a}).catch(a=>{throw e(),a}),T("tauri://update-install").catch(a=>{throw e(),a})})}async function Rt(){let t;function e(){t&&t(),t=void 0}return new Promise((n,r)=>{function s(l){return e(),n({manifest:l,shouldUpdate:!0})}function a(l){if(l.error)return e(),r(l.error);if(l.status==="UPTODATE")return e(),n({shouldUpdate:!1})}H("tauri://update-available",l=>{s(l?.payload)}).catch(l=>{throw e(),l}),Y(a).then(l=>{t=l}).catch(l=>{throw e(),l}),T("tauri://update").catch(l=>{throw e(),l})})}var te={};c(te,{CloseRequestedEvent:()=>x,LogicalPosition:()=>D,LogicalSize:()=>C,PhysicalPosition:()=>y,PhysicalSize:()=>h,UserAttentionType:()=>ue,WebviewWindow:()=>m,WebviewWindowHandle:()=>S,WindowManager:()=>W,appWindow:()=>B,availableMonitors:()=>Nt,currentMonitor:()=>Ut,getAll:()=>ce,getCurrent:()=>kt,primaryMonitor:()=>It});var C=class{constructor(e,n){this.type="Logical";this.width=e,this.height=n}},h=class{constructor(e,n){this.type="Physical";this.width=e,this.height=n}toLogical(e){return new C(this.width/e,this.height/e)}},D=class{constructor(e,n){this.type="Logical";this.x=e,this.y=n}},y=class{constructor(e,n){this.type="Physical";this.x=e,this.y=n}toLogical(e){return new D(this.x/e,this.y/e)}},ue=(n=>(n[n.Critical=1]="Critical",n[n.Informational=2]="Informational",n))(ue||{});function kt(){return new m(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0})}function ce(){return window.__TAURI_METADATA__.__windows.map(t=>new m(t.label,{skip:!0}))}var le=["tauri://created","tauri://error"],S=class{constructor(e){this.label=e,this.listeners=Object.create(null)}async listen(e,n){return this._handleTauriEvent(e,n)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(n),1)}):_(e,this.label,n)}async once(e,n){return this._handleTauriEvent(e,n)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(n),1)}):v(e,this.label,n)}async emit(e,n){if(le.includes(e)){for(let r of this.listeners[e]||[])r({event:e,id:-1,windowLabel:this.label,payload:n});return Promise.resolve()}return w(e,this.label,n)}_handleTauriEvent(e,n){return le.includes(e)?(e in this.listeners?this.listeners[e].push(n):this.listeners[e]=[n],!0):!1}},W=class extends S{async scaleFactor(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"scaleFactor"}}}})}async innerPosition(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerPosition"}}}}).then(({x:e,y:n})=>new y(e,n))}async outerPosition(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerPosition"}}}}).then(({x:e,y:n})=>new y(e,n))}async innerSize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerSize"}}}}).then(({width:e,height:n})=>new h(e,n))}async outerSize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerSize"}}}}).then(({width:e,height:n})=>new h(e,n))}async isFullscreen(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isFullscreen"}}}})}async isMaximized(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMaximized"}}}})}async isDecorated(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isDecorated"}}}})}async isResizable(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isResizable"}}}})}async isVisible(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isVisible"}}}})}async theme(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"theme"}}}})}async center(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"center"}}}})}async requestUserAttention(e){let n=null;return e&&(e===1?n={type:"Critical"}:n={type:"Informational"}),i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"requestUserAttention",payload:n}}}})}async setResizable(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setResizable",payload:e}}}})}async setTitle(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setTitle",payload:e}}}})}async maximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"maximize"}}}})}async unmaximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unmaximize"}}}})}async toggleMaximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"toggleMaximize"}}}})}async minimize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"minimize"}}}})}async unminimize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unminimize"}}}})}async show(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"show"}}}})}async hide(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"hide"}}}})}async close(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"close"}}}})}async setDecorations(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setDecorations",payload:e}}}})}async setAlwaysOnTop(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setAlwaysOnTop",payload:e}}}})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSize",payload:{type:e.type,data:{width:e.width,height:e.height}}}}}})}async setMinSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMinSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setMaxSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMaxSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setFullscreen(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFullscreen",payload:e}}}})}async setFocus(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFocus"}}}})}async setIcon(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIcon",payload:{icon:typeof e=="string"?e:Array.from(e)}}}}})}async setSkipTaskbar(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSkipTaskbar",payload:e}}}})}async setCursorGrab(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorGrab",payload:e}}}})}async setCursorVisible(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorVisible",payload:e}}}})}async setCursorIcon(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorIcon",payload:e}}}})}async setCursorPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setIgnoreCursorEvents(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIgnoreCursorEvents",payload:e}}}})}async startDragging(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"startDragging"}}}})}async onResized(e){return this.listen("tauri://resize",e)}async onMoved(e){return this.listen("tauri://move",e)}async onCloseRequested(e){return this.listen("tauri://close-requested",n=>{let r=new x(n);Promise.resolve(e(r)).then(()=>{if(!r.isPreventDefault())return this.close()})})}async onFocusChanged(e){let n=await this.listen("tauri://focus",s=>{e({...s,payload:!0})}),r=await this.listen("tauri://blur",s=>{e({...s,payload:!1})});return()=>{n(),r()}}async onScaleChanged(e){return this.listen("tauri://scale-change",e)}async onMenuClicked(e){return this.listen("tauri://menu",e)}async onFileDropEvent(e){let n=await this.listen("tauri://file-drop",a=>{e({...a,payload:{type:"drop",paths:a.payload}})}),r=await this.listen("tauri://file-drop-hover",a=>{e({...a,payload:{type:"hover",paths:a.payload}})}),s=await this.listen("tauri://file-drop-cancelled",a=>{e({...a,payload:{type:"cancel"}})});return()=>{n(),r(),s()}}async onThemeChanged(e){return this.listen("tauri://theme-changed",e)}},x=class{constructor(e){this._preventDefault=!1;this.event=e.event,this.windowLabel=e.windowLabel,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}},m=class extends W{constructor(e,n={}){super(e),n?.skip||i({__tauriModule:"Window",message:{cmd:"createWebview",data:{options:{label:e,...n}}}}).then(async()=>this.emit("tauri://created")).catch(async r=>this.emit("tauri://error",r))}static getByLabel(e){return ce().some(n=>n.label===e)?new m(e,{skip:!0}):null}},B;"__TAURI_METADATA__"in window?B=new m(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0}):(console.warn(`Could not find "window.__TAURI_METADATA__". The "appWindow" value will reference the "main" window label. +Note that this is not an issue if running this frontend on a browser instead of a Tauri window.`),B=new m("main",{skip:!0}));function ee(t){return t===null?null:{name:t.name,scaleFactor:t.scaleFactor,position:new y(t.position.x,t.position.y),size:new h(t.size.width,t.size.height)}}async function Ut(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"currentMonitor"}}}}).then(ee)}async function It(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"primaryMonitor"}}}}).then(ee)}async function Nt(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"availableMonitors"}}}}).then(t=>t.map(ee))}var ne={};c(ne,{EOL:()=>zt,arch:()=>qt,platform:()=>Ht,tempdir:()=>Gt,type:()=>jt,version:()=>Vt});var zt=b()?`\r `:` `;async function Ht(){return i({__tauriModule:"Os",message:{cmd:"platform"}})}async function Vt(){return i({__tauriModule:"Os",message:{cmd:"version"}})}async function jt(){return i({__tauriModule:"Os",message:{cmd:"osType"}})}async function qt(){return i({__tauriModule:"Os",message:{cmd:"arch"}})}async function Gt(){return i({__tauriModule:"Os",message:{cmd:"tempdir"}})}var $t=f;return he(Jt);})(); window.__TAURI__ = __TAURI_IIFE__ From dd65bc99b20f508f46db0b9c7dbb9c40623224e8 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Fri, 9 Dec 2022 15:17:14 -0300 Subject: [PATCH 097/423] chore(ci): remove integration tests --- .../covector-version-or-publish-next.yml | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/.github/workflows/covector-version-or-publish-next.yml b/.github/workflows/covector-version-or-publish-next.yml index c958ea36854..2c68d56f2ca 100644 --- a/.github/workflows/covector-version-or-publish-next.yml +++ b/.github/workflows/covector-version-or-publish-next.yml @@ -10,51 +10,6 @@ on: - next jobs: - run-integration-tests: - runs-on: ${{ matrix.platform }} - - strategy: - fail-fast: false - matrix: - platform: [ubuntu-latest, macos-latest, windows-latest] - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: install stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - name: install Linux dependencies - if: matrix.platform == 'ubuntu-latest' - run: | - sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev libfuse2 - - - uses: Swatinem/rust-cache@v2 - with: - workspaces: | - core -> ../target - tooling/cli - - - name: build CLI - uses: actions-rs/cargo@v1 - with: - command: build - args: --manifest-path ./tooling/cli/Cargo.toml - - - name: run integration tests - run: cargo test --test '*' -- --ignored - - - name: run CLI tests - timeout-minutes: 30 - run: | - cd ./tooling/cli/node - yarn - yarn build - yarn test - version-or-publish: runs-on: ubuntu-latest timeout-minutes: 65 @@ -62,8 +17,6 @@ jobs: change: ${{ steps.covector.outputs.change }} commandRan: ${{ steps.covector.outputs.commandRan }} successfulPublish: ${{ steps.covector.outputs.successfulPublish }} - needs: - - run-integration-tests steps: - uses: actions/checkout@v2 From be446a866546d4d1a0b7b1b8022dcb41ad76641d Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sat, 10 Dec 2022 06:37:16 -0800 Subject: [PATCH 098/423] fix(cli): use vendored openSSL (#5805) --- tooling/cli/Cargo.lock | 11 +++++++++++ tooling/cli/Cargo.toml | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 9858c5269d8..d904fb3fe7c 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -2579,6 +2579,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-src" +version = "111.24.0+1.1.1s" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3498f259dab01178c6228c6b00dcef0ed2a2d5e20d648c017861227773ea4abd" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.74" @@ -2588,6 +2597,7 @@ dependencies = [ "autocfg", "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -3850,6 +3860,7 @@ dependencies = [ "notify", "notify-debouncer-mini", "once_cell", + "openssl", "os_info", "os_pipe", "regex", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index ebd0ae86d53..377f65f257a 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -92,5 +92,9 @@ winapi = { version = "0.3", features = [ "handleapi", "processenv", "winbase", " [target."cfg(unix)".dependencies] libc = "0.2" +[target.'cfg(target_os = "macos")'.dependencies] +# temporary fix, tauri-mobile should provide the openssl-vendored feature +openssl = { version = "0.10", features = ["vendored"] } + [profile.release] lto = true From 2620ab294dfabfa01b1e51b87d2402b43de9e532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 12 Dec 2022 20:41:26 +0800 Subject: [PATCH 099/423] fix(cli): ios build description (#5809) --- tooling/cli/src/mobile/ios.rs | 1 + tooling/cli/src/mobile/ios/build.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index 33ea44d91d6..bb51e02779b 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -73,6 +73,7 @@ pub struct InitOptions { #[derive(Subcommand)] enum Commands { Init(InitOptions), + /// Open project in Xcode Open, Dev(dev::Options), Build(build::Options), diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index 5efe53f6564..33773482d64 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -20,7 +20,7 @@ use tauri_mobile::{ use std::fs; #[derive(Debug, Clone, Parser)] -#[clap(about = "Android build")] +#[clap(about = "iOS build")] pub struct Options { /// Builds with the debug flag #[clap(short, long)] From 1e4a675843c486bddc11292d09fb766e98758514 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 12 Dec 2022 05:36:47 -0800 Subject: [PATCH 100/423] fix(cli): run on iOS device on Xcode 14 (#5807) --- .changes/fix-ios-run-xcode14.md | 6 +++ examples/api/src-tauri/Cargo.lock | 14 +++--- tooling/cli/src/info.rs | 1 - tooling/cli/src/mobile/ios/project.rs | 10 ++++ tooling/cli/src/mobile/ios/xcode_script.rs | 53 ++++++++++++++++---- tooling/cli/templates/mobile/ios/project.yml | 7 +-- 6 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 .changes/fix-ios-run-xcode14.md diff --git a/.changes/fix-ios-run-xcode14.md b/.changes/fix-ios-run-xcode14.md new file mode 100644 index 00000000000..592244de577 --- /dev/null +++ b/.changes/fix-ios-run-xcode14.md @@ -0,0 +1,6 @@ +--- +"cli.rs": patch +"cli.js": patch +--- + +Fixes running on device using Xcode 14. diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 6f89c3e5370..5c66e3081f5 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -2935,7 +2935,7 @@ dependencies = [ [[package]] name = "tauri" -version = "1.2.2" +version = "2.0.0-alpha.0" dependencies = [ "android_logger", "anyhow", @@ -3001,7 +3001,7 @@ dependencies = [ [[package]] name = "tauri-build" -version = "1.2.1" +version = "2.0.0-alpha.0" dependencies = [ "anyhow", "cargo_toml", @@ -3017,7 +3017,7 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "1.2.1" +version = "2.0.0-alpha.0" dependencies = [ "base64", "brotli", @@ -3042,7 +3042,7 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "1.2.1" +version = "2.0.0-alpha.0" dependencies = [ "heck 0.4.0", "proc-macro2", @@ -3054,7 +3054,7 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "0.12.1" +version = "0.13.0-alpha.0" dependencies = [ "gtk", "http", @@ -3072,7 +3072,7 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "0.12.2" +version = "0.13.0-alpha.0" dependencies = [ "cocoa", "gtk", @@ -3090,7 +3090,7 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "1.2.1" +version = "2.0.0-alpha.0" dependencies = [ "aes-gcm", "brotli", diff --git a/tooling/cli/src/info.rs b/tooling/cli/src/info.rs index f63b620d800..b3e99cc0f7c 100644 --- a/tooling/cli/src/info.rs +++ b/tooling/cli/src/info.rs @@ -901,7 +901,6 @@ pub fn command(_options: Options) -> Result<()> { .map(|t| format!("{} (ID: {})", t.name, t.id)) .collect::>() .join(", ") - .to_string() }, ) .display(); diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index 4067be50417..94ab10cf7ce 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -158,6 +158,16 @@ pub fn gen( })?; } + let externals_dir = dest.join("Externals"); + if !externals_dir.is_dir() { + create_dir_all(&externals_dir).map_err(|cause| { + anyhow::anyhow!( + "failed to create Externals dir {path}: {cause}", + path = externals_dir.display() + ) + })?; + } + // Create all asset catalog directories if they don't already exist for dir in asset_catalogs { std::fs::create_dir_all(dir).map_err(|cause| { diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 7d2746f8cdb..5ce3443abc5 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -1,7 +1,12 @@ use super::{env, with_config}; -use crate::Result; -use clap::Parser; +use crate::{ + helpers::config::get as get_config, + interface::{AppInterface, AppSettings, Interface, Options as InterfaceOptions}, + Result, +}; +use clap::Parser; +use heck::AsSnakeCase; use tauri_mobile::{apple::target::Target, opts::Profile, util}; use std::{collections::HashMap, ffi::OsStr, path::PathBuf}; @@ -124,12 +129,14 @@ pub fn command(options: Options) -> Result<()> { let isysroot = format!("-isysroot {}", options.sdk_root.display()); + let tauri_config = get_config(None)?; + for arch in options.arches { // Set target-specific flags - let triple = match arch.as_str() { - "arm64" => "aarch64_apple_ios", - "arm64-sim" => "aarch64_apple_ios_sim", - "x86_64" => "x86_64_apple_ios", + let (env_triple, rust_triple) = match arch.as_str() { + "arm64" => ("aarch64_apple_ios", "aarch64-apple-ios"), + "arm64-sim" => ("aarch64_apple_ios_sim", "aarch64-apple-ios-sim"), + "x86_64" => ("x86_64_apple_ios", "x86_64-apple-ios"), "Simulator" => continue, _ => { return Err(anyhow::anyhow!( @@ -138,9 +145,15 @@ pub fn command(options: Options) -> Result<()> { )) } }; - let cflags = format!("CFLAGS_{}", triple); - let cxxflags = format!("CFLAGS_{}", triple); - let objc_include_path = format!("OBJC_INCLUDE_PATH_{}", triple); + + let interface = AppInterface::new( + tauri_config.lock().unwrap().as_ref().unwrap(), + Some(rust_triple.into()), + )?; + + let cflags = format!("CFLAGS_{}", env_triple); + let cxxflags = format!("CFLAGS_{}", env_triple); + let objc_include_path = format!("OBJC_INCLUDE_PATH_{}", env_triple); let mut target_env = host_env.clone(); target_env.insert(cflags.as_ref(), isysroot.as_ref()); target_env.insert(cxxflags.as_ref(), isysroot.as_ref()); @@ -165,6 +178,28 @@ pub fn command(options: Options) -> Result<()> { &env, target_env, )?; + + let bin_path = interface + .app_settings() + .app_binary_path(&InterfaceOptions { + debug: matches!(profile, Profile::Debug), + target: Some(rust_triple.into()), + ..Default::default() + })?; + let out_dir = bin_path.parent().unwrap(); + + std::fs::create_dir_all(format!( + "gen/apple/Externals/{rust_triple}/{}", + profile.as_str() + ))?; + std::fs::copy( + out_dir.join(format!("lib{}.a", AsSnakeCase(config.app().name()))), + format!( + "gen/apple/Externals/{rust_triple}/{}/lib{}.a", + profile.as_str(), + AsSnakeCase(config.app().name()) + ), + )?; } Ok(()) }) diff --git a/tooling/cli/templates/mobile/ios/project.yml b/tooling/cli/templates/mobile/ios/project.yml index 31fe4bf979b..92053c8fccd 100644 --- a/tooling/cli/templates/mobile/ios/project.yml +++ b/tooling/cli/templates/mobile/ios/project.yml @@ -31,6 +31,7 @@ targets: sources: - path: Sources - path: Assets.xcassets + - path: Externals - path: {{app.asset-dir}} buildPhase: resources type: folder @@ -71,9 +72,9 @@ targets: ENABLE_BITCODE: false ARCHS: [{{join ios-valid-archs}}] VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} - LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) "{{prefix-path "target/x86_64-apple-ios/$(CONFIGURATION)"}}" - LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) "{{prefix-path "target/aarch64-apple-ios/$(CONFIGURATION)"}}" - LIBRARY_SEARCH_PATHS[arch=arm64-sim]: $(inherited) "{{prefix-path "target/aarch64-apple-ios-sim/$(CONFIGURATION)"}}" + LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/x86_64-apple-ios/$(CONFIGURATION) + LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/aarch64-apple-ios/$(CONFIGURATION) + LIBRARY_SEARCH_PATHS[arch=arm64-sim]: $(inherited) $(PROJECT_DIR)/Externals/aarch64-apple-ios-sim/$(CONFIGURATION) ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true groups: [app] dependencies: From 8f47825aacf820129f4e604855c37033bb77ab16 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 12 Dec 2022 15:14:04 -0300 Subject: [PATCH 101/423] chore: remove unused imports on mobile builds --- core/tauri/src/window.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index 76e6f0fea27..156bfd21f13 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -18,20 +18,28 @@ use crate::{ manager::WindowManager, runtime::{ http::{Request as HttpRequest, Response as HttpResponse}, - menu::Menu, monitor::Monitor as RuntimeMonitor, webview::{WebviewAttributes, WindowBuilder as _}, window::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, + dpi::{PhysicalPosition, PhysicalSize}, DetachedWindow, JsEventListenerKey, PendingWindow, }, - Dispatch, RuntimeHandle, UserAttentionType, + Dispatch, RuntimeHandle, }, sealed::ManagerBase, sealed::RuntimeOrDispatch, utils::config::WindowUrl, - CursorIcon, EventLoopMessage, Icon, Invoke, InvokeError, InvokeMessage, InvokeResolver, Manager, - PageLoadPayload, Runtime, Theme, WindowEvent, + EventLoopMessage, Invoke, InvokeError, InvokeMessage, InvokeResolver, Manager, PageLoadPayload, + Runtime, Theme, WindowEvent, +}; +#[cfg(desktop)] +use crate::{ + runtime::{ + menu::Menu, + window::dpi::{Position, Size}, + UserAttentionType, + }, + CursorIcon, Icon, }; use serde::Serialize; From a9b4cf20a3e9a5cc984727a56111591504e084c0 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 12 Dec 2022 17:59:39 -0800 Subject: [PATCH 102/423] fix(core): use entire request URL on dev server proxy (#5819) --- .changes/fix-dev-server-proxy-path.md | 5 +++++ core/tauri/src/manager.rs | 31 ++++++++++++++++++--------- 2 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 .changes/fix-dev-server-proxy-path.md diff --git a/.changes/fix-dev-server-proxy-path.md b/.changes/fix-dev-server-proxy-path.md new file mode 100644 index 00000000000..40fc18d041b --- /dev/null +++ b/.changes/fix-dev-server-proxy-path.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Properly proxy dev server requests with query strings and fragments. diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 1c27f243370..817ed2b0ed6 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -886,18 +886,26 @@ impl WindowManager { ) -> Box Result> + Send + Sync> { #[cfg(dev)] - let url = self.get_url().into_owned(); + let url = { + let mut url = self.get_url().as_str().to_string(); + if url.ends_with('/') { + url.pop(); + } + url + }; #[cfg(not(dev))] let manager = self.clone(); let window_origin = window_origin.to_string(); Box::new(move |request| { - let path = request - .uri() - .split(&['?', '#'][..]) - // ignore query string and fragment - .next() - .unwrap() + // use the entire URI as we are going to proxy the request + #[cfg(dev)] + let path = request.uri(); + // ignore query string and fragment + #[cfg(not(dev))] + let path = request.uri().split(&['?', '#'][..]).next().unwrap(); + + let path = path .strip_prefix("tauri://localhost") .map(|p| p.to_string()) // the `strip_prefix` only returns None when a request is made to `https://tauri.$P` on Windows @@ -910,9 +918,12 @@ impl WindowManager { #[cfg(dev)] let mut response = { use attohttpc::StatusCode; - let mut url = url.clone(); - url.set_path(&path); - let mut proxy_builder = attohttpc::get(url.as_str()).danger_accept_invalid_certs(true); + let decoded_path = percent_encoding::percent_decode(path.as_bytes()) + .decode_utf8_lossy() + .to_string(); + let url = format!("{url}{decoded_path}"); + println!("request url {url}, original path {path}, decoded {decoded_path}"); + let mut proxy_builder = attohttpc::get(&url).danger_accept_invalid_certs(true); for (name, value) in request.headers() { proxy_builder = proxy_builder.header(name, value); } From 76204b893846a04552f8f8b87ad2c9b55e1b417f Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 12 Dec 2022 17:59:51 -0800 Subject: [PATCH 103/423] feat(cli): improve local IP detection (#5817) --- .changes/improve-local-ip-detection.md | 6 +++++ tooling/cli/src/dev.rs | 31 ++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 .changes/improve-local-ip-detection.md diff --git a/.changes/improve-local-ip-detection.md b/.changes/improve-local-ip-detection.md new file mode 100644 index 00000000000..4d3d3d01e85 --- /dev/null +++ b/.changes/improve-local-ip-detection.md @@ -0,0 +1,6 @@ +--- +"cli.rs": patch +"cli.js": patch +--- + +Improve local IP address detection with user selection. diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index 97b5009fd39..fcaa8f825c6 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -11,9 +11,9 @@ use crate::{ interface::{AppInterface, DevProcess, ExitReason, Interface}, CommandExt, Result, }; -use clap::{ArgAction, Parser}; use anyhow::{bail, Context}; +use clap::{ArgAction, Parser}; use log::{error, info, warn}; use once_cell::sync::OnceCell; use shared_child::SharedChild; @@ -88,7 +88,34 @@ fn command_internal(mut options: Options) -> Result<()> { fn local_ip_address() -> &'static IpAddr { static LOCAL_IP: OnceCell = OnceCell::new(); - LOCAL_IP.get_or_init(|| local_ip_address::local_ip().expect("failed to resolve local IP address")) + LOCAL_IP.get_or_init(|| { + let addresses: Vec = local_ip_address::list_afinet_netifas() + .expect("failed to list networks") + .into_iter() + .map(|(_, ipaddr)| ipaddr) + .filter(|ipaddr| match ipaddr { + IpAddr::V4(i) => i != &Ipv4Addr::LOCALHOST, + _ => false, + }) + .collect(); + match addresses.len() { + 0 => panic!("No external IP detected."), + 1 => { + let ipaddr = addresses.first().unwrap(); + log::info!("Detected external IP {ipaddr}."); + *ipaddr + } + _ => { + let selected = dialoguer::Select::with_theme(&dialoguer::theme::ColorfulTheme::default()) + .with_prompt("What external IP should we use for your development server?") + .items(&addresses) + .default(0) + .interact() + .expect("failed to select external IP"); + *addresses.get(selected).unwrap() + } + } + }) } pub fn setup(options: &mut Options, mobile: bool) -> Result { From 3ad5e72ff147b76267c010c778a3b94bba209bb0 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 12 Dec 2022 18:00:03 -0800 Subject: [PATCH 104/423] feat(core): cache dev server proxy responses for 304 status code (#5818) --- .changes/dev-proxy-response-cache.md | 5 ++++ core/tauri/src/manager.rs | 42 ++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 .changes/dev-proxy-response-cache.md diff --git a/.changes/dev-proxy-response-cache.md b/.changes/dev-proxy-response-cache.md new file mode 100644 index 00000000000..f6327c33639 --- /dev/null +++ b/.changes/dev-proxy-response-cache.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Implement response cache on the dev server proxy, used when the server responds with status 304. diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 817ed2b0ed6..c4f2df46ba2 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -449,8 +449,7 @@ impl WindowManager { window_labels_array = serde_json::to_string(&window_labels)?, current_window_label = serde_json::to_string(&label)?, )) - .initialization_script(&self.initialization_script(&ipc_init.into_string(),&pattern_init.into_string(),&plugin_init, is_init_global)?) - ; + .initialization_script(&self.initialization_script(&ipc_init.into_string(),&pattern_init.into_string(),&plugin_init, is_init_global)?); #[cfg(feature = "isolation")] if let Pattern::Isolation { schema, .. } = self.pattern() { @@ -897,6 +896,17 @@ impl WindowManager { let manager = self.clone(); let window_origin = window_origin.to_string(); + #[cfg(dev)] + #[derive(Clone)] + struct CachedResponse { + status: http::StatusCode, + headers: http::HeaderMap, + body: Vec, + } + + #[cfg(dev)] + let response_cache = Arc::new(Mutex::new(HashMap::new())); + Box::new(move |request| { // use the entire URI as we are going to proxy the request #[cfg(dev)] @@ -929,14 +939,30 @@ impl WindowManager { } match proxy_builder.send() { Ok(r) => { - for (name, value) in r.headers() { - builder = builder.header(name, value); + let mut response_cache_ = response_cache.lock().unwrap(); + let mut response = None; + if r.status() == StatusCode::NOT_MODIFIED { + response = response_cache_.get(&url); } - let mut status = r.status(); - if status == StatusCode::NOT_MODIFIED { - status = StatusCode::OK; + let response = if let Some(r) = response { + r + } else { + let (status, headers, reader) = r.split(); + let body = reader.bytes()?; + let response = CachedResponse { + status, + headers, + body, + }; + response_cache_.insert(url.clone(), response); + response_cache_.get(&url).unwrap() + }; + for (name, value) in &response.headers { + builder = builder.header(name, value); } - builder.status(status).body(r.bytes()?)? + builder + .status(response.status) + .body(response.body.clone())? } Err(e) => { debug_eprintln!("Failed to request {}: {}", url.as_str(), e); From fc33c4218cf156460efcb7545b6ab4d8b43ce2d2 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 14 Dec 2022 12:17:43 -0300 Subject: [PATCH 105/423] fix(ci): use client-payload input to trigger cli.js publish workflow --- .github/workflows/covector-version-or-publish-next.yml | 2 +- .github/workflows/covector-version-or-publish.yml | 2 +- .github/workflows/publish-cli-js.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/covector-version-or-publish-next.yml b/.github/workflows/covector-version-or-publish-next.yml index 2c68d56f2ca..19269c16a8c 100644 --- a/.github/workflows/covector-version-or-publish-next.yml +++ b/.github/workflows/covector-version-or-publish-next.yml @@ -77,7 +77,7 @@ jobs: token: ${{ secrets.ORG_TAURI_BOT_PAT }} repository: tauri-apps/tauri event-type: publish-clijs - inputs: '{"releaseId": "${{ steps.covector.outputs.cli.js-releaseId }}" }' + client-payload: '{"releaseId": "${{ steps.covector.outputs.cli.js-releaseId }}" }' - name: Trigger cli.rs publishing workflow if: | diff --git a/.github/workflows/covector-version-or-publish.yml b/.github/workflows/covector-version-or-publish.yml index 98556c0e046..05449342731 100644 --- a/.github/workflows/covector-version-or-publish.yml +++ b/.github/workflows/covector-version-or-publish.yml @@ -124,7 +124,7 @@ jobs: token: ${{ secrets.ORG_TAURI_BOT_PAT }} repository: tauri-apps/tauri event-type: publish-clijs - inputs: '{"releaseId": "${{ steps.covector.outputs.cli.js-releaseId }}" }' + client-payload: '{"releaseId": "${{ steps.covector.outputs.cli.js-releaseId }}" }' - name: Trigger cli.rs publishing workflow if: | diff --git a/.github/workflows/publish-cli-js.yml b/.github/workflows/publish-cli-js.yml index f30248b1c58..e7f2ed6ffd0 100644 --- a/.github/workflows/publish-cli-js.yml +++ b/.github/workflows/publish-cli-js.yml @@ -397,4 +397,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.ORG_NPM_TOKEN }} - RELEASE_ID: ${{ github.event.inputs.releaseId }} + RELEASE_ID: ${{ github.event.client_payload.releaseId || github.event.inputs.releaseId }} From 8301d17ab870305ab90bb4f8071ea476f53a6873 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 15 Dec 2022 17:14:17 -0300 Subject: [PATCH 106/423] chore: use covector branch that sets the target_commitish release field --- .github/workflows/covector-version-or-publish-next.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/covector-version-or-publish-next.yml b/.github/workflows/covector-version-or-publish-next.yml index 19269c16a8c..984294921e8 100644 --- a/.github/workflows/covector-version-or-publish-next.yml +++ b/.github/workflows/covector-version-or-publish-next.yml @@ -37,7 +37,7 @@ jobs: git config --global user.email "${{ github.event.pusher.email }}" - name: covector version or publish (publish when no change files present) - uses: jbolda/covector/packages/action@covector-v0 + uses: lucasfernog/covector/packages/action@feat/target-commitish id: covector env: NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }} From d600405a1051c881d7ee50bbcac56824b8aecfd1 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 15 Dec 2022 17:21:27 -0300 Subject: [PATCH 107/423] chore(deps): bump wry to 0.23.4 --- core/tauri-runtime-wry/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 485ae76c2bd..2594efea550 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -13,7 +13,7 @@ exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" [dependencies] -wry = { version = "0.23", default-features = false, features = [ "file-drop", "protocol" ] } +wry = { version = "0.23.4", default-features = false, features = [ "file-drop", "protocol" ] } tauri-runtime = { version = "0.13.0-alpha.0", path = "../tauri-runtime" } tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } From d21c4a0a896afbff02c49afa851fbdee75d2e8e6 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 15 Dec 2022 17:56:23 -0300 Subject: [PATCH 108/423] chore: fix clippy warnings --- core/config-schema/build.rs | 4 +-- core/tauri-build/src/codegen/context.rs | 4 +-- core/tauri-build/src/lib.rs | 18 ++++++------- core/tauri-codegen/src/context.rs | 7 ++--- core/tauri-codegen/src/embedded_assets.rs | 4 +-- core/tauri-macros/src/mobile.rs | 5 +--- core/tauri-runtime-wry/build.rs | 2 +- core/tauri-runtime/build.rs | 2 +- core/tauri-utils/src/config.rs | 19 +++++++------ core/tauri-utils/src/html.rs | 10 +++---- core/tauri-utils/src/mime_type.rs | 2 +- core/tauri-utils/src/platform.rs | 8 +++--- core/tauri/build.rs | 10 +++---- core/tauri/src/api/dir.rs | 2 +- core/tauri/src/api/file/extract.rs | 4 +-- core/tauri/src/api/http.rs | 7 +++-- core/tauri/src/api/ipc.rs | 8 +++--- core/tauri/src/api/shell.rs | 2 +- core/tauri/src/app.rs | 5 ++-- core/tauri/src/endpoints.rs | 3 +-- core/tauri/src/endpoints/event.rs | 2 +- core/tauri/src/endpoints/shell.rs | 4 +-- core/tauri/src/event.rs | 13 ++++----- core/tauri/src/hooks.rs | 2 +- core/tauri/src/lib.rs | 23 +++++++--------- core/tauri/src/manager.rs | 13 +++++---- core/tauri/src/pattern.rs | 4 +-- core/tauri/src/plugin.rs | 6 ++--- core/tauri/src/scope/http.rs | 2 +- core/tauri/src/updater/core.rs | 28 +++++++++----------- core/tauri/src/updater/mod.rs | 4 +-- core/tests/app-updater/src/main.rs | 4 +-- core/tests/app-updater/tests/update.rs | 7 +++-- core/tests/restart/src/main.rs | 2 +- core/tests/restart/tests/restart.rs | 2 +- examples/commands/commands.rs | 2 +- examples/commands/main.rs | 26 +++++++++--------- examples/multiwindow/main.rs | 2 +- examples/parent-window/main.rs | 2 +- examples/streaming/main.rs | 2 +- tooling/bundler/src/bundle/linux/debian.rs | 2 +- tooling/bundler/src/bundle/path_utils.rs | 5 +++- tooling/bundler/src/bundle/settings.rs | 4 +-- tooling/cli/src/build.rs | 2 +- tooling/cli/src/dev.rs | 2 +- tooling/cli/src/helpers/updater_signature.rs | 8 +++--- tooling/cli/src/info.rs | 2 +- tooling/cli/src/interface/rust.rs | 2 +- tooling/cli/src/interface/rust/desktop.rs | 2 +- tooling/cli/src/mobile/android/build.rs | 2 +- tooling/cli/src/mobile/android/dev.rs | 2 +- tooling/cli/src/plugin/init.rs | 2 +- 52 files changed, 145 insertions(+), 166 deletions(-) diff --git a/core/config-schema/build.rs b/core/config-schema/build.rs index fd0097699e5..31fc2aba9a1 100644 --- a/core/config-schema/build.rs +++ b/core/config-schema/build.rs @@ -17,8 +17,8 @@ pub fn main() -> Result<(), Box> { crate_dir.join("schema.json"), crate_dir.join("../../tooling/cli/schema.json"), ] { - let mut schema_file = BufWriter::new(File::create(&file)?); - write!(schema_file, "{}", schema_str)?; + let mut schema_file = BufWriter::new(File::create(file)?); + write!(schema_file, "{schema_str}")?; } Ok(()) diff --git a/core/tauri-build/src/codegen/context.rs b/core/tauri-build/src/codegen/context.rs index 2d681229d27..a21591fe308 100644 --- a/core/tauri-build/src/codegen/context.rs +++ b/core/tauri-build/src/codegen/context.rs @@ -86,7 +86,7 @@ impl CodegenContext { pub fn build(self) -> PathBuf { match self.try_build() { Ok(out) => out, - Err(error) => panic!("Error found during Codegen::build: {}", error), + Err(error) => panic!("Error found during Codegen::build: {error}"), } } @@ -161,7 +161,7 @@ impl CodegenContext { ) })?; - writeln!(file, "{}", code).with_context(|| { + writeln!(file, "{code}").with_context(|| { format!( "Unable to write tokenstream to out file during tauri-build {}", out.display() diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 40b2dd370df..09469f9d965 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -35,8 +35,8 @@ fn copy_file(from: impl AsRef, to: impl AsRef) -> Result<()> { Ok(()) } -fn copy_binaries<'a>( - binaries: ResourcePaths<'a>, +fn copy_binaries( + binaries: ResourcePaths, target_triple: &str, path: &Path, package_name: Option<&String>, @@ -48,7 +48,7 @@ fn copy_binaries<'a>( .file_name() .expect("failed to extract external binary filename") .to_string_lossy() - .replace(&format!("-{}", target_triple), ""); + .replace(&format!("-{target_triple}"), ""); if package_name.map_or(false, |n| n == &file_name) { return Err(anyhow::anyhow!( @@ -72,7 +72,7 @@ fn copy_resources(resources: ResourcePaths<'_>, path: &Path) -> Result<()> { let src = src?; println!("cargo:rerun-if-changed={}", src.display()); let dest = path.join(resource_relpath(&src)); - copy_file(&src, &dest)?; + copy_file(&src, dest)?; } Ok(()) } @@ -90,7 +90,7 @@ fn has_feature(feature: &str) -> bool { // `alias` must be a snake case string. fn cfg_alias(alias: &str, has_feature: bool) { if has_feature { - println!("cargo:rustc-cfg={}", alias); + println!("cargo:rustc-cfg={alias}"); } } @@ -179,8 +179,8 @@ impl Attributes { /// This is typically desirable when running inside a build script; see [`try_build`] for no panics. pub fn build() { if let Err(error) = try_build(Attributes::default()) { - let error = format!("{:#}", error); - println!("{}", error); + let error = format!("{error:#}"); + println!("{error}"); if error.starts_with("unknown field") { print!("found an unknown configuration field. This usually means that you are using a CLI version that is newer than `tauri-build` and is incompatible. "); println!( @@ -229,7 +229,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> { } } domain.pop(); - println!("cargo:rustc-env=TAURI_ANDROID_DOMAIN={}", domain); + println!("cargo:rustc-env=TAURI_ANDROID_DOMAIN={domain}"); cfg_alias("dev", !has_feature("custom-protocol")); @@ -288,7 +288,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> { let target_triple = std::env::var("TARGET").unwrap(); - println!("cargo:rustc-env=TAURI_TARGET_TRIPLE={}", target_triple); + println!("cargo:rustc-env=TAURI_TARGET_TRIPLE={target_triple}"); let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); // TODO: far from ideal, but there's no other way to get the target dir, see diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index eb4036f3842..c3ac02b56b3 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -142,7 +142,7 @@ pub fn context_codegen(data: ContextData) -> Result Result { let dir = config_parent.join(dir); if !dir.exists() { - panic!( - "The isolation application path is set to `{:?}` but it does not exist", - dir - ) + panic!("The isolation application path is set to `{dir:?}` but it does not exist") } let mut sets_isolation_hook = false; diff --git a/core/tauri-codegen/src/embedded_assets.rs b/core/tauri-codegen/src/embedded_assets.rs index 4f5559570d6..27282696973 100644 --- a/core/tauri-codegen/src/embedded_assets.rs +++ b/core/tauri-codegen/src/embedded_assets.rs @@ -343,14 +343,14 @@ impl EmbeddedAssets { let mut hex = String::with_capacity(2 * bytes.len()); for b in bytes { - write!(hex, "{:02x}", b).map_err(EmbeddedAssetsError::Hex)?; + write!(hex, "{b:02x}").map_err(EmbeddedAssetsError::Hex)?; } hex }; // use the content hash to determine filename, keep extensions that exist let out_path = if let Some(ext) = path.extension().and_then(|e| e.to_str()) { - out_dir.join(format!("{}.{}", hash, ext)) + out_dir.join(format!("{hash}.{ext}")) } else { out_dir.join(hash) }; diff --git a/core/tauri-macros/src/mobile.rs b/core/tauri-macros/src/mobile.rs index 99d4efdb07b..44bd177fb51 100644 --- a/core/tauri-macros/src/mobile.rs +++ b/core/tauri-macros/src/mobile.rs @@ -19,10 +19,7 @@ fn get_env_var String>( error.replace( syn::Error::new( function.span(), - format!( - "`{}` env var not set, do you have a build script with tauri-build?", - name, - ), + format!("`{name}` env var not set, do you have a build script with tauri-build?",), ) .into_compile_error(), ); diff --git a/core/tauri-runtime-wry/build.rs b/core/tauri-runtime-wry/build.rs index f8d92f3f750..c0ac84b7f2c 100644 --- a/core/tauri-runtime-wry/build.rs +++ b/core/tauri-runtime-wry/build.rs @@ -6,7 +6,7 @@ // `alias` must be a snake case string. fn alias(alias: &str, has_feature: bool) { if has_feature { - println!("cargo:rustc-cfg={}", alias); + println!("cargo:rustc-cfg={alias}"); } } diff --git a/core/tauri-runtime/build.rs b/core/tauri-runtime/build.rs index f8d92f3f750..c0ac84b7f2c 100644 --- a/core/tauri-runtime/build.rs +++ b/core/tauri-runtime/build.rs @@ -6,7 +6,7 @@ // `alias` must be a snake case string. fn alias(alias: &str, has_feature: bool) { if has_feature { - println!("cargo:rustc-cfg={}", alias); + println!("cargo:rustc-cfg={alias}"); } } diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 5650b2c64e4..6ae0deb36e3 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -55,7 +55,7 @@ pub enum WindowUrl { impl fmt::Display for WindowUrl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::External(url) => write!(f, "{}", url), + Self::External(url) => write!(f, "{url}"), Self::App(path) => write!(f, "{}", path.display()), } } @@ -125,7 +125,7 @@ impl<'de> Deserialize<'de> for BundleType { "app" => Ok(Self::App), "dmg" => Ok(Self::Dmg), "updater" => Ok(Self::Updater), - _ => Err(DeError::custom(format!("unknown bundle target '{}'", s))), + _ => Err(DeError::custom(format!("unknown bundle target '{s}'"))), } } } @@ -223,7 +223,7 @@ impl<'de> Deserialize<'de> for BundleTarget { match BundleTargetInner::deserialize(deserializer)? { BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All), - BundleTargetInner::All(t) => Err(DeError::custom(format!("invalid bundle type {}", t))), + BundleTargetInner::All(t) => Err(DeError::custom(format!("invalid bundle type {t}"))), BundleTargetInner::List(l) => Ok(Self::List(l)), BundleTargetInner::One(t) => Ok(Self::One(t)), } @@ -988,7 +988,7 @@ impl CspDirectiveSources { /// Whether the given source is configured on this directive or not. pub fn contains(&self, source: &str) -> bool { match self { - Self::Inline(s) => s.contains(&format!("{} ", source)) || s.contains(&format!(" {}", source)), + Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")), Self::List(l) => l.contains(&source.into()), } } @@ -1054,7 +1054,7 @@ impl From for HashMap { impl Display for Csp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Policy(s) => write!(f, "{}", s), + Self::Policy(s) => write!(f, "{s}"), Self::DirectiveMap(m) => { let len = m.len(); let mut i = 0; @@ -2349,8 +2349,7 @@ impl<'de> Deserialize<'de> for WindowsUpdateInstallMode { "quiet" => Ok(Self::Quiet), "passive" => Ok(Self::Passive), _ => Err(DeError::custom(format!( - "unknown update install mode '{}'", - s + "unknown update install mode '{s}'" ))), } } @@ -2506,7 +2505,7 @@ pub enum AppUrl { impl std::fmt::Display for AppUrl { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Url(url) => write!(f, "{}", url), + Self::Url(url) => write!(f, "{url}"), Self::Files(files) => write!(f, "{}", serde_json::to_string(files).unwrap()), } } @@ -2645,9 +2644,9 @@ impl<'d> serde::Deserialize<'d> for PackageVersion { let path = PathBuf::from(value); if path.exists() { let json_str = read_to_string(&path) - .map_err(|e| DeError::custom(format!("failed to read version JSON file: {}", e)))?; + .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?; let package_json: serde_json::Value = serde_json::from_str(&json_str) - .map_err(|e| DeError::custom(format!("failed to read version JSON file: {}", e)))?; + .map_err(|e| DeError::custom(format!("failed to read version JSON file: {e}")))?; if let Some(obj) = package_json.as_object() { let version = obj .get("version") diff --git a/core/tauri-utils/src/html.rs b/core/tauri-utils/src/html.rs index b8744261b81..77bc8b800d2 100644 --- a/core/tauri-utils/src/html.rs +++ b/core/tauri-utils/src/html.rs @@ -37,7 +37,7 @@ fn serialize_node_ref_internal( traversal_scope: TraversalScope, ) -> crate::Result<()> { match (traversal_scope, node.data()) { - (ref scope, &NodeData::Element(ref element)) => { + (ref scope, NodeData::Element(element)) => { if *scope == TraversalScope::IncludeNode { let attrs = element.attributes.borrow(); @@ -82,16 +82,16 @@ fn serialize_node_ref_internal( (TraversalScope::ChildrenOnly(_), _) => Ok(()), - (TraversalScope::IncludeNode, &NodeData::Doctype(ref doctype)) => { + (TraversalScope::IncludeNode, NodeData::Doctype(doctype)) => { serializer.write_doctype(&doctype.name).map_err(Into::into) } - (TraversalScope::IncludeNode, &NodeData::Text(ref text)) => { + (TraversalScope::IncludeNode, NodeData::Text(text)) => { serializer.write_text(&text.borrow()).map_err(Into::into) } - (TraversalScope::IncludeNode, &NodeData::Comment(ref text)) => { + (TraversalScope::IncludeNode, NodeData::Comment(text)) => { serializer.write_comment(&text.borrow()).map_err(Into::into) } - (TraversalScope::IncludeNode, &NodeData::ProcessingInstruction(ref contents)) => { + (TraversalScope::IncludeNode, NodeData::ProcessingInstruction(contents)) => { let contents = contents.borrow(); serializer .write_processing_instruction(&contents.0, &contents.1) diff --git a/core/tauri-utils/src/mime_type.rs b/core/tauri-utils/src/mime_type.rs index be2471ea953..60bd08f0e00 100644 --- a/core/tauri-utils/src/mime_type.rs +++ b/core/tauri-utils/src/mime_type.rs @@ -39,7 +39,7 @@ impl std::fmt::Display for MimeType { MimeType::Svg => "image/svg+xml", MimeType::Mp4 => "video/mp4", }; - write!(f, "{}", mime) + write!(f, "{mime}") } } diff --git a/core/tauri-utils/src/platform.rs b/core/tauri-utils/src/platform.rs index 96614ffa21d..d16f275e8b7 100644 --- a/core/tauri-utils/src/platform.rs +++ b/core/tauri-utils/src/platform.rs @@ -133,10 +133,10 @@ pub fn target_triple() -> crate::Result { return Err(crate::Error::Environment); }; - format!("{}-{}", os, env) + format!("{os}-{env}") }; - Ok(format!("{}-{}", arch, os)) + Ok(format!("{arch}-{os}")) } /// Computes the resource directory of the current environment. @@ -157,8 +157,8 @@ pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result bool { // `alias` must be a snake case string. fn alias(alias: &str, has_feature: bool) { if has_feature { - println!("cargo:rustc-cfg={}", alias); + println!("cargo:rustc-cfg={alias}"); } } @@ -136,8 +136,8 @@ fn main() { let checked_features_out_path = Path::new(&std::env::var("OUT_DIR").unwrap()).join("checked_features"); std::fs::write( - &checked_features_out_path, - &CHECKED_FEATURES.get().unwrap().lock().unwrap().join(","), + checked_features_out_path, + CHECKED_FEATURES.get().unwrap().lock().unwrap().join(","), ) .expect("failed to write checked_features file"); } @@ -152,14 +152,14 @@ fn main() { // // Note that both `module` and `apis` strings must be written in kebab case. fn alias_module(module: &str, apis: &[&str], api_all: bool) { - let all_feature_name = format!("{}-all", module); + let all_feature_name = format!("{module}-all"); let all = has_feature(&all_feature_name) || api_all; alias(&all_feature_name.to_snake_case(), all); let mut any = all; for api in apis { - let has = has_feature(&format!("{}-{}", module, api)) || all; + let has = has_feature(&format!("{module}-{api}")) || all; alias( &format!("{}_{}", AsSnakeCase(module), AsSnakeCase(api)), has, diff --git a/core/tauri/src/api/dir.rs b/core/tauri/src/api/dir.rs index 771a8925e2b..77aa2323a09 100644 --- a/core/tauri/src/api/dir.rs +++ b/core/tauri/src/api/dir.rs @@ -226,7 +226,7 @@ mod test { fn check_test_dir() { // create a callback closure that takes in a TempDir type and prints it. let callback = |td: &tempfile::TempDir| { - println!("{:?}", td); + println!("{td:?}"); }; // execute the with_temp_dir function on the callback diff --git a/core/tauri/src/api/file/extract.rs b/core/tauri/src/api/file/extract.rs index 28e2ad9a472..9e2eb76e8b3 100644 --- a/core/tauri/src/api/file/extract.rs +++ b/core/tauri/src/api/file/extract.rs @@ -150,7 +150,7 @@ impl<'a, R: std::fmt::Debug + Read + Seek> std::fmt::Debug for Extract<'a, R> { impl<'a, R: Read + Seek> Extract<'a, R> { /// Create archive from reader. pub fn from_cursor(mut reader: R, archive_format: ArchiveFormat) -> Extract<'a, R> { - if reader.seek(io::SeekFrom::Start(0)).is_err() { + if reader.rewind().is_err() { #[cfg(debug_assertions)] eprintln!("Could not seek to start of the file"); } @@ -245,7 +245,7 @@ impl<'a, R: Read + Seek> Extract<'a, R> { // such as: τê▒Σ║ñµÿô.app/, that does not work as expected. // Here we require the file name must be a valid UTF-8. let file_name = String::from_utf8(file.name_raw().to_vec())?; - let out_path = into_dir.join(&file_name); + let out_path = into_dir.join(file_name); if file.is_dir() { fs::create_dir_all(&out_path)?; } else { diff --git a/core/tauri/src/api/http.rs b/core/tauri/src/api/http.rs index 70ab403882b..7b9614c1915 100644 --- a/core/tauri/src/api/http.rs +++ b/core/tauri/src/api/http.rs @@ -368,7 +368,7 @@ impl TryFrom for Vec { type Error = crate::api::Error; fn try_from(file: FilePart) -> crate::api::Result { let bytes = match file { - FilePart::Path(path) => std::fs::read(&path)?, + FilePart::Path(path) => std::fs::read(path)?, FilePart::Contents(bytes) => bytes, }; Ok(bytes) @@ -441,8 +441,7 @@ impl<'de> Deserialize<'de> for HeaderMap { headers.insert(key, value); } else { return Err(serde::de::Error::custom(format!( - "invalid header `{}` `{}`", - key, value + "invalid header `{key}` `{value}`" ))); } } @@ -662,7 +661,7 @@ impl Response { let data = match self.0 { ResponseType::Json => self.1.json()?, ResponseType::Text => Value::String(self.1.text()?), - ResponseType::Binary => serde_json::to_value(&self.1.bytes()?)?, + ResponseType::Binary => serde_json::to_value(self.1.bytes()?)?, }; Ok(ResponseData { diff --git a/core/tauri/src/api/ipc.rs b/core/tauri/src/api/ipc.rs index cbffb02e095..f09be323287 100644 --- a/core/tauri/src/api/ipc.rs +++ b/core/tauri/src/api/ipc.rs @@ -242,22 +242,22 @@ mod test { } let raw_str = "T".repeat(MIN_JSON_PARSE_LEN); - assert_eq!(serialize_js(&raw_str).unwrap(), format!("\"{}\"", raw_str)); + assert_eq!(serialize_js(&raw_str).unwrap(), format!("\"{raw_str}\"")); assert_eq!( serialize_js(&JsonObj { value: raw_str.clone() }) .unwrap(), - format!("JSON.parse('{{\"value\":\"{}\"}}')", raw_str) + format!("JSON.parse('{{\"value\":\"{raw_str}\"}}')") ); assert_eq!( serialize_js(&JsonObj { - value: format!("\"{}\"", raw_str) + value: format!("\"{raw_str}\"") }) .unwrap(), - format!("JSON.parse('{{\"value\":\"\\\\\"{}\\\\\"\"}}')", raw_str) + format!("JSON.parse('{{\"value\":\"\\\\\"{raw_str}\\\\\"\"}}')") ); let dangerous_json = RawValue::from_string( diff --git a/core/tauri/src/api/shell.rs b/core/tauri/src/api/shell.rs index 9353e2f3f2b..6ca16cc75bf 100644 --- a/core/tauri/src/api/shell.rs +++ b/core/tauri/src/api/shell.rs @@ -112,5 +112,5 @@ pub fn open>( ) -> crate::api::Result<()> { scope .open(path.as_ref(), with) - .map_err(|err| crate::api::Error::Shell(format!("failed to open: {}", err))) + .map_err(|err| crate::api::Error::Shell(format!("failed to open: {err}"))) } diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index b834284d9ee..22463efa978 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -868,7 +868,7 @@ impl App { self.runtime.take().unwrap().run(move |event| match event { RuntimeRunEvent::Ready => { if let Err(e) = setup(&mut self) { - panic!("Failed to setup app: {}", e); + panic!("Failed to setup app: {e}"); } on_event_loop_event( &app_handle, @@ -1290,8 +1290,7 @@ impl Builder { let type_name = std::any::type_name::(); assert!( self.state.set(state), - "state for type '{}' is already being managed", - type_name + "state for type '{type_name}' is already being managed", ); self } diff --git a/core/tauri/src/endpoints.rs b/core/tauri/src/endpoints.rs index 9dd2ca97489..b776d813f50 100644 --- a/core/tauri/src/endpoints.rs +++ b/core/tauri/src/endpoints.rs @@ -238,8 +238,7 @@ pub(crate) fn handle( if let Some(unknown_variant_name) = s.next() { if unknown_variant_name == module { return resolver.reject(format!( - "The `{}` module is not enabled. You must enable one of its APIs in the allowlist.", - module + "The `{module}` module is not enabled. You must enable one of its APIs in the allowlist.", )); } else if module == "Window" { return resolver.reject(window::into_allowlist_error(unknown_variant_name).to_string()); diff --git a/core/tauri/src/endpoints/event.rs b/core/tauri/src/endpoints/event.rs index 6d21d9316b0..8dd42f7af7b 100644 --- a/core/tauri/src/endpoints/event.rs +++ b/core/tauri/src/endpoints/event.rs @@ -138,7 +138,7 @@ impl Cmd { serde_json::to_string(&p) .map_err(|e| { #[cfg(debug_assertions)] - eprintln!("{}", e); + eprintln!("{e}"); e }) .ok() diff --git a/core/tauri/src/endpoints/shell.rs b/core/tauri/src/endpoints/shell.rs index 57aaf7db916..c85168549df 100644 --- a/core/tauri/src/endpoints/shell.rs +++ b/core/tauri/src/endpoints/shell.rs @@ -135,7 +135,7 @@ impl Cmd { Ok(cmd) => cmd, Err(e) => { #[cfg(debug_assertions)] - eprintln!("{}", e); + eprintln!("{e}"); return Err(crate::Error::ProgramNotAllowed(PathBuf::from(program)).into_anyhow()); } } @@ -154,7 +154,7 @@ impl Cmd { if let Some(encoding) = crate::api::process::Encoding::for_label(encoding.as_bytes()) { command = command.encoding(encoding); } else { - return Err(anyhow::anyhow!(format!("unknown encoding {}", encoding))); + return Err(anyhow::anyhow!(format!("unknown encoding {encoding}"))); } } let (mut rx, child) = command.spawn()?; diff --git a/core/tauri/src/event.rs b/core/tauri/src/event.rs index d19fed996eb..1605be2bc2b 100644 --- a/core/tauri/src/event.rs +++ b/core/tauri/src/event.rs @@ -232,7 +232,7 @@ mod test { // dummy event handler function fn event_fn(s: Event) { - println!("{:?}", s); + println!("{s:?}"); } proptest! { @@ -304,18 +304,15 @@ pub fn unlisten_js(listeners_object_name: String, event_name: String, event_id: format!( " (function () {{ - const listeners = (window['{listeners}'] || {{}})['{event_name}'] + const listeners = (window['{listeners_object_name}'] || {{}})['{event_name}'] if (listeners) {{ - const index = window['{listeners}']['{event_name}'].findIndex(e => e.id === {event_id}) + const index = window['{listeners_object_name}']['{event_name}'].findIndex(e => e.id === {event_id}) if (index > -1) {{ - window['{listeners}']['{event_name}'].splice(index, 1) + window['{listeners_object_name}']['{event_name}'].splice(index, 1) }} }} }})() ", - listeners = listeners_object_name, - event_name = event_name, - event_id = event_id, ) } @@ -353,7 +350,7 @@ pub fn listen_js( event_id = event_id, window_label = if let Some(l) = window_label { crate::runtime::window::assert_label_is_valid(&l); - format!("'{}'", l) + format!("'{l}'") } else { "null".to_owned() }, diff --git a/core/tauri/src/hooks.rs b/core/tauri/src/hooks.rs index 8d992b770af..bd2337d69c7 100644 --- a/core/tauri/src/hooks.rs +++ b/core/tauri/src/hooks.rs @@ -99,7 +99,7 @@ impl InvokeError { /// Create an [`InvokeError`] as a string of the [`anyhow::Error`] message. #[inline(always)] pub fn from_anyhow(error: anyhow::Error) -> Self { - Self(JsonValue::String(format!("{:#}", error))) + Self(JsonValue::String(format!("{error:#}"))) } } diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index f4b430a3209..e14b1bd5c17 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -470,8 +470,7 @@ impl TryFrom for runtime::Icon { }) } _ => panic!( - "image `{}` extension not supported; please file a Tauri feature request. `png` or `ico` icons are supported with the `icon-png` and `icon-ico` feature flags", - extension + "image `{extension}` extension not supported; please file a Tauri feature request. `png` or `ico` icons are supported with the `icon-png` and `icon-ico` feature flags" ), } } @@ -875,8 +874,8 @@ mod tests { let lib_code = read_to_string(manifest_dir.join("src/lib.rs")).expect("failed to read lib.rs"); for f in get_manifest().features.keys() { - if !(f.starts_with("__") || f == "default" || lib_code.contains(&format!("*{}**", f))) { - panic!("Feature {} is not documented", f); + if !(f.starts_with("__") || f == "default" || lib_code.contains(&format!("*{f}**"))) { + panic!("Feature {f} is not documented"); } } } @@ -888,8 +887,7 @@ mod tests { for checked_feature in checked_features { if !manifest.features.iter().any(|(f, _)| f == checked_feature) { panic!( - "Feature {} was checked in the alias build step but it does not exist in core/tauri/Cargo.toml", - checked_feature + "Feature {checked_feature} was checked in the alias build step but it does not exist in core/tauri/Cargo.toml" ); } } @@ -925,24 +923,21 @@ mod tests { let module = module_all_feature.replace("-all", ""); assert!( checked_features.contains(&module_all_feature.as_str()), - "`{}` is not aliased", - module + "`{module}` is not aliased" ); - let module_prefix = format!("{}-", module); + let module_prefix = format!("{module}-"); // we assume that module features are the ones that start with `-` // though it's not 100% accurate, we have an allowed list to fix it let module_features = manifest .features - .iter() - .map(|(f, _)| f) + .keys() .filter(|f| f.starts_with(&module_prefix)); for module_feature in module_features { assert!( allowed.contains(&module_feature.as_str()) || checked_features.contains(&module_feature.as_str()), - "`{}` is not aliased", - module_feature + "`{module_feature}` is not aliased" ); } } @@ -970,7 +965,7 @@ mod test_utils { fn check_spawn_task(task in "[a-z]+") { // create dummy task function let dummy_task = async move { - format!("{}-run-dummy-task", task); + format!("{task}-run-dummy-task"); }; // call spawn crate::async_runtime::spawn(dummy_task); diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index c4f2df46ba2..35a40c2e85c 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -179,7 +179,7 @@ fn replace_csp_nonce( if !(nonces.is_empty() && hashes.is_empty()) { let nonce_sources = nonces .into_iter() - .map(|n| format!("'nonce-{}'", n)) + .map(|n| format!("'nonce-{n}'")) .collect::>(); let sources = csp.entry(directive.into()).or_insert_with(Default::default); let self_source = "'self'".to_string(); @@ -487,7 +487,7 @@ impl WindowManager { window_url.scheme(), window_url.host().unwrap(), if let Some(port) = window_url.port() { - format!(":{}", port) + format!(":{port}") } else { "".into() } @@ -713,8 +713,8 @@ impl WindowManager { } = &self.inner.pattern { let assets = assets.clone(); - let schema_ = schema.clone(); - let url_base = format!("{}://localhost", schema_); + let _schema_ = schema.clone(); + let url_base = format!("{schema}://localhost"); let aes_gcm_key = *crypto_keys.aes_gcm().raw(); pending.register_uri_scheme_protocol(schema, move |request| { @@ -813,7 +813,7 @@ impl WindowManager { let asset_response = assets .get(&path.as_str().into()) .or_else(|| { - eprintln!("Asset `{}` not found; fallback to {}.html", path, path); + eprintln!("Asset `{path}` not found; fallback to {path}.html"); let fallback = format!("{}.html", path.as_str()).into(); let asset = assets.get(&fallback); asset_path = fallback; @@ -1500,8 +1500,7 @@ fn on_window_event( let windows = windows_map.values(); for window in windows { window.eval(&format!( - r#"(function () {{ const metadata = window.__TAURI_METADATA__; if (metadata != null) {{ metadata.__windows = window.__TAURI_METADATA__.__windows.filter(w => w.label !== "{}"); }} }})()"#, - label + r#"(function () {{ const metadata = window.__TAURI_METADATA__; if (metadata != null) {{ metadata.__windows = window.__TAURI_METADATA__.__windows.filter(w => w.label !== "{label}"); }} }})()"#, ))?; } } diff --git a/core/tauri/src/pattern.rs b/core/tauri/src/pattern.rs index fa86ad7535f..25bd1da4f24 100644 --- a/core/tauri/src/pattern.rs +++ b/core/tauri/src/pattern.rs @@ -87,8 +87,8 @@ pub(crate) struct PatternJavascript { #[allow(dead_code)] pub(crate) fn format_real_schema(schema: &str) -> String { if cfg!(windows) || cfg!(target_os = "android") { - format!("https://{}.localhost", schema) + format!("https://{schema}.localhost") } else { - format!("{}://localhost", schema) + format!("{schema}://localhost") } } diff --git a/core/tauri/src/plugin.rs b/core/tauri/src/plugin.rs index d7aac7b8c91..6c42d9927d9 100644 --- a/core/tauri/src/plugin.rs +++ b/core/tauri/src/plugin.rs @@ -545,7 +545,7 @@ impl PluginStore { .values() .filter_map(|p| p.initialization_script()) .fold(String::new(), |acc, script| { - format!("{}\n(function () {{ {} }})();", acc, script) + format!("{acc}\n(function () {{ {script} }})();") }) } @@ -586,9 +586,7 @@ impl PluginStore { .unwrap_or_else(String::new); plugin.extend_api(invoke); } else { - invoke - .resolver - .reject(format!("plugin {} not found", target)); + invoke.resolver.reject(format!("plugin {target} not found")); } } } diff --git a/core/tauri/src/scope/http.rs b/core/tauri/src/scope/http.rs index 627b96d17cb..e96eb5831a0 100644 --- a/core/tauri/src/scope/http.rs +++ b/core/tauri/src/scope/http.rs @@ -21,7 +21,7 @@ impl Scope { .iter() .map(|url| { glob::Pattern::new(url.as_str()) - .unwrap_or_else(|_| panic!("scoped URL is not a valid glob pattern: `{}`", url)) + .unwrap_or_else(|_| panic!("scoped URL is not a valid glob pattern: `{url}`")) }) .collect(), } diff --git a/core/tauri/src/updater/core.rs b/core/tauri/src/updater/core.rs index 57085bcfdc0..e263ccd7c63 100644 --- a/core/tauri/src/updater/core.rs +++ b/core/tauri/src/updater/core.rs @@ -96,7 +96,7 @@ impl<'de> Deserialize<'de> for RemoteRelease { let pub_date = if let Some(date) = release.pub_date { Some( OffsetDateTime::parse(&date, &time::format_description::well_known::Rfc3339) - .map_err(|e| DeError::custom(format!("invalid value for `pub_date`: {}", e)))?, + .map_err(|e| DeError::custom(format!("invalid value for `pub_date`: {e}")))?, ) } else { None @@ -336,7 +336,7 @@ impl UpdateBuilder { (target.clone(), target) } else { let target = get_updater_target().ok_or(Error::UnsupportedOs)?; - (target.to_string(), format!("{}-{}", target, arch)) + (target.to_string(), format!("{target}-{arch}")) }; // Get the extract_path from the provided executable_path @@ -1049,14 +1049,13 @@ mod test { format!( r#" {{ - "name": "v{}", + "name": "v{version}", "notes": "This is the latest version! Once updated you shouldn't see this prompt.", "pub_date": "2020-06-25T14:14:19Z", - "signature": "{}", - "url": "{}" + "signature": "{public_signature}", + "url": "{download_url}" }} - "#, - version, public_signature, download_url + "# ) } @@ -1069,15 +1068,14 @@ mod test { format!( r#" {{ - "name": "v{}", + "name": "v{version}", "notes": "This is the latest version! Once updated you shouldn't see this prompt.", "pub_date": "2020-06-25T14:14:19Z", - "signature": "{}", - "url": "{}", - "with_elevated_task": {} + "signature": "{public_signature}", + "url": "{download_url}", + "with_elevated_task": {with_elevated_task} }} - "#, - version, public_signature, download_url, with_elevated_task + "# ) } @@ -1515,7 +1513,7 @@ mod test { }"#; fn missing_field_error(field: &str) -> String { - format!("the `{}` field was not set on the updater response", field) + format!("the `{field}` field was not set on the updater response") } let test_cases = [ @@ -1547,7 +1545,7 @@ mod test { .target("test-target") .build()); if let Err(e) = check_update { - println!("ERROR: {}, expected: {}", e, error); + println!("ERROR: {e}, expected: {error}"); assert!(e.to_string().contains(&error)); } else { panic!("unexpected Ok response"); diff --git a/core/tauri/src/updater/mod.rs b/core/tauri/src/updater/mod.rs index e235d8e49dc..240c06bc8a0 100644 --- a/core/tauri/src/updater/mod.rs +++ b/core/tauri/src/updater/mod.rs @@ -109,7 +109,7 @@ pub const EVENT_STATUS_UPTODATE: &str = "UPTODATE"; /// Gets the target string used on the updater. pub fn target() -> Option { if let (Some(target), Some(arch)) = (core::get_updater_target(), core::get_updater_arch()) { - Some(format!("{}-{}", target, arch)) + Some(format!("{target}-{arch}")) } else { None } @@ -565,7 +565,7 @@ async fn prompt_for_install( // something more conventional. let should_install = ask( parent_window, - format!(r#"A new version of {} is available! "#, app_name), + format!(r#"A new version of {app_name} is available! "#), format!( r#"{} {} is now available -- you have {}. diff --git a/core/tests/app-updater/src/main.rs b/core/tests/app-updater/src/main.rs index d957fa1fd23..f0ffd2a758a 100644 --- a/core/tests/app-updater/src/main.rs +++ b/core/tests/app-updater/src/main.rs @@ -15,13 +15,13 @@ fn main() { match handle.updater().check().await { Ok(update) => { if let Err(e) = update.download_and_install().await { - println!("{}", e); + println!("{e}"); std::process::exit(1); } std::process::exit(0); } Err(e) => { - println!("{}", e); + println!("{e}"); std::process::exit(1); } } diff --git a/core/tests/app-updater/tests/update.rs b/core/tests/app-updater/tests/update.rs index 0f38abece5c..c7b147cdc83 100644 --- a/core/tests/app-updater/tests/update.rs +++ b/core/tests/app-updater/tests/update.rs @@ -55,12 +55,12 @@ fn get_cli_bin_path(cli_dir: &Path, debug: bool) -> Option { } fn build_app(cli_bin_path: &Path, cwd: &Path, config: &Config, bundle_updater: bool) { - let mut command = Command::new(&cli_bin_path); + let mut command = Command::new(cli_bin_path); command .args(["build", "--debug", "--verbose"]) .arg("--config") .arg(serde_json::to_string(config).unwrap()) - .current_dir(&cwd); + .current_dir(cwd); #[cfg(windows)] command.args(["--bundles", "msi"]); @@ -88,8 +88,7 @@ fn build_app(cli_bin_path: &Path, cwd: &Path, config: &Config, bundle_updater: b #[cfg(target_os = "linux")] fn bundle_path(root_dir: &Path, version: &str) -> PathBuf { root_dir.join(format!( - "target/debug/bundle/appimage/app-updater_{}_amd64.AppImage", - version + "target/debug/bundle/appimage/app-updater_{version}_amd64.AppImage" )) } diff --git a/core/tests/restart/src/main.rs b/core/tests/restart/src/main.rs index 3a6848c5753..b03243562af 100644 --- a/core/tests/restart/src/main.rs +++ b/core/tests/restart/src/main.rs @@ -24,7 +24,7 @@ fn main() { env.args.clear(); tauri::api::process::restart(&env) } - Some(invalid) => panic!("only argument `restart` is allowed, {} is invalid", invalid), + Some(invalid) => panic!("only argument `restart` is allowed, {invalid} is invalid"), None => {} }; } diff --git a/core/tests/restart/tests/restart.rs b/core/tests/restart/tests/restart.rs index 3a6662ec1e2..db458a4ebaa 100644 --- a/core/tests/restart/tests/restart.rs +++ b/core/tests/restart/tests/restart.rs @@ -32,7 +32,7 @@ fn symlink_runner(create_symlinks: impl Fn(&Path) -> io::Result) -> Res if cfg!(windows) { compiled_binary.set_extension("exe"); } - println!("{:?}", compiled_binary); + println!("{compiled_binary:?}"); // set up all the temporary file paths let temp = tempfile::TempDir::new()?; diff --git a/examples/commands/commands.rs b/examples/commands/commands.rs index 3689569c88e..f9c1c4442d2 100644 --- a/examples/commands/commands.rs +++ b/examples/commands/commands.rs @@ -18,7 +18,7 @@ pub fn resolver(_argument: String) {} #[command] pub fn simple_command(the_argument: String) { - println!("{}", the_argument); + println!("{the_argument}"); } #[command] diff --git a/examples/commands/main.rs b/examples/commands/main.rs index 5821eacfad4..81409d4011a 100644 --- a/examples/commands/main.rs +++ b/examples/commands/main.rs @@ -37,12 +37,12 @@ fn window_label(window: Window) { #[command] async fn async_simple_command(the_argument: String) { - println!("{}", the_argument); + println!("{the_argument}"); } #[command(rename_all = "snake_case")] async fn async_simple_command_snake(the_argument: String) { - println!("{}", the_argument); + println!("{the_argument}"); } #[command] @@ -57,7 +57,7 @@ async fn async_stateful_command( #[command(async)] fn future_simple_command(the_argument: String) -> impl std::future::Future { - println!("{}", the_argument); + println!("{the_argument}"); std::future::ready(()) } @@ -65,7 +65,7 @@ fn future_simple_command(the_argument: String) -> impl std::future::Future impl std::future::Future { - println!("{}", the_argument); + println!("{the_argument}"); std::future::ready(the_argument) } @@ -73,7 +73,7 @@ fn future_simple_command_with_return( fn future_simple_command_with_result( the_argument: String, ) -> impl std::future::Future> { - println!("{}", the_argument); + println!("{the_argument}"); std::future::ready(Ok(the_argument)) } @@ -93,7 +93,7 @@ fn force_async_with_result(the_argument: &str) -> Result<&str, MyError> { #[command(async, rename_all = "snake_case")] fn future_simple_command_snake(the_argument: String) -> impl std::future::Future { - println!("{}", the_argument); + println!("{the_argument}"); std::future::ready(()) } @@ -101,7 +101,7 @@ fn future_simple_command_snake(the_argument: String) -> impl std::future::Future fn future_simple_command_with_return_snake( the_argument: String, ) -> impl std::future::Future { - println!("{}", the_argument); + println!("{the_argument}"); std::future::ready(the_argument) } @@ -109,7 +109,7 @@ fn future_simple_command_with_return_snake( fn future_simple_command_with_result_snake( the_argument: String, ) -> impl std::future::Future> { - println!("{}", the_argument); + println!("{the_argument}"); std::future::ready(Ok(the_argument)) } @@ -129,7 +129,7 @@ fn force_async_with_result_snake(the_argument: &str) -> Result<&str, MyError> { #[command] fn simple_command_with_result(the_argument: String) -> Result { - println!("{}", the_argument); + println!("{the_argument}"); (!the_argument.is_empty()) .then(|| the_argument) .ok_or(MyError::FooError) @@ -148,7 +148,7 @@ fn stateful_command_with_result( #[command(rename_all = "snake_case")] fn simple_command_with_result_snake(the_argument: String) -> Result { - println!("{}", the_argument); + println!("{the_argument}"); (!the_argument.is_empty()) .then(|| the_argument) .ok_or(MyError::FooError) @@ -167,7 +167,7 @@ fn stateful_command_with_result_snake( #[command] async fn async_simple_command_with_result(the_argument: String) -> Result { - println!("{}", the_argument); + println!("{the_argument}"); Ok(the_argument) } @@ -195,7 +195,7 @@ struct Person<'a> { #[command] fn command_arguments_struct(Person { name, age }: Person<'_>) { - println!("received person struct with name: {} | age: {}", name, age) + println!("received person struct with name: {name} | age: {age}") } #[derive(Deserialize)] @@ -203,7 +203,7 @@ struct InlinePerson<'a>(&'a str, u8); #[command] fn command_arguments_tuple_struct(InlinePerson(name, age): InlinePerson<'_>) { - println!("received person tuple with name: {} | age: {}", name, age) + println!("received person tuple with name: {name} | age: {age}") } #[command] diff --git a/examples/multiwindow/main.rs b/examples/multiwindow/main.rs index ae3a09ac5e9..3b5c98c7fa6 100644 --- a/examples/multiwindow/main.rs +++ b/examples/multiwindow/main.rs @@ -14,7 +14,7 @@ fn main() { .on_page_load(|window, _payload| { let label = window.label().to_string(); window.listen("clicked".to_string(), move |_payload| { - println!("got 'clicked' event on window '{}'", label); + println!("got 'clicked' event on window '{label}'"); }); }) .setup(|app| { diff --git a/examples/parent-window/main.rs b/examples/parent-window/main.rs index 5cd0e7012f4..835f7a43f7f 100644 --- a/examples/parent-window/main.rs +++ b/examples/parent-window/main.rs @@ -28,7 +28,7 @@ fn main() { .on_page_load(|window, _payload| { let label = window.label().to_string(); window.listen("clicked".to_string(), move |_payload| { - println!("got 'clicked' event on window '{}'", label); + println!("got 'clicked' event on window '{label}'"); }); }) .invoke_handler(tauri::generate_handler![create_child_window]) diff --git a/examples/streaming/main.rs b/examples/streaming/main.rs index 4dc479fea86..56e72d046ab 100644 --- a/examples/streaming/main.rs +++ b/examples/streaming/main.rs @@ -23,7 +23,7 @@ fn main() { if !video_file.exists() { // Downloading with curl this saves us from adding // a Rust HTTP client dependency. - println!("Downloading {}", video_url); + println!("Downloading {video_url}"); let status = Command::new("curl") .arg("-L") .arg("-o") diff --git a/tooling/bundler/src/bundle/linux/debian.rs b/tooling/bundler/src/bundle/linux/debian.rs index 826a79f8b52..8c8b436849f 100644 --- a/tooling/bundler/src/bundle/linux/debian.rs +++ b/tooling/bundler/src/bundle/linux/debian.rs @@ -117,7 +117,7 @@ pub fn generate_data( for bin in settings.binaries() { let bin_path = settings.binary_path(bin); - common::copy_file(&bin_path, &bin_dir.join(bin.name())) + common::copy_file(&bin_path, bin_dir.join(bin.name())) .with_context(|| format!("Failed to copy binary from {:?}", bin_path))?; } diff --git a/tooling/bundler/src/bundle/path_utils.rs b/tooling/bundler/src/bundle/path_utils.rs index efde257c26b..f3c55046415 100644 --- a/tooling/bundler/src/bundle/path_utils.rs +++ b/tooling/bundler/src/bundle/path_utils.rs @@ -206,7 +206,10 @@ where let mut work = true; while work { - result_copy = copy_file(&file, &path, &file_options); + #[allow(clippy::needless_borrow)] + { + result_copy = copy_file(&file, &path, &file_options); + } match result_copy { Ok(val) => { result += val; diff --git a/tooling/bundler/src/bundle/settings.rs b/tooling/bundler/src/bundle/settings.rs index 4be02fd935d..3aff75adb36 100644 --- a/tooling/bundler/src/bundle/settings.rs +++ b/tooling/bundler/src/bundle/settings.rs @@ -655,7 +655,7 @@ impl Settings { .to_string_lossy() .replace(&format!("-{}", self.target), ""), ); - common::copy_file(&src, &dest)?; + common::copy_file(&src, dest)?; } Ok(()) } @@ -665,7 +665,7 @@ impl Settings { for src in self.resource_files() { let src = src?; let dest = path.join(tauri_utils::resources::resource_relpath(&src)); - common::copy_file(&src, &dest)?; + common::copy_file(&src, dest)?; } Ok(()) } diff --git a/tooling/cli/src/build.rs b/tooling/cli/src/build.rs index 6bf24ac5e6b..cc85f24e60b 100644 --- a/tooling/cli/src/build.rs +++ b/tooling/cli/src/build.rs @@ -240,7 +240,7 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { options.config = merge_config; let tauri_path = tauri_dir(); - set_current_dir(&tauri_path).with_context(|| "failed to change current working directory")?; + set_current_dir(tauri_path).with_context(|| "failed to change current working directory")?; let config = get_config(options.config.as_deref())?; diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index fcaa8f825c6..a4e6e399a59 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -130,7 +130,7 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { None }; - set_current_dir(&tauri_path).with_context(|| "failed to change current working directory")?; + set_current_dir(tauri_path).with_context(|| "failed to change current working directory")?; let config = get_config(options.config.as_deref())?; diff --git a/tooling/cli/src/helpers/updater_signature.rs b/tooling/cli/src/helpers/updater_signature.rs index 800e4e6ffa9..d022053dab8 100644 --- a/tooling/cli/src/helpers/updater_signature.rs +++ b/tooling/cli/src/helpers/updater_signature.rs @@ -35,8 +35,8 @@ pub fn generate_key(password: Option) -> crate::Result { let pk_box_str = pk.to_box().unwrap().to_string(); let sk_box_str = sk.to_box(None).unwrap().to_string(); - let encoded_pk = encode(&pk_box_str); - let encoded_sk = encode(&sk_box_str); + let encoded_pk = encode(pk_box_str); + let encoded_sk = encode(sk_box_str); Ok(KeyPair { pk: encoded_pk, @@ -46,7 +46,7 @@ pub fn generate_key(password: Option) -> crate::Result { /// Transform a base64 String to readable string for the main signer pub fn decode_key(base64_key: String) -> crate::Result { - let decoded_str = &decode(&base64_key)?[..]; + let decoded_str = &decode(base64_key)?[..]; Ok(String::from(str::from_utf8(decoded_str)?)) } @@ -128,7 +128,7 @@ where Some("signature from tauri secret key"), )?; - let encoded_signature = encode(&signature_box.to_string()); + let encoded_signature = encode(signature_box.to_string()); signature_box_writer.write_all(encoded_signature.as_bytes())?; signature_box_writer.flush()?; Ok((fs::canonicalize(&signature_path)?, signature_box)) diff --git a/tooling/cli/src/info.rs b/tooling/cli/src/info.rs index b3e99cc0f7c..8b75901528a 100644 --- a/tooling/cli/src/info.rs +++ b/tooling/cli/src/info.rs @@ -450,7 +450,7 @@ fn crate_version( v } else if let Some(p) = p.path { let manifest_path = tauri_dir.join(&p).join("Cargo.toml"); - let v = match read_to_string(&manifest_path) + let v = match read_to_string(manifest_path) .map_err(|_| ()) .and_then(|m| toml::from_str::(&m).map_err(|_| ())) { diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 5e470282257..06edad93339 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -654,7 +654,7 @@ impl AppSettings for RustAppSettings { } .into(); - Ok(out_dir.join(bin_name).with_extension(&binary_extension)) + Ok(out_dir.join(bin_name).with_extension(binary_extension)) } fn get_binaries(&self, config: &Config, target: &str) -> crate::Result> { diff --git a/tooling/cli/src/interface/rust/desktop.rs b/tooling/cli/src/interface/rust/desktop.rs index b84c6e5526c..2ad00feb75c 100644 --- a/tooling/cli/src/interface/rust/desktop.rs +++ b/tooling/cli/src/interface/rust/desktop.rs @@ -330,7 +330,7 @@ fn build_command( args.push(target); } - let mut build_cmd = Command::new(&runner); + let mut build_cmd = Command::new(runner); build_cmd.arg("build"); build_cmd.args(args); diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index fdb99bc9b7d..8dd66bb6c00 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -120,7 +120,7 @@ fn run_build( ..Default::default() })?; let out_dir = bin_path.parent().unwrap(); - let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; + let _lock = flock::open_rw(out_dir.join("lock").with_extension("android"), "Android")?; let cli_options = CliOptions { features: build_options.features.clone(), diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index d8ee8e41089..521012c2fbb 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -104,7 +104,7 @@ fn run_dev( ..Default::default() })?; let out_dir = bin_path.parent().unwrap(); - let _lock = flock::open_rw(&out_dir.join("lock").with_extension("android"), "Android")?; + let _lock = flock::open_rw(out_dir.join("lock").with_extension("android"), "Android")?; let env = env()?; init_dot_cargo(app, Some((&env, config)))?; diff --git a/tooling/cli/src/plugin/init.rs b/tooling/cli/src/plugin/init.rs index 75bd9947704..62eafcc02ae 100644 --- a/tooling/cli/src/plugin/init.rs +++ b/tooling/cli/src/plugin/init.rs @@ -56,7 +56,7 @@ impl Options { pub fn command(mut options: Options) -> Result<()> { options.load(); - let template_target_path = PathBuf::from(options.directory).join(&format!( + let template_target_path = PathBuf::from(options.directory).join(format!( "tauri-plugin-{}", AsKebabCase(&options.plugin_name) )); From 61af5ce1ed07f00c16064aa22c8167f3402ff6e8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 18:32:12 -0300 Subject: [PATCH 109/423] (NEXT) Apply Version Updates From Current Changes (#5814) Co-authored-by: lucasfernog --- .changes/pre.json | 4 ++++ core/tauri/CHANGELOG.md | 7 +++++++ core/tauri/Cargo.toml | 2 +- tooling/cli/CHANGELOG.md | 7 +++++++ tooling/cli/Cargo.lock | 2 +- tooling/cli/Cargo.toml | 7 +++---- tooling/cli/metadata.json | 4 ++-- tooling/cli/node/CHANGELOG.md | 7 +++++++ tooling/cli/node/package.json | 2 +- 9 files changed, 33 insertions(+), 9 deletions(-) diff --git a/.changes/pre.json b/.changes/pre.json index 3ccd3733290..3a03b6487a8 100644 --- a/.changes/pre.json +++ b/.changes/pre.json @@ -7,7 +7,11 @@ ".changes/cli-mobile-dev.md", ".changes/codegen-mobile-devurl.md", ".changes/default-tls-features.md", + ".changes/dev-proxy-response-cache.md", ".changes/dev-proxy.md", + ".changes/fix-dev-server-proxy-path.md", + ".changes/fix-ios-run-xcode14.md", + ".changes/improve-local-ip-detection.md", ".changes/mobile-config.md", ".changes/mobile-entry-point-macro.md", ".changes/mobile-init.md", diff --git a/core/tauri/CHANGELOG.md b/core/tauri/CHANGELOG.md index 731487e9f77..a543053615d 100644 --- a/core/tauri/CHANGELOG.md +++ b/core/tauri/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.0-alpha.1] + +- Implement response cache on the dev server proxy, used when the server responds with status 304. + - [3ad5e72f](https://www.github.com/tauri-apps/tauri/commit/3ad5e72ff147b76267c010c778a3b94bba209bb0) feat(core): cache dev server proxy responses for 304 status code ([#5818](https://www.github.com/tauri-apps/tauri/pull/5818)) on 2022-12-12 +- Properly proxy dev server requests with query strings and fragments. + - [a9b4cf20](https://www.github.com/tauri-apps/tauri/commit/a9b4cf20a3e9a5cc984727a56111591504e084c0) fix(core): use entire request URL on dev server proxy ([#5819](https://www.github.com/tauri-apps/tauri/pull/5819)) on 2022-12-12 + ## \[2.0.0-alpha.0] - Added the `default-tls` and `reqwest-default-tls` Cargo features for enabling TLS suppport to connect over HTTPS. diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 457f207ea68..7a4d7a5be47 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR MIT" name = "tauri" readme = "README.md" repository = "https://github.com/tauri-apps/tauri" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" [package.metadata.docs.rs] no-default-features = true diff --git a/tooling/cli/CHANGELOG.md b/tooling/cli/CHANGELOG.md index d27d5860006..2c8d108297a 100644 --- a/tooling/cli/CHANGELOG.md +++ b/tooling/cli/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.0-alpha.1] + +- Fixes running on device using Xcode 14. + - [1e4a6758](https://www.github.com/tauri-apps/tauri/commit/1e4a675843c486bddc11292d09fb766e98758514) fix(cli): run on iOS device on Xcode 14 ([#5807](https://www.github.com/tauri-apps/tauri/pull/5807)) on 2022-12-12 +- Improve local IP address detection with user selection. + - [76204b89](https://www.github.com/tauri-apps/tauri/commit/76204b893846a04552f8f8b87ad2c9b55e1b417f) feat(cli): improve local IP detection ([#5817](https://www.github.com/tauri-apps/tauri/pull/5817)) on 2022-12-12 + ## \[2.0.0-alpha.0] - Added `android build` command. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index d904fb3fe7c..7cb952d53c9 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3832,7 +3832,7 @@ dependencies = [ [[package]] name = "tauri-cli" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" dependencies = [ "anyhow", "axum", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 377f65f257a..0616cae2be3 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -3,7 +3,7 @@ members = [ "node" ] [package] name = "tauri-cli" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" authors = [ "Tauri Programme within The Commons Conservancy" ] edition = "2021" rust-version = "1.59" @@ -92,9 +92,8 @@ winapi = { version = "0.3", features = [ "handleapi", "processenv", "winbase", " [target."cfg(unix)".dependencies] libc = "0.2" -[target.'cfg(target_os = "macos")'.dependencies] -# temporary fix, tauri-mobile should provide the openssl-vendored feature -openssl = { version = "0.10", features = ["vendored"] } +[target."cfg(target_os = \"macos\")".dependencies] +openssl = { version = "0.10", features = [ "vendored" ] } [profile.release] lto = true diff --git a/tooling/cli/metadata.json b/tooling/cli/metadata.json index f8933df5ada..1ee441f90f0 100644 --- a/tooling/cli/metadata.json +++ b/tooling/cli/metadata.json @@ -1,8 +1,8 @@ { "cli.js": { - "version": "2.0.0-alpha.0", + "version": "2.0.0-alpha.1", "node": ">= 10.0.0" }, - "tauri": "2.0.0-alpha.0", + "tauri": "2.0.0-alpha.1", "tauri-build": "2.0.0-alpha.0" } diff --git a/tooling/cli/node/CHANGELOG.md b/tooling/cli/node/CHANGELOG.md index 0729cda1fa3..f2557270239 100644 --- a/tooling/cli/node/CHANGELOG.md +++ b/tooling/cli/node/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.0-alpha.1] + +- Fixes running on device using Xcode 14. + - [1e4a6758](https://www.github.com/tauri-apps/tauri/commit/1e4a675843c486bddc11292d09fb766e98758514) fix(cli): run on iOS device on Xcode 14 ([#5807](https://www.github.com/tauri-apps/tauri/pull/5807)) on 2022-12-12 +- Improve local IP address detection with user selection. + - [76204b89](https://www.github.com/tauri-apps/tauri/commit/76204b893846a04552f8f8b87ad2c9b55e1b417f) feat(cli): improve local IP detection ([#5817](https://www.github.com/tauri-apps/tauri/pull/5817)) on 2022-12-12 + ## \[2.0.0-alpha.0] - Added `android build` command. diff --git a/tooling/cli/node/package.json b/tooling/cli/node/package.json index 2ecf80c9caf..38c85edd460 100644 --- a/tooling/cli/node/package.json +++ b/tooling/cli/node/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/cli", - "version": "2.0.0-alpha.0", + "version": "2.0.0-alpha.1", "description": "Command line interface for building Tauri apps", "funding": { "type": "opencollective", From 0189c1dbef7c66177c39b3b1892c8c016df24107 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 19 Dec 2022 11:13:30 -0300 Subject: [PATCH 110/423] chore: remove println statement --- core/tauri/src/manager.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 35a40c2e85c..6e71704b207 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -932,7 +932,6 @@ impl WindowManager { .decode_utf8_lossy() .to_string(); let url = format!("{url}{decoded_path}"); - println!("request url {url}, original path {path}, decoded {decoded_path}"); let mut proxy_builder = attohttpc::get(&url).danger_accept_invalid_certs(true); for (name, value) in request.headers() { proxy_builder = proxy_builder.header(name, value); From 9ad0a9a0aa88a67c3d81ef84df4aad23556affde Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Thu, 22 Dec 2022 15:53:46 +0200 Subject: [PATCH 111/423] Merge pull request from GHSA-6mv3-wm7j-h4w5 * fix(core): use `require_literal_separator` when matching paths * document the need for `require_literal_separator` * use `require_literal_leading_dot` --- .../glob-match-require_literal_separator.md | 5 + core/tauri/src/scope/fs.rs | 122 ++++++++++++++---- 2 files changed, 103 insertions(+), 24 deletions(-) create mode 100644 .changes/glob-match-require_literal_separator.md diff --git a/.changes/glob-match-require_literal_separator.md b/.changes/glob-match-require_literal_separator.md new file mode 100644 index 00000000000..482a810280d --- /dev/null +++ b/.changes/glob-match-require_literal_separator.md @@ -0,0 +1,5 @@ +--- +"tauri": "patch" +--- + +Fix the filesystem scope allowing sub-directories of the directory picked by the dialog when `recursive` option was `false`. diff --git a/core/tauri/src/scope/fs.rs b/core/tauri/src/scope/fs.rs index 32d92652a94..8979eb8f155 100644 --- a/core/tauri/src/scope/fs.rs +++ b/core/tauri/src/scope/fs.rs @@ -141,7 +141,7 @@ impl Scope { /// Extend the allowed patterns with the given directory. /// /// After this function has been called, the frontend will be able to use the Tauri API to read - /// the directory and all of its files and subdirectories. + /// the directory and all of its files. If `recursive` is `true`, subdirectories will be accessible too. pub fn allow_directory>(&self, path: P, recursive: bool) -> crate::Result<()> { let path = path.as_ref(); { @@ -216,13 +216,22 @@ impl Scope { if let Ok(path) = path { let path: PathBuf = path.components().collect(); + let options = glob::MatchOptions { + // this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt` + // see: https://github.com/tauri-apps/tauri/security/advisories/GHSA-6mv3-wm7j-h4w5 + require_literal_separator: true, + // dotfiles are not supposed to be exposed by default + #[cfg(unix)] + require_literal_leading_dot: true, + ..Default::default() + }; let forbidden = self .forbidden_patterns .lock() .unwrap() .iter() - .any(|p| p.matches_path(&path)); + .any(|p| p.matches_path_with(&path, options)); if forbidden { false @@ -232,7 +241,7 @@ impl Scope { .lock() .unwrap() .iter() - .any(|p| p.matches_path(&path)); + .any(|p| p.matches_path_with(&path, options)); allowed } } else { @@ -269,32 +278,97 @@ mod tests { #[test] fn path_is_escaped() { let scope = new_scope(); - scope.allow_directory("/home/tauri/**", false).unwrap(); - assert!(scope.is_allowed("/home/tauri/**")); - assert!(scope.is_allowed("/home/tauri/**/file")); - assert!(!scope.is_allowed("/home/tauri/anyfile")); + #[cfg(unix)] + { + scope.allow_directory("/home/tauri/**", false).unwrap(); + assert!(scope.is_allowed("/home/tauri/**")); + assert!(scope.is_allowed("/home/tauri/**/file")); + assert!(!scope.is_allowed("/home/tauri/anyfile")); + } + #[cfg(windows)] + { + scope.allow_directory("C:\\home\\tauri\\**", false).unwrap(); + assert!(scope.is_allowed("C:\\home\\tauri\\**")); + assert!(scope.is_allowed("C:\\home\\tauri\\**\\file")); + assert!(!scope.is_allowed("C:\\home\\tauri\\anyfile")); + } let scope = new_scope(); - scope.allow_file("/home/tauri/**").unwrap(); - assert!(scope.is_allowed("/home/tauri/**")); - assert!(!scope.is_allowed("/home/tauri/**/file")); - assert!(!scope.is_allowed("/home/tauri/anyfile")); + #[cfg(unix)] + { + scope.allow_file("/home/tauri/**").unwrap(); + assert!(scope.is_allowed("/home/tauri/**")); + assert!(!scope.is_allowed("/home/tauri/**/file")); + assert!(!scope.is_allowed("/home/tauri/anyfile")); + } + #[cfg(windows)] + { + scope.allow_file("C:\\home\\tauri\\**").unwrap(); + assert!(scope.is_allowed("C:\\home\\tauri\\**")); + assert!(!scope.is_allowed("C:\\home\\tauri\\**\\file")); + assert!(!scope.is_allowed("C:\\home\\tauri\\anyfile")); + } + + let scope = new_scope(); + #[cfg(unix)] + { + scope.allow_directory("/home/tauri", true).unwrap(); + scope.forbid_directory("/home/tauri/**", false).unwrap(); + assert!(!scope.is_allowed("/home/tauri/**")); + assert!(!scope.is_allowed("/home/tauri/**/file")); + assert!(scope.is_allowed("/home/tauri/**/inner/file")); + assert!(scope.is_allowed("/home/tauri/inner/folder/anyfile")); + assert!(scope.is_allowed("/home/tauri/anyfile")); + } + #[cfg(windows)] + { + scope.allow_directory("C:\\home\\tauri", true).unwrap(); + scope + .forbid_directory("C:\\home\\tauri\\**", false) + .unwrap(); + assert!(!scope.is_allowed("C:\\home\\tauri\\**")); + assert!(!scope.is_allowed("C:\\home\\tauri\\**\\file")); + assert!(scope.is_allowed("C:\\home\\tauri\\**\\inner\\file")); + assert!(scope.is_allowed("C:\\home\\tauri\\inner\\folder\\anyfile")); + assert!(scope.is_allowed("C:\\home\\tauri\\anyfile")); + } let scope = new_scope(); - scope.allow_directory("/home/tauri", true).unwrap(); - scope.forbid_directory("/home/tauri/**", false).unwrap(); - assert!(!scope.is_allowed("/home/tauri/**")); - assert!(!scope.is_allowed("/home/tauri/**/file")); - assert!(!scope.is_allowed("/home/tauri/**/inner/file")); - assert!(scope.is_allowed("/home/tauri/inner/folder/anyfile")); - assert!(scope.is_allowed("/home/tauri/anyfile")); + #[cfg(unix)] + { + scope.allow_directory("/home/tauri", true).unwrap(); + scope.forbid_file("/home/tauri/**").unwrap(); + assert!(!scope.is_allowed("/home/tauri/**")); + assert!(scope.is_allowed("/home/tauri/**/file")); + assert!(scope.is_allowed("/home/tauri/**/inner/file")); + assert!(scope.is_allowed("/home/tauri/anyfile")); + } + #[cfg(windows)] + { + scope.allow_directory("C:\\home\\tauri", true).unwrap(); + scope.forbid_file("C:\\home\\tauri\\**").unwrap(); + assert!(!scope.is_allowed("C:\\home\\tauri\\**")); + assert!(scope.is_allowed("C:\\home\\tauri\\**\\file")); + assert!(scope.is_allowed("C:\\home\\tauri\\**\\inner\\file")); + assert!(scope.is_allowed("C:\\home\\tauri\\anyfile")); + } let scope = new_scope(); - scope.allow_directory("/home/tauri", true).unwrap(); - scope.forbid_file("/home/tauri/**").unwrap(); - assert!(!scope.is_allowed("/home/tauri/**")); - assert!(scope.is_allowed("/home/tauri/**/file")); - assert!(scope.is_allowed("/home/tauri/**/inner/file")); - assert!(scope.is_allowed("/home/tauri/anyfile")); + #[cfg(unix)] + { + scope.allow_directory("/home/tauri", false).unwrap(); + assert!(scope.is_allowed("/home/tauri/**")); + assert!(!scope.is_allowed("/home/tauri/**/file")); + assert!(!scope.is_allowed("/home/tauri/**/inner/file")); + assert!(scope.is_allowed("/home/tauri/anyfile")); + } + #[cfg(windows)] + { + scope.allow_directory("C:\\home\\tauri", false).unwrap(); + assert!(scope.is_allowed("C:\\home\\tauri\\**")); + assert!(!scope.is_allowed("C:\\home\\tauri\\**\\file")); + assert!(!scope.is_allowed("C:\\home\\tauri\\**\\inner\\file")); + assert!(scope.is_allowed("C:\\home\\tauri\\anyfile")); + } } } From 6999cdf99ef1f0fa62febe5477c21813240de0aa Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 22 Dec 2022 11:38:44 -0300 Subject: [PATCH 112/423] chore(ci): update covector --- .github/workflows/covector-version-or-publish-next.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/covector-version-or-publish-next.yml b/.github/workflows/covector-version-or-publish-next.yml index 984294921e8..4a835376477 100644 --- a/.github/workflows/covector-version-or-publish-next.yml +++ b/.github/workflows/covector-version-or-publish-next.yml @@ -37,7 +37,7 @@ jobs: git config --global user.email "${{ github.event.pusher.email }}" - name: covector version or publish (publish when no change files present) - uses: lucasfernog/covector/packages/action@feat/target-commitish + uses: lucasfernog/covector/packages/action@patches id: covector env: NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }} From 7945e549b73ca104290d7568559635f2d53e0eed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Dec 2022 11:42:10 -0300 Subject: [PATCH 113/423] (NEXT) Apply Version Updates From Current Changes (#5892) Co-authored-by: lucasfernog --- .changes/pre.json | 1 + core/tauri/CHANGELOG.md | 5 +++++ core/tauri/Cargo.toml | 2 +- tooling/cli/metadata.json | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.changes/pre.json b/.changes/pre.json index 3a03b6487a8..f025ae492b4 100644 --- a/.changes/pre.json +++ b/.changes/pre.json @@ -11,6 +11,7 @@ ".changes/dev-proxy.md", ".changes/fix-dev-server-proxy-path.md", ".changes/fix-ios-run-xcode14.md", + ".changes/glob-match-require_literal_separator.md", ".changes/improve-local-ip-detection.md", ".changes/mobile-config.md", ".changes/mobile-entry-point-macro.md", diff --git a/core/tauri/CHANGELOG.md b/core/tauri/CHANGELOG.md index a543053615d..73af8c9e0e2 100644 --- a/core/tauri/CHANGELOG.md +++ b/core/tauri/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## \[2.0.0-alpha.2] + +- Fix the filesystem scope allowing sub-directories of the directory picked by the dialog when `recursive` option was `false`. + - [9ad0a9a0](https://www.github.com/tauri-apps/tauri/commit/9ad0a9a0aa88a67c3d81ef84df4aad23556affde) Merge pull request from GHSA-6mv3-wm7j-h4w5 on 2022-12-22 + ## \[2.0.0-alpha.1] - Implement response cache on the dev server proxy, used when the server responds with status 304. diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 7a4d7a5be47..7253ecf41c6 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR MIT" name = "tauri" readme = "README.md" repository = "https://github.com/tauri-apps/tauri" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" [package.metadata.docs.rs] no-default-features = true diff --git a/tooling/cli/metadata.json b/tooling/cli/metadata.json index 1ee441f90f0..880f2c35e4a 100644 --- a/tooling/cli/metadata.json +++ b/tooling/cli/metadata.json @@ -3,6 +3,6 @@ "version": "2.0.0-alpha.1", "node": ">= 10.0.0" }, - "tauri": "2.0.0-alpha.1", + "tauri": "2.0.0-alpha.2", "tauri-build": "2.0.0-alpha.0" } From ced8e9a7be5d8dfd5fb047e1e1d49dc58bed3844 Mon Sep 17 00:00:00 2001 From: Jonas Kruckenberg Date: Tue, 27 Dec 2022 14:41:23 +0100 Subject: [PATCH 114/423] docs: iOS config is under the `bundle`key (#5856) --- tooling/cli/src/mobile/ios.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index bb51e02779b..f627dfa441d 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -110,12 +110,12 @@ pub fn get_config( let teams = find_development_teams().unwrap_or_default(); match teams.len() { 0 => { - log::error!("No code signing certificates found. You must add one and set the certificate development team ID on the `tauri > iOS > developmentTeam` config value or the `{APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME}` environment variable. To list the available certificates, run `tauri info`."); + log::error!("No code signing certificates found. You must add one and set the certificate development team ID on the `tauri > bundle > iOS > developmentTeam` config value or the `{APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME}` environment variable. To list the available certificates, run `tauri info`."); exit(1); } 1 => teams.first().unwrap().id.clone(), _ => { - log::error!("You must set the code signing certificate development team ID on the `tauri > iOS > developmentTeam` config value or the `{APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME}` environment variable. Available certificates: {}", teams.iter().map(|t| format!("{} (ID: {})", t.name, t.id)).collect::>().join(", ")); + log::error!("You must set the code signing certificate development team ID on the `tauri > bundle > iOS > developmentTeam` config value or the `{APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME}` environment variable. Available certificates: {}", teams.iter().map(|t| format!("{} (ID: {})", t.name, t.id)).collect::>().join(", ")); exit(1); } } From dee9460f9c9bc92e9c638e7691e616849ac2085b Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Tue, 27 Dec 2022 13:12:01 -0800 Subject: [PATCH 115/423] feat: keep CLI alive when iOS app exits, show logs, closes #5855 (#5902) --- .changes/ios-keep-alive.md | 6 ++++ .changes/ios-logs.md | 7 ++++ .changes/mobile-env-vars-rename.md | 6 ++++ core/tauri-build/src/lib.rs | 10 ++++-- core/tauri-macros/src/mobile.rs | 7 ++-- core/tauri/Cargo.toml | 4 ++- core/tauri/src/lib.rs | 40 ++++++++++++++++++++-- examples/api/src-tauri/Cargo.lock | 54 +++++++++++++++++------------- tooling/cli/Cargo.lock | 4 +-- tooling/cli/Cargo.toml | 2 +- tooling/cli/src/mobile/ios/dev.rs | 4 +-- 11 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 .changes/ios-keep-alive.md create mode 100644 .changes/ios-logs.md create mode 100644 .changes/mobile-env-vars-rename.md diff --git a/.changes/ios-keep-alive.md b/.changes/ios-keep-alive.md new file mode 100644 index 00000000000..2313abdd81d --- /dev/null +++ b/.changes/ios-keep-alive.md @@ -0,0 +1,6 @@ +--- +"cli.rs": patch +"cli.js": patch +--- + +Keep the process alive even when the iOS application is closed. diff --git a/.changes/ios-logs.md b/.changes/ios-logs.md new file mode 100644 index 00000000000..52493a06fff --- /dev/null +++ b/.changes/ios-logs.md @@ -0,0 +1,7 @@ +--- +"cli.rs": patch +"cli.js": patch +"tauri": patch +--- + +Show all application logs on iOS. diff --git a/.changes/mobile-env-vars-rename.md b/.changes/mobile-env-vars-rename.md new file mode 100644 index 00000000000..d977243e1fb --- /dev/null +++ b/.changes/mobile-env-vars-rename.md @@ -0,0 +1,6 @@ +--- +"tauri-build": patch +"tauri-macros": patch +--- + +Refactor mobile environment variables. diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 09469f9d965..5e62e94953d 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -222,14 +222,20 @@ pub fn try_build(attributes: Attributes) -> Result<()> { let s = config.tauri.bundle.identifier.split('.'); let last = s.clone().count() - 1; let mut domain = String::new(); + let mut android_package_prefix = String::new(); for (i, w) in s.enumerate() { if i != last { domain.push_str(w); - domain.push('_'); + domain.push('.'); + + android_package_prefix.push_str(w); + android_package_prefix.push('_'); } } domain.pop(); - println!("cargo:rustc-env=TAURI_ANDROID_DOMAIN={domain}"); + android_package_prefix.pop(); + println!("cargo:rustc-env=TAURI_MOBILE_DOMAIN={domain}"); + println!("cargo:rustc-env=TAURI_ANDROID_PACKAGE_PREFIX={android_package_prefix}"); cfg_alias("dev", !has_feature("custom-protocol")); diff --git a/core/tauri-macros/src/mobile.rs b/core/tauri-macros/src/mobile.rs index 44bd177fb51..4502b0d6fca 100644 --- a/core/tauri-macros/src/mobile.rs +++ b/core/tauri-macros/src/mobile.rs @@ -33,13 +33,14 @@ pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { let function_name = function.sig.ident.clone(); let mut error = None; - let domain = get_env_var("TAURI_ANDROID_DOMAIN", |r| r, &mut error, &function); + let domain = get_env_var("TAURI_ANDROID_PACKAGE_PREFIX", |r| r, &mut error, &function); let app_name = get_env_var( "CARGO_PKG_NAME", |r| r.replace('_', "_1"), &mut error, &function, ); + let domain_str = var("TAURI_MOBILE_DOMAIN").unwrap(); let app_name_str = var("CARGO_PKG_NAME").unwrap(); if let Some(e) = error { @@ -59,9 +60,11 @@ pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { #function fn _start_app() { - ::tauri::init_logging(#app_name_str); + #[cfg(target_os = "ios")] + ::tauri::init_logging(&format!("{}.{}", #domain_str, #app_name_str)); #[cfg(target_os = "android")] { + ::tauri::init_logging(#app_name_str); use ::tauri::paste; ::tauri::wry_android_binding!(#domain, #app_name, _start_app, ::tauri::wry); } diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 7253ecf41c6..7215cdb172f 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -114,7 +114,9 @@ android_logger = "0.9" log = "0.4" [target."cfg(target_os = \"ios\")".dependencies] -env_logger = "0.9.0" +oslog = "0.2" +log = "0.4" +libc = "0.2" [build-dependencies] heck = "0.4" diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index e14b1bd5c17..d09e7a61f63 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -290,8 +290,44 @@ pub fn init_logging(tag: &str) { #[cfg(target_os = "ios")] #[doc(hidden)] -pub fn init_logging(_tag: &str) { - env_logger::init(); +pub fn init_logging(subsystem: &str) { + use std::{ + ffi::CString, + fs::File, + io::{BufRead, BufReader}, + os::unix::prelude::*, + thread, + }; + + let mut logpipe: [RawFd; 2] = Default::default(); + unsafe { + libc::pipe(logpipe.as_mut_ptr()); + libc::dup2(logpipe[1], libc::STDOUT_FILENO); + libc::dup2(logpipe[1], libc::STDERR_FILENO); + } + thread::spawn(move || unsafe { + let file = File::from_raw_fd(logpipe[0]); + let mut reader = BufReader::new(file); + let mut buffer = String::new(); + loop { + buffer.clear(); + if let Ok(len) = reader.read_line(&mut buffer) { + if len == 0 { + break; + } else if let Ok(msg) = CString::new(buffer.clone()) + .map_err(|_| ()) + .and_then(|c| c.into_string().map_err(|_| ())) + { + log::info!("{}", msg); + } + } + } + }); + + oslog::OsLogger::new(subsystem) + .level_filter(log::LevelFilter::Trace) + .init() + .unwrap(); } /// Updater events. diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 5c66e3081f5..1a48aaa4f2f 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -80,7 +80,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ec2333c185d826313162cee39d3fcc6a84ba08114a839bebf53b961e7e75773" dependencies = [ "android_log-sys", - "env_logger 0.7.1", + "env_logger", "lazy_static", "log", ] @@ -610,6 +610,19 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "dbus" version = "0.9.6" @@ -717,19 +730,6 @@ dependencies = [ "regex", ] -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "fastrand" version = "1.8.0" @@ -1279,12 +1279,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" version = "0.14.23" @@ -1941,6 +1935,17 @@ version = "6.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" +[[package]] +name = "oslog" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d2043d1f61d77cb2f4b1f7b7b2295f40507f5f8e9d1c8bf10a1ca5f97a3969" +dependencies = [ + "cc", + "dashmap", + "log", +] + [[package]] name = "overload" version = "0.1.1" @@ -2935,7 +2940,7 @@ dependencies = [ [[package]] name = "tauri" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.2" dependencies = [ "android_logger", "anyhow", @@ -2947,7 +2952,6 @@ dependencies = [ "dirs-next", "embed_plist", "encoding_rs", - "env_logger 0.9.3", "flate2", "futures-util", "glib", @@ -2958,6 +2962,7 @@ dependencies = [ "ico", "ignore", "infer 0.9.0", + "libc", "log", "minisign-verify", "notify-rust", @@ -2966,6 +2971,7 @@ dependencies = [ "open", "os_info", "os_pipe", + "oslog", "paste", "percent-encoding", "png", @@ -4037,9 +4043,9 @@ dependencies = [ [[package]] name = "wry" -version = "0.23.1" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76c9236a810d4af02213f89f5bc55bf3262d40c4407b13a9fc847156ef8b856" +checksum = "4c1ad8e2424f554cc5bdebe8aa374ef5b433feff817aebabca0389961fc7ef98" dependencies = [ "base64", "block", diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 7cb952d53c9..3052a8a88a3 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3908,9 +3908,9 @@ dependencies = [ [[package]] name = "tauri-mobile" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b23191ab3de2efc8d266ec26c16c1c90bd9e171f6f8ccdc56cd31cc9ad97130" +checksum = "723e99ec18695c25936deb0bc4e271ff4d25f48de88d03d59959fe8a31d9bf91" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 0616cae2be3..498816db6a8 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -39,7 +39,7 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -tauri-mobile = { version = "0.1", default-features = false } +tauri-mobile = { version = "0.1.1", default-features = false } textwrap = { version = "0.11.0", features = [ "term_size" ] } jsonrpsee = { version = "0.16", features = [ "client", "server" ] } thiserror = "1" diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 1c2ce58bfd4..ad3bb6bdbbf 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -189,15 +189,13 @@ fn run( Profile::Release }; - let non_interactive = true; // ios-deploy --noninteractive (quit when app crashes or exits) - device_prompt(env, device) .map_err(|e| RunError::FailedToPromptForDevice(e.to_string()))? .run( config, env, NoiseLevel::FranklyQuitePedantic, - non_interactive, + false, // do not quit on app exit profile, ) .map(DevChild::new) From e873bae09f0f27517f720a753f51c1dcb903f883 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Wed, 28 Dec 2022 09:46:58 -0800 Subject: [PATCH 116/423] fix(cli): Cargo target dir detection on Android, closes #5865 (#5932) --- .changes/target-dir-detection.md | 6 +++++ tooling/cli/Cargo.lock | 4 ++-- tooling/cli/Cargo.toml | 2 +- tooling/cli/src/interface/mod.rs | 1 + tooling/cli/src/interface/rust.rs | 12 ++++++++++ tooling/cli/src/mobile/mod.rs | 39 ++++++++++++++++++------------- 6 files changed, 45 insertions(+), 19 deletions(-) create mode 100644 .changes/target-dir-detection.md diff --git a/.changes/target-dir-detection.md b/.changes/target-dir-detection.md new file mode 100644 index 00000000000..eab17d39582 --- /dev/null +++ b/.changes/target-dir-detection.md @@ -0,0 +1,6 @@ +--- +"cli.rs": patch +"cli.js": patch +--- + +Fix target directory detection when compiling for Android. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 3052a8a88a3..8e6bf598566 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3908,9 +3908,9 @@ dependencies = [ [[package]] name = "tauri-mobile" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e99ec18695c25936deb0bc4e271ff4d25f48de88d03d59959fe8a31d9bf91" +checksum = "749adb9f2c4844357bbc9ee4566ef5fe9755dc0956d6ba005180add824d835ac" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 498816db6a8..c0bf1b9bbae 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -39,7 +39,7 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -tauri-mobile = { version = "0.1.1", default-features = false } +tauri-mobile = { version = "0.1.3", default-features = false } textwrap = { version = "0.11.0", features = [ "term_size" ] } jsonrpsee = { version = "0.16", features = [ "client", "server" ] } thiserror = "1" diff --git a/tooling/cli/src/interface/mod.rs b/tooling/cli/src/interface/mod.rs index dfa00b93da8..40a474c6ade 100644 --- a/tooling/cli/src/interface/mod.rs +++ b/tooling/cli/src/interface/mod.rs @@ -36,6 +36,7 @@ pub trait AppSettings { config: &Config, target: &str, ) -> crate::Result>; + fn app_name(&self) -> Option; fn get_bundler_settings( &self, diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 06edad93339..1adaa438b74 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -760,6 +760,18 @@ impl AppSettings for RustAppSettings { Ok(binaries) } + + fn app_name(&self) -> Option { + self + .manifest + .inner + .as_table() + .get("package") + .and_then(|p| p.as_table()) + .and_then(|p| p.get("name")) + .and_then(|n| n.as_str()) + .map(|n| n.to_string()) + } } impl RustAppSettings { diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 21425ea351e..587a1698322 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -4,7 +4,7 @@ use crate::{ helpers::{app_paths::tauri_dir, config::Config as TauriConfig}, - interface::DevProcess, + interface::{AppInterface, AppSettings, DevProcess, Interface, Options as InterfaceOptions}, }; use anyhow::{bail, Result}; use jsonrpsee::client_transport::ws::WsTransportClientBuilder; @@ -31,7 +31,7 @@ use tauri_mobile::{ bossy, config::app::{App, Raw as RawAppConfig}, env::Error as EnvError, - opts::NoiseLevel, + opts::{NoiseLevel, Profile}, }; use tokio::runtime::Runtime; @@ -220,19 +220,14 @@ fn get_app(config: &TauriConfig) -> App { } reverse_domain.pop(); - let manifest_path = tauri_dir().join("Cargo.toml"); - let app_name = if let Ok(manifest) = crate::interface::manifest::read_manifest(&manifest_path) { - manifest - .as_table() - .get("package") - .and_then(|p| p.as_table()) - .and_then(|p| p.get("name")) - .and_then(|n| n.as_str()) - .map(|n| n.to_string()) - .unwrap_or(app_name) - } else { - app_name - }; + let interface = AppInterface::new( + config, + // the target triple is not relevant + Some("".into()), + ) + .expect("failed to load interface"); + + let app_name = interface.app_settings().app_name().unwrap_or(app_name); let raw = RawAppConfig { name: app_name, @@ -241,7 +236,19 @@ fn get_app(config: &TauriConfig) -> App { asset_dir: None, template_pack: None, }; - App::from_raw(tauri_dir(), raw).unwrap() + App::from_raw(tauri_dir(), raw) + .unwrap() + .with_target_dir_resolver(move |target, profile| { + let bin_path = interface + .app_settings() + .app_binary_path(&InterfaceOptions { + debug: matches!(profile, Profile::Debug), + target: Some(target.into()), + ..Default::default() + }) + .expect("failed to resolve target directory"); + bin_path.parent().unwrap().to_path_buf() + }) } fn ensure_init(project_dir: PathBuf, target: Target) -> Result<()> { From 17d80ab2364ef21e4c0d6b6be7095e72002f3e74 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Wed, 28 Dec 2022 10:02:07 -0800 Subject: [PATCH 117/423] feat(cli): improve error message when lib is not found, closes #5867 (#5933) --- tooling/cli/Cargo.lock | 4 ++-- tooling/cli/Cargo.toml | 2 +- tooling/cli/src/mobile/ios/xcode_script.rs | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 8e6bf598566..17cb938e8ae 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3908,9 +3908,9 @@ dependencies = [ [[package]] name = "tauri-mobile" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749adb9f2c4844357bbc9ee4566ef5fe9755dc0956d6ba005180add824d835ac" +checksum = "dff04e700514e294069c697559b74f2cc168e8d6c984e2e4ba99a40d63468061" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index c0bf1b9bbae..37dd5d35d54 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -39,7 +39,7 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -tauri-mobile = { version = "0.1.3", default-features = false } +tauri-mobile = { version = "0.1.4", default-features = false } textwrap = { version = "0.11.0", features = [ "term_size" ] } jsonrpsee = { version = "0.16", features = [ "client", "server" ] } thiserror = "1" diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 5ce3443abc5..94a8af78646 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -188,12 +188,16 @@ pub fn command(options: Options) -> Result<()> { })?; let out_dir = bin_path.parent().unwrap(); + let lib_path = out_dir.join(format!("lib{}.a", AsSnakeCase(config.app().name()))); + if !lib_path.exists() { + return Err(anyhow::anyhow!("Library not found at {}. Make sure your Cargo.toml file has a [lib] block with `crate-type = [\"staticlib\", \"cdylib\", \"rlib\"]`", lib_path.display())); + } std::fs::create_dir_all(format!( "gen/apple/Externals/{rust_triple}/{}", profile.as_str() ))?; std::fs::copy( - out_dir.join(format!("lib{}.a", AsSnakeCase(config.app().name()))), + lib_path, format!( "gen/apple/Externals/{rust_triple}/{}/lib{}.a", profile.as_str(), From 2c4a0bbd1fbe15d7500264e6490772397e1917ed Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Wed, 28 Dec 2022 12:58:14 -0800 Subject: [PATCH 118/423] feat(cli): force colored logs on mobile commands (#5934) --- .changes/force-colored-logs.md | 6 ++++++ tooling/cli/src/mobile/mod.rs | 1 + 2 files changed, 7 insertions(+) create mode 100644 .changes/force-colored-logs.md diff --git a/.changes/force-colored-logs.md b/.changes/force-colored-logs.md new file mode 100644 index 00000000000..406ff82b149 --- /dev/null +++ b/.changes/force-colored-logs.md @@ -0,0 +1,6 @@ +--- +"cli.rs": patch +"cli.js": patch +--- + +Force colored logs on mobile commands. diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 587a1698322..33edad6dcfc 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -129,6 +129,7 @@ pub struct CliOptions { fn env_vars() -> HashMap { let mut vars = HashMap::new(); + vars.insert("RUST_LOG_STYLE".into(), "always".into()); for (k, v) in std::env::vars_os() { let k = k.to_string_lossy(); if (k.starts_with("TAURI") && k != "TAURI_PRIVATE_KEY" && k != "TAURI_KEY_PASSWORD") From 50f6dd87b1ac2c99f8794b055f1acba4ef7d34d3 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Fri, 6 Jan 2023 19:31:48 +0200 Subject: [PATCH 119/423] feat: improvements to support hyphens in crate name (#5989) Co-authored-by: Lucas Nogueira --- .changes/mobile-lib-name.md | 6 + core/tauri-macros/src/mobile.rs | 2 +- core/tauri-runtime-wry/Cargo.toml | 2 +- core/tauri-runtime-wry/src/lib.rs | 3 +- core/tauri-runtime/src/http/response.rs | 21 +- core/tauri/Cargo.toml | 2 +- core/tauri/src/manager.rs | 6 +- core/tauri/src/updater/core.rs | 2 +- core/tests/app-updater/tests/update.rs | 2 +- tooling/cli/Cargo.lock | 1223 ++++++++--------- tooling/cli/Cargo.toml | 4 +- tooling/cli/node/package.json | 3 + tooling/cli/node/yarn.lock | 20 +- tooling/cli/src/icon.rs | 2 +- tooling/cli/src/interface/mod.rs | 1 + tooling/cli/src/interface/rust.rs | 12 + tooling/cli/src/mobile/android.rs | 9 +- tooling/cli/src/mobile/android/project.rs | 4 +- tooling/cli/src/mobile/ios/xcode_script.rs | 5 +- tooling/cli/src/mobile/mod.rs | 2 + .../app/src-tauri/Cargo.crate-manifest | 7 +- .../templates/app/src-tauri/src/desktop.rs | 8 - .../cli/templates/app/src-tauri/src/lib.rs | 46 +- .../cli/templates/app/src-tauri/src/main.rs | 8 +- .../cli/templates/app/src-tauri/src/mobile.rs | 4 - 25 files changed, 651 insertions(+), 753 deletions(-) create mode 100644 .changes/mobile-lib-name.md delete mode 100644 tooling/cli/templates/app/src-tauri/src/desktop.rs delete mode 100644 tooling/cli/templates/app/src-tauri/src/mobile.rs diff --git a/.changes/mobile-lib-name.md b/.changes/mobile-lib-name.md new file mode 100644 index 00000000000..be976b9e493 --- /dev/null +++ b/.changes/mobile-lib-name.md @@ -0,0 +1,6 @@ +--- +"cli.rs": patch +"cli.js": patch +--- + +Add support to custom and kebab case library names for mobile apps. diff --git a/core/tauri-macros/src/mobile.rs b/core/tauri-macros/src/mobile.rs index 4502b0d6fca..17e0a26f12c 100644 --- a/core/tauri-macros/src/mobile.rs +++ b/core/tauri-macros/src/mobile.rs @@ -36,7 +36,7 @@ pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { let domain = get_env_var("TAURI_ANDROID_PACKAGE_PREFIX", |r| r, &mut error, &function); let app_name = get_env_var( "CARGO_PKG_NAME", - |r| r.replace('_', "_1"), + |r| r.replace('-', "_"), &mut error, &function, ); diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 2594efea550..e9493d88222 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -13,7 +13,7 @@ exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" [dependencies] -wry = { version = "0.23.4", default-features = false, features = [ "file-drop", "protocol" ] } +wry = { git = "https://github.com/tauri-apps/wry", branch = "dev", default-features = false, features = [ "file-drop", "protocol" ] } tauri-runtime = { version = "0.13.0-alpha.0", path = "../tauri-runtime" } tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index d377dfb8680..cc3ebba2caa 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -79,6 +79,7 @@ pub use wry::application::platform::macos::{ }; use std::{ + borrow::Cow, cell::RefCell, collections::{ hash_map::Entry::{Occupied, Vacant}, @@ -284,7 +285,7 @@ impl From<&WryRequest>> for HttpRequestWrapper { } // response -struct HttpResponseWrapper(WryResponse>); +struct HttpResponseWrapper(WryResponse>); impl From for HttpResponseWrapper { fn from(response: HttpResponse) -> Self { let (parts, body) = response.into_parts(); diff --git a/core/tauri-runtime/src/http/response.rs b/core/tauri-runtime/src/http/response.rs index 67641e863f3..532d23b5b33 100644 --- a/core/tauri-runtime/src/http/response.rs +++ b/core/tauri-runtime/src/http/response.rs @@ -7,7 +7,7 @@ use super::{ status::StatusCode, version::Version, }; -use std::fmt; +use std::{borrow::Cow, fmt}; type Result = core::result::Result>; @@ -33,7 +33,7 @@ type Result = core::result::Result>; /// pub struct Response { head: ResponseParts, - body: Vec, + body: Cow<'static, [u8]>, } /// Component parts of an HTTP `Response` @@ -67,7 +67,7 @@ pub struct Builder { impl Response { /// Creates a new blank `Response` with the body #[inline] - pub fn new(body: Vec) -> Response { + pub fn new(body: Cow<'static, [u8]>) -> Response { Response { head: ResponseParts::new(), body, @@ -81,7 +81,7 @@ impl Response { /// This API is used internally. It may have breaking changes in the future. #[inline] #[doc(hidden)] - pub fn into_parts(self) -> (ResponseParts, Vec) { + pub fn into_parts(self) -> (ResponseParts, Cow<'static, [u8]>) { (self.head, self.body) } @@ -129,13 +129,13 @@ impl Response { /// Returns a mutable reference to the associated HTTP body. #[inline] - pub fn body_mut(&mut self) -> &mut Vec { + pub fn body_mut(&mut self) -> &mut Cow<'static, [u8]> { &mut self.body } /// Returns a reference to the associated HTTP body. #[inline] - pub fn body(&self) -> &Vec { + pub fn body(&self) -> &Cow<'static, [u8]> { &self.body } } @@ -143,7 +143,7 @@ impl Response { impl Default for Response { #[inline] fn default() -> Response { - Response::new(Vec::new()) + Response::new(Default::default()) } } @@ -280,8 +280,11 @@ impl Builder { /// .body(Vec::new()) /// .unwrap(); /// ``` - pub fn body(self, body: Vec) -> Result { - self.inner.map(move |head| Response { head, body }) + pub fn body(self, body: impl Into>) -> Result { + self.inner.map(move |head| Response { + head, + body: body.into(), + }) } // private diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 7215cdb172f..051158d6937 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -60,7 +60,7 @@ state = "0.5" tar = "0.4.38" tempfile = "3" zip = { version = "0.6", default-features = false, optional = true } -ignore = "0.4" +ignore = "=0.4.18" flate2 = "1.0" http = "0.2" dirs-next = "2.0" diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 6e71704b207..fff687eab1c 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -901,7 +901,7 @@ impl WindowManager { struct CachedResponse { status: http::StatusCode, headers: http::HeaderMap, - body: Vec, + body: Cow<'static, [u8]>, } #[cfg(dev)] @@ -951,7 +951,7 @@ impl WindowManager { let response = CachedResponse { status, headers, - body, + body: body.into(), }; response_cache_.insert(url.clone(), response); response_cache_.get(&url).unwrap() @@ -988,7 +988,7 @@ impl WindowManager { let response_csp = String::from_utf8_lossy(response_csp.as_bytes()); let html = String::from_utf8_lossy(response.body()); let body = html.replacen(tauri_utils::html::CSP_TOKEN, &response_csp, 1); - *response.body_mut() = body.as_bytes().to_vec(); + *response.body_mut() = body.as_bytes().to_vec().into(); } Ok(response) }) diff --git a/core/tauri/src/updater/core.rs b/core/tauri/src/updater/core.rs index e263ccd7c63..c212bf91e42 100644 --- a/core/tauri/src/updater/core.rs +++ b/core/tauri/src/updater/core.rs @@ -895,7 +895,7 @@ fn copy_files_and_run(archive_buffer: R, extract_path: &Path) -> })?; let _ = std::process::Command::new("touch") - .arg(&extract_path) + .arg(extract_path) .status(); Ok(()) diff --git a/core/tests/app-updater/tests/update.rs b/core/tests/app-updater/tests/update.rs index c7b147cdc83..a755bd4f2f9 100644 --- a/core/tests/app-updater/tests/update.rs +++ b/core/tests/app-updater/tests/update.rs @@ -94,7 +94,7 @@ fn bundle_path(root_dir: &Path, version: &str) -> PathBuf { #[cfg(target_os = "macos")] fn bundle_path(root_dir: &Path, _version: &str) -> PathBuf { - root_dir.join(format!("target/debug/bundle/macos/app-updater.app")) + root_dir.join("target/debug/bundle/macos/app-updater.app") } #[cfg(target_os = "ios")] diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 17cb938e8ae..246df9fdbd8 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -20,7 +20,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ - "generic-array 0.14.5", + "generic-array", ] [[package]] @@ -30,9 +30,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if", - "cipher", + "cipher 0.3.0", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] @@ -43,7 +43,7 @@ checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" dependencies = [ "aead", "aes", - "cipher", + "cipher 0.3.0", "ctr", "ghash", "subtle", @@ -51,11 +51,12 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" dependencies = [ - "getrandom 0.2.7", + "cfg-if", + "getrandom 0.2.8", "once_cell", "serde", "version_check", @@ -63,27 +64,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "ar" @@ -115,9 +107,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" dependencies = [ "proc-macro2", "quote", @@ -143,7 +135,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -156,9 +148,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.5.16" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" +checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" dependencies = [ "async-trait", "axum-core", @@ -169,7 +161,7 @@ dependencies = [ "http", "http-body", "hyper", - "itoa 1.0.2", + "itoa 1.0.5", "matchit", "memchr", "mime", @@ -178,7 +170,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sha-1 0.10.0", + "sha-1 0.10.1", "sync_wrapper", "tokio", "tokio-tungstenite", @@ -190,9 +182,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" +checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc" dependencies = [ "async-trait", "bytes", @@ -206,15 +198,15 @@ dependencies = [ [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64ct" -version = "1.0.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" [[package]] name = "beef" @@ -270,65 +262,39 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.5", + "generic-array", ] [[package]] name = "block-buffer" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "block-padding" -version = "0.1.5" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ - "byte-tools", + "generic-array", ] [[package]] name = "bstr" -version = "0.2.17" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" dependencies = [ "memchr", + "serde", ] [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "bytecount" @@ -338,9 +304,9 @@ checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" [[package]] name = "bytemuck" -version = "1.9.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" +checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" [[package]] name = "byteorder" @@ -350,15 +316,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "bzip2" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" dependencies = [ "bzip2-sys", "libc", @@ -377,9 +343,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.73" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" dependencies = [ "jobserver", ] @@ -400,58 +366,47 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chunked_transfer" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" - [[package]] name = "cipher" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.5", + "generic-array", ] [[package]] -name = "clap" -version = "2.34.0" +name = "cipher" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", + "crypto-common", + "inout", ] [[package]] name = "clap" -version = "4.0.9" +version = "4.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30607dd93c420c6f1f80b544be522a0238a7db35e6a12968d28910983fee0df0" +checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" dependencies = [ - "atty", "bitflags", "clap_derive", "clap_lex", + "is-terminal", "once_cell", - "strsim 0.10.0", + "strsim", "termcolor", ] [[package]] name = "clap_derive" -version = "4.0.9" +version = "4.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a307492e1a34939f79d3b6b9650bd2b971513cd775436bf2b78defeb5af00b" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" dependencies = [ - "heck 0.4.0", + "heck", "proc-macro-error", "proc-macro2", "quote", @@ -512,9 +467,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.4" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" dependencies = [ "bytes", "memchr", @@ -528,17 +483,15 @@ checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" [[package]] name = "console" -version = "0.15.0" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +checksum = "c9b6515d269224923b26b5febea2ed42b2d5f2ce37284a4dd670fedd6cb8347a" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "regex", - "terminal_size", "unicode-width", - "winapi", + "windows-sys 0.42.0", ] [[package]] @@ -555,9 +508,12 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "convert_case" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "core-foundation" @@ -605,9 +561,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] @@ -623,9 +579,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if", "crossbeam-utils", @@ -633,9 +589,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -644,35 +600,39 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.9" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", - "once_cell", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.10" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", - "once_cell", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.5", + "generic-array", "typenum", ] @@ -705,9 +665,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.22" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", "syn", @@ -719,17 +679,17 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "cipher", + "cipher 0.3.0", ] [[package]] name = "ctrlc" -version = "3.2.2" +version = "3.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865" +checksum = "1631ca6e3c59112501a9d87fd86f21591ff77acd31331e8a73f8d80a65bbdd71" dependencies = [ "nix", - "winapi", + "windows-sys 0.42.0", ] [[package]] @@ -752,7 +712,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", + "strsim", "syn", ] @@ -782,46 +742,37 @@ dependencies = [ [[package]] name = "deunicode" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c9736e15e7df1638a7f6eee92a6511615c738246a052af5ba86f039b65aede" +checksum = "8c1bba4f227a4a53d12b653f50ca7bf10c9119ae2aba56aff9e0338b5c98f36a" [[package]] name = "dialoguer" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c8ae48e400addc32a8710c8d62d55cb84249a7d58ac4cd959daecfbaddc545" +checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1" dependencies = [ "console", "tempfile", "zeroize", ] -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.5", + "generic-array", ] [[package]] name = "digest" -version = "0.10.3" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.3", "crypto-common", "subtle", ] @@ -864,27 +815,27 @@ dependencies = [ [[package]] name = "dunce" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" +checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" [[package]] name = "dyn-clone" -version = "1.0.6" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140206b78fb2bc3edbcfc9b5ccbd0b30699cfe8d348b8b31b330e47df5291a5a" +checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "embed-resource" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc24ff8d764818e9ab17963b0593c535f077a513f565e75e4352d758bc4d8c0" +checksum = "e62abb876c07e4754fae5c14cafa77937841f01740637e17d78dc04352f32a5e" dependencies = [ "cc", "rustc_version", @@ -980,9 +931,9 @@ checksum = "4e4f5d6e192964d498b45abee72ca445e91909094bc8e8791259e82c2a0d1aa6" [[package]] name = "env_logger" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" dependencies = [ "atty", "humantime", @@ -991,6 +942,27 @@ dependencies = [ "termcolor", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -999,30 +971,24 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "exr" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9a7880199e74c6d3fe45579df2f436c5913a71405494cb89d59234d86b47dc5" +checksum = "8eb5f255b5980bb0c8cf676b675d1a99be40f316881444f44e0462eaf5df5ded" dependencies = [ "bit_field", "flume", "half", "lebe", - "miniz_oxide 0.5.4", + "miniz_oxide", "smallvec", "threadpool", ] -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - [[package]] name = "fancy-regex" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95b4efe5be9104a4a18a9916e86654319895138be727b229820c39257c30dda" +checksum = "0678ab2d46fa5195aaf59ad034c083d351377d4af57f3e073c074d0da3e3c766" dependencies = [ "bit-set", "regex", @@ -1030,46 +996,46 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "filetime" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi", + "windows-sys 0.42.0", ] [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", - "miniz_oxide 0.5.4", + "miniz_oxide", ] [[package]] name = "flume" -version = "0.10.13" +version = "0.10.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceeb589a3157cac0ab8cc585feb749bd2cea5cb55a6ee802ad72d9fd38303da" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" dependencies = [ "futures-core", "futures-sink", "nanorand", "pin-project", - "spin 0.9.3", + "spin 0.9.4", ] [[package]] @@ -1095,19 +1061,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] [[package]] name = "fraction" -version = "0.10.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb65943183b6b3cbf00f64c181e8178217e30194381b150e4f87ec59864c803" +checksum = "7aa5de57a62c2440ece64342ea59efb7171aa7d016faf8dfcb8795066a17146b" dependencies = [ "lazy_static", "num", @@ -1144,9 +1109,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", @@ -1158,9 +1123,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", @@ -1168,15 +1133,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-lite" @@ -1195,9 +1160,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", @@ -1206,15 +1171,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-timer" @@ -1228,9 +1193,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-channel", "futures-core", @@ -1255,18 +1220,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.12.4" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -1285,9 +1241,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "js-sys", @@ -1302,7 +1258,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" dependencies = [ - "opaque-debug 0.3.0", + "opaque-debug", "polyval", ] @@ -1324,9 +1280,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", "bstr", @@ -1337,9 +1293,9 @@ dependencies = [ [[package]] name = "gloo-net" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec897194fb9ac576c708f63d35604bc58f2a262b8cec0fabfed26f3991255f21" +checksum = "9050ff8617e950288d7bf7f300707639fdeda5ca0d0ecf380cff448cfd52f4a6" dependencies = [ "futures-channel", "futures-core", @@ -1357,9 +1313,9 @@ dependencies = [ [[package]] name = "gloo-timers" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +checksum = "98c4a8d6391675c6b2ee1a6c8d06e8e2d03605c44cec1270675985a4c2a5500b" dependencies = [ "futures-channel", "futures-core", @@ -1369,9 +1325,9 @@ dependencies = [ [[package]] name = "gloo-utils" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40913a05c8297adca04392f707b1e73b12ba7b8eab7244a4961580b1fd34063c" +checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" dependencies = [ "js-sys", "serde", @@ -1382,9 +1338,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ "bytes", "fnv", @@ -1401,9 +1357,12 @@ dependencies = [ [[package]] name = "half" -version = "1.8.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "6c467d36af040b7b2681f5fddd27427f6da8d3d072f575a265e181d2f8e8d157" +dependencies = [ + "crunchy", +] [[package]] name = "handlebars" @@ -1421,9 +1380,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.1" +version = "4.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66d0c1b6e3abfd1e72818798925e16e02ed77e1b47f6c25a95a23b377ee4299" +checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" dependencies = [ "log", "pest", @@ -1435,18 +1394,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" - -[[package]] -name = "heck" -version = "0.3.3" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -1463,6 +1413,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" @@ -1475,14 +1434,14 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest 0.10.6", ] [[package]] name = "home" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" dependencies = [ "winapi", ] @@ -1509,7 +1468,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.2", + "itoa 1.0.5", ] [[package]] @@ -1549,9 +1508,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -1562,7 +1521,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.2", + "itoa 1.0.5", "pin-project-lite", "socket2", "tokio", @@ -1573,9 +1532,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59df7c4e19c950e6e0e868dcc0a300b09a9b88e9ec55bd879ca819087a77355d" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", "hyper", @@ -1595,11 +1554,10 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -1634,7 +1592,7 @@ dependencies = [ "exr", "gif", "jpeg-decoder", - "num-rational 0.4.1", + "num-rational", "num-traits", "png", "scoped_threadpool", @@ -1643,18 +1601,18 @@ dependencies = [ [[package]] name = "include_dir" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "482a2e29200b7eed25d7fdbd14423326760b7f6658d21a4cf12d55a50713c69f" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e074c19deab2501407c91ba1860fa3d6820bfde307db6d8cb851b55a10be89b" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ "proc-macro2", "quote", @@ -1662,9 +1620,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -1699,6 +1657,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -1708,26 +1675,48 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys 0.42.0", +] + [[package]] name = "ipnet" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" [[package]] -name = "iso8601" +name = "is-terminal" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5b94fbeb759754d87e1daea745bc8efd3037cd16980331fe1d1524c9a79ce96" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys 0.42.0", +] + +[[package]] +name = "iso8601" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "296af15e112ec6dc38c9fd3ae027b5337a75466e8eed757bd7d5cf742ea85eb6" dependencies = [ "nom", ] [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] @@ -1740,9 +1729,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "java-properties" @@ -1757,9 +1746,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] @@ -1775,18 +1764,18 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] [[package]] name = "json-patch" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f995a3c8f2bc3dd52a18a583e90f9ec109c047fa1603a853e46bcda14d2e279d" +checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" dependencies = [ "serde", "serde_json", @@ -1806,9 +1795,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5af9646e616e37c61093ef85e25bd883ae0c22e2fa1e6eedfe590048247116e3" +checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -1821,9 +1810,9 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e85cfc9c2f17eab237fdfa2efe5c1608fd06a90e1e0d7fd7b10f2d0e153f375" +checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" dependencies = [ "anyhow", "futures-channel", @@ -1846,9 +1835,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673d68136e2f0f67323bab95b3a7177df26ac21ddbf395fc32d60f30fe5a1364" +checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" dependencies = [ "anyhow", "arrayvec 0.7.2", @@ -1875,9 +1864,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42007820863ab29f3adeacf43886ef54abaedb35bc33dada25771db4e1f94de4" +checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" dependencies = [ "async-trait", "hyper", @@ -1894,9 +1883,9 @@ dependencies = [ [[package]] name = "jsonrpsee-server" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78f34520019321bd466d00620606db2f40827362d0185b3b95040328eb502f6" +checksum = "1fb69dad85df79527c019659a992498d03f8495390496da2f07e6c24c2b356fc" dependencies = [ "futures-channel", "futures-util", @@ -1916,9 +1905,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7985a27ee315c7c8c5c5033ac133e9472aec881edfd947780f5a9970efb7cbbf" +checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" dependencies = [ "anyhow", "beef", @@ -1930,9 +1919,9 @@ dependencies = [ [[package]] name = "jsonrpsee-wasm-client" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46811fcec615d8e58228e7e281b3238693b26da1eb2469ac208af40a217bc8d9" +checksum = "a77310456f43c6c89bcba1f6b2fc2a28300da7c341f320f5128f8c83cc63232d" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -1941,9 +1930,9 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480fc9922f10b8fca3f07c07c51e137ddcf13fd60a304f117cfaa9e9bf41c60b" +checksum = "0b83daeecfc6517cfe210df24e570fb06213533dfb990318fae781f4c7119dd9" dependencies = [ "http", "jsonrpsee-client-transport", @@ -1953,18 +1942,19 @@ dependencies = [ [[package]] name = "jsonschema" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebd40599e7f1230ce296f73b88c022b98ed66689f97eaa54bbeadc337a2ffa6" +checksum = "6ca9e2b45609132ae2214d50482c03aeee78826cd6fd53a8940915b81acedf16" dependencies = [ "ahash", "anyhow", "base64", "bytecount", + "clap", "fancy-regex", "fraction", "iso8601", - "itoa 1.0.2", + "itoa 1.0.5", "lazy_static", "memchr", "num-cmp", @@ -1974,17 +1964,16 @@ dependencies = [ "reqwest", "serde", "serde_json", - "structopt", "time", "url", - "uuid 0.8.2", + "uuid 1.2.2", ] [[package]] name = "kqueue" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6112e8f37b59803ac47a42d14f1f3a59bbf72fc6857ffc5be455e28a691f8e" +checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" dependencies = [ "kqueue-sys", "libc", @@ -2039,9 +2028,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.126" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libflate" @@ -2065,9 +2054,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", @@ -2082,6 +2071,12 @@ dependencies = [ "safemem", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "local-ip-address" version = "0.4.9" @@ -2096,9 +2091,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -2129,12 +2124,6 @@ dependencies = [ "libc", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "markup5ever" version = "0.10.1" @@ -2175,9 +2164,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] @@ -2196,24 +2185,15 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "minisign" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43935b78ea0886357ab9259bd227879a54b12a83de261c3270aad584500cba2f" +checksum = "ce49953dd06a44e1034590bb619bfe8900c29500053c0c0f83e9260a34466aa5" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "rpassword", "scrypt", ] -[[package]] -name = "miniz_oxide" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.6.2" @@ -2225,14 +2205,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -2241,14 +2221,14 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", ] [[package]] name = "napi" -version = "2.10.0" +version = "2.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bace9a4026eaa6631804e2ff9030c47beb0483fbb12dc17950fe1530c4961f84" +checksum = "838b5b414a008e75b97edb3c3e6f189034af789a0608686299b149d3b0e66c39" dependencies = [ "bitflags", "ctor", @@ -2265,11 +2245,11 @@ checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e" [[package]] name = "napi-derive" -version = "2.9.0" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be75210f300e9fbf386ccac1c8eaaed23410e2f7f7aa9295b78c436a172ef51" +checksum = "af4e44e34e70aa61be9036ae652e27c20db5bca80e006be0f482419f6601352a" dependencies = [ - "convert_case 0.5.0", + "convert_case 0.6.0", "napi-derive-backend", "proc-macro2", "quote", @@ -2278,11 +2258,11 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ba4800264fac8a7726b208d5dd4c6d637d1027d73b026061a69d3339a0a930" +checksum = "17925fff04b6fa636f8e4b4608cc1a4f1360b64ac8ecbfdb7da1be1dc74f6843" dependencies = [ - "convert_case 0.5.0", + "convert_case 0.6.0", "once_cell", "proc-macro2", "quote", @@ -2301,9 +2281,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -2335,13 +2315,14 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "nix" -version = "0.24.1" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9" +checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" dependencies = [ "bitflags", "cfg-if", "libc", + "static_assertions", ] [[package]] @@ -2352,9 +2333,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "nom" -version = "7.1.1" +version = "7.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" dependencies = [ "memchr", "minimal-lexical", @@ -2380,9 +2361,9 @@ dependencies = [ [[package]] name = "notify-debouncer-mini" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c538ea1dd436b41e751922510cfbcaea2def87ed6ed94aa1edc15dc31b4c179" +checksum = "e23e9fa24f094b143c1eb61f90ac6457de87be6987bc70746e0179f7dbc9007b" dependencies = [ "crossbeam-channel", "notify", @@ -2390,23 +2371,23 @@ dependencies = [ [[package]] name = "num" -version = "0.2.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" dependencies = [ "num-bigint", "num-complex", "num-integer", "num-iter", - "num-rational 0.2.4", + "num-rational", "num-traits", ] [[package]] name = "num-bigint" -version = "0.2.6" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", @@ -2421,11 +2402,10 @@ checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" [[package]] name = "num-complex" -version = "0.2.4" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" dependencies = [ - "autocfg", "num-traits", ] @@ -2450,18 +2430,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-rational" version = "0.4.1" @@ -2469,6 +2437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", + "num-bigint", "num-integer", "num-traits", ] @@ -2484,20 +2453,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.6" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ + "hermit-abi 0.2.6", "libc", ] @@ -2531,15 +2491,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.14.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -2549,9 +2503,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.40" +version = "0.10.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" dependencies = [ "bitflags", "cfg-if", @@ -2590,9 +2544,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.74" +version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ "autocfg", "cc", @@ -2604,9 +2558,9 @@ dependencies = [ [[package]] name = "os_info" -version = "3.5.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5209b2162b2c140df493a93689e04f8deab3a67634f5bc7a553c0a98e5b8d399" +checksum = "c4750134fb6a5d49afc80777394ad5d95b04bc12068c6abb92fae8f43817270f" dependencies = [ "log", "serde", @@ -2615,19 +2569,19 @@ dependencies = [ [[package]] name = "os_pipe" -version = "1.0.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c92f2b54f081d635c77e7120862d48db8e91f7f21cef23ab1b4fe9971c59f55" +checksum = "c6a252f1f8c11e84b3ab59d7a488e48e4478a93937e027076638c49536204639" dependencies = [ "libc", - "winapi", + "windows-sys 0.42.0", ] [[package]] name = "os_str_bytes" -version = "6.1.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "parking" @@ -2647,25 +2601,25 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "password-hash" -version = "0.3.2" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] @@ -2683,11 +2637,11 @@ dependencies = [ [[package]] name = "pbkdf2" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.3", + "digest 0.10.6", "hmac", "password-hash", "sha2", @@ -2695,24 +2649,25 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.1.3" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" dependencies = [ + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.1.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +checksum = "96504449aa860c8dcde14f9fba5c58dc6658688ca1fe363589d6327b8662c603" dependencies = [ "pest", "pest_generator", @@ -2720,9 +2675,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.3" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +checksum = "798e0220d1111ae63d66cb66a5dcb3fc2d986d520b98e49e1852bfdb11d7c5e7" dependencies = [ "pest", "pest_meta", @@ -2733,13 +2688,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.1.3" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +checksum = "984298b75898e30a843e278a9f2452c31e349a073a0ce6fd950a12a74464e065" dependencies = [ - "maplit", + "once_cell", "pest", - "sha-1 0.8.2", + "sha1", ] [[package]] @@ -2842,18 +2797,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", @@ -2874,9 +2829,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "plist" @@ -2901,7 +2856,7 @@ dependencies = [ "bitflags", "crc32fast", "flate2", - "miniz_oxide 0.6.2", + "miniz_oxide", ] [[package]] @@ -2912,15 +2867,15 @@ checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug", "universal-hash", ] [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "precomputed-hash" @@ -2954,15 +2909,15 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.19" +version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" dependencies = [ "unicode-ident", ] @@ -2975,9 +2930,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -3004,7 +2959,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3024,7 +2979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -3038,11 +2993,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", ] [[package]] @@ -3065,21 +3020,19 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -3089,9 +3042,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -3102,16 +3055,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "redox_syscall", "thiserror", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", @@ -3120,9 +3073,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -3135,9 +3088,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" dependencies = [ "base64", "bytes", @@ -3190,9 +3143,20 @@ checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" [[package]] name = "rpassword" -version = "5.0.1" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +dependencies = [ + "libc", + "rtoolbox", + "winapi", +] + +[[package]] +name = "rtoolbox" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" dependencies = [ "libc", "winapi", @@ -3213,11 +3177,25 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.36.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.42.0", +] + [[package]] name = "rustls" -version = "0.20.6" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" dependencies = [ "log", "ring", @@ -3248,9 +3226,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "safemem" @@ -3260,11 +3238,11 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] name = "salsa20" -version = "0.9.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0fbb5f676da676c260ba276a8f43a8dc67cf02d1438423aeb1c677a7212686" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher", + "cipher 0.4.3", ] [[package]] @@ -3288,9 +3266,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed" +checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" dependencies = [ "dyn-clone", "schemars_derive", @@ -3301,9 +3279,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743" +checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" dependencies = [ "proc-macro2", "quote", @@ -3325,9 +3303,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scrypt" -version = "0.8.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e73d6d7c6311ebdbd9184ad6c4447b2f36337e327bda107d3ba9e3c374f9d325" +checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" dependencies = [ "hmac", "pbkdf2", @@ -3347,9 +3325,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ "bitflags", "core-foundation 0.9.3", @@ -3390,9 +3368,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.10" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "send_wrapper" @@ -3402,18 +3380,18 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -3433,11 +3411,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ - "itoa 1.0.2", + "itoa 1.0.5", "ryu", "serde", ] @@ -3449,7 +3427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.2", + "itoa 1.0.5", "ryu", "serde", ] @@ -3508,18 +3486,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - [[package]] name = "sha-1" version = "0.9.8" @@ -3530,29 +3496,29 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] name = "sha-1" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.6", ] [[package]] name = "sha1" -version = "0.10.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.6", ] [[package]] @@ -3563,13 +3529,13 @@ checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.6", ] [[package]] @@ -3599,9 +3565,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.8.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" @@ -3637,9 +3603,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" dependencies = [ "lock_api", ] @@ -3664,9 +3630,9 @@ checksum = "8207e78455ffdf55661170876f88daf85356e4edd54e0a3dbc79586ca1e50cbe" [[package]] name = "stfu8" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "019f0c664fd85d5a87dcfb62b40b691055392a35a6e59f4df83d4b770db7e876" +checksum = "1310970b29733b601839578f8ba24991a97057dbedc4ac0decea835474054ee7" dependencies = [ "lazy_static", "regex", @@ -3698,42 +3664,12 @@ dependencies = [ "quote", ] -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "sublime_fuzzy" version = "0.7.0" @@ -3754,9 +3690,9 @@ checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08" [[package]] name = "syn" -version = "1.0.98" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -3771,9 +3707,9 @@ checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" [[package]] name = "sysctl" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1123645dfaf2b5eac6b6c88addafc359c789b8ef2a770ecaef758c1ddf363ea4" +checksum = "225e483f02d0ad107168dc57381a8a40c3aeea6abe47f37506931f861643cfa8" dependencies = [ "bitflags", "byteorder", @@ -3803,8 +3739,8 @@ dependencies = [ "bitness", "dirs-next", "glob", - "handlebars 4.3.1", - "heck 0.4.0", + "handlebars 4.3.6", + "heck", "hex", "image", "libflate", @@ -3816,7 +3752,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "strsim 0.10.0", + "strsim", "tar", "tauri-icns", "tauri-utils", @@ -3824,7 +3760,7 @@ dependencies = [ "thiserror", "time", "toml", - "uuid 1.1.2", + "uuid 1.2.2", "walkdir", "winreg", "zip", @@ -3837,14 +3773,14 @@ dependencies = [ "anyhow", "axum", "base64", - "clap 4.0.9", + "clap", "colored 2.0.0", "common-path", "ctrlc", "dialoguer", "env_logger", - "handlebars 4.3.1", - "heck 0.4.0", + "handlebars 4.3.6", + "heck", "html5ever", "ignore", "image", @@ -3909,8 +3845,7 @@ dependencies = [ [[package]] name = "tauri-mobile" version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff04e700514e294069c697559b74f2cc168e8d6c984e2e4ba99a40d63468061" +source = "git+https://github.com/tauri-apps/tauri-mobile?branch=dev#e66a6ab0e5dc3b474dad6793621c499974953915" dependencies = [ "cocoa", "colored 1.9.3", @@ -3921,7 +3856,7 @@ dependencies = [ "english-numbers", "freedesktop_entry_parser", "handlebars 3.5.5", - "heck 0.4.0", + "heck", "home", "ignore", "indexmap", @@ -3951,9 +3886,9 @@ version = "2.0.0-alpha.0" dependencies = [ "aes-gcm", "ctor", - "getrandom 0.2.7", + "getrandom 0.2.8", "glob", - "heck 0.4.0", + "heck", "html5ever", "infer", "json-patch", @@ -4018,16 +3953,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -4046,18 +3971,18 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -4084,9 +4009,9 @@ dependencies = [ [[package]] name = "tiff" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17def29300a156c19ae30814710d9c63cd50288a49c6fd3a10ccfbe4cf886fd" +checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" dependencies = [ "flate2", "jpeg-decoder", @@ -4095,21 +4020,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.11" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ - "itoa 1.0.2", - "libc", - "num_threads", + "itoa 1.0.5", + "serde", + "time-core", "time-macros", ] +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + [[package]] name = "time-macros" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] [[package]] name = "tinyvec" @@ -4128,9 +4062,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.21.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "7125661431c26622a80ca5051a2f936c9a678318e0351007b0cc313143024e5c" dependencies = [ "autocfg", "bytes", @@ -4141,14 +4075,14 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.42.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -4206,9 +4140,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" dependencies = [ "indexmap", "serde", @@ -4243,9 +4177,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ "bitflags", "bytes", @@ -4262,9 +4196,9 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" @@ -4274,9 +4208,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", @@ -4298,9 +4232,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", ] @@ -4333,7 +4267,7 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "sha-1 0.10.0", + "sha-1 0.10.1", "thiserror", "url", "utf-8", @@ -4341,15 +4275,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "uname" @@ -4368,15 +4302,15 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" -version = "0.1.20" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] @@ -4389,9 +4323,9 @@ checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "universal-hash" @@ -4399,7 +4333,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" dependencies = [ - "generic-array 0.14.5", + "generic-array", "subtle", ] @@ -4411,12 +4345,11 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f" +checksum = "733b5ad78377302af52c0dbcb2623d78fe50e4b3bf215948ff29e9ee031d8566" dependencies = [ "base64", - "chunked_transfer", "flate2", "log", "once_cell", @@ -4428,9 +4361,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", @@ -4452,11 +4385,11 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.1.2" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "sha1_smol", ] @@ -4477,12 +4410,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" @@ -4501,9 +4428,9 @@ dependencies = [ [[package]] name = "vswhom-sys" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22025f6d8eb903ebf920ea6933b70b1e495be37e2cb4099e62c80454aaf57c39" +checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" dependencies = [ "cc", "libc", @@ -4550,25 +4477,23 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", - "serde", - "serde_json", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -4577,9 +4502,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.31" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if", "js-sys", @@ -4589,9 +4514,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4599,9 +4524,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -4612,15 +4537,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -4638,18 +4563,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.3" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki", ] [[package]] name = "weezl" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "winapi" @@ -4868,15 +4793,15 @@ checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" [[package]] name = "zeroize" -version = "1.5.5" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94693807d016b2f2d2e14420eb3bfcca689311ff775dcf113d74ea624b7cdf07" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" [[package]] name = "zip" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf225bcf73bb52cbb496e70475c7bd7a3f769df699c0020f6c7bd9a96dcf0b8d" +checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080" dependencies = [ "aes", "byteorder", @@ -4894,18 +4819,18 @@ dependencies = [ [[package]] name = "zstd" -version = "0.10.2+zstd.1.5.2" +version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4a6bd64f22b5e3e94b4e238669ff9f10815c27a5180108b849d24174a83847" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "4.1.6+zstd.1.5.2" +version = "5.0.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b61c51bb270702d6167b8ce67340d2754b088d0c091b06e593aa772c3ee9bb" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" dependencies = [ "libc", "zstd-sys", @@ -4913,9 +4838,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "1.6.3+zstd.1.5.2" +version = "2.0.4+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8" +checksum = "4fa202f2ef00074143e219d15b62ffc317d17cc33909feac471c044087cad7b0" dependencies = [ "cc", "libc", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 37dd5d35d54..be08a2ff162 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -39,7 +39,7 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -tauri-mobile = { version = "0.1.4", default-features = false } +tauri-mobile = { git = "https://github.com/tauri-apps/tauri-mobile", branch = "dev", default-features = false } textwrap = { version = "0.11.0", features = [ "term_size" ] } jsonrpsee = { version = "0.16", features = [ "client", "server" ] } thiserror = "1" @@ -73,7 +73,7 @@ heck = "0.4" dialoguer = "0.10" url = { version = "2.3", features = [ "serde" ] } os_pipe = "1" -ignore = "0.4" +ignore = "=0.4.18" ctrlc = "3.2" log = { version = "0.4.17", features = [ "kv_unstable", "kv_unstable_std" ] } env_logger = "0.9.1" diff --git a/tooling/cli/node/package.json b/tooling/cli/node/package.json index 38c85edd460..04192567782 100644 --- a/tooling/cli/node/package.json +++ b/tooling/cli/node/package.json @@ -45,6 +45,9 @@ "jest-transform-toml": "1.0.0", "prettier": "2.8.1" }, + "resolutions": { + "json5": "2.2.3" + }, "engines": { "node": ">= 10" }, diff --git a/tooling/cli/node/yarn.lock b/tooling/cli/node/yarn.lock index 4b249b6c73a..aae226aea4a 100644 --- a/tooling/cli/node/yarn.lock +++ b/tooling/cli/node/yarn.lock @@ -1822,17 +1822,10 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - -json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@2.2.3, json5@^2.1.2, json5@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonfile@^6.0.1: version "6.1.0" @@ -1911,11 +1904,6 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" diff --git a/tooling/cli/src/icon.rs b/tooling/cli/src/icon.rs index 6535349d179..faa351dcc3f 100644 --- a/tooling/cli/src/icon.rs +++ b/tooling/cli/src/icon.rs @@ -328,7 +328,7 @@ fn png(source: &DynamicImage, out_dir: &Path) -> Result<()> { |_app, config, _metadata, _cli_options| { let android_out = out_dir.parent().unwrap().join(format!( "gen/android/{}/app/src/main/res/", - config.app().name() + config.app().name_snake() )); let out = if android_out.exists() { android_out diff --git a/tooling/cli/src/interface/mod.rs b/tooling/cli/src/interface/mod.rs index 40a474c6ade..79c65aeb48f 100644 --- a/tooling/cli/src/interface/mod.rs +++ b/tooling/cli/src/interface/mod.rs @@ -37,6 +37,7 @@ pub trait AppSettings { target: &str, ) -> crate::Result>; fn app_name(&self) -> Option; + fn lib_name(&self) -> Option; fn get_bundler_settings( &self, diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 1adaa438b74..2bbfd370cd5 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -772,6 +772,18 @@ impl AppSettings for RustAppSettings { .and_then(|n| n.as_str()) .map(|n| n.to_string()) } + + fn lib_name(&self) -> Option { + self + .manifest + .inner + .as_table() + .get("lib") + .and_then(|p| p.as_table()) + .and_then(|p| p.get("name")) + .and_then(|n| n.as_str()) + .map(|n| n.to_string()) + } } impl RustAppSettings { diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 304abcf52dc..a78ee4fafb9 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -107,8 +107,11 @@ pub fn get_config( ..Default::default() }; - set_var("WRY_ANDROID_REVERSED_DOMAIN", app.reverse_domain()); - set_var("WRY_ANDROID_APP_NAME_SNAKE_CASE", app.name()); + set_var( + "WRY_ANDROID_PACKAGE", + format!("{}.{}", app.reverse_domain(), app.name_snake()), + ); + set_var("WRY_ANDROID_LIBRARY", app.lib_name()); set_var( "WRY_ANDROID_KOTLIN_FILES_OUT_DIR", config @@ -117,7 +120,7 @@ pub fn get_config( .join(format!( "java/{}/{}", app.reverse_domain().replace('.', "/"), - app.name() + app.name_snake() )) .join("generated"), ); diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs index abfe5ad0e1c..2db507e20c5 100644 --- a/tooling/cli/src/mobile/android/project.rs +++ b/tooling/cli/src/mobile/android/project.rs @@ -42,7 +42,7 @@ pub fn gen( Path::new(&os::replace_path_separator( util::relativize_path( config.app().root_dir(), - config.project_dir().join(config.app().name()), + config.project_dir().join(config.app().name_snake()), ) .into_os_string(), )), @@ -83,7 +83,7 @@ pub fn gen( map.insert("windows", cfg!(windows)); let domain = config.app().reverse_domain().replace('.', "/"); - let package_path = format!("java/{}/{}", domain, config.app().name()); + let package_path = format!("java/{}/{}", domain, config.app().name_snake()); map.insert("package-path", &package_path); diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 94a8af78646..25a2369338b 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -6,7 +6,6 @@ use crate::{ }; use clap::Parser; -use heck::AsSnakeCase; use tauri_mobile::{apple::target::Target, opts::Profile, util}; use std::{collections::HashMap, ffi::OsStr, path::PathBuf}; @@ -188,7 +187,7 @@ pub fn command(options: Options) -> Result<()> { })?; let out_dir = bin_path.parent().unwrap(); - let lib_path = out_dir.join(format!("lib{}.a", AsSnakeCase(config.app().name()))); + let lib_path = out_dir.join(format!("lib{}.a", config.app().lib_name())); if !lib_path.exists() { return Err(anyhow::anyhow!("Library not found at {}. Make sure your Cargo.toml file has a [lib] block with `crate-type = [\"staticlib\", \"cdylib\", \"rlib\"]`", lib_path.display())); } @@ -201,7 +200,7 @@ pub fn command(options: Options) -> Result<()> { format!( "gen/apple/Externals/{rust_triple}/{}/lib{}.a", profile.as_str(), - AsSnakeCase(config.app().name()) + config.app().lib_name() ), )?; } diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 33edad6dcfc..30e94e771b4 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -229,9 +229,11 @@ fn get_app(config: &TauriConfig) -> App { .expect("failed to load interface"); let app_name = interface.app_settings().app_name().unwrap_or(app_name); + let lib_name = interface.app_settings().lib_name(); let raw = RawAppConfig { name: app_name, + lib_name, stylized_name: config.package.product_name.clone(), domain, asset_dir: None, diff --git a/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest b/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest index 8ff358f73a1..bae491d80b1 100755 --- a/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest +++ b/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest @@ -11,6 +11,7 @@ rust-version = "1.59" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] +name = "app_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] @@ -22,9 +23,7 @@ serde = { version = "1.0", features = ["derive"] } tauri = {{{ tauri_dep }}} [features] -# by default Tauri runs in production mode -# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL -default = [ "custom-protocol" ] -# this feature is used for production builds where `devPath` points to the filesystem +default = [] +# this feature is used for production builds or when `devPath` points to the filesystem # DO NOT remove this custom-protocol = [ "tauri/custom-protocol" ] diff --git a/tooling/cli/templates/app/src-tauri/src/desktop.rs b/tooling/cli/templates/app/src-tauri/src/desktop.rs deleted file mode 100644 index c43e6eaf38f..00000000000 --- a/tooling/cli/templates/app/src-tauri/src/desktop.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![cfg_attr( - all(not(debug_assertions), target_os = "windows"), - windows_subsystem = "windows" -)] - -pub fn main() { - app::AppBuilder::new().run(); -} diff --git a/tooling/cli/templates/app/src-tauri/src/lib.rs b/tooling/cli/templates/app/src-tauri/src/lib.rs index d8c1e87726f..23a44f80899 100644 --- a/tooling/cli/templates/app/src-tauri/src/lib.rs +++ b/tooling/cli/templates/app/src-tauri/src/lib.rs @@ -1,41 +1,7 @@ -use tauri::App; - -#[cfg(mobile)] -mod mobile; -#[cfg(mobile)] -pub use mobile::*; - -pub type SetupHook = Box Result<(), Box> + Send>; - -#[derive(Default)] -pub struct AppBuilder { - setup: Option, -} - -impl AppBuilder { - pub fn new() -> Self { - Self::default() - } - - #[must_use] - pub fn setup(mut self, setup: F) -> Self - where - F: FnOnce(&mut App) -> Result<(), Box> + Send + 'static, - { - self.setup.replace(Box::new(setup)); - self - } - - pub fn run(self) { - let setup = self.setup; - tauri::Builder::default() - .setup(move |app| { - if let Some(setup) = setup { - (setup)(app)?; - } - Ok(()) - }) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); - } +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .invoke_handler(tauri::generate_handler![greet]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); } diff --git a/tooling/cli/templates/app/src-tauri/src/main.rs b/tooling/cli/templates/app/src-tauri/src/main.rs index 1ade16f98e3..52b6a6f297c 100644 --- a/tooling/cli/templates/app/src-tauri/src/main.rs +++ b/tooling/cli/templates/app/src-tauri/src/main.rs @@ -1,7 +1,9 @@ -#[cfg(desktop)] -mod desktop; +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] fn main() { #[cfg(desktop)] - desktop::main(); + app_lib::run(); } diff --git a/tooling/cli/templates/app/src-tauri/src/mobile.rs b/tooling/cli/templates/app/src-tauri/src/mobile.rs deleted file mode 100644 index cacaf5f11d2..00000000000 --- a/tooling/cli/templates/app/src-tauri/src/mobile.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[tauri::mobile_entry_point] -fn main() { - super::AppBuilder::new().run() -} From c36d451bcd9576dd4e0c673078c421dcb83932be Mon Sep 17 00:00:00 2001 From: Simon Hyll Date: Mon, 16 Jan 2023 16:35:23 +0100 Subject: [PATCH 120/423] fix: removed greet from template (#6066) * fix: removed greet from template * fix: removed the entire line instead as per request --- tooling/cli/templates/app/src-tauri/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tooling/cli/templates/app/src-tauri/src/lib.rs b/tooling/cli/templates/app/src-tauri/src/lib.rs index 23a44f80899..cfef8d12b93 100644 --- a/tooling/cli/templates/app/src-tauri/src/lib.rs +++ b/tooling/cli/templates/app/src-tauri/src/lib.rs @@ -1,7 +1,6 @@ #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() - .invoke_handler(tauri::generate_handler![greet]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } From 276a0362de62998c3b485213c96dc0361f3ad68a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 17 Jan 2023 12:42:27 -0300 Subject: [PATCH 121/423] chore(deps): update wry --- core/tauri-runtime-wry/src/lib.rs | 4 ++-- examples/api/src-tauri/Cargo.lock | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index cc3ebba2caa..81b0cb4015f 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -969,10 +969,10 @@ fn decode_path(path: PathBuf) -> PathBuf { impl From for FileDropEvent { fn from(event: FileDropEventWrapper) -> Self { match event.0 { - WryFileDropEvent::Hovered(paths) => { + WryFileDropEvent::Hovered { paths, position: _ } => { FileDropEvent::Hovered(paths.into_iter().map(decode_path).collect()) } - WryFileDropEvent::Dropped(paths) => { + WryFileDropEvent::Dropped { paths, position: _ } => { FileDropEvent::Dropped(paths.into_iter().map(decode_path).collect()) } // default to cancelled diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 1a48aaa4f2f..2a254201bdd 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -2881,9 +2881,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2845fd58915455c5faf2c9ac5d8a5ed43bd23ab57f0a67d63612936209eae74" +checksum = "704522803dda895767f69198af8351b0a3f4fe2e293c3ca54cce0ecba05a97f2" dependencies = [ "bitflags", "cairo-rs", @@ -2915,11 +2915,11 @@ dependencies = [ "objc", "once_cell", "parking_lot", - "paste", "png", "raw-window-handle", "scopeguard", "serde", + "tao-macros", "unicode-segmentation", "uuid 1.2.1", "windows 0.39.0", @@ -2927,6 +2927,17 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "tao-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b6fcd8245d45a39ffc8715183d92ae242750eb57b285eb3bcd63dfd512afd09" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tar" version = "0.4.38" @@ -4043,9 +4054,8 @@ dependencies = [ [[package]] name = "wry" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1ad8e2424f554cc5bdebe8aa374ef5b433feff817aebabca0389961fc7ef98" +version = "0.24.1" +source = "git+https://github.com/tauri-apps/wry?branch=dev#bce39e2be195194e547b0021e770e45a3df15fa1" dependencies = [ "base64", "block", From 630a7f4b18cef169bfd48673609306fec434e397 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Tue, 17 Jan 2023 08:13:53 -0800 Subject: [PATCH 122/423] refactor: remove mobile log initialization, ref #6049 (#6081) --- .changes/package-info-crate-name.md | 6 ++++ .changes/remove-mobile-log.md | 7 ++++ core/tauri-build/src/lib.rs | 6 ---- core/tauri-codegen/src/context.rs | 1 + core/tauri-macros/src/mobile.rs | 5 +-- core/tauri-utils/src/lib.rs | 2 ++ core/tauri/Cargo.toml | 2 -- core/tauri/src/lib.rs | 17 +-------- core/tauri/src/test/mod.rs | 1 + examples/api/src-tauri/Cargo.lock | 55 +---------------------------- examples/api/src-tauri/Cargo.toml | 1 + examples/api/src-tauri/src/cmd.rs | 2 +- 12 files changed, 22 insertions(+), 83 deletions(-) create mode 100644 .changes/package-info-crate-name.md create mode 100644 .changes/remove-mobile-log.md diff --git a/.changes/package-info-crate-name.md b/.changes/package-info-crate-name.md new file mode 100644 index 00000000000..4fab428bb86 --- /dev/null +++ b/.changes/package-info-crate-name.md @@ -0,0 +1,6 @@ +--- +"tauri-utils": patch +"tauri-codegen": patch +--- + +Added `crate_name` field on `PackageInfo`. diff --git a/.changes/remove-mobile-log.md b/.changes/remove-mobile-log.md new file mode 100644 index 00000000000..b0391e28852 --- /dev/null +++ b/.changes/remove-mobile-log.md @@ -0,0 +1,7 @@ +--- +"tauri": patch +"tauri-macros": patch +"tauri-build": patch +--- + +Removed mobile logging initialization, which will be handled by `tauri-plugin-log`. diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 5e62e94953d..b3d157a74ff 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -221,20 +221,14 @@ pub fn try_build(attributes: Attributes) -> Result<()> { let s = config.tauri.bundle.identifier.split('.'); let last = s.clone().count() - 1; - let mut domain = String::new(); let mut android_package_prefix = String::new(); for (i, w) in s.enumerate() { if i != last { - domain.push_str(w); - domain.push('.'); - android_package_prefix.push_str(w); android_package_prefix.push('_'); } } - domain.pop(); android_package_prefix.pop(); - println!("cargo:rustc-env=TAURI_MOBILE_DOMAIN={domain}"); println!("cargo:rustc-env=TAURI_ANDROID_PACKAGE_PREFIX={android_package_prefix}"); cfg_alias("dev", !has_feature("custom-protocol")); diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index c3ac02b56b3..32e2c05faef 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -303,6 +303,7 @@ pub fn context_codegen(data: ContextData) -> Result TokenStream { &mut error, &function, ); - let domain_str = var("TAURI_MOBILE_DOMAIN").unwrap(); - let app_name_str = var("CARGO_PKG_NAME").unwrap(); if let Some(e) = error { quote!(#e).into() @@ -61,10 +59,9 @@ pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { fn _start_app() { #[cfg(target_os = "ios")] - ::tauri::init_logging(&format!("{}.{}", #domain_str, #app_name_str)); + ::tauri::log_stdout(); #[cfg(target_os = "android")] { - ::tauri::init_logging(#app_name_str); use ::tauri::paste; ::tauri::wry_android_binding!(#domain, #app_name, _start_app, ::tauri::wry); } diff --git a/core/tauri-utils/src/lib.rs b/core/tauri-utils/src/lib.rs index 895d9c80c88..1fb1202eae5 100644 --- a/core/tauri-utils/src/lib.rs +++ b/core/tauri-utils/src/lib.rs @@ -34,6 +34,8 @@ pub struct PackageInfo { pub authors: &'static str, /// The crate description. pub description: &'static str, + /// The crate name. + pub crate_name: &'static str, } impl PackageInfo { diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 051158d6937..f008d8f5ab8 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -110,11 +110,9 @@ win7-notifications = { version = "0.3.1", optional = true } [target."cfg(target_os = \"android\")".dependencies] paste = "1.0" -android_logger = "0.9" log = "0.4" [target."cfg(target_os = \"ios\")".dependencies] -oslog = "0.2" log = "0.4" libc = "0.2" diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index d09e7a61f63..237c64988f1 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -278,19 +278,9 @@ pub use self::runtime::ClipboardManager; #[cfg_attr(doc_cfg, doc(cfg(feature = "global-shortcut")))] pub use self::runtime::GlobalShortcutManager; -#[cfg(target_os = "android")] -#[doc(hidden)] -pub fn init_logging(tag: &str) { - android_logger::init_once( - android_logger::Config::default() - .with_min_level(log::Level::Trace) - .with_tag(tag), - ); -} - #[cfg(target_os = "ios")] #[doc(hidden)] -pub fn init_logging(subsystem: &str) { +pub fn log_stdout() { use std::{ ffi::CString, fs::File, @@ -323,11 +313,6 @@ pub fn init_logging(subsystem: &str) { } } }); - - oslog::OsLogger::new(subsystem) - .level_filter(log::LevelFilter::Trace) - .init() - .unwrap(); } /// Updater events. diff --git a/core/tauri/src/test/mod.rs b/core/tauri/src/test/mod.rs index 9d096375a1d..217a85d263e 100644 --- a/core/tauri/src/test/mod.rs +++ b/core/tauri/src/test/mod.rs @@ -74,6 +74,7 @@ pub fn mock_context(assets: A) -> crate::Context { version: "0.1.0".parse().unwrap(), authors: "Tauri", description: "Tauri test", + crate_name: "test", }, _info_plist: (), pattern: Pattern::Brownfield(std::marker::PhantomData), diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 2a254201bdd..f2c2265c3b8 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -67,24 +67,6 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "android_log-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" - -[[package]] -name = "android_logger" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec2333c185d826313162cee39d3fcc6a84ba08114a839bebf53b961e7e75773" -dependencies = [ - "android_log-sys", - "env_logger", - "lazy_static", - "log", -] - [[package]] name = "anyhow" version = "1.0.66" @@ -95,6 +77,7 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" name = "api" version = "0.1.0" dependencies = [ + "log", "serde", "serde_json", "tauri", @@ -610,19 +593,6 @@ dependencies = [ "syn", ] -[[package]] -name = "dashmap" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" -dependencies = [ - "cfg-if", - "hashbrown", - "lock_api", - "once_cell", - "parking_lot_core", -] - [[package]] name = "dbus" version = "0.9.6" @@ -720,16 +690,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "log", - "regex", -] - [[package]] name = "fastrand" version = "1.8.0" @@ -1935,17 +1895,6 @@ version = "6.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" -[[package]] -name = "oslog" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d2043d1f61d77cb2f4b1f7b7b2295f40507f5f8e9d1c8bf10a1ca5f97a3969" -dependencies = [ - "cc", - "dashmap", - "log", -] - [[package]] name = "overload" version = "0.1.1" @@ -2953,7 +2902,6 @@ dependencies = [ name = "tauri" version = "2.0.0-alpha.2" dependencies = [ - "android_logger", "anyhow", "attohttpc", "base64", @@ -2982,7 +2930,6 @@ dependencies = [ "open", "os_info", "os_pipe", - "oslog", "paste", "percent-encoding", "png", diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 4f936ca8361..599453b8dda 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -16,6 +16,7 @@ tauri-build = { path = "../../../core/tauri-build", features = ["codegen", "isol serde_json = "1.0" serde = { version = "1.0", features = [ "derive" ] } tiny_http = "0.11" +log = "0.4" [dependencies.tauri] path = "../../../core/tauri" diff --git a/examples/api/src-tauri/src/cmd.rs b/examples/api/src-tauri/src/cmd.rs index 78e188023ec..58bd57b2e9c 100644 --- a/examples/api/src-tauri/src/cmd.rs +++ b/examples/api/src-tauri/src/cmd.rs @@ -14,7 +14,7 @@ pub struct RequestBody { #[command] pub fn log_operation(event: String, payload: Option) { - println!("{} {:?}", event, payload); + log::info!("{} {:?}", event, payload); } #[command] From 8cc111494d74161e489152e52191e1442dd99759 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 17 Jan 2023 19:27:23 -0300 Subject: [PATCH 123/423] fix(cli): print Android logs for all tags --- .changes/logcat-all-tags.md | 6 ++++++ tooling/cli/Cargo.lock | 4 ++-- tooling/cli/src/mobile/android.rs | 15 +++++++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 .changes/logcat-all-tags.md diff --git a/.changes/logcat-all-tags.md b/.changes/logcat-all-tags.md new file mode 100644 index 00000000000..bc10a620090 --- /dev/null +++ b/.changes/logcat-all-tags.md @@ -0,0 +1,6 @@ +--- +"cli.rs": patch +"cli.js": patch +--- + +Print log output for all tags on Android development. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 246df9fdbd8..bff0f2e3cea 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3844,8 +3844,8 @@ dependencies = [ [[package]] name = "tauri-mobile" -version = "0.1.4" -source = "git+https://github.com/tauri-apps/tauri-mobile?branch=dev#e66a6ab0e5dc3b474dad6793621c499974953915" +version = "0.2.0" +source = "git+https://github.com/tauri-apps/tauri-mobile?branch=dev#01c5f5083760714163e527bf9b8c80157150aed8" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index a78ee4fafb9..c9551aa4ae7 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -19,7 +19,7 @@ use tauri_mobile::{ target::Target, }, config::app::App, - opts::NoiseLevel, + opts::{FilterLevel, NoiseLevel}, os, util::prompt, }; @@ -95,7 +95,18 @@ pub fn get_config( let raw = RawAndroidConfig { features: android_options.features.clone(), - logcat_filter_specs: vec!["RustStdoutStderr".into(), "*:E".into()], + logcat_filter_specs: vec![ + "RustStdoutStderr".into(), + format!( + "*:{}", + match cli_options.noise_level { + NoiseLevel::Polite => FilterLevel::Info, + NoiseLevel::LoudAndProud => FilterLevel::Debug, + NoiseLevel::FranklyQuitePedantic => FilterLevel::Verbose, + } + .logcat() + ), + ], ..Default::default() }; let config = AndroidConfig::from_raw(app.clone(), Some(raw)).unwrap(); From 78eaadae2e75ab165d1970e592bb1455bb8636e3 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 23 Jan 2023 13:25:25 -0800 Subject: [PATCH 124/423] refactor(core): only proxy on mobile (#6126) Co-authored-by: Amr Bashir --- .changes/only-proxy-on-mobile.md | 5 ++++ core/tauri/src/manager.rs | 42 +++++++++++++++++++------------- 2 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 .changes/only-proxy-on-mobile.md diff --git a/.changes/only-proxy-on-mobile.md b/.changes/only-proxy-on-mobile.md new file mode 100644 index 00000000000..a8558d2b45c --- /dev/null +++ b/.changes/only-proxy-on-mobile.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +Only proxy the dev server on mobile to simplify desktop usage. diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index fff687eab1c..48cccf053a1 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -73,6 +73,12 @@ const WINDOW_FILE_DROP_HOVER_EVENT: &str = "tauri://file-drop-hover"; const WINDOW_FILE_DROP_CANCELLED_EVENT: &str = "tauri://file-drop-cancelled"; const MENU_EVENT: &str = "tauri://menu"; +// we need to proxy the dev server on mobile because we can't use `localhost`, so we use the local IP address +// and we do not get a secure context without the custom protocol that proxies to the dev server +// additionally, we need the custom protocol to inject the initialization scripts on Android +// must also keep in sync with the `let mut response` assignment in prepare_uri_scheme_protocol +const PROXY_DEV_SERVER: bool = cfg!(all(dev, mobile)); + #[derive(Default)] /// Spaced and quoted Content-Security-Policy hash values. struct CspHashStrings { @@ -373,7 +379,7 @@ impl WindowManager { fn get_browser_origin(&self) -> String { match self.base_path() { AppUrl::Url(WindowUrl::External(url)) => { - if cfg!(dev) && !cfg!(target_os = "linux") { + if PROXY_DEV_SERVER { format_real_schema("tauri") } else { url.origin().ascii_serialization() @@ -884,7 +890,7 @@ impl WindowManager { >, ) -> Box Result> + Send + Sync> { - #[cfg(dev)] + #[cfg(all(dev, mobile))] let url = { let mut url = self.get_url().as_str().to_string(); if url.ends_with('/') { @@ -892,11 +898,11 @@ impl WindowManager { } url }; - #[cfg(not(dev))] + #[cfg(not(all(dev, mobile)))] let manager = self.clone(); let window_origin = window_origin.to_string(); - #[cfg(dev)] + #[cfg(all(dev, mobile))] #[derive(Clone)] struct CachedResponse { status: http::StatusCode, @@ -904,16 +910,17 @@ impl WindowManager { body: Cow<'static, [u8]>, } - #[cfg(dev)] + #[cfg(all(dev, mobile))] let response_cache = Arc::new(Mutex::new(HashMap::new())); Box::new(move |request| { // use the entire URI as we are going to proxy the request - #[cfg(dev)] - let path = request.uri(); - // ignore query string and fragment - #[cfg(not(dev))] - let path = request.uri().split(&['?', '#'][..]).next().unwrap(); + let path = if PROXY_DEV_SERVER { + request.uri() + } else { + // ignore query string and fragment + request.uri().split(&['?', '#'][..]).next().unwrap() + }; let path = path .strip_prefix("tauri://localhost") @@ -925,7 +932,7 @@ impl WindowManager { let mut builder = HttpResponseBuilder::new().header("Access-Control-Allow-Origin", &window_origin); - #[cfg(dev)] + #[cfg(all(dev, mobile))] let mut response = { use attohttpc::StatusCode; let decoded_path = percent_encoding::percent_decode(path.as_bytes()) @@ -970,7 +977,7 @@ impl WindowManager { } }; - #[cfg(not(dev))] + #[cfg(not(all(dev, mobile)))] let mut response = { let asset = manager.get_asset(path)?; builder = builder.mimetype(&asset.mime_type); @@ -1207,10 +1214,11 @@ impl WindowManager { #[allow(unused_mut)] // mut url only for the data-url parsing let (is_local, mut url) = match &pending.webview_attributes.url { WindowUrl::App(path) => { - #[cfg(target_os = "linux")] - let url = self.get_url(); - #[cfg(not(target_os = "linux"))] - let url: Cow<'_, Url> = Cow::Owned(Url::parse("tauri://localhost").unwrap()); + let url = if PROXY_DEV_SERVER { + Cow::Owned(Url::parse("tauri://localhost").unwrap()) + } else { + self.get_url() + }; ( true, // ignore "index.html" just to simplify the url @@ -1229,7 +1237,7 @@ impl WindowManager { let config_url = self.get_url(); let is_local = config_url.make_relative(url).is_some(); let mut url = url.clone(); - if is_local && !cfg!(target_os = "linux") { + if is_local && PROXY_DEV_SERVER { url.set_scheme("tauri").unwrap(); url.set_host(Some("localhost")).unwrap(); } From 1af9be904a309138b9f79dc741391000b1652c75 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 23 Jan 2023 13:26:13 -0800 Subject: [PATCH 125/423] feat(cli): properly fill target for TAURI_ env vars on mobile (#6116) --- .changes/fix-mobile-env-vars.md | 6 +++ examples/api/vite.config.js | 2 +- tooling/cli/src/dev.rs | 26 +----------- tooling/cli/src/mobile/android.rs | 3 +- tooling/cli/src/mobile/android/build.rs | 12 +++++- tooling/cli/src/mobile/android/dev.rs | 54 +++++++++++++------------ tooling/cli/src/mobile/ios.rs | 3 +- tooling/cli/src/mobile/ios/build.rs | 12 +++++- tooling/cli/src/mobile/ios/dev.rs | 48 +++++++++++++--------- tooling/cli/src/mobile/mod.rs | 44 +++++++++++++++++++- 10 files changed, 133 insertions(+), 77 deletions(-) create mode 100644 .changes/fix-mobile-env-vars.md diff --git a/.changes/fix-mobile-env-vars.md b/.changes/fix-mobile-env-vars.md new file mode 100644 index 00000000000..8c1bd2c8a4f --- /dev/null +++ b/.changes/fix-mobile-env-vars.md @@ -0,0 +1,6 @@ +--- +"cli.rs": patch +"cli.js": patch +--- + +Fixes `TAURI_*` environment variables for hook scripts on mobile commands. diff --git a/examples/api/vite.config.js b/examples/api/vite.config.js index 26249285ea4..f4d59a2933a 100644 --- a/examples/api/vite.config.js +++ b/examples/api/vite.config.js @@ -5,7 +5,7 @@ import { internalIpV4 } from 'internal-ip' // https://vitejs.dev/config/ export default defineConfig(async ({ command, mode }) => { - const host = await internalIpV4() + const host = process.env.TAURI_PLATFORM === 'android' || process.env.TAURI_PLATFORM === 'ios' ? (await internalIpV4()) : 'localhost' return { plugins: [Unocss(), svelte()], build: { diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index a4e6e399a59..df10d98aab7 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -86,7 +86,7 @@ fn command_internal(mut options: Options) -> Result<()> { }) } -fn local_ip_address() -> &'static IpAddr { +pub fn local_ip_address() -> &'static IpAddr { static LOCAL_IP: OnceCell = OnceCell::new(); LOCAL_IP.get_or_init(|| { let addresses: Vec = local_ip_address::list_afinet_netifas() @@ -148,30 +148,6 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { .dev_path .clone(); - if mobile { - if let AppUrl::Url(WindowUrl::External(url)) = &mut dev_path { - let localhost = match url.host() { - Some(url::Host::Domain(d)) => d == "localhost", - Some(url::Host::Ipv4(i)) => { - i == std::net::Ipv4Addr::LOCALHOST || i == std::net::Ipv4Addr::UNSPECIFIED - } - _ => false, - }; - if localhost { - let ip = local_ip_address(); - url.set_host(Some(&ip.to_string())).unwrap(); - if let Some(c) = &options.config { - let mut c: tauri_utils::config::Config = serde_json::from_str(c)?; - c.build.dev_path = dev_path.clone(); - options.config = Some(serde_json::to_string(&c).unwrap()); - } else { - options.config = Some(format!(r#"{{ "build": {{ "devPath": "{}" }} }}"#, url)) - } - reload_config(options.config.as_deref())?; - } - } - } - if let Some(before_dev) = config .lock() .unwrap() diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index c9551aa4ae7..7a8ea1d3dae 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -27,7 +27,8 @@ use tauri_mobile::{ use super::{ ensure_init, get_app, init::{command as init_command, init_dot_cargo}, - log_finished, read_options, CliOptions, Target as MobileTarget, MIN_DEVICE_MATCH_SCORE, + log_finished, read_options, setup_dev_config, CliOptions, Target as MobileTarget, + MIN_DEVICE_MATCH_SCORE, }; use crate::{ helpers::config::{get as get_tauri_config, Config as TauriConfig}, diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index 8dd66bb6c00..22b7ca948b0 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -3,6 +3,7 @@ use super::{ MobileTarget, }; use crate::{ + build::Options as BuildOptions, helpers::flock, interface::{AppSettings, Interface, Options as InterfaceOptions}, mobile::{write_options, CliOptions}, @@ -53,7 +54,7 @@ pub struct Options { pub open: bool, } -impl From for crate::build::Options { +impl From for BuildOptions { fn from(options: Options) -> Self { Self { runner: None, @@ -111,7 +112,14 @@ fn run_build( options.aab = true; } - let mut build_options = options.clone().into(); + let mut build_options: BuildOptions = options.clone().into(); + build_options.target = Some( + Target::all() + .get(Target::DEFAULT_KEY) + .unwrap() + .triple + .into(), + ); let interface = crate::build::setup(&mut build_options, true)?; let app_settings = interface.app_settings(); diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 521012c2fbb..95c66bfe5e2 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -1,8 +1,9 @@ use super::{ - delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, - MobileTarget, + delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, + setup_dev_config, with_config, MobileTarget, }; use crate::{ + dev::Options as DevOptions, helpers::flock, interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions}, mobile::{write_options, CliOptions, DevChild, DevProcess}, @@ -13,6 +14,7 @@ use clap::{ArgAction, Parser}; use tauri_mobile::{ android::{ config::{Config as AndroidConfig, Metadata as AndroidMetadata}, + device::Device, env::Env, }, config::app::App, @@ -55,7 +57,7 @@ pub struct Options { pub device: Option, } -impl From for crate::dev::Options { +impl From for DevOptions { fn from(options: Options) -> Self { Self { runner: None, @@ -89,13 +91,29 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { } fn run_dev( - options: Options, + mut options: Options, app: &App, config: &AndroidConfig, metadata: &AndroidMetadata, noise_level: NoiseLevel, ) -> Result<()> { - let mut dev_options = options.clone().into(); + setup_dev_config(&mut options.config)?; + let env = env()?; + let device = match device_prompt(&env, options.device.as_deref()) { + Ok(d) => Some(d), + Err(e) => { + log::error!("{e}"); + None + } + }; + + let mut dev_options: DevOptions = options.clone().into(); + dev_options.target = Some( + device + .as_ref() + .map(|d| d.target().triple.to_string()) + .unwrap_or_else(|| "aarch64-linux-android".into()), + ); let mut interface = crate::dev::setup(&mut dev_options, true)?; let app_settings = interface.app_settings(); @@ -106,13 +124,11 @@ fn run_dev( let out_dir = bin_path.parent().unwrap(); let _lock = flock::open_rw(out_dir.join("lock").with_extension("android"), "Android")?; - let env = env()?; init_dot_cargo(app, Some((&env, config)))?; let open = options.open; let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; - let device = options.device; interface.mobile_dev( MobileOptions { debug: true, @@ -133,30 +149,21 @@ fn run_dev( if open { open_and_wait(config, &env) - } else { - match run( - device.as_deref(), - options, - config, - &env, - metadata, - noise_level, - ) { + } else if let Some(device) = &device { + match run(device, options, config, &env, metadata, noise_level) { Ok(c) => { crate::dev::wait_dev_process(c.clone(), move |status, reason| { crate::dev::on_app_exit(status, reason, exit_on_panic, no_watch) }); Ok(Box::new(c) as Box) } - Err(RunError::FailedToPromptForDevice(e)) => { - log::error!("{}", e); - open_and_wait(config, &env) - } Err(e) => { crate::dev::kill_before_dev_process(); Err(e.into()) } } + } else { + open_and_wait(config, &env) } }, ) @@ -164,14 +171,12 @@ fn run_dev( #[derive(Debug, thiserror::Error)] enum RunError { - #[error("{0}")] - FailedToPromptForDevice(String), #[error("{0}")] RunFailed(String), } fn run( - device: Option<&str>, + device: &Device<'_>, options: MobileOptions, config: &AndroidConfig, env: &Env, @@ -186,8 +191,7 @@ fn run( let build_app_bundle = metadata.asset_packs().is_some(); - device_prompt(env, device) - .map_err(|e| RunError::FailedToPromptForDevice(e.to_string()))? + device .run( config, env, diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index f627dfa441d..33dc9751d31 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -25,7 +25,8 @@ use tauri_mobile::{ use super::{ ensure_init, env, get_app, init::{command as init_command, init_dot_cargo}, - log_finished, read_options, CliOptions, Target as MobileTarget, MIN_DEVICE_MATCH_SCORE, + log_finished, read_options, setup_dev_config, CliOptions, Target as MobileTarget, + MIN_DEVICE_MATCH_SCORE, }; use crate::{ helpers::config::{get as get_tauri_config, Config as TauriConfig}, diff --git a/tooling/cli/src/mobile/ios/build.rs b/tooling/cli/src/mobile/ios/build.rs index 33773482d64..a872d036cfb 100644 --- a/tooling/cli/src/mobile/ios/build.rs +++ b/tooling/cli/src/mobile/ios/build.rs @@ -3,6 +3,7 @@ use super::{ MobileTarget, }; use crate::{ + build::Options as BuildOptions, helpers::flock, interface::{AppSettings, Interface, Options as InterfaceOptions}, mobile::{write_options, CliOptions}, @@ -49,7 +50,7 @@ pub struct Options { pub open: bool, } -impl From for crate::build::Options { +impl From for BuildOptions { fn from(options: Options) -> Self { Self { runner: None, @@ -97,7 +98,14 @@ fn run_build( Profile::Release }; - let mut build_options = options.clone().into(); + let mut build_options: BuildOptions = options.clone().into(); + build_options.target = Some( + Target::all() + .get(Target::DEFAULT_KEY) + .unwrap() + .triple + .into(), + ); let interface = crate::build::setup(&mut build_options, true)?; let app_settings = interface.app_settings(); diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index ad3bb6bdbbf..61741471889 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -1,8 +1,9 @@ use super::{ - device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, MobileTarget, - APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME, + device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, setup_dev_config, with_config, + MobileTarget, APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME, }; use crate::{ + dev::Options as DevOptions, helpers::flock, interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions}, mobile::{write_options, CliOptions, DevChild, DevProcess}, @@ -12,7 +13,7 @@ use clap::{ArgAction, Parser}; use dialoguer::{theme::ColorfulTheme, Select}; use tauri_mobile::{ - apple::{config::Config as AppleConfig, teams::find_development_teams}, + apple::{config::Config as AppleConfig, device::Device, teams::find_development_teams}, config::app::App, env::Env, opts::{NoiseLevel, Profile}, @@ -48,7 +49,7 @@ pub struct Options { pub device: Option, } -impl From for crate::dev::Options { +impl From for DevOptions { fn from(options: Options) -> Self { Self { runner: None, @@ -105,12 +106,28 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { } fn run_dev( - options: Options, + mut options: Options, app: &App, config: &AppleConfig, noise_level: NoiseLevel, ) -> Result<()> { - let mut dev_options = options.clone().into(); + setup_dev_config(&mut options.config)?; + let env = env()?; + let device = match device_prompt(&env, options.device.as_deref()) { + Ok(d) => Some(d), + Err(e) => { + log::error!("{e}"); + None + } + }; + + let mut dev_options: DevOptions = options.clone().into(); + dev_options.target = Some( + device + .as_ref() + .map(|d| d.target().triple.to_string()) + .unwrap_or_else(|| "aarch64-apple-ios".into()), + ); let mut interface = crate::dev::setup(&mut dev_options, true)?; let app_settings = interface.app_settings(); @@ -121,13 +138,11 @@ fn run_dev( let out_dir = bin_path.parent().unwrap(); let _lock = flock::open_rw(&out_dir.join("lock").with_extension("ios"), "iOS")?; - let env = env()?; init_dot_cargo(app, None)?; let open = options.open; let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; - let device = options.device; interface.mobile_dev( MobileOptions { debug: true, @@ -148,23 +163,21 @@ fn run_dev( if open { open_and_wait(config, &env) - } else { - match run(device.as_deref(), options, config, &env) { + } else if let Some(device) = &device { + match run(device, options, config, &env) { Ok(c) => { crate::dev::wait_dev_process(c.clone(), move |status, reason| { crate::dev::on_app_exit(status, reason, exit_on_panic, no_watch) }); Ok(Box::new(c) as Box) } - Err(RunError::FailedToPromptForDevice(e)) => { - log::error!("{}", e); - open_and_wait(config, &env) - } Err(e) => { crate::dev::kill_before_dev_process(); Err(e.into()) } } + } else { + open_and_wait(config, &env) } }, ) @@ -172,13 +185,11 @@ fn run_dev( #[derive(Debug, thiserror::Error)] enum RunError { - #[error("{0}")] - FailedToPromptForDevice(String), #[error("{0}")] RunFailed(String), } fn run( - device: Option<&str>, + device: &Device<'_>, options: MobileOptions, config: &AppleConfig, env: &Env, @@ -189,8 +200,7 @@ fn run( Profile::Release }; - device_prompt(env, device) - .map_err(|e| RunError::FailedToPromptForDevice(e.to_string()))? + device .run( config, env, diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index 30e94e771b4..bac4f89e206 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -3,7 +3,12 @@ // SPDX-License-Identifier: MIT use crate::{ - helpers::{app_paths::tauri_dir, config::Config as TauriConfig}, + helpers::{ + app_paths::tauri_dir, + config::{ + get as get_config, reload as reload_config, AppUrl, Config as TauriConfig, WindowUrl, + }, + }, interface::{AppInterface, AppSettings, DevProcess, Interface, Options as InterfaceOptions}, }; use anyhow::{bail, Result}; @@ -127,6 +132,43 @@ pub struct CliOptions { pub vars: HashMap, } +fn setup_dev_config(config_extension: &mut Option) -> crate::Result<()> { + let config = get_config(config_extension.as_deref())?; + + let mut dev_path = config + .lock() + .unwrap() + .as_ref() + .unwrap() + .build + .dev_path + .clone(); + + if let AppUrl::Url(WindowUrl::External(url)) = &mut dev_path { + let localhost = match url.host() { + Some(url::Host::Domain(d)) => d == "localhost", + Some(url::Host::Ipv4(i)) => { + i == std::net::Ipv4Addr::LOCALHOST || i == std::net::Ipv4Addr::UNSPECIFIED + } + _ => false, + }; + if localhost { + let ip = crate::dev::local_ip_address(); + url.set_host(Some(&ip.to_string())).unwrap(); + if let Some(c) = config_extension { + let mut c: tauri_utils::config::Config = serde_json::from_str(c)?; + c.build.dev_path = dev_path.clone(); + config_extension.replace(serde_json::to_string(&c).unwrap()); + } else { + config_extension.replace(format!(r#"{{ "build": {{ "devPath": "{}" }} }}"#, url)); + } + reload_config(config_extension.as_deref())?; + } + } + + Ok(()) +} + fn env_vars() -> HashMap { let mut vars = HashMap::new(); vars.insert("RUST_LOG_STYLE".into(), "always".into()); From 8835633955be01e3f984c1beb0c6814975c4c6e5 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 26 Jan 2023 12:33:52 -0300 Subject: [PATCH 126/423] fix(cli): do not prompt for device when `--open` is provided --- tooling/cli/src/mobile/android/dev.rs | 14 +++++++++----- tooling/cli/src/mobile/ios/dev.rs | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index 95c66bfe5e2..b9e4d222646 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -99,11 +99,15 @@ fn run_dev( ) -> Result<()> { setup_dev_config(&mut options.config)?; let env = env()?; - let device = match device_prompt(&env, options.device.as_deref()) { - Ok(d) => Some(d), - Err(e) => { - log::error!("{e}"); - None + let device = if options.open { + None + } else { + match device_prompt(&env, options.device.as_deref()) { + Ok(d) => Some(d), + Err(e) => { + log::error!("{e}"); + None + } } }; diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 61741471889..0d7b7025bd0 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -113,11 +113,15 @@ fn run_dev( ) -> Result<()> { setup_dev_config(&mut options.config)?; let env = env()?; - let device = match device_prompt(&env, options.device.as_deref()) { - Ok(d) => Some(d), - Err(e) => { - log::error!("{e}"); - None + let device = if options.open { + None + } else { + match device_prompt(&env, options.device.as_deref()) { + Ok(d) => Some(d), + Err(e) => { + log::error!("{e}"); + None + } } }; From 7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f Mon Sep 17 00:00:00 2001 From: "Ngo Iok Ui (Wu Yu Wei)" Date: Mon, 30 Jan 2023 02:08:27 +0800 Subject: [PATCH 127/423] Update gtk to 0.16 (#6155) Co-authored-by: Wu Yu Wei Co-authored-by: Lucas Nogueira --- .changes/config.json | 2 +- .changes/gtk16.md | 8 ++++++ .changes/msrv-1.64.md | 13 ++++++++++ .devcontainer/Dockerfile | 2 +- .docker/cross/aarch64.Dockerfile | 2 +- .github/workflows/artifacts-updater.yml | 2 +- .github/workflows/bench.yml | 2 +- .../workflows/covector-version-or-publish.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/lint-fmt-core.yml | 2 +- .github/workflows/publish-cli-js.yml | 4 +-- .github/workflows/test-cli-js.yml | 2 +- .github/workflows/test-core.yml | 12 ++++----- .github/workflows/test-mobile.yml | 2 +- .github/workflows/udeps.yml | 2 +- README.md | 2 +- core/tauri-build/Cargo.toml | 2 +- core/tauri-codegen/Cargo.toml | 2 +- core/tauri-macros/Cargo.toml | 2 +- core/tauri-runtime-wry/Cargo.toml | 8 +++--- core/tauri-runtime/Cargo.toml | 4 +-- core/tauri-utils/Cargo.toml | 2 +- core/tauri/Cargo.toml | 10 +++---- examples/api/src-tauri/Cargo.toml | 2 +- examples/commands/main.rs | 8 +++--- examples/resources/src-tauri/Cargo.toml | 2 +- examples/sidecar/src-tauri/Cargo.toml | 2 +- .../tauri-dynamic-lib/src-app1/Cargo.toml | 2 +- .../tauri-dynamic-lib/src-tauri/Cargo.toml | 2 +- examples/updater/src-tauri/Cargo.toml | 2 +- examples/web/core/tauri/Cargo.toml | 2 +- tooling/bench/Cargo.toml | 2 +- .../tests/cpu_intensive/src-tauri/Cargo.toml | 2 +- .../tests/files_transfer/src-tauri/Cargo.toml | 2 +- .../tests/helloworld/src-tauri/Cargo.toml | 2 +- tooling/bundler/Cargo.toml | 2 +- tooling/bundler/src/bundle/category.rs | 3 +-- tooling/bundler/src/bundle/common.rs | 20 +++++--------- tooling/bundler/src/bundle/linux/debian.rs | 26 +++++++++---------- tooling/bundler/src/bundle/path_utils.rs | 10 +++---- tooling/bundler/src/bundle/platform.rs | 4 +-- tooling/bundler/src/bundle/settings.rs | 3 +-- tooling/cli/Cargo.toml | 2 +- .../jest/fixtures/app/src-tauri/Cargo.toml | 2 +- tooling/cli/src/build.rs | 9 +++---- tooling/cli/src/dev.rs | 9 +++---- tooling/cli/src/helpers/flock.rs | 2 +- tooling/cli/src/helpers/updater_signature.rs | 4 +-- tooling/cli/src/icon.rs | 11 +++----- tooling/cli/src/info.rs | 14 +++++----- tooling/cli/src/interface/rust.rs | 4 +-- .../cli/src/interface/rust/cargo_config.rs | 2 +- tooling/cli/src/interface/rust/desktop.rs | 7 +++-- tooling/cli/src/interface/rust/manifest.rs | 4 +-- tooling/cli/src/lib.rs | 6 ++--- tooling/cli/src/mobile/init.rs | 9 +++---- tooling/cli/src/mobile/mod.rs | 2 +- .../app/src-tauri/Cargo.crate-manifest | 2 +- .../plugin/backend/.changes/config.json | 2 +- .../plugin/backend/.github/workflows/lint.yml | 2 +- .../plugin/backend/.github/workflows/test.yml | 2 +- .../plugin/backend/Cargo.crate-manifest | 2 +- .../vanilla/src-tauri/Cargo.crate-manifest | 2 +- .../plugin/with-api/.changes/config.json | 2 +- .../with-api/.github/workflows/clippy.yml | 2 +- .../with-api/.github/workflows/test.yml | 2 +- .../plugin/with-api/Cargo.crate-manifest | 2 +- .../svelte-app/src-tauri/Cargo.crate-manifest | 2 +- 68 files changed, 150 insertions(+), 148 deletions(-) create mode 100644 .changes/gtk16.md create mode 100644 .changes/msrv-1.64.md diff --git a/.changes/config.json b/.changes/config.json index 5df56a75d06..fccd29e9f32 100644 --- a/.changes/config.json +++ b/.changes/config.json @@ -7,7 +7,7 @@ "getPublishedVersion": "node ../../.scripts/covector/package-latest-version.js cargo ${ pkgFile.pkg.package.name } ${ pkgFile.pkg.package.version }", "prepublish": [ "sudo apt-get update", - "sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev", + "sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev", "cargo install cargo-audit --features=fix", { "command": "cargo generate-lockfile", diff --git a/.changes/gtk16.md b/.changes/gtk16.md new file mode 100644 index 00000000000..035f8c1d289 --- /dev/null +++ b/.changes/gtk16.md @@ -0,0 +1,8 @@ +--- +"tauri-runtime": minor +"tauri-runtime-wry": minor +"tauri": minor +--- + +Update gtk to 0.16. + diff --git a/.changes/msrv-1.64.md b/.changes/msrv-1.64.md new file mode 100644 index 00000000000..b61ad529709 --- /dev/null +++ b/.changes/msrv-1.64.md @@ -0,0 +1,13 @@ +--- +"cli.rs": minor +"tauri-bundler": minor +"tauri": minor +"tauri-build": minor +"tauri-codegen": minor +"tauri-macros": minor +"tauri-utils": minor +"tauri-runtime": minor +"tauri-runtime-wry": minor +--- + +Bump the MSRV to 1.64. diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 0435c8cf7b0..63d7bcd3ae6 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,7 +6,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} # Derived from Tauri contribution and setup guides: # See: https://github.com/tauri-apps/tauri/blob/dev/.github/CONTRIBUTING.md#development-guide # See: https://tauri.app/v1/guides/getting-started/prerequisites/#setting-up-linux -ARG TAURI_BUILD_DEPS="build-essential curl libappindicator3-dev libgtk-3-dev librsvg2-dev libssl-dev libwebkit2gtk-4.0-dev wget" +ARG TAURI_BUILD_DEPS="build-essential curl libappindicator3-dev libgtk-3-dev librsvg2-dev libssl-dev libwebkit2gtk-4.1-dev wget" RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ && apt-get install -y --no-install-recommends $TAURI_BUILD_DEPS diff --git a/.docker/cross/aarch64.Dockerfile b/.docker/cross/aarch64.Dockerfile index 6dfb5bdaa71..498d055b75f 100644 --- a/.docker/cross/aarch64.Dockerfile +++ b/.docker/cross/aarch64.Dockerfile @@ -41,4 +41,4 @@ ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ RUN dpkg --add-architecture arm64 RUN apt-get update -RUN apt-get install --assume-yes --no-install-recommends libssl-dev:arm64 libdbus-1-dev:arm64 libsoup2.4-dev:arm64 libssl-dev:arm64 libgtk-3-dev:arm64 webkit2gtk-4.0-dev:arm64 libappindicator3-1:arm64 librsvg2-dev:arm64 patchelf:arm64 +RUN apt-get install --assume-yes --no-install-recommends libssl-dev:arm64 libdbus-1-dev:arm64 libsoup2.4-dev:arm64 libssl-dev:arm64 libgtk-3-dev:arm64 webkit2gtk-4.1-dev:arm64 libappindicator3-1:arm64 librsvg2-dev:arm64 patchelf:arm64 diff --git a/.github/workflows/artifacts-updater.yml b/.github/workflows/artifacts-updater.yml index fb1d316c3cb..2d654905c17 100644 --- a/.github/workflows/artifacts-updater.yml +++ b/.github/workflows/artifacts-updater.yml @@ -40,7 +40,7 @@ jobs: if: matrix.platform == 'ubuntu-latest' run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev + sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev - uses: Swatinem/rust-cache@v2 with: diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index c8315cf6a2b..8a489e659c7 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -47,7 +47,7 @@ jobs: run: | python -m pip install --upgrade pip sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev xvfb + sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev xvfb wget https://github.com/sharkdp/hyperfine/releases/download/v1.11.0/hyperfine_1.11.0_amd64.deb sudo dpkg -i hyperfine_1.11.0_amd64.deb pip install memory_profiler diff --git a/.github/workflows/covector-version-or-publish.yml b/.github/workflows/covector-version-or-publish.yml index 05449342731..415318023e7 100644 --- a/.github/workflows/covector-version-or-publish.yml +++ b/.github/workflows/covector-version-or-publish.yml @@ -30,7 +30,7 @@ jobs: if: matrix.platform == 'ubuntu-latest' run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev libfuse2 + sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev libfuse2 - uses: Swatinem/rust-cache@v2 with: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index dcf2e217980..bb01a2919cd 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -111,7 +111,7 @@ jobs: - name: install dependencies run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev + sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev - name: Test run: | diff --git a/.github/workflows/lint-fmt-core.yml b/.github/workflows/lint-fmt-core.yml index 04fe41f4295..e7ccbc46663 100644 --- a/.github/workflows/lint-fmt-core.yml +++ b/.github/workflows/lint-fmt-core.yml @@ -62,7 +62,7 @@ jobs: - name: install dependencies run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev + sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev - uses: actions-rs/toolchain@v1 with: diff --git a/.github/workflows/publish-cli-js.yml b/.github/workflows/publish-cli-js.yml index e7f2ed6ffd0..234c68f2689 100644 --- a/.github/workflows/publish-cli-js.yml +++ b/.github/workflows/publish-cli-js.yml @@ -257,7 +257,7 @@ jobs: - name: install system dependencies run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev + sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev - name: Test bindings run: yarn test test-linux-x64-musl-binding: @@ -351,7 +351,7 @@ jobs: set -e export PATH=/usr/local/cargo/bin/:/usr/local/fnm:$PATH apt-get update - DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install --no-install-recommends -y unzip webkit2gtk-4.0 libayatana-appindicator3-dev + DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install --no-install-recommends -y unzip webkit2gtk-4.1 libayatana-appindicator3-dev bash curl https://sh.rustup.rs -sSf | bash -s -- -y curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-dir "/usr/local/fnm" --skip-shell diff --git a/.github/workflows/test-cli-js.yml b/.github/workflows/test-cli-js.yml index c178140c2c0..2060fb66fda 100644 --- a/.github/workflows/test-cli-js.yml +++ b/.github/workflows/test-cli-js.yml @@ -51,7 +51,7 @@ jobs: if: matrix.platform == 'ubuntu-latest' run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev + sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev - uses: Swatinem/rust-cache@v2 with: diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index 258bd18b717..abbc4d916af 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -34,35 +34,35 @@ jobs: - { target: x86_64-pc-windows-msvc, os: windows-latest, - toolchain: '1.61.0', + toolchain: '1.64.0', cross: false, command: 'test' } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, - toolchain: '1.59.0', + toolchain: '1.64.0', cross: false, command: 'test' } - { target: x86_64-apple-darwin, os: macos-latest, - toolchain: '1.59.0', + toolchain: '1.64.0', cross: false, command: 'test' } - { target: aarch64-apple-ios, os: macos-latest, - toolchain: '1.59.0', + toolchain: '1.64.0', cross: false, command: 'build' } - { target: aarch64-linux-android, os: ubuntu-latest, - toolchain: '1.59.0', + toolchain: '1.64.0', cross: true, command: 'build' } @@ -95,7 +95,7 @@ jobs: if: contains(matrix.platform.target, 'unknown-linux') run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev + sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev - uses: Swatinem/rust-cache@v2 with: diff --git a/.github/workflows/test-mobile.yml b/.github/workflows/test-mobile.yml index 6ebdf91eb30..73d83e9ddb2 100644 --- a/.github/workflows/test-mobile.yml +++ b/.github/workflows/test-mobile.yml @@ -33,7 +33,7 @@ jobs: if: matrix.platform == 'ubuntu-latest' run: | sudo apt-get update - sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 + sudo apt-get install -y libgtk-3-dev webkit2gtk-4.1 - name: setup node uses: actions/setup-node@v3 diff --git a/.github/workflows/udeps.yml b/.github/workflows/udeps.yml index dea90577245..41c294fbc8a 100644 --- a/.github/workflows/udeps.yml +++ b/.github/workflows/udeps.yml @@ -157,7 +157,7 @@ jobs: - name: Install required packages run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev + sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev - uses: actions-rs/cargo@v1 with: diff --git a/README.md b/README.md index 4feac90fd06..8a8f56f8602 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ For **developing** Tauri apps refer to the [Getting Started guide on tauri.app]( For **running** Tauri apps we support the below configurations (these are automatically added as dependencies for .deb and are bundled for AppImage so that your users don't need to manually install them): - Debian (Ubuntu 18.04 and above or equivalent) with the following packages installed: - - `libwebkit2gtk-4.0-37`, `libgtk-3-0`, `libayatana-appindicator3-1`1 + - `libwebkit2gtk-4.1-0`, `libgtk-3-0`, `libayatana-appindicator3-1`1 - Arch with the following packages installed: - `webkit2gtk`, `gtk3`, `libayatana-appindicator`1 - Fedora (latest 2 versions) with the following packages installed: diff --git a/core/tauri-build/Cargo.toml b/core/tauri-build/Cargo.toml index 1840ac2654a..1f0b3617ffd 100644 --- a/core/tauri-build/Cargo.toml +++ b/core/tauri-build/Cargo.toml @@ -8,7 +8,7 @@ homepage = "https://tauri.app" repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-build" description = "build time code to pair with https://crates.io/crates/tauri" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" diff --git a/core/tauri-codegen/Cargo.toml b/core/tauri-codegen/Cargo.toml index 6c434d130a0..36cc7b747eb 100644 --- a/core/tauri-codegen/Cargo.toml +++ b/core/tauri-codegen/Cargo.toml @@ -8,7 +8,7 @@ homepage = "https://tauri.app" repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-codegen" description = "code generation meant to be consumed inside of `tauri` through `tauri-build` or `tauri-macros`" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" diff --git a/core/tauri-macros/Cargo.toml b/core/tauri-macros/Cargo.toml index e79b037ef07..575d0f8bdf1 100644 --- a/core/tauri-macros/Cargo.toml +++ b/core/tauri-macros/Cargo.toml @@ -8,7 +8,7 @@ homepage = "https://tauri.app" repository = "https://github.com/tauri-apps/tauri" description = "Macros for the tauri crate." edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index e9493d88222..c195ccf4f84 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -8,12 +8,12 @@ homepage = "https://tauri.app" repository = "https://github.com/tauri-apps/tauri" description = "Wry bindings to the Tauri runtime" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" [dependencies] -wry = { git = "https://github.com/tauri-apps/wry", branch = "dev", default-features = false, features = [ "file-drop", "protocol" ] } +wry = { version = "0.25.0", default-features = false, features = [ "file-drop", "protocol" ] } tauri-runtime = { version = "0.13.0-alpha.0", path = "../tauri-runtime" } tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } @@ -28,8 +28,8 @@ webview2-com = "0.19.1" features = [ "Win32_Foundation" ] [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -gtk = { version = "0.15", features = [ "v3_20" ] } -webkit2gtk = { version = "0.18.2", features = [ "v2_22" ] } +gtk = { version = "0.16", features = [ "v3_24" ] } +webkit2gtk = { version = "0.19.1", features = [ "v2_38" ] } percent-encoding = "2.1" [target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies] diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml index 9e670d96477..75d46e94a54 100644 --- a/core/tauri-runtime/Cargo.toml +++ b/core/tauri-runtime/Cargo.toml @@ -8,7 +8,7 @@ homepage = "https://tauri.app" repository = "https://github.com/tauri-apps/tauri" description = "Runtime for Tauri applications" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" @@ -41,7 +41,7 @@ webview2-com = "0.19.1" features = [ "Win32_Foundation" ] [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -gtk = { version = "0.15", features = [ "v3_20" ] } +gtk = { version = "0.16", features = [ "v3_24" ] } [features] devtools = [ ] diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml index 785b415defc..84ea058a114 100644 --- a/core/tauri-utils/Cargo.toml +++ b/core/tauri-utils/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://tauri.app" repository = "https://github.com/tauri-apps/tauri" description = "Utilities for Tauri" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index f008d8f5ab8..d325b224378 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -3,7 +3,7 @@ authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "web-programming" ] description = "Make tiny, secure apps for all desktop platforms with Tauri" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = [ "/test", "/.scripts", "CHANGELOG.md", "/target" ] homepage = "https://tauri.app" license = "Apache-2.0 OR MIT" @@ -87,13 +87,13 @@ ico = { version = "0.2.0", optional = true } encoding_rs = "0.8.31" [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -rfd = { version = "0.10", optional = true } +rfd = { git = "https://github.com/PolyMeilex/rfd", version = "0.10", optional = true } notify-rust = { version = "4.5", default-features = false, features = [ "d" ], optional = true } [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -gtk = { version = "0.15", features = [ "v3_20" ] } -glib = "0.15" -webkit2gtk = { version = "0.18.2", features = [ "v2_22" ] } +gtk = { version = "0.16", features = [ "v3_24" ] } +glib = "0.16" +webkit2gtk = { version = "0.19.1", features = [ "v2_38" ] } [target."cfg(target_os = \"macos\")".dependencies] embed_plist = "1.2" diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 599453b8dda..31a0ad7fbc9 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ name = "api" version = "0.1.0" description = "An example Tauri Application showcasing the api" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" license = "Apache-2.0 OR MIT" [lib] diff --git a/examples/commands/main.rs b/examples/commands/main.rs index 81409d4011a..ce20be00686 100644 --- a/examples/commands/main.rs +++ b/examples/commands/main.rs @@ -85,7 +85,7 @@ fn force_async(the_argument: String) -> String { #[command(async)] fn force_async_with_result(the_argument: &str) -> Result<&str, MyError> { (!the_argument.is_empty()) - .then(|| the_argument) + .then_some(the_argument) .ok_or(MyError::FooError) } @@ -121,7 +121,7 @@ fn force_async_snake(the_argument: String) -> String { #[command(rename_all = "snake_case", async)] fn force_async_with_result_snake(the_argument: &str) -> Result<&str, MyError> { (!the_argument.is_empty()) - .then(|| the_argument) + .then_some(the_argument) .ok_or(MyError::FooError) } @@ -131,7 +131,7 @@ fn force_async_with_result_snake(the_argument: &str) -> Result<&str, MyError> { fn simple_command_with_result(the_argument: String) -> Result { println!("{the_argument}"); (!the_argument.is_empty()) - .then(|| the_argument) + .then_some(the_argument) .ok_or(MyError::FooError) } @@ -150,7 +150,7 @@ fn stateful_command_with_result( fn simple_command_with_result_snake(the_argument: String) -> Result { println!("{the_argument}"); (!the_argument.is_empty()) - .then(|| the_argument) + .then_some(the_argument) .ok_or(MyError::FooError) } diff --git a/examples/resources/src-tauri/Cargo.toml b/examples/resources/src-tauri/Cargo.toml index 50daca46e63..f30c127c515 100644 --- a/examples/resources/src-tauri/Cargo.toml +++ b/examples/resources/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ name = "resources" version = "0.1.0" description = "A Tauri application that uses Node.js with app resources" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [build-dependencies] tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] } diff --git a/examples/sidecar/src-tauri/Cargo.toml b/examples/sidecar/src-tauri/Cargo.toml index ab770ce2137..c6b24de7a7f 100644 --- a/examples/sidecar/src-tauri/Cargo.toml +++ b/examples/sidecar/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ name = "sidecar" version = "0.1.0" description = "A Tauri application with a sidecar binary" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [build-dependencies] tauri-build = { path = "../../../core/tauri-build", features = ["codegen"] } diff --git a/examples/tauri-dynamic-lib/src-app1/Cargo.toml b/examples/tauri-dynamic-lib/src-app1/Cargo.toml index c2341022f33..756c0083b82 100644 --- a/examples/tauri-dynamic-lib/src-app1/Cargo.toml +++ b/examples/tauri-dynamic-lib/src-app1/Cargo.toml @@ -3,7 +3,7 @@ name = "app1" version = "0.1.0" description = "A simple app that makes a dll call" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [workspace] diff --git a/examples/tauri-dynamic-lib/src-tauri/Cargo.toml b/examples/tauri-dynamic-lib/src-tauri/Cargo.toml index 66921386d18..a9217c63928 100644 --- a/examples/tauri-dynamic-lib/src-tauri/Cargo.toml +++ b/examples/tauri-dynamic-lib/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ name = "tauri_app" version = "0.1.0" description = "A very simple Dll Library that runs tauri and launches a webview window" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [workspace] diff --git a/examples/updater/src-tauri/Cargo.toml b/examples/updater/src-tauri/Cargo.toml index 50b414f8161..cc9d0f37311 100644 --- a/examples/updater/src-tauri/Cargo.toml +++ b/examples/updater/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ name = "updater-example" version = "0.1.0" description = "A very simple Tauri Application" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" license = "Apache-2.0 OR MIT" [build-dependencies] diff --git a/examples/web/core/tauri/Cargo.toml b/examples/web/core/tauri/Cargo.toml index a4e5ba1be6f..99c147a8781 100644 --- a/examples/web/core/tauri/Cargo.toml +++ b/examples/web/core/tauri/Cargo.toml @@ -7,7 +7,7 @@ license = "" repository = "" default-run = "app" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [build-dependencies] tauri-build = { path = "../../../../core/tauri-build", features = [] } diff --git a/tooling/bench/Cargo.toml b/tooling/bench/Cargo.toml index 888545cffb1..0c977b566ce 100644 --- a/tooling/bench/Cargo.toml +++ b/tooling/bench/Cargo.toml @@ -5,7 +5,7 @@ name = "tauri_bench" version = "0.1.0" authors = [ "Tauri Programme within The Commons Conservancy" ] edition = "2021" -rust-version = "1.59" +rust-version = "1.64" license = "Apache-2.0 OR MIT" description = "Cross-platform WebView rendering library" repository = "https://github.com/tauri-apps/wry" diff --git a/tooling/bench/tests/cpu_intensive/src-tauri/Cargo.toml b/tooling/bench/tests/cpu_intensive/src-tauri/Cargo.toml index 5120e5f3060..717d2869037 100644 --- a/tooling/bench/tests/cpu_intensive/src-tauri/Cargo.toml +++ b/tooling/bench/tests/cpu_intensive/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ name = "bench_cpu_intensive" version = "0.1.0" description = "A very simple Tauri Application" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [build-dependencies] tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] } diff --git a/tooling/bench/tests/files_transfer/src-tauri/Cargo.toml b/tooling/bench/tests/files_transfer/src-tauri/Cargo.toml index f643bd0fdb6..a925da8a5f9 100644 --- a/tooling/bench/tests/files_transfer/src-tauri/Cargo.toml +++ b/tooling/bench/tests/files_transfer/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ name = "bench_files_transfer" version = "0.1.0" description = "A very simple Tauri Application" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [build-dependencies] tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] } diff --git a/tooling/bench/tests/helloworld/src-tauri/Cargo.toml b/tooling/bench/tests/helloworld/src-tauri/Cargo.toml index 0b3040181f5..1ff22a0098c 100644 --- a/tooling/bench/tests/helloworld/src-tauri/Cargo.toml +++ b/tooling/bench/tests/helloworld/src-tauri/Cargo.toml @@ -3,7 +3,7 @@ name = "bench_helloworld" version = "0.1.0" description = "A very simple Tauri Application" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [build-dependencies] tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] } diff --git a/tooling/bundler/Cargo.toml b/tooling/bundler/Cargo.toml index 5b12fc16d69..6de0c5dc337 100644 --- a/tooling/bundler/Cargo.toml +++ b/tooling/bundler/Cargo.toml @@ -13,7 +13,7 @@ keywords = [ "bundle", "cargo", "tauri" ] repository = "https://github.com/tauri-apps/tauri" description = "Wrap rust executables in OS-specific app bundles for Tauri" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = [ "CHANGELOG.md", "/target", "rustfmt.toml" ] [dependencies] diff --git a/tooling/bundler/src/bundle/category.rs b/tooling/bundler/src/bundle/category.rs index 2aff14c25c8..63861e13a85 100644 --- a/tooling/bundler/src/bundle/category.rs +++ b/tooling/bundler/src/bundle/category.rs @@ -254,8 +254,7 @@ impl<'d> serde::de::Visitor<'d> for AppCategoryVisitor { match self.did_you_mean { Some(string) => write!( formatter, - "a valid app category string (did you mean \"{}\"?)", - string + "a valid app category string (did you mean \"{string}\"?)" ), None => write!(formatter, "a valid app category string"), } diff --git a/tooling/bundler/src/bundle/common.rs b/tooling/bundler/src/bundle/common.rs index 19e91b15e6b..fe3c61dc625 100644 --- a/tooling/bundler/src/bundle/common.rs +++ b/tooling/bundler/src/bundle/common.rs @@ -72,14 +72,12 @@ pub fn copy_file(from: impl AsRef, to: impl AsRef) -> crate::Result< let to = to.as_ref(); if !from.exists() { return Err(crate::Error::GenericError(format!( - "{:?} does not exist", - from + "{from:?} does not exist" ))); } if !from.is_file() { return Err(crate::Error::GenericError(format!( - "{:?} is not a file", - from + "{from:?} is not a file" ))); } let dest_dir = to.parent().expect("No data in parent"); @@ -96,20 +94,17 @@ pub fn copy_file(from: impl AsRef, to: impl AsRef) -> crate::Result< pub fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> { if !from.exists() { return Err(crate::Error::GenericError(format!( - "{:?} does not exist", - from + "{from:?} does not exist" ))); } if !from.is_dir() { return Err(crate::Error::GenericError(format!( - "{:?} is not a Directory", - from + "{from:?} is not a Directory" ))); } if to.exists() { return Err(crate::Error::GenericError(format!( - "{:?} already exists", - from + "{from:?} already exists" ))); } let parent = to.parent().expect("No data in parent"); @@ -142,7 +137,7 @@ pub trait CommandExt { impl CommandExt for Command { fn output_ok(&mut self) -> crate::Result { let program = self.get_program().to_string_lossy().into_owned(); - debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg))); + debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}"))); self.stdout(Stdio::piped()); self.stderr(Stdio::piped()); @@ -196,8 +191,7 @@ impl CommandExt for Command { Ok(output) } else { Err(crate::Error::GenericError(format!( - "failed to run {}", - program + "failed to run {program}" ))) } } diff --git a/tooling/bundler/src/bundle/linux/debian.rs b/tooling/bundler/src/bundle/linux/debian.rs index 8c8b436849f..1c9d921dba8 100644 --- a/tooling/bundler/src/bundle/linux/debian.rs +++ b/tooling/bundler/src/bundle/linux/debian.rs @@ -65,13 +65,13 @@ pub fn bundle_project(settings: &Settings) -> crate::Result> { settings.version_string(), arch ); - let package_name = format!("{}.deb", package_base_name); + let package_name = format!("{package_base_name}.deb"); let base_dir = settings.project_out_directory().join("bundle/deb"); let package_dir = base_dir.join(&package_base_name); if package_dir.exists() { fs::remove_dir_all(&package_dir) - .with_context(|| format!("Failed to remove old {}", package_base_name))?; + .with_context(|| format!("Failed to remove old {package_base_name}"))?; } let package_path = base_dir.join(&package_name); @@ -118,7 +118,7 @@ pub fn generate_data( for bin in settings.binaries() { let bin_path = settings.binary_path(bin); common::copy_file(&bin_path, bin_dir.join(bin.name())) - .with_context(|| format!("Failed to copy binary from {:?}", bin_path))?; + .with_context(|| format!("Failed to copy binary from {bin_path:?}"))?; } copy_resource_files(settings, &data_dir).with_context(|| "Failed to copy resource files")?; @@ -137,7 +137,7 @@ pub fn generate_data( /// Generate the application desktop file and store it under the `data_dir`. fn generate_desktop_file(settings: &Settings, data_dir: &Path) -> crate::Result<()> { let bin_name = settings.main_binary_name(); - let desktop_file_name = format!("{}.desktop", bin_name); + let desktop_file_name = format!("{bin_name}.desktop"); let desktop_file_path = data_dir .join("usr/share/applications") .join(desktop_file_name); @@ -153,8 +153,8 @@ fn generate_desktop_file(settings: &Settings, data_dir: &Path) -> crate::Result< if !settings.short_description().is_empty() { writeln!(file, "Comment={}", settings.short_description())?; } - writeln!(file, "Exec={}", bin_name)?; - writeln!(file, "Icon={}", bin_name)?; + writeln!(file, "Exec={bin_name}")?; + writeln!(file, "Icon={bin_name}")?; writeln!(file, "Name={}", settings.product_name())?; writeln!(file, "Terminal=false")?; writeln!(file, "Type=Application")?; @@ -174,11 +174,11 @@ fn generate_control_file( let mut file = common::create_file(&dest_path)?; writeln!(file, "Package: {}", AsKebabCase(settings.product_name()))?; writeln!(file, "Version: {}", settings.version_string())?; - writeln!(file, "Architecture: {}", arch)?; + writeln!(file, "Architecture: {arch}")?; // Installed-Size must be divided by 1024, see https://www.debian.org/doc/debian-policy/ch-controlfields.html#installed-size writeln!(file, "Installed-Size: {}", total_dir_size(data_dir)? / 1024)?; let authors = settings.authors_comma_separated().unwrap_or_default(); - writeln!(file, "Maintainer: {}", authors)?; + writeln!(file, "Maintainer: {authors}")?; if !settings.homepage_url().is_empty() { writeln!(file, "Homepage: {}", settings.homepage_url())?; } @@ -194,13 +194,13 @@ fn generate_control_file( if long_description.is_empty() { long_description = "(none)"; } - writeln!(file, "Description: {}", short_description)?; + writeln!(file, "Description: {short_description}")?; for line in long_description.lines() { let line = line.trim(); if line.is_empty() { writeln!(file, " .")?; } else { - writeln!(file, " {}", line)?; + writeln!(file, " {line}")?; } } writeln!(file, "Priority: optional")?; @@ -223,14 +223,14 @@ fn generate_md5sums(control_dir: &Path, data_dir: &Path) -> crate::Result<()> { let mut hash = md5::Context::new(); io::copy(&mut file, &mut hash)?; for byte in hash.compute().iter() { - write!(md5sums_file, "{:02x}", byte)?; + write!(md5sums_file, "{byte:02x}")?; } let rel_path = path.strip_prefix(data_dir)?; let path_str = rel_path.to_str().ok_or_else(|| { - let msg = format!("Non-UTF-8 path: {:?}", rel_path); + let msg = format!("Non-UTF-8 path: {rel_path:?}"); io::Error::new(io::ErrorKind::InvalidData, msg) })?; - writeln!(md5sums_file, " {}", path_str)?; + writeln!(md5sums_file, " {path_str}")?; } Ok(()) } diff --git a/tooling/bundler/src/bundle/path_utils.rs b/tooling/bundler/src/bundle/path_utils.rs index f3c55046415..45b0c3c8cf7 100644 --- a/tooling/bundler/src/bundle/path_utils.rs +++ b/tooling/bundler/src/bundle/path_utils.rs @@ -104,7 +104,7 @@ where let from = from.as_ref(); if !from.exists() { if let Some(msg) = from.to_str() { - let msg = format!("Path \"{}\" does not exist or you don't have access", msg); + let msg = format!("Path \"{msg}\" does not exist or you don't have access"); return Err(crate::Error::PathUtilError(msg)); } return Err(crate::Error::PathUtilError( @@ -114,7 +114,7 @@ where if !from.is_file() { if let Some(msg) = from.to_str() { - let msg = format!("Path \"{}\" is not a file!", msg); + let msg = format!("Path \"{msg}\" is not a file!"); return Err(crate::Error::PathUtilError(msg)); } return Err(crate::Error::PathUtilError( @@ -127,7 +127,7 @@ where } if let Some(msg) = to.as_ref().to_str() { - let msg = format!("Path \"{}\" is exist", msg); + let msg = format!("Path \"{msg}\" is exist"); return Err(crate::Error::PathUtilError(msg)); } } @@ -145,7 +145,7 @@ where let from = from.as_ref(); if !from.exists() { if let Some(msg) = from.to_str() { - let msg = format!("Path \"{}\" does not exist or you don't have access!", msg); + let msg = format!("Path \"{msg}\" does not exist or you don't have access!"); return Err(crate::Error::PathUtilError(msg)); } return Err(crate::Error::PathUtilError( @@ -154,7 +154,7 @@ where } if !from.is_dir() { if let Some(msg) = from.to_str() { - let msg = format!("Path \"{}\" is not a directory!", msg); + let msg = format!("Path \"{msg}\" is not a directory!"); return Err(crate::Error::PathUtilError(msg)); } return Err(crate::Error::PathUtilError( diff --git a/tooling/bundler/src/bundle/platform.rs b/tooling/bundler/src/bundle/platform.rs index d06300a3c78..64792c2003c 100644 --- a/tooling/bundler/src/bundle/platform.rs +++ b/tooling/bundler/src/bundle/platform.rs @@ -95,10 +95,10 @@ pub fn target_triple() -> Result { ))); }; - format!("{}-{}", os, env) + format!("{os}-{env}") }; - Ok(format!("{}-{}", arch, os)) + Ok(format!("{arch}-{os}")) } #[cfg(test)] diff --git a/tooling/bundler/src/bundle/settings.rs b/tooling/bundler/src/bundle/settings.rs index 3aff75adb36..6f448530a03 100644 --- a/tooling/bundler/src/bundle/settings.rs +++ b/tooling/bundler/src/bundle/settings.rs @@ -574,8 +574,7 @@ impl Settings { "windows" => vec![PackageType::WindowsMsi], os => { return Err(crate::Error::GenericError(format!( - "Native {} bundles not yet supported.", - os + "Native {os} bundles not yet supported." ))) } }; diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index be08a2ff162..4c75ee57bf4 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -6,7 +6,7 @@ name = "tauri-cli" version = "2.0.0-alpha.1" authors = [ "Tauri Programme within The Commons Conservancy" ] edition = "2021" -rust-version = "1.59" +rust-version = "1.64" categories = [ "gui", "web-programming" ] license = "Apache-2.0 OR MIT" homepage = "https://tauri.app" diff --git a/tooling/cli/node/test/jest/fixtures/app/src-tauri/Cargo.toml b/tooling/cli/node/test/jest/fixtures/app/src-tauri/Cargo.toml index 9fec9d9ede2..c0281ee26ba 100644 --- a/tooling/cli/node/test/jest/fixtures/app/src-tauri/Cargo.toml +++ b/tooling/cli/node/test/jest/fixtures/app/src-tauri/Cargo.toml @@ -8,7 +8,7 @@ authors = [ "Tauri Programme within The Commons Conservancy" ] license = "" repository = "" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [package.metadata.bundle] identifier = "com.tauri.dev" diff --git a/tooling/cli/src/build.rs b/tooling/cli/src/build.rs index cc85f24e60b..0f052a448a8 100644 --- a/tooling/cli/src/build.rs +++ b/tooling/cli/src/build.rs @@ -89,8 +89,7 @@ pub fn command(mut options: Options) -> Result<()> { } None => { return Err(anyhow::anyhow!(format!( - "Unsupported bundle format: {}", - name + "Unsupported bundle format: {name}" ))); } } @@ -361,7 +360,7 @@ fn run_hook(name: &str, hook: HookCommand, interface: &AppInterface, debug: bool .current_dir(cwd) .envs(env) .piped() - .with_context(|| format!("failed to run `{}` with `sh -c`", script))?; + .with_context(|| format!("failed to run `{script}` with `sh -c`"))?; if !status.success() { bail!( @@ -396,9 +395,9 @@ mod pkgconfig_utils { pub fn get_appindicator_library_path() -> PathBuf { match get_library_path("ayatana-appindicator3-0.1") { - Some(p) => format!("{}/libayatana-appindicator3.so.1", p).into(), + Some(p) => format!("{p}/libayatana-appindicator3.so.1").into(), None => match get_library_path("appindicator3-0.1") { - Some(p) => format!("{}/libappindicator3.so.1", p).into(), + Some(p) => format!("{p}/libappindicator3.so.1").into(), None => panic!("Can't detect any appindicator library"), }, } diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index df10d98aab7..ec10a8d5284 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -231,7 +231,7 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { command.stderr(os_pipe::dup_stderr()?); let child = SharedChild::spawn(&mut command) - .unwrap_or_else(|_| panic!("failed to run `{}`", before_dev)); + .unwrap_or_else(|_| panic!("failed to run `{before_dev}`")); let child = Arc::new(child); let child_ = child.clone(); @@ -294,7 +294,7 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { let server_address = SocketAddr::new(ip, port); let path = path.canonicalize()?; start_dev_server(server_address, path); - let server_url = format!("http://{}", server_address); + let server_url = format!("http://{server_address}"); dev_path = AppUrl::Url(WindowUrl::External(server_url.parse().unwrap())); // TODO: in v2, use an env var to pass the url to the app context @@ -306,10 +306,7 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { c.build.dev_path = dev_path.clone(); options.config = Some(serde_json::to_string(&c).unwrap()); } else { - options.config = Some(format!( - r#"{{ "build": {{ "devPath": "{}" }} }}"#, - server_url - )) + options.config = Some(format!(r#"{{ "build": {{ "devPath": "{server_url}" }} }}"#)) } } diff --git a/tooling/cli/src/helpers/flock.rs b/tooling/cli/src/helpers/flock.rs index b5ff6c8a1f3..c2fcc603a4d 100644 --- a/tooling/cli/src/helpers/flock.rs +++ b/tooling/cli/src/helpers/flock.rs @@ -209,7 +209,7 @@ fn acquire( } } } - let msg = format!("waiting for file lock on {}", msg); + let msg = format!("waiting for file lock on {msg}"); log::info!(action = "Blocking"; "{}", &msg); lock_block().with_context(|| format!("failed to lock file: {}", path.display()))?; diff --git a/tooling/cli/src/helpers/updater_signature.rs b/tooling/cli/src/helpers/updater_signature.rs index d022053dab8..6b86092145c 100644 --- a/tooling/cli/src/helpers/updater_signature.rs +++ b/tooling/cli/src/helpers/updater_signature.rs @@ -81,11 +81,11 @@ where } let mut sk_writer = create_file(sk_path)?; - write!(sk_writer, "{:}", key)?; + write!(sk_writer, "{key:}")?; sk_writer.flush()?; let mut pk_writer = create_file(pk_path)?; - write!(pk_writer, "{:}", pubkey)?; + write!(pk_writer, "{pubkey:}")?; pk_writer.flush()?; Ok((fs::canonicalize(sk_path)?, fs::canonicalize(pk_path)?)) diff --git a/tooling/cli/src/icon.rs b/tooling/cli/src/icon.rs index faa351dcc3f..cc71e48bc19 100644 --- a/tooling/cli/src/icon.rs +++ b/tooling/cli/src/icon.rs @@ -83,7 +83,7 @@ fn appx(source: &DynamicImage, out_dir: &Path) -> Result<()> { resize_and_save_png(source, 50, &out_dir.join("StoreLogo.png"))?; for size in [30, 44, 71, 89, 107, 142, 150, 284, 310] { - let file_name = format!("Square{}x{}Logo.png", size, size); + let file_name = format!("Square{size}x{size}Logo.png"); log::info!(action = "Appx"; "Creating {}", file_name); resize_and_save_png(source, size, &out_dir.join(&file_name))?; @@ -115,7 +115,7 @@ fn icns(source: &DynamicImage, out_dir: &Path) -> Result<()> { &image, IconType::from_ostype(entry.ostype.parse().unwrap()).unwrap(), ) - .with_context(|| format!("Can't add {} to Icns Family", name))?; + .with_context(|| format!("Can't add {name} to Icns Family"))?; } let mut out_file = BufWriter::new(File::create(out_dir.join("icon.icns"))?); @@ -169,7 +169,7 @@ fn png(source: &DynamicImage, out_dir: &Path) -> Result<()> { let file_name = match size { 256 => "128x128@2x.png".to_string(), 512 => "icon.png".to_string(), - _ => format!("{}x{}.png", size, size), + _ => format!("{size}x{size}.png"), }; entries.push(PngEntry { @@ -307,10 +307,7 @@ fn png(source: &DynamicImage, out_dir: &Path) -> Result<()> { }); } for multiplier in target.multipliers { - let name = format!( - "AppIcon-{size_str}@{multiplier}x.png", - multiplier = multiplier - ); + let name = format!("AppIcon-{size_str}@{multiplier}x.png"); entries.push(PngEntry { out_path: out_dir.join(&name), name, diff --git a/tooling/cli/src/info.rs b/tooling/cli/src/info.rs index 8b75901528a..931b77fe0e6 100644 --- a/tooling/cli/src/info.rs +++ b/tooling/cli/src/info.rs @@ -128,7 +128,7 @@ pub(crate) fn cli_upstream_version() -> Result { } fn crate_latest_version(name: &str) -> Option { - let url = format!("https://docs.rs/crate/{}/", name); + let url = format!("https://docs.rs/crate/{name}/"); match ureq::get(&url).call() { Ok(response) => match (response.status(), response.header("location")) { (302, Some(location)) => Some(location.replace(&url, "")), @@ -431,7 +431,7 @@ fn crate_version( crate_lock_package.version.clone() }; ( - format!("{} (no manifest)", version_string), + format!("{version_string} (no manifest)"), vec![crate_lock_package.version.clone()], ) } @@ -457,14 +457,14 @@ fn crate_version( Ok(manifest) => manifest.package.version, Err(_) => "unknown version".to_string(), }; - format!("path:{:?} [{}]", p, v) + format!("path:{p:?} [{v}]") } else if let Some(g) = p.git { is_git = true; - let mut v = format!("git:{}", g); + let mut v = format!("git:{g}"); if let Some(branch) = p.branch { - let _ = write!(v, "&branch={}", branch); + let _ = write!(v, "&branch={branch}"); } else if let Some(rev) = p.rev { - let _ = write!(v, "#{}", rev); + let _ = write!(v, "#{rev}"); } v } else { @@ -505,7 +505,7 @@ fn crate_version( (Some(version), Some(target_version)) => { let target_version = semver::Version::parse(&target_version).unwrap(); if version < target_version { - Some(format!(" (outdated, latest: {})", target_version)) + Some(format!(" (outdated, latest: {target_version})")) } else { None } diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 2bbfd370cd5..1b6a1e53c6d 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -950,8 +950,8 @@ fn tauri_config_to_bundle_settings( } } - // provides `libwebkit2gtk-4.0.so.37` and all `4.0` versions have the -37 package name - depends.push("libwebkit2gtk-4.0-37".to_string()); + // provides `libwebkit2gtk-4.1.so.37` and all `4.0` versions have the -37 package name + depends.push("libwebkit2gtk-4.1-0".to_string()); depends.push("libgtk-3-0".to_string()); } diff --git a/tooling/cli/src/interface/rust/cargo_config.rs b/tooling/cli/src/interface/rust/cargo_config.rs index b55389bff84..8adca17dd1c 100644 --- a/tooling/cli/src/interface/rust/cargo_config.rs +++ b/tooling/cli/src/interface/rust/cargo_config.rs @@ -103,7 +103,7 @@ fn get_file_path( warn: bool, ) -> Result> { let possible = dir.join(filename_without_extension); - let possible_with_extension = dir.join(format!("{}.toml", filename_without_extension)); + let possible_with_extension = dir.join(format!("{filename_without_extension}.toml")); if possible.exists() { if warn && possible_with_extension.exists() { diff --git a/tooling/cli/src/interface/rust/desktop.rs b/tooling/cli/src/interface/rust/desktop.rs index 2ad00feb75c..92960a4fe4c 100644 --- a/tooling/cli/src/interface/rust/desktop.rs +++ b/tooling/cli/src/interface/rust/desktop.rs @@ -151,10 +151,10 @@ pub fn build( let triple_out_dir = app_settings .out_dir(Some(triple.into()), options.debug) - .with_context(|| format!("failed to get {} out dir", triple))?; + .with_context(|| format!("failed to get {triple} out dir"))?; build_production_app(options, available_targets, config_features.clone()) - .with_context(|| format!("failed to build {} binary", triple))?; + .with_context(|| format!("failed to build {triple} binary"))?; lipo_cmd.arg(triple_out_dir.join(bin_name)); } @@ -162,8 +162,7 @@ pub fn build( let lipo_status = lipo_cmd.output_ok()?.status; if !lipo_status.success() { return Err(anyhow::anyhow!(format!( - "Result of `lipo` command was unsuccessful: {}. (Is `lipo` installed?)", - lipo_status + "Result of `lipo` command was unsuccessful: {lipo_status}. (Is `lipo` installed?)" ))); } } else { diff --git a/tooling/cli/src/interface/rust/manifest.rs b/tooling/cli/src/interface/rust/manifest.rs index 35b2c4eb837..b47b8e437a7 100644 --- a/tooling/cli/src/interface/rust/manifest.rs +++ b/tooling/cli/src/interface/rust/manifest.rs @@ -54,7 +54,7 @@ impl Manifest { let mut all_enabled_features: Vec = self .tauri_features .iter() - .map(|f| format!("tauri/{}", f)) + .map(|f| format!("tauri/{f}")) .collect(); let manifest_features = self.features(); @@ -86,7 +86,7 @@ pub fn read_manifest(manifest_path: &Path) -> crate::Result { let mut manifest_str = String::new(); let mut manifest_file = File::open(manifest_path) - .with_context(|| format!("failed to open `{:?}` file", manifest_path))?; + .with_context(|| format!("failed to open `{manifest_path:?}` file"))?; manifest_file.read_to_string(&mut manifest_str)?; let manifest: Document = manifest_str diff --git a/tooling/cli/src/lib.rs b/tooling/cli/src/lib.rs index 1d8af2edeed..24fa3e902f2 100644 --- a/tooling/cli/src/lib.rs +++ b/tooling/cli/src/lib.rs @@ -184,7 +184,7 @@ where .try_init(); if let Err(err) = init_res { - eprintln!("Failed to attach logger: {}", err); + eprintln!("Failed to attach logger: {err}"); } match cli.command { @@ -235,14 +235,14 @@ impl CommandExt for Command { self.stdout(os_pipe::dup_stdout()?); self.stderr(os_pipe::dup_stderr()?); let program = self.get_program().to_string_lossy().into_owned(); - debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg))); + debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}"))); self.status().map_err(Into::into) } fn output_ok(&mut self) -> crate::Result { let program = self.get_program().to_string_lossy().into_owned(); - debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg))); + debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}"))); self.stdout(Stdio::piped()); self.stderr(Stdio::piped()); diff --git a/tooling/cli/src/mobile/init.rs b/tooling/cli/src/mobile/init.rs index 4a07876c36d..dd9b97f7bb7 100644 --- a/tooling/cli/src/mobile/init.rs +++ b/tooling/cli/src/mobile/init.rs @@ -193,10 +193,7 @@ fn get_str<'a>(helper: &'a Helper) -> &'a str { .unwrap_or("") } -fn get_str_array<'a>( - helper: &'a Helper, - formatter: impl Fn(&str) -> String, -) -> Option> { +fn get_str_array(helper: &Helper, formatter: impl Fn(&str) -> String) -> Option> { helper.param(0).and_then(|v| { v.value().as_array().and_then(|arr| { arr @@ -249,7 +246,7 @@ fn quote_and_join( ) -> HelperResult { out .write( - &get_str_array(helper, |s| format!("{:?}", s)) + &get_str_array(helper, |s| format!("{s:?}")) .ok_or_else(|| RenderError::new("`quote-and-join` helper wasn't given an array"))? .join(", "), ) @@ -265,7 +262,7 @@ fn quote_and_join_colon_prefix( ) -> HelperResult { out .write( - &get_str_array(helper, |s| format!("{:?}", format!(":{}", s))) + &get_str_array(helper, |s| format!("{:?}", format!(":{s}"))) .ok_or_else(|| { RenderError::new("`quote-and-join-colon-prefix` helper wasn't given an array") })? diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index bac4f89e206..e92f0fa4f90 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -160,7 +160,7 @@ fn setup_dev_config(config_extension: &mut Option) -> crate::Result<()> c.build.dev_path = dev_path.clone(); config_extension.replace(serde_json::to_string(&c).unwrap()); } else { - config_extension.replace(format!(r#"{{ "build": {{ "devPath": "{}" }} }}"#, url)); + config_extension.replace(format!(r#"{{ "build": {{ "devPath": "{url}" }} }}"#)); } reload_config(config_extension.as_deref())?; } diff --git a/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest b/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest index bae491d80b1..0d69e61d371 100755 --- a/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest +++ b/tooling/cli/templates/app/src-tauri/Cargo.crate-manifest @@ -6,7 +6,7 @@ authors = ["you"] license = "" repository = "" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tooling/cli/templates/plugin/backend/.changes/config.json b/tooling/cli/templates/plugin/backend/.changes/config.json index 680d32c8243..6ce1cfed51a 100755 --- a/tooling/cli/templates/plugin/backend/.changes/config.json +++ b/tooling/cli/templates/plugin/backend/.changes/config.json @@ -6,7 +6,7 @@ "getPublishedVersion": "cargo search ${ pkg.pkg } --limit 1 | sed -nE 's/^[^\"]*\"//; s/\".*//1p' -", "prepublish": [ "sudo apt-get update", - "sudo apt-get install -y webkit2gtk-4.0", + "sudo apt-get install -y webkit2gtk-4.1", "cargo install cargo-audit", { "command": "cargo generate-lockfile", diff --git a/tooling/cli/templates/plugin/backend/.github/workflows/lint.yml b/tooling/cli/templates/plugin/backend/.github/workflows/lint.yml index 037e7b3b55f..6269a79eaf1 100644 --- a/tooling/cli/templates/plugin/backend/.github/workflows/lint.yml +++ b/tooling/cli/templates/plugin/backend/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: - name: install webkit2gtk run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 + sudo apt-get install -y webkit2gtk-4.1 - name: Install clippy with stable toolchain uses: actions-rs/toolchain@v1 with: diff --git a/tooling/cli/templates/plugin/backend/.github/workflows/test.yml b/tooling/cli/templates/plugin/backend/.github/workflows/test.yml index 284e08cf925..30124773b82 100755 --- a/tooling/cli/templates/plugin/backend/.github/workflows/test.yml +++ b/tooling/cli/templates/plugin/backend/.github/workflows/test.yml @@ -35,7 +35,7 @@ jobs: if: matrix.os == 'ubuntu-latest' run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 + sudo apt-get install -y webkit2gtk-4.1 - uses: Swatinem/rust-cache@v2 diff --git a/tooling/cli/templates/plugin/backend/Cargo.crate-manifest b/tooling/cli/templates/plugin/backend/Cargo.crate-manifest index 87d9731ab40..21bf73ed06e 100755 --- a/tooling/cli/templates/plugin/backend/Cargo.crate-manifest +++ b/tooling/cli/templates/plugin/backend/Cargo.crate-manifest @@ -4,7 +4,7 @@ version = "0.0.0" authors = [ "{{ author }}" ] description = "" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = ["/examples"] [dependencies] diff --git a/tooling/cli/templates/plugin/backend/examples/vanilla/src-tauri/Cargo.crate-manifest b/tooling/cli/templates/plugin/backend/examples/vanilla/src-tauri/Cargo.crate-manifest index 74a685bdfd7..dc239129cb7 100644 --- a/tooling/cli/templates/plugin/backend/examples/vanilla/src-tauri/Cargo.crate-manifest +++ b/tooling/cli/templates/plugin/backend/examples/vanilla/src-tauri/Cargo.crate-manifest @@ -5,7 +5,7 @@ description = "A Tauri App" authors = [ "{{ author }}" ] repository = "" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [dependencies] serde_json = "1.0" diff --git a/tooling/cli/templates/plugin/with-api/.changes/config.json b/tooling/cli/templates/plugin/with-api/.changes/config.json index 25df39379e3..b12ed482934 100755 --- a/tooling/cli/templates/plugin/with-api/.changes/config.json +++ b/tooling/cli/templates/plugin/with-api/.changes/config.json @@ -6,7 +6,7 @@ "getPublishedVersion": "cargo search ${ pkg.pkg } --limit 1 | sed -nE 's/^[^\"]*\"//; s/\".*//1p' -", "prepublish": [ "sudo apt-get update", - "sudo apt-get install -y webkit2gtk-4.0", + "sudo apt-get install -y webkit2gtk-4.1", "cargo install cargo-audit", { "command": "cargo generate-lockfile", diff --git a/tooling/cli/templates/plugin/with-api/.github/workflows/clippy.yml b/tooling/cli/templates/plugin/with-api/.github/workflows/clippy.yml index 037e7b3b55f..6269a79eaf1 100644 --- a/tooling/cli/templates/plugin/with-api/.github/workflows/clippy.yml +++ b/tooling/cli/templates/plugin/with-api/.github/workflows/clippy.yml @@ -21,7 +21,7 @@ jobs: - name: install webkit2gtk run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 + sudo apt-get install -y webkit2gtk-4.1 - name: Install clippy with stable toolchain uses: actions-rs/toolchain@v1 with: diff --git a/tooling/cli/templates/plugin/with-api/.github/workflows/test.yml b/tooling/cli/templates/plugin/with-api/.github/workflows/test.yml index 81823087373..49e7850b167 100644 --- a/tooling/cli/templates/plugin/with-api/.github/workflows/test.yml +++ b/tooling/cli/templates/plugin/with-api/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: - name: Install Linux dependencies run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.0 + sudo apt-get install -y webkit2gtk-4.1 - uses: Swatinem/rust-cache@v2 diff --git a/tooling/cli/templates/plugin/with-api/Cargo.crate-manifest b/tooling/cli/templates/plugin/with-api/Cargo.crate-manifest index d3ed73625b3..10529fa2ed7 100644 --- a/tooling/cli/templates/plugin/with-api/Cargo.crate-manifest +++ b/tooling/cli/templates/plugin/with-api/Cargo.crate-manifest @@ -4,7 +4,7 @@ version = "0.0.0" authors = [ "{{ author }}" ] description = "" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" exclude = ["/examples", "/webview-dist", "/webview-src", "node_modules"] [dependencies] diff --git a/tooling/cli/templates/plugin/with-api/examples/svelte-app/src-tauri/Cargo.crate-manifest b/tooling/cli/templates/plugin/with-api/examples/svelte-app/src-tauri/Cargo.crate-manifest index 74a685bdfd7..dc239129cb7 100644 --- a/tooling/cli/templates/plugin/with-api/examples/svelte-app/src-tauri/Cargo.crate-manifest +++ b/tooling/cli/templates/plugin/with-api/examples/svelte-app/src-tauri/Cargo.crate-manifest @@ -5,7 +5,7 @@ description = "A Tauri App" authors = [ "{{ author }}" ] repository = "" edition = "2021" -rust-version = "1.59" +rust-version = "1.64" [dependencies] serde_json = "1.0" From 553a81f48cf0819ef06020327113f79995e70c1a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 30 Jan 2023 11:54:02 -0300 Subject: [PATCH 128/423] feat(examples): add tauri-plugin-log to API example --- examples/api/src-tauri/Cargo.lock | 367 +++++++++++++++++------------- examples/api/src-tauri/Cargo.toml | 4 + examples/api/src-tauri/src/lib.rs | 5 + 3 files changed, 219 insertions(+), 157 deletions(-) diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index f2c2265c3b8..c54d484e6c5 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -67,6 +67,24 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8619b80c242aa7bd638b5c7ddd952addeecb71f69c75e33f1d47b2804f8f883a" +dependencies = [ + "android_log-sys", + "env_logger", + "log", + "once_cell", +] + [[package]] name = "anyhow" version = "1.0.66" @@ -82,6 +100,7 @@ dependencies = [ "serde_json", "tauri", "tauri-build", + "tauri-plugin-log", "tiny_http", "window-shadows", "window-vibrancy", @@ -95,9 +114,9 @@ checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "atk" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" +checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" dependencies = [ "atk-sys", "bitflags", @@ -107,14 +126,14 @@ dependencies = [ [[package]] name = "atk-sys" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" +checksum = "11ad703eb64dc058024f0e57ccfa069e15a413b98dbd50a1a950e743b7f11148" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps", ] [[package]] @@ -214,6 +233,16 @@ version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +[[package]] +name = "byte-unit" +version = "4.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3348673602e04848647fffaa8e9a861e7b5d5cae6570727b41bde0f722514484" +dependencies = [ + "serde", + "utf8-width", +] + [[package]] name = "bytemuck" version = "1.12.3" @@ -237,26 +266,27 @@ dependencies = [ [[package]] name = "cairo-rs" -version = "0.15.12" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" +checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" dependencies = [ "bitflags", "cairo-sys-rs", "glib", "libc", + "once_cell", "thiserror", ] [[package]] name = "cairo-sys-rs" -version = "0.15.1" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" dependencies = [ "glib-sys", "libc", - "system-deps 6.0.3", + "system-deps", ] [[package]] @@ -302,15 +332,6 @@ dependencies = [ "uuid 1.2.1", ] -[[package]] -name = "cfg-expr" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" -dependencies = [ - "smallvec", -] - [[package]] name = "cfg-expr" version = "0.11.0" @@ -593,6 +614,19 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "dbus" version = "0.9.6" @@ -690,6 +724,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "log", + "regex", +] + [[package]] name = "fastrand" version = "1.8.0" @@ -699,6 +743,15 @@ dependencies = [ "instant", ] +[[package]] +name = "fern" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" +dependencies = [ + "log", +] + [[package]] name = "field-offset" version = "0.3.4" @@ -851,9 +904,9 @@ dependencies = [ [[package]] name = "gdk" -version = "0.15.4" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" +checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" dependencies = [ "bitflags", "cairo-rs", @@ -867,9 +920,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.15.11" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" +checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05" dependencies = [ "bitflags", "gdk-pixbuf-sys", @@ -880,22 +933,22 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" -version = "0.15.10" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" +checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps", ] [[package]] name = "gdk-sys" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" +checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -905,19 +958,19 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.0.3", + "system-deps", ] [[package]] name = "gdkx11-sys" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" +checksum = "9fa2bf8b5b8c414bc5d05e48b271896d0fd3ddb57464a3108438082da61de6af" dependencies = [ "gdk-sys", "glib-sys", "libc", - "system-deps 6.0.3", + "system-deps", "x11", ] @@ -978,45 +1031,50 @@ dependencies = [ [[package]] name = "gio" -version = "0.15.12" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" +checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-io", + "futures-util", "gio-sys", "glib", "libc", "once_cell", + "pin-project-lite", + "smallvec", "thiserror", ] [[package]] name = "gio-sys" -version = "0.15.10" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" +checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps", "winapi", ] [[package]] name = "glib" -version = "0.15.12" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-executor", "futures-task", + "futures-util", + "gio-sys", "glib-macros", "glib-sys", "gobject-sys", @@ -1028,9 +1086,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.15.11" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" +checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf" dependencies = [ "anyhow", "heck 0.4.0", @@ -1043,12 +1101,12 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.15.10" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" dependencies = [ "libc", - "system-deps 6.0.3", + "system-deps", ] [[package]] @@ -1072,20 +1130,20 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.15.10" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" dependencies = [ "glib-sys", "libc", - "system-deps 6.0.3", + "system-deps", ] [[package]] name = "gtk" -version = "0.15.5" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" +checksum = "e4d3507d43908c866c805f74c9dd593c0ce7ba5c38e576e41846639cdcd4bee6" dependencies = [ "atk", "bitflags", @@ -1106,9 +1164,9 @@ dependencies = [ [[package]] name = "gtk-sys" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" +checksum = "89b5f8946685d5fe44497007786600c2f368ff6b1e61a16251c89f72a97520a3" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -1119,14 +1177,14 @@ dependencies = [ "gobject-sys", "libc", "pango-sys", - "system-deps 6.0.3", + "system-deps", ] [[package]] name = "gtk3-macros" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9" +checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff" dependencies = [ "anyhow", "proc-macro-crate", @@ -1377,9 +1435,9 @@ checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "javascriptcore-rs" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" +checksum = "110b9902c80c12bf113c432d0b71c7a94490b294a8234f326fd0abca2fac0b00" dependencies = [ "bitflags", "glib", @@ -1388,14 +1446,14 @@ dependencies = [ [[package]] name = "javascriptcore-rs-sys" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" +checksum = "98a216519a52cd941a733a0ad3f1023cfdb1cd47f3955e8e863ed56f558f916c" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] @@ -1458,9 +1516,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libappindicator" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2d3cb96d092b4824cb306c9e544c856a4cb6210c1081945187f7f1924b47e8" +checksum = "89e1edfdc9b0853358306c6dfb4b77c79c779174256fe93d80c0b5ebca451a2f" dependencies = [ "glib", "gtk", @@ -1471,9 +1529,9 @@ dependencies = [ [[package]] name = "libappindicator-sys" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b3b6681973cea8cc3bce7391e6d7d5502720b80a581c9a95c9cbaf592826aa" +checksum = "08fcb2bea89cee9613982501ec83eaa2d09256b24540ae463c52a28906163918" dependencies = [ "gtk-sys", "libloading", @@ -1531,6 +1589,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", + "value-bag", ] [[package]] @@ -1895,6 +1954,17 @@ version = "6.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" +[[package]] +name = "oslog" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d2043d1f61d77cb2f4b1f7b7b2295f40507f5f8e9d1c8bf10a1ca5f97a3969" +dependencies = [ + "cc", + "dashmap", + "log", +] + [[package]] name = "overload" version = "0.1.1" @@ -1903,11 +1973,12 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pango" -version = "0.15.10" +version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" +checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" dependencies = [ "bitflags", + "gio", "glib", "libc", "once_cell", @@ -1916,14 +1987,14 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.15.10" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" +checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps", ] [[package]] @@ -2395,8 +2466,7 @@ dependencies = [ [[package]] name = "rfd" version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" +source = "git+https://github.com/PolyMeilex/rfd#c2ea907bbcd055239d615cd8029a86089494bddc" dependencies = [ "block", "dispatch", @@ -2404,7 +2474,6 @@ dependencies = [ "gobject-sys", "gtk-sys", "js-sys", - "lazy_static", "log", "objc", "objc-foundation", @@ -2413,7 +2482,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.37.0", + "windows 0.43.0", ] [[package]] @@ -2690,31 +2759,31 @@ dependencies = [ ] [[package]] -name = "soup2" -version = "0.2.1" +name = "soup3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +checksum = "82bc46048125fefd69d30b32b9d263d6556c9ffe82a7a7df181a86d912da5616" dependencies = [ "bitflags", + "futures-channel", "gio", "glib", "libc", "once_cell", - "soup2-sys", + "soup3-sys", ] [[package]] -name = "soup2-sys" -version = "0.2.0" +name = "soup3-sys" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +checksum = "014bbeb1c4cdb30739dc181e8d98b7908f124d9555843afa89b5570aaf4ec62b" dependencies = [ - "bitflags", "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] @@ -2802,37 +2871,24 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "system-deps" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" -dependencies = [ - "cfg-expr 0.9.1", - "heck 0.3.3", - "pkg-config", - "toml", - "version-compare 0.0.11", -] - [[package]] name = "system-deps" version = "6.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" dependencies = [ - "cfg-expr 0.11.0", + "cfg-expr", "heck 0.4.0", "pkg-config", "toml", - "version-compare 0.1.0", + "version-compare", ] [[package]] name = "tao" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704522803dda895767f69198af8351b0a3f4fe2e293c3ca54cce0ecba05a97f2" +checksum = "0d021218bcb43d4bf42112b1da5f7d8454502ffb188489ba0089fe4e3e34a8f6" dependencies = [ "bitflags", "cairo-rs", @@ -3016,6 +3072,23 @@ dependencies = [ "tauri-utils", ] +[[package]] +name = "tauri-plugin-log" +version = "0.1.0" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=next#37e0511020bd7d06cd29a293779efb1e2b96ba38" +dependencies = [ + "android_logger", + "byte-unit", + "fern", + "log", + "oslog", + "serde", + "serde_json", + "serde_repr", + "tauri", + "time", +] + [[package]] name = "tauri-runtime" version = "0.13.0-alpha.0" @@ -3421,6 +3494,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8-width" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" + [[package]] name = "uuid" version = "0.8.2" @@ -3443,10 +3522,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] -name = "version-compare" -version = "0.0.11" +name = "value-bag" +version = "1.0.0-alpha.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", +] [[package]] name = "version-compare" @@ -3571,9 +3654,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "0.18.2" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" +checksum = "50668f506786de0e8c2542a07c4720effbd6e58be11746f2b451875d048d7f56" dependencies = [ "bitflags", "cairo-rs", @@ -3589,20 +3672,18 @@ dependencies = [ "javascriptcore-rs", "libc", "once_cell", - "soup2", + "soup3", "webkit2gtk-sys", ] [[package]] name = "webkit2gtk-sys" -version = "0.18.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" +checksum = "d0ac7a95ddd3fdfcaf83d8e513b4b1ad101b95b413b6aa6662ed95f284fc3d5b" dependencies = [ - "atk-sys", "bitflags", "cairo-sys-rs", - "gdk-pixbuf-sys", "gdk-sys", "gio-sys", "glib-sys", @@ -3610,10 +3691,9 @@ dependencies = [ "gtk-sys", "javascriptcore-rs-sys", "libc", - "pango-sys", "pkg-config", - "soup2-sys", - "system-deps 6.0.3", + "soup3-sys", + "system-deps", ] [[package]] @@ -3732,19 +3812,6 @@ dependencies = [ "windows_x86_64_msvc 0.32.0", ] -[[package]] -name = "windows" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" -dependencies = [ - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", -] - [[package]] name = "windows" version = "0.39.0" @@ -3759,6 +3826,21 @@ dependencies = [ "windows_x86_64_msvc 0.39.0", ] +[[package]] +name = "windows" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + [[package]] name = "windows-bindgen" version = "0.39.0" @@ -3837,12 +3919,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - [[package]] name = "windows_aarch64_msvc" version = "0.39.0" @@ -3867,12 +3943,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - [[package]] name = "windows_i686_gnu" version = "0.39.0" @@ -3897,12 +3967,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - [[package]] name = "windows_i686_msvc" version = "0.39.0" @@ -3927,12 +3991,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - [[package]] name = "windows_x86_64_gnu" version = "0.39.0" @@ -3963,12 +4021,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - [[package]] name = "windows_x86_64_msvc" version = "0.39.0" @@ -4001,8 +4053,9 @@ dependencies = [ [[package]] name = "wry" -version = "0.24.1" -source = "git+https://github.com/tauri-apps/wry?branch=dev#bce39e2be195194e547b0021e770e45a3df15fa1" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164aae72fb3759aa178ebb274c572728a871abce011e53821e9468335314fe00" dependencies = [ "base64", "block", @@ -4025,7 +4078,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "soup2", + "soup3", "tao", "thiserror", "url", diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 31a0ad7fbc9..eecf650f3e7 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -17,6 +17,10 @@ serde_json = "1.0" serde = { version = "1.0", features = [ "derive" ] } tiny_http = "0.11" log = "0.4" +tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "next" } + +[patch.'https://github.com/tauri-apps/tauri'] +tauri = { path = "../../../core/tauri" } [dependencies.tauri] path = "../../../core/tauri" diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index ca001726919..b44c207ff6c 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -60,6 +60,11 @@ impl AppBuilder { #[allow(unused_mut)] let mut builder = tauri::Builder::default() + .plugin( + tauri_plugin_log::Builder::default() + .level(log::LevelFilter::Info) + .build(), + ) .setup(move |app| { if let Some(setup) = setup { (setup)(app)?; From f0a1d9cdbcfb645ce1c5f1cdd597f764991772cd Mon Sep 17 00:00:00 2001 From: "Ngo Iok Ui (Wu Yu Wei)" Date: Fri, 3 Feb 2023 03:44:57 +0800 Subject: [PATCH 129/423] chore: update rfd and wry versions (#6174) Co-authored-by: Wu Yu Wei Co-authored-by: Lucas Nogueira --- .changes/rfd101.md | 6 ++++++ .changes/wry26.md | 6 ++++++ core/tauri-runtime-wry/Cargo.toml | 2 +- core/tauri/Cargo.toml | 2 +- tooling/cli/Cargo.lock | 5 +++-- tooling/cli/Cargo.toml | 2 +- 6 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 .changes/rfd101.md create mode 100644 .changes/wry26.md diff --git a/.changes/rfd101.md b/.changes/rfd101.md new file mode 100644 index 00000000000..ef230029a9b --- /dev/null +++ b/.changes/rfd101.md @@ -0,0 +1,6 @@ +--- +"tauri": minor +--- + +Update rfd to 0.11. + diff --git a/.changes/wry26.md b/.changes/wry26.md new file mode 100644 index 00000000000..2775757c55c --- /dev/null +++ b/.changes/wry26.md @@ -0,0 +1,6 @@ +--- +"tauri-runtime-wry": minor +--- + +Update wry to 0.26. + diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index c195ccf4f84..26144538ae4 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -13,7 +13,7 @@ exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" [dependencies] -wry = { version = "0.25.0", default-features = false, features = [ "file-drop", "protocol" ] } +wry = { version = "0.26.0", default-features = false, features = [ "file-drop", "protocol" ] } tauri-runtime = { version = "0.13.0-alpha.0", path = "../tauri-runtime" } tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index d325b224378..b8389a55df7 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -87,7 +87,7 @@ ico = { version = "0.2.0", optional = true } encoding_rs = "0.8.31" [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -rfd = { git = "https://github.com/PolyMeilex/rfd", version = "0.10", optional = true } +rfd = { version = "0.11.0", optional = true } notify-rust = { version = "4.5", default-features = false, features = [ "d" ], optional = true } [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index bff0f2e3cea..34a18513831 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3844,8 +3844,9 @@ dependencies = [ [[package]] name = "tauri-mobile" -version = "0.2.0" -source = "git+https://github.com/tauri-apps/tauri-mobile?branch=dev#01c5f5083760714163e527bf9b8c80157150aed8" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030f6c282001e94a89a347f4d1684f146091140058b57d0af04862a53694dbe5" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 4c75ee57bf4..9753206456c 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -39,7 +39,7 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -tauri-mobile = { git = "https://github.com/tauri-apps/tauri-mobile", branch = "dev", default-features = false } +tauri-mobile = { version = "0.2", default-features = false } textwrap = { version = "0.11.0", features = [ "term_size" ] } jsonrpsee = { version = "0.16", features = [ "client", "server" ] } thiserror = "1" From 0111b88d753721e788d5c1512a50b4eaee0a10f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 17:55:23 -0300 Subject: [PATCH 130/423] (NEXT) Apply Version Updates From Current Changes (#5924) Co-authored-by: lucasfernog Co-authored-by: Lucas Nogueira --- .changes/pre.json | 17 ++++++++++++++++- core/tauri-build/CHANGELOG.md | 9 +++++++++ core/tauri-build/Cargo.toml | 6 +++--- core/tauri-codegen/CHANGELOG.md | 7 +++++++ core/tauri-codegen/Cargo.toml | 4 ++-- core/tauri-macros/CHANGELOG.md | 9 +++++++++ core/tauri-macros/Cargo.toml | 6 +++--- core/tauri-runtime-wry/CHANGELOG.md | 9 +++++++++ core/tauri-runtime-wry/Cargo.toml | 6 +++--- core/tauri-runtime/CHANGELOG.md | 7 +++++++ core/tauri-runtime/Cargo.toml | 4 ++-- core/tauri-utils/CHANGELOG.md | 7 +++++++ core/tauri-utils/Cargo.toml | 2 +- core/tauri/CHANGELOG.md | 15 +++++++++++++++ core/tauri/Cargo.toml | 10 +++++----- tooling/bundler/CHANGELOG.md | 5 +++++ tooling/bundler/Cargo.toml | 4 ++-- tooling/cli/CHANGELOG.md | 19 +++++++++++++++++++ tooling/cli/Cargo.lock | 6 +++--- tooling/cli/Cargo.toml | 6 +++--- tooling/cli/metadata.json | 6 +++--- tooling/cli/node/CHANGELOG.md | 17 +++++++++++++++++ tooling/cli/node/package.json | 2 +- 23 files changed, 151 insertions(+), 32 deletions(-) diff --git a/.changes/pre.json b/.changes/pre.json index f025ae492b4..eb612d6cd27 100644 --- a/.changes/pre.json +++ b/.changes/pre.json @@ -11,15 +11,30 @@ ".changes/dev-proxy.md", ".changes/fix-dev-server-proxy-path.md", ".changes/fix-ios-run-xcode14.md", + ".changes/fix-mobile-env-vars.md", + ".changes/force-colored-logs.md", ".changes/glob-match-require_literal_separator.md", + ".changes/gtk16.md", ".changes/improve-local-ip-detection.md", + ".changes/ios-keep-alive.md", + ".changes/ios-logs.md", + ".changes/logcat-all-tags.md", ".changes/mobile-config.md", ".changes/mobile-entry-point-macro.md", + ".changes/mobile-env-vars-rename.md", ".changes/mobile-init.md", + ".changes/mobile-lib-name.md", ".changes/mobile-open.md", ".changes/mobile-webview-access.md", ".changes/mobile.md", + ".changes/msrv-1.64.md", + ".changes/only-proxy-on-mobile.md", + ".changes/package-info-crate-name.md", ".changes/refactor-setup.md", - ".changes/tauri-mobile-entry-point.md" + ".changes/remove-mobile-log.md", + ".changes/rfd101.md", + ".changes/target-dir-detection.md", + ".changes/tauri-mobile-entry-point.md", + ".changes/wry26.md" ] } diff --git a/core/tauri-build/CHANGELOG.md b/core/tauri-build/CHANGELOG.md index 7a77a98ef0e..b53665c67c0 100644 --- a/core/tauri-build/CHANGELOG.md +++ b/core/tauri-build/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## \[2.0.0-alpha.1] + +- Refactor mobile environment variables. + - [dee9460f](https://www.github.com/tauri-apps/tauri/commit/dee9460f9c9bc92e9c638e7691e616849ac2085b) feat: keep CLI alive when iOS app exits, show logs, closes [#5855](https://www.github.com/tauri-apps/tauri/pull/5855) ([#5902](https://www.github.com/tauri-apps/tauri/pull/5902)) on 2022-12-27 +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Removed mobile logging initialization, which will be handled by `tauri-plugin-log`. + - [](https://www.github.com/tauri-apps/tauri/commit/undefined) on undefined + ## \[2.0.0-alpha.0] - Set environment variables used by `tauri::mobile_entry_point`. diff --git a/core/tauri-build/Cargo.toml b/core/tauri-build/Cargo.toml index 1f0b3617ffd..eb000f4e771 100644 --- a/core/tauri-build/Cargo.toml +++ b/core/tauri-build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-build" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -19,8 +19,8 @@ rustdoc-args = [ "--cfg", "doc_cfg" ] [dependencies] anyhow = "1" quote = { version = "1", optional = true } -tauri-codegen = { version = "2.0.0-alpha.0", path = "../tauri-codegen", optional = true } -tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils", features = [ "build", "resources" ] } +tauri-codegen = { version = "2.0.0-alpha.1", path = "../tauri-codegen", optional = true } +tauri-utils = { version = "2.0.0-alpha.1", path = "../tauri-utils", features = [ "build", "resources" ] } cargo_toml = "0.13" serde_json = "1" heck = "0.4" diff --git a/core/tauri-codegen/CHANGELOG.md b/core/tauri-codegen/CHANGELOG.md index e32ce10b394..3c110e02045 100644 --- a/core/tauri-codegen/CHANGELOG.md +++ b/core/tauri-codegen/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.0-alpha.1] + +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Added `crate_name` field on `PackageInfo`. + - [630a7f4b](https://www.github.com/tauri-apps/tauri/commit/630a7f4b18cef169bfd48673609306fec434e397) refactor: remove mobile log initialization, ref [#6049](https://www.github.com/tauri-apps/tauri/pull/6049) ([#6081](https://www.github.com/tauri-apps/tauri/pull/6081)) on 2023-01-17 + ## \[2.0.0-alpha.0] - Change `devPath` URL to use the local IP address on iOS and Android. diff --git a/core/tauri-codegen/Cargo.toml b/core/tauri-codegen/Cargo.toml index 36cc7b747eb..bc36df3b4db 100644 --- a/core/tauri-codegen/Cargo.toml +++ b/core/tauri-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-codegen" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -19,7 +19,7 @@ proc-macro2 = "1" quote = "1" serde = { version = "1", features = [ "derive" ] } serde_json = "1" -tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils", features = [ "build" ] } +tauri-utils = { version = "2.0.0-alpha.1", path = "../tauri-utils", features = [ "build" ] } thiserror = "1" walkdir = "2" brotli = { version = "3", optional = true, default-features = false, features = [ "std" ] } diff --git a/core/tauri-macros/CHANGELOG.md b/core/tauri-macros/CHANGELOG.md index 32d3e7e7067..897a4f185f7 100644 --- a/core/tauri-macros/CHANGELOG.md +++ b/core/tauri-macros/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## \[2.0.0-alpha.1] + +- Refactor mobile environment variables. + - [dee9460f](https://www.github.com/tauri-apps/tauri/commit/dee9460f9c9bc92e9c638e7691e616849ac2085b) feat: keep CLI alive when iOS app exits, show logs, closes [#5855](https://www.github.com/tauri-apps/tauri/pull/5855) ([#5902](https://www.github.com/tauri-apps/tauri/pull/5902)) on 2022-12-27 +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Removed mobile logging initialization, which will be handled by `tauri-plugin-log`. + - [](https://www.github.com/tauri-apps/tauri/commit/undefined) on undefined + ## \[2.0.0-alpha.0] - Added the `mobile_entry_point` macro. diff --git a/core/tauri-macros/Cargo.toml b/core/tauri-macros/Cargo.toml index 575d0f8bdf1..563777fe2ed 100644 --- a/core/tauri-macros/Cargo.toml +++ b/core/tauri-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-macros" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "os", "filesystem", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -20,8 +20,8 @@ proc-macro2 = "1" quote = "1" syn = { version = "1", features = [ "full" ] } heck = "0.4" -tauri-codegen = { version = "2.0.0-alpha.0", default-features = false, path = "../tauri-codegen" } -tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } +tauri-codegen = { version = "2.0.0-alpha.1", default-features = false, path = "../tauri-codegen" } +tauri-utils = { version = "2.0.0-alpha.1", path = "../tauri-utils" } [features] custom-protocol = [ ] diff --git a/core/tauri-runtime-wry/CHANGELOG.md b/core/tauri-runtime-wry/CHANGELOG.md index 84d2d789392..e121d5df09a 100644 --- a/core/tauri-runtime-wry/CHANGELOG.md +++ b/core/tauri-runtime-wry/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## \[0.13.0-alpha.1] + +- Update gtk to 0.16. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Update wry to 0.26. + - [f0a1d9cd](https://www.github.com/tauri-apps/tauri/commit/f0a1d9cdbcfb645ce1c5f1cdd597f764991772cd) chore: update rfd and wry versions ([#6174](https://www.github.com/tauri-apps/tauri/pull/6174)) on 2023-02-03 + ## \[0.13.0-alpha.0] - Support `with_webview` for Android platform alowing execution of JNI code in context. diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 26144538ae4..6ab46fe0724 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-runtime-wry" -version = "0.13.0-alpha.0" +version = "0.13.0-alpha.1" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -14,8 +14,8 @@ readme = "README.md" [dependencies] wry = { version = "0.26.0", default-features = false, features = [ "file-drop", "protocol" ] } -tauri-runtime = { version = "0.13.0-alpha.0", path = "../tauri-runtime" } -tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } +tauri-runtime = { version = "0.13.0-alpha.1", path = "../tauri-runtime" } +tauri-utils = { version = "2.0.0-alpha.1", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } rand = "0.8" raw-window-handle = "0.5" diff --git a/core/tauri-runtime/CHANGELOG.md b/core/tauri-runtime/CHANGELOG.md index c488a06e150..58dcd34e372 100644 --- a/core/tauri-runtime/CHANGELOG.md +++ b/core/tauri-runtime/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[0.13.0-alpha.1] + +- Update gtk to 0.16. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 + ## \[0.13.0-alpha.0] - Parse `android` and `ios` Tauri configuration files. diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml index 75d46e94a54..76052f276d8 100644 --- a/core/tauri-runtime/Cargo.toml +++ b/core/tauri-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-runtime" -version = "0.13.0-alpha.0" +version = "0.13.0-alpha.1" authors = [ "Tauri Programme within The Commons Conservancy" ] categories = [ "gui", "web-programming" ] license = "Apache-2.0 OR MIT" @@ -26,7 +26,7 @@ targets = [ serde = { version = "1.0", features = [ "derive" ] } serde_json = "1.0" thiserror = "1.0" -tauri-utils = { version = "2.0.0-alpha.0", path = "../tauri-utils" } +tauri-utils = { version = "2.0.0-alpha.1", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } http = "0.2.4" http-range = "0.1.4" diff --git a/core/tauri-utils/CHANGELOG.md b/core/tauri-utils/CHANGELOG.md index 5e81091a6e4..49d9503c3b0 100644 --- a/core/tauri-utils/CHANGELOG.md +++ b/core/tauri-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## \[2.0.0-alpha.1] + +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Added `crate_name` field on `PackageInfo`. + - [630a7f4b](https://www.github.com/tauri-apps/tauri/commit/630a7f4b18cef169bfd48673609306fec434e397) refactor: remove mobile log initialization, ref [#6049](https://www.github.com/tauri-apps/tauri/pull/6049) ([#6081](https://www.github.com/tauri-apps/tauri/pull/6081)) on 2023-01-17 + ## \[2.0.0-alpha.0] - Parse `android` and `ios` Tauri configuration files. diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml index 84ea058a114..01436199866 100644 --- a/core/tauri-utils/Cargo.toml +++ b/core/tauri-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-utils" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" authors = [ "Tauri Programme within The Commons Conservancy" ] license = "Apache-2.0 OR MIT" homepage = "https://tauri.app" diff --git a/core/tauri/CHANGELOG.md b/core/tauri/CHANGELOG.md index 73af8c9e0e2..91204f1a109 100644 --- a/core/tauri/CHANGELOG.md +++ b/core/tauri/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## \[2.0.0-alpha.3] + +- Update gtk to 0.16. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Show all application logs on iOS. + - [dee9460f](https://www.github.com/tauri-apps/tauri/commit/dee9460f9c9bc92e9c638e7691e616849ac2085b) feat: keep CLI alive when iOS app exits, show logs, closes [#5855](https://www.github.com/tauri-apps/tauri/pull/5855) ([#5902](https://www.github.com/tauri-apps/tauri/pull/5902)) on 2022-12-27 +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Only proxy the dev server on mobile to simplify desktop usage. + - [78eaadae](https://www.github.com/tauri-apps/tauri/commit/78eaadae2e75ab165d1970e592bb1455bb8636e3) refactor(core): only proxy on mobile ([#6126](https://www.github.com/tauri-apps/tauri/pull/6126)) on 2023-01-23 +- Removed mobile logging initialization, which will be handled by `tauri-plugin-log`. + - [](https://www.github.com/tauri-apps/tauri/commit/undefined) on undefined +- Update rfd to 0.11. + - [f0a1d9cd](https://www.github.com/tauri-apps/tauri/commit/f0a1d9cdbcfb645ce1c5f1cdd597f764991772cd) chore: update rfd and wry versions ([#6174](https://www.github.com/tauri-apps/tauri/pull/6174)) on 2023-02-03 + ## \[2.0.0-alpha.2] - Fix the filesystem scope allowing sub-directories of the directory picked by the dialog when `recursive` option was `false`. diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index b8389a55df7..4d9ef454343 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR MIT" name = "tauri" readme = "README.md" repository = "https://github.com/tauri-apps/tauri" -version = "2.0.0-alpha.2" +version = "2.0.0-alpha.3" [package.metadata.docs.rs] no-default-features = true @@ -49,10 +49,10 @@ url = { version = "2.3" } anyhow = "1.0" thiserror = "1.0" once_cell = "1" -tauri-runtime = { version = "0.13.0-alpha.0", path = "../tauri-runtime" } -tauri-macros = { version = "2.0.0-alpha.0", path = "../tauri-macros" } -tauri-utils = { version = "2.0.0-alpha.0", features = [ "resources" ], path = "../tauri-utils" } -tauri-runtime-wry = { version = "0.13.0-alpha.0", path = "../tauri-runtime-wry", optional = true } +tauri-runtime = { version = "0.13.0-alpha.1", path = "../tauri-runtime" } +tauri-macros = { version = "2.0.0-alpha.1", path = "../tauri-macros" } +tauri-utils = { version = "2.0.0-alpha.1", features = [ "resources" ], path = "../tauri-utils" } +tauri-runtime-wry = { version = "0.13.0-alpha.1", path = "../tauri-runtime-wry", optional = true } rand = "0.8" semver = { version = "1.0", features = [ "serde" ] } serde_repr = "0.1" diff --git a/tooling/bundler/CHANGELOG.md b/tooling/bundler/CHANGELOG.md index 1011ffe01e7..a0eb497d35a 100644 --- a/tooling/bundler/CHANGELOG.md +++ b/tooling/bundler/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## \[2.0.0-alpha.1] + +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 + ## \[2.0.0-alpha.0] - First mobile alpha release! diff --git a/tooling/bundler/Cargo.toml b/tooling/bundler/Cargo.toml index 6de0c5dc337..939a4f6909b 100644 --- a/tooling/bundler/Cargo.toml +++ b/tooling/bundler/Cargo.toml @@ -2,7 +2,7 @@ workspace = { } [package] name = "tauri-bundler" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" authors = [ "George Burton ", "Tauri Programme within The Commons Conservancy" @@ -17,7 +17,7 @@ rust-version = "1.64" exclude = [ "CHANGELOG.md", "/target", "rustfmt.toml" ] [dependencies] -tauri-utils = { version = "2.0.0-alpha.0", path = "../../core/tauri-utils", features = [ "resources" ] } +tauri-utils = { version = "2.0.0-alpha.1", path = "../../core/tauri-utils", features = [ "resources" ] } image = "0.24.5" libflate = "1.2" anyhow = "1.0" diff --git a/tooling/cli/CHANGELOG.md b/tooling/cli/CHANGELOG.md index 2c8d108297a..0bd14a976e0 100644 --- a/tooling/cli/CHANGELOG.md +++ b/tooling/cli/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## \[2.0.0-alpha.2] + +- Fixes `TAURI_*` environment variables for hook scripts on mobile commands. + - [1af9be90](https://www.github.com/tauri-apps/tauri/commit/1af9be904a309138b9f79dc741391000b1652c75) feat(cli): properly fill target for TAURI\_ env vars on mobile ([#6116](https://www.github.com/tauri-apps/tauri/pull/6116)) on 2023-01-23 +- Force colored logs on mobile commands. + - [2c4a0bbd](https://www.github.com/tauri-apps/tauri/commit/2c4a0bbd1fbe15d7500264e6490772397e1917ed) feat(cli): force colored logs on mobile commands ([#5934](https://www.github.com/tauri-apps/tauri/pull/5934)) on 2022-12-28 +- Keep the process alive even when the iOS application is closed. + - [dee9460f](https://www.github.com/tauri-apps/tauri/commit/dee9460f9c9bc92e9c638e7691e616849ac2085b) feat: keep CLI alive when iOS app exits, show logs, closes [#5855](https://www.github.com/tauri-apps/tauri/pull/5855) ([#5902](https://www.github.com/tauri-apps/tauri/pull/5902)) on 2022-12-27 +- Show all application logs on iOS. + - [dee9460f](https://www.github.com/tauri-apps/tauri/commit/dee9460f9c9bc92e9c638e7691e616849ac2085b) feat: keep CLI alive when iOS app exits, show logs, closes [#5855](https://www.github.com/tauri-apps/tauri/pull/5855) ([#5902](https://www.github.com/tauri-apps/tauri/pull/5902)) on 2022-12-27 +- Print log output for all tags on Android development. + - [8cc11149](https://www.github.com/tauri-apps/tauri/commit/8cc111494d74161e489152e52191e1442dd99759) fix(cli): print Android logs for all tags on 2023-01-17 +- Add support to custom and kebab case library names for mobile apps. + - [50f6dd87](https://www.github.com/tauri-apps/tauri/commit/50f6dd87b1ac2c99f8794b055f1acba4ef7d34d3) feat: improvements to support hyphens in crate name ([#5989](https://www.github.com/tauri-apps/tauri/pull/5989)) on 2023-01-06 +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Fix target directory detection when compiling for Android. + - [e873bae0](https://www.github.com/tauri-apps/tauri/commit/e873bae09f0f27517f720a753f51c1dcb903f883) fix(cli): Cargo target dir detection on Android, closes [#5865](https://www.github.com/tauri-apps/tauri/pull/5865) ([#5932](https://www.github.com/tauri-apps/tauri/pull/5932)) on 2022-12-28 + ## \[2.0.0-alpha.1] - Fixes running on device using Xcode 14. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 34a18513831..f2274bc6574 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3731,7 +3731,7 @@ dependencies = [ [[package]] name = "tauri-bundler" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" dependencies = [ "anyhow", "ar", @@ -3768,7 +3768,7 @@ dependencies = [ [[package]] name = "tauri-cli" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" dependencies = [ "anyhow", "axum", @@ -3883,7 +3883,7 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" dependencies = [ "aes-gcm", "ctor", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 9753206456c..4c0459ca14a 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -3,7 +3,7 @@ members = [ "node" ] [package] name = "tauri-cli" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" authors = [ "Tauri Programme within The Commons Conservancy" ] edition = "2021" rust-version = "1.64" @@ -46,7 +46,7 @@ thiserror = "1" sublime_fuzzy = "0.7" clap = { version = "4.0", features = [ "derive" ] } anyhow = "1.0" -tauri-bundler = { version = "2.0.0-alpha.0", path = "../bundler" } +tauri-bundler = { version = "2.0.0-alpha.1", path = "../bundler" } colored = "2.0" once_cell = "1" serde = { version = "1.0", features = [ "derive" ] } @@ -56,7 +56,7 @@ notify-debouncer-mini = "0.2" shared_child = "1.0" toml_edit = "0.14" json-patch = "0.2" -tauri-utils = { version = "2.0.0-alpha.0", path = "../../core/tauri-utils", features = [ "isolation", "schema", "config-json5", "config-toml" ] } +tauri-utils = { version = "2.0.0-alpha.1", path = "../../core/tauri-utils", features = [ "isolation", "schema", "config-json5", "config-toml" ] } toml = "0.5" jsonschema = "0.16" handlebars = "4.3" diff --git a/tooling/cli/metadata.json b/tooling/cli/metadata.json index 880f2c35e4a..560120ea864 100644 --- a/tooling/cli/metadata.json +++ b/tooling/cli/metadata.json @@ -1,8 +1,8 @@ { "cli.js": { - "version": "2.0.0-alpha.1", + "version": "2.0.0-alpha.2", "node": ">= 10.0.0" }, - "tauri": "2.0.0-alpha.2", - "tauri-build": "2.0.0-alpha.0" + "tauri": "2.0.0-alpha.3", + "tauri-build": "2.0.0-alpha.1" } diff --git a/tooling/cli/node/CHANGELOG.md b/tooling/cli/node/CHANGELOG.md index f2557270239..502d54d21f8 100644 --- a/tooling/cli/node/CHANGELOG.md +++ b/tooling/cli/node/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## \[2.0.0-alpha.2] + +- Fixes `TAURI_*` environment variables for hook scripts on mobile commands. + - [1af9be90](https://www.github.com/tauri-apps/tauri/commit/1af9be904a309138b9f79dc741391000b1652c75) feat(cli): properly fill target for TAURI\_ env vars on mobile ([#6116](https://www.github.com/tauri-apps/tauri/pull/6116)) on 2023-01-23 +- Force colored logs on mobile commands. + - [2c4a0bbd](https://www.github.com/tauri-apps/tauri/commit/2c4a0bbd1fbe15d7500264e6490772397e1917ed) feat(cli): force colored logs on mobile commands ([#5934](https://www.github.com/tauri-apps/tauri/pull/5934)) on 2022-12-28 +- Keep the process alive even when the iOS application is closed. + - [dee9460f](https://www.github.com/tauri-apps/tauri/commit/dee9460f9c9bc92e9c638e7691e616849ac2085b) feat: keep CLI alive when iOS app exits, show logs, closes [#5855](https://www.github.com/tauri-apps/tauri/pull/5855) ([#5902](https://www.github.com/tauri-apps/tauri/pull/5902)) on 2022-12-27 +- Show all application logs on iOS. + - [dee9460f](https://www.github.com/tauri-apps/tauri/commit/dee9460f9c9bc92e9c638e7691e616849ac2085b) feat: keep CLI alive when iOS app exits, show logs, closes [#5855](https://www.github.com/tauri-apps/tauri/pull/5855) ([#5902](https://www.github.com/tauri-apps/tauri/pull/5902)) on 2022-12-27 +- Print log output for all tags on Android development. + - [8cc11149](https://www.github.com/tauri-apps/tauri/commit/8cc111494d74161e489152e52191e1442dd99759) fix(cli): print Android logs for all tags on 2023-01-17 +- Add support to custom and kebab case library names for mobile apps. + - [50f6dd87](https://www.github.com/tauri-apps/tauri/commit/50f6dd87b1ac2c99f8794b055f1acba4ef7d34d3) feat: improvements to support hyphens in crate name ([#5989](https://www.github.com/tauri-apps/tauri/pull/5989)) on 2023-01-06 +- Fix target directory detection when compiling for Android. + - [e873bae0](https://www.github.com/tauri-apps/tauri/commit/e873bae09f0f27517f720a753f51c1dcb903f883) fix(cli): Cargo target dir detection on Android, closes [#5865](https://www.github.com/tauri-apps/tauri/pull/5865) ([#5932](https://www.github.com/tauri-apps/tauri/pull/5932)) on 2022-12-28 + ## \[2.0.0-alpha.1] - Fixes running on device using Xcode 14. diff --git a/tooling/cli/node/package.json b/tooling/cli/node/package.json index 04192567782..38f6caedda3 100644 --- a/tooling/cli/node/package.json +++ b/tooling/cli/node/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/cli", - "version": "2.0.0-alpha.1", + "version": "2.0.0-alpha.2", "description": "Command line interface for building Tauri apps", "funding": { "type": "opencollective", From 7500a0e23ac652250081807f518134a2dd0bb0d8 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 2 Feb 2023 18:22:34 -0300 Subject: [PATCH 131/423] chore(ci): use jbolda/covector again The patches we needed have been released! --- .github/workflows/covector-version-or-publish-next.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/covector-version-or-publish-next.yml b/.github/workflows/covector-version-or-publish-next.yml index 4a835376477..19269c16a8c 100644 --- a/.github/workflows/covector-version-or-publish-next.yml +++ b/.github/workflows/covector-version-or-publish-next.yml @@ -37,7 +37,7 @@ jobs: git config --global user.email "${{ github.event.pusher.email }}" - name: covector version or publish (publish when no change files present) - uses: lucasfernog/covector/packages/action@patches + uses: jbolda/covector/packages/action@covector-v0 id: covector env: NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }} From 84998532d6921caa56f45370a1d98b7fe8b363c5 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Fri, 3 Feb 2023 08:19:49 -0800 Subject: [PATCH 132/423] fix(ci): use vendored openSSL on the CLI (#6191) --- tooling/cli/Cargo.lock | 1 - tooling/cli/Cargo.toml | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index f2274bc6574..1af4e72700b 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -3796,7 +3796,6 @@ dependencies = [ "notify", "notify-debouncer-mini", "once_cell", - "openssl", "os_info", "os_pipe", "regex", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 4c0459ca14a..e1d8b3dbce8 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -39,7 +39,7 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -tauri-mobile = { version = "0.2", default-features = false } +tauri-mobile = { version = "0.2", default-features = false, features = ["openssl-vendored"] } textwrap = { version = "0.11.0", features = [ "term_size" ] } jsonrpsee = { version = "0.16", features = [ "client", "server" ] } thiserror = "1" @@ -92,8 +92,5 @@ winapi = { version = "0.3", features = [ "handleapi", "processenv", "winbase", " [target."cfg(unix)".dependencies] libc = "0.2" -[target."cfg(target_os = \"macos\")".dependencies] -openssl = { version = "0.10", features = [ "vendored" ] } - [profile.release] lto = true From 17f26764c899a150099867d6f553d0bcfe03af8f Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 6 Feb 2023 03:56:00 -0800 Subject: [PATCH 133/423] feat: initial work for Android plugins (#6167) --- core/tauri-build/Cargo.toml | 1 + core/tauri-build/src/lib.rs | 2 + core/tauri-build/src/mobile.rs | 93 + core/tauri-macros/src/command/handler.rs | 3 +- core/tauri-macros/src/command/wrapper.rs | 4 +- core/tauri-runtime-wry/Cargo.toml | 5 +- core/tauri-runtime-wry/src/lib.rs | 63 +- core/tauri-runtime/Cargo.toml | 3 + core/tauri-runtime/src/lib.rs | 22 + core/tauri-runtime/src/window.rs | 26 + core/tauri/Cargo.toml | 1 + core/tauri/src/app.rs | 74 +- core/tauri/src/hooks.rs | 23 +- core/tauri/src/manager.rs | 31 +- core/tauri/src/plugin.rs | 31 +- core/tauri/src/test/mock_runtime.rs | 25 + core/tauri/src/window.rs | 303 +- examples/api/src-tauri/Cargo.lock | 108 +- examples/api/src-tauri/Cargo.toml | 1 + examples/api/src-tauri/src/lib.rs | 1 + .../src-tauri/tauri-plugin-sample/Cargo.lock | 3169 +++++++++++++++++ .../src-tauri/tauri-plugin-sample/Cargo.toml | 10 + .../tauri-plugin-sample/android/.gitignore | 1 + .../android/build.gradle.kts | 45 + .../android/consumer-rules.pro | 0 .../android/proguard-rules.pro | 21 + .../android/settings.gradle | 2 + .../plugin/test/ExampleInstrumentedTest.kt | 24 + .../android/src/main/AndroidManifest.xml | 4 + .../src/main/java/com/plugin/test/Example.kt | 10 + .../java/com/plugin/test/ExamplePlugin.kt | 19 + .../java/com/plugin/test/ExampleUnitTest.kt | 17 + .../src-tauri/tauri-plugin-sample/build.rs | 11 + .../src-tauri/tauri-plugin-sample/src/lib.rs | 21 + examples/api/src-tauri/tauri.conf.json | 3 +- tooling/cli/mobile/android/.gitignore | 1 + tooling/cli/mobile/android/build.gradle.kts | 44 + tooling/cli/mobile/android/consumer-rules.pro | 0 tooling/cli/mobile/android/proguard-rules.pro | 21 + .../java/app/tauri/ExampleInstrumentedTest.kt | 24 + .../android/src/main/AndroidManifest.xml | 4 + .../android/src/main/java/app/tauri/Logger.kt | 81 + .../plugin/InvalidPluginMethodException.kt | 7 + .../src/main/java/app/tauri/plugin/Invoke.kt | 165 + .../src/main/java/app/tauri/plugin/JSArray.kt | 41 + .../main/java/app/tauri/plugin/JSObject.kt | 146 + .../src/main/java/app/tauri/plugin/Plugin.kt | 7 + .../java/app/tauri/plugin/PluginHandle.kt | 37 + .../java/app/tauri/plugin/PluginManager.kt | 49 + .../java/app/tauri/plugin/PluginMethod.kt | 4 + .../java/app/tauri/plugin/PluginMethodData.kt | 14 + .../java/app/tauri/plugin/PluginResult.kt | 63 + .../test/java/app/tauri/ExampleUnitTest.kt | 17 + tooling/cli/src/mobile/android.rs | 10 + tooling/cli/src/mobile/android/build.rs | 14 +- tooling/cli/src/mobile/android/dev.rs | 12 +- tooling/cli/src/mobile/android/project.rs | 99 +- tooling/cli/src/mobile/mod.rs | 21 +- tooling/cli/src/plugin/init.rs | 66 +- .../cli/templates/mobile/android/.gitignore | 3 + .../mobile/android/app/build.gradle.kts | 1 + .../android/app/src/main/MainActivity.kt | 6 +- .../templates/mobile/android/settings.gradle | 3 + .../cli/templates/plugin/android/.gitignore | 1 + .../templates/plugin/android/build.gradle.kts | 45 + .../plugin/android/consumer-rules.pro | 0 .../plugin/android/proguard-rules.pro | 21 + .../templates/plugin/android/settings.gradle | 2 + .../plugin/test/ExampleInstrumentedTest.kt | 24 + .../android/src/main/AndroidManifest.xml | 4 + .../plugin/android/src/main/Example.kt | 10 + .../plugin/android/src/main/ExamplePlugin.kt | 19 + .../java/com/plugin/test/ExampleUnitTest.kt | 17 + 73 files changed, 5069 insertions(+), 211 deletions(-) create mode 100644 core/tauri-build/src/mobile.rs create mode 100644 examples/api/src-tauri/tauri-plugin-sample/Cargo.lock create mode 100644 examples/api/src-tauri/tauri-plugin-sample/Cargo.toml create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/.gitignore create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/proguard-rules.pro create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/settings.gradle create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/src/androidTest/java/com/plugin/test/ExampleInstrumentedTest.kt create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/src/main/AndroidManifest.xml create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/test/Example.kt create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/test/ExamplePlugin.kt create mode 100644 examples/api/src-tauri/tauri-plugin-sample/android/src/test/java/com/plugin/test/ExampleUnitTest.kt create mode 100644 examples/api/src-tauri/tauri-plugin-sample/build.rs create mode 100644 examples/api/src-tauri/tauri-plugin-sample/src/lib.rs create mode 100644 tooling/cli/mobile/android/.gitignore create mode 100644 tooling/cli/mobile/android/build.gradle.kts create mode 100644 tooling/cli/mobile/android/consumer-rules.pro create mode 100644 tooling/cli/mobile/android/proguard-rules.pro create mode 100644 tooling/cli/mobile/android/src/androidTest/java/app/tauri/ExampleInstrumentedTest.kt create mode 100644 tooling/cli/mobile/android/src/main/AndroidManifest.xml create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/Logger.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/InvalidPluginMethodException.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/JSArray.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/JSObject.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginMethod.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginMethodData.kt create mode 100644 tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginResult.kt create mode 100644 tooling/cli/mobile/android/src/test/java/app/tauri/ExampleUnitTest.kt create mode 100644 tooling/cli/templates/plugin/android/.gitignore create mode 100644 tooling/cli/templates/plugin/android/build.gradle.kts create mode 100644 tooling/cli/templates/plugin/android/consumer-rules.pro create mode 100644 tooling/cli/templates/plugin/android/proguard-rules.pro create mode 100644 tooling/cli/templates/plugin/android/settings.gradle create mode 100644 tooling/cli/templates/plugin/android/src/androidTest/java/com/plugin/test/ExampleInstrumentedTest.kt create mode 100644 tooling/cli/templates/plugin/android/src/main/AndroidManifest.xml create mode 100644 tooling/cli/templates/plugin/android/src/main/Example.kt create mode 100644 tooling/cli/templates/plugin/android/src/main/ExamplePlugin.kt create mode 100644 tooling/cli/templates/plugin/android/src/test/java/com/plugin/test/ExampleUnitTest.kt diff --git a/core/tauri-build/Cargo.toml b/core/tauri-build/Cargo.toml index eb000f4e771..540c9dbe315 100644 --- a/core/tauri-build/Cargo.toml +++ b/core/tauri-build/Cargo.toml @@ -25,6 +25,7 @@ cargo_toml = "0.13" serde_json = "1" heck = "0.4" json-patch = "0.2" +walkdir = "2" [target."cfg(windows)".dependencies] winres = "0.1" diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index b3d157a74ff..964929c088f 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -13,6 +13,8 @@ use std::path::{Path, PathBuf}; #[cfg(feature = "codegen")] mod codegen; +/// Mobile build functions. +pub mod mobile; #[cfg(windows)] mod static_vcruntime; diff --git a/core/tauri-build/src/mobile.rs b/core/tauri-build/src/mobile.rs new file mode 100644 index 00000000000..8bcef4e76e5 --- /dev/null +++ b/core/tauri-build/src/mobile.rs @@ -0,0 +1,93 @@ +use std::{ + env::var, + fs, + path::{PathBuf, MAIN_SEPARATOR}, +}; + +use anyhow::Result; + +#[derive(Default)] +pub struct PluginBuilder { + android_path: Option, +} + +impl PluginBuilder { + /// Creates a new builder for mobile plugin functionality. + pub fn new() -> Self { + Self::default() + } + + /// Sets the Android project path. + pub fn android_path>(mut self, android_path: P) -> Self { + self.android_path.replace(android_path.into()); + self + } + + /// Injects the mobile templates in the given path relative to the manifest root. + pub fn run(self) -> Result<()> { + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + if target_os == "android" { + if let Some(path) = self.android_path { + let manifest_dir = var("CARGO_MANIFEST_DIR").map(PathBuf::from).unwrap(); + if let (Ok(out_dir), Ok(gradle_settings_path), Ok(app_build_gradle_path)) = ( + var("TAURI_PLUGIN_OUTPUT_PATH"), + var("TAURI_GRADLE_SETTINGS_PATH"), + var("TAURI_APP_GRADLE_BUILD_PATH"), + ) { + let source = manifest_dir.join(path); + let pkg_name = var("CARGO_PKG_NAME").unwrap(); + + println!("cargo:rerun-if-env-changed=TAURI_PLUGIN_OUTPUT_PATH"); + println!("cargo:rerun-if-env-changed=TAURI_GRADLE_SETTINGS_PATH"); + println!( + "cargo:rerun-if-changed={}{}{}", + out_dir, MAIN_SEPARATOR, pkg_name + ); + println!("cargo:rerun-if-changed={}", gradle_settings_path); + println!("cargo:rerun-if-changed={}", app_build_gradle_path); + + let target = PathBuf::from(out_dir).join(&pkg_name); + let _ = fs::remove_dir_all(&target); + + for entry in walkdir::WalkDir::new(&source) { + let entry = entry?; + let rel_path = entry.path().strip_prefix(&source)?; + let dest_path = target.join(rel_path); + if entry.file_type().is_dir() { + fs::create_dir(dest_path)?; + } else { + fs::copy(entry.path(), dest_path)?; + } + } + + let gradle_settings = fs::read_to_string(&gradle_settings_path)?; + let include = format!( + "include ':{pkg_name}' +project(':{pkg_name}').projectDir = new File('./tauri-plugins/{pkg_name}')" + ); + if !gradle_settings.contains(&include) { + fs::write( + &gradle_settings_path, + &format!("{gradle_settings}\n{include}"), + )?; + } + + let app_build_gradle = fs::read_to_string(&app_build_gradle_path)?; + let implementation = format!(r#"implementation(project(":{pkg_name}"))"#); + let target_implementation = r#"implementation(project(":tauri-android"))"#; + if !app_build_gradle.contains(&implementation) { + fs::write( + &app_build_gradle_path, + app_build_gradle.replace( + target_implementation, + &format!("{target_implementation}\n {implementation}"), + ), + )? + } + } + } + } + + Ok(()) + } +} diff --git a/core/tauri-macros/src/command/handler.rs b/core/tauri-macros/src/command/handler.rs index a170133902d..89ea708678d 100644 --- a/core/tauri-macros/src/command/handler.rs +++ b/core/tauri-macros/src/command/handler.rs @@ -59,7 +59,8 @@ impl From for proc_macro::TokenStream { match #cmd { #(stringify!(#commands) => #wrappers!(#paths, #invoke),)* _ => { - #invoke.resolver.reject(format!("command {} not found", #cmd)) + #invoke.resolver.reject(format!("command {} not found", #cmd)); + return false; }, } }) diff --git a/core/tauri-macros/src/command/wrapper.rs b/core/tauri-macros/src/command/wrapper.rs index 6dfa6db3a76..2c7b4f953ca 100644 --- a/core/tauri-macros/src/command/wrapper.rs +++ b/core/tauri-macros/src/command/wrapper.rs @@ -159,6 +159,7 @@ fn body_async(function: &ItemFn, invoke: &Invoke, case: ArgumentCase) -> syn::Re let kind = (&result).async_kind(); kind.future(result).await }); + return true; } }) } @@ -179,13 +180,14 @@ fn body_blocking( // the body of a `match` to early return any argument that wasn't successful in parsing. let match_body = quote!({ Ok(arg) => arg, - Err(err) => return #resolver.invoke_error(err), + Err(err) => {#resolver.invoke_error(err); return true }, }); Ok(quote! { let result = $path(#(match #args #match_body),*); let kind = (&result).blocking_kind(); kind.block(result, #resolver); + return true; }) } diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 6ab46fe0724..eb0e59a4c39 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -13,7 +13,7 @@ exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" [dependencies] -wry = { version = "0.26.0", default-features = false, features = [ "file-drop", "protocol" ] } +wry = { git = "https://github.com/tauri-apps/wry", branch = "dev", default-features = false, features = [ "file-drop", "protocol" ] } tauri-runtime = { version = "0.13.0-alpha.1", path = "../tauri-runtime" } tauri-utils = { version = "2.0.0-alpha.1", path = "../tauri-utils" } uuid = { version = "1", features = [ "v4" ] } @@ -35,6 +35,9 @@ percent-encoding = "2.1" [target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies] cocoa = "0.24" +[target."cfg(target_os = \"android\")".dependencies] +jni = "0.20" + [features] dox = [ "wry/dox" ] devtools = [ "wry/devtools", "tauri-runtime/devtools" ] diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 81b0cb4015f..2c6df681ce0 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -66,9 +66,13 @@ use wry::{ pub use wry; pub use wry::application::window::{Window, WindowBuilder as WryWindowBuilder, WindowId}; - #[cfg(windows)] use wry::webview::WebviewExtWindows; +#[cfg(target_os = "android")] +use wry::webview::{ + prelude::{dispatch, find_class}, + WebViewBuilderExtAndroid, WebviewExtAndroid, +}; #[cfg(target_os = "macos")] use tauri_runtime::{menu::NativeImage, ActivationPolicy}; @@ -1161,16 +1165,6 @@ pub struct WryDispatcher { #[allow(clippy::non_send_fields_in_send_ty)] unsafe impl Sync for WryDispatcher {} -impl WryDispatcher { - #[cfg(any(desktop, target_os = "android"))] - pub fn with_webview(&self, f: F) -> Result<()> { - send_user_message( - &self.context, - Message::Window(self.window_id, WindowMessage::WithWebview(Box::new(f))), - ) - } -} - impl Dispatch for WryDispatcher { type Runtime = Wry; type WindowBuilder = WindowBuilderWrapper; @@ -1197,6 +1191,17 @@ impl Dispatch for WryDispatcher { id } + #[cfg(any(desktop, target_os = "android"))] + fn with_webview) + Send + 'static>(&self, f: F) -> Result<()> { + send_user_message( + &self.context, + Message::Window( + self.window_id, + WindowMessage::WithWebview(Box::new(move |webview| f(Box::new(webview)))), + ), + ) + } + #[cfg(any(debug_assertions, feature = "devtools"))] fn open_devtools(&self) { let _ = send_user_message( @@ -1814,6 +1819,26 @@ impl RuntimeHandle for WryHandle { Message::Application(ApplicationMessage::Hide), ) } + + #[cfg(target_os = "android")] + fn find_class<'a>( + &'a self, + env: jni::JNIEnv<'a>, + activity: jni::objects::JObject<'a>, + name: impl Into, + ) -> std::result::Result, jni::errors::Error> { + find_class(env, activity, name.into()) + } + + #[cfg(target_os = "android")] + fn run_on_android_context(&self, f: F) + where + F: FnOnce(jni::JNIEnv<'_>, jni::objects::JObject<'_>, jni::objects::JObject<'_>) + + Send + + 'static, + { + dispatch(f) + } } impl Wry { @@ -2290,7 +2315,6 @@ fn handle_user_message( } #[cfg(target_os = "android")] { - use wry::webview::WebviewExtAndroid; f(w.handle()) } } @@ -2957,6 +2981,8 @@ fn create_webview( url, menu_ids, js_event_listeners, + #[cfg(target_os = "android")] + on_webview_created, .. } = pending; let webview_id_map = context.webview_id_map.clone(); @@ -3066,6 +3092,19 @@ fn create_webview( webview_builder = webview_builder.with_devtools(true); } + #[cfg(target_os = "android")] + { + if let Some(on_webview_created) = on_webview_created { + webview_builder = webview_builder.on_webview_created(move |ctx| { + on_webview_created(tauri_runtime::window::CreationContext { + env: ctx.env, + activity: ctx.activity, + webview: ctx.webview, + }) + }); + } + } + let webview = webview_builder .with_web_context(web_context) .build() diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml index 76052f276d8..27eace6c81d 100644 --- a/core/tauri-runtime/Cargo.toml +++ b/core/tauri-runtime/Cargo.toml @@ -43,6 +43,9 @@ webview2-com = "0.19.1" [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] gtk = { version = "0.16", features = [ "v3_24" ] } +[target."cfg(target_os = \"android\")".dependencies] +jni = "0.20" + [features] devtools = [ ] system-tray = [ ] diff --git a/core/tauri-runtime/src/lib.rs b/core/tauri-runtime/src/lib.rs index 4dbde5901e6..6cef8289a02 100644 --- a/core/tauri-runtime/src/lib.rs +++ b/core/tauri-runtime/src/lib.rs @@ -362,6 +362,25 @@ pub trait RuntimeHandle: Debug + Clone + Send + Sync + Sized + 'st #[cfg(target_os = "macos")] #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))] fn hide(&self) -> Result<()>; + + /// Finds an Android class in the project scope. + #[cfg(target_os = "android")] + fn find_class<'a>( + &'a self, + env: jni::JNIEnv<'a>, + activity: jni::objects::JObject<'a>, + name: impl Into, + ) -> std::result::Result, jni::errors::Error>; + + /// Dispatch a closure to run on the Android context. + /// + /// The closure takes the JNI env, the Android activity instance and the possibly null webview. + #[cfg(target_os = "android")] + fn run_on_android_context(&self, f: F) + where + F: FnOnce(jni::JNIEnv<'_>, jni::objects::JObject<'_>, jni::objects::JObject<'_>) + + Send + + 'static; } /// A global shortcut manager. @@ -486,6 +505,9 @@ pub trait Dispatch: Debug + Clone + Send + Sync + Sized + 'static /// Registers a window event handler. fn on_menu_event(&self, f: F) -> Uuid; + #[cfg(any(desktop, target_os = "android"))] + fn with_webview) + Send + 'static>(&self, f: F) -> Result<()>; + /// Open the web inspector which is usually called devtools. #[cfg(any(debug_assertions, feature = "devtools"))] fn open_devtools(&self); diff --git a/core/tauri-runtime/src/window.rs b/core/tauri-runtime/src/window.rs index c07408b5479..2f59312807f 100644 --- a/core/tauri-runtime/src/window.rs +++ b/core/tauri-runtime/src/window.rs @@ -208,6 +208,13 @@ impl Default for CursorIcon { } } +#[cfg(target_os = "android")] +pub struct CreationContext<'a> { + pub env: jni::JNIEnv<'a>, + pub activity: jni::objects::JObject<'a>, + pub webview: jni::objects::JObject<'a>, +} + /// A webview window that has yet to be built. pub struct PendingWindow> { /// The label that the window will be named. @@ -232,6 +239,10 @@ pub struct PendingWindow> { /// A HashMap mapping JS event names with associated listener ids. pub js_event_listeners: Arc>>>, + + #[cfg(target_os = "android")] + pub on_webview_created: + Option) -> Result<(), jni::errors::Error> + Send>>, } pub fn is_label_valid(label: &str) -> bool { @@ -271,6 +282,8 @@ impl> PendingWindow { url: "tauri://localhost".to_string(), menu_ids: Arc::new(Mutex::new(menu_ids)), js_event_listeners: Default::default(), + #[cfg(target_os = "android")] + on_webview_created: None, }) } } @@ -300,6 +313,8 @@ impl> PendingWindow { url: "tauri://localhost".to_string(), menu_ids: Arc::new(Mutex::new(menu_ids)), js_event_listeners: Default::default(), + #[cfg(target_os = "android")] + on_webview_created: None, }) } } @@ -326,6 +341,17 @@ impl> PendingWindow { .uri_scheme_protocols .insert(uri_scheme, Box::new(move |data| (protocol)(data))); } + + #[cfg(target_os = "android")] + pub fn on_webview_created< + F: Fn(CreationContext<'_>) -> Result<(), jni::errors::Error> + Send + 'static, + >( + mut self, + f: F, + ) -> Self { + self.on_webview_created.replace(Box::new(f)); + self + } } /// Key for a JS event listener. diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 4d9ef454343..bb7a40a298f 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -111,6 +111,7 @@ win7-notifications = { version = "0.3.1", optional = true } [target."cfg(target_os = \"android\")".dependencies] paste = "1.0" log = "0.4" +jni = "0.20" [target."cfg(target_os = \"ios\")".dependencies] log = "0.4" diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 22463efa978..eb217182b73 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -366,7 +366,7 @@ impl AssetResolver { #[default_runtime(crate::Wry, wry)] #[derive(Debug)] pub struct AppHandle { - runtime_handle: R::Handle, + pub(crate) runtime_handle: R::Handle, pub(crate) manager: WindowManager, #[cfg(all(desktop, feature = "global-shortcut"))] global_shortcut_manager: R::GlobalShortcutManager, @@ -383,6 +383,74 @@ impl AppHandle { pub(crate) fn create_proxy(&self) -> R::EventLoopProxy { self.runtime_handle.create_proxy() } + + /// Initializes an Android plugin. + #[cfg(target_os = "android")] + pub fn initialize_android_plugin( + &self, + plugin_name: &'static str, + plugin_identifier: &str, + class_name: &str, + ) -> crate::Result<()> { + use jni::{errors::Error as JniError, objects::JObject, JNIEnv}; + + fn initialize_plugin<'a, R: Runtime>( + env: JNIEnv<'a>, + activity: JObject<'a>, + webview: JObject<'a>, + runtime_handle: &R::Handle, + plugin_name: &'static str, + plugin_class: String, + ) -> Result<(), JniError> { + let plugin_manager = env + .call_method( + activity, + "getPluginManager", + format!("()Lapp/tauri/plugin/PluginManager;"), + &[], + )? + .l()?; + + // instantiate plugin + let plugin_class = runtime_handle.find_class(env, activity, plugin_class)?; + let plugin = env.new_object( + plugin_class, + "(Landroid/app/Activity;)V", + &[activity.into()], + )?; + + // load plugin + env.call_method( + plugin_manager, + "load", + format!("(Landroid/webkit/WebView;Ljava/lang/String;Lapp/tauri/plugin/Plugin;)V"), + &[ + webview.into(), + env.new_string(plugin_name)?.into(), + plugin.into(), + ], + )?; + + Ok(()) + } + + let plugin_class = format!("{}/{}", plugin_identifier.replace(".", "/"), class_name); + let runtime_handle = self.runtime_handle.clone(); + self + .runtime_handle + .run_on_android_context(move |env, activity, webview| { + let _ = initialize_plugin::( + env, + activity, + webview, + &runtime_handle, + plugin_name, + plugin_class, + ); + }); + + Ok(()) + } } /// APIs specific to the wry runtime. @@ -1050,7 +1118,7 @@ impl Builder { #[cfg(any(windows, target_os = "linux"))] runtime_any_thread: false, setup: Box::new(|_| Ok(())), - invoke_handler: Box::new(|_| ()), + invoke_handler: Box::new(|_| false), invoke_responder: Arc::new(window_invoke_responder), invoke_initialization_script: "Object.defineProperty(window, '__TAURI_POST_MESSAGE__', { value: (message) => window.ipc.postMessage(JSON.stringify(message)) })".into(), @@ -1102,7 +1170,7 @@ impl Builder { #[must_use] pub fn invoke_handler(mut self, invoke_handler: F) -> Self where - F: Fn(Invoke) + Send + Sync + 'static, + F: Fn(Invoke) -> bool + Send + Sync + 'static, { self.invoke_handler = Box::new(invoke_handler); self diff --git a/core/tauri/src/hooks.rs b/core/tauri/src/hooks.rs index bd2337d69c7..df80296ec8a 100644 --- a/core/tauri/src/hooks.rs +++ b/core/tauri/src/hooks.rs @@ -19,7 +19,7 @@ pub type SetupHook = Box) -> Result<(), Box> + Send>; /// A closure that is run every time Tauri receives a message it doesn't explicitly handle. -pub type InvokeHandler = dyn Fn(Invoke) + Send + Sync + 'static; +pub type InvokeHandler = dyn Fn(Invoke) -> bool + Send + Sync + 'static; /// A closure that is responsible for respond a JS message. pub type InvokeResponder = @@ -167,6 +167,16 @@ pub struct InvokeResolver { pub(crate) error: CallbackFn, } +impl Clone for InvokeResolver { + fn clone(&self) -> Self { + Self { + window: self.window.clone(), + callback: self.callback, + error: self.error, + } + } +} + impl InvokeResolver { pub(crate) fn new(window: Window, callback: CallbackFn, error: CallbackFn) -> Self { Self { @@ -293,6 +303,17 @@ pub struct InvokeMessage { pub(crate) payload: JsonValue, } +impl Clone for InvokeMessage { + fn clone(&self) -> Self { + Self { + window: self.window.clone(), + state: self.state.clone(), + command: self.command.clone(), + payload: self.payload.clone(), + } + } +} + impl InvokeMessage { /// Create an new [`InvokeMessage`] from a payload send to a window. pub(crate) fn new( diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 48cccf053a1..2d9deb672e5 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -1151,7 +1151,7 @@ mod test { let manager: WindowManager = WindowManager::with_handlers( context, PluginStore::default(), - Box::new(|_| ()), + Box::new(|_| false), Box::new(|_, _| ()), Default::default(), StateManager::new(), @@ -1183,13 +1183,13 @@ impl WindowManager { .on_page_load(window, payload); } - pub fn extend_api(&self, invoke: Invoke) { + pub fn extend_api(&self, plugin: &str, invoke: Invoke) -> bool { self .inner .plugins .lock() .expect("poisoned plugin store") - .extend_api(invoke); + .extend_api(plugin, invoke) } pub fn initialize_plugins(&self, app: &AppHandle) -> crate::Result<()> { @@ -1285,6 +1285,31 @@ impl WindowManager { } } + #[cfg(target_os = "android")] + { + pending = pending.on_webview_created(move |ctx| { + let plugin_manager = ctx + .env + .call_method( + ctx.activity, + "getPluginManager", + format!("()Lapp/tauri/plugin/PluginManager;"), + &[], + )? + .l()?; + + // tell the manager the webview is ready + ctx.env.call_method( + plugin_manager, + "onWebViewCreated", + format!("(Landroid/webkit/WebView;)V"), + &[ctx.webview.into()], + )?; + + Ok(()) + }); + } + if is_local { let label = pending.label.clone(); pending = self.prepare_pending_window( diff --git a/core/tauri/src/plugin.rs b/core/tauri/src/plugin.rs index 6c42d9927d9..fe46334981e 100644 --- a/core/tauri/src/plugin.rs +++ b/core/tauri/src/plugin.rs @@ -54,7 +54,9 @@ pub trait Plugin: Send { /// Extend commands to [`crate::Builder::invoke_handler`]. #[allow(unused_variables)] - fn extend_api(&mut self, invoke: Invoke) {} + fn extend_api(&mut self, invoke: Invoke) -> bool { + false + } } type SetupHook = dyn FnOnce(&AppHandle) -> Result<()> + Send; @@ -156,7 +158,7 @@ impl Builder { setup: None, setup_with_config: None, js_init_script: None, - invoke_handler: Box::new(|_| ()), + invoke_handler: Box::new(|_| false), on_page_load: Box::new(|_, _| ()), on_webview_ready: Box::new(|_| ()), on_event: Box::new(|_, _| ()), @@ -190,7 +192,7 @@ impl Builder { #[must_use] pub fn invoke_handler(mut self, invoke_handler: F) -> Self where - F: Fn(Invoke) + Send + Sync + 'static, + F: Fn(Invoke) -> bool + Send + Sync + 'static, { self.invoke_handler = Box::new(invoke_handler); self @@ -482,7 +484,7 @@ impl Plugin for TauriPlugin { (self.on_event)(app, event) } - fn extend_api(&mut self, invoke: Invoke) { + fn extend_api(&mut self, invoke: Invoke) -> bool { (self.invoke_handler)(invoke) } } @@ -573,20 +575,15 @@ impl PluginStore { .for_each(|plugin| plugin.on_event(app, event)) } - pub(crate) fn extend_api(&mut self, mut invoke: Invoke) { - let command = invoke.message.command.replace("plugin:", ""); - let mut tokens = command.split('|'); - // safe to unwrap: split always has a least one item - let target = tokens.next().unwrap(); - - if let Some(plugin) = self.store.get_mut(target) { - invoke.message.command = tokens - .next() - .map(|c| c.to_string()) - .unwrap_or_else(String::new); - plugin.extend_api(invoke); + /// Runs the plugin `extend_api` hook if it exists. Returns whether the invoke message was handled or not. + /// + /// The message is not handled when the plugin exists **and** the command does not. + pub(crate) fn extend_api(&mut self, plugin: &str, invoke: Invoke) -> bool { + if let Some(plugin) = self.store.get_mut(plugin) { + plugin.extend_api(invoke) } else { - invoke.resolver.reject(format!("plugin {target} not found")); + invoke.resolver.reject(format!("plugin {plugin} not found")); + true } } } diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index 166766a6fc1..c1a046c8244 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -106,6 +106,26 @@ impl RuntimeHandle for MockRuntimeHandle { fn hide(&self) -> Result<()> { Ok(()) } + + #[cfg(target_os = "android")] + fn find_class<'a>( + &'a self, + env: jni::JNIEnv<'a>, + activity: jni::objects::JObject<'a>, + name: impl Into, + ) -> std::result::Result, jni::errors::Error> { + todo!() + } + + #[cfg(target_os = "android")] + fn run_on_android_context(&self, f: F) + where + F: FnOnce(jni::JNIEnv<'_>, jni::objects::JObject<'_>, jni::objects::JObject<'_>) + + Send + + 'static, + { + todo!() + } } #[derive(Debug, Clone)] @@ -318,6 +338,11 @@ impl Dispatch for MockDispatcher { Uuid::new_v4() } + #[cfg(any(desktop, target_os = "android"))] + fn with_webview) + Send + 'static>(&self, f: F) -> Result<()> { + Ok(()) + } + #[cfg(any(debug_assertions, feature = "devtools"))] fn open_devtools(&self) {} diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index 156bfd21f13..5c9bddfe7c7 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -732,77 +732,6 @@ impl PlatformWebview { } } -/// APIs specific to the wry runtime. -#[cfg(feature = "wry")] -impl Window { - /// Executes a closure, providing it with the webview handle that is specific to the current platform. - /// - /// The closure is executed on the main thread. - /// - /// # Examples - /// - /// ```rust,no_run - /// #[cfg(target_os = "macos")] - /// #[macro_use] - /// extern crate objc; - /// use tauri::Manager; - /// - /// fn main() { - /// tauri::Builder::default() - /// .setup(|app| { - /// let main_window = app.get_window("main").unwrap(); - /// main_window.with_webview(|webview| { - /// #[cfg(target_os = "linux")] - /// { - /// // see https://docs.rs/webkit2gtk/0.18.2/webkit2gtk/struct.WebView.html - /// // and https://docs.rs/webkit2gtk/0.18.2/webkit2gtk/trait.WebViewExt.html - /// use webkit2gtk::traits::WebViewExt; - /// webview.inner().set_zoom_level(4.); - /// } - /// - /// #[cfg(windows)] - /// unsafe { - /// // see https://docs.rs/webview2-com/0.19.1/webview2_com/Microsoft/Web/WebView2/Win32/struct.ICoreWebView2Controller.html - /// webview.controller().SetZoomFactor(4.).unwrap(); - /// } - /// - /// #[cfg(target_os = "macos")] - /// unsafe { - /// let () = msg_send![webview.inner(), setPageZoom: 4.]; - /// let () = msg_send![webview.controller(), removeAllUserScripts]; - /// let bg_color: cocoa::base::id = msg_send![class!(NSColor), colorWithDeviceRed:0.5 green:0.2 blue:0.4 alpha:1.]; - /// let () = msg_send![webview.ns_window(), setBackgroundColor: bg_color]; - /// } - /// - /// #[cfg(target_os = "android")] - /// { - /// use jni::objects::JValue; - /// webview.jni_handle().exec(|env, _, webview| { - /// env.call_method(webview, "zoomBy", "(F)V", &[JValue::Float(4.)]).unwrap(); - /// }) - /// } - /// }); - /// Ok(()) - /// }); - /// } - /// ``` - #[cfg(any(desktop, target_os = "android"))] - #[cfg_attr( - doc_cfg, - doc(cfg(all(feature = "wry", any(desktop, target_os = "android")))) - )] - pub fn with_webview( - &self, - f: F, - ) -> crate::Result<()> { - self - .window - .dispatcher - .with_webview(|w| f(PlatformWebview(w))) - .map_err(Into::into) - } -} - /// Base window functions. impl Window { /// Create a new window that is attached to the manager. @@ -873,6 +802,73 @@ impl Window { f(MenuEvent { menu_item_id: id }) }) } + + /// Executes a closure, providing it with the webview handle that is specific to the current platform. + /// + /// The closure is executed on the main thread. + /// + /// # Examples + /// + /// ```rust,no_run + /// #[cfg(target_os = "macos")] + /// #[macro_use] + /// extern crate objc; + /// use tauri::Manager; + /// + /// fn main() { + /// tauri::Builder::default() + /// .setup(|app| { + /// let main_window = app.get_window("main").unwrap(); + /// main_window.with_webview(|webview| { + /// #[cfg(target_os = "linux")] + /// { + /// // see https://docs.rs/webkit2gtk/0.18.2/webkit2gtk/struct.WebView.html + /// // and https://docs.rs/webkit2gtk/0.18.2/webkit2gtk/trait.WebViewExt.html + /// use webkit2gtk::traits::WebViewExt; + /// webview.inner().set_zoom_level(4.); + /// } + /// + /// #[cfg(windows)] + /// unsafe { + /// // see https://docs.rs/webview2-com/0.19.1/webview2_com/Microsoft/Web/WebView2/Win32/struct.ICoreWebView2Controller.html + /// webview.controller().SetZoomFactor(4.).unwrap(); + /// } + /// + /// #[cfg(target_os = "macos")] + /// unsafe { + /// let () = msg_send![webview.inner(), setPageZoom: 4.]; + /// let () = msg_send![webview.controller(), removeAllUserScripts]; + /// let bg_color: cocoa::base::id = msg_send![class!(NSColor), colorWithDeviceRed:0.5 green:0.2 blue:0.4 alpha:1.]; + /// let () = msg_send![webview.ns_window(), setBackgroundColor: bg_color]; + /// } + /// + /// #[cfg(target_os = "android")] + /// { + /// use jni::objects::JValue; + /// webview.jni_handle().exec(|env, _, webview| { + /// env.call_method(webview, "zoomBy", "(F)V", &[JValue::Float(4.)]).unwrap(); + /// }) + /// } + /// }); + /// Ok(()) + /// }); + /// } + /// ``` + #[cfg(all(feature = "wry", any(desktop, target_os = "android")))] + #[cfg_attr( + doc_cfg, + doc(all(feature = "wry", any(desktop, target_os = "android"))) + )] + pub fn with_webview( + &self, + f: F, + ) -> crate::Result<()> { + self + .window + .dispatcher + .with_webview(|w| f(PlatformWebview(*w.downcast().unwrap()))) + .map_err(Into::into) + } } /// Window getters. @@ -1303,9 +1299,10 @@ impl Window { payload.cmd.to_string(), payload.inner, ); - let resolver = InvokeResolver::new(self, payload.callback, payload.error); + #[allow(clippy::redundant_clone)] + let resolver = InvokeResolver::new(self.clone(), payload.callback, payload.error); - let invoke = Invoke { message, resolver }; + let mut invoke = Invoke { message, resolver }; if let Some(module) = &payload.tauri_module { crate::endpoints::handle( module.to_string(), @@ -1314,7 +1311,165 @@ impl Window { manager.package_info(), ); } else if payload.cmd.starts_with("plugin:") { - manager.extend_api(invoke); + let command = invoke.message.command.replace("plugin:", ""); + let mut tokens = command.split('|'); + // safe to unwrap: split always has a least one item + let plugin = tokens.next().unwrap(); + invoke.message.command = tokens + .next() + .map(|c| c.to_string()) + .unwrap_or_else(String::new); + + #[cfg(target_os = "android")] + let (message, resolver) = (invoke.message.clone(), invoke.resolver.clone()); + + #[allow(unused_variables)] + let handled = manager.extend_api(plugin, invoke); + + #[cfg(target_os = "android")] + { + if !handled { + let runtime_handle = self.app_handle.runtime_handle.clone(); + let plugin = plugin.to_string(); + self.with_webview(move |webview| { + webview.jni_handle().exec(move |env, activity, webview| { + use crate::api::ipc::CallbackFn; + use jni::{ + errors::Error as JniError, + objects::{JObject, JValue}, + JNIEnv, + }; + use serde_json::Value as JsonValue; + + fn json_to_java<'a, R: Runtime>( + env: JNIEnv<'a>, + activity: JObject<'a>, + runtime_handle: &R::Handle, + json: JsonValue, + ) -> Result<(&'static str, JValue<'a>), JniError> { + let (class, v) = match json { + JsonValue::Null => ("Ljava/lang/Object;", JObject::null().into()), + JsonValue::Bool(val) => ("Z", val.into()), + JsonValue::Number(val) => { + if let Some(v) = val.as_i64() { + ("J", v.into()) + } else if let Some(v) = val.as_f64() { + ("D", v.into()) + } else { + ("Ljava/lang/Object;", JObject::null().into()) + } + } + JsonValue::String(val) => ( + "Ljava/lang/Object;", + JObject::from(env.new_string(&val)?).into(), + ), + JsonValue::Array(val) => { + let js_array_class = + runtime_handle.find_class(env, activity, "app/tauri/plugin/JSArray")?; + let data = env.new_object(js_array_class, "()V", &[])?; + + for v in val { + let (signature, val) = + json_to_java::(env, activity, runtime_handle, v)?; + env.call_method( + data, + "put", + format!("({signature})Lorg/json/JSONArray;"), + &[val], + )?; + } + + ("Ljava/lang/Object;", data.into()) + } + JsonValue::Object(val) => { + let js_object_class = + runtime_handle.find_class(env, activity, "app/tauri/plugin/JSObject")?; + let data = env.new_object(js_object_class, "()V", &[])?; + + for (key, value) in val { + let (signature, val) = + json_to_java::(env, activity, runtime_handle, value)?; + env.call_method( + data, + "put", + format!("(Ljava/lang/String;{signature})Lapp/tauri/plugin/JSObject;"), + &[env.new_string(&key)?.into(), val], + )?; + } + + ("Ljava/lang/Object;", data.into()) + } + }; + Ok((class, v)) + } + + fn to_jsobject<'a, R: Runtime>( + env: JNIEnv<'a>, + activity: JObject<'a>, + runtime_handle: &R::Handle, + json: JsonValue, + ) -> Result, JniError> { + if let JsonValue::Object(_) = &json { + json_to_java::(env, activity, runtime_handle, json) + .map(|(_class, data)| data) + } else { + Ok(JObject::null().into()) + } + } + + fn handle_message( + plugin: &str, + runtime_handle: &R::Handle, + message: InvokeMessage, + callback: CallbackFn, + error: CallbackFn, + env: JNIEnv<'_>, + activity: JObject<'_>, + webview: JObject<'_>, + ) -> Result<(), JniError> { + let data = to_jsobject::(env, activity, runtime_handle, message.payload)?; + let plugin_manager = env + .call_method( + activity, + "getPluginManager", + "()Lapp/tauri/plugin/PluginManager;", + &[], + )? + .l()?; + + env.call_method( + plugin_manager, + "postMessage", + "(Landroid/webkit/WebView;Ljava/lang/String;Ljava/lang/String;Lapp/tauri/plugin/JSObject;JJ)V", + &[ + webview.into(), + env.new_string(plugin)?.into(), + env.new_string(&message.command)?.into(), + data.into(), + (callback.0 as i64).into(), + (error.0 as i64).into(), + ], + )?; + + Ok(()) + } + + if let Err(e) = handle_message( + &plugin, + &runtime_handle, + message, + resolver.callback, + resolver.error, + env, + activity, + webview, + ) { + resolver.reject(format!("failed to reach Android layer: {e}")); + } + }); + })?; + } + } } else { manager.run_invoke_handler(invoke); } diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index c54d484e6c5..47038bde574 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -101,6 +101,7 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-log", + "tauri-plugin-sample", "tiny_http", "window-shadows", "window-vibrancy", @@ -2465,8 +2466,9 @@ dependencies = [ [[package]] name = "rfd" -version = "0.10.0" -source = "git+https://github.com/PolyMeilex/rfd#c2ea907bbcd055239d615cd8029a86089494bddc" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2583255eadc4e0d816cb7648371cc91e49cbac85b0748b6ab417093bff4040" dependencies = [ "block", "dispatch", @@ -2482,7 +2484,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.43.0", + "windows 0.44.0", ] [[package]] @@ -2956,7 +2958,7 @@ dependencies = [ [[package]] name = "tauri" -version = "2.0.0-alpha.2" +version = "2.0.0-alpha.3" dependencies = [ "anyhow", "attohttpc", @@ -2977,6 +2979,7 @@ dependencies = [ "ico", "ignore", "infer 0.9.0", + "jni", "libc", "log", "minisign-verify", @@ -3021,7 +3024,7 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" dependencies = [ "anyhow", "cargo_toml", @@ -3032,12 +3035,13 @@ dependencies = [ "serde_json", "tauri-codegen", "tauri-utils", + "walkdir", "winres", ] [[package]] name = "tauri-codegen" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" dependencies = [ "base64", "brotli", @@ -3062,7 +3066,7 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" dependencies = [ "heck 0.4.0", "proc-macro2", @@ -3089,13 +3093,22 @@ dependencies = [ "time", ] +[[package]] +name = "tauri-plugin-sample" +version = "0.1.0" +dependencies = [ + "tauri", + "tauri-build", +] + [[package]] name = "tauri-runtime" -version = "0.13.0-alpha.0" +version = "0.13.0-alpha.1" dependencies = [ "gtk", "http", "http-range", + "jni", "rand 0.8.5", "raw-window-handle", "serde", @@ -3109,10 +3122,11 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "0.13.0-alpha.0" +version = "0.13.0-alpha.1" dependencies = [ "cocoa", "gtk", + "jni", "percent-encoding", "rand 0.8.5", "raw-window-handle", @@ -3127,7 +3141,7 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.0.0-alpha.0" +version = "2.0.0-alpha.1" dependencies = [ "aes-gcm", "brotli", @@ -3654,9 +3668,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50668f506786de0e8c2542a07c4720effbd6e58be11746f2b451875d048d7f56" +checksum = "d8eea819afe15eb8dcdff4f19d8bfda540bae84d874c10e6f4b8faf2d6704bd1" dependencies = [ "bitflags", "cairo-rs", @@ -3828,17 +3842,11 @@ dependencies = [ [[package]] name = "windows" -version = "0.43.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" +checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows-targets", ] [[package]] @@ -3887,12 +3895,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc 0.42.1", ] [[package]] @@ -3903,9 +3926,9 @@ checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" @@ -3927,9 +3950,9 @@ checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" @@ -3951,9 +3974,9 @@ checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" @@ -3975,9 +3998,9 @@ checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" @@ -3999,15 +4022,15 @@ checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" @@ -4029,9 +4052,9 @@ checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -4053,9 +4076,8 @@ dependencies = [ [[package]] name = "wry" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164aae72fb3759aa178ebb274c572728a871abce011e53821e9468335314fe00" +version = "0.26.0" +source = "git+https://github.com/tauri-apps/wry?branch=dev#a9e186cab4456d7ac2c265e61e71b345f7d269c4" dependencies = [ "base64", "block", diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index eecf650f3e7..768b4e1433e 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -18,6 +18,7 @@ serde = { version = "1.0", features = [ "derive" ] } tiny_http = "0.11" log = "0.4" tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "next" } +tauri-plugin-sample = { path = "./tauri-plugin-sample/" } [patch.'https://github.com/tauri-apps/tauri'] tauri = { path = "../../../core/tauri" } diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index b44c207ff6c..173b37cf0f3 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -65,6 +65,7 @@ impl AppBuilder { .level(log::LevelFilter::Info) .build(), ) + .plugin(tauri_plugin_sample::init()) .setup(move |app| { if let Some(setup) = setup { (setup)(app)?; diff --git a/examples/api/src-tauri/tauri-plugin-sample/Cargo.lock b/examples/api/src-tauri/tauri-plugin-sample/Cargo.lock new file mode 100644 index 00000000000..9dd0abc8186 --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/Cargo.lock @@ -0,0 +1,3169 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ec2333c185d826313162cee39d3fcc6a84ba08114a839bebf53b961e7e75773" +dependencies = [ + "android_log-sys", + "env_logger", + "lazy_static", + "log", +] + +[[package]] +name = "anyhow" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" + +[[package]] +name = "atk" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" +dependencies = [ + "atk-sys", + "bitflags", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.0.3", +] + +[[package]] +name = "attohttpc" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b85f766c20e6ae766956f7a2fcc4e0931e79a7e1f48b29132b5d647021114914" +dependencies = [ + "flate2", + "http", + "log", + "serde", + "serde_json", + "serde_urlencoded", + "url", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cairo-rs" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +dependencies = [ + "glib-sys", + "libc", + "system-deps 6.0.3", +] + +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f89d248799e3f15f91b70917f65381062a01bb8e222700ea0e5a7ff9785f9c" +dependencies = [ + "byteorder", + "uuid 0.8.2", +] + +[[package]] +name = "cfg-expr" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-expr" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cocoa" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "dtoa-short" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03329ae10e79ede66c9ce4dc930aa8599043b0743008548680f25b91502d6" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "field-offset" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" +dependencies = [ + "memoffset", + "rustc_version 0.3.3", +] + +[[package]] +name = "filetime" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-executor" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" +dependencies = [ + "bitflags", + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.0.3", +] + +[[package]] +name = "gdk-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps 6.0.3", +] + +[[package]] +name = "gdkx11-sys" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps 6.0.3", + "x11", +] + +[[package]] +name = "generator" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266041a359dfa931b370ef684cceb84b166beb14f7f0421f4a6a3d0c446d12e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gio" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-io", + "gio-sys", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.0.3", + "winapi", +] + +[[package]] +name = "glib" +version = "0.15.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" +dependencies = [ + "anyhow", + "heck 0.4.0", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +dependencies = [ + "libc", + "system-deps 6.0.3", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "gobject-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +dependencies = [ + "glib-sys", + "libc", + "system-deps 6.0.3", +] + +[[package]] +name = "gtk" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" +dependencies = [ + "atk", + "bitflags", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "once_cell", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps 6.0.3", +] + +[[package]] +name = "gtk3-macros" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9" +dependencies = [ + "anyhow", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "html5ever" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.5", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "ico" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031530fe562d8c8d71c0635013d6d155bbfe8ba0aa4b4d2d24ce8af6b71047bd" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "image" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-rational", + "num-traits", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "infer" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b2b533137b9cad970793453d4f921c2e91312a6d88b1085c07bc15fc51bb3b" +dependencies = [ + "cfb", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "javascriptcore-rs" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" +dependencies = [ + "bitflags", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 5.0.0", +] + +[[package]] +name = "jni" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "json-patch" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" +dependencies = [ + "serde", + "serde_json", + "treediff", +] + +[[package]] +name = "kuchiki" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" +dependencies = [ + "cssparser", + "html5ever", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" +dependencies = [ + "log", + "phf 0.8.0", + "phf_codegen", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "ndk" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "oslog" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d2043d1f61d77cb2f4b1f7b7b2295f40507f5f8e9d1c8bf10a1ca5f97a3969" +dependencies = [ + "cc", + "dashmap", + "log", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pango" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" +dependencies = [ + "bitflags", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 6.0.3", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pest" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "plist" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" +dependencies = [ + "base64", + "indexmap", + "line-wrap", + "serde", + "time", + "xml-rs", +] + +[[package]] +name = "png" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +dependencies = [ + "bitflags", + "crc32fast", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" +dependencies = [ + "cty", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.8", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.16", +] + +[[package]] +name = "rustversion" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa 1.0.5", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.5", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "soup2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +dependencies = [ + "bitflags", + "gio", + "glib", + "libc", + "once_cell", + "soup2-sys", +] + +[[package]] +name = "soup2-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +dependencies = [ + "bitflags", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps 5.0.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "state" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" +dependencies = [ + "loom", +] + +[[package]] +name = "string_cache" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" +dependencies = [ + "cfg-expr 0.9.1", + "heck 0.3.3", + "pkg-config", + "toml", + "version-compare 0.0.11", +] + +[[package]] +name = "system-deps" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +dependencies = [ + "cfg-expr 0.11.0", + "heck 0.4.0", + "pkg-config", + "toml", + "version-compare 0.1.1", +] + +[[package]] +name = "tao" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704522803dda895767f69198af8351b0a3f4fe2e293c3ca54cce0ecba05a97f2" +dependencies = [ + "bitflags", + "cairo-rs", + "cc", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "gdk", + "gdk-pixbuf", + "gdk-sys", + "gdkx11-sys", + "gio", + "glib", + "glib-sys", + "gtk", + "image", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "png", + "raw-window-handle", + "scopeguard", + "serde", + "tao-macros", + "unicode-segmentation", + "uuid 1.2.2", + "windows", + "windows-implement", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b6fcd8245d45a39ffc8715183d92ae242750eb57b285eb3bcd63dfd512afd09" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tauri" +version = "2.0.0-alpha.2" +dependencies = [ + "android_logger", + "anyhow", + "attohttpc", + "cocoa", + "dirs-next", + "embed_plist", + "encoding_rs", + "flate2", + "futures-util", + "glib", + "glob", + "gtk", + "heck 0.4.0", + "http", + "ignore", + "libc", + "log", + "objc", + "once_cell", + "oslog", + "paste", + "percent-encoding", + "rand 0.8.5", + "raw-window-handle", + "semver 1.0.16", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "state", + "tar", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "tempfile", + "thiserror", + "tokio", + "url", + "uuid 1.2.2", + "webkit2gtk", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-codegen" +version = "2.0.0-alpha.0" +dependencies = [ + "base64", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "semver 1.0.16", + "serde", + "serde_json", + "sha2", + "tauri-utils", + "thiserror", + "time", + "url", + "uuid 1.2.2", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.0.0-alpha.0" +dependencies = [ + "heck 0.4.0", + "proc-macro2", + "quote", + "syn", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin-sample" +version = "0.1.0" +dependencies = [ + "tauri", + "walkdir", +] + +[[package]] +name = "tauri-runtime" +version = "0.13.0-alpha.0" +dependencies = [ + "gtk", + "http", + "http-range", + "jni", + "rand 0.8.5", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror", + "uuid 1.2.2", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "0.13.0-alpha.0" +dependencies = [ + "cocoa", + "gtk", + "jni", + "percent-encoding", + "rand 0.8.5", + "raw-window-handle", + "tauri-runtime", + "tauri-utils", + "uuid 1.2.2", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.0.0-alpha.0" +dependencies = [ + "brotli", + "ctor", + "glob", + "heck 0.4.0", + "html5ever", + "infer", + "json-patch", + "kuchiki", + "memchr", + "phf 0.10.1", + "proc-macro2", + "quote", + "semver 1.0.16", + "serde", + "serde_json", + "serde_with", + "thiserror", + "url", + "walkdir", + "windows", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +dependencies = [ + "itoa 1.0.5", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" +dependencies = [ + "autocfg", + "bytes", + "memchr", + "num_cpus", + "pin-project-lite", + "windows-sys", +] + +[[package]] +name = "toml" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "treediff" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" +dependencies = [ + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" + +[[package]] +name = "uuid" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version-compare" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "webkit2gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup2", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" +dependencies = [ + "atk-sys", + "bitflags", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pango-sys", + "pkg-config", + "soup2-sys", + "system-deps 6.0.3", +] + +[[package]] +name = "webview2-com" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-implement", +] + +[[package]] +name = "webview2-com-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "webview2-com-sys" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" +dependencies = [ + "regex", + "serde", + "serde_json", + "thiserror", + "windows", + "windows-bindgen", + "windows-metadata", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" +dependencies = [ + "windows-implement", + "windows_aarch64_msvc 0.39.0", + "windows_i686_gnu 0.39.0", + "windows_i686_msvc 0.39.0", + "windows_x86_64_gnu 0.39.0", + "windows_x86_64_msvc 0.39.0", +] + +[[package]] +name = "windows-bindgen" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" +dependencies = [ + "windows-metadata", + "windows-tokens", +] + +[[package]] +name = "windows-implement" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" +dependencies = [ + "syn", + "windows-tokens", +] + +[[package]] +name = "windows-metadata" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-tokens" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "wry" +version = "0.24.1" +dependencies = [ + "base64", + "block", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dunce", + "gdk", + "gio", + "glib", + "gtk", + "html5ever", + "http", + "kuchiki", + "libc", + "log", + "objc", + "objc_id", + "once_cell", + "serde", + "serde_json", + "sha2", + "soup2", + "tao", + "thiserror", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-implement", +] + +[[package]] +name = "x11" +version = "2.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2638d5b9c17ac40575fb54bb461a4b1d2a8d1b4ffcc4ff237d254ec59ddeb82" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1536d6965a5d4e573c7ef73a2c15ebcd0b2de3347bdf526c34c297c00ac40f0" +dependencies = [ + "lazy_static", + "libc", + "pkg-config", +] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/examples/api/src-tauri/tauri-plugin-sample/Cargo.toml b/examples/api/src-tauri/tauri-plugin-sample/Cargo.toml new file mode 100644 index 00000000000..e920682fc1c --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "tauri-plugin-sample" +version = "0.1.0" +edition = "2021" + +[dependencies] +tauri = { path = "../../../../core/tauri" } + +[build-dependencies] +tauri-build = { path = "../../../../core/tauri-build/" } diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/.gitignore b/examples/api/src-tauri/tauri-plugin-sample/android/.gitignore new file mode 100644 index 00000000000..42afabfd2ab --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/android/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts b/examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts new file mode 100644 index 00000000000..83f86a9a9f0 --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/android/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "com.plugin.test" + compileSdk = 32 + + defaultConfig { + minSdk = 24 + targetSdk = 32 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.appcompat:appcompat:1.6.0") + implementation("com.google.android.material:material:1.7.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + implementation(project(":tauri-android")) +} \ No newline at end of file diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro b/examples/api/src-tauri/tauri-plugin-sample/android/consumer-rules.pro new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/proguard-rules.pro b/examples/api/src-tauri/tauri-plugin-sample/android/proguard-rules.pro new file mode 100644 index 00000000000..481bb434814 --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/settings.gradle b/examples/api/src-tauri/tauri-plugin-sample/android/settings.gradle new file mode 100644 index 00000000000..32f9aac23d1 --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/android/settings.gradle @@ -0,0 +1,2 @@ +include ':tauri-android' +project(':tauri-android').projectDir = new File('./tauri-api') diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/androidTest/java/com/plugin/test/ExampleInstrumentedTest.kt b/examples/api/src-tauri/tauri-plugin-sample/android/src/androidTest/java/com/plugin/test/ExampleInstrumentedTest.kt new file mode 100644 index 00000000000..1010bda3ea9 --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/androidTest/java/com/plugin/test/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.plugin.test + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.plugin.test.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/main/AndroidManifest.xml b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..a5918e68abc --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/test/Example.kt b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/test/Example.kt new file mode 100644 index 00000000000..8253a9b7c43 --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/test/Example.kt @@ -0,0 +1,10 @@ +package com.plugin.test + +import android.util.Log + +class Example { + fun pong(value: String): String { + Log.i("Pong", value) + return value + } +} diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/test/ExamplePlugin.kt b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/test/ExamplePlugin.kt new file mode 100644 index 00000000000..37d2769cee6 --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/test/ExamplePlugin.kt @@ -0,0 +1,19 @@ +package com.plugin.test + +import android.app.Activity +import app.tauri.plugin.JSObject +import app.tauri.plugin.Plugin +import app.tauri.plugin.Invoke +import app.tauri.plugin.PluginMethod + +class ExamplePlugin(private val activity: Activity): Plugin() { + private val implementation = Example() + + @PluginMethod + fun ping(invoke: Invoke) { + val value = invoke.getString("value") ?: "" + val ret = JSObject() + ret.put("value", implementation.pong(value)) + invoke.resolve(ret) + } +} diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/test/java/com/plugin/test/ExampleUnitTest.kt b/examples/api/src-tauri/tauri-plugin-sample/android/src/test/java/com/plugin/test/ExampleUnitTest.kt new file mode 100644 index 00000000000..62cdd277efb --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/test/java/com/plugin/test/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.plugin.test + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/examples/api/src-tauri/tauri-plugin-sample/build.rs b/examples/api/src-tauri/tauri-plugin-sample/build.rs new file mode 100644 index 00000000000..d58b7f93ff4 --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/build.rs @@ -0,0 +1,11 @@ +use std::process::exit; + +fn main() { + if let Err(error) = tauri_build::mobile::PluginBuilder::new() + .android_path("android") + .run() + { + println!("{error:#}"); + exit(1); + } +} diff --git a/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs b/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs new file mode 100644 index 00000000000..5e5b94c9acb --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs @@ -0,0 +1,21 @@ +use tauri::{ + plugin::{Builder, TauriPlugin}, + Runtime, +}; + +const PLUGIN_NAME: &str = "sample"; +#[cfg(target_os = "android")] +const PLUGIN_IDENTIFIER: &str = "com.plugin.test"; + +pub fn init() -> TauriPlugin { + #[allow(unused_mut)] + let mut builder = Builder::new(PLUGIN_NAME); + #[cfg(target_os = "android")] + { + builder = builder.setup(|app| { + app.initialize_android_plugin(PLUGIN_NAME, PLUGIN_IDENTIFIER, "ExamplePlugin")?; + Ok(()) + }); + } + builder.build() +} diff --git a/examples/api/src-tauri/tauri.conf.json b/examples/api/src-tauri/tauri.conf.json index 192781efb8d..fe8e127e688 100644 --- a/examples/api/src-tauri/tauri.conf.json +++ b/examples/api/src-tauri/tauri.conf.json @@ -4,7 +4,8 @@ "distDir": "../dist", "devPath": "http://localhost:5173", "beforeDevCommand": "yarn dev", - "beforeBuildCommand": "yarn build" + "beforeBuildCommand": "yarn build", + "withGlobalTauri": true }, "package": { "productName": "Tauri API", diff --git a/tooling/cli/mobile/android/.gitignore b/tooling/cli/mobile/android/.gitignore new file mode 100644 index 00000000000..42afabfd2ab --- /dev/null +++ b/tooling/cli/mobile/android/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/tooling/cli/mobile/android/build.gradle.kts b/tooling/cli/mobile/android/build.gradle.kts new file mode 100644 index 00000000000..3ed1b0dc308 --- /dev/null +++ b/tooling/cli/mobile/android/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "app.tauri" + compileSdk = 33 + + defaultConfig { + minSdk = 24 + targetSdk = 33 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation("androidx.core:core-ktx:1.7.0") + implementation("androidx.appcompat:appcompat:1.6.0") + implementation("com.google.android.material:material:1.7.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} \ No newline at end of file diff --git a/tooling/cli/mobile/android/consumer-rules.pro b/tooling/cli/mobile/android/consumer-rules.pro new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tooling/cli/mobile/android/proguard-rules.pro b/tooling/cli/mobile/android/proguard-rules.pro new file mode 100644 index 00000000000..481bb434814 --- /dev/null +++ b/tooling/cli/mobile/android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/tooling/cli/mobile/android/src/androidTest/java/app/tauri/ExampleInstrumentedTest.kt b/tooling/cli/mobile/android/src/androidTest/java/app/tauri/ExampleInstrumentedTest.kt new file mode 100644 index 00000000000..d283b837546 --- /dev/null +++ b/tooling/cli/mobile/android/src/androidTest/java/app/tauri/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package app.tauri + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("app.tauri.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/tooling/cli/mobile/android/src/main/AndroidManifest.xml b/tooling/cli/mobile/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..a5918e68abc --- /dev/null +++ b/tooling/cli/mobile/android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/Logger.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/Logger.kt new file mode 100644 index 00000000000..ee8d511be22 --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/Logger.kt @@ -0,0 +1,81 @@ +package app.tauri + +// taken from https://github.com/ionic-team/capacitor/blob/6658bca41e78239347e458175b14ca8bd5c1d6e8/android/capacitor/src/main/java/com/getcapacitor/Logger.java + +import android.text.TextUtils; +import android.util.Log; + +class Logger { + companion object { + private const val LOG_TAG_CORE = "Tauri" + + fun tags(vararg subtags: String): String { + return if (subtags.isNotEmpty()) { + LOG_TAG_CORE + "/" + TextUtils.join("/", subtags) + } else LOG_TAG_CORE + } + + fun verbose(message: String) { + verbose(LOG_TAG_CORE, message) + } + + fun verbose(tag: String, message: String) { + if (!shouldLog()) { + return + } + Log.v(tag, message) + } + + fun debug(message: String) { + debug(LOG_TAG_CORE, message) + } + + fun debug(tag: String, message: String) { + if (!shouldLog()) { + return + } + Log.d(tag, message) + } + + fun info(message: String) { + info(LOG_TAG_CORE, message) + } + + fun info(tag: String, message: String) { + if (!shouldLog()) { + return + } + Log.i(tag, message) + } + + fun warn(message: String) { + warn(LOG_TAG_CORE, message) + } + + fun warn(tag: String, message: String) { + if (!shouldLog()) { + return + } + Log.w(tag, message) + } + + fun error(message: String) { + error(LOG_TAG_CORE, message, null) + } + + fun error(message: String, e: Throwable?) { + error(LOG_TAG_CORE, message, e) + } + + fun error(tag: String, message: String, e: Throwable?) { + if (!shouldLog()) { + return + } + Log.e(tag, message, e) + } + + private fun shouldLog(): Boolean { + return BuildConfig.DEBUG + } + } +} diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/InvalidPluginMethodException.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/InvalidPluginMethodException.kt new file mode 100644 index 00000000000..eac3f62fb43 --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/InvalidPluginMethodException.kt @@ -0,0 +1,7 @@ +package app.tauri.plugin + +internal class InvalidPluginMethodException : Exception { + constructor(s: String?) : super(s) {} + constructor(t: Throwable?) : super(t) {} + constructor(s: String?, t: Throwable?) : super(s, t) {} +} diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt new file mode 100644 index 00000000000..3bb7a7fc5d7 --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt @@ -0,0 +1,165 @@ +package app.tauri.plugin + +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import app.tauri.Logger + +class Invoke( + private val sendResponse: (succcess: PluginResult?, error: PluginResult?) -> Unit, + val data: JSObject?) { + + fun resolve(data: JSObject?) { + val result = PluginResult(data) + sendResponse(result, null) + } + + fun resolve() { + sendResponse(null, null) + } + + fun reject(msg: String?, code: String?, ex: Exception?, data: JSObject?) { + val errorResult = PluginResult() + if (ex != null) { + Logger.error(Logger.tags("Plugin"), msg!!, ex) + } + try { + errorResult.put("message", msg) + errorResult.put("code", code) + if (null != data) { + errorResult.put("data", data) + } + } catch (jsonEx: Exception) { + Logger.error(Logger.tags("Plugin"), jsonEx.message!!, jsonEx) + } + sendResponse(null, errorResult) + } + + fun reject(msg: String?, ex: Exception?, data: JSObject?) { + reject(msg, null, ex, data) + } + + fun reject(msg: String?, code: String?, data: JSObject?) { + reject(msg, code, null, data) + } + + fun reject(msg: String?, code: String?, ex: Exception?) { + reject(msg, code, ex, null) + } + + fun reject(msg: String?, data: JSObject?) { + reject(msg, null, null, data) + } + + fun reject(msg: String?, ex: Exception?) { + reject(msg, null, ex, null) + } + + fun reject(msg: String?, code: String?) { + reject(msg, code, null, null) + } + + fun reject(msg: String?) { + reject(msg, null, null, null) + } + + fun getString(name: String): String? { + return this.getString(name, null) + } + + fun getString(name: String, defaultValue: String?): String? { + val value = data!!.opt(name) ?: return defaultValue + return if (value is String) { + value + } else defaultValue + } + + fun getInt(name: String): Int? { + return this.getInt(name, null) + } + + fun getInt(name: String, defaultValue: Int?): Int? { + val value = data!!.opt(name) ?: return defaultValue + return if (value is Int) { + value + } else defaultValue + } + + fun getLong(name: String): Long? { + return this.getLong(name, null) + } + + fun getLong(name: String, defaultValue: Long?): Long? { + val value = data!!.opt(name) ?: return defaultValue + return if (value is Long) { + value + } else defaultValue + } + + fun getFloat(name: String): Float? { + return this.getFloat(name, null) + } + + fun getFloat(name: String, defaultValue: Float?): Float? { + val value = data!!.opt(name) ?: return defaultValue + if (value is Float) { + return value + } + if (value is Double) { + return value.toFloat() + } + return if (value is Int) { + value.toFloat() + } else defaultValue + } + + fun getDouble(name: String): Double? { + return this.getDouble(name, null) + } + + fun getDouble(name: String, defaultValue: Double?): Double? { + val value = data!!.opt(name) ?: return defaultValue + if (value is Double) { + return value + } + if (value is Float) { + return value.toDouble() + } + return if (value is Int) { + value.toDouble() + } else defaultValue + } + + fun getBoolean(name: String): Boolean? { + return this.getBoolean(name, null) + } + + fun getBoolean(name: String, defaultValue: Boolean?): Boolean? { + val value = data!!.opt(name) ?: return defaultValue + return if (value is Boolean) { + value + } else defaultValue + } + + fun getObject(name: String): JSObject? { + return this.getObject(name, null) + } + + fun getObject(name: String, defaultValue: JSObject?): JSObject? { + val value = data!!.opt(name) ?: return defaultValue + return if (value is JSObject) value else defaultValue + } + + fun getArray(name: String): JSArray? { + return this.getArray(name, null) + } + + fun getArray(name: String, defaultValue: JSArray?): JSArray? { + val value = data!!.opt(name) ?: return defaultValue + return if (value is JSArray) value else defaultValue + } + + fun hasOption(name: String): Boolean { + return data!!.has(name) + } +} diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/JSArray.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/JSArray.kt new file mode 100644 index 00000000000..be98c1fd16c --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/JSArray.kt @@ -0,0 +1,41 @@ +package app.tauri.plugin + +import org.json.JSONArray +import org.json.JSONException + +class JSArray : JSONArray { + constructor() : super() {} + constructor(json: String?) : super(json) {} + constructor(copyFrom: Collection<*>?) : super(copyFrom) {} + constructor(array: Any?) : super(array) {} + + @Suppress("UNCHECKED_CAST", "ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + @Throws(JSONException::class) + fun toList(): List { + val items: MutableList = ArrayList() + var o: Any? = null + for (i in 0 until this.length()) { + this.get(i).also { o = it } + try { + items.add(this.get(i) as E) + } catch (ex: Exception) { + throw JSONException("Not all items are instances of the given type") + } + } + return items + } + + companion object { + /** + * Create a new JSArray without throwing a error + */ + fun from(array: Any?): JSArray? { + try { + return JSArray(array) + } catch (ex: JSONException) { + // + } + return null + } + } +} diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/JSObject.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/JSObject.kt new file mode 100644 index 00000000000..929812e5781 --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/JSObject.kt @@ -0,0 +1,146 @@ +package app.tauri.plugin + +import org.json.JSONException +import org.json.JSONObject + +class JSObject : JSONObject { + constructor() : super() + constructor(json: String) : super(json) + constructor(obj: JSONObject, names: Array) : super(obj, names) + + override fun getString(key: String): String { + return getString(key, "") + } + + fun getString(key: String, defaultValue: String): String { + try { + val value = super.getString(key) + if (!super.isNull(key)) { + return value + } + } catch (_: JSONException) { + } + return defaultValue + } + + fun getInteger(key: String): Int? { + return getInteger(key, null) + } + + fun getInteger(key: String, defaultValue: Int?): Int? { + try { + return super.getInt(key) + } catch (_: JSONException) { + } + return defaultValue + } + + fun getBoolean(key: String, defaultValue: Boolean?): Boolean? { + try { + return super.getBoolean(key) + } catch (_: JSONException) { + } + return defaultValue + } + + /** + * Fetch boolean from jsonObject + */ + fun getBool(key: String): Boolean? { + return getBoolean(key, null) + } + + fun getJSObject(name: String): JSObject? { + try { + return getJSObject(name, null) + } catch (e: JSONException) { + } + return null + } + + @Throws(JSONException::class) + fun getJSObject(name: String, defaultValue: JSObject?): JSObject? { + try { + val obj = get(name) + if (obj is JSONObject) { + val keysIter = obj.keys() + val keys: MutableList = ArrayList() + while (keysIter.hasNext()) { + keys.add(keysIter.next()) + } + return JSObject(obj, keys.toTypedArray()) + } + } catch (_: JSONException) { + } + return defaultValue + } + + override fun put(key: String, value: Boolean): JSObject { + try { + super.put(key, value) + } catch (_: JSONException) { + } + return this + } + + override fun put(key: String, value: Int): JSObject { + try { + super.put(key, value) + } catch (_: JSONException) { + } + return this + } + + override fun put(key: String, value: Long): JSObject { + try { + super.put(key, value) + } catch (_: JSONException) { + } + return this + } + + override fun put(key: String, value: Double): JSObject { + try { + super.put(key, value) + } catch (_: JSONException) { + } + return this + } + + override fun put(key: String, value: Any?): JSObject { + try { + super.put(key, value) + } catch (_: JSONException) { + } + return this + } + + fun put(key: String, value: String?): JSObject { + try { + super.put(key, value) + } catch (_: JSONException) { + } + return this + } + + @Throws(JSONException::class) + fun putSafe(key: String, value: Any?): JSObject { + return super.put(key, value) as JSObject + } + + companion object { + /** + * Convert a pathetic JSONObject into a JSObject + * @param obj + */ + @Throws(JSONException::class) + fun fromJSONObject(obj: JSONObject): JSObject { + val keysIter = obj.keys() + val keys: MutableList = ArrayList() + while (keysIter.hasNext()) { + keys.add(keysIter.next()) + } + return JSObject(obj, keys.toTypedArray()) + } + } +} diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt new file mode 100644 index 00000000000..0300636c425 --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt @@ -0,0 +1,7 @@ +package app.tauri.plugin + +import android.webkit.WebView + +abstract class Plugin { + open fun load(webView: WebView) {} +} diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt new file mode 100644 index 00000000000..c4986ef5e7f --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt @@ -0,0 +1,37 @@ +package app.tauri.plugin + +import android.webkit.WebView +import java.lang.reflect.Method + +class PluginHandle(val instance: Plugin) { + private val pluginMethods: HashMap = HashMap() + var loaded = false + + init { + indexMethods() + } + + fun load(webView: WebView) { + instance.load(webView) + loaded = true + } + + @Throws( + InvalidPluginMethodException::class, + IllegalAccessException::class + ) + fun invoke(methodName: String, invoke: Invoke) { + val methodMeta = pluginMethods[methodName] + ?: throw InvalidPluginMethodException("No method " + methodName + " found for plugin " + instance.javaClass.name) + methodMeta.method.invoke(instance, invoke) + } + + private fun indexMethods() { + val methods: Array = instance.javaClass.methods + for (methodReflect in methods) { + val method: PluginMethod = methodReflect.getAnnotation(PluginMethod::class.java) ?: continue + val methodMeta = PluginMethodData(methodReflect, method) + pluginMethods.put(methodReflect.name, methodMeta) + } + } +} diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt new file mode 100644 index 00000000000..0b3a6c2e595 --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt @@ -0,0 +1,49 @@ +package app.tauri.plugin + +import android.webkit.WebView +import app.tauri.Logger + +class PluginManager { + private val plugins: HashMap = HashMap() + + fun onWebViewCreated(webView: WebView) { + for ((_, plugin) in plugins) { + if (!plugin.loaded) { + plugin.load(webView) + } + } + } + + fun load(webView: WebView?, name: String, plugin: Plugin) { + val handle = PluginHandle(plugin) + plugins[name] = handle + if (webView != null) { + plugin.load(webView) + } + } + + fun postMessage(webView: WebView, pluginId: String, methodName: String, data: JSObject, callback: Long, error: Long) { + Logger.verbose( + Logger.tags("Plugin"), + "Tauri plugin: pluginId: $pluginId, methodName: $methodName, callback: $callback, error: $error" + ) + + val invoke = Invoke({ successResult, errorResult -> + val (fn, result) = if (errorResult == null) Pair(callback, successResult) else Pair( + error, + errorResult + ) + webView.evaluateJavascript("window['_$fn']($result)", null) + }, data) + try { + val plugin = plugins[pluginId] + if (plugin == null) { + invoke.reject("Plugin $pluginId not initialized") + } else { + plugins[pluginId]?.invoke(methodName, invoke) + } + } catch (e: Exception) { + invoke.reject(e.toString()) + } + } +} diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginMethod.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginMethod.kt new file mode 100644 index 00000000000..cbee37361b5 --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginMethod.kt @@ -0,0 +1,4 @@ +package app.tauri.plugin + +@Retention(AnnotationRetention.RUNTIME) +annotation class PluginMethod(val returnType: String = "promise") { } diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginMethodData.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginMethodData.kt new file mode 100644 index 00000000000..1b4e88f081f --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginMethodData.kt @@ -0,0 +1,14 @@ +package app.tauri.plugin + +import java.lang.reflect.Method + +class PluginMethodData( + val method: Method, methodDecorator: PluginMethod +) { + + // The name of the method + val name: String = method.name + + // The return type of the method (see PluginMethod for constants) + val returnType: String = methodDecorator.returnType +} diff --git a/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginResult.kt b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginResult.kt new file mode 100644 index 00000000000..47ff3ea9678 --- /dev/null +++ b/tooling/cli/mobile/android/src/main/java/app/tauri/plugin/PluginResult.kt @@ -0,0 +1,63 @@ +package app.tauri.plugin + +import android.annotation.SuppressLint +import app.tauri.Logger +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* + +class PluginResult @JvmOverloads constructor(json: JSObject? = JSObject()) { + private val json: JSObject + + init { + this.json = json ?: JSObject() + } + + fun put(name: String, value: Boolean): PluginResult { + return jsonPut(name, value) + } + + fun put(name: String, value: Double): PluginResult { + return jsonPut(name, value) + } + + fun put(name: String, value: Int): PluginResult { + return jsonPut(name, value) + } + + fun put(name: String, value: Long): PluginResult { + return jsonPut(name, value) + } + + /** + * Format a date as an ISO string + */ + @SuppressLint("SimpleDateFormat") + fun put(name: String, value: Date): PluginResult { + val tz: TimeZone = TimeZone.getTimeZone("UTC") + val df: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'") + df.timeZone = tz + return jsonPut(name, df.format(value)) + } + + fun put(name: String, value: Any?): PluginResult { + return jsonPut(name, value) + } + + fun put(name: String, value: PluginResult): PluginResult { + return jsonPut(name, value.json) + } + + private fun jsonPut(name: String, value: Any?): PluginResult { + try { + json.put(name, value) + } catch (ex: Exception) { + Logger.error(Logger.tags("Plugin"), "", ex) + } + return this + } + + override fun toString(): String { + return json.toString() + } +} diff --git a/tooling/cli/mobile/android/src/test/java/app/tauri/ExampleUnitTest.kt b/tooling/cli/mobile/android/src/test/java/app/tauri/ExampleUnitTest.kt new file mode 100644 index 00000000000..637e05ce58f --- /dev/null +++ b/tooling/cli/mobile/android/src/test/java/app/tauri/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package app.tauri + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index 7a8ea1d3dae..31a38eaa3a9 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -136,6 +136,16 @@ pub fn get_config( )) .join("generated"), ); + let plugin_output_path = config.project_dir().join("tauri-plugins"); + set_var("TAURI_PLUGIN_OUTPUT_PATH", plugin_output_path); + set_var( + "TAURI_GRADLE_SETTINGS_PATH", + config.project_dir().join("settings.gradle"), + ); + set_var( + "TAURI_APP_GRADLE_BUILD_PATH", + config.project_dir().join("app").join("build.gradle.kts"), + ); (app, config, metadata) } diff --git a/tooling/cli/src/mobile/android/build.rs b/tooling/cli/src/mobile/android/build.rs index 22b7ca948b0..ed31548d28a 100644 --- a/tooling/cli/src/mobile/android/build.rs +++ b/tooling/cli/src/mobile/android/build.rs @@ -120,13 +120,16 @@ fn run_build( .triple .into(), ); - let interface = crate::build::setup(&mut build_options, true)?; + let mut interface = crate::build::setup(&mut build_options, true)?; - let app_settings = interface.app_settings(); - let bin_path = app_settings.app_binary_path(&InterfaceOptions { + let interface_options = InterfaceOptions { debug: build_options.debug, + target: build_options.target.clone(), ..Default::default() - })?; + }; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&interface_options)?; let out_dir = bin_path.parent().unwrap(); let _lock = flock::open_rw(out_dir.join("lock").with_extension("android"), "Android")?; @@ -143,6 +146,9 @@ fn run_build( .get_or_insert(Vec::new()) .push("custom-protocol".into()); + // run an initial build to initialize plugins + interface.build(interface_options)?; + let apk_outputs = if options.apk { apk::build( config, diff --git a/tooling/cli/src/mobile/android/dev.rs b/tooling/cli/src/mobile/android/dev.rs index b9e4d222646..897db7fa5f3 100644 --- a/tooling/cli/src/mobile/android/dev.rs +++ b/tooling/cli/src/mobile/android/dev.rs @@ -120,16 +120,22 @@ fn run_dev( ); let mut interface = crate::dev::setup(&mut dev_options, true)?; - let app_settings = interface.app_settings(); - let bin_path = app_settings.app_binary_path(&InterfaceOptions { + let interface_options = InterfaceOptions { debug: !dev_options.release_mode, + target: dev_options.target.clone(), ..Default::default() - })?; + }; + + let app_settings = interface.app_settings(); + let bin_path = app_settings.app_binary_path(&interface_options)?; let out_dir = bin_path.parent().unwrap(); let _lock = flock::open_rw(out_dir.join("lock").with_extension("android"), "Android")?; init_dot_cargo(app, Some((&env, config)))?; + // run an initial build to initialize plugins + interface.build(interface_options)?; + let open = options.open; let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; diff --git a/tooling/cli/src/mobile/android/project.rs b/tooling/cli/src/mobile/android/project.rs index 2db507e20c5..b1afa6e8a5d 100644 --- a/tooling/cli/src/mobile/android/project.rs +++ b/tooling/cli/src/mobile/android/project.rs @@ -21,7 +21,11 @@ use tauri_mobile::{ }, }; -use std::{ffi::OsStr, fs, path::Path}; +use std::{ + ffi::OsStr, + fs, + path::{Path, PathBuf}, +}; const TEMPLATE_DIR: Dir<'_> = include_dir!("templates/mobile/android"); @@ -93,49 +97,7 @@ pub fn gen( map.inner(), &TEMPLATE_DIR, &dest, - &mut |path| { - let mut iter = path.iter(); - let root = iter.next().unwrap().to_str().unwrap(); - let path_without_root: std::path::PathBuf = iter.collect(); - let path = match ( - root, - path.extension().and_then(|o| o.to_str()), - path_without_root.strip_prefix("src/main"), - ) { - ("app" | "buildSrc", Some("kt"), Ok(path)) => { - let parent = path.parent().unwrap(); - let file_name = path.file_name().unwrap(); - let out_dir = dest - .join(root) - .join("src/main") - .join(&package_path) - .join(parent); - out_dir.join(file_name) - } - _ => dest.join(path), - }; - - let parent = path.parent().unwrap().to_path_buf(); - if !created_dirs.contains(&parent) { - fs::create_dir_all(&parent)?; - created_dirs.push(parent); - } - - let mut options = fs::OpenOptions::new(); - options.write(true); - - #[cfg(unix)] - if path.file_name().unwrap() == OsStr::new("gradlew") { - use std::os::unix::fs::OpenOptionsExt; - options.mode(0o755); - } - - if path.file_name().unwrap() == OsStr::new("BuildTask.kt") || !path.exists() { - options.create(true).open(path).map(Some) - } else { - Ok(None) - } - }, + &mut |path| generate_out_file(path, &dest, &package_path, &mut created_dirs), ) .with_context(|| "failed to process template")?; @@ -183,3 +145,52 @@ pub fn gen( Ok(()) } + +pub(crate) fn generate_out_file( + path: &Path, + dest: &Path, + package_path: &str, + created_dirs: &mut Vec, +) -> std::io::Result> { + let mut iter = path.iter(); + let root = iter.next().unwrap().to_str().unwrap(); + let path_without_root: std::path::PathBuf = iter.collect(); + let path = match ( + root, + path.extension().and_then(|o| o.to_str()), + path_without_root.strip_prefix("src/main"), + ) { + ("app" | "buildSrc", Some("kt"), Ok(path)) => { + let parent = path.parent().unwrap(); + let file_name = path.file_name().unwrap(); + let out_dir = dest + .join(root) + .join("src/main") + .join(package_path) + .join(parent); + out_dir.join(file_name) + } + _ => dest.join(path), + }; + + let parent = path.parent().unwrap().to_path_buf(); + if !created_dirs.contains(&parent) { + fs::create_dir_all(&parent)?; + created_dirs.push(parent); + } + + let mut options = fs::OpenOptions::new(); + options.write(true); + + #[cfg(unix)] + if path.file_name().unwrap() == OsStr::new("gradlew") { + use std::os::unix::fs::OpenOptionsExt; + options.mode(0o755); + } + + if path.file_name().unwrap() == OsStr::new("BuildTask.kt") || !path.exists() { + options.create(true).open(path).map(Some) + } else { + Ok(None) + } +} diff --git a/tooling/cli/src/mobile/mod.rs b/tooling/cli/src/mobile/mod.rs index e92f0fa4f90..b5eb14f33ad 100644 --- a/tooling/cli/src/mobile/mod.rs +++ b/tooling/cli/src/mobile/mod.rs @@ -11,19 +11,22 @@ use crate::{ }, interface::{AppInterface, AppSettings, DevProcess, Interface, Options as InterfaceOptions}, }; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; +use include_dir::{include_dir, Dir}; use jsonrpsee::client_transport::ws::WsTransportClientBuilder; use jsonrpsee::core::client::{Client, ClientBuilder, ClientT}; use jsonrpsee::rpc_params; use jsonrpsee::server::{RpcModule, ServerBuilder, ServerHandle}; use serde::{Deserialize, Serialize}; use shared_child::SharedChild; + use std::{ collections::HashMap, env::set_var, env::var, ffi::OsString, fmt::Write, + fs::{create_dir_all, remove_dir_all}, net::SocketAddr, path::PathBuf, process::ExitStatus, @@ -51,6 +54,7 @@ mod init; pub mod ios; const MIN_DEVICE_MATCH_SCORE: isize = 0; +static ANDROID_API_PROJECT_DIR: Dir<'_> = include_dir!("mobile/android"); #[derive(Clone)] pub struct DevChild { @@ -305,6 +309,21 @@ fn ensure_init(project_dir: PathBuf, target: Target) -> Result<()> { target.command_name(), ) } else { + create_dir_all(project_dir.join("tauri-plugins"))?; + + #[allow(irrefutable_let_patterns)] + if let Target::Android = target { + let tauri_api_dir_path = project_dir.join("tauri-api"); + if tauri_api_dir_path.exists() { + remove_dir_all(&tauri_api_dir_path)?; + } + create_dir_all(&tauri_api_dir_path)?; + + ANDROID_API_PROJECT_DIR + .extract(tauri_api_dir_path) + .context("failed to extract Tauri API project")?; + } + Ok(()) } } diff --git a/tooling/cli/src/plugin/init.rs b/tooling/cli/src/plugin/init.rs index 62eafcc02ae..fee5dacf9ea 100644 --- a/tooling/cli/src/plugin/init.rs +++ b/tooling/cli/src/plugin/init.rs @@ -9,14 +9,19 @@ use crate::{ }; use anyhow::Context; use clap::Parser; +use dialoguer::Input; use handlebars::{to_json, Handlebars}; use heck::{AsKebabCase, ToKebabCase, ToSnakeCase}; use include_dir::{include_dir, Dir}; use log::warn; -use std::{collections::BTreeMap, env::current_dir, fs::remove_dir_all, path::PathBuf}; +use std::{ + collections::BTreeMap, env::current_dir, fmt::Display, fs::remove_dir_all, path::PathBuf, + str::FromStr, +}; const BACKEND_PLUGIN_DIR: Dir<'_> = include_dir!("templates/plugin/backend"); const API_PLUGIN_DIR: Dir<'_> = include_dir!("templates/plugin/with-api"); +const ANDROID_PLUGIN_DIR: Dir<'_> = include_dir!("templates/plugin/android"); #[derive(Debug, Parser)] #[clap(about = "Initializes a Tauri plugin project")] @@ -40,6 +45,9 @@ pub struct Options { /// Author name #[clap(short, long)] author: Option, + /// Adds native Android support. + #[clap(long)] + android: bool, } impl Options { @@ -127,6 +135,62 @@ pub fn command(mut options: Options) -> Result<()> { &template_target_path, ) .with_context(|| "failed to render Tauri template")?; + + if options.android { + let plugin_id = request_input( + "What should be the Package ID for your plugin?", + Some(format!("com.plugin.{}", options.plugin_name)), + false, + false, + )? + .unwrap(); + + let mut data = BTreeMap::new(); + data.insert("package_id", to_json(&plugin_id)); + + let mut created_dirs = Vec::new(); + template::render_with_generator( + &handlebars, + &data, + &ANDROID_PLUGIN_DIR, + &template_target_path, + &mut |path| { + crate::mobile::android::project::generate_out_file( + path, + &template_target_path.join("android"), + &plugin_id.replace('.', "/"), + &mut created_dirs, + ) + }, + ) + .with_context(|| "failed to render Tauri template")?; + } } Ok(()) } + +fn request_input( + prompt: &str, + initial: Option, + skip: bool, + allow_empty: bool, +) -> Result> +where + T: Clone + FromStr + Display + ToString, + T::Err: Display + std::fmt::Debug, +{ + if skip { + Ok(initial) + } else { + let theme = dialoguer::theme::ColorfulTheme::default(); + let mut builder = Input::with_theme(&theme); + builder.with_prompt(prompt); + builder.allow_empty(allow_empty); + + if let Some(v) = initial { + builder.with_initial_text(v.to_string()); + } + + builder.interact_text().map(Some).map_err(Into::into) + } +} diff --git a/tooling/cli/templates/mobile/android/.gitignore b/tooling/cli/templates/mobile/android/.gitignore index 09f56a44322..9421ffb1aba 100644 --- a/tooling/cli/templates/mobile/android/.gitignore +++ b/tooling/cli/templates/mobile/android/.gitignore @@ -15,3 +15,6 @@ build .externalNativeBuild .cxx local.properties + +/tauri-plugins +/tauri-api diff --git a/tooling/cli/templates/mobile/android/app/build.gradle.kts b/tooling/cli/templates/mobile/android/app/build.gradle.kts index 48978e8d617..9b3d8f532fd 100644 --- a/tooling/cli/templates/mobile/android/app/build.gradle.kts +++ b/tooling/cli/templates/mobile/android/app/build.gradle.kts @@ -86,6 +86,7 @@ dependencies { testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.4") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") + implementation(project(":tauri-android")) } afterEvaluate { diff --git a/tooling/cli/templates/mobile/android/app/src/main/MainActivity.kt b/tooling/cli/templates/mobile/android/app/src/main/MainActivity.kt index 8425f5e2eee..b099c29ea62 100644 --- a/tooling/cli/templates/mobile/android/app/src/main/MainActivity.kt +++ b/tooling/cli/templates/mobile/android/app/src/main/MainActivity.kt @@ -1,3 +1,7 @@ package {{reverse-domain app.domain}}.{{snake-case app.name}} -class MainActivity : TauriActivity() +import app.tauri.plugin.PluginManager + +class MainActivity : TauriActivity() { + var pluginManager: PluginManager = PluginManager() +} diff --git a/tooling/cli/templates/mobile/android/settings.gradle b/tooling/cli/templates/mobile/android/settings.gradle index 74680c9c1bd..7708e58014d 100644 --- a/tooling/cli/templates/mobile/android/settings.gradle +++ b/tooling/cli/templates/mobile/android/settings.gradle @@ -1,3 +1,6 @@ include ':app' {{~#each asset-packs}} include ':{{this}}'{{/each}} + +include ':tauri-android' +project(':tauri-android').projectDir = new File('./tauri-api') diff --git a/tooling/cli/templates/plugin/android/.gitignore b/tooling/cli/templates/plugin/android/.gitignore new file mode 100644 index 00000000000..42afabfd2ab --- /dev/null +++ b/tooling/cli/templates/plugin/android/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/tooling/cli/templates/plugin/android/build.gradle.kts b/tooling/cli/templates/plugin/android/build.gradle.kts new file mode 100644 index 00000000000..83f86a9a9f0 --- /dev/null +++ b/tooling/cli/templates/plugin/android/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "com.plugin.test" + compileSdk = 32 + + defaultConfig { + minSdk = 24 + targetSdk = 32 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.appcompat:appcompat:1.6.0") + implementation("com.google.android.material:material:1.7.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + implementation(project(":tauri-android")) +} \ No newline at end of file diff --git a/tooling/cli/templates/plugin/android/consumer-rules.pro b/tooling/cli/templates/plugin/android/consumer-rules.pro new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tooling/cli/templates/plugin/android/proguard-rules.pro b/tooling/cli/templates/plugin/android/proguard-rules.pro new file mode 100644 index 00000000000..481bb434814 --- /dev/null +++ b/tooling/cli/templates/plugin/android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/tooling/cli/templates/plugin/android/settings.gradle b/tooling/cli/templates/plugin/android/settings.gradle new file mode 100644 index 00000000000..32f9aac23d1 --- /dev/null +++ b/tooling/cli/templates/plugin/android/settings.gradle @@ -0,0 +1,2 @@ +include ':tauri-android' +project(':tauri-android').projectDir = new File('./tauri-api') diff --git a/tooling/cli/templates/plugin/android/src/androidTest/java/com/plugin/test/ExampleInstrumentedTest.kt b/tooling/cli/templates/plugin/android/src/androidTest/java/com/plugin/test/ExampleInstrumentedTest.kt new file mode 100644 index 00000000000..1010bda3ea9 --- /dev/null +++ b/tooling/cli/templates/plugin/android/src/androidTest/java/com/plugin/test/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.plugin.test + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.plugin.test.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/tooling/cli/templates/plugin/android/src/main/AndroidManifest.xml b/tooling/cli/templates/plugin/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..a5918e68abc --- /dev/null +++ b/tooling/cli/templates/plugin/android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/tooling/cli/templates/plugin/android/src/main/Example.kt b/tooling/cli/templates/plugin/android/src/main/Example.kt new file mode 100644 index 00000000000..5f9049c6c8e --- /dev/null +++ b/tooling/cli/templates/plugin/android/src/main/Example.kt @@ -0,0 +1,10 @@ +package {{package_id}} + +import android.util.Log + +class Example { + fun pong(value: String): String { + Log.i("Pong", value) + return value + } +} diff --git a/tooling/cli/templates/plugin/android/src/main/ExamplePlugin.kt b/tooling/cli/templates/plugin/android/src/main/ExamplePlugin.kt new file mode 100644 index 00000000000..b351381246b --- /dev/null +++ b/tooling/cli/templates/plugin/android/src/main/ExamplePlugin.kt @@ -0,0 +1,19 @@ +package {{package_id}} + +import android.app.Activity +import app.tauri.plugin.JSObject +import app.tauri.plugin.Plugin +import app.tauri.plugin.Invoke +import app.tauri.plugin.PluginMethod + +class ExamplePlugin(private val activity: Activity): Plugin() { + private val implementation = Example() + + @PluginMethod + fun ping(invoke: Invoke) { + val value = invoke.getString("value") ?: "" + val ret = JSObject() + ret.put("value", implementation.pong(value)) + invoke.resolve(ret) + } +} diff --git a/tooling/cli/templates/plugin/android/src/test/java/com/plugin/test/ExampleUnitTest.kt b/tooling/cli/templates/plugin/android/src/test/java/com/plugin/test/ExampleUnitTest.kt new file mode 100644 index 00000000000..62cdd277efb --- /dev/null +++ b/tooling/cli/templates/plugin/android/src/test/java/com/plugin/test/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.plugin.test + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file From a81750d779bc72f0fdb7de90b7fbddfd8049b328 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 8 Feb 2023 00:38:10 +0200 Subject: [PATCH 134/423] feat(core): add shadow APIs (#6206) Co-authored-by: Lucas Nogueira --- .changes/shadow-api.md | 5 + .changes/shadow-config.md | 5 + .changes/shadow.md | 7 + core/config-schema/schema.json | 13 + core/tauri-runtime-wry/Cargo.toml | 8 +- core/tauri-runtime-wry/src/lib.rs | 29 +- core/tauri-runtime/Cargo.toml | 8 +- core/tauri-runtime/src/lib.rs | 5 +- core/tauri-runtime/src/webview.rs | 12 + core/tauri-utils/Cargo.toml | 2 +- core/tauri-utils/src/config.rs | 99 ++-- core/tauri/Cargo.toml | 10 +- core/tauri/build.rs | 1 + core/tauri/scripts/bundle.global.js | 8 +- core/tauri/src/endpoints/window.rs | 5 + core/tauri/src/lib.rs | 1 + core/tauri/src/test/mock_runtime.rs | 8 + core/tauri/src/window.rs | 32 ++ examples/api/dist/assets/index.css | 2 +- examples/api/dist/assets/index.js | 80 +-- examples/api/src-tauri/Cargo.lock | 680 +++++++++++++------------- examples/api/src-tauri/Cargo.toml | 4 - examples/api/src-tauri/src/lib.rs | 12 +- examples/api/src/App.svelte | 4 +- examples/splashscreen/tauri.conf.json | 1 + tooling/api/src/window.ts | 82 +++- tooling/cli/Cargo.lock | 553 +++++++++++---------- tooling/cli/schema.json | 13 + 28 files changed, 950 insertions(+), 739 deletions(-) create mode 100644 .changes/shadow-api.md create mode 100644 .changes/shadow-config.md create mode 100644 .changes/shadow.md diff --git a/.changes/shadow-api.md b/.changes/shadow-api.md new file mode 100644 index 00000000000..ce9337b22d0 --- /dev/null +++ b/.changes/shadow-api.md @@ -0,0 +1,5 @@ +--- +'api': minor +--- + +Added the `shadow` option when creating a window and `setShadow` function. diff --git a/.changes/shadow-config.md b/.changes/shadow-config.md new file mode 100644 index 00000000000..9b922b9df3d --- /dev/null +++ b/.changes/shadow-config.md @@ -0,0 +1,5 @@ +--- +'tauri-utils': minor +--- + +Added the `shadow` option to the window configuration and `set_shadow` option to the `window` allow list. diff --git a/.changes/shadow.md b/.changes/shadow.md new file mode 100644 index 00000000000..439213262d4 --- /dev/null +++ b/.changes/shadow.md @@ -0,0 +1,7 @@ +--- +'tauri': minor +'tauri-runtime-wry': minor +'tauri-runtime': minor +--- + +Added the `shadow` option when creating a window and `Window::set_shadow`. diff --git a/core/config-schema/schema.json b/core/config-schema/schema.json index b439d0d218d..f9bdc4030c9 100644 --- a/core/config-schema/schema.json +++ b/core/config-schema/schema.json @@ -118,6 +118,7 @@ "setMinSize": false, "setPosition": false, "setResizable": false, + "setShadow": false, "setSize": false, "setSkipTaskbar": false, "setTitle": false, @@ -393,6 +394,7 @@ "setMinSize": false, "setPosition": false, "setResizable": false, + "setShadow": false, "setSize": false, "setSkipTaskbar": false, "setTitle": false, @@ -684,6 +686,11 @@ "string", "null" ] + }, + "shadow": { + "description": "Whether or not the window has shadow.\n\n## Platform-specific\n\n- **Windows:** - `false` has no effect on decorated window, shadow are always ON. - `true` will make ndecorated window have a 1px white border, and on Windows 11, it will have a rounded corners. - **Linux:** Unsupported.", + "default": false, + "type": "boolean" } }, "additionalProperties": false @@ -1699,6 +1706,7 @@ "setMinSize": false, "setPosition": false, "setResizable": false, + "setShadow": false, "setSize": false, "setSkipTaskbar": false, "setTitle": false, @@ -2031,6 +2039,11 @@ "default": false, "type": "boolean" }, + "setShadow": { + "description": "Allows setting the shadow flag of the window.", + "default": false, + "type": "boolean" + }, "setAlwaysOnTop": { "description": "Allows setting the always_on_top flag of the window.", "default": false, diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index eb0e59a4c39..7d0619f84e0 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -21,11 +21,11 @@ rand = "0.8" raw-window-handle = "0.5" [target."cfg(windows)".dependencies] -webview2-com = "0.19.1" +webview2-com = "0.22" - [target."cfg(windows)".dependencies.windows] - version = "0.39.0" - features = [ "Win32_Foundation" ] +[target."cfg(windows)".dependencies.windows] +version = "0.44" +features = [ "Win32_Foundation" ] [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] gtk = { version = "0.16", features = [ "v3_24" ] } diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 2c6df681ce0..1a7bd6807e6 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -741,7 +741,8 @@ impl WindowBuilder for WindowBuilderWrapper { .maximized(config.maximized) .always_on_top(config.always_on_top) .skip_taskbar(config.skip_taskbar) - .theme(config.theme); + .theme(config.theme) + .shadow(config.shadow); if let (Some(min_width), Some(min_height)) = (config.min_width, config.min_height) { window = window.min_inner_size(min_width, min_height); @@ -849,6 +850,18 @@ impl WindowBuilder for WindowBuilderWrapper { self } + fn shadow(#[allow(unused_mut)] mut self, _enable: bool) -> Self { + #[cfg(windows)] + { + self.inner = self.inner.with_undecorated_shadow(_enable); + } + #[cfg(target_os = "macos")] + { + self.inner = self.inner.with_has_shadow(_enable); + } + self + } + #[cfg(windows)] fn parent_window(mut self, parent: HWND) -> Self { self.inner = self.inner.with_parent_window(parent); @@ -1066,6 +1079,7 @@ pub enum WindowMessage { Hide, Close, SetDecorations(bool), + SetShadow(bool), SetAlwaysOnTop(bool), SetSize(Size), SetMinSize(Option), @@ -1430,6 +1444,13 @@ impl Dispatch for WryDispatcher { ) } + fn set_shadow(&self, enable: bool) -> Result<()> { + send_user_message( + &self.context, + Message::Window(self.window_id, WindowMessage::SetShadow(enable)), + ) + } + fn set_always_on_top(&self, always_on_top: bool) -> Result<()> { send_user_message( &self.context, @@ -2420,6 +2441,12 @@ fn handle_user_message( panic!("cannot handle `WindowMessage::Close` on the main thread") } WindowMessage::SetDecorations(decorations) => window.set_decorations(decorations), + WindowMessage::SetShadow(_enable) => { + #[cfg(windows)] + window.set_undecorated_shadow(_enable); + #[cfg(target_os = "macos")] + window.set_has_shadow(_enable); + } WindowMessage::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top), WindowMessage::SetSize(size) => { window.set_inner_size(SizeWrapper::from(size).0); diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml index 27eace6c81d..2a392c406f7 100644 --- a/core/tauri-runtime/Cargo.toml +++ b/core/tauri-runtime/Cargo.toml @@ -34,11 +34,11 @@ raw-window-handle = "0.5" rand = "0.8" [target."cfg(windows)".dependencies] -webview2-com = "0.19.1" +webview2-com = "0.22" - [target."cfg(windows)".dependencies.windows] - version = "0.39.0" - features = [ "Win32_Foundation" ] +[target."cfg(windows)".dependencies.windows] +version = "0.44" +features = [ "Win32_Foundation" ] [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] gtk = { version = "0.16", features = [ "v3_24" ] } diff --git a/core/tauri-runtime/src/lib.rs b/core/tauri-runtime/src/lib.rs index 6cef8289a02..1fe35d929d2 100644 --- a/core/tauri-runtime/src/lib.rs +++ b/core/tauri-runtime/src/lib.rs @@ -639,9 +639,12 @@ pub trait Dispatch: Debug + Clone + Send + Sync + Sized + 'static /// Closes the window. fn close(&self) -> Result<()>; - /// Updates the hasDecorations flag. + /// Updates the decorations flag. fn set_decorations(&self, decorations: bool) -> Result<()>; + /// Updates the shadow flag. + fn set_shadow(&self, enable: bool) -> Result<()>; + /// Updates the window alwaysOnTop flag. fn set_always_on_top(&self, always_on_top: bool) -> Result<()>; diff --git a/core/tauri-runtime/src/webview.rs b/core/tauri-runtime/src/webview.rs index 8b6a0a6940c..db9c8dfcc65 100644 --- a/core/tauri-runtime/src/webview.rs +++ b/core/tauri-runtime/src/webview.rs @@ -179,6 +179,18 @@ pub trait WindowBuilder: WindowBuilderBase { #[must_use] fn skip_taskbar(self, skip: bool) -> Self; + /// Sets whether or not the window has shadow. + /// + /// ## Platform-specific + /// + /// - **Windows:** + /// - `false` has no effect on decorated window, shadows are always ON. + /// - `true` will make ndecorated window have a 1px white border, + /// and on Windows 11, it will have a rounded corners. + /// - **Linux:** Unsupported. + #[must_use] + fn shadow(self, enable: bool) -> Self; + /// Sets a parent to the window to be created. /// /// A child window has the WS_CHILD style and is confined to the client area of its parent window. diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml index 01436199866..f7724a5e53c 100644 --- a/core/tauri-utils/Cargo.toml +++ b/core/tauri-utils/Cargo.toml @@ -41,7 +41,7 @@ infer = "0.7" heck = "0.4" [target."cfg(windows)".dependencies.windows] -version = "0.39.0" +version = "0.44.0" features = [ "implement", "Win32_Foundation", diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 6ae0deb36e3..65f97a38851 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -431,7 +431,7 @@ pub enum WebviewInstallMode { /// Results in a smaller installer size, but is not recommended on Windows 7. DownloadBootstrapper { /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`. - #[serde(default = "default_webview_install_silent")] + #[serde(default = "default_true")] silent: bool, }, /// Embed the bootstrapper and run it. @@ -439,7 +439,7 @@ pub enum WebviewInstallMode { /// Increases the installer size by around 1.8MB, but offers better support on Windows 7. EmbedBootstrapper { /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`. - #[serde(default = "default_webview_install_silent")] + #[serde(default = "default_true")] silent: bool, }, /// Embed the offline installer and run it. @@ -447,7 +447,7 @@ pub enum WebviewInstallMode { /// Increases the installer size by around 127MB. OfflineInstaller { /// Instructs the installer to run the installer in silent mode. Defaults to `true`. - #[serde(default = "default_webview_install_silent")] + #[serde(default = "default_true")] silent: bool, }, /// Embed a fixed webview2 version and use it at runtime. @@ -461,14 +461,10 @@ pub enum WebviewInstallMode { }, } -fn default_webview_install_silent() -> bool { - true -} - impl Default for WebviewInstallMode { fn default() -> Self { Self::DownloadBootstrapper { - silent: default_webview_install_silent(), + silent: default_true(), } } } @@ -508,7 +504,7 @@ pub struct WindowsConfig { /// For instance, if `1.2.1` is installed, the user won't be able to install app version `1.2.0` or `1.1.5`. /// /// The default value of this flag is `true`. - #[serde(default = "default_allow_downgrades", alias = "allow-downgrades")] + #[serde(default = "default_true", alias = "allow-downgrades")] pub allow_downgrades: bool, /// Configuration for the MSI generated with WiX. pub wix: Option, @@ -523,16 +519,12 @@ impl Default for WindowsConfig { tsp: false, webview_install_mode: Default::default(), webview_fixed_runtime_path: None, - allow_downgrades: default_allow_downgrades(), + allow_downgrades: default_true(), wix: None, } } } -fn default_allow_downgrades() -> bool { - true -} - /// Configuration for tauri-bundler. #[skip_serializing_none] #[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] @@ -805,7 +797,7 @@ pub struct WindowConfig { /// Whether the file drop is enabled or not on the webview. By default it is enabled. /// /// Disabling it is required to use drag and drop on the frontend on Windows. - #[serde(default = "default_file_drop_enabled", alias = "file-drop-enabled")] + #[serde(default = "default_true", alias = "file-drop-enabled")] pub file_drop_enabled: bool, /// Whether or not the window starts centered or not. #[serde(default)] @@ -833,7 +825,7 @@ pub struct WindowConfig { #[serde(alias = "max-height")] pub max_height: Option, /// Whether the window is resizable or not. - #[serde(default = "default_resizable")] + #[serde(default = "default_true")] pub resizable: bool, /// The window title. #[serde(default = "default_title")] @@ -842,7 +834,7 @@ pub struct WindowConfig { #[serde(default)] pub fullscreen: bool, /// Whether the window will be initially focused or not. - #[serde(default = "default_focus")] + #[serde(default = "default_true")] pub focus: bool, /// Whether the window is transparent or not. /// @@ -854,10 +846,10 @@ pub struct WindowConfig { #[serde(default)] pub maximized: bool, /// Whether the window is visible or not. - #[serde(default = "default_visible")] + #[serde(default = "default_true")] pub visible: bool, /// Whether the window should have borders and bars. - #[serde(default = "default_decorations")] + #[serde(default = "default_true")] pub decorations: bool, /// Whether the window should always be on top of other windows. #[serde(default, alias = "always-on-top")] @@ -884,6 +876,17 @@ pub struct WindowConfig { /// [tabbing identifier]: #[serde(default, alias = "tabbing-identifier")] pub tabbing_identifier: Option, + /// Whether or not the window has shadow. + /// + /// ## Platform-specific + /// + /// - **Windows:** + /// - `false` has no effect on decorated window, shadow are always ON. + /// - `true` will make ndecorated window have a 1px white border, + /// and on Windows 11, it will have a rounded corners. + /// - **Linux:** Unsupported. + #[serde(default)] + pub shadow: bool, } impl Default for WindowConfig { @@ -892,7 +895,7 @@ impl Default for WindowConfig { label: default_window_label(), url: WindowUrl::default(), user_agent: None, - file_drop_enabled: default_file_drop_enabled(), + file_drop_enabled: default_true(), center: false, x: None, y: None, @@ -902,14 +905,14 @@ impl Default for WindowConfig { min_height: None, max_width: None, max_height: None, - resizable: default_resizable(), + resizable: default_true(), title: default_title(), fullscreen: false, focus: false, transparent: false, maximized: false, - visible: default_visible(), - decorations: default_decorations(), + visible: default_true(), + decorations: default_true(), always_on_top: false, skip_taskbar: false, theme: None, @@ -917,6 +920,7 @@ impl Default for WindowConfig { hidden_title: false, accept_first_mouse: false, tabbing_identifier: None, + shadow: false, } } } @@ -933,7 +937,7 @@ fn default_height() -> f64 { 600f64 } -fn default_resizable() -> bool { +fn default_true() -> bool { true } @@ -941,22 +945,6 @@ fn default_title() -> String { "Tauri App".to_string() } -fn default_focus() -> bool { - true -} - -fn default_visible() -> bool { - true -} - -fn default_decorations() -> bool { - true -} - -fn default_file_drop_enabled() -> bool { - true -} - /// A Content-Security-Policy directive source list. /// See . #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] @@ -1329,6 +1317,9 @@ pub struct WindowAllowlistConfig { /// Allows setting the decorations flag of the window. #[serde(default, alias = "set-decorations")] pub set_decorations: bool, + /// Allows setting the shadow flag of the window. + #[serde(default, alias = "set-shadow")] + pub set_shadow: bool, /// Allows setting the always_on_top flag of the window. #[serde(default, alias = "set-always-on-top")] pub set_always_on_top: bool, @@ -1410,6 +1401,7 @@ impl Allowlist for WindowAllowlistConfig { set_cursor_icon: true, set_cursor_position: true, set_ignore_cursor_events: true, + set_shadow: true, start_dragging: true, print: true, }; @@ -1441,6 +1433,7 @@ impl Allowlist for WindowAllowlistConfig { check_feature!(self, features, hide, "window-hide"); check_feature!(self, features, close, "window-close"); check_feature!(self, features, set_decorations, "window-set-decorations"); + check_feature!(self, features, set_shadow, "window-set-shadow"); check_feature!( self, features, @@ -2376,7 +2369,7 @@ pub struct UpdaterConfig { #[serde(default)] pub active: bool, /// Display built-in dialog or use event system if disabled. - #[serde(default = "default_dialog")] + #[serde(default = "default_true")] pub dialog: bool, /// The updater endpoints. TLS is enforced on production. /// @@ -2407,7 +2400,7 @@ impl<'de> Deserialize<'de> for UpdaterConfig { struct InnerUpdaterConfig { #[serde(default)] active: bool, - #[serde(default = "default_dialog")] + #[serde(default = "default_true")] dialog: bool, endpoints: Option>, pubkey: Option, @@ -2437,7 +2430,7 @@ impl Default for UpdaterConfig { fn default() -> Self { Self { active: false, - dialog: default_dialog(), + dialog: default_true(), endpoints: None, pubkey: "".into(), windows: Default::default(), @@ -2458,26 +2451,12 @@ pub struct SystemTrayConfig { #[serde(default, alias = "icon-as-template")] pub icon_as_template: bool, /// A Boolean value that determines whether the menu should appear when the tray icon receives a left click on macOS. - #[serde( - default = "default_tray_menu_on_left_click", - alias = "menu-on-left-click" - )] + #[serde(default = "default_true", alias = "menu-on-left-click")] pub menu_on_left_click: bool, /// Title for MacOS tray pub title: Option, } -fn default_tray_menu_on_left_click() -> bool { - true -} - -// We enable the unnecessary_wraps because we need -// to use an Option for dialog otherwise the CLI schema will mark -// the dialog as a required field which is not as we default it to true. -fn default_dialog() -> bool { - true -} - /// General configuration for the iOS target. #[skip_serializing_none] #[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] @@ -3060,6 +3039,7 @@ mod build { let hidden_title = self.hidden_title; let accept_first_mouse = self.accept_first_mouse; let tabbing_identifier = opt_str_lit(self.tabbing_identifier.as_ref()); + let shadow = self.shadow; literal_struct!( tokens, @@ -3091,7 +3071,8 @@ mod build { title_bar_style, hidden_title, accept_first_mouse, - tabbing_identifier + tabbing_identifier, + shadow ); } } diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index bb7a40a298f..50be52a2298 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -101,12 +101,12 @@ cocoa = "0.24" objc = "0.2" [target."cfg(windows)".dependencies] -webview2-com = "0.19.1" +webview2-com = "0.22" win7-notifications = { version = "0.3.1", optional = true } - [target."cfg(windows)".dependencies.windows] - version = "0.39.0" - features = [ "Win32_Foundation" ] +[target."cfg(windows)".dependencies.windows] +version = "0.44" +features = [ "Win32_Foundation" ] [target."cfg(target_os = \"android\")".dependencies] paste = "1.0" @@ -250,6 +250,7 @@ window-all = [ "window-hide", "window-close", "window-set-decorations", + "window-set-shadow", "window-set-always-on-top", "window-set-size", "window-set-min-size", @@ -280,6 +281,7 @@ window-show = [ ] window-hide = [ ] window-close = [ ] window-set-decorations = [ ] +window-set-shadow = [ ] window-set-always-on-top = [ ] window-set-size = [ ] window-set-min-size = [ ] diff --git a/core/tauri/build.rs b/core/tauri/build.rs index 59208e9ec46..7e6a6083e17 100644 --- a/core/tauri/build.rs +++ b/core/tauri/build.rs @@ -80,6 +80,7 @@ fn main() { "hide", "close", "set-decorations", + "set-shadow", "set-always-on-top", "set-size", "set-min-size", diff --git a/core/tauri/scripts/bundle.global.js b/core/tauri/scripts/bundle.global.js index 711c6ef9e88..9b125f1afb8 100644 --- a/core/tauri/scripts/bundle.global.js +++ b/core/tauri/scripts/bundle.global.js @@ -1,8 +1,8 @@ -"use strict";var __TAURI_IIFE__=(()=>{var L=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var me=Object.getOwnPropertyNames;var pe=Object.prototype.hasOwnProperty;var c=(t,e)=>{for(var n in e)L(t,n,{get:e[n],enumerable:!0})},ge=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of me(e))!pe.call(t,s)&&s!==n&&L(t,s,{get:()=>e[s],enumerable:!(r=de(e,s))||r.enumerable});return t};var he=t=>ge(L({},"__esModule",{value:!0}),t);var Jt={};c(Jt,{app:()=>k,cli:()=>U,clipboard:()=>I,dialog:()=>N,event:()=>V,fs:()=>j,globalShortcut:()=>q,http:()=>$,invoke:()=>$t,notification:()=>J,os:()=>ne,path:()=>K,process:()=>Q,shell:()=>Z,tauri:()=>R,updater:()=>X,window:()=>te});var k={};c(k,{getName:()=>be,getTauriVersion:()=>Pe,getVersion:()=>_e,hide:()=>ve,show:()=>we});var R={};c(R,{convertFileSrc:()=>fe,invoke:()=>f,transformCallback:()=>d});function ye(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function d(t,e=!1){let n=ye(),r=`_${n}`;return Object.defineProperty(window,r,{value:s=>(e&&Reflect.deleteProperty(window,r),t?.(s)),writable:!1,configurable:!0}),n}async function f(t,e={}){return new Promise((n,r)=>{let s=d(l=>{n(l),Reflect.deleteProperty(window,`_${a}`)},!0),a=d(l=>{r(l),Reflect.deleteProperty(window,`_${s}`)},!0);window.__TAURI_IPC__({cmd:t,callback:s,error:a,...e})})}function fe(t,e="asset"){let n=encodeURIComponent(t);return navigator.userAgent.includes("Windows")?`https://${e}.localhost/${n}`:`${e}://localhost/${n}`}async function i(t){return f("tauri",t)}async function _e(){return i({__tauriModule:"App",message:{cmd:"getAppVersion"}})}async function be(){return i({__tauriModule:"App",message:{cmd:"getAppName"}})}async function Pe(){return i({__tauriModule:"App",message:{cmd:"getTauriVersion"}})}async function we(){return i({__tauriModule:"App",message:{cmd:"show"}})}async function ve(){return i({__tauriModule:"App",message:{cmd:"hide"}})}var U={};c(U,{getMatches:()=>Me});async function Me(){return i({__tauriModule:"Cli",message:{cmd:"cliMatches"}})}var I={};c(I,{readText:()=>Oe,writeText:()=>Te});async function Te(t){return i({__tauriModule:"Clipboard",message:{cmd:"writeText",data:t}})}async function Oe(){return i({__tauriModule:"Clipboard",message:{cmd:"readText",data:null}})}var N={};c(N,{ask:()=>Ce,confirm:()=>De,message:()=>Ae,open:()=>Fe,save:()=>Ee});async function Fe(t={}){return typeof t=="object"&&Object.freeze(t),i({__tauriModule:"Dialog",message:{cmd:"openDialog",options:t}})}async function Ee(t={}){return typeof t=="object"&&Object.freeze(t),i({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:t}})}async function Ae(t,e){let n=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:t.toString(),title:n?.title?.toString(),type:n?.type}})}async function Ce(t,e){let n=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"askDialog",message:t.toString(),title:n?.title?.toString(),type:n?.type}})}async function De(t,e){let n=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"confirmDialog",message:t.toString(),title:n?.title?.toString(),type:n?.type}})}var V={};c(V,{TauriEvent:()=>M,emit:()=>T,listen:()=>z,once:()=>H});async function ie(t,e){return i({__tauriModule:"Event",message:{cmd:"unlisten",event:t,eventId:e}})}async function w(t,e,n){await i({__tauriModule:"Event",message:{cmd:"emit",event:t,windowLabel:e,payload:n}})}async function _(t,e,n){return i({__tauriModule:"Event",message:{cmd:"listen",event:t,windowLabel:e,handler:d(n)}}).then(r=>async()=>ie(t,r))}async function v(t,e,n){return _(t,e,r=>{n(r),ie(t,r.id).catch(()=>{})})}var M=(u=>(u.WINDOW_RESIZED="tauri://resize",u.WINDOW_MOVED="tauri://move",u.WINDOW_CLOSE_REQUESTED="tauri://close-requested",u.WINDOW_CREATED="tauri://window-created",u.WINDOW_DESTROYED="tauri://destroyed",u.WINDOW_FOCUS="tauri://focus",u.WINDOW_BLUR="tauri://blur",u.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",u.WINDOW_THEME_CHANGED="tauri://theme-changed",u.WINDOW_FILE_DROP="tauri://file-drop",u.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",u.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",u.MENU="tauri://menu",u.CHECK_UPDATE="tauri://update",u.UPDATE_AVAILABLE="tauri://update-available",u.INSTALL_UPDATE="tauri://update-install",u.STATUS_UPDATE="tauri://update-status",u.DOWNLOAD_PROGRESS="tauri://update-download-progress",u))(M||{});async function z(t,e){return _(t,null,e)}async function H(t,e){return v(t,null,e)}async function T(t,e){return w(t,void 0,e)}var j={};c(j,{BaseDirectory:()=>O,Dir:()=>O,copyFile:()=>Ne,createDir:()=>Ue,exists:()=>Ve,readBinaryFile:()=>xe,readDir:()=>ke,readTextFile:()=>We,removeDir:()=>Ie,removeFile:()=>ze,renameFile:()=>He,writeBinaryFile:()=>Re,writeFile:()=>Le,writeTextFile:()=>Le});var O=(o=>(o[o.Audio=1]="Audio",o[o.Cache=2]="Cache",o[o.Config=3]="Config",o[o.Data=4]="Data",o[o.LocalData=5]="LocalData",o[o.Desktop=6]="Desktop",o[o.Document=7]="Document",o[o.Download=8]="Download",o[o.Executable=9]="Executable",o[o.Font=10]="Font",o[o.Home=11]="Home",o[o.Picture=12]="Picture",o[o.Public=13]="Public",o[o.Runtime=14]="Runtime",o[o.Template=15]="Template",o[o.Video=16]="Video",o[o.Resource=17]="Resource",o[o.App=18]="App",o[o.Log=19]="Log",o[o.Temp=20]="Temp",o[o.AppConfig=21]="AppConfig",o[o.AppData=22]="AppData",o[o.AppLocalData=23]="AppLocalData",o[o.AppCache=24]="AppCache",o[o.AppLog=25]="AppLog",o))(O||{});async function We(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"readTextFile",path:t,options:e}})}async function xe(t,e={}){let n=await i({__tauriModule:"Fs",message:{cmd:"readFile",path:t,options:e}});return Uint8Array.from(n)}async function Le(t,e,n){typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t);let r={path:"",contents:""},s=n;return typeof t=="string"?r.path=t:(r.path=t.path,r.contents=t.contents),typeof e=="string"?r.contents=e??"":s=e,i({__tauriModule:"Fs",message:{cmd:"writeFile",path:r.path,contents:Array.from(new TextEncoder().encode(r.contents)),options:s}})}async function Re(t,e,n){typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t);let r={path:"",contents:[]},s=n;return typeof t=="string"?r.path=t:(r.path=t.path,r.contents=t.contents),e&&"dir"in e?s=e:typeof t=="string"&&(r.contents=e??[]),i({__tauriModule:"Fs",message:{cmd:"writeFile",path:r.path,contents:Array.from(r.contents instanceof ArrayBuffer?new Uint8Array(r.contents):r.contents),options:s}})}async function ke(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"readDir",path:t,options:e}})}async function Ue(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"createDir",path:t,options:e}})}async function Ie(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"removeDir",path:t,options:e}})}async function Ne(t,e,n={}){return i({__tauriModule:"Fs",message:{cmd:"copyFile",source:t,destination:e,options:n}})}async function ze(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"removeFile",path:t,options:e}})}async function He(t,e,n={}){return i({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:t,newPath:e,options:n}})}async function Ve(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"exists",path:t,options:e}})}var q={};c(q,{isRegistered:()=>Ge,register:()=>je,registerAll:()=>qe,unregister:()=>$e,unregisterAll:()=>Je});async function je(t,e){return i({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:t,handler:d(e)}})}async function qe(t,e){return i({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:t,handler:d(e)}})}async function Ge(t){return i({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:t}})}async function $e(t){return i({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:t}})}async function Je(){return i({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}})}var $={};c($,{Body:()=>p,Client:()=>E,Response:()=>F,ResponseType:()=>re,fetch:()=>Ke,getClient:()=>se});var re=(r=>(r[r.JSON=1]="JSON",r[r.Text=2]="Text",r[r.Binary=3]="Binary",r))(re||{}),p=class{constructor(e,n){this.type=e,this.payload=n}static form(e){let n={},r=(s,a)=>{if(a!==null){let l;typeof a=="string"?l=a:a instanceof Uint8Array||Array.isArray(a)?l=Array.from(a):a instanceof File?l={file:a.name,mime:a.type,fileName:a.name}:typeof a.file=="string"?l={file:a.file,mime:a.mime,fileName:a.fileName}:l={file:Array.from(a.file),mime:a.mime,fileName:a.fileName},n[String(s)]=l}};if(e instanceof FormData)for(let[s,a]of e)r(s,a);else for(let[s,a]of Object.entries(e))r(s,a);return new p("Form",n)}static json(e){return new p("Json",e)}static text(e){return new p("Text",e)}static bytes(e){return new p("Bytes",Array.from(e instanceof ArrayBuffer?new Uint8Array(e):e))}},F=class{constructor(e){this.url=e.url,this.status=e.status,this.ok=this.status>=200&&this.status<300,this.headers=e.headers,this.rawHeaders=e.rawHeaders,this.data=e.data}},E=class{constructor(e){this.id=e}async drop(){return i({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}})}async request(e){let n=!e.responseType||e.responseType===1;return n&&(e.responseType=2),i({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:e}}).then(r=>{let s=new F(r);if(n){try{s.data=JSON.parse(s.data)}catch(a){if(s.ok&&s.data==="")s.data={};else if(s.ok)throw Error(`Failed to parse response \`${s.data}\` as JSON: ${a}; - try setting the \`responseType\` option to \`ResponseType.Text\` or \`ResponseType.Binary\` if the API does not return a JSON response.`)}return s}return s})}async get(e,n){return this.request({method:"GET",url:e,...n})}async post(e,n,r){return this.request({method:"POST",url:e,body:n,...r})}async put(e,n,r){return this.request({method:"PUT",url:e,body:n,...r})}async patch(e,n){return this.request({method:"PATCH",url:e,...n})}async delete(e,n){return this.request({method:"DELETE",url:e,...n})}};async function se(t){return i({__tauriModule:"Http",message:{cmd:"createClient",options:t}}).then(e=>new E(e))}var G=null;async function Ke(t,e){return G===null&&(G=await se()),G.request({url:t,method:e?.method??"GET",...e})}var J={};c(J,{isPermissionGranted:()=>Qe,requestPermission:()=>Ze,sendNotification:()=>Ye});async function Qe(){return window.Notification.permission!=="default"?Promise.resolve(window.Notification.permission==="granted"):i({__tauriModule:"Notification",message:{cmd:"isNotificationPermissionGranted"}})}async function Ze(){return window.Notification.requestPermission()}function Ye(t){typeof t=="string"?new window.Notification(t):new window.Notification(t.title,t)}var K={};c(K,{BaseDirectory:()=>O,appCacheDir:()=>tt,appConfigDir:()=>ae,appDataDir:()=>Be,appDir:()=>Xe,appLocalDataDir:()=>et,appLogDir:()=>oe,audioDir:()=>nt,basename:()=>At,cacheDir:()=>it,configDir:()=>rt,dataDir:()=>st,delimiter:()=>vt,desktopDir:()=>at,dirname:()=>Ft,documentDir:()=>ot,downloadDir:()=>lt,executableDir:()=>ut,extname:()=>Et,fontDir:()=>ct,homeDir:()=>dt,isAbsolute:()=>Ct,join:()=>Ot,localDataDir:()=>mt,logDir:()=>Pt,normalize:()=>Tt,pictureDir:()=>pt,publicDir:()=>gt,resolve:()=>Mt,resolveResource:()=>yt,resourceDir:()=>ht,runtimeDir:()=>ft,sep:()=>wt,templateDir:()=>_t,videoDir:()=>bt});function b(){return navigator.appVersion.includes("Win")}async function Xe(){return ae()}async function ae(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:21}})}async function Be(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:22}})}async function et(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:23}})}async function tt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:24}})}async function nt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:1}})}async function it(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:2}})}async function rt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:3}})}async function st(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:4}})}async function at(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:6}})}async function ot(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:7}})}async function lt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:8}})}async function ut(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:9}})}async function ct(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:10}})}async function dt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:11}})}async function mt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:5}})}async function pt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:12}})}async function gt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:13}})}async function ht(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:17}})}async function yt(t){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:t,directory:17}})}async function ft(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:14}})}async function _t(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:15}})}async function bt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:16}})}async function Pt(){return oe()}async function oe(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:25}})}var wt=b()?"\\":"/",vt=b()?";":":";async function Mt(...t){return i({__tauriModule:"Path",message:{cmd:"resolve",paths:t}})}async function Tt(t){return i({__tauriModule:"Path",message:{cmd:"normalize",path:t}})}async function Ot(...t){return i({__tauriModule:"Path",message:{cmd:"join",paths:t}})}async function Ft(t){return i({__tauriModule:"Path",message:{cmd:"dirname",path:t}})}async function Et(t){return i({__tauriModule:"Path",message:{cmd:"extname",path:t}})}async function At(t,e){return i({__tauriModule:"Path",message:{cmd:"basename",path:t,ext:e}})}async function Ct(t){return i({__tauriModule:"Path",message:{cmd:"isAbsolute",path:t}})}var Q={};c(Q,{exit:()=>Dt,relaunch:()=>St});async function Dt(t=0){return i({__tauriModule:"Process",message:{cmd:"exit",exitCode:t}})}async function St(){return i({__tauriModule:"Process",message:{cmd:"relaunch"}})}var Z={};c(Z,{Child:()=>A,Command:()=>P,EventEmitter:()=>g,open:()=>xt});async function Wt(t,e,n=[],r){return typeof n=="object"&&Object.freeze(n),i({__tauriModule:"Shell",message:{cmd:"execute",program:e,args:n,options:r,onEventFn:d(t)}})}var g=class{constructor(){this.eventListeners=Object.create(null)}addListener(e,n){return this.on(e,n)}removeListener(e,n){return this.off(e,n)}on(e,n){return e in this.eventListeners?this.eventListeners[e].push(n):this.eventListeners[e]=[n],this}once(e,n){let r=(...s)=>{this.removeListener(e,r),n(...s)};return this.addListener(e,r)}off(e,n){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter(r=>r!==n)),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,...n){if(e in this.eventListeners){let r=this.eventListeners[e];for(let s of r)s(...n);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,n){return e in this.eventListeners?this.eventListeners[e].unshift(n):this.eventListeners[e]=[n],this}prependOnceListener(e,n){let r=(...s)=>{this.removeListener(e,r),n(...s)};return this.prependListener(e,r)}},A=class{constructor(e){this.pid=e}async write(e){return i({__tauriModule:"Shell",message:{cmd:"stdinWrite",pid:this.pid,buffer:typeof e=="string"?e:Array.from(e)}})}async kill(){return i({__tauriModule:"Shell",message:{cmd:"killChild",pid:this.pid}})}},P=class extends g{constructor(n,r=[],s){super();this.stdout=new g;this.stderr=new g;this.program=n,this.args=typeof r=="string"?[r]:r,this.options=s??{}}static sidecar(n,r=[],s){let a=new P(n,r,s);return a.options.sidecar=!0,a}async spawn(){return Wt(n=>{switch(n.event){case"Error":this.emit("error",n.payload);break;case"Terminated":this.emit("close",n.payload);break;case"Stdout":this.stdout.emit("data",n.payload);break;case"Stderr":this.stderr.emit("data",n.payload);break}},this.program,this.args,this.options).then(n=>new A(n))}async execute(){return new Promise((n,r)=>{this.on("error",r);let s=[],a=[];this.stdout.on("data",l=>{s.push(l)}),this.stderr.on("data",l=>{a.push(l)}),this.on("close",l=>{n({code:l.code,signal:l.signal,stdout:s.join(` +"use strict";var __TAURI_IIFE__=(()=>{var L=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var me=Object.getOwnPropertyNames;var pe=Object.prototype.hasOwnProperty;var d=(t,e)=>{for(var n in e)L(t,n,{get:e[n],enumerable:!0})},ge=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of me(e))!pe.call(t,s)&&s!==n&&L(t,s,{get:()=>e[s],enumerable:!(r=ce(e,s))||r.enumerable});return t};var he=t=>ge(L({},"__esModule",{value:!0}),t);var Jt={};d(Jt,{app:()=>k,cli:()=>U,clipboard:()=>I,dialog:()=>N,event:()=>V,fs:()=>j,globalShortcut:()=>q,http:()=>$,invoke:()=>$t,notification:()=>J,os:()=>ne,path:()=>K,process:()=>Q,shell:()=>Z,tauri:()=>R,updater:()=>X,window:()=>te});var k={};d(k,{getName:()=>be,getTauriVersion:()=>Pe,getVersion:()=>_e,hide:()=>ve,show:()=>we});var R={};d(R,{convertFileSrc:()=>fe,invoke:()=>f,transformCallback:()=>c});function ye(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function c(t,e=!1){let n=ye(),r=`_${n}`;return Object.defineProperty(window,r,{value:s=>(e&&Reflect.deleteProperty(window,r),t?.(s)),writable:!1,configurable:!0}),n}async function f(t,e={}){return new Promise((n,r)=>{let s=c(l=>{n(l),Reflect.deleteProperty(window,`_${a}`)},!0),a=c(l=>{r(l),Reflect.deleteProperty(window,`_${s}`)},!0);window.__TAURI_IPC__({cmd:t,callback:s,error:a,...e})})}function fe(t,e="asset"){let n=encodeURIComponent(t);return navigator.userAgent.includes("Windows")?`https://${e}.localhost/${n}`:`${e}://localhost/${n}`}async function i(t){return f("tauri",t)}async function _e(){return i({__tauriModule:"App",message:{cmd:"getAppVersion"}})}async function be(){return i({__tauriModule:"App",message:{cmd:"getAppName"}})}async function Pe(){return i({__tauriModule:"App",message:{cmd:"getTauriVersion"}})}async function we(){return i({__tauriModule:"App",message:{cmd:"show"}})}async function ve(){return i({__tauriModule:"App",message:{cmd:"hide"}})}var U={};d(U,{getMatches:()=>Me});async function Me(){return i({__tauriModule:"Cli",message:{cmd:"cliMatches"}})}var I={};d(I,{readText:()=>Oe,writeText:()=>Te});async function Te(t){return i({__tauriModule:"Clipboard",message:{cmd:"writeText",data:t}})}async function Oe(){return i({__tauriModule:"Clipboard",message:{cmd:"readText",data:null}})}var N={};d(N,{ask:()=>Ce,confirm:()=>De,message:()=>Ae,open:()=>Fe,save:()=>Ee});async function Fe(t={}){return typeof t=="object"&&Object.freeze(t),i({__tauriModule:"Dialog",message:{cmd:"openDialog",options:t}})}async function Ee(t={}){return typeof t=="object"&&Object.freeze(t),i({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:t}})}async function Ae(t,e){let n=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:t.toString(),title:n?.title?.toString(),type:n?.type}})}async function Ce(t,e){let n=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"askDialog",message:t.toString(),title:n?.title?.toString(),type:n?.type}})}async function De(t,e){let n=typeof e=="string"?{title:e}:e;return i({__tauriModule:"Dialog",message:{cmd:"confirmDialog",message:t.toString(),title:n?.title?.toString(),type:n?.type}})}var V={};d(V,{TauriEvent:()=>M,emit:()=>T,listen:()=>z,once:()=>H});async function ie(t,e){return i({__tauriModule:"Event",message:{cmd:"unlisten",event:t,eventId:e}})}async function w(t,e,n){await i({__tauriModule:"Event",message:{cmd:"emit",event:t,windowLabel:e,payload:n}})}async function _(t,e,n){return i({__tauriModule:"Event",message:{cmd:"listen",event:t,windowLabel:e,handler:c(n)}}).then(r=>async()=>ie(t,r))}async function v(t,e,n){return _(t,e,r=>{n(r),ie(t,r.id).catch(()=>{})})}var M=(u=>(u.WINDOW_RESIZED="tauri://resize",u.WINDOW_MOVED="tauri://move",u.WINDOW_CLOSE_REQUESTED="tauri://close-requested",u.WINDOW_CREATED="tauri://window-created",u.WINDOW_DESTROYED="tauri://destroyed",u.WINDOW_FOCUS="tauri://focus",u.WINDOW_BLUR="tauri://blur",u.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",u.WINDOW_THEME_CHANGED="tauri://theme-changed",u.WINDOW_FILE_DROP="tauri://file-drop",u.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",u.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",u.MENU="tauri://menu",u.CHECK_UPDATE="tauri://update",u.UPDATE_AVAILABLE="tauri://update-available",u.INSTALL_UPDATE="tauri://update-install",u.STATUS_UPDATE="tauri://update-status",u.DOWNLOAD_PROGRESS="tauri://update-download-progress",u))(M||{});async function z(t,e){return _(t,null,e)}async function H(t,e){return v(t,null,e)}async function T(t,e){return w(t,void 0,e)}var j={};d(j,{BaseDirectory:()=>O,Dir:()=>O,copyFile:()=>Ne,createDir:()=>Ue,exists:()=>Ve,readBinaryFile:()=>xe,readDir:()=>ke,readTextFile:()=>We,removeDir:()=>Ie,removeFile:()=>ze,renameFile:()=>He,writeBinaryFile:()=>Re,writeFile:()=>Le,writeTextFile:()=>Le});var O=(o=>(o[o.Audio=1]="Audio",o[o.Cache=2]="Cache",o[o.Config=3]="Config",o[o.Data=4]="Data",o[o.LocalData=5]="LocalData",o[o.Desktop=6]="Desktop",o[o.Document=7]="Document",o[o.Download=8]="Download",o[o.Executable=9]="Executable",o[o.Font=10]="Font",o[o.Home=11]="Home",o[o.Picture=12]="Picture",o[o.Public=13]="Public",o[o.Runtime=14]="Runtime",o[o.Template=15]="Template",o[o.Video=16]="Video",o[o.Resource=17]="Resource",o[o.App=18]="App",o[o.Log=19]="Log",o[o.Temp=20]="Temp",o[o.AppConfig=21]="AppConfig",o[o.AppData=22]="AppData",o[o.AppLocalData=23]="AppLocalData",o[o.AppCache=24]="AppCache",o[o.AppLog=25]="AppLog",o))(O||{});async function We(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"readTextFile",path:t,options:e}})}async function xe(t,e={}){let n=await i({__tauriModule:"Fs",message:{cmd:"readFile",path:t,options:e}});return Uint8Array.from(n)}async function Le(t,e,n){typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t);let r={path:"",contents:""},s=n;return typeof t=="string"?r.path=t:(r.path=t.path,r.contents=t.contents),typeof e=="string"?r.contents=e??"":s=e,i({__tauriModule:"Fs",message:{cmd:"writeFile",path:r.path,contents:Array.from(new TextEncoder().encode(r.contents)),options:s}})}async function Re(t,e,n){typeof n=="object"&&Object.freeze(n),typeof t=="object"&&Object.freeze(t);let r={path:"",contents:[]},s=n;return typeof t=="string"?r.path=t:(r.path=t.path,r.contents=t.contents),e&&"dir"in e?s=e:typeof t=="string"&&(r.contents=e??[]),i({__tauriModule:"Fs",message:{cmd:"writeFile",path:r.path,contents:Array.from(r.contents instanceof ArrayBuffer?new Uint8Array(r.contents):r.contents),options:s}})}async function ke(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"readDir",path:t,options:e}})}async function Ue(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"createDir",path:t,options:e}})}async function Ie(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"removeDir",path:t,options:e}})}async function Ne(t,e,n={}){return i({__tauriModule:"Fs",message:{cmd:"copyFile",source:t,destination:e,options:n}})}async function ze(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"removeFile",path:t,options:e}})}async function He(t,e,n={}){return i({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:t,newPath:e,options:n}})}async function Ve(t,e={}){return i({__tauriModule:"Fs",message:{cmd:"exists",path:t,options:e}})}var q={};d(q,{isRegistered:()=>Ge,register:()=>je,registerAll:()=>qe,unregister:()=>$e,unregisterAll:()=>Je});async function je(t,e){return i({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:t,handler:c(e)}})}async function qe(t,e){return i({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:t,handler:c(e)}})}async function Ge(t){return i({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:t}})}async function $e(t){return i({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:t}})}async function Je(){return i({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}})}var $={};d($,{Body:()=>p,Client:()=>E,Response:()=>F,ResponseType:()=>re,fetch:()=>Ke,getClient:()=>se});var re=(r=>(r[r.JSON=1]="JSON",r[r.Text=2]="Text",r[r.Binary=3]="Binary",r))(re||{}),p=class{constructor(e,n){this.type=e,this.payload=n}static form(e){let n={},r=(s,a)=>{if(a!==null){let l;typeof a=="string"?l=a:a instanceof Uint8Array||Array.isArray(a)?l=Array.from(a):a instanceof File?l={file:a.name,mime:a.type,fileName:a.name}:typeof a.file=="string"?l={file:a.file,mime:a.mime,fileName:a.fileName}:l={file:Array.from(a.file),mime:a.mime,fileName:a.fileName},n[String(s)]=l}};if(e instanceof FormData)for(let[s,a]of e)r(s,a);else for(let[s,a]of Object.entries(e))r(s,a);return new p("Form",n)}static json(e){return new p("Json",e)}static text(e){return new p("Text",e)}static bytes(e){return new p("Bytes",Array.from(e instanceof ArrayBuffer?new Uint8Array(e):e))}},F=class{constructor(e){this.url=e.url,this.status=e.status,this.ok=this.status>=200&&this.status<300,this.headers=e.headers,this.rawHeaders=e.rawHeaders,this.data=e.data}},E=class{constructor(e){this.id=e}async drop(){return i({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}})}async request(e){let n=!e.responseType||e.responseType===1;return n&&(e.responseType=2),i({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:e}}).then(r=>{let s=new F(r);if(n){try{s.data=JSON.parse(s.data)}catch(a){if(s.ok&&s.data==="")s.data={};else if(s.ok)throw Error(`Failed to parse response \`${s.data}\` as JSON: ${a}; + try setting the \`responseType\` option to \`ResponseType.Text\` or \`ResponseType.Binary\` if the API does not return a JSON response.`)}return s}return s})}async get(e,n){return this.request({method:"GET",url:e,...n})}async post(e,n,r){return this.request({method:"POST",url:e,body:n,...r})}async put(e,n,r){return this.request({method:"PUT",url:e,body:n,...r})}async patch(e,n){return this.request({method:"PATCH",url:e,...n})}async delete(e,n){return this.request({method:"DELETE",url:e,...n})}};async function se(t){return i({__tauriModule:"Http",message:{cmd:"createClient",options:t}}).then(e=>new E(e))}var G=null;async function Ke(t,e){return G===null&&(G=await se()),G.request({url:t,method:e?.method??"GET",...e})}var J={};d(J,{isPermissionGranted:()=>Qe,requestPermission:()=>Ze,sendNotification:()=>Ye});async function Qe(){return window.Notification.permission!=="default"?Promise.resolve(window.Notification.permission==="granted"):i({__tauriModule:"Notification",message:{cmd:"isNotificationPermissionGranted"}})}async function Ze(){return window.Notification.requestPermission()}function Ye(t){typeof t=="string"?new window.Notification(t):new window.Notification(t.title,t)}var K={};d(K,{BaseDirectory:()=>O,appCacheDir:()=>tt,appConfigDir:()=>ae,appDataDir:()=>Be,appDir:()=>Xe,appLocalDataDir:()=>et,appLogDir:()=>oe,audioDir:()=>nt,basename:()=>At,cacheDir:()=>it,configDir:()=>rt,dataDir:()=>st,delimiter:()=>vt,desktopDir:()=>at,dirname:()=>Ft,documentDir:()=>ot,downloadDir:()=>lt,executableDir:()=>ut,extname:()=>Et,fontDir:()=>dt,homeDir:()=>ct,isAbsolute:()=>Ct,join:()=>Ot,localDataDir:()=>mt,logDir:()=>Pt,normalize:()=>Tt,pictureDir:()=>pt,publicDir:()=>gt,resolve:()=>Mt,resolveResource:()=>yt,resourceDir:()=>ht,runtimeDir:()=>ft,sep:()=>wt,templateDir:()=>_t,videoDir:()=>bt});function b(){return navigator.appVersion.includes("Win")}async function Xe(){return ae()}async function ae(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:21}})}async function Be(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:22}})}async function et(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:23}})}async function tt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:24}})}async function nt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:1}})}async function it(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:2}})}async function rt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:3}})}async function st(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:4}})}async function at(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:6}})}async function ot(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:7}})}async function lt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:8}})}async function ut(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:9}})}async function dt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:10}})}async function ct(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:11}})}async function mt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:5}})}async function pt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:12}})}async function gt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:13}})}async function ht(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:17}})}async function yt(t){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:t,directory:17}})}async function ft(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:14}})}async function _t(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:15}})}async function bt(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:16}})}async function Pt(){return oe()}async function oe(){return i({__tauriModule:"Path",message:{cmd:"resolvePath",path:"",directory:25}})}var wt=b()?"\\":"/",vt=b()?";":":";async function Mt(...t){return i({__tauriModule:"Path",message:{cmd:"resolve",paths:t}})}async function Tt(t){return i({__tauriModule:"Path",message:{cmd:"normalize",path:t}})}async function Ot(...t){return i({__tauriModule:"Path",message:{cmd:"join",paths:t}})}async function Ft(t){return i({__tauriModule:"Path",message:{cmd:"dirname",path:t}})}async function Et(t){return i({__tauriModule:"Path",message:{cmd:"extname",path:t}})}async function At(t,e){return i({__tauriModule:"Path",message:{cmd:"basename",path:t,ext:e}})}async function Ct(t){return i({__tauriModule:"Path",message:{cmd:"isAbsolute",path:t}})}var Q={};d(Q,{exit:()=>Dt,relaunch:()=>St});async function Dt(t=0){return i({__tauriModule:"Process",message:{cmd:"exit",exitCode:t}})}async function St(){return i({__tauriModule:"Process",message:{cmd:"relaunch"}})}var Z={};d(Z,{Child:()=>A,Command:()=>P,EventEmitter:()=>g,open:()=>xt});async function Wt(t,e,n=[],r){return typeof n=="object"&&Object.freeze(n),i({__tauriModule:"Shell",message:{cmd:"execute",program:e,args:n,options:r,onEventFn:c(t)}})}var g=class{constructor(){this.eventListeners=Object.create(null)}addListener(e,n){return this.on(e,n)}removeListener(e,n){return this.off(e,n)}on(e,n){return e in this.eventListeners?this.eventListeners[e].push(n):this.eventListeners[e]=[n],this}once(e,n){let r=(...s)=>{this.removeListener(e,r),n(...s)};return this.addListener(e,r)}off(e,n){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter(r=>r!==n)),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,...n){if(e in this.eventListeners){let r=this.eventListeners[e];for(let s of r)s(...n);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,n){return e in this.eventListeners?this.eventListeners[e].unshift(n):this.eventListeners[e]=[n],this}prependOnceListener(e,n){let r=(...s)=>{this.removeListener(e,r),n(...s)};return this.prependListener(e,r)}},A=class{constructor(e){this.pid=e}async write(e){return i({__tauriModule:"Shell",message:{cmd:"stdinWrite",pid:this.pid,buffer:typeof e=="string"?e:Array.from(e)}})}async kill(){return i({__tauriModule:"Shell",message:{cmd:"killChild",pid:this.pid}})}},P=class extends g{constructor(n,r=[],s){super();this.stdout=new g;this.stderr=new g;this.program=n,this.args=typeof r=="string"?[r]:r,this.options=s??{}}static sidecar(n,r=[],s){let a=new P(n,r,s);return a.options.sidecar=!0,a}async spawn(){return Wt(n=>{switch(n.event){case"Error":this.emit("error",n.payload);break;case"Terminated":this.emit("close",n.payload);break;case"Stdout":this.stdout.emit("data",n.payload);break;case"Stderr":this.stderr.emit("data",n.payload);break}},this.program,this.args,this.options).then(n=>new A(n))}async execute(){return new Promise((n,r)=>{this.on("error",r);let s=[],a=[];this.stdout.on("data",l=>{s.push(l)}),this.stderr.on("data",l=>{a.push(l)}),this.on("close",l=>{n({code:l.code,signal:l.signal,stdout:s.join(` `),stderr:a.join(` -`)})}),this.spawn().catch(r)})}};async function xt(t,e){return i({__tauriModule:"Shell",message:{cmd:"open",path:t,with:e}})}var X={};c(X,{checkUpdate:()=>Rt,installUpdate:()=>Lt,onUpdaterEvent:()=>Y});async function Y(t){return z("tauri://update-status",e=>{t(e?.payload)})}async function Lt(){let t;function e(){t&&t(),t=void 0}return new Promise((n,r)=>{function s(a){if(a.error)return e(),r(a.error);if(a.status==="DONE")return e(),n()}Y(s).then(a=>{t=a}).catch(a=>{throw e(),a}),T("tauri://update-install").catch(a=>{throw e(),a})})}async function Rt(){let t;function e(){t&&t(),t=void 0}return new Promise((n,r)=>{function s(l){return e(),n({manifest:l,shouldUpdate:!0})}function a(l){if(l.error)return e(),r(l.error);if(l.status==="UPTODATE")return e(),n({shouldUpdate:!1})}H("tauri://update-available",l=>{s(l?.payload)}).catch(l=>{throw e(),l}),Y(a).then(l=>{t=l}).catch(l=>{throw e(),l}),T("tauri://update").catch(l=>{throw e(),l})})}var te={};c(te,{CloseRequestedEvent:()=>x,LogicalPosition:()=>D,LogicalSize:()=>C,PhysicalPosition:()=>y,PhysicalSize:()=>h,UserAttentionType:()=>ue,WebviewWindow:()=>m,WebviewWindowHandle:()=>S,WindowManager:()=>W,appWindow:()=>B,availableMonitors:()=>Nt,currentMonitor:()=>Ut,getAll:()=>ce,getCurrent:()=>kt,primaryMonitor:()=>It});var C=class{constructor(e,n){this.type="Logical";this.width=e,this.height=n}},h=class{constructor(e,n){this.type="Physical";this.width=e,this.height=n}toLogical(e){return new C(this.width/e,this.height/e)}},D=class{constructor(e,n){this.type="Logical";this.x=e,this.y=n}},y=class{constructor(e,n){this.type="Physical";this.x=e,this.y=n}toLogical(e){return new D(this.x/e,this.y/e)}},ue=(n=>(n[n.Critical=1]="Critical",n[n.Informational=2]="Informational",n))(ue||{});function kt(){return new m(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0})}function ce(){return window.__TAURI_METADATA__.__windows.map(t=>new m(t.label,{skip:!0}))}var le=["tauri://created","tauri://error"],S=class{constructor(e){this.label=e,this.listeners=Object.create(null)}async listen(e,n){return this._handleTauriEvent(e,n)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(n),1)}):_(e,this.label,n)}async once(e,n){return this._handleTauriEvent(e,n)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(n),1)}):v(e,this.label,n)}async emit(e,n){if(le.includes(e)){for(let r of this.listeners[e]||[])r({event:e,id:-1,windowLabel:this.label,payload:n});return Promise.resolve()}return w(e,this.label,n)}_handleTauriEvent(e,n){return le.includes(e)?(e in this.listeners?this.listeners[e].push(n):this.listeners[e]=[n],!0):!1}},W=class extends S{async scaleFactor(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"scaleFactor"}}}})}async innerPosition(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerPosition"}}}}).then(({x:e,y:n})=>new y(e,n))}async outerPosition(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerPosition"}}}}).then(({x:e,y:n})=>new y(e,n))}async innerSize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerSize"}}}}).then(({width:e,height:n})=>new h(e,n))}async outerSize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerSize"}}}}).then(({width:e,height:n})=>new h(e,n))}async isFullscreen(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isFullscreen"}}}})}async isMaximized(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMaximized"}}}})}async isDecorated(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isDecorated"}}}})}async isResizable(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isResizable"}}}})}async isVisible(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isVisible"}}}})}async theme(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"theme"}}}})}async center(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"center"}}}})}async requestUserAttention(e){let n=null;return e&&(e===1?n={type:"Critical"}:n={type:"Informational"}),i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"requestUserAttention",payload:n}}}})}async setResizable(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setResizable",payload:e}}}})}async setTitle(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setTitle",payload:e}}}})}async maximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"maximize"}}}})}async unmaximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unmaximize"}}}})}async toggleMaximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"toggleMaximize"}}}})}async minimize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"minimize"}}}})}async unminimize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unminimize"}}}})}async show(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"show"}}}})}async hide(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"hide"}}}})}async close(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"close"}}}})}async setDecorations(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setDecorations",payload:e}}}})}async setAlwaysOnTop(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setAlwaysOnTop",payload:e}}}})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSize",payload:{type:e.type,data:{width:e.width,height:e.height}}}}}})}async setMinSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMinSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setMaxSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMaxSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setFullscreen(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFullscreen",payload:e}}}})}async setFocus(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFocus"}}}})}async setIcon(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIcon",payload:{icon:typeof e=="string"?e:Array.from(e)}}}}})}async setSkipTaskbar(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSkipTaskbar",payload:e}}}})}async setCursorGrab(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorGrab",payload:e}}}})}async setCursorVisible(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorVisible",payload:e}}}})}async setCursorIcon(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorIcon",payload:e}}}})}async setCursorPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setIgnoreCursorEvents(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIgnoreCursorEvents",payload:e}}}})}async startDragging(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"startDragging"}}}})}async onResized(e){return this.listen("tauri://resize",e)}async onMoved(e){return this.listen("tauri://move",e)}async onCloseRequested(e){return this.listen("tauri://close-requested",n=>{let r=new x(n);Promise.resolve(e(r)).then(()=>{if(!r.isPreventDefault())return this.close()})})}async onFocusChanged(e){let n=await this.listen("tauri://focus",s=>{e({...s,payload:!0})}),r=await this.listen("tauri://blur",s=>{e({...s,payload:!1})});return()=>{n(),r()}}async onScaleChanged(e){return this.listen("tauri://scale-change",e)}async onMenuClicked(e){return this.listen("tauri://menu",e)}async onFileDropEvent(e){let n=await this.listen("tauri://file-drop",a=>{e({...a,payload:{type:"drop",paths:a.payload}})}),r=await this.listen("tauri://file-drop-hover",a=>{e({...a,payload:{type:"hover",paths:a.payload}})}),s=await this.listen("tauri://file-drop-cancelled",a=>{e({...a,payload:{type:"cancel"}})});return()=>{n(),r(),s()}}async onThemeChanged(e){return this.listen("tauri://theme-changed",e)}},x=class{constructor(e){this._preventDefault=!1;this.event=e.event,this.windowLabel=e.windowLabel,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}},m=class extends W{constructor(e,n={}){super(e),n?.skip||i({__tauriModule:"Window",message:{cmd:"createWebview",data:{options:{label:e,...n}}}}).then(async()=>this.emit("tauri://created")).catch(async r=>this.emit("tauri://error",r))}static getByLabel(e){return ce().some(n=>n.label===e)?new m(e,{skip:!0}):null}},B;"__TAURI_METADATA__"in window?B=new m(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0}):(console.warn(`Could not find "window.__TAURI_METADATA__". The "appWindow" value will reference the "main" window label. -Note that this is not an issue if running this frontend on a browser instead of a Tauri window.`),B=new m("main",{skip:!0}));function ee(t){return t===null?null:{name:t.name,scaleFactor:t.scaleFactor,position:new y(t.position.x,t.position.y),size:new h(t.size.width,t.size.height)}}async function Ut(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"currentMonitor"}}}}).then(ee)}async function It(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"primaryMonitor"}}}}).then(ee)}async function Nt(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"availableMonitors"}}}}).then(t=>t.map(ee))}var ne={};c(ne,{EOL:()=>zt,arch:()=>qt,platform:()=>Ht,tempdir:()=>Gt,type:()=>jt,version:()=>Vt});var zt=b()?`\r +`)})}),this.spawn().catch(r)})}};async function xt(t,e){return i({__tauriModule:"Shell",message:{cmd:"open",path:t,with:e}})}var X={};d(X,{checkUpdate:()=>Rt,installUpdate:()=>Lt,onUpdaterEvent:()=>Y});async function Y(t){return z("tauri://update-status",e=>{t(e?.payload)})}async function Lt(){let t;function e(){t&&t(),t=void 0}return new Promise((n,r)=>{function s(a){if(a.error)return e(),r(a.error);if(a.status==="DONE")return e(),n()}Y(s).then(a=>{t=a}).catch(a=>{throw e(),a}),T("tauri://update-install").catch(a=>{throw e(),a})})}async function Rt(){let t;function e(){t&&t(),t=void 0}return new Promise((n,r)=>{function s(l){return e(),n({manifest:l,shouldUpdate:!0})}function a(l){if(l.error)return e(),r(l.error);if(l.status==="UPTODATE")return e(),n({shouldUpdate:!1})}H("tauri://update-available",l=>{s(l?.payload)}).catch(l=>{throw e(),l}),Y(a).then(l=>{t=l}).catch(l=>{throw e(),l}),T("tauri://update").catch(l=>{throw e(),l})})}var te={};d(te,{CloseRequestedEvent:()=>x,LogicalPosition:()=>D,LogicalSize:()=>C,PhysicalPosition:()=>y,PhysicalSize:()=>h,UserAttentionType:()=>ue,WebviewWindow:()=>m,WebviewWindowHandle:()=>S,WindowManager:()=>W,appWindow:()=>B,availableMonitors:()=>Nt,currentMonitor:()=>Ut,getAll:()=>de,getCurrent:()=>kt,primaryMonitor:()=>It});var C=class{constructor(e,n){this.type="Logical";this.width=e,this.height=n}},h=class{constructor(e,n){this.type="Physical";this.width=e,this.height=n}toLogical(e){return new C(this.width/e,this.height/e)}},D=class{constructor(e,n){this.type="Logical";this.x=e,this.y=n}},y=class{constructor(e,n){this.type="Physical";this.x=e,this.y=n}toLogical(e){return new D(this.x/e,this.y/e)}},ue=(n=>(n[n.Critical=1]="Critical",n[n.Informational=2]="Informational",n))(ue||{});function kt(){return new m(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0})}function de(){return window.__TAURI_METADATA__.__windows.map(t=>new m(t.label,{skip:!0}))}var le=["tauri://created","tauri://error"],S=class{constructor(e){this.label=e,this.listeners=Object.create(null)}async listen(e,n){return this._handleTauriEvent(e,n)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(n),1)}):_(e,this.label,n)}async once(e,n){return this._handleTauriEvent(e,n)?Promise.resolve(()=>{let r=this.listeners[e];r.splice(r.indexOf(n),1)}):v(e,this.label,n)}async emit(e,n){if(le.includes(e)){for(let r of this.listeners[e]||[])r({event:e,id:-1,windowLabel:this.label,payload:n});return Promise.resolve()}return w(e,this.label,n)}_handleTauriEvent(e,n){return le.includes(e)?(e in this.listeners?this.listeners[e].push(n):this.listeners[e]=[n],!0):!1}},W=class extends S{async scaleFactor(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"scaleFactor"}}}})}async innerPosition(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerPosition"}}}}).then(({x:e,y:n})=>new y(e,n))}async outerPosition(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerPosition"}}}}).then(({x:e,y:n})=>new y(e,n))}async innerSize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerSize"}}}}).then(({width:e,height:n})=>new h(e,n))}async outerSize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerSize"}}}}).then(({width:e,height:n})=>new h(e,n))}async isFullscreen(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isFullscreen"}}}})}async isMaximized(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMaximized"}}}})}async isDecorated(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isDecorated"}}}})}async isResizable(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isResizable"}}}})}async isVisible(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isVisible"}}}})}async theme(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"theme"}}}})}async center(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"center"}}}})}async requestUserAttention(e){let n=null;return e&&(e===1?n={type:"Critical"}:n={type:"Informational"}),i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"requestUserAttention",payload:n}}}})}async setResizable(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setResizable",payload:e}}}})}async setTitle(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setTitle",payload:e}}}})}async maximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"maximize"}}}})}async unmaximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unmaximize"}}}})}async toggleMaximize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"toggleMaximize"}}}})}async minimize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"minimize"}}}})}async unminimize(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unminimize"}}}})}async show(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"show"}}}})}async hide(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"hide"}}}})}async close(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"close"}}}})}async setDecorations(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setDecorations",payload:e}}}})}async setShadow(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setShadow",payload:e}}}})}async setAlwaysOnTop(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setAlwaysOnTop",payload:e}}}})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSize",payload:{type:e.type,data:{width:e.width,height:e.height}}}}}})}async setMinSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMinSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setMaxSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMaxSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setFullscreen(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFullscreen",payload:e}}}})}async setFocus(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFocus"}}}})}async setIcon(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIcon",payload:{icon:typeof e=="string"?e:Array.from(e)}}}}})}async setSkipTaskbar(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSkipTaskbar",payload:e}}}})}async setCursorGrab(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorGrab",payload:e}}}})}async setCursorVisible(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorVisible",payload:e}}}})}async setCursorIcon(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorIcon",payload:e}}}})}async setCursorPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setIgnoreCursorEvents(e){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIgnoreCursorEvents",payload:e}}}})}async startDragging(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"startDragging"}}}})}async onResized(e){return this.listen("tauri://resize",e)}async onMoved(e){return this.listen("tauri://move",e)}async onCloseRequested(e){return this.listen("tauri://close-requested",n=>{let r=new x(n);Promise.resolve(e(r)).then(()=>{if(!r.isPreventDefault())return this.close()})})}async onFocusChanged(e){let n=await this.listen("tauri://focus",s=>{e({...s,payload:!0})}),r=await this.listen("tauri://blur",s=>{e({...s,payload:!1})});return()=>{n(),r()}}async onScaleChanged(e){return this.listen("tauri://scale-change",e)}async onMenuClicked(e){return this.listen("tauri://menu",e)}async onFileDropEvent(e){let n=await this.listen("tauri://file-drop",a=>{e({...a,payload:{type:"drop",paths:a.payload}})}),r=await this.listen("tauri://file-drop-hover",a=>{e({...a,payload:{type:"hover",paths:a.payload}})}),s=await this.listen("tauri://file-drop-cancelled",a=>{e({...a,payload:{type:"cancel"}})});return()=>{n(),r(),s()}}async onThemeChanged(e){return this.listen("tauri://theme-changed",e)}},x=class{constructor(e){this._preventDefault=!1;this.event=e.event,this.windowLabel=e.windowLabel,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}},m=class extends W{constructor(e,n={}){super(e),n?.skip||i({__tauriModule:"Window",message:{cmd:"createWebview",data:{options:{label:e,...n}}}}).then(async()=>this.emit("tauri://created")).catch(async r=>this.emit("tauri://error",r))}static getByLabel(e){return de().some(n=>n.label===e)?new m(e,{skip:!0}):null}},B;"__TAURI_METADATA__"in window?B=new m(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0}):(console.warn(`Could not find "window.__TAURI_METADATA__". The "appWindow" value will reference the "main" window label. +Note that this is not an issue if running this frontend on a browser instead of a Tauri window.`),B=new m("main",{skip:!0}));function ee(t){return t===null?null:{name:t.name,scaleFactor:t.scaleFactor,position:new y(t.position.x,t.position.y),size:new h(t.size.width,t.size.height)}}async function Ut(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"currentMonitor"}}}}).then(ee)}async function It(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"primaryMonitor"}}}}).then(ee)}async function Nt(){return i({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"availableMonitors"}}}}).then(t=>t.map(ee))}var ne={};d(ne,{EOL:()=>zt,arch:()=>qt,platform:()=>Ht,tempdir:()=>Gt,type:()=>jt,version:()=>Vt});var zt=b()?`\r `:` `;async function Ht(){return i({__tauriModule:"Os",message:{cmd:"platform"}})}async function Vt(){return i({__tauriModule:"Os",message:{cmd:"version"}})}async function jt(){return i({__tauriModule:"Os",message:{cmd:"osType"}})}async function qt(){return i({__tauriModule:"Os",message:{cmd:"arch"}})}async function Gt(){return i({__tauriModule:"Os",message:{cmd:"tempdir"}})}var $t=f;return he(Jt);})(); window.__TAURI__ = __TAURI_IIFE__ diff --git a/core/tauri/src/endpoints/window.rs b/core/tauri/src/endpoints/window.rs index f4a545cdae2..e20e63e663f 100644 --- a/core/tauri/src/endpoints/window.rs +++ b/core/tauri/src/endpoints/window.rs @@ -98,6 +98,8 @@ pub enum WindowManagerCmd { Close, #[cfg(window_set_decorations)] SetDecorations(bool), + #[cfg(window_set_shadow)] + SetShadow(bool), #[cfg(window_set_always_on_top)] #[serde(rename_all = "camelCase")] SetAlwaysOnTop(bool), @@ -161,6 +163,7 @@ pub fn into_allowlist_error(variant: &str) -> crate::Error { "hide" => crate::Error::ApiNotAllowlisted("window > hide".to_string()), "close" => crate::Error::ApiNotAllowlisted("window > close".to_string()), "setDecorations" => crate::Error::ApiNotAllowlisted("window > setDecorations".to_string()), + "setShadow" => crate::Error::ApiNotAllowlisted("window > setShadow".to_string()), "setAlwaysOnTop" => crate::Error::ApiNotAllowlisted("window > setAlwaysOnTop".to_string()), "setSize" => crate::Error::ApiNotAllowlisted("window > setSize".to_string()), "setMinSize" => crate::Error::ApiNotAllowlisted("window > setMinSize".to_string()), @@ -293,6 +296,8 @@ impl Cmd { WindowManagerCmd::Close => window.close()?, #[cfg(all(desktop, window_set_decorations))] WindowManagerCmd::SetDecorations(decorations) => window.set_decorations(decorations)?, + #[cfg(all(desktop, window_set_shadow))] + WindowManagerCmd::SetShadow(enable) => window.set_shadow(enable)?, #[cfg(all(desktop, window_set_always_on_top))] WindowManagerCmd::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top)?, #[cfg(all(desktop, window_set_size))] diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 237c64988f1..bccd39d9607 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -135,6 +135,7 @@ //! - **window-hide**: Enables the [`hide` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#hide). //! - **window-close**: Enables the [`close` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#close). //! - **window-set-decorations**: Enables the [`setDecorations` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setdecorations). +//! - **window-set-shadow**: Enables the [`setShadow` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setshadow). //! - **window-set-always-on-top**: Enables the [`setAlwaysOnTop` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setalwaysontop). //! - **window-set-size**: Enables the [`setSize` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setsize). //! - **window-set-min-size**: Enables the [`setMinSize` API](https://tauri.app/en/docs/api/js/classes/window.WebviewWindow#setminsize). diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index c1a046c8244..764d7d556ab 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -278,6 +278,10 @@ impl WindowBuilder for MockWindowBuilder { self } + fn shadow(self, enable: bool) -> Self { + self + } + #[cfg(windows)] fn parent_window(self, parent: HWND) -> Self { self @@ -502,6 +506,10 @@ impl Dispatch for MockDispatcher { Ok(()) } + fn set_shadow(&self, shadow: bool) -> Result<()> { + Ok(()) + } + fn set_always_on_top(&self, always_on_top: bool) -> Result<()> { Ok(()) } diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index 5c9bddfe7c7..47b65c1fb1f 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -425,6 +425,21 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self } + /// Sets whether or not the window has shadow. + /// + /// ## Platform-specific + /// + /// - **Windows:** + /// - `false` has no effect on decorated window, shadows are always ON. + /// - `true` will make ndecorated window have a 1px white border, + /// and on Windows 11, it will have a rounded corners. + /// - **Linux:** Unsupported. + #[must_use] + pub fn shadow(mut self, enable: bool) -> Self { + self.window_builder = self.window_builder.shadow(enable); + self + } + /// Sets a parent to the window to be created. /// /// A child window has the WS_CHILD style and is confined to the client area of its parent window. @@ -1134,6 +1149,23 @@ impl Window { .map_err(Into::into) } + /// Determines if this window should have shadow. + /// + /// ## Platform-specific + /// + /// - **Windows:** + /// - `false` has no effect on decorated window, shadow are always ON. + /// - `true` will make ndecorated window have a 1px white border, + /// and on Windows 11, it will have a rounded corners. + /// - **Linux:** Unsupported. + pub fn set_shadow(&self, enable: bool) -> crate::Result<()> { + self + .window + .dispatcher + .set_shadow(enable) + .map_err(Into::into) + } + /// Determines if this window should always be on top of other windows. pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> { self diff --git a/examples/api/dist/assets/index.css b/examples/api/dist/assets/index.css index c1feae7e602..a5061964d9b 100644 --- a/examples/api/dist/assets/index.css +++ b/examples/api/dist/assets/index.css @@ -1 +1 @@ -*,::before,::after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x:var(--un-empty,/*!*/ /*!*/);--un-pan-y:var(--un-empty,/*!*/ /*!*/);--un-pinch-zoom:var(--un-empty,/*!*/ /*!*/);--un-scroll-snap-strictness:proximity;--un-ordinal:var(--un-empty,/*!*/ /*!*/);--un-slashed-zero:var(--un-empty,/*!*/ /*!*/);--un-numeric-figure:var(--un-empty,/*!*/ /*!*/);--un-numeric-spacing:var(--un-empty,/*!*/ /*!*/);--un-numeric-fraction:var(--un-empty,/*!*/ /*!*/);--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 #0000;--un-ring-shadow:0 0 #0000;--un-shadow-inset:var(--un-empty,/*!*/ /*!*/);--un-shadow:0 0 #0000;--un-ring-inset:var(--un-empty,/*!*/ /*!*/);--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,0.5);--un-blur:var(--un-empty,/*!*/ /*!*/);--un-brightness:var(--un-empty,/*!*/ /*!*/);--un-contrast:var(--un-empty,/*!*/ /*!*/);--un-drop-shadow:var(--un-empty,/*!*/ /*!*/);--un-grayscale:var(--un-empty,/*!*/ /*!*/);--un-hue-rotate:var(--un-empty,/*!*/ /*!*/);--un-invert:var(--un-empty,/*!*/ /*!*/);--un-saturate:var(--un-empty,/*!*/ /*!*/);--un-sepia:var(--un-empty,/*!*/ /*!*/);--un-backdrop-blur:var(--un-empty,/*!*/ /*!*/);--un-backdrop-brightness:var(--un-empty,/*!*/ /*!*/);--un-backdrop-contrast:var(--un-empty,/*!*/ /*!*/);--un-backdrop-grayscale:var(--un-empty,/*!*/ /*!*/);--un-backdrop-hue-rotate:var(--un-empty,/*!*/ /*!*/);--un-backdrop-invert:var(--un-empty,/*!*/ /*!*/);--un-backdrop-opacity:var(--un-empty,/*!*/ /*!*/);--un-backdrop-saturate:var(--un-empty,/*!*/ /*!*/);--un-backdrop-sepia:var(--un-empty,/*!*/ /*!*/);}@font-face { font-family: 'Fira Code'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/s/firacode/v21/uU9eCBsR6Z2vfE9aq3bL0fxyUs4tcw4W_D1sFVc.ttf) format('truetype');}@font-face { font-family: 'Fira Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/s/firamono/v14/N0bX2SlFPv1weGeLZDtQIQ.ttf) format('truetype');}@font-face { font-family: 'Fira Mono'; font-style: normal; font-weight: 700; font-display: swap; src: url(https://fonts.gstatic.com/s/firamono/v14/N0bS2SlFPv1weGeLZDtondv3mQ.ttf) format('truetype');}@font-face { font-family: 'Rubik'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/s/rubik/v21/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-B4i1UA.ttf) format('truetype');}.i-codicon-bell-dot{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M12.994 7.875A4.008 4.008 0 0 1 12 8h-.01v.217c0 .909.143 1.818.442 2.691l.371 1.113h-9.63v-.012l.37-1.113a8.633 8.633 0 0 0 .443-2.691V6.004c0-.563.12-1.113.347-1.616c.227-.514.55-.969.969-1.34c.419-.382.91-.67 1.436-.837c.538-.18 1.1-.24 1.65-.18l.12.018a4 4 0 0 1 .673-.887a5.15 5.15 0 0 0-.697-.135c-.694-.072-1.4 0-2.07.227c-.67.215-1.28.574-1.794 1.053a4.923 4.923 0 0 0-1.208 1.675a5.067 5.067 0 0 0-.431 2.022v2.2a7.61 7.61 0 0 1-.383 2.37L2 12.343l.479.658h3.505c0 .526.215 1.04.586 1.412c.37.37.885.586 1.412.586c.526 0 1.04-.215 1.411-.586s.587-.886.587-1.412h3.505l.478-.658l-.586-1.77a7.63 7.63 0 0 1-.383-2.381v-.318ZM7.982 14.02a.997.997 0 0 0 .706-.3a.939.939 0 0 0 .287-.705H6.977c0 .263.107.514.299.706a.999.999 0 0 0 .706.299Z' clip-rule='evenodd'/%3E%3Cpath d='M12 7a3 3 0 1 0 0-6a3 3 0 0 0 0 6Z'/%3E%3C/g%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-chrome-close{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='m7.116 8l-4.558 4.558l.884.884L8 8.884l4.558 4.558l.884-.884L8.884 8l4.558-4.558l-.884-.884L8 7.116L3.442 2.558l-.884.884L7.116 8z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-chrome-maximize{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M3 3v10h10V3H3zm9 9H4V4h8v8z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-chrome-minimize{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M14 8v1H3V8h11z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-chrome-restore{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M3 5v9h9V5H3zm8 8H4V6h7v7z'/%3E%3Cpath fill-rule='evenodd' d='M5 5h1V4h7v7h-1v1h2V3H5v2z' clip-rule='evenodd'/%3E%3C/g%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-clear-all{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m10 12.6l.7.7l1.6-1.6l1.6 1.6l.8-.7L13 11l1.7-1.6l-.8-.8l-1.6 1.7l-1.6-1.7l-.7.8l1.6 1.6l-1.6 1.6zM1 4h14V3H1v1zm0 3h14V6H1v1zm8 2.5V9H1v1h8v-.5zM9 13v-1H1v1h8z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-clippy{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M7 13.992H4v-9h8v2h1v-2.5l-.5-.5H11v-1h-1a2 2 0 0 0-4 0H4.94v1H3.5l-.5.5v10l.5.5H7v-1zm0-11.2a1 1 0 0 1 .8-.8a1 1 0 0 1 .58.06a.94.94 0 0 1 .45.36a1 1 0 1 1-1.75.94a1 1 0 0 1-.08-.56zm7.08 9.46L13 13.342v-5.35h-1v5.34l-1.08-1.08l-.71.71l1.94 1.93h.71l1.93-1.93l-.71-.71zm-5.92-4.16h.71l1.93 1.93l-.71.71l-1.08-1.08v5.34h-1v-5.35l-1.08 1.09l-.71-.71l1.94-1.93z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-close{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='m8 8.707l3.646 3.647l.708-.707L8.707 8l3.647-3.646l-.707-.708L8 7.293L4.354 3.646l-.707.708L7.293 8l-3.646 3.646l.707.708L8 8.707z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-cloud-download{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M11.957 6h.05a2.99 2.99 0 0 1 2.116.879a3.003 3.003 0 0 1 0 4.242a2.99 2.99 0 0 1-2.117.879v-1a2.002 2.002 0 0 0 0-4h-.914l-.123-.857a2.49 2.49 0 0 0-2.126-2.122A2.478 2.478 0 0 0 6.231 5.5l-.333.762l-.809-.189A2.49 2.49 0 0 0 4.523 6c-.662 0-1.297.263-1.764.732A2.503 2.503 0 0 0 4.523 11h.498v1h-.498a3.486 3.486 0 0 1-2.628-1.16a3.502 3.502 0 0 1 1.958-5.78a3.462 3.462 0 0 1 1.468.04a3.486 3.486 0 0 1 3.657-2.06A3.479 3.479 0 0 1 11.957 6zm-5.25 5.121l1.314 1.314V7h.994v5.4l1.278-1.279l.707.707l-2.146 2.147h-.708L6 11.829l.707-.708z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-files{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 24 24' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M17.5 0h-9L7 1.5V6H2.5L1 7.5v15.07L2.5 24h12.07L16 22.57V18h4.7l1.3-1.43V4.5L17.5 0zm0 2.12l2.38 2.38H17.5V2.12zm-3 20.38h-12v-15H7v9.07L8.5 18h6v4.5zm6-6h-12v-15H16V6h4.5v10.5z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-hubot{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M8.48 4h4l.5.5v2.03h.52l.5.5V8l-.5.5h-.52v3l-.5.5H9.36l-2.5 2.76L6 14.4V12H3.5l-.5-.64V8.5h-.5L2 8v-.97l.5-.5H3V4.36L3.53 4h4V2.86A1 1 0 0 1 7 2a1 1 0 0 1 2 0a1 1 0 0 1-.52.83V4zM12 8V5H4v5.86l2.5.14H7v2.19l1.8-2.04l.35-.15H12V8zm-2.12.51a2.71 2.71 0 0 1-1.37.74v-.01a2.71 2.71 0 0 1-2.42-.74l-.7.71c.34.34.745.608 1.19.79c.45.188.932.286 1.42.29a3.7 3.7 0 0 0 2.58-1.07l-.7-.71zM6.49 6.5h-1v1h1v-1zm3 0h1v1h-1v-1z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-link-external{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M1.5 1H6v1H2v12h12v-4h1v4.5l-.5.5h-13l-.5-.5v-13l.5-.5z'/%3E%3Cpath d='M15 1.5V8h-1V2.707L7.243 9.465l-.707-.708L13.293 2H8V1h6.5l.5.5z'/%3E%3C/g%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-menu{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M16 5H0V4h16v1zm0 8H0v-1h16v1zm0-4.008H0V8h16v.992z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-multiple-windows{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='m6 1.5l.5-.5h8l.5.5v7l-.5.5H12V8h2V4H7v1H6V1.5zM7 2v1h7V2H7zM1.5 7l-.5.5v7l.5.5h8l.5-.5v-7L9.5 7h-8zM2 9V8h7v1H2zm0 1h7v4H2v-4z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-radio-tower{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M2.998 5.58a5.55 5.55 0 0 1 1.62-3.88l-.71-.7a6.45 6.45 0 0 0 0 9.16l.71-.7a5.55 5.55 0 0 1-1.62-3.88zm1.06 0a4.42 4.42 0 0 0 1.32 3.17l.71-.71a3.27 3.27 0 0 1-.76-1.12a3.45 3.45 0 0 1 0-2.67a3.22 3.22 0 0 1 .76-1.13l-.71-.71a4.46 4.46 0 0 0-1.32 3.17zm7.65 3.21l-.71-.71c.33-.32.59-.704.76-1.13a3.449 3.449 0 0 0 0-2.67a3.22 3.22 0 0 0-.76-1.13l.71-.7a4.468 4.468 0 0 1 0 6.34zM13.068 1l-.71.71a5.43 5.43 0 0 1 0 7.74l.71.71a6.45 6.45 0 0 0 0-9.16zM9.993 5.43a1.5 1.5 0 0 1-.245.98a2 2 0 0 1-.27.23l3.44 7.73l-.92.4l-.77-1.73h-5.54l-.77 1.73l-.92-.4l3.44-7.73a1.52 1.52 0 0 1-.33-1.63a1.55 1.55 0 0 1 .56-.68a1.5 1.5 0 0 1 2.325 1.1zm-1.595-.34a.52.52 0 0 0-.25.14a.52.52 0 0 0-.11.22a.48.48 0 0 0 0 .29c.04.09.102.17.18.23a.54.54 0 0 0 .28.08a.51.51 0 0 0 .5-.5a.54.54 0 0 0-.08-.28a.58.58 0 0 0-.23-.18a.48.48 0 0 0-.29 0zm.23 2.05h-.27l-.87 1.94h2l-.86-1.94zm2.2 4.94l-.89-2h-2.88l-.89 2h4.66z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-record-keys{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M14 3H3a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1zm0 8H3V4h11v7zm-3-6h-1v1h1V5zm-1 2H9v1h1V7zm2-2h1v1h-1V5zm1 4h-1v1h1V9zM6 9h5v1H6V9zm7-2h-2v1h2V7zM8 5h1v1H8V5zm0 2H7v1h1V7zM4 9h1v1H4V9zm0-4h1v1H4V5zm3 0H6v1h1V5zM4 7h2v1H4V7z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-terminal{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 24 24' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M3 1.5L1.5 3v18L3 22.5h18l1.5-1.5V3L21 1.5H3zM3 21V3h18v18H3zm5.656-4.01l1.038 1.061l5.26-5.243v-.912l-5.26-5.26l-1.035 1.06l4.59 4.702l-4.593 4.592z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-terminal-bash{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M13.655 3.56L8.918.75a1.785 1.785 0 0 0-1.82 0L2.363 3.56a1.889 1.889 0 0 0-.921 1.628v5.624a1.889 1.889 0 0 0 .913 1.627l4.736 2.812a1.785 1.785 0 0 0 1.82 0l4.736-2.812a1.888 1.888 0 0 0 .913-1.627V5.188a1.889 1.889 0 0 0-.904-1.627zm-3.669 8.781v.404a.149.149 0 0 1-.07.124l-.239.137c-.038.02-.07 0-.07-.053v-.396a.78.78 0 0 1-.545.053a.073.073 0 0 1-.027-.09l.086-.365a.153.153 0 0 1 .071-.096a.048.048 0 0 1 .038 0a.662.662 0 0 0 .497-.063a.662.662 0 0 0 .37-.567c0-.206-.112-.292-.384-.293c-.344 0-.661-.066-.67-.574A1.47 1.47 0 0 1 9.6 9.437V9.03a.147.147 0 0 1 .07-.126l.231-.147c.038-.02.07 0 .07.054v.409a.754.754 0 0 1 .453-.055a.073.073 0 0 1 .03.095l-.081.362a.156.156 0 0 1-.065.09a.055.055 0 0 1-.035 0a.6.6 0 0 0-.436.072a.549.549 0 0 0-.331.486c0 .185.098.242.425.248c.438 0 .627.199.632.639a1.568 1.568 0 0 1-.576 1.185zm2.481-.68a.094.094 0 0 1-.036.092l-1.198.727a.034.034 0 0 1-.04.003a.035.035 0 0 1-.016-.037v-.31a.086.086 0 0 1 .055-.076l1.179-.706a.035.035 0 0 1 .056.035v.273zm.827-6.914L8.812 7.515c-.559.331-.97.693-.97 1.367v5.52c0 .404.165.662.413.741a1.465 1.465 0 0 1-.248.025c-.264 0-.522-.072-.748-.207L2.522 12.15a1.558 1.558 0 0 1-.75-1.338V5.188a1.558 1.558 0 0 1 .75-1.34l4.738-2.81a1.46 1.46 0 0 1 1.489 0l4.736 2.812a1.548 1.548 0 0 1 .728 1.083c-.154-.334-.508-.427-.92-.185h.002z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-window{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M14.5 2h-13l-.5.5v11l.5.5h13l.5-.5v-11l-.5-.5zM14 13H2V6h12v7zm0-8H2V3h12v2z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-broadcast{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M128 88a40 40 0 1 0 40 40a40 40 0 0 0-40-40Zm0 64a24 24 0 1 1 24-24a24.1 24.1 0 0 1-24 24Zm-59-48.9a64.5 64.5 0 0 0 0 49.8a65.4 65.4 0 0 0 13.7 20.4a7.9 7.9 0 0 1 0 11.3a8 8 0 0 1-5.6 2.3a8.3 8.3 0 0 1-5.7-2.3a80 80 0 0 1-17.1-25.5a79.9 79.9 0 0 1 0-62.2a80 80 0 0 1 17.1-25.5a8 8 0 0 1 11.3 0a7.9 7.9 0 0 1 0 11.3A65.4 65.4 0 0 0 69 103.1Zm132.7 56a80 80 0 0 1-17.1 25.5a8.3 8.3 0 0 1-5.7 2.3a8 8 0 0 1-5.6-2.3a7.9 7.9 0 0 1 0-11.3a65.4 65.4 0 0 0 13.7-20.4a64.5 64.5 0 0 0 0-49.8a65.4 65.4 0 0 0-13.7-20.4a7.9 7.9 0 0 1 0-11.3a8 8 0 0 1 11.3 0a80 80 0 0 1 17.1 25.5a79.9 79.9 0 0 1 0 62.2ZM54.5 201.5a8.1 8.1 0 0 1 0 11.4a8.3 8.3 0 0 1-5.7 2.3a8.5 8.5 0 0 1-5.7-2.3a121.8 121.8 0 0 1-25.7-38.2a120.7 120.7 0 0 1 0-93.4a121.8 121.8 0 0 1 25.7-38.2a8.1 8.1 0 0 1 11.4 11.4A103.5 103.5 0 0 0 24 128a103.5 103.5 0 0 0 30.5 73.5ZM248 128a120.2 120.2 0 0 1-9.4 46.7a121.8 121.8 0 0 1-25.7 38.2a8.5 8.5 0 0 1-5.7 2.3a8.3 8.3 0 0 1-5.7-2.3a8.1 8.1 0 0 1 0-11.4A103.5 103.5 0 0 0 232 128a103.5 103.5 0 0 0-30.5-73.5a8.1 8.1 0 1 1 11.4-11.4a121.8 121.8 0 0 1 25.7 38.2A120.2 120.2 0 0 1 248 128Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-globe-hemisphere-west{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M221.6 173.3A102.9 102.9 0 0 0 232 128a104.2 104.2 0 0 0-77.2-100.5h-.5A103.8 103.8 0 0 0 60.4 49l-1.3 1.2A103.9 103.9 0 0 0 128 232h2.4a104.3 104.3 0 0 0 90.6-57.4ZM216 128a89.3 89.3 0 0 1-5.5 30.7l-46.4-28.5a16.6 16.6 0 0 0-6.3-2.3l-22.8-3a16.1 16.1 0 0 0-15.3 6.8h-8.6l-3.8-7.9a15.9 15.9 0 0 0-11-8.7l-6.6-1.4l4.6-10.8h21.4a16.1 16.1 0 0 0 7.7-2l12.2-6.8a16.1 16.1 0 0 0 3-2.1l26.9-24.4a15.7 15.7 0 0 0 4.5-16.9a88 88 0 0 1 46 77.3Zm-68.8-85.9l7.6 13.7l-26.9 24.3l-12.2 6.8H94.3a15.9 15.9 0 0 0-14.7 9.8l-5.3 12.4l-10.9-29.2l8.1-19.3a88 88 0 0 1 75.7-18.5ZM40 128a87.1 87.1 0 0 1 9.5-39.7l10.4 27.9a16.1 16.1 0 0 0 11.6 10l5.5 1.2h.1l15.8 3.4l3.8 7.9a16.3 16.3 0 0 0 14.4 9h1.2l-7.7 17.2a15.9 15.9 0 0 0 2.8 17.4l18.8 20.4l-2.5 13.2A88.1 88.1 0 0 1 40 128Zm100.1 87.2l1.8-9.5a16 16 0 0 0-3.9-13.9l-18.8-20.3l12.7-28.7l1-2.1l22.8 3.1l47.8 29.4a88.5 88.5 0 0 1-63.4 42Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-hand-waving{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m220.2 104l-20-34.7a28.1 28.1 0 0 0-47.3-1.9l-17.3-30a28.1 28.1 0 0 0-38.3-10.3a29.4 29.4 0 0 0-9.9 9.6a27.9 27.9 0 0 0-11.5-6.2a27.2 27.2 0 0 0-21.2 2.8a27.9 27.9 0 0 0-10.3 38.2l3.4 5.8A28.5 28.5 0 0 0 36 81a28.1 28.1 0 0 0-10.2 38.2l42 72.8a88 88 0 1 0 152.4-88Zm-6.7 62.6a71.2 71.2 0 0 1-33.5 43.7A72.1 72.1 0 0 1 81.6 184l-42-72.8a12 12 0 0 1 20.8-12l22 38.1l.6.9v.2l.5.5l.2.2l.7.6h.1l.7.5h.3l.6.3h.2l.9.3h.1l.8.2h2.2l.9-.2h.3l.6-.2h.3l.9-.4a8.1 8.1 0 0 0 2.9-11l-22-38.1l-16-27.7a12 12 0 0 1-1.2-9.1a11.8 11.8 0 0 1 5.6-7.3a12 12 0 0 1 9.1-1.2a12.5 12.5 0 0 1 7.3 5.6l8 14h.1l26 45a7 7 0 0 0 1.5 1.9a8 8 0 0 0 12.3-9.9l-26-45a12 12 0 1 1 20.8-12l30 51.9l6.3 11a48.1 48.1 0 0 0-10.9 61a8 8 0 0 0 13.8-8a32 32 0 0 1 11.7-43.7l.7-.4l.5-.4h.1l.6-.6l.5-.5l.4-.5l.3-.6h.1l.2-.5v-.2a1.9 1.9 0 0 0 .2-.7h.1c0-.2.1-.4.1-.6s0-.2.1-.2v-2.1a6.4 6.4 0 0 0-.2-.7a1.9 1.9 0 0 0-.2-.7v-.2c0-.2-.1-.3-.2-.5l-.3-.7l-10-17.4a12 12 0 0 1 13.5-17.5a11.8 11.8 0 0 1 7.2 5.5l20 34.7a70.9 70.9 0 0 1 7.2 53.8Zm-125.8 78a8.2 8.2 0 0 1-6.6 3.4a8.6 8.6 0 0 1-4.6-1.4A117.9 117.9 0 0 1 41.1 208a8 8 0 1 1 13.8-8a102.6 102.6 0 0 0 30.8 33.4a8.1 8.1 0 0 1 2 11.2ZM168 31a8 8 0 0 1 8-8a60.2 60.2 0 0 1 52 30a7.9 7.9 0 0 1-3 10.9a7.1 7.1 0 0 1-4 1.1a8 8 0 0 1-6.9-4A44 44 0 0 0 176 39a8 8 0 0 1-8-8Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-moon{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M224.3 150.3a8.1 8.1 0 0 0-7.8-5.7l-2.2.4A84 84 0 0 1 111 41.6a5.7 5.7 0 0 0 .3-1.8a7.9 7.9 0 0 0-10.3-8.1a100 100 0 1 0 123.3 123.2a7.2 7.2 0 0 0 0-4.6ZM128 212A84 84 0 0 1 92.8 51.7a99.9 99.9 0 0 0 111.5 111.5A84.4 84.4 0 0 1 128 212Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-sun{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M128 60a68 68 0 1 0 68 68a68.1 68.1 0 0 0-68-68Zm0 120a52 52 0 1 1 52-52a52 52 0 0 1-52 52Zm-8-144V16a8 8 0 0 1 16 0v20a8 8 0 0 1-16 0ZM43.1 54.5a8.1 8.1 0 1 1 11.4-11.4l14.1 14.2a8 8 0 0 1 0 11.3a8.1 8.1 0 0 1-11.3 0ZM36 136H16a8 8 0 0 1 0-16h20a8 8 0 0 1 0 16Zm32.6 51.4a8 8 0 0 1 0 11.3l-14.1 14.2a8.3 8.3 0 0 1-5.7 2.3a8.5 8.5 0 0 1-5.7-2.3a8.1 8.1 0 0 1 0-11.4l14.2-14.1a8 8 0 0 1 11.3 0ZM136 220v20a8 8 0 0 1-16 0v-20a8 8 0 0 1 16 0Zm76.9-18.5a8.1 8.1 0 0 1 0 11.4a8.5 8.5 0 0 1-5.7 2.3a8.3 8.3 0 0 1-5.7-2.3l-14.1-14.2a8 8 0 0 1 11.3-11.3ZM248 128a8 8 0 0 1-8 8h-20a8 8 0 0 1 0-16h20a8 8 0 0 1 8 8Zm-60.6-59.4a8 8 0 0 1 0-11.3l14.1-14.2a8.1 8.1 0 0 1 11.4 11.4l-14.2 14.1a8.1 8.1 0 0 1-11.3 0Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.note{position:relative;display:inline-flex;align-items:center;border-left-width:4px;border-left-style:solid;--un-border-opacity:1;border-color:rgba(53,120,229,var(--un-border-opacity));border-radius:0.25rem;background-color:rgba(53,120,229,0.1);padding:0.5rem;text-decoration:none;}.note-red{position:relative;display:inline-flex;align-items:center;border-left-width:4px;border-left-style:solid;--un-border-opacity:1;border-color:rgba(53,120,229,var(--un-border-opacity));border-radius:0.25rem;background-color:rgba(53,120,229,0.1);background-color:rgba(185,28,28,0.1);padding:0.5rem;text-decoration:none;}.nv{position:relative;display:flex;align-items:center;border-radius:0.25rem;padding:0.5rem;--un-text-opacity:1;color:rgba(194,197,202,var(--un-text-opacity));text-decoration:none;transition-property:all;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:125ms;}.nv_selected{position:relative;display:flex;align-items:center;border-left-width:4px;border-left-style:solid;border-radius:0.25rem;--un-bg-opacity:.05;background-color:hsla(0,0%,100%,var(--un-bg-opacity));padding:0.5rem;--un-text-opacity:1;color:rgba(194,197,202,var(--un-text-opacity));color:rgba(53,120,229,var(--un-text-opacity));text-decoration:none;transition-property:all;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:125ms;}.input{height:2.5rem;display:flex;align-items:center;border-radius:0.25rem;border-style:none;--un-bg-opacity:1;background-color:rgba(233,236,239,var(--un-bg-opacity));padding:0.5rem;--un-text-opacity:1;color:rgba(28,30,33,var(--un-text-opacity));--un-shadow:var(--un-shadow-inset) 0 4px 6px -1px var(--un-shadow-color, rgba(0,0,0,0.1)),var(--un-shadow-inset) 0 2px 4px -2px var(--un-shadow-color, rgba(0,0,0,0.1));box-shadow:var(--un-ring-offset-shadow, 0 0 #0000), var(--un-ring-shadow, 0 0 #0000), var(--un-shadow);outline:2px solid transparent;outline-offset:2px;}.btn{user-select:none;border-radius:0.25rem;border-style:none;--un-bg-opacity:1;background-color:rgba(53,120,229,var(--un-bg-opacity));padding:0.5rem;font-weight:400;--un-text-opacity:1;color:rgba(28,30,33,var(--un-text-opacity));color:rgba(255,255,255,var(--un-text-opacity));--un-shadow:var(--un-shadow-inset) 0 4px 6px -1px var(--un-shadow-color, rgba(0,0,0,0.1)),var(--un-shadow-inset) 0 2px 4px -2px var(--un-shadow-color, rgba(0,0,0,0.1));box-shadow:var(--un-ring-offset-shadow, 0 0 #0000), var(--un-ring-shadow, 0 0 #0000), var(--un-shadow);outline:2px solid transparent;outline-offset:2px;}.nv_selected:hover,.nv:hover{border-left-width:4px;border-left-style:solid;--un-bg-opacity:.05;background-color:hsla(0,0%,100%,var(--un-bg-opacity));--un-text-opacity:1;color:rgba(53,120,229,var(--un-text-opacity));}.dark .note{--un-border-opacity:1;border-color:rgba(103,214,237,var(--un-border-opacity));background-color:rgba(103,214,237,0.1);}.dark .note-red{--un-border-opacity:1;border-color:rgba(103,214,237,var(--un-border-opacity));background-color:rgba(103,214,237,0.1);background-color:rgba(185,28,28,0.1);}.btn:hover{--un-bg-opacity:1;background-color:rgba(45,102,195,var(--un-bg-opacity));}.dark .btn{--un-bg-opacity:1;background-color:rgba(103,214,237,var(--un-bg-opacity));font-weight:600;--un-text-opacity:1;color:rgba(28,30,33,var(--un-text-opacity));}.dark .btn:hover{--un-bg-opacity:1;background-color:rgba(57,202,232,var(--un-bg-opacity));}.dark .input{--un-bg-opacity:1;background-color:rgba(36,37,38,var(--un-bg-opacity));--un-text-opacity:1;color:rgba(227,227,227,var(--un-text-opacity));}.dark .note-red::after,.note-red::after{--un-bg-opacity:1;background-color:rgba(185,28,28,var(--un-bg-opacity));}.btn:active{--un-bg-opacity:1;background-color:rgba(37,84,160,var(--un-bg-opacity));}.dark .btn:active{--un-bg-opacity:1;background-color:rgba(25,181,213,var(--un-bg-opacity));}.dark .nv_selected,.dark .nv_selected:hover,.dark .nv:hover{--un-text-opacity:1;color:rgba(103,214,237,var(--un-text-opacity));} ::-webkit-scrollbar-thumb { background-color: #3578E5; } .dark ::-webkit-scrollbar-thumb { background-color: #67d6ed; } code { font-size: 0.75rem; font-family: "Fira Code","Fira Mono",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; border-radius: 0.25rem; background-color: #d6d8da; } .code-block { font-family: "Fira Code","Fira Mono",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; font-size: 0.875rem; } .dark code { background-color: #282a2e; } .visible{visibility:visible;}.absolute{position:absolute;}.left-2{left:0.5rem;}.top-2{top:0.5rem;}.z-2000{z-index:2000;}.grid{display:grid;}.grid-rows-\[2fr_auto\]{grid-template-rows:2fr auto;}.grid-rows-\[2px_2rem_1fr\]{grid-template-rows:2px 2rem 1fr;}.grid-rows-\[auto_1fr\]{grid-template-rows:auto 1fr;}.my-2{margin-top:0.5rem;margin-bottom:0.5rem;}.mb-2{margin-bottom:0.5rem;}.mr-2{margin-right:0.5rem;}.display-none,.hidden{display:none;}.children-h-10>*,.children\:h10>*{height:2.5rem;}.children\:h-100\%>*,.h-100\%{height:100%;}.children\:w-12>*{width:3rem;}.h-15rem{height:15rem;}.h-2px{height:2px;}.h-8{height:2rem;}.h-85\%{height:85%;}.h-auto{height:auto;}.h-screen{height:100vh;}.w-100\%{width:100%;}.w-8{width:2rem;}.w-screen{width:100vw;}.flex{display:flex;}.children\:inline-flex>*{display:inline-flex;}.flex-1{flex:1 1 0%;}.children-flex-none>*{flex:none;}.children\:grow>*,.grow{flex-grow:1;}.flex-row{flex-direction:row;}.flex-col{flex-direction:column;}.flex-wrap{flex-wrap:wrap;}@keyframes fade-in{from{opacity:0}to{opacity:1}}@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.animate-fade-in{animation:fade-in 1s linear 1;}.animate-spin{animation:spin 1s linear infinite;}.animate-duration-300ms{animation-duration:300ms;}.cursor-ns-resize{cursor:ns-resize;}.cursor-pointer{cursor:pointer;}.select-none{user-select:none;}.children\:items-center>*,.items-center{align-items:center;}.self-center{align-self:center;}.children\:justify-center>*,.justify-center{justify-content:center;}.justify-between{justify-content:space-between;}.gap-1{grid-gap:0.25rem;gap:0.25rem;}.gap-2{grid-gap:0.5rem;gap:0.5rem;}.overflow-hidden{overflow:hidden;}.overflow-y-auto{overflow-y:auto;}.rd-1{border-radius:0.25rem;}.rd-8{border-radius:2rem;}.bg-accent{--un-bg-opacity:1;background-color:rgba(53,120,229,var(--un-bg-opacity));}.bg-black\/20{background-color:rgba(0,0,0,0.2);}.bg-darkPrimaryLighter{--un-bg-opacity:1;background-color:rgba(36,37,38,var(--un-bg-opacity));}.bg-darkPrimaryLighter\/60{background-color:rgba(36,37,38,0.6);}.bg-primary{--un-bg-opacity:1;background-color:rgba(255,255,255,var(--un-bg-opacity));}.bg-white\/5{background-color:rgba(255,255,255,0.05);}.dark .dark\:bg-darkAccent{--un-bg-opacity:1;background-color:rgba(103,214,237,var(--un-bg-opacity));}.dark .dark\:bg-darkPrimary{--un-bg-opacity:1;background-color:rgba(27,27,29,var(--un-bg-opacity));}.dark .dark\:hover\:bg-darkHoverOverlay:hover{--un-bg-opacity:.05;background-color:hsla(0,0%,100%,var(--un-bg-opacity));}.dark .dark\:hover\:bg-red-700:hover,.hover\:bg-red-700:hover{--un-bg-opacity:1;background-color:rgba(185,28,28,var(--un-bg-opacity));}.hover\:bg-hoverOverlay:hover{--un-bg-opacity:.05;background-color:rgba(0,0,0,var(--un-bg-opacity));}.active\:bg-accentDark:active{--un-bg-opacity:1;background-color:rgba(48,108,206,var(--un-bg-opacity));}.active\:bg-hoverOverlay\/25:active{background-color:rgba(0,0,0,0.25);}.active\:bg-hoverOverlayDarker:active{--un-bg-opacity:.1;background-color:rgba(0,0,0,var(--un-bg-opacity));}.active\:bg-red-700\/90:active,.dark .dark\:active\:bg-red-700\/90:active{background-color:rgba(185,28,28,0.9);}.dark .dark\:active\:bg-darkAccentDark:active{--un-bg-opacity:1;background-color:rgba(73,206,233,var(--un-bg-opacity));}.dark .dark\:active\:bg-darkHoverOverlay\/25:active{background-color:hsla(0,0%,100%,0.25);}.dark .dark\:active\:bg-darkHoverOverlayDarker:active{--un-bg-opacity:.1;background-color:hsla(0,0%,100%,var(--un-bg-opacity));}.p-1{padding:0.25rem;}.p-7{padding:1.75rem;}.px{padding-left:1rem;padding-right:1rem;}.px-2{padding-left:0.5rem;padding-right:0.5rem;}.px-5{padding-left:1.25rem;padding-right:1.25rem;}.children-pb-2>*{padding-bottom:0.5rem;}.children-pt8>*{padding-top:2rem;}.pl-2{padding-left:0.5rem;}.all\:font-mono *{font-family:"Fira Code","Fira Mono",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;}.all\:text-xs *{font-size:0.75rem;line-height:1rem;}.text-sm{font-size:0.875rem;line-height:1.25rem;}.font-700{font-weight:700;}.font-semibold{font-weight:600;}.dark .dark\:text-darkAccent{--un-text-opacity:1;color:rgba(103,214,237,var(--un-text-opacity));}.dark .dark\:text-darkAccentText,.text-primaryText{--un-text-opacity:1;color:rgba(28,30,33,var(--un-text-opacity));}.dark .dark\:text-darkPrimaryText,.hover\:text-darkPrimaryText:hover,.text-darkPrimaryText,.active\:text-darkPrimaryText:active{--un-text-opacity:1;color:rgba(227,227,227,var(--un-text-opacity));}.text-accent{--un-text-opacity:1;color:rgba(53,120,229,var(--un-text-opacity));}.text-accentText{--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity));}.filter{filter:var(--un-blur) var(--un-brightness) var(--un-contrast) var(--un-drop-shadow) var(--un-grayscale) var(--un-hue-rotate) var(--un-invert) var(--un-saturate) var(--un-sepia);}.transition-colors-250{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:250ms;}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}@media (max-width: 639.9px){.lt-sm\:absolute{position:absolute;}.lt-sm\:z-1999{z-index:1999;}.lt-sm\:h-screen{height:100vh;}.lt-sm\:flex{display:flex;}.lt-sm\:bg-darkPrimaryLighter{--un-bg-opacity:1;background-color:rgba(36,37,38,var(--un-bg-opacity));}.lt-sm\:pl-10{padding-left:2.5rem;}.lt-sm\:shadow{--un-shadow:var(--un-shadow-inset) 0 1px 3px 0 var(--un-shadow-color, rgba(0,0,0,0.1)),var(--un-shadow-inset) 0 1px 2px -1px var(--un-shadow-color, rgba(0,0,0,0.1));box-shadow:var(--un-ring-offset-shadow, 0 0 #0000), var(--un-ring-shadow, 0 0 #0000), var(--un-shadow);}.lt-sm\:shadow-lg{--un-shadow:var(--un-shadow-inset) 0 10px 15px -3px var(--un-shadow-color, rgba(0,0,0,0.1)),var(--un-shadow-inset) 0 4px 6px -4px var(--un-shadow-color, rgba(0,0,0,0.1));box-shadow:var(--un-ring-offset-shadow, 0 0 #0000), var(--un-ring-shadow, 0 0 #0000), var(--un-shadow);}.lt-sm\:transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}}*:not(h1,h2,h3,h4,h5,h6){margin:0;padding:0}*{box-sizing:border-box;font-family:Rubik,sans-serif}::-webkit-scrollbar{width:.25rem;height:3px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{border-radius:.25rem}code{padding:.05rem .25rem}code.code-block{padding:.5rem}#sidebar{width:18.75rem}@media screen and (max-width: 640px){#sidebar{--translate-x: -18.75rem;transform:translate(var(--translate-x))}}ul.svelte-gbh3pt{list-style:none;margin:0;padding:0;padding-left:var(--nodePaddingLeft, 1rem);border-left:var(--nodeBorderLeft, 1px dotted #9ca3af);color:var(--nodeColor, #374151)}.hidden.svelte-gbh3pt{display:none}.bracket.svelte-gbh3pt{cursor:pointer}.bracket.svelte-gbh3pt:hover{background:var(--bracketHoverBackground, #d1d5db)}.comma.svelte-gbh3pt{color:var(--nodeColor, #374151)}.val.svelte-gbh3pt{color:var(--leafDefaultColor, #9ca3af)}.val.string.svelte-gbh3pt{color:var(--leafStringColor, #059669)}.val.number.svelte-gbh3pt{color:var(--leafNumberColor, #d97706)}.val.boolean.svelte-gbh3pt{color:var(--leafBooleanColor, #2563eb)}.spinner.svelte-4xesec{height:1.2rem;width:1.2rem;border-radius:50rem;color:currentColor;border:2px dashed currentColor} +*,::before,::after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x:var(--un-empty,/*!*/ /*!*/);--un-pan-y:var(--un-empty,/*!*/ /*!*/);--un-pinch-zoom:var(--un-empty,/*!*/ /*!*/);--un-scroll-snap-strictness:proximity;--un-ordinal:var(--un-empty,/*!*/ /*!*/);--un-slashed-zero:var(--un-empty,/*!*/ /*!*/);--un-numeric-figure:var(--un-empty,/*!*/ /*!*/);--un-numeric-spacing:var(--un-empty,/*!*/ /*!*/);--un-numeric-fraction:var(--un-empty,/*!*/ /*!*/);--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 #0000;--un-ring-shadow:0 0 #0000;--un-shadow-inset:var(--un-empty,/*!*/ /*!*/);--un-shadow:0 0 #0000;--un-ring-inset:var(--un-empty,/*!*/ /*!*/);--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,0.5);--un-blur:var(--un-empty,/*!*/ /*!*/);--un-brightness:var(--un-empty,/*!*/ /*!*/);--un-contrast:var(--un-empty,/*!*/ /*!*/);--un-drop-shadow:var(--un-empty,/*!*/ /*!*/);--un-grayscale:var(--un-empty,/*!*/ /*!*/);--un-hue-rotate:var(--un-empty,/*!*/ /*!*/);--un-invert:var(--un-empty,/*!*/ /*!*/);--un-saturate:var(--un-empty,/*!*/ /*!*/);--un-sepia:var(--un-empty,/*!*/ /*!*/);--un-backdrop-blur:var(--un-empty,/*!*/ /*!*/);--un-backdrop-brightness:var(--un-empty,/*!*/ /*!*/);--un-backdrop-contrast:var(--un-empty,/*!*/ /*!*/);--un-backdrop-grayscale:var(--un-empty,/*!*/ /*!*/);--un-backdrop-hue-rotate:var(--un-empty,/*!*/ /*!*/);--un-backdrop-invert:var(--un-empty,/*!*/ /*!*/);--un-backdrop-opacity:var(--un-empty,/*!*/ /*!*/);--un-backdrop-saturate:var(--un-empty,/*!*/ /*!*/);--un-backdrop-sepia:var(--un-empty,/*!*/ /*!*/);}@font-face { font-family: 'Fira Code'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/s/firacode/v21/uU9eCBsR6Z2vfE9aq3bL0fxyUs4tcw4W_D1sFVc.ttf) format('truetype');}@font-face { font-family: 'Fira Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/s/firamono/v14/N0bX2SlFPv1weGeLZDtQIQ.ttf) format('truetype');}@font-face { font-family: 'Fira Mono'; font-style: normal; font-weight: 700; font-display: swap; src: url(https://fonts.gstatic.com/s/firamono/v14/N0bS2SlFPv1weGeLZDtondv3mQ.ttf) format('truetype');}@font-face { font-family: 'Rubik'; font-style: normal; font-weight: 400; font-display: swap; src: url(https://fonts.gstatic.com/s/rubik/v23/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-B4i1UA.ttf) format('truetype');}.i-codicon-bell-dot{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M12.994 7.875A4.008 4.008 0 0 1 12 8h-.01v.217c0 .909.143 1.818.442 2.691l.371 1.113h-9.63v-.012l.37-1.113a8.633 8.633 0 0 0 .443-2.691V6.004c0-.563.12-1.113.347-1.616c.227-.514.55-.969.969-1.34c.419-.382.91-.67 1.436-.837c.538-.18 1.1-.24 1.65-.18l.12.018a4 4 0 0 1 .673-.887a5.15 5.15 0 0 0-.697-.135c-.694-.072-1.4 0-2.07.227c-.67.215-1.28.574-1.794 1.053a4.923 4.923 0 0 0-1.208 1.675a5.067 5.067 0 0 0-.431 2.022v2.2a7.61 7.61 0 0 1-.383 2.37L2 12.343l.479.658h3.505c0 .526.215 1.04.586 1.412c.37.37.885.586 1.412.586c.526 0 1.04-.215 1.411-.586s.587-.886.587-1.412h3.505l.478-.658l-.586-1.77a7.63 7.63 0 0 1-.383-2.381v-.318ZM7.982 14.02a.997.997 0 0 0 .706-.3a.939.939 0 0 0 .287-.705H6.977c0 .263.107.514.299.706a.999.999 0 0 0 .706.299Z' clip-rule='evenodd'/%3E%3Cpath d='M12 7a3 3 0 1 0 0-6a3 3 0 0 0 0 6Z'/%3E%3C/g%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-chrome-close{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='m7.116 8l-4.558 4.558l.884.884L8 8.884l4.558 4.558l.884-.884L8.884 8l4.558-4.558l-.884-.884L8 7.116L3.442 2.558l-.884.884L7.116 8z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-chrome-maximize{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M3 3v10h10V3H3zm9 9H4V4h8v8z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-chrome-minimize{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M14 8v1H3V8h11z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-chrome-restore{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M3 5v9h9V5H3zm8 8H4V6h7v7z'/%3E%3Cpath fill-rule='evenodd' d='M5 5h1V4h7v7h-1v1h2V3H5v2z' clip-rule='evenodd'/%3E%3C/g%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-clear-all{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m10 12.6l.7.7l1.6-1.6l1.6 1.6l.8-.7L13 11l1.7-1.6l-.8-.8l-1.6 1.7l-1.6-1.7l-.7.8l1.6 1.6l-1.6 1.6zM1 4h14V3H1v1zm0 3h14V6H1v1zm8 2.5V9H1v1h8v-.5zM9 13v-1H1v1h8z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-clippy{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M7 13.992H4v-9h8v2h1v-2.5l-.5-.5H11v-1h-1a2 2 0 0 0-4 0H4.94v1H3.5l-.5.5v10l.5.5H7v-1zm0-11.2a1 1 0 0 1 .8-.8a1 1 0 0 1 .58.06a.94.94 0 0 1 .45.36a1 1 0 1 1-1.75.94a1 1 0 0 1-.08-.56zm7.08 9.46L13 13.342v-5.35h-1v5.34l-1.08-1.08l-.71.71l1.94 1.93h.71l1.93-1.93l-.71-.71zm-5.92-4.16h.71l1.93 1.93l-.71.71l-1.08-1.08v5.34h-1v-5.35l-1.08 1.09l-.71-.71l1.94-1.93z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-close{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='m8 8.707l3.646 3.647l.708-.707L8.707 8l3.647-3.646l-.707-.708L8 7.293L4.354 3.646l-.707.708L7.293 8l-3.646 3.646l.707.708L8 8.707z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-cloud-download{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M11.957 6h.05a2.99 2.99 0 0 1 2.116.879a3.003 3.003 0 0 1 0 4.242a2.99 2.99 0 0 1-2.117.879v-1a2.002 2.002 0 0 0 0-4h-.914l-.123-.857a2.49 2.49 0 0 0-2.126-2.122A2.478 2.478 0 0 0 6.231 5.5l-.333.762l-.809-.189A2.49 2.49 0 0 0 4.523 6c-.662 0-1.297.263-1.764.732A2.503 2.503 0 0 0 4.523 11h.498v1h-.498a3.486 3.486 0 0 1-2.628-1.16a3.502 3.502 0 0 1 1.958-5.78a3.462 3.462 0 0 1 1.468.04a3.486 3.486 0 0 1 3.657-2.06A3.479 3.479 0 0 1 11.957 6zm-5.25 5.121l1.314 1.314V7h.994v5.4l1.278-1.279l.707.707l-2.146 2.147h-.708L6 11.829l.707-.708z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-files{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 24 24' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M17.5 0h-9L7 1.5V6H2.5L1 7.5v15.07L2.5 24h12.07L16 22.57V18h4.7l1.3-1.43V4.5L17.5 0zm0 2.12l2.38 2.38H17.5V2.12zm-3 20.38h-12v-15H7v9.07L8.5 18h6v4.5zm6-6h-12v-15H16V6h4.5v10.5z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-hubot{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M8.48 4h4l.5.5v2.03h.52l.5.5V8l-.5.5h-.52v3l-.5.5H9.36l-2.5 2.76L6 14.4V12H3.5l-.5-.64V8.5h-.5L2 8v-.97l.5-.5H3V4.36L3.53 4h4V2.86A1 1 0 0 1 7 2a1 1 0 0 1 2 0a1 1 0 0 1-.52.83V4zM12 8V5H4v5.86l2.5.14H7v2.19l1.8-2.04l.35-.15H12V8zm-2.12.51a2.71 2.71 0 0 1-1.37.74v-.01a2.71 2.71 0 0 1-2.42-.74l-.7.71c.34.34.745.608 1.19.79c.45.188.932.286 1.42.29a3.7 3.7 0 0 0 2.58-1.07l-.7-.71zM6.49 6.5h-1v1h1v-1zm3 0h1v1h-1v-1z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-link-external{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cg fill='currentColor'%3E%3Cpath d='M1.5 1H6v1H2v12h12v-4h1v4.5l-.5.5h-13l-.5-.5v-13l.5-.5z'/%3E%3Cpath d='M15 1.5V8h-1V2.707L7.243 9.465l-.707-.708L13.293 2H8V1h6.5l.5.5z'/%3E%3C/g%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-menu{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M16 5H0V4h16v1zm0 8H0v-1h16v1zm0-4.008H0V8h16v.992z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-multiple-windows{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='m6 1.5l.5-.5h8l.5.5v7l-.5.5H12V8h2V4H7v1H6V1.5zM7 2v1h7V2H7zM1.5 7l-.5.5v7l.5.5h8l.5-.5v-7L9.5 7h-8zM2 9V8h7v1H2zm0 1h7v4H2v-4z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-radio-tower{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M2.998 5.58a5.55 5.55 0 0 1 1.62-3.88l-.71-.7a6.45 6.45 0 0 0 0 9.16l.71-.7a5.55 5.55 0 0 1-1.62-3.88zm1.06 0a4.42 4.42 0 0 0 1.32 3.17l.71-.71a3.27 3.27 0 0 1-.76-1.12a3.45 3.45 0 0 1 0-2.67a3.22 3.22 0 0 1 .76-1.13l-.71-.71a4.46 4.46 0 0 0-1.32 3.17zm7.65 3.21l-.71-.71c.33-.32.59-.704.76-1.13a3.449 3.449 0 0 0 0-2.67a3.22 3.22 0 0 0-.76-1.13l.71-.7a4.468 4.468 0 0 1 0 6.34zM13.068 1l-.71.71a5.43 5.43 0 0 1 0 7.74l.71.71a6.45 6.45 0 0 0 0-9.16zM9.993 5.43a1.5 1.5 0 0 1-.245.98a2 2 0 0 1-.27.23l3.44 7.73l-.92.4l-.77-1.73h-5.54l-.77 1.73l-.92-.4l3.44-7.73a1.52 1.52 0 0 1-.33-1.63a1.55 1.55 0 0 1 .56-.68a1.5 1.5 0 0 1 2.325 1.1zm-1.595-.34a.52.52 0 0 0-.25.14a.52.52 0 0 0-.11.22a.48.48 0 0 0 0 .29c.04.09.102.17.18.23a.54.54 0 0 0 .28.08a.51.51 0 0 0 .5-.5a.54.54 0 0 0-.08-.28a.58.58 0 0 0-.23-.18a.48.48 0 0 0-.29 0zm.23 2.05h-.27l-.87 1.94h2l-.86-1.94zm2.2 4.94l-.89-2h-2.88l-.89 2h4.66z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-record-keys{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M14 3H3a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1zm0 8H3V4h11v7zm-3-6h-1v1h1V5zm-1 2H9v1h1V7zm2-2h1v1h-1V5zm1 4h-1v1h1V9zM6 9h5v1H6V9zm7-2h-2v1h2V7zM8 5h1v1H8V5zm0 2H7v1h1V7zM4 9h1v1H4V9zm0-4h1v1H4V5zm3 0H6v1h1V5zM4 7h2v1H4V7z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-terminal{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 24 24' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' fill-rule='evenodd' d='M3 1.5L1.5 3v18L3 22.5h18l1.5-1.5V3L21 1.5H3zM3 21V3h18v18H3zm5.656-4.01l1.038 1.061l5.26-5.243v-.912l-5.26-5.26l-1.035 1.06l4.59 4.702l-4.593 4.592z' clip-rule='evenodd'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-terminal-bash{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M13.655 3.56L8.918.75a1.785 1.785 0 0 0-1.82 0L2.363 3.56a1.889 1.889 0 0 0-.921 1.628v5.624a1.889 1.889 0 0 0 .913 1.627l4.736 2.812a1.785 1.785 0 0 0 1.82 0l4.736-2.812a1.888 1.888 0 0 0 .913-1.627V5.188a1.889 1.889 0 0 0-.904-1.627zm-3.669 8.781v.404a.149.149 0 0 1-.07.124l-.239.137c-.038.02-.07 0-.07-.053v-.396a.78.78 0 0 1-.545.053a.073.073 0 0 1-.027-.09l.086-.365a.153.153 0 0 1 .071-.096a.048.048 0 0 1 .038 0a.662.662 0 0 0 .497-.063a.662.662 0 0 0 .37-.567c0-.206-.112-.292-.384-.293c-.344 0-.661-.066-.67-.574A1.47 1.47 0 0 1 9.6 9.437V9.03a.147.147 0 0 1 .07-.126l.231-.147c.038-.02.07 0 .07.054v.409a.754.754 0 0 1 .453-.055a.073.073 0 0 1 .03.095l-.081.362a.156.156 0 0 1-.065.09a.055.055 0 0 1-.035 0a.6.6 0 0 0-.436.072a.549.549 0 0 0-.331.486c0 .185.098.242.425.248c.438 0 .627.199.632.639a1.568 1.568 0 0 1-.576 1.185zm2.481-.68a.094.094 0 0 1-.036.092l-1.198.727a.034.034 0 0 1-.04.003a.035.035 0 0 1-.016-.037v-.31a.086.086 0 0 1 .055-.076l1.179-.706a.035.035 0 0 1 .056.035v.273zm.827-6.914L8.812 7.515c-.559.331-.97.693-.97 1.367v5.52c0 .404.165.662.413.741a1.465 1.465 0 0 1-.248.025c-.264 0-.522-.072-.748-.207L2.522 12.15a1.558 1.558 0 0 1-.75-1.338V5.188a1.558 1.558 0 0 1 .75-1.34l4.738-2.81a1.46 1.46 0 0 1 1.489 0l4.736 2.812a1.548 1.548 0 0 1 .728 1.083c-.154-.334-.508-.427-.92-.185h.002z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-codicon-window{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M14.5 2h-13l-.5.5v11l.5.5h13l.5-.5v-11l-.5-.5zM14 13H2V6h12v7zm0-8H2V3h12v2z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-broadcast{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M128 88a40 40 0 1 0 40 40a40 40 0 0 0-40-40Zm0 64a24 24 0 1 1 24-24a24.1 24.1 0 0 1-24 24Zm-59-48.9a64.5 64.5 0 0 0 0 49.8a65.4 65.4 0 0 0 13.7 20.4a7.9 7.9 0 0 1 0 11.3a8 8 0 0 1-5.6 2.3a8.3 8.3 0 0 1-5.7-2.3a80 80 0 0 1-17.1-25.5a79.9 79.9 0 0 1 0-62.2a80 80 0 0 1 17.1-25.5a8 8 0 0 1 11.3 0a7.9 7.9 0 0 1 0 11.3A65.4 65.4 0 0 0 69 103.1Zm132.7 56a80 80 0 0 1-17.1 25.5a8.3 8.3 0 0 1-5.7 2.3a8 8 0 0 1-5.6-2.3a7.9 7.9 0 0 1 0-11.3a65.4 65.4 0 0 0 13.7-20.4a64.5 64.5 0 0 0 0-49.8a65.4 65.4 0 0 0-13.7-20.4a7.9 7.9 0 0 1 0-11.3a8 8 0 0 1 11.3 0a80 80 0 0 1 17.1 25.5a79.9 79.9 0 0 1 0 62.2ZM54.5 201.5a8.1 8.1 0 0 1 0 11.4a8.3 8.3 0 0 1-5.7 2.3a8.5 8.5 0 0 1-5.7-2.3a121.8 121.8 0 0 1-25.7-38.2a120.7 120.7 0 0 1 0-93.4a121.8 121.8 0 0 1 25.7-38.2a8.1 8.1 0 0 1 11.4 11.4A103.5 103.5 0 0 0 24 128a103.5 103.5 0 0 0 30.5 73.5ZM248 128a120.2 120.2 0 0 1-9.4 46.7a121.8 121.8 0 0 1-25.7 38.2a8.5 8.5 0 0 1-5.7 2.3a8.3 8.3 0 0 1-5.7-2.3a8.1 8.1 0 0 1 0-11.4A103.5 103.5 0 0 0 232 128a103.5 103.5 0 0 0-30.5-73.5a8.1 8.1 0 1 1 11.4-11.4a121.8 121.8 0 0 1 25.7 38.2A120.2 120.2 0 0 1 248 128Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-globe-hemisphere-west{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M221.6 173.3A102.9 102.9 0 0 0 232 128a104.2 104.2 0 0 0-77.2-100.5h-.5A103.8 103.8 0 0 0 60.4 49l-1.3 1.2A103.9 103.9 0 0 0 128 232h2.4a104.3 104.3 0 0 0 90.6-57.4ZM216 128a89.3 89.3 0 0 1-5.5 30.7l-46.4-28.5a16.6 16.6 0 0 0-6.3-2.3l-22.8-3a16.1 16.1 0 0 0-15.3 6.8h-8.6l-3.8-7.9a15.9 15.9 0 0 0-11-8.7l-6.6-1.4l4.6-10.8h21.4a16.1 16.1 0 0 0 7.7-2l12.2-6.8a16.1 16.1 0 0 0 3-2.1l26.9-24.4a15.7 15.7 0 0 0 4.5-16.9a88 88 0 0 1 46 77.3Zm-68.8-85.9l7.6 13.7l-26.9 24.3l-12.2 6.8H94.3a15.9 15.9 0 0 0-14.7 9.8l-5.3 12.4l-10.9-29.2l8.1-19.3a88 88 0 0 1 75.7-18.5ZM40 128a87.1 87.1 0 0 1 9.5-39.7l10.4 27.9a16.1 16.1 0 0 0 11.6 10l5.5 1.2h.1l15.8 3.4l3.8 7.9a16.3 16.3 0 0 0 14.4 9h1.2l-7.7 17.2a15.9 15.9 0 0 0 2.8 17.4l18.8 20.4l-2.5 13.2A88.1 88.1 0 0 1 40 128Zm100.1 87.2l1.8-9.5a16 16 0 0 0-3.9-13.9l-18.8-20.3l12.7-28.7l1-2.1l22.8 3.1l47.8 29.4a88.5 88.5 0 0 1-63.4 42Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-hand-waving{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m220.2 104l-20-34.7a28.1 28.1 0 0 0-47.3-1.9l-17.3-30a28.1 28.1 0 0 0-38.3-10.3a29.4 29.4 0 0 0-9.9 9.6a27.9 27.9 0 0 0-11.5-6.2a27.2 27.2 0 0 0-21.2 2.8a27.9 27.9 0 0 0-10.3 38.2l3.4 5.8A28.5 28.5 0 0 0 36 81a28.1 28.1 0 0 0-10.2 38.2l42 72.8a88 88 0 1 0 152.4-88Zm-6.7 62.6a71.2 71.2 0 0 1-33.5 43.7A72.1 72.1 0 0 1 81.6 184l-42-72.8a12 12 0 0 1 20.8-12l22 38.1l.6.9v.2l.5.5l.2.2l.7.6h.1l.7.5h.3l.6.3h.2l.9.3h.1l.8.2h2.2l.9-.2h.3l.6-.2h.3l.9-.4a8.1 8.1 0 0 0 2.9-11l-22-38.1l-16-27.7a12 12 0 0 1-1.2-9.1a11.8 11.8 0 0 1 5.6-7.3a12 12 0 0 1 9.1-1.2a12.5 12.5 0 0 1 7.3 5.6l8 14h.1l26 45a7 7 0 0 0 1.5 1.9a8 8 0 0 0 12.3-9.9l-26-45a12 12 0 1 1 20.8-12l30 51.9l6.3 11a48.1 48.1 0 0 0-10.9 61a8 8 0 0 0 13.8-8a32 32 0 0 1 11.7-43.7l.7-.4l.5-.4h.1l.6-.6l.5-.5l.4-.5l.3-.6h.1l.2-.5v-.2a1.9 1.9 0 0 0 .2-.7h.1c0-.2.1-.4.1-.6s0-.2.1-.2v-2.1a6.4 6.4 0 0 0-.2-.7a1.9 1.9 0 0 0-.2-.7v-.2c0-.2-.1-.3-.2-.5l-.3-.7l-10-17.4a12 12 0 0 1 13.5-17.5a11.8 11.8 0 0 1 7.2 5.5l20 34.7a70.9 70.9 0 0 1 7.2 53.8Zm-125.8 78a8.2 8.2 0 0 1-6.6 3.4a8.6 8.6 0 0 1-4.6-1.4A117.9 117.9 0 0 1 41.1 208a8 8 0 1 1 13.8-8a102.6 102.6 0 0 0 30.8 33.4a8.1 8.1 0 0 1 2 11.2ZM168 31a8 8 0 0 1 8-8a60.2 60.2 0 0 1 52 30a7.9 7.9 0 0 1-3 10.9a7.1 7.1 0 0 1-4 1.1a8 8 0 0 1-6.9-4A44 44 0 0 0 176 39a8 8 0 0 1-8-8Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-moon{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M224.3 150.3a8.1 8.1 0 0 0-7.8-5.7l-2.2.4A84 84 0 0 1 111 41.6a5.7 5.7 0 0 0 .3-1.8a7.9 7.9 0 0 0-10.3-8.1a100 100 0 1 0 123.3 123.2a7.2 7.2 0 0 0 0-4.6ZM128 212A84 84 0 0 1 92.8 51.7a99.9 99.9 0 0 0 111.5 111.5A84.4 84.4 0 0 1 128 212Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.i-ph-sun{--un-icon:url("data:image/svg+xml;utf8,%3Csvg preserveAspectRatio='xMidYMid meet' viewBox='0 0 256 256' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M128 60a68 68 0 1 0 68 68a68.1 68.1 0 0 0-68-68Zm0 120a52 52 0 1 1 52-52a52 52 0 0 1-52 52Zm-8-144V16a8 8 0 0 1 16 0v20a8 8 0 0 1-16 0ZM43.1 54.5a8.1 8.1 0 1 1 11.4-11.4l14.1 14.2a8 8 0 0 1 0 11.3a8.1 8.1 0 0 1-11.3 0ZM36 136H16a8 8 0 0 1 0-16h20a8 8 0 0 1 0 16Zm32.6 51.4a8 8 0 0 1 0 11.3l-14.1 14.2a8.3 8.3 0 0 1-5.7 2.3a8.5 8.5 0 0 1-5.7-2.3a8.1 8.1 0 0 1 0-11.4l14.2-14.1a8 8 0 0 1 11.3 0ZM136 220v20a8 8 0 0 1-16 0v-20a8 8 0 0 1 16 0Zm76.9-18.5a8.1 8.1 0 0 1 0 11.4a8.5 8.5 0 0 1-5.7 2.3a8.3 8.3 0 0 1-5.7-2.3l-14.1-14.2a8 8 0 0 1 11.3-11.3ZM248 128a8 8 0 0 1-8 8h-20a8 8 0 0 1 0-16h20a8 8 0 0 1 8 8Zm-60.6-59.4a8 8 0 0 1 0-11.3l14.1-14.2a8.1 8.1 0 0 1 11.4 11.4l-14.2 14.1a8.1 8.1 0 0 1-11.3 0Z'/%3E%3C/svg%3E");mask:var(--un-icon) no-repeat;mask-size:100% 100%;-webkit-mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;background-color:currentColor;width:1em;height:1em;}.note{position:relative;display:inline-flex;align-items:center;border-left-width:4px;border-left-style:solid;--un-border-opacity:1;border-color:rgba(53,120,229,var(--un-border-opacity));border-radius:0.25rem;background-color:rgba(53,120,229,0.1);padding:0.5rem;text-decoration:none;}.note-red{position:relative;display:inline-flex;align-items:center;border-left-width:4px;border-left-style:solid;--un-border-opacity:1;border-color:rgba(53,120,229,var(--un-border-opacity));border-radius:0.25rem;background-color:rgba(53,120,229,0.1);background-color:rgba(185,28,28,0.1);padding:0.5rem;text-decoration:none;}.nv{position:relative;display:flex;align-items:center;border-radius:0.25rem;padding:0.5rem;--un-text-opacity:1;color:rgba(194,197,202,var(--un-text-opacity));text-decoration:none;transition-property:all;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:125ms;}.nv_selected{position:relative;display:flex;align-items:center;border-left-width:4px;border-left-style:solid;border-radius:0.25rem;--un-bg-opacity:.05;background-color:hsla(0,0%,100%,var(--un-bg-opacity));padding:0.5rem;--un-text-opacity:1;color:rgba(194,197,202,var(--un-text-opacity));color:rgba(53,120,229,var(--un-text-opacity));text-decoration:none;transition-property:all;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:125ms;}.input{height:2.5rem;display:flex;align-items:center;border-radius:0.25rem;border-style:none;--un-bg-opacity:1;background-color:rgba(233,236,239,var(--un-bg-opacity));padding:0.5rem;--un-text-opacity:1;color:rgba(28,30,33,var(--un-text-opacity));--un-shadow:var(--un-shadow-inset) 0 4px 6px -1px var(--un-shadow-color, rgba(0,0,0,0.1)),var(--un-shadow-inset) 0 2px 4px -2px var(--un-shadow-color, rgba(0,0,0,0.1));box-shadow:var(--un-ring-offset-shadow, 0 0 #0000), var(--un-ring-shadow, 0 0 #0000), var(--un-shadow);outline:2px solid transparent;outline-offset:2px;}.btn{user-select:none;border-radius:0.25rem;border-style:none;--un-bg-opacity:1;background-color:rgba(53,120,229,var(--un-bg-opacity));padding:0.5rem;font-weight:400;--un-text-opacity:1;color:rgba(28,30,33,var(--un-text-opacity));color:rgba(255,255,255,var(--un-text-opacity));--un-shadow:var(--un-shadow-inset) 0 4px 6px -1px var(--un-shadow-color, rgba(0,0,0,0.1)),var(--un-shadow-inset) 0 2px 4px -2px var(--un-shadow-color, rgba(0,0,0,0.1));box-shadow:var(--un-ring-offset-shadow, 0 0 #0000), var(--un-ring-shadow, 0 0 #0000), var(--un-shadow);outline:2px solid transparent;outline-offset:2px;}.nv_selected:hover,.nv:hover{border-left-width:4px;border-left-style:solid;--un-bg-opacity:.05;background-color:hsla(0,0%,100%,var(--un-bg-opacity));--un-text-opacity:1;color:rgba(53,120,229,var(--un-text-opacity));}.dark .note{--un-border-opacity:1;border-color:rgba(103,214,237,var(--un-border-opacity));background-color:rgba(103,214,237,0.1);}.dark .note-red{--un-border-opacity:1;border-color:rgba(103,214,237,var(--un-border-opacity));background-color:rgba(103,214,237,0.1);background-color:rgba(185,28,28,0.1);}.btn:hover{--un-bg-opacity:1;background-color:rgba(45,102,195,var(--un-bg-opacity));}.dark .btn{--un-bg-opacity:1;background-color:rgba(103,214,237,var(--un-bg-opacity));font-weight:600;--un-text-opacity:1;color:rgba(28,30,33,var(--un-text-opacity));}.dark .btn:hover{--un-bg-opacity:1;background-color:rgba(57,202,232,var(--un-bg-opacity));}.dark .input{--un-bg-opacity:1;background-color:rgba(36,37,38,var(--un-bg-opacity));--un-text-opacity:1;color:rgba(227,227,227,var(--un-text-opacity));}.dark .note-red::after,.note-red::after{--un-bg-opacity:1;background-color:rgba(185,28,28,var(--un-bg-opacity));}.btn:active{--un-bg-opacity:1;background-color:rgba(37,84,160,var(--un-bg-opacity));}.dark .btn:active{--un-bg-opacity:1;background-color:rgba(25,181,213,var(--un-bg-opacity));}.dark .nv_selected,.dark .nv_selected:hover,.dark .nv:hover{--un-text-opacity:1;color:rgba(103,214,237,var(--un-text-opacity));} ::-webkit-scrollbar-thumb { background-color: #3578E5; } .dark ::-webkit-scrollbar-thumb { background-color: #67d6ed; } code { font-size: 0.75rem; font-family: "Fira Code","Fira Mono",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; border-radius: 0.25rem; background-color: #d6d8da; } .code-block { font-family: "Fira Code","Fira Mono",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; font-size: 0.875rem; } .dark code { background-color: #282a2e; } .visible{visibility:visible;}.absolute{position:absolute;}.left-2{left:0.5rem;}.top-2{top:0.5rem;}.z-2000{z-index:2000;}.grid{display:grid;}.grid-rows-\[2fr_auto\]{grid-template-rows:2fr auto;}.grid-rows-\[2px_2rem_1fr\]{grid-template-rows:2px 2rem 1fr;}.grid-rows-\[auto_1fr\]{grid-template-rows:auto 1fr;}.my-2{margin-top:0.5rem;margin-bottom:0.5rem;}.mb-2{margin-bottom:0.5rem;}.mr-2{margin-right:0.5rem;}.display-none,.hidden{display:none;}.children-h-10>*,.children\:h10>*{height:2.5rem;}.children\:h-100\%>*,.h-100\%{height:100%;}.children\:w-12>*{width:3rem;}.h-15rem{height:15rem;}.h-2px{height:2px;}.h-8{height:2rem;}.h-85\%{height:85%;}.h-auto{height:auto;}.h-screen{height:100vh;}.w-100\%{width:100%;}.w-8{width:2rem;}.w-screen{width:100vw;}.flex{display:flex;}.children\:inline-flex>*{display:inline-flex;}.flex-1{flex:1 1 0%;}.children-flex-none>*{flex:none;}.children\:grow>*,.grow{flex-grow:1;}.flex-row{flex-direction:row;}.flex-col{flex-direction:column;}.flex-wrap{flex-wrap:wrap;}@keyframes fade-in{from{opacity:0}to{opacity:1}}@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.animate-fade-in{animation:fade-in 1s linear 1;}.animate-spin{animation:spin 1s linear infinite;}.animate-duration-300ms{animation-duration:300ms;}.cursor-ns-resize{cursor:ns-resize;}.cursor-pointer{cursor:pointer;}.select-none{user-select:none;}.children\:items-center>*,.items-center{align-items:center;}.self-center{align-self:center;}.children\:justify-center>*,.justify-center{justify-content:center;}.justify-between{justify-content:space-between;}.gap-1{grid-gap:0.25rem;gap:0.25rem;}.gap-2{grid-gap:0.5rem;gap:0.5rem;}.overflow-hidden{overflow:hidden;}.overflow-y-auto{overflow-y:auto;}.rd-1{border-radius:0.25rem;}.rd-8{border-radius:2rem;}.bg-accent{--un-bg-opacity:1;background-color:rgba(53,120,229,var(--un-bg-opacity));}.bg-black\/20{background-color:rgba(0,0,0,0.2);}.bg-darkPrimaryLighter{--un-bg-opacity:1;background-color:rgba(36,37,38,var(--un-bg-opacity));}.bg-primary{--un-bg-opacity:1;background-color:rgba(255,255,255,var(--un-bg-opacity));}.bg-white\/5{background-color:rgba(255,255,255,0.05);}.dark .dark\:bg-darkAccent{--un-bg-opacity:1;background-color:rgba(103,214,237,var(--un-bg-opacity));}.dark .dark\:bg-darkPrimary{--un-bg-opacity:1;background-color:rgba(27,27,29,var(--un-bg-opacity));}.dark .dark\:hover\:bg-darkHoverOverlay:hover{--un-bg-opacity:.05;background-color:hsla(0,0%,100%,var(--un-bg-opacity));}.dark .dark\:hover\:bg-red-700:hover,.hover\:bg-red-700:hover{--un-bg-opacity:1;background-color:rgba(185,28,28,var(--un-bg-opacity));}.hover\:bg-hoverOverlay:hover{--un-bg-opacity:.05;background-color:rgba(0,0,0,var(--un-bg-opacity));}.active\:bg-accentDark:active{--un-bg-opacity:1;background-color:rgba(48,108,206,var(--un-bg-opacity));}.active\:bg-hoverOverlay\/25:active{background-color:rgba(0,0,0,0.25);}.active\:bg-hoverOverlayDarker:active{--un-bg-opacity:.1;background-color:rgba(0,0,0,var(--un-bg-opacity));}.active\:bg-red-700\/90:active,.dark .dark\:active\:bg-red-700\/90:active{background-color:rgba(185,28,28,0.9);}.dark .dark\:active\:bg-darkAccentDark:active{--un-bg-opacity:1;background-color:rgba(73,206,233,var(--un-bg-opacity));}.dark .dark\:active\:bg-darkHoverOverlay\/25:active{background-color:hsla(0,0%,100%,0.25);}.dark .dark\:active\:bg-darkHoverOverlayDarker:active{--un-bg-opacity:.1;background-color:hsla(0,0%,100%,var(--un-bg-opacity));}.p-1{padding:0.25rem;}.p-7{padding:1.75rem;}.px{padding-left:1rem;padding-right:1rem;}.px-2{padding-left:0.5rem;padding-right:0.5rem;}.px-5{padding-left:1.25rem;padding-right:1.25rem;}.children-pb-2>*{padding-bottom:0.5rem;}.children-pt8>*{padding-top:2rem;}.pl-2{padding-left:0.5rem;}.all\:font-mono *{font-family:"Fira Code","Fira Mono",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;}.all\:text-xs *{font-size:0.75rem;line-height:1rem;}.text-sm{font-size:0.875rem;line-height:1.25rem;}.font-700{font-weight:700;}.font-semibold{font-weight:600;}.dark .dark\:text-darkAccent{--un-text-opacity:1;color:rgba(103,214,237,var(--un-text-opacity));}.dark .dark\:text-darkAccentText,.text-primaryText{--un-text-opacity:1;color:rgba(28,30,33,var(--un-text-opacity));}.dark .dark\:text-darkPrimaryText,.hover\:text-darkPrimaryText:hover,.text-darkPrimaryText,.active\:text-darkPrimaryText:active{--un-text-opacity:1;color:rgba(227,227,227,var(--un-text-opacity));}.text-accent{--un-text-opacity:1;color:rgba(53,120,229,var(--un-text-opacity));}.text-accentText{--un-text-opacity:1;color:rgba(255,255,255,var(--un-text-opacity));}.filter{filter:var(--un-blur) var(--un-brightness) var(--un-contrast) var(--un-drop-shadow) var(--un-grayscale) var(--un-hue-rotate) var(--un-invert) var(--un-saturate) var(--un-sepia);}.transition-colors-250{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:250ms;}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}@media (max-width: 639.9px){.lt-sm\:absolute{position:absolute;}.lt-sm\:z-1999{z-index:1999;}.lt-sm\:h-screen{height:100vh;}.lt-sm\:flex{display:flex;}.lt-sm\:pl-10{padding-left:2.5rem;}.lt-sm\:shadow{--un-shadow:var(--un-shadow-inset) 0 1px 3px 0 var(--un-shadow-color, rgba(0,0,0,0.1)),var(--un-shadow-inset) 0 1px 2px -1px var(--un-shadow-color, rgba(0,0,0,0.1));box-shadow:var(--un-ring-offset-shadow, 0 0 #0000), var(--un-ring-shadow, 0 0 #0000), var(--un-shadow);}.lt-sm\:shadow-lg{--un-shadow:var(--un-shadow-inset) 0 10px 15px -3px var(--un-shadow-color, rgba(0,0,0,0.1)),var(--un-shadow-inset) 0 4px 6px -4px var(--un-shadow-color, rgba(0,0,0,0.1));box-shadow:var(--un-ring-offset-shadow, 0 0 #0000), var(--un-ring-shadow, 0 0 #0000), var(--un-shadow);}.lt-sm\:transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}}*:not(h1,h2,h3,h4,h5,h6){margin:0;padding:0}*{box-sizing:border-box;font-family:Rubik,sans-serif}::-webkit-scrollbar{width:.25rem;height:3px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{border-radius:.25rem}code{padding:.05rem .25rem}code.code-block{padding:.5rem}#sidebar{width:18.75rem}@media screen and (max-width: 640px){#sidebar{--translate-x: -18.75rem;transform:translate(var(--translate-x))}}ul.svelte-gbh3pt{list-style:none;margin:0;padding:0;padding-left:var(--nodePaddingLeft, 1rem);border-left:var(--nodeBorderLeft, 1px dotted #9ca3af);color:var(--nodeColor, #374151)}.hidden.svelte-gbh3pt{display:none}.bracket.svelte-gbh3pt{cursor:pointer}.bracket.svelte-gbh3pt:hover{background:var(--bracketHoverBackground, #d1d5db)}.comma.svelte-gbh3pt{color:var(--nodeColor, #374151)}.val.svelte-gbh3pt{color:var(--leafDefaultColor, #9ca3af)}.val.string.svelte-gbh3pt{color:var(--leafStringColor, #059669)}.val.number.svelte-gbh3pt{color:var(--leafNumberColor, #d97706)}.val.boolean.svelte-gbh3pt{color:var(--leafBooleanColor, #2563eb)}.spinner.svelte-4xesec{height:1.2rem;width:1.2rem;border-radius:50rem;color:currentColor;border:2px dashed currentColor} diff --git a/examples/api/dist/assets/index.js b/examples/api/dist/assets/index.js index f86184eb141..6a4093e32f5 100644 --- a/examples/api/dist/assets/index.js +++ b/examples/api/dist/assets/index.js @@ -1,46 +1,46 @@ -const to=function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))i(l);new MutationObserver(l=>{for(const o of l)if(o.type==="childList")for(const u of o.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&i(u)}).observe(document,{childList:!0,subtree:!0});function n(l){const o={};return l.integrity&&(o.integrity=l.integrity),l.referrerpolicy&&(o.referrerPolicy=l.referrerpolicy),l.crossorigin==="use-credentials"?o.credentials="include":l.crossorigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function i(l){if(l.ep)return;l.ep=!0;const o=n(l);fetch(l.href,o)}};to();function V(){}function bs(e){return e()}function jl(){return Object.create(null)}function se(e){e.forEach(bs)}function no(e){return typeof e=="function"}function me(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}let Un;function io(e,t){return Un||(Un=document.createElement("a")),Un.href=t,e===Un.href}function lo(e){return Object.keys(e).length===0}function so(e,...t){if(e==null)return V;const n=e.subscribe(...t);return n.unsubscribe?()=>n.unsubscribe():n}function gs(e,t,n){e.$$.on_destroy.push(so(t,n))}function s(e,t){e.appendChild(t)}function m(e,t,n){e.insertBefore(t,n||null)}function p(e){e.parentNode.removeChild(e)}function ft(e,t){for(let n=0;ne.removeEventListener(t,n,i)}function Xn(e){return function(t){return t.preventDefault(),e.call(this,t)}}function a(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function le(e){return e===""?null:+e}function ro(e){return Array.from(e.childNodes)}function K(e,t){t=""+t,e.wholeText!==t&&(e.data=t)}function B(e,t){e.value=t==null?"":t}function zt(e,t){for(let n=0;n{Gn.delete(e),i&&(n&&e.d(1),i())}),e.o(t)}else i&&i()}function $n(e){e&&e.c()}function Vt(e,t,n,i){const{fragment:l,on_mount:o,on_destroy:u,after_update:d}=e.$$;l&&l.m(t,n),i||Dt(()=>{const c=o.map(bs).filter(no);u?u.push(...c):se(c),e.$$.on_mount=[]}),d.forEach(Dt)}function Gt(e,t){const n=e.$$;n.fragment!==null&&(se(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function po(e,t){e.$$.dirty[0]===-1&&(jt.push(e),co(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{const g=y.length?y[0]:_;return f.ctx&&l(f.ctx[k],f.ctx[k]=g)&&(!f.skip_bound&&f.bound[k]&&f.bound[k](g),v&&po(e,k)),_}):[],f.update(),v=!0,se(f.before_update),f.fragment=i?i(f.ctx):!1,t.target){if(t.hydrate){const k=ro(t.target);f.fragment&&f.fragment.l(k),k.forEach(p)}else f.fragment&&f.fragment.c();t.intro&&ze(e.$$.fragment),Vt(e,t.target,t.anchor,t.customElement),ys()}qt(c)}class we{$destroy(){Gt(this,1),this.$destroy=V}$on(t,n){const i=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return i.push(n),()=>{const l=i.indexOf(n);l!==-1&&i.splice(l,1)}}$set(t){this.$$set&&!lo(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const Et=[];function ws(e,t=V){let n;const i=new Set;function l(d){if(me(e,d)&&(e=d,n)){const c=!Et.length;for(const f of i)f[1](),Et.push(f,e);if(c){for(let f=0;f{i.delete(f),i.size===0&&(n(),n=null)}}return{set:l,update:o,subscribe:u}}var mo=Object.defineProperty,Te=(e,t)=>{for(var n in t)mo(e,n,{get:t[n],enumerable:!0})},ho={};Te(ho,{convertFileSrc:()=>ks,invoke:()=>Kn,transformCallback:()=>pt});function _o(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function pt(e,t=!1){let n=_o(),i=`_${n}`;return Object.defineProperty(window,i,{value:l=>(t&&Reflect.deleteProperty(window,i),e==null?void 0:e(l)),writable:!1,configurable:!0}),n}async function Kn(e,t={}){return new Promise((n,i)=>{let l=pt(u=>{n(u),Reflect.deleteProperty(window,`_${o}`)},!0),o=pt(u=>{i(u),Reflect.deleteProperty(window,`_${l}`)},!0);window.__TAURI_IPC__({cmd:e,callback:l,error:o,...t})})}function ks(e,t="asset"){let n=encodeURIComponent(e);return navigator.userAgent.includes("Windows")?`https://${t}.localhost/${n}`:`${t}://localhost/${n}`}async function L(e){return Kn("tauri",e)}var bo={};Te(bo,{Child:()=>Ms,Command:()=>Wi,EventEmitter:()=>Jn,open:()=>Oi});async function go(e,t,n=[],i){return typeof n=="object"&&Object.freeze(n),L({__tauriModule:"Shell",message:{cmd:"execute",program:t,args:n,options:i,onEventFn:pt(e)}})}var Jn=class{constructor(){this.eventListeners=Object.create(null)}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}on(e,t){return e in this.eventListeners?this.eventListeners[e].push(t):this.eventListeners[e]=[t],this}once(e,t){let n=(...i)=>{this.removeListener(e,n),t(...i)};return this.addListener(e,n)}off(e,t){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter(n=>n!==t)),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,...t){if(e in this.eventListeners){let n=this.eventListeners[e];for(let i of n)i(...t);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,t){return e in this.eventListeners?this.eventListeners[e].unshift(t):this.eventListeners[e]=[t],this}prependOnceListener(e,t){let n=(...i)=>{this.removeListener(e,n),t(...i)};return this.prependListener(e,n)}},Ms=class{constructor(e){this.pid=e}async write(e){return L({__tauriModule:"Shell",message:{cmd:"stdinWrite",pid:this.pid,buffer:typeof e=="string"?e:Array.from(e)}})}async kill(){return L({__tauriModule:"Shell",message:{cmd:"killChild",pid:this.pid}})}},Wi=class extends Jn{constructor(e,t=[],n){super(),this.stdout=new Jn,this.stderr=new Jn,this.program=e,this.args=typeof t=="string"?[t]:t,this.options=n!=null?n:{}}static sidecar(e,t=[],n){let i=new Wi(e,t,n);return i.options.sidecar=!0,i}async spawn(){return go(e=>{switch(e.event){case"Error":this.emit("error",e.payload);break;case"Terminated":this.emit("close",e.payload);break;case"Stdout":this.stdout.emit("data",e.payload);break;case"Stderr":this.stderr.emit("data",e.payload);break}},this.program,this.args,this.options).then(e=>new Ms(e))}async execute(){return new Promise((e,t)=>{this.on("error",t);let n=[],i=[];this.stdout.on("data",l=>{n.push(l)}),this.stderr.on("data",l=>{i.push(l)}),this.on("close",l=>{e({code:l.code,signal:l.signal,stdout:n.join(` +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))i(l);new MutationObserver(l=>{for(const o of l)if(o.type==="childList")for(const u of o.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&i(u)}).observe(document,{childList:!0,subtree:!0});function n(l){const o={};return l.integrity&&(o.integrity=l.integrity),l.referrerpolicy&&(o.referrerPolicy=l.referrerpolicy),l.crossorigin==="use-credentials"?o.credentials="include":l.crossorigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function i(l){if(l.ep)return;l.ep=!0;const o=n(l);fetch(l.href,o)}})();function V(){}function bs(e){return e()}function jl(){return Object.create(null)}function ue(e){e.forEach(bs)}function to(e){return typeof e=="function"}function he(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}let Un;function no(e,t){return Un||(Un=document.createElement("a")),Un.href=t,e===Un.href}function io(e){return Object.keys(e).length===0}function lo(e,...t){if(e==null)return V;const n=e.subscribe(...t);return n.unsubscribe?()=>n.unsubscribe():n}function gs(e,t,n){e.$$.on_destroy.push(lo(t,n))}function s(e,t){e.appendChild(t)}function m(e,t,n){e.insertBefore(t,n||null)}function p(e){e.parentNode.removeChild(e)}function ft(e,t){for(let n=0;ne.removeEventListener(t,n,i)}function Xn(e){return function(t){return t.preventDefault(),e.call(this,t)}}function r(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function re(e){return e===""?null:+e}function oo(e){return Array.from(e.childNodes)}function $(e,t){t=""+t,e.wholeText!==t&&(e.data=t)}function q(e,t){e.value=t==null?"":t}function zt(e,t){for(let n=0;n{Gn.delete(e),i&&(n&&e.d(1),i())}),e.o(t)}else i&&i()}function Kn(e){e&&e.c()}function Vt(e,t,n,i){const{fragment:l,on_mount:o,on_destroy:u,after_update:d}=e.$$;l&&l.m(t,n),i||Dt(()=>{const c=o.map(bs).filter(to);u?u.push(...c):ue(c),e.$$.on_mount=[]}),d.forEach(Dt)}function Gt(e,t){const n=e.$$;n.fragment!==null&&(ue(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function fo(e,t){e.$$.dirty[0]===-1&&(jt.push(e),uo(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{const g=v.length?v[0]:_;return f.ctx&&l(f.ctx[k],f.ctx[k]=g)&&(!f.skip_bound&&f.bound[k]&&f.bound[k](g),y&&fo(e,k)),_}):[],f.update(),y=!0,ue(f.before_update),f.fragment=i?i(f.ctx):!1,t.target){if(t.hydrate){const k=oo(t.target);f.fragment&&f.fragment.l(k),k.forEach(p)}else f.fragment&&f.fragment.c();t.intro&&Me(e.$$.fragment),Vt(e,t.target,t.anchor,t.customElement),vs()}qt(c)}class ye{$destroy(){Gt(this,1),this.$destroy=V}$on(t,n){const i=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return i.push(n),()=>{const l=i.indexOf(n);l!==-1&&i.splice(l,1)}}$set(t){this.$$set&&!io(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const Et=[];function ws(e,t=V){let n;const i=new Set;function l(d){if(he(e,d)&&(e=d,n)){const c=!Et.length;for(const f of i)f[1](),Et.push(f,e);if(c){for(let f=0;f{i.delete(f),i.size===0&&(n(),n=null)}}return{set:l,update:o,subscribe:u}}var po=Object.defineProperty,we=(e,t)=>{for(var n in t)po(e,n,{get:t[n],enumerable:!0})},mo={};we(mo,{convertFileSrc:()=>ks,invoke:()=>$n,transformCallback:()=>pt});function ho(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function pt(e,t=!1){let n=ho(),i=`_${n}`;return Object.defineProperty(window,i,{value:l=>(t&&Reflect.deleteProperty(window,i),e==null?void 0:e(l)),writable:!1,configurable:!0}),n}async function $n(e,t={}){return new Promise((n,i)=>{let l=pt(u=>{n(u),Reflect.deleteProperty(window,`_${o}`)},!0),o=pt(u=>{i(u),Reflect.deleteProperty(window,`_${l}`)},!0);window.__TAURI_IPC__({cmd:e,callback:l,error:o,...t})})}function ks(e,t="asset"){let n=encodeURIComponent(e);return navigator.userAgent.includes("Windows")?`https://${t}.localhost/${n}`:`${t}://localhost/${n}`}async function S(e){return $n("tauri",e)}var _o={};we(_o,{Child:()=>Ms,Command:()=>Oi,EventEmitter:()=>Jn,open:()=>Pi});async function bo(e,t,n=[],i){return typeof n=="object"&&Object.freeze(n),S({__tauriModule:"Shell",message:{cmd:"execute",program:t,args:n,options:i,onEventFn:pt(e)}})}var Jn=class{constructor(){this.eventListeners=Object.create(null)}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}on(e,t){return e in this.eventListeners?this.eventListeners[e].push(t):this.eventListeners[e]=[t],this}once(e,t){let n=(...i)=>{this.removeListener(e,n),t(...i)};return this.addListener(e,n)}off(e,t){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter(n=>n!==t)),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,...t){if(e in this.eventListeners){let n=this.eventListeners[e];for(let i of n)i(...t);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,t){return e in this.eventListeners?this.eventListeners[e].unshift(t):this.eventListeners[e]=[t],this}prependOnceListener(e,t){let n=(...i)=>{this.removeListener(e,n),t(...i)};return this.prependListener(e,n)}},Ms=class{constructor(e){this.pid=e}async write(e){return S({__tauriModule:"Shell",message:{cmd:"stdinWrite",pid:this.pid,buffer:typeof e=="string"?e:Array.from(e)}})}async kill(){return S({__tauriModule:"Shell",message:{cmd:"killChild",pid:this.pid}})}},Oi=class extends Jn{constructor(e,t=[],n){super(),this.stdout=new Jn,this.stderr=new Jn,this.program=e,this.args=typeof t=="string"?[t]:t,this.options=n!=null?n:{}}static sidecar(e,t=[],n){let i=new Oi(e,t,n);return i.options.sidecar=!0,i}async spawn(){return bo(e=>{switch(e.event){case"Error":this.emit("error",e.payload);break;case"Terminated":this.emit("close",e.payload);break;case"Stdout":this.stdout.emit("data",e.payload);break;case"Stderr":this.stderr.emit("data",e.payload);break}},this.program,this.args,this.options).then(e=>new Ms(e))}async execute(){return new Promise((e,t)=>{this.on("error",t);let n=[],i=[];this.stdout.on("data",l=>{n.push(l)}),this.stderr.on("data",l=>{i.push(l)}),this.on("close",l=>{e({code:l.code,signal:l.signal,stdout:n.join(` `),stderr:i.join(` -`)})}),this.spawn().catch(t)})}};async function Oi(e,t){return L({__tauriModule:"Shell",message:{cmd:"open",path:e,with:t}})}var vo={};Te(vo,{TauriEvent:()=>Ls,emit:()=>ni,listen:()=>Xt,once:()=>Ss});async function Cs(e,t){return L({__tauriModule:"Event",message:{cmd:"unlisten",event:e,eventId:t}})}async function Ts(e,t,n){await L({__tauriModule:"Event",message:{cmd:"emit",event:e,windowLabel:t,payload:n}})}async function Ri(e,t,n){return L({__tauriModule:"Event",message:{cmd:"listen",event:e,windowLabel:t,handler:pt(n)}}).then(i=>async()=>Cs(e,i))}async function As(e,t,n){return Ri(e,t,i=>{n(i),Cs(e,i.id).catch(()=>{})})}var Ls=(e=>(e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_FILE_DROP="tauri://file-drop",e.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",e.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",e.MENU="tauri://menu",e.CHECK_UPDATE="tauri://update",e.UPDATE_AVAILABLE="tauri://update-available",e.INSTALL_UPDATE="tauri://update-install",e.STATUS_UPDATE="tauri://update-status",e.DOWNLOAD_PROGRESS="tauri://update-download-progress",e))(Ls||{});async function Xt(e,t){return Ri(e,null,t)}async function Ss(e,t){return As(e,null,t)}async function ni(e,t){return Ts(e,void 0,t)}var yo={};Te(yo,{CloseRequestedEvent:()=>Ws,LogicalPosition:()=>Es,LogicalSize:()=>Qn,PhysicalPosition:()=>xe,PhysicalSize:()=>dt,UserAttentionType:()=>Ii,WebviewWindow:()=>mt,WebviewWindowHandle:()=>Ds,WindowManager:()=>Ps,appWindow:()=>je,availableMonitors:()=>Mo,currentMonitor:()=>wo,getAll:()=>zs,getCurrent:()=>Ut,primaryMonitor:()=>ko});var Qn=class{constructor(e,t){this.type="Logical",this.width=e,this.height=t}},dt=class{constructor(e,t){this.type="Physical",this.width=e,this.height=t}toLogical(e){return new Qn(this.width/e,this.height/e)}},Es=class{constructor(e,t){this.type="Logical",this.x=e,this.y=t}},xe=class{constructor(e,t){this.type="Physical",this.x=e,this.y=t}toLogical(e){return new Es(this.x/e,this.y/e)}},Ii=(e=>(e[e.Critical=1]="Critical",e[e.Informational=2]="Informational",e))(Ii||{});function Ut(){return new mt(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0})}function zs(){return window.__TAURI_METADATA__.__windows.map(e=>new mt(e.label,{skip:!0}))}var ql=["tauri://created","tauri://error"],Ds=class{constructor(e){this.label=e,this.listeners=Object.create(null)}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let n=this.listeners[e];n.splice(n.indexOf(t),1)}):Ri(e,this.label,t)}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let n=this.listeners[e];n.splice(n.indexOf(t),1)}):As(e,this.label,t)}async emit(e,t){if(ql.includes(e)){for(let n of this.listeners[e]||[])n({event:e,id:-1,windowLabel:this.label,payload:t});return Promise.resolve()}return Ts(e,this.label,t)}_handleTauriEvent(e,t){return ql.includes(e)?(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0):!1}},Ps=class extends Ds{async scaleFactor(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"scaleFactor"}}}})}async innerPosition(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerPosition"}}}}).then(({x:e,y:t})=>new xe(e,t))}async outerPosition(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerPosition"}}}}).then(({x:e,y:t})=>new xe(e,t))}async innerSize(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerSize"}}}}).then(({width:e,height:t})=>new dt(e,t))}async outerSize(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerSize"}}}}).then(({width:e,height:t})=>new dt(e,t))}async isFullscreen(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isFullscreen"}}}})}async isMaximized(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMaximized"}}}})}async isDecorated(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isDecorated"}}}})}async isResizable(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isResizable"}}}})}async isVisible(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isVisible"}}}})}async theme(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"theme"}}}})}async center(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"center"}}}})}async requestUserAttention(e){let t=null;return e&&(e===1?t={type:"Critical"}:t={type:"Informational"}),L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"requestUserAttention",payload:t}}}})}async setResizable(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setResizable",payload:e}}}})}async setTitle(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setTitle",payload:e}}}})}async maximize(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"maximize"}}}})}async unmaximize(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unmaximize"}}}})}async toggleMaximize(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"toggleMaximize"}}}})}async minimize(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"minimize"}}}})}async unminimize(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unminimize"}}}})}async show(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"show"}}}})}async hide(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"hide"}}}})}async close(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"close"}}}})}async setDecorations(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setDecorations",payload:e}}}})}async setAlwaysOnTop(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setAlwaysOnTop",payload:e}}}})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSize",payload:{type:e.type,data:{width:e.width,height:e.height}}}}}})}async setMinSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMinSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setMaxSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMaxSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setFullscreen(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFullscreen",payload:e}}}})}async setFocus(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFocus"}}}})}async setIcon(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIcon",payload:{icon:typeof e=="string"?e:Array.from(e)}}}}})}async setSkipTaskbar(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSkipTaskbar",payload:e}}}})}async setCursorGrab(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorGrab",payload:e}}}})}async setCursorVisible(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorVisible",payload:e}}}})}async setCursorIcon(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorIcon",payload:e}}}})}async setCursorPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setIgnoreCursorEvents(e){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIgnoreCursorEvents",payload:e}}}})}async startDragging(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"startDragging"}}}})}async onResized(e){return this.listen("tauri://resize",e)}async onMoved(e){return this.listen("tauri://move",e)}async onCloseRequested(e){return this.listen("tauri://close-requested",t=>{let n=new Ws(t);Promise.resolve(e(n)).then(()=>{if(!n.isPreventDefault())return this.close()})})}async onFocusChanged(e){let t=await this.listen("tauri://focus",i=>{e({...i,payload:!0})}),n=await this.listen("tauri://blur",i=>{e({...i,payload:!1})});return()=>{t(),n()}}async onScaleChanged(e){return this.listen("tauri://scale-change",e)}async onMenuClicked(e){return this.listen("tauri://menu",e)}async onFileDropEvent(e){let t=await this.listen("tauri://file-drop",l=>{e({...l,payload:{type:"drop",paths:l.payload}})}),n=await this.listen("tauri://file-drop-hover",l=>{e({...l,payload:{type:"hover",paths:l.payload}})}),i=await this.listen("tauri://file-drop-cancelled",l=>{e({...l,payload:{type:"cancel"}})});return()=>{t(),n(),i()}}async onThemeChanged(e){return this.listen("tauri://theme-changed",e)}},Ws=class{constructor(e){this._preventDefault=!1,this.event=e.event,this.windowLabel=e.windowLabel,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}},mt=class extends Ps{constructor(e,t={}){super(e),t!=null&&t.skip||L({__tauriModule:"Window",message:{cmd:"createWebview",data:{options:{label:e,...t}}}}).then(async()=>this.emit("tauri://created")).catch(async n=>this.emit("tauri://error",n))}static getByLabel(e){return zs().some(t=>t.label===e)?new mt(e,{skip:!0}):null}},je;"__TAURI_METADATA__"in window?je=new mt(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0}):(console.warn(`Could not find "window.__TAURI_METADATA__". The "appWindow" value will reference the "main" window label. -Note that this is not an issue if running this frontend on a browser instead of a Tauri window.`),je=new mt("main",{skip:!0}));function Hi(e){return e===null?null:{name:e.name,scaleFactor:e.scaleFactor,position:new xe(e.position.x,e.position.y),size:new dt(e.size.width,e.size.height)}}async function wo(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"currentMonitor"}}}}).then(Hi)}async function ko(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"primaryMonitor"}}}}).then(Hi)}async function Mo(){return L({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"availableMonitors"}}}}).then(e=>e.map(Hi))}function Co(){return navigator.appVersion.includes("Win")}var To={};Te(To,{EOL:()=>Ao,arch:()=>Eo,platform:()=>Os,tempdir:()=>zo,type:()=>So,version:()=>Lo});var Ao=Co()?`\r +`)})}),this.spawn().catch(t)})}};async function Pi(e,t){return S({__tauriModule:"Shell",message:{cmd:"open",path:e,with:t}})}var go={};we(go,{TauriEvent:()=>Ss,emit:()=>ni,listen:()=>Xt,once:()=>Ls});async function Ts(e,t){return S({__tauriModule:"Event",message:{cmd:"unlisten",event:e,eventId:t}})}async function Cs(e,t,n){await S({__tauriModule:"Event",message:{cmd:"emit",event:e,windowLabel:t,payload:n}})}async function Ri(e,t,n){return S({__tauriModule:"Event",message:{cmd:"listen",event:e,windowLabel:t,handler:pt(n)}}).then(i=>async()=>Ts(e,i))}async function As(e,t,n){return Ri(e,t,i=>{n(i),Ts(e,i.id).catch(()=>{})})}var Ss=(e=>(e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_FILE_DROP="tauri://file-drop",e.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",e.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",e.MENU="tauri://menu",e.CHECK_UPDATE="tauri://update",e.UPDATE_AVAILABLE="tauri://update-available",e.INSTALL_UPDATE="tauri://update-install",e.STATUS_UPDATE="tauri://update-status",e.DOWNLOAD_PROGRESS="tauri://update-download-progress",e))(Ss||{});async function Xt(e,t){return Ri(e,null,t)}async function Ls(e,t){return As(e,null,t)}async function ni(e,t){return Cs(e,void 0,t)}var yo={};we(yo,{CloseRequestedEvent:()=>Os,LogicalPosition:()=>Es,LogicalSize:()=>Qn,PhysicalPosition:()=>et,PhysicalSize:()=>dt,UserAttentionType:()=>Ii,WebviewWindow:()=>mt,WebviewWindowHandle:()=>Ds,WindowManager:()=>Ws,appWindow:()=>Ue,availableMonitors:()=>ko,currentMonitor:()=>vo,getAll:()=>zs,getCurrent:()=>Ut,primaryMonitor:()=>wo});var Qn=class{constructor(e,t){this.type="Logical",this.width=e,this.height=t}},dt=class{constructor(e,t){this.type="Physical",this.width=e,this.height=t}toLogical(e){return new Qn(this.width/e,this.height/e)}},Es=class{constructor(e,t){this.type="Logical",this.x=e,this.y=t}},et=class{constructor(e,t){this.type="Physical",this.x=e,this.y=t}toLogical(e){return new Es(this.x/e,this.y/e)}},Ii=(e=>(e[e.Critical=1]="Critical",e[e.Informational=2]="Informational",e))(Ii||{});function Ut(){return new mt(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0})}function zs(){return window.__TAURI_METADATA__.__windows.map(e=>new mt(e.label,{skip:!0}))}var ql=["tauri://created","tauri://error"],Ds=class{constructor(e){this.label=e,this.listeners=Object.create(null)}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let n=this.listeners[e];n.splice(n.indexOf(t),1)}):Ri(e,this.label,t)}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve(()=>{let n=this.listeners[e];n.splice(n.indexOf(t),1)}):As(e,this.label,t)}async emit(e,t){if(ql.includes(e)){for(let n of this.listeners[e]||[])n({event:e,id:-1,windowLabel:this.label,payload:t});return Promise.resolve()}return Cs(e,this.label,t)}_handleTauriEvent(e,t){return ql.includes(e)?(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0):!1}},Ws=class extends Ds{async scaleFactor(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"scaleFactor"}}}})}async innerPosition(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerPosition"}}}}).then(({x:e,y:t})=>new et(e,t))}async outerPosition(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerPosition"}}}}).then(({x:e,y:t})=>new et(e,t))}async innerSize(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerSize"}}}}).then(({width:e,height:t})=>new dt(e,t))}async outerSize(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerSize"}}}}).then(({width:e,height:t})=>new dt(e,t))}async isFullscreen(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isFullscreen"}}}})}async isMaximized(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMaximized"}}}})}async isDecorated(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isDecorated"}}}})}async isResizable(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isResizable"}}}})}async isVisible(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isVisible"}}}})}async theme(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"theme"}}}})}async center(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"center"}}}})}async requestUserAttention(e){let t=null;return e&&(e===1?t={type:"Critical"}:t={type:"Informational"}),S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"requestUserAttention",payload:t}}}})}async setResizable(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setResizable",payload:e}}}})}async setTitle(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setTitle",payload:e}}}})}async maximize(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"maximize"}}}})}async unmaximize(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unmaximize"}}}})}async toggleMaximize(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"toggleMaximize"}}}})}async minimize(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"minimize"}}}})}async unminimize(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unminimize"}}}})}async show(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"show"}}}})}async hide(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"hide"}}}})}async close(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"close"}}}})}async setDecorations(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setDecorations",payload:e}}}})}async setShadow(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setShadow",payload:e}}}})}async setAlwaysOnTop(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setAlwaysOnTop",payload:e}}}})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSize",payload:{type:e.type,data:{width:e.width,height:e.height}}}}}})}async setMinSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMinSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setMaxSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMaxSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setFullscreen(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFullscreen",payload:e}}}})}async setFocus(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFocus"}}}})}async setIcon(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIcon",payload:{icon:typeof e=="string"?e:Array.from(e)}}}}})}async setSkipTaskbar(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSkipTaskbar",payload:e}}}})}async setCursorGrab(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorGrab",payload:e}}}})}async setCursorVisible(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorVisible",payload:e}}}})}async setCursorIcon(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorIcon",payload:e}}}})}async setCursorPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setIgnoreCursorEvents(e){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIgnoreCursorEvents",payload:e}}}})}async startDragging(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"startDragging"}}}})}async onResized(e){return this.listen("tauri://resize",e)}async onMoved(e){return this.listen("tauri://move",e)}async onCloseRequested(e){return this.listen("tauri://close-requested",t=>{let n=new Os(t);Promise.resolve(e(n)).then(()=>{if(!n.isPreventDefault())return this.close()})})}async onFocusChanged(e){let t=await this.listen("tauri://focus",i=>{e({...i,payload:!0})}),n=await this.listen("tauri://blur",i=>{e({...i,payload:!1})});return()=>{t(),n()}}async onScaleChanged(e){return this.listen("tauri://scale-change",e)}async onMenuClicked(e){return this.listen("tauri://menu",e)}async onFileDropEvent(e){let t=await this.listen("tauri://file-drop",l=>{e({...l,payload:{type:"drop",paths:l.payload}})}),n=await this.listen("tauri://file-drop-hover",l=>{e({...l,payload:{type:"hover",paths:l.payload}})}),i=await this.listen("tauri://file-drop-cancelled",l=>{e({...l,payload:{type:"cancel"}})});return()=>{t(),n(),i()}}async onThemeChanged(e){return this.listen("tauri://theme-changed",e)}},Os=class{constructor(e){this._preventDefault=!1,this.event=e.event,this.windowLabel=e.windowLabel,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}},mt=class extends Ws{constructor(e,t={}){super(e),t!=null&&t.skip||S({__tauriModule:"Window",message:{cmd:"createWebview",data:{options:{label:e,...t}}}}).then(async()=>this.emit("tauri://created")).catch(async n=>this.emit("tauri://error",n))}static getByLabel(e){return zs().some(t=>t.label===e)?new mt(e,{skip:!0}):null}},Ue;"__TAURI_METADATA__"in window?Ue=new mt(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0}):(console.warn(`Could not find "window.__TAURI_METADATA__". The "appWindow" value will reference the "main" window label. +Note that this is not an issue if running this frontend on a browser instead of a Tauri window.`),Ue=new mt("main",{skip:!0}));function Hi(e){return e===null?null:{name:e.name,scaleFactor:e.scaleFactor,position:new et(e.position.x,e.position.y),size:new dt(e.size.width,e.size.height)}}async function vo(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"currentMonitor"}}}}).then(Hi)}async function wo(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"primaryMonitor"}}}}).then(Hi)}async function ko(){return S({__tauriModule:"Window",message:{cmd:"manage",data:{cmd:{type:"availableMonitors"}}}}).then(e=>e.map(Hi))}function Mo(){return navigator.appVersion.includes("Win")}var To={};we(To,{EOL:()=>Co,arch:()=>Lo,platform:()=>Ps,tempdir:()=>Eo,type:()=>So,version:()=>Ao});var Co=Mo()?`\r `:` -`;async function Os(){return L({__tauriModule:"Os",message:{cmd:"platform"}})}async function Lo(){return L({__tauriModule:"Os",message:{cmd:"version"}})}async function So(){return L({__tauriModule:"Os",message:{cmd:"osType"}})}async function Eo(){return L({__tauriModule:"Os",message:{cmd:"arch"}})}async function zo(){return L({__tauriModule:"Os",message:{cmd:"tempdir"}})}var Do={};Te(Do,{getName:()=>Is,getTauriVersion:()=>Hs,getVersion:()=>Rs,hide:()=>Ns,show:()=>Fs});async function Rs(){return L({__tauriModule:"App",message:{cmd:"getAppVersion"}})}async function Is(){return L({__tauriModule:"App",message:{cmd:"getAppName"}})}async function Hs(){return L({__tauriModule:"App",message:{cmd:"getTauriVersion"}})}async function Fs(){return L({__tauriModule:"App",message:{cmd:"show"}})}async function Ns(){return L({__tauriModule:"App",message:{cmd:"hide"}})}var Po={};Te(Po,{exit:()=>js,relaunch:()=>Fi});async function js(e=0){return L({__tauriModule:"Process",message:{cmd:"exit",exitCode:e}})}async function Fi(){return L({__tauriModule:"Process",message:{cmd:"relaunch"}})}function Wo(e){let t,n,i,l,o,u,d,c,f,v,k,_,y,g,b,A,D,F,W,j,S,C,T,P,M,N;return{c(){t=r("p"),t.innerHTML=`This is a demo of Tauri's API capabilities using the @tauri-apps/api package. It's used as the main validation app, serving as the test bed of our +`;async function Ps(){return S({__tauriModule:"Os",message:{cmd:"platform"}})}async function Ao(){return S({__tauriModule:"Os",message:{cmd:"version"}})}async function So(){return S({__tauriModule:"Os",message:{cmd:"osType"}})}async function Lo(){return S({__tauriModule:"Os",message:{cmd:"arch"}})}async function Eo(){return S({__tauriModule:"Os",message:{cmd:"tempdir"}})}var zo={};we(zo,{getName:()=>Is,getTauriVersion:()=>Hs,getVersion:()=>Rs,hide:()=>Ns,show:()=>Fs});async function Rs(){return S({__tauriModule:"App",message:{cmd:"getAppVersion"}})}async function Is(){return S({__tauriModule:"App",message:{cmd:"getAppName"}})}async function Hs(){return S({__tauriModule:"App",message:{cmd:"getTauriVersion"}})}async function Fs(){return S({__tauriModule:"App",message:{cmd:"show"}})}async function Ns(){return S({__tauriModule:"App",message:{cmd:"hide"}})}var Do={};we(Do,{exit:()=>js,relaunch:()=>Fi});async function js(e=0){return S({__tauriModule:"Process",message:{cmd:"exit",exitCode:e}})}async function Fi(){return S({__tauriModule:"Process",message:{cmd:"relaunch"}})}function Wo(e){let t,n,i,l,o,u,d,c,f,y,k,_,v,g,b,A,O,I,P,F,L,T,C,W,M,j;return{c(){t=a("p"),t.innerHTML=`This is a demo of Tauri's API capabilities using the @tauri-apps/api package. It's used as the main validation app, serving as the test bed of our development process. In the future, this app will be used on Tauri's integration - tests.`,n=h(),i=r("br"),l=h(),o=r("br"),u=h(),d=r("pre"),c=z("App name: "),f=r("code"),v=z(e[2]),k=z(` -App version: `),_=r("code"),y=z(e[0]),g=z(` -Tauri version: `),b=r("code"),A=z(e[1]),D=z(` -`),F=h(),W=r("br"),j=h(),S=r("div"),C=r("button"),C.textContent="Close application",T=h(),P=r("button"),P.textContent="Relaunch application",a(C,"class","btn"),a(P,"class","btn"),a(S,"class","flex flex-wrap gap-1 shadow-")},m(U,J){m(U,t,J),m(U,n,J),m(U,i,J),m(U,l,J),m(U,o,J),m(U,u,J),m(U,d,J),s(d,c),s(d,f),s(f,v),s(d,k),s(d,_),s(_,y),s(d,g),s(d,b),s(b,A),s(d,D),m(U,F,J),m(U,W,J),m(U,j,J),m(U,S,J),s(S,C),s(S,T),s(S,P),M||(N=[E(C,"click",e[3]),E(P,"click",e[4])],M=!0)},p(U,[J]){J&4&&K(v,U[2]),J&1&&K(y,U[0]),J&2&&K(A,U[1])},i:V,o:V,d(U){U&&p(t),U&&p(n),U&&p(i),U&&p(l),U&&p(o),U&&p(u),U&&p(d),U&&p(F),U&&p(W),U&&p(j),U&&p(S),M=!1,se(N)}}}function Oo(e,t,n){let i="0.0.0",l="0.0.0",o="Unknown";Is().then(c=>{n(2,o=c)}),Rs().then(c=>{n(0,i=c)}),Hs().then(c=>{n(1,l=c)});async function u(){await js()}async function d(){await Fi()}return[i,l,o,u,d]}class Ro extends we{constructor(t){super(),ye(this,t,Oo,Wo,me,{})}}var Io={};Te(Io,{getMatches:()=>Us});async function Us(){return L({__tauriModule:"Cli",message:{cmd:"cliMatches"}})}function Ho(e){let t,n,i,l,o,u,d,c,f,v,k,_,y;return{c(){t=r("p"),t.innerHTML=`This binary can be run from the terminal and takes the following arguments: + tests.`,n=h(),i=a("br"),l=h(),o=a("br"),u=h(),d=a("pre"),c=z("App name: "),f=a("code"),y=z(e[2]),k=z(` +App version: `),_=a("code"),v=z(e[0]),g=z(` +Tauri version: `),b=a("code"),A=z(e[1]),O=z(` +`),I=h(),P=a("br"),F=h(),L=a("div"),T=a("button"),T.textContent="Close application",C=h(),W=a("button"),W.textContent="Relaunch application",r(T,"class","btn"),r(W,"class","btn"),r(L,"class","flex flex-wrap gap-1 shadow-")},m(U,J){m(U,t,J),m(U,n,J),m(U,i,J),m(U,l,J),m(U,o,J),m(U,u,J),m(U,d,J),s(d,c),s(d,f),s(f,y),s(d,k),s(d,_),s(_,v),s(d,g),s(d,b),s(b,A),s(d,O),m(U,I,J),m(U,P,J),m(U,F,J),m(U,L,J),s(L,T),s(L,C),s(L,W),M||(j=[E(T,"click",e[3]),E(W,"click",e[4])],M=!0)},p(U,[J]){J&4&&$(y,U[2]),J&1&&$(v,U[0]),J&2&&$(A,U[1])},i:V,o:V,d(U){U&&p(t),U&&p(n),U&&p(i),U&&p(l),U&&p(o),U&&p(u),U&&p(d),U&&p(I),U&&p(P),U&&p(F),U&&p(L),M=!1,ue(j)}}}function Oo(e,t,n){let i="0.0.0",l="0.0.0",o="Unknown";Is().then(c=>{n(2,o=c)}),Rs().then(c=>{n(0,i=c)}),Hs().then(c=>{n(1,l=c)});async function u(){await js()}async function d(){await Fi()}return[i,l,o,u,d]}class Po extends ye{constructor(t){super(),ge(this,t,Oo,Wo,he,{})}}var Ro={};we(Ro,{getMatches:()=>Us});async function Us(){return S({__tauriModule:"Cli",message:{cmd:"cliMatches"}})}function Io(e){let t,n,i,l,o,u,d,c,f,y,k,_,v;return{c(){t=a("p"),t.innerHTML=`This binary can be run from the terminal and takes the following arguments:

  --config <PATH>
   --theme <light|dark|system>
   --verbose
- Additionally, it has a update --background subcommand.`,n=h(),i=r("br"),l=h(),o=r("div"),o.textContent="Note that the arguments are only parsed, not implemented.",u=h(),d=r("br"),c=h(),f=r("br"),v=h(),k=r("button"),k.textContent="Get matches",a(o,"class","note"),a(k,"class","btn"),a(k,"id","cli-matches")},m(g,b){m(g,t,b),m(g,n,b),m(g,i,b),m(g,l,b),m(g,o,b),m(g,u,b),m(g,d,b),m(g,c,b),m(g,f,b),m(g,v,b),m(g,k,b),_||(y=E(k,"click",e[0]),_=!0)},p:V,i:V,o:V,d(g){g&&p(t),g&&p(n),g&&p(i),g&&p(l),g&&p(o),g&&p(u),g&&p(d),g&&p(c),g&&p(f),g&&p(v),g&&p(k),_=!1,y()}}}function Fo(e,t,n){let{onMessage:i}=t;function l(){Us().then(i).catch(i)}return e.$$set=o=>{"onMessage"in o&&n(1,i=o.onMessage)},[l,i]}class No extends we{constructor(t){super(),ye(this,t,Fo,Ho,me,{onMessage:1})}}function jo(e){let t,n,i,l,o,u,d,c;return{c(){t=r("div"),n=r("button"),n.textContent="Call Log API",i=h(),l=r("button"),l.textContent="Call Request (async) API",o=h(),u=r("button"),u.textContent="Send event to Rust",a(n,"class","btn"),a(n,"id","log"),a(l,"class","btn"),a(l,"id","request"),a(u,"class","btn"),a(u,"id","event")},m(f,v){m(f,t,v),s(t,n),s(t,i),s(t,l),s(t,o),s(t,u),d||(c=[E(n,"click",e[0]),E(l,"click",e[1]),E(u,"click",e[2])],d=!0)},p:V,i:V,o:V,d(f){f&&p(t),d=!1,se(c)}}}function Uo(e,t,n){let{onMessage:i}=t,l;ut(async()=>{l=await Xt("rust-event",i)}),Pi(()=>{l&&l()});function o(){Kn("log_operation",{event:"tauri-click",payload:"this payload is optional because we used Option in Rust"})}function u(){Kn("perform_request",{endpoint:"dummy endpoint arg",body:{id:5,name:"test"}}).then(i).catch(i)}function d(){ni("js-event","this is the payload string")}return e.$$set=c=>{"onMessage"in c&&n(3,i=c.onMessage)},[o,u,d,i]}class qo extends we{constructor(t){super(),ye(this,t,Uo,jo,me,{onMessage:3})}}var Bo={};Te(Bo,{ask:()=>Bs,confirm:()=>Go,message:()=>Vo,open:()=>Ni,save:()=>qs});async function Ni(e={}){return typeof e=="object"&&Object.freeze(e),L({__tauriModule:"Dialog",message:{cmd:"openDialog",options:e}})}async function qs(e={}){return typeof e=="object"&&Object.freeze(e),L({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:e}})}async function Vo(e,t){var n;let i=typeof t=="string"?{title:t}:t;return L({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:e.toString(),title:(n=i==null?void 0:i.title)==null?void 0:n.toString(),type:i==null?void 0:i.type}})}async function Bs(e,t){var n;let i=typeof t=="string"?{title:t}:t;return L({__tauriModule:"Dialog",message:{cmd:"askDialog",message:e.toString(),title:(n=i==null?void 0:i.title)==null?void 0:n.toString(),type:i==null?void 0:i.type}})}async function Go(e,t){var n;let i=typeof t=="string"?{title:t}:t;return L({__tauriModule:"Dialog",message:{cmd:"confirmDialog",message:e.toString(),title:(n=i==null?void 0:i.title)==null?void 0:n.toString(),type:i==null?void 0:i.type}})}var Jo={};Te(Jo,{BaseDirectory:()=>Jt,Dir:()=>Jt,copyFile:()=>Qo,createDir:()=>$o,exists:()=>er,readBinaryFile:()=>ji,readDir:()=>Vs,readTextFile:()=>Xo,removeDir:()=>Ko,removeFile:()=>Zo,renameFile:()=>xo,writeBinaryFile:()=>Yo,writeFile:()=>Di,writeTextFile:()=>Di});var Jt=(e=>(e[e.Audio=1]="Audio",e[e.Cache=2]="Cache",e[e.Config=3]="Config",e[e.Data=4]="Data",e[e.LocalData=5]="LocalData",e[e.Desktop=6]="Desktop",e[e.Document=7]="Document",e[e.Download=8]="Download",e[e.Executable=9]="Executable",e[e.Font=10]="Font",e[e.Home=11]="Home",e[e.Picture=12]="Picture",e[e.Public=13]="Public",e[e.Runtime=14]="Runtime",e[e.Template=15]="Template",e[e.Video=16]="Video",e[e.Resource=17]="Resource",e[e.App=18]="App",e[e.Log=19]="Log",e[e.Temp=20]="Temp",e[e.AppConfig=21]="AppConfig",e[e.AppData=22]="AppData",e[e.AppLocalData=23]="AppLocalData",e[e.AppCache=24]="AppCache",e[e.AppLog=25]="AppLog",e))(Jt||{});async function Xo(e,t={}){return L({__tauriModule:"Fs",message:{cmd:"readTextFile",path:e,options:t}})}async function ji(e,t={}){let n=await L({__tauriModule:"Fs",message:{cmd:"readFile",path:e,options:t}});return Uint8Array.from(n)}async function Di(e,t,n){typeof n=="object"&&Object.freeze(n),typeof e=="object"&&Object.freeze(e);let i={path:"",contents:""},l=n;return typeof e=="string"?i.path=e:(i.path=e.path,i.contents=e.contents),typeof t=="string"?i.contents=t!=null?t:"":l=t,L({__tauriModule:"Fs",message:{cmd:"writeFile",path:i.path,contents:Array.from(new TextEncoder().encode(i.contents)),options:l}})}async function Yo(e,t,n){typeof n=="object"&&Object.freeze(n),typeof e=="object"&&Object.freeze(e);let i={path:"",contents:[]},l=n;return typeof e=="string"?i.path=e:(i.path=e.path,i.contents=e.contents),t&&"dir"in t?l=t:typeof e=="string"&&(i.contents=t!=null?t:[]),L({__tauriModule:"Fs",message:{cmd:"writeFile",path:i.path,contents:Array.from(i.contents instanceof ArrayBuffer?new Uint8Array(i.contents):i.contents),options:l}})}async function Vs(e,t={}){return L({__tauriModule:"Fs",message:{cmd:"readDir",path:e,options:t}})}async function $o(e,t={}){return L({__tauriModule:"Fs",message:{cmd:"createDir",path:e,options:t}})}async function Ko(e,t={}){return L({__tauriModule:"Fs",message:{cmd:"removeDir",path:e,options:t}})}async function Qo(e,t,n={}){return L({__tauriModule:"Fs",message:{cmd:"copyFile",source:e,destination:t,options:n}})}async function Zo(e,t={}){return L({__tauriModule:"Fs",message:{cmd:"removeFile",path:e,options:t}})}async function xo(e,t,n={}){return L({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:e,newPath:t,options:n}})}async function er(e,t={}){return L({__tauriModule:"Fs",message:{cmd:"exists",path:e,options:t}})}function tr(e){let t,n,i,l,o,u,d,c,f,v,k,_,y,g,b,A,D,F,W,j,S,C,T,P;return{c(){t=r("div"),n=r("input"),i=h(),l=r("input"),o=h(),u=r("br"),d=h(),c=r("div"),f=r("input"),v=h(),k=r("label"),k.textContent="Multiple",_=h(),y=r("div"),g=r("input"),b=h(),A=r("label"),A.textContent="Directory",D=h(),F=r("br"),W=h(),j=r("button"),j.textContent="Open dialog",S=h(),C=r("button"),C.textContent="Open save dialog",a(n,"class","input"),a(n,"id","dialog-default-path"),a(n,"placeholder","Default path"),a(l,"class","input"),a(l,"id","dialog-filter"),a(l,"placeholder","Extensions filter, comma-separated"),a(t,"class","flex gap-2 children:grow"),a(f,"type","checkbox"),a(f,"id","dialog-multiple"),a(k,"for","dialog-multiple"),a(g,"type","checkbox"),a(g,"id","dialog-directory"),a(A,"for","dialog-directory"),a(j,"class","btn"),a(j,"id","open-dialog"),a(C,"class","btn"),a(C,"id","save-dialog")},m(M,N){m(M,t,N),s(t,n),B(n,e[0]),s(t,i),s(t,l),B(l,e[1]),m(M,o,N),m(M,u,N),m(M,d,N),m(M,c,N),s(c,f),f.checked=e[2],s(c,v),s(c,k),m(M,_,N),m(M,y,N),s(y,g),g.checked=e[3],s(y,b),s(y,A),m(M,D,N),m(M,F,N),m(M,W,N),m(M,j,N),m(M,S,N),m(M,C,N),T||(P=[E(n,"input",e[8]),E(l,"input",e[9]),E(f,"change",e[10]),E(g,"change",e[11]),E(j,"click",e[4]),E(C,"click",e[5])],T=!0)},p(M,[N]){N&1&&n.value!==M[0]&&B(n,M[0]),N&2&&l.value!==M[1]&&B(l,M[1]),N&4&&(f.checked=M[2]),N&8&&(g.checked=M[3])},i:V,o:V,d(M){M&&p(t),M&&p(o),M&&p(u),M&&p(d),M&&p(c),M&&p(_),M&&p(y),M&&p(D),M&&p(F),M&&p(W),M&&p(j),M&&p(S),M&&p(C),T=!1,se(P)}}}function nr(e,t){var n=new Blob([e],{type:"application/octet-binary"}),i=new FileReader;i.onload=function(l){var o=l.target.result;t(o.substr(o.indexOf(",")+1))},i.readAsDataURL(n)}function ir(e,t,n){let{onMessage:i}=t,{insecureRenderHtml:l}=t,o=null,u=null,d=!1,c=!1;function f(){Ni({title:"My wonderful open dialog",defaultPath:o,filters:u?[{name:"Tauri Example",extensions:u.split(",").map(b=>b.trim())}]:[],multiple:d,directory:c}).then(function(b){if(Array.isArray(b))i(b);else{var A=b,D=A.match(/\S+\.\S+$/g);ji(A).then(function(F){D&&(A.includes(".png")||A.includes(".jpg"))?nr(new Uint8Array(F),function(W){var j="data:image/png;base64,"+W;l('')}):i(b)}).catch(i(b))}}).catch(i)}function v(){qs({title:"My wonderful save dialog",defaultPath:o,filters:u?[{name:"Tauri Example",extensions:u.split(",").map(b=>b.trim())}]:[]}).then(i).catch(i)}function k(){o=this.value,n(0,o)}function _(){u=this.value,n(1,u)}function y(){d=this.checked,n(2,d)}function g(){c=this.checked,n(3,c)}return e.$$set=b=>{"onMessage"in b&&n(6,i=b.onMessage),"insecureRenderHtml"in b&&n(7,l=b.insecureRenderHtml)},[o,u,d,c,f,v,i,l,k,_,y,g]}class lr extends we{constructor(t){super(),ye(this,t,ir,tr,me,{onMessage:6,insecureRenderHtml:7})}}function Bl(e,t,n){const i=e.slice();return i[9]=t[n],i}function Vl(e){let t,n=e[9][0]+"",i,l;return{c(){t=r("option"),i=z(n),t.__value=l=e[9][1],t.value=t.__value},m(o,u){m(o,t,u),s(t,i)},p:V,d(o){o&&p(t)}}}function sr(e){let t,n,i,l,o,u,d,c,f,v,k,_,y,g,b,A,D,F,W,j=e[2],S=[];for(let C=0;CisNaN(parseInt(_))).map(_=>[_,Jt[_]]);function c(){const _=o.match(/\S+\.\S+$/g),y={dir:Gl()};(_?ji(o,y):Vs(o,y)).then(function(b){if(_)if(o.includes(".png")||o.includes(".jpg"))or(new Uint8Array(b),function(A){const D="data:image/png;base64,"+A;l('')});else{const A=String.fromCharCode.apply(null,b);l(''),setTimeout(()=>{const D=document.getElementById("file-response");D.value=A,document.getElementById("file-save").addEventListener("click",function(){Di(o,D.value,{dir:Gl()}).catch(i)})})}else i(b)}).catch(i)}function f(){n(1,u.src=ks(o),u)}function v(){o=this.value,n(0,o)}function k(_){Yn[_?"unshift":"push"](()=>{u=_,n(1,u)})}return e.$$set=_=>{"onMessage"in _&&n(5,i=_.onMessage),"insecureRenderHtml"in _&&n(6,l=_.insecureRenderHtml)},[o,u,d,c,f,i,l,v,k]}class ar extends we{constructor(t){super(),ye(this,t,rr,sr,me,{onMessage:5,insecureRenderHtml:6})}}var ur={};Te(ur,{Body:()=>et,Client:()=>Js,Response:()=>Gs,ResponseType:()=>Ui,fetch:()=>cr,getClient:()=>Zn});var Ui=(e=>(e[e.JSON=1]="JSON",e[e.Text=2]="Text",e[e.Binary=3]="Binary",e))(Ui||{}),et=class{constructor(e,t){this.type=e,this.payload=t}static form(e){let t={},n=(i,l)=>{if(l!==null){let o;typeof l=="string"?o=l:l instanceof Uint8Array||Array.isArray(l)?o=Array.from(l):l instanceof File?o={file:l.name,mime:l.type,fileName:l.name}:typeof l.file=="string"?o={file:l.file,mime:l.mime,fileName:l.fileName}:o={file:Array.from(l.file),mime:l.mime,fileName:l.fileName},t[i]=o}};if(e instanceof FormData)for(let[i,l]of e)n(i,l);else for(let i in e){let l=e[i];n(i,l)}return new et("Form",t)}static json(e){return new et("Json",e)}static text(e){return new et("Text",e)}static bytes(e){return new et("Bytes",Array.from(e instanceof ArrayBuffer?new Uint8Array(e):e))}},Gs=class{constructor(e){this.url=e.url,this.status=e.status,this.ok=this.status>=200&&this.status<300,this.headers=e.headers,this.rawHeaders=e.rawHeaders,this.data=e.data}},Js=class{constructor(e){this.id=e}async drop(){return L({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}})}async request(e){let t=!e.responseType||e.responseType===1;return t&&(e.responseType=2),L({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:e}}).then(n=>{let i=new Gs(n);if(t){try{i.data=JSON.parse(i.data)}catch(l){if(i.ok&&i.data==="")i.data={};else if(i.ok)throw Error(`Failed to parse response \`${i.data}\` as JSON: ${l}; - try setting the \`responseType\` option to \`ResponseType.Text\` or \`ResponseType.Binary\` if the API does not return a JSON response.`)}return i}return i})}async get(e,t){return this.request({method:"GET",url:e,...t})}async post(e,t,n){return this.request({method:"POST",url:e,body:t,...n})}async put(e,t,n){return this.request({method:"PUT",url:e,body:t,...n})}async patch(e,t){return this.request({method:"PATCH",url:e,...t})}async delete(e,t){return this.request({method:"DELETE",url:e,...t})}};async function Zn(e){return L({__tauriModule:"Http",message:{cmd:"createClient",options:e}}).then(t=>new Js(t))}var Si=null;async function cr(e,t){var n;return Si===null&&(Si=await Zn()),Si.request({url:e,method:(n=t==null?void 0:t.method)!=null?n:"GET",...t})}function Jl(e,t,n){const i=e.slice();return i[12]=t[n],i[14]=n,i}function Xl(e){let t,n,i,l,o,u,d,c,f,v,k,_,y,g,b,A,D,F=e[5],W=[];for(let T=0;TRe(W[T],1,1,()=>{W[T]=null});let S=!e[3]&&Ql(),C=!e[3]&&e[8]&&Zl();return{c(){t=r("span"),n=r("span"),i=z(e[6]),l=h(),o=r("ul");for(let T=0;T{v[g]=null}),ti(),o=v[l],o?o.p(_,y):(o=v[l]=f[l](_),o.c()),ze(o,1),o.m(t,u))},i(_){d||(ze(o),d=!0)},o(_){Re(o),d=!1},d(_){_&&p(t),c&&c.d(),v[l].d()}}}function Ql(e){let t;return{c(){t=r("span"),t.textContent=",",a(t,"class","comma svelte-gbh3pt")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function Zl(e){let t;return{c(){t=r("span"),t.textContent=",",a(t,"class","comma svelte-gbh3pt")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function pr(e){let t,n,i=e[5].length&&Xl(e);return{c(){i&&i.c(),t=xn()},m(l,o){i&&i.m(l,o),m(l,t,o),n=!0},p(l,[o]){l[5].length?i?(i.p(l,o),o&32&&ze(i,1)):(i=Xl(l),i.c(),ze(i,1),i.m(t.parentNode,t)):i&&(ei(),Re(i,1,1,()=>{i=null}),ti())},i(l){n||(ze(i),n=!0)},o(l){Re(i),n=!1},d(l){i&&i.d(l),l&&p(t)}}}const mr="...";function hr(e,t,n){let{json:i}=t,{depth:l=1/0}=t,{_lvl:o=0}=t,{_last:u=!0}=t;const d=b=>b===null?"null":typeof b;let c,f,v,k,_;const y=b=>{switch(d(b)){case"string":return`"${b}"`;case"function":return"f () {...}";case"symbol":return b.toString();default:return b}},g=()=>{n(8,_=!_)};return e.$$set=b=>{"json"in b&&n(0,i=b.json),"depth"in b&&n(1,l=b.depth),"_lvl"in b&&n(2,o=b._lvl),"_last"in b&&n(3,u=b._last)},e.$$.update=()=>{e.$$.dirty&17&&(n(5,c=d(i)==="object"?Object.keys(i):[]),n(4,f=Array.isArray(i)),n(6,v=f?"[":"{"),n(7,k=f?"]":"}")),e.$$.dirty&6&&n(8,_=le[9].call(n)),a(k,"class","input h-auto w-100%"),a(k,"id","request-body"),a(k,"placeholder","Request body"),a(k,"rows","5"),a(b,"class","btn"),a(b,"id","make-request"),a(C,"class","input"),a(P,"class","input"),a(S,"class","flex gap-2 children:grow"),a(Q,"type","checkbox"),a($,"class","btn"),a($,"type","button")},m(R,q){m(R,t,q),s(t,n),s(n,i),s(n,l),s(n,o),s(n,u),s(n,d),zt(n,e[0]),s(t,c),s(t,f),s(t,v),s(t,k),B(k,e[1]),s(t,_),s(t,y),s(t,g),s(t,b),m(R,A,q),m(R,D,q),m(R,F,q),m(R,W,q),m(R,j,q),m(R,S,q),s(S,C),B(C,e[2]),s(S,T),s(S,P),B(P,e[3]),m(R,M,q),m(R,N,q),m(R,U,q),m(R,J,q),s(J,Q),Q.checked=e[5],s(J,he),m(R,x,q),m(R,ne,q),m(R,Y,q),m(R,_e,q),m(R,O,q),m(R,$,q),m(R,ie,q),m(R,oe,q),m(R,Z,q),m(R,pe,q),m(R,re,q),Vt(be,R,q),ee=!0,ke||(Ae=[E(n,"change",e[9]),E(k,"input",e[10]),E(t,"submit",Xn(e[6])),E(C,"input",e[11]),E(P,"input",e[12]),E(Q,"change",e[13]),E($,"click",e[7])],ke=!0)},p(R,[q]){q&1&&zt(n,R[0]),q&2&&B(k,R[1]),q&4&&C.value!==R[2]&&B(C,R[2]),q&8&&P.value!==R[3]&&B(P,R[3]),q&32&&(Q.checked=R[5]);const We={};q&16&&(We.json=R[4]),be.$set(We)},i(R){ee||(ze(be.$$.fragment,R),ee=!0)},o(R){Re(be.$$.fragment,R),ee=!1},d(R){R&&p(t),R&&p(A),R&&p(D),R&&p(F),R&&p(W),R&&p(j),R&&p(S),R&&p(M),R&&p(N),R&&p(U),R&&p(J),R&&p(x),R&&p(ne),R&&p(Y),R&&p(_e),R&&p(O),R&&p($),R&&p(ie),R&&p(oe),R&&p(Z),R&&p(pe),R&&p(re),Gt(be,R),ke=!1,se(Ae)}}}function br(e,t,n){let i="GET",l="",{onMessage:o}=t;async function u(){const D=await Zn().catch(j=>{throw o(j),j}),W={url:"http://localhost:3003",method:i||"GET"||"GET"};l.startsWith("{")&&l.endsWith("}")||l.startsWith("[")&&l.endsWith("]")?W.body=et.json(JSON.parse(l)):l!==""&&(W.body=et.text(l)),D.request(W).then(o).catch(o)}let d="baz",c="qux",f=null,v=!0;async function k(){const D=await Zn().catch(F=>{throw o(F),F});n(4,f=await D.request({url:"http://localhost:3003",method:"POST",body:et.form({foo:d,bar:c}),headers:v?{"Content-Type":"multipart/form-data"}:void 0,responseType:Ui.Text}))}function _(){i=Ei(this),n(0,i)}function y(){l=this.value,n(1,l)}function g(){d=this.value,n(2,d)}function b(){c=this.value,n(3,c)}function A(){v=this.checked,n(5,v)}return e.$$set=D=>{"onMessage"in D&&n(8,o=D.onMessage)},[i,l,d,c,f,v,u,k,o,_,y,g,b,A]}class gr extends we{constructor(t){super(),ye(this,t,br,_r,me,{onMessage:8})}}function vr(e){let t,n,i;return{c(){t=r("button"),t.textContent="Send test notification",a(t,"class","btn"),a(t,"id","notification")},m(l,o){m(l,t,o),n||(i=E(t,"click",yr),n=!0)},p:V,i:V,o:V,d(l){l&&p(t),n=!1,i()}}}function yr(){new Notification("Notification title",{body:"This is the notification body"})}function wr(e,t,n){let{onMessage:i}=t;return e.$$set=l=>{"onMessage"in l&&n(0,i=l.onMessage)},[i]}class kr extends we{constructor(t){super(),ye(this,t,wr,vr,me,{onMessage:0})}}function xl(e,t,n){const i=e.slice();return i[67]=t[n],i}function es(e,t,n){const i=e.slice();return i[70]=t[n],i}function ts(e){let t,n,i,l,o,u,d=Object.keys(e[1]),c=[];for(let f=0;fe[39].call(i))},m(f,v){m(f,t,v),m(f,n,v),m(f,i,v),s(i,l);for(let k=0;ke[57].call(Ne)),a($e,"class","input"),a($e,"type","number"),a(Ke,"class","input"),a(Ke,"type","number"),a(Fe,"class","flex gap-2"),a(Qe,"class","input grow"),a(Qe,"id","title"),a(Ft,"class","btn"),a(Ft,"type","submit"),a(rt,"class","flex gap-1"),a(Ze,"class","input grow"),a(Ze,"id","url"),a(Nt,"class","btn"),a(Nt,"id","open-url"),a(at,"class","flex gap-1"),a(ot,"class","flex flex-col gap-1")},m(w,I){m(w,t,I),m(w,n,I),m(w,i,I),s(i,l),s(i,o),s(i,u),s(i,d),s(i,c),s(i,f),s(i,v),s(i,k),s(i,_),m(w,y,I),m(w,g,I),m(w,b,I),m(w,A,I),s(A,D),s(D,F),s(D,W),W.checked=e[3],s(A,j),s(A,S),s(S,C),s(S,T),T.checked=e[2],s(A,P),s(A,M),s(M,N),s(M,U),U.checked=e[4],s(A,J),s(A,Q),s(Q,he),s(Q,x),x.checked=e[5],s(A,ne),s(A,Y),s(Y,_e),s(Y,O),O.checked=e[6],m(w,$,I),m(w,ie,I),m(w,oe,I),m(w,Z,I),s(Z,pe),s(pe,re),s(re,be),s(re,ee),B(ee,e[13]),s(pe,ke),s(pe,Ae),s(Ae,R),s(Ae,q),B(q,e[14]),s(Z,We),s(Z,Le),s(Le,Me),s(Me,ae),s(Me,de),B(de,e[7]),s(Le,ue),s(Le,Se),s(Se,tt),s(Se,fe),B(fe,e[8]),s(Z,ce),s(Z,H),s(H,te),s(te,G),s(te,ge),B(ge,e[9]),s(H,Yt),s(H,ht),s(ht,$t),s(ht,Oe),B(Oe,e[10]),s(Z,Kt),s(Z,Ue),s(Ue,_t),s(_t,Qt),s(_t,X),B(X,e[11]),s(Ue,Pt),s(Ue,nt),s(nt,Wt),s(nt,De),B(De,e[12]),m(w,bt,I),m(w,gt,I),m(w,vt,I),m(w,Ee,I),s(Ee,Ie),s(Ie,Pe),s(Pe,it),s(Pe,Ot),s(Pe,lt),s(lt,Rt),s(lt,yt),s(Pe,It),s(Pe,wt),s(wt,Bi),s(wt,ii),s(Ie,Vi),s(Ie,qe),s(qe,xt),s(qe,Gi),s(qe,en),s(en,Ji),s(en,li),s(qe,Xi),s(qe,nn),s(nn,Yi),s(nn,si),s(Ee,$i),s(Ee,kt),s(kt,Be),s(Be,sn),s(Be,Ki),s(Be,on),s(on,Qi),s(on,oi),s(Be,Zi),s(Be,an),s(an,xi),s(an,ri),s(kt,el),s(kt,Ve),s(Ve,cn),s(Ve,tl),s(Ve,dn),s(dn,nl),s(dn,ai),s(Ve,il),s(Ve,pn),s(pn,ll),s(pn,ui),s(Ee,sl),s(Ee,Mt),s(Mt,Ge),s(Ge,hn),s(Ge,ol),s(Ge,_n),s(_n,rl),s(_n,ci),s(Ge,al),s(Ge,gn),s(gn,ul),s(gn,di),s(Mt,cl),s(Mt,Je),s(Je,yn),s(Je,dl),s(Je,wn),s(wn,fl),s(wn,fi),s(Je,pl),s(Je,Mn),s(Mn,ml),s(Mn,pi),s(Ee,hl),s(Ee,Ct),s(Ct,Xe),s(Xe,Tn),s(Xe,_l),s(Xe,An),s(An,bl),s(An,mi),s(Xe,gl),s(Xe,Sn),s(Sn,vl),s(Sn,hi),s(Ct,yl),s(Ct,Ye),s(Ye,zn),s(Ye,wl),s(Ye,Dn),s(Dn,kl),s(Dn,_i),s(Ye,Ml),s(Ye,Wn),s(Wn,Cl),s(Wn,bi),m(w,gi,I),m(w,vi,I),m(w,yi,I),m(w,Ht,I),m(w,wi,I),m(w,He,I),s(He,Rn),s(Rn,Tt),Tt.checked=e[15],s(Rn,Tl),s(He,Al),s(He,In),s(In,At),At.checked=e[16],s(In,Ll),s(He,Sl),s(He,Hn),s(Hn,Lt),Lt.checked=e[20],s(Hn,El),m(w,ki,I),m(w,Fe,I),s(Fe,Fn),s(Fn,zl),s(Fn,Ne);for(let ve=0;ve=1,v,k,_,y=f&&ts(e),g=e[1][e[0]]&&is(e);return{c(){t=r("div"),n=r("div"),i=r("input"),l=h(),o=r("button"),o.textContent="New window",u=h(),d=r("br"),c=h(),y&&y.c(),v=h(),g&&g.c(),a(i,"class","input grow"),a(i,"type","text"),a(i,"placeholder","New Window label.."),a(o,"class","btn"),a(n,"class","flex gap-1"),a(t,"class","flex flex-col children:grow gap-2")},m(b,A){m(b,t,A),s(t,n),s(n,i),B(i,e[21]),s(n,l),s(n,o),s(t,u),s(t,d),s(t,c),y&&y.m(t,null),s(t,v),g&&g.m(t,null),k||(_=[E(i,"input",e[38]),E(o,"click",e[35])],k=!0)},p(b,A){A[0]&2097152&&i.value!==b[21]&&B(i,b[21]),A[0]&2&&(f=Object.keys(b[1]).length>=1),f?y?y.p(b,A):(y=ts(b),y.c(),y.m(t,v)):y&&(y.d(1),y=null),b[1][b[0]]?g?g.p(b,A):(g=is(b),g.c(),g.m(t,null)):g&&(g.d(1),g=null)},i:V,o:V,d(b){b&&p(t),y&&y.d(),g&&g.d(),k=!1,se(_)}}}function Cr(e,t,n){let i=je.label;const l={[je.label]:je},o=["default","crosshair","hand","arrow","move","text","wait","help","progress","notAllowed","contextMenu","cell","verticalText","alias","copy","noDrop","grab","grabbing","allScroll","zoomIn","zoomOut","eResize","nResize","neResize","nwResize","sResize","seResize","swResize","wResize","ewResize","nsResize","neswResize","nwseResize","colResize","rowResize"];let{onMessage:u}=t,d,c="https://tauri.app",f=!0,v=!1,k=!0,_=!1,y=!1,g=null,b=null,A=null,D=null,F=null,W=null,j=null,S=null,C=1,T=new xe(j,S),P=new xe(j,S),M=new dt(g,b),N=new dt(g,b),U,J,Q=!1,he=!0,x=null,ne=null,Y="default",_e=!1,O="Awesome Tauri Example!";function $(){Oi(c)}function ie(){l[i].setTitle(O)}function oe(){l[i].hide(),setTimeout(l[i].show,2e3)}function Z(){l[i].minimize(),setTimeout(l[i].unminimize,2e3)}function pe(){Ni({multiple:!1}).then(X=>{typeof X=="string"&&l[i].setIcon(X)})}function re(){if(!d)return;const X=new mt(d);n(1,l[d]=X,l),X.once("tauri://error",function(){u("Error creating new webview")})}function be(){l[i].innerSize().then(X=>{n(26,M=X),n(7,g=M.width),n(8,b=M.height)}),l[i].outerSize().then(X=>{n(27,N=X)})}function ee(){l[i].innerPosition().then(X=>{n(24,T=X)}),l[i].outerPosition().then(X=>{n(25,P=X),n(13,j=P.x),n(14,S=P.y)})}async function ke(X){!X||(U&&U(),J&&J(),J=await X.listen("tauri://move",ee),U=await X.listen("tauri://resize",be))}async function Ae(){await l[i].minimize(),await l[i].requestUserAttention(Ii.Critical),await new Promise(X=>setTimeout(X,3e3)),await l[i].requestUserAttention(null)}function R(){d=this.value,n(21,d)}function q(){i=Ei(this),n(0,i),n(1,l)}const We=()=>l[i].center();function Le(){v=this.checked,n(3,v)}function Me(){f=this.checked,n(2,f)}function ae(){k=this.checked,n(4,k)}function de(){_=this.checked,n(5,_)}function ue(){y=this.checked,n(6,y)}function Se(){j=le(this.value),n(13,j)}function tt(){S=le(this.value),n(14,S)}function fe(){g=le(this.value),n(7,g)}function ce(){b=le(this.value),n(8,b)}function H(){A=le(this.value),n(9,A)}function te(){D=le(this.value),n(10,D)}function G(){F=le(this.value),n(11,F)}function ge(){W=le(this.value),n(12,W)}function Yt(){Q=this.checked,n(15,Q)}function ht(){he=this.checked,n(16,he)}function $t(){_e=this.checked,n(20,_e)}function Oe(){Y=Ei(this),n(19,Y),n(29,o)}function Kt(){x=le(this.value),n(17,x)}function Ue(){ne=le(this.value),n(18,ne)}function _t(){O=this.value,n(28,O)}function Qt(){c=this.value,n(22,c)}return e.$$set=X=>{"onMessage"in X&&n(37,u=X.onMessage)},e.$$.update=()=>{var X,Pt,nt,Wt,De,bt,gt,vt,Ee,Ie,Pe,it,Ot,lt,Rt,st,yt,It;e.$$.dirty[0]&3&&(l[i],ee(),be()),e.$$.dirty[0]&7&&((X=l[i])==null||X.setResizable(f)),e.$$.dirty[0]&11&&(v?(Pt=l[i])==null||Pt.maximize():(nt=l[i])==null||nt.unmaximize()),e.$$.dirty[0]&19&&((Wt=l[i])==null||Wt.setDecorations(k)),e.$$.dirty[0]&35&&((De=l[i])==null||De.setAlwaysOnTop(_)),e.$$.dirty[0]&67&&((bt=l[i])==null||bt.setFullscreen(y)),e.$$.dirty[0]&387&&g&&b&&((gt=l[i])==null||gt.setSize(new dt(g,b))),e.$$.dirty[0]&1539&&(A&&D?(vt=l[i])==null||vt.setMinSize(new Qn(A,D)):(Ee=l[i])==null||Ee.setMinSize(null)),e.$$.dirty[0]&6147&&(F>800&&W>400?(Ie=l[i])==null||Ie.setMaxSize(new Qn(F,W)):(Pe=l[i])==null||Pe.setMaxSize(null)),e.$$.dirty[0]&24579&&j!==null&&S!==null&&((it=l[i])==null||it.setPosition(new xe(j,S))),e.$$.dirty[0]&3&&((Ot=l[i])==null||Ot.scaleFactor().then(wt=>n(23,C=wt))),e.$$.dirty[0]&3&&ke(l[i]),e.$$.dirty[0]&32771&&((lt=l[i])==null||lt.setCursorGrab(Q)),e.$$.dirty[0]&65539&&((Rt=l[i])==null||Rt.setCursorVisible(he)),e.$$.dirty[0]&524291&&((st=l[i])==null||st.setCursorIcon(Y)),e.$$.dirty[0]&393219&&x!==null&&ne!==null&&((yt=l[i])==null||yt.setCursorPosition(new xe(x,ne))),e.$$.dirty[0]&1048579&&((It=l[i])==null||It.setIgnoreCursorEvents(_e))},[i,l,f,v,k,_,y,g,b,A,D,F,W,j,S,Q,he,x,ne,Y,_e,d,c,C,T,P,M,N,O,o,$,ie,oe,Z,pe,re,Ae,u,R,q,We,Le,Me,ae,de,ue,Se,tt,fe,ce,H,te,G,ge,Yt,ht,$t,Oe,Kt,Ue,_t,Qt]}class Tr extends we{constructor(t){super(),ye(this,t,Cr,Mr,me,{onMessage:37},null,[-1,-1,-1])}}var Ar={};Te(Ar,{isRegistered:()=>Sr,register:()=>Ys,registerAll:()=>Lr,unregister:()=>$s,unregisterAll:()=>Ks});async function Ys(e,t){return L({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:e,handler:pt(t)}})}async function Lr(e,t){return L({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:e,handler:pt(t)}})}async function Sr(e){return L({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:e}})}async function $s(e){return L({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:e}})}async function Ks(){return L({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}})}function ss(e,t,n){const i=e.slice();return i[9]=t[n],i}function os(e){let t,n=e[9]+"",i,l,o,u,d;function c(){return e[8](e[9])}return{c(){t=r("div"),i=z(n),l=h(),o=r("button"),o.textContent="Unregister",a(o,"class","btn"),a(o,"type","button"),a(t,"class","flex justify-between")},m(f,v){m(f,t,v),s(t,i),s(t,l),s(t,o),u||(d=E(o,"click",c),u=!0)},p(f,v){e=f,v&2&&n!==(n=e[9]+"")&&K(i,n)},d(f){f&&p(t),u=!1,d()}}}function rs(e){let t,n,i,l,o;return{c(){t=r("br"),n=h(),i=r("button"),i.textContent="Unregister all",a(i,"class","btn"),a(i,"type","button")},m(u,d){m(u,t,d),m(u,n,d),m(u,i,d),l||(o=E(i,"click",e[5]),l=!0)},p:V,d(u){u&&p(t),u&&p(n),u&&p(i),l=!1,o()}}}function Er(e){let t,n,i,l,o,u,d,c,f,v,k,_=e[1],y=[];for(let b=0;b<_.length;b+=1)y[b]=os(ss(e,_,b));let g=e[1].length>1&&rs(e);return{c(){t=r("div"),n=r("input"),i=h(),l=r("button"),l.textContent="Register",o=h(),u=r("br"),d=h(),c=r("div");for(let b=0;b1?g?g.p(b,A):(g=rs(b),g.c(),g.m(c,null)):g&&(g.d(1),g=null)},i:V,o:V,d(b){b&&p(t),b&&p(o),b&&p(u),b&&p(d),b&&p(c),ft(y,b),g&&g.d(),v=!1,se(k)}}}function zr(e,t,n){let i,{onMessage:l}=t;const o=ws([]);gs(e,o,_=>n(1,i=_));let u="CmdOrControl+X";function d(){const _=u;Ys(_,()=>{l(`Shortcut ${_} triggered`)}).then(()=>{o.update(y=>[...y,_]),l(`Shortcut ${_} registered successfully`)}).catch(l)}function c(_){const y=_;$s(y).then(()=>{o.update(g=>g.filter(b=>b!==y)),l(`Shortcut ${y} unregistered`)}).catch(l)}function f(){Ks().then(()=>{o.update(()=>[]),l("Unregistered all shortcuts")}).catch(l)}function v(){u=this.value,n(0,u)}const k=_=>c(_);return e.$$set=_=>{"onMessage"in _&&n(6,l=_.onMessage)},[u,i,o,d,c,f,l,v,k]}class Dr extends we{constructor(t){super(),ye(this,t,zr,Er,me,{onMessage:6})}}function as(e){let t,n,i,l,o,u,d;return{c(){t=r("br"),n=h(),i=r("input"),l=h(),o=r("button"),o.textContent="Write",a(i,"class","input"),a(i,"placeholder","write to stdin"),a(o,"class","btn")},m(c,f){m(c,t,f),m(c,n,f),m(c,i,f),B(i,e[4]),m(c,l,f),m(c,o,f),u||(d=[E(i,"input",e[14]),E(o,"click",e[8])],u=!0)},p(c,f){f&16&&i.value!==c[4]&&B(i,c[4])},d(c){c&&p(t),c&&p(n),c&&p(i),c&&p(l),c&&p(o),u=!1,se(d)}}}function Pr(e){let t,n,i,l,o,u,d,c,f,v,k,_,y,g,b,A,D,F,W,j,S,C,T,P,M=e[5]&&as(e);return{c(){t=r("div"),n=r("div"),i=z(`Script: - `),l=r("input"),o=h(),u=r("div"),d=z(`Encoding: - `),c=r("input"),f=h(),v=r("div"),k=z(`Working directory: - `),_=r("input"),y=h(),g=r("div"),b=z(`Arguments: - `),A=r("input"),D=h(),F=r("div"),W=r("button"),W.textContent="Run",j=h(),S=r("button"),S.textContent="Kill",C=h(),M&&M.c(),a(l,"class","grow input"),a(n,"class","flex items-center gap-1"),a(c,"class","grow input"),a(u,"class","flex items-center gap-1"),a(_,"class","grow input"),a(_,"placeholder","Working directory"),a(v,"class","flex items-center gap-1"),a(A,"class","grow input"),a(A,"placeholder","Environment variables"),a(g,"class","flex items-center gap-1"),a(W,"class","btn"),a(S,"class","btn"),a(F,"class","flex children:grow gap-1"),a(t,"class","flex flex-col childre:grow gap-1")},m(N,U){m(N,t,U),s(t,n),s(n,i),s(n,l),B(l,e[0]),s(t,o),s(t,u),s(u,d),s(u,c),B(c,e[3]),s(t,f),s(t,v),s(v,k),s(v,_),B(_,e[1]),s(t,y),s(t,g),s(g,b),s(g,A),B(A,e[2]),s(t,D),s(t,F),s(F,W),s(F,j),s(F,S),s(t,C),M&&M.m(t,null),T||(P=[E(l,"input",e[10]),E(c,"input",e[11]),E(_,"input",e[12]),E(A,"input",e[13]),E(W,"click",e[6]),E(S,"click",e[7])],T=!0)},p(N,[U]){U&1&&l.value!==N[0]&&B(l,N[0]),U&8&&c.value!==N[3]&&B(c,N[3]),U&2&&_.value!==N[1]&&B(_,N[1]),U&4&&A.value!==N[2]&&B(A,N[2]),N[5]?M?M.p(N,U):(M=as(N),M.c(),M.m(t,null)):M&&(M.d(1),M=null)},i:V,o:V,d(N){N&&p(t),M&&M.d(),T=!1,se(P)}}}function Wr(e,t,n){const i=navigator.userAgent.includes("Windows");let l=i?"cmd":"sh",o=i?["/C"]:["-c"],{onMessage:u}=t,d='echo "hello world"',c=null,f="SOMETHING=value ANOTHER=2",v="",k="",_;function y(){return f.split(" ").reduce((C,T)=>{let[P,M]=T.split("=");return{...C,[P]:M}},{})}function g(){n(5,_=null);const C=new Wi(l,[...o,d],{cwd:c||null,env:y(),encoding:v});C.on("close",T=>{u(`command finished with code ${T.code} and signal ${T.signal}`),n(5,_=null)}),C.on("error",T=>u(`command error: "${T}"`)),C.stdout.on("data",T=>u(`command stdout: "${T}"`)),C.stderr.on("data",T=>u(`command stderr: "${T}"`)),C.spawn().then(T=>{n(5,_=T)}).catch(u)}function b(){_.kill().then(()=>u("killed child process")).catch(u)}function A(){_.write(k).catch(u)}function D(){d=this.value,n(0,d)}function F(){v=this.value,n(3,v)}function W(){c=this.value,n(1,c)}function j(){f=this.value,n(2,f)}function S(){k=this.value,n(4,k)}return e.$$set=C=>{"onMessage"in C&&n(9,u=C.onMessage)},[d,c,f,v,k,_,g,b,A,u,D,F,W,j,S]}class Or extends we{constructor(t){super(),ye(this,t,Wr,Pr,me,{onMessage:9})}}var Rr={};Te(Rr,{checkUpdate:()=>Zs,installUpdate:()=>Qs,onUpdaterEvent:()=>qi});async function qi(e){return Xt("tauri://update-status",t=>{e(t==null?void 0:t.payload)})}async function Qs(){let e;function t(){e&&e(),e=void 0}return new Promise((n,i)=>{function l(o){if(o.error)return t(),i(o.error);if(o.status==="DONE")return t(),n()}qi(l).then(o=>{e=o}).catch(o=>{throw t(),o}),ni("tauri://update-install").catch(o=>{throw t(),o})})}async function Zs(){let e;function t(){e&&e(),e=void 0}return new Promise((n,i)=>{function l(u){return t(),n({manifest:u,shouldUpdate:!0})}function o(u){if(u.error)return t(),i(u.error);if(u.status==="UPTODATE")return t(),n({shouldUpdate:!1})}Ss("tauri://update-available",u=>{l(u==null?void 0:u.payload)}).catch(u=>{throw t(),u}),qi(o).then(u=>{e=u}).catch(u=>{throw t(),u}),ni("tauri://update").catch(u=>{throw t(),u})})}function Ir(e){let t;return{c(){t=r("button"),t.innerHTML='
',a(t,"class","btn text-accentText dark:text-darkAccentText flex items-center justify-center")},m(n,i){m(n,t,i)},p:V,d(n){n&&p(t)}}}function Hr(e){let t,n,i;return{c(){t=r("button"),t.textContent="Install update",a(t,"class","btn")},m(l,o){m(l,t,o),n||(i=E(t,"click",e[4]),n=!0)},p:V,d(l){l&&p(t),n=!1,i()}}}function Fr(e){let t,n,i;return{c(){t=r("button"),t.textContent="Check update",a(t,"class","btn")},m(l,o){m(l,t,o),n||(i=E(t,"click",e[3]),n=!0)},p:V,d(l){l&&p(t),n=!1,i()}}}function Nr(e){let t;function n(o,u){return!o[0]&&!o[2]?Fr:!o[1]&&o[2]?Hr:Ir}let i=n(e),l=i(e);return{c(){t=r("div"),l.c(),a(t,"class","flex children:grow children:h10")},m(o,u){m(o,t,u),l.m(t,null)},p(o,[u]){i===(i=n(o))&&l?l.p(o,u):(l.d(1),l=i(o),l&&(l.c(),l.m(t,null)))},i:V,o:V,d(o){o&&p(t),l.d()}}}function jr(e,t,n){let{onMessage:i}=t,l;ut(async()=>{l=await Xt("tauri://update-status",i)}),Pi(()=>{l&&l()});let o,u,d;async function c(){n(0,o=!0);try{const{shouldUpdate:v,manifest:k}=await Zs();i(`Should update: ${v}`),i(k),n(2,d=v)}catch(v){i(v)}finally{n(0,o=!1)}}async function f(){n(1,u=!0);try{await Qs(),i("Installation complete, restart required."),await Fi()}catch(v){i(v)}finally{n(1,u=!1)}}return e.$$set=v=>{"onMessage"in v&&n(5,i=v.onMessage)},[o,u,d,c,f,i]}class Ur extends we{constructor(t){super(),ye(this,t,jr,Nr,me,{onMessage:5})}}var qr={};Te(qr,{readText:()=>eo,writeText:()=>xs});async function xs(e){return L({__tauriModule:"Clipboard",message:{cmd:"writeText",data:e}})}async function eo(){return L({__tauriModule:"Clipboard",message:{cmd:"readText",data:null}})}function Br(e){let t,n,i,l,o,u,d,c;return{c(){t=r("div"),n=r("input"),i=h(),l=r("button"),l.textContent="Write",o=h(),u=r("button"),u.textContent="Read",a(n,"class","grow input"),a(n,"placeholder","Text to write to the clipboard"),a(l,"class","btn"),a(l,"type","button"),a(u,"class","btn"),a(u,"type","button"),a(t,"class","flex gap-1")},m(f,v){m(f,t,v),s(t,n),B(n,e[0]),s(t,i),s(t,l),s(t,o),s(t,u),d||(c=[E(n,"input",e[4]),E(l,"click",e[1]),E(u,"click",e[2])],d=!0)},p(f,[v]){v&1&&n.value!==f[0]&&B(n,f[0])},i:V,o:V,d(f){f&&p(t),d=!1,se(c)}}}function Vr(e,t,n){let{onMessage:i}=t,l="clipboard message";function o(){xs(l).then(()=>{i("Wrote to the clipboard")}).catch(i)}function u(){eo().then(c=>{i(`Clipboard contents: ${c}`)}).catch(i)}function d(){l=this.value,n(0,l)}return e.$$set=c=>{"onMessage"in c&&n(3,i=c.onMessage)},[l,o,u,i,d]}class Gr extends we{constructor(t){super(),ye(this,t,Vr,Br,me,{onMessage:3})}}function Jr(e){let t;return{c(){t=r("div"),t.innerHTML=`
Not available for Linux
- `,a(t,"class","flex flex-col gap-2")},m(n,i){m(n,t,i)},p:V,i:V,o:V,d(n){n&&p(t)}}}function Xr(e,t,n){let{onMessage:i}=t;const l=window.constraints={audio:!0,video:!0};function o(d){const c=document.querySelector("video"),f=d.getVideoTracks();i("Got stream with constraints:",l),i(`Using video device: ${f[0].label}`),window.stream=d,c.srcObject=d}function u(d){if(d.name==="ConstraintNotSatisfiedError"){const c=l.video;i(`The resolution ${c.width.exact}x${c.height.exact} px is not supported by your device.`)}else d.name==="PermissionDeniedError"&&i("Permissions have not been granted to use your camera and microphone, you need to allow the page access to your devices in order for the demo to work.");i(`getUserMedia error: ${d.name}`,d)}return ut(async()=>{try{const d=await navigator.mediaDevices.getUserMedia(l);o(d)}catch(d){u(d)}}),Pi(()=>{window.stream.getTracks().forEach(function(d){d.stop()})}),e.$$set=d=>{"onMessage"in d&&n(0,i=d.onMessage)},[i]}class Yr extends we{constructor(t){super(),ye(this,t,Xr,Jr,me,{onMessage:0})}}function $r(e){let t,n,i,l,o,u;return{c(){t=r("div"),n=r("button"),n.textContent="Show",i=h(),l=r("button"),l.textContent="Hide",a(n,"class","btn"),a(n,"id","show"),a(n,"title","Hides and shows the app after 2 seconds"),a(l,"class","btn"),a(l,"id","hide")},m(d,c){m(d,t,c),s(t,n),s(t,i),s(t,l),o||(u=[E(n,"click",e[0]),E(l,"click",e[1])],o=!0)},p:V,i:V,o:V,d(d){d&&p(t),o=!1,se(u)}}}function Kr(e,t,n){let{onMessage:i}=t;function l(){o().then(()=>{setTimeout(()=>{Fs().then(()=>i("Shown app")).catch(i)},2e3)}).catch(i)}function o(){return Ns().then(()=>i("Hide app")).catch(i)}return e.$$set=u=>{"onMessage"in u&&n(2,i=u.onMessage)},[l,o,i]}class Qr extends we{constructor(t){super(),ye(this,t,Kr,$r,me,{onMessage:2})}}function us(e,t,n){const i=e.slice();return i[32]=t[n],i}function cs(e,t,n){const i=e.slice();return i[35]=t[n],i}function ds(e){let t,n,i,l,o,u,d,c,f,v,k,_,y,g,b;function A(C,T){return C[3]?xr:Zr}let D=A(e),F=D(e);function W(C,T){return C[2]?ta:ea}let j=W(e),S=j(e);return{c(){t=r("div"),n=r("span"),n.textContent="Tauri API Validation",i=h(),l=r("span"),o=r("span"),F.c(),d=h(),c=r("span"),c.innerHTML='
',f=h(),v=r("span"),S.c(),_=h(),y=r("span"),y.innerHTML='
',a(n,"class","lt-sm:pl-10 text-darkPrimaryText"),a(o,"title",u=e[3]?"Switch to Light mode":"Switch to Dark mode"),a(o,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),a(c,"title","Minimize"),a(c,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),a(v,"title",k=e[2]?"Restore":"Maximize"),a(v,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),a(y,"title","Close"),a(y,"class","hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText "),a(l,"class","h-100% children:h-100% children:w-12 children:inline-flex children:items-center children:justify-center"),a(t,"class","w-screen select-none h-8 pl-2 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"),a(t,"data-tauri-drag-region","")},m(C,T){m(C,t,T),s(t,n),s(t,i),s(t,l),s(l,o),F.m(o,null),s(l,d),s(l,c),s(l,f),s(l,v),S.m(v,null),s(l,_),s(l,y),g||(b=[E(o,"click",e[12]),E(c,"click",e[9]),E(v,"click",e[10]),E(y,"click",e[11])],g=!0)},p(C,T){D!==(D=A(C))&&(F.d(1),F=D(C),F&&(F.c(),F.m(o,null))),T[0]&8&&u!==(u=C[3]?"Switch to Light mode":"Switch to Dark mode")&&a(o,"title",u),j!==(j=W(C))&&(S.d(1),S=j(C),S&&(S.c(),S.m(v,null))),T[0]&4&&k!==(k=C[2]?"Restore":"Maximize")&&a(v,"title",k)},d(C){C&&p(t),F.d(),S.d(),g=!1,se(b)}}}function Zr(e){let t;return{c(){t=r("div"),a(t,"class","i-ph-moon")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function xr(e){let t;return{c(){t=r("div"),a(t,"class","i-ph-sun")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function ea(e){let t;return{c(){t=r("div"),a(t,"class","i-codicon-chrome-maximize")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function ta(e){let t;return{c(){t=r("div"),a(t,"class","i-codicon-chrome-restore")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function na(e){let t;return{c(){t=r("span"),a(t,"class","i-codicon-menu animate-duration-300ms animate-fade-in")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function ia(e){let t;return{c(){t=r("span"),a(t,"class","i-codicon-close animate-duration-300ms animate-fade-in")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function fs(e){let t,n,i,l,o,u,d,c,f;function v(y,g){return y[3]?sa:la}let k=v(e),_=k(e);return{c(){t=r("a"),_.c(),n=h(),i=r("br"),l=h(),o=r("div"),u=h(),d=r("br"),a(t,"href","##"),a(t,"class","nv justify-between h-8"),a(o,"class","bg-white/5 h-2px")},m(y,g){m(y,t,g),_.m(t,null),m(y,n,g),m(y,i,g),m(y,l,g),m(y,o,g),m(y,u,g),m(y,d,g),c||(f=E(t,"click",e[12]),c=!0)},p(y,g){k!==(k=v(y))&&(_.d(1),_=k(y),_&&(_.c(),_.m(t,null)))},d(y){y&&p(t),_.d(),y&&p(n),y&&p(i),y&&p(l),y&&p(o),y&&p(u),y&&p(d),c=!1,f()}}}function la(e){let t,n;return{c(){t=z(`Switch to Dark mode - `),n=r("div"),a(n,"class","i-ph-moon")},m(i,l){m(i,t,l),m(i,n,l)},d(i){i&&p(t),i&&p(n)}}}function sa(e){let t,n;return{c(){t=z(`Switch to Light mode - `),n=r("div"),a(n,"class","i-ph-sun")},m(i,l){m(i,t,l),m(i,n,l)},d(i){i&&p(t),i&&p(n)}}}function oa(e){let t,n,i,l,o,u=e[35].label+"",d,c,f,v;function k(){return e[20](e[35])}return{c(){t=r("a"),n=r("div"),l=h(),o=r("p"),d=z(u),a(n,"class",i=e[35].icon+" mr-2"),a(t,"href","##"),a(t,"class",c="nv "+(e[1]===e[35]?"nv_selected":""))},m(_,y){m(_,t,y),s(t,n),s(t,l),s(t,o),s(o,d),f||(v=E(t,"click",k),f=!0)},p(_,y){e=_,y[0]&2&&c!==(c="nv "+(e[1]===e[35]?"nv_selected":""))&&a(t,"class",c)},d(_){_&&p(t),f=!1,v()}}}function ps(e){let t,n=e[35]&&oa(e);return{c(){n&&n.c(),t=xn()},m(i,l){n&&n.m(i,l),m(i,t,l)},p(i,l){i[35]&&n.p(i,l)},d(i){n&&n.d(i),i&&p(t)}}}function ms(e){let t,n=e[32].html+"",i;return{c(){t=new ao(!1),i=xn(),t.a=i},m(l,o){t.m(n,l,o),m(l,i,o)},p(l,o){o[0]&64&&n!==(n=l[32].html+"")&&t.p(n)},d(l){l&&p(i),l&&t.d()}}}function ra(e){let t,n,i,l,o,u,d,c,f,v,k,_,y,g,b,A,D,F,W,j,S,C,T,P,M,N,U,J=e[1].label+"",Q,he,x,ne,Y,_e,O,$,ie,oe,Z,pe,re,be,ee,ke,Ae,R,q=e[5]&&ds(e);function We(H,te){return H[0]?ia:na}let Le=We(e),Me=Le(e),ae=!e[5]&&fs(e),de=e[7],ue=[];for(let H=0;H`,k=h(),_=r("a"),_.innerHTML=`GitHub - `,y=h(),g=r("a"),g.innerHTML=`Source - `,b=h(),A=r("br"),D=h(),F=r("div"),W=h(),j=r("br"),S=h(),C=r("div");for(let H=0;H',be=h(),ee=r("div");for(let H=0;H{Gt(G,1)}),ti()}Se?(Y=new Se(tt(H)),$n(Y.$$.fragment),ze(Y.$$.fragment,1),Vt(Y,ne,null)):Y=null}if(te[0]&64){fe=H[6];let G;for(G=0;G{await confirm("Are you sure?")||O.preventDefault()}),je.onFileDropEvent(O=>{D(`File drop: ${JSON.stringify(O.payload)}`)});const l=navigator.userAgent.toLowerCase(),o=l.includes("android")||l.includes("iphone"),u=[{label:"Welcome",component:Ro,icon:"i-ph-hand-waving"},{label:"Communication",component:qo,icon:"i-codicon-radio-tower"},!o&&{label:"CLI",component:No,icon:"i-codicon-terminal"},!o&&{label:"Dialog",component:lr,icon:"i-codicon-multiple-windows"},{label:"File system",component:ar,icon:"i-codicon-files"},{label:"HTTP",component:gr,icon:"i-ph-globe-hemisphere-west"},!o&&{label:"Notifications",component:kr,icon:"i-codicon-bell-dot"},!o&&{label:"App",component:Qr,icon:"i-codicon-hubot"},!o&&{label:"Window",component:Tr,icon:"i-codicon-window"},!o&&{label:"Shortcuts",component:Dr,icon:"i-codicon-record-keys"},{label:"Shell",component:Or,icon:"i-codicon-terminal-bash"},!o&&{label:"Updater",component:Ur,icon:"i-codicon-cloud-download"},!o&&{label:"Clipboard",component:Gr,icon:"i-codicon-clippy"},{label:"WebRTC",component:Yr,icon:"i-ph-broadcast"}];let d=u[0];function c(O){n(1,d=O)}let f;ut(async()=>{const O=Ut();n(2,f=await O.isMaximized()),Xt("tauri://resize",async()=>{n(2,f=await O.isMaximized())})});function v(){Ut().minimize()}async function k(){const O=Ut();await O.isMaximized()?O.unmaximize():O.maximize()}let _=!1;async function y(){_||(_=await Bs("Are you sure that you want to close this window?",{title:"Tauri API"}),_&&Ut().close())}let g;ut(()=>{n(3,g=localStorage&&localStorage.getItem("theme")=="dark"),_s(g)});function b(){n(3,g=!g),_s(g)}let A=ws([]);gs(e,A,O=>n(6,i=O));function D(O){A.update($=>[{html:`
[${new Date().toLocaleTimeString()}]: `+(typeof O=="string"?O:JSON.stringify(O,null,1))+"
"},...$])}function F(O){A.update($=>[{html:`
[${new Date().toLocaleTimeString()}]: `+O+"
"},...$])}function W(){A.update(()=>[])}let j,S,C;function T(O){C=O.clientY;const $=window.getComputedStyle(j);S=parseInt($.height,10);const ie=Z=>{const pe=Z.clientY-C,re=S-pe;n(4,j.style.height=`${re{document.removeEventListener("mouseup",oe),document.removeEventListener("mousemove",ie)};document.addEventListener("mouseup",oe),document.addEventListener("mousemove",ie)}let P;ut(async()=>{n(5,P=await Os()==="win32")});let M=!1,N,U,J=!1,Q=0,he=0;const x=(O,$,ie)=>Math.min(Math.max($,O),ie);ut(()=>{n(18,N=document.querySelector("#sidebar")),U=document.querySelector("#sidebarToggle"),document.addEventListener("click",O=>{U.contains(O.target)?n(0,M=!M):M&&!N.contains(O.target)&&n(0,M=!1)}),document.addEventListener("touchstart",O=>{if(U.contains(O.target))return;const $=O.touches[0].clientX;(0<$&&$<20&&!M||M)&&(J=!0,Q=$)}),document.addEventListener("touchmove",O=>{if(J){const $=O.touches[0].clientX;he=$;const ie=($-Q)/10;N.style.setProperty("--translate-x",`-${x(0,M?0-ie:18.75-ie,18.75)}rem`)}}),document.addEventListener("touchend",()=>{if(J){const O=(he-Q)/10;n(0,M=M?O>-(18.75/2):O>18.75/2)}J=!1})});const ne=()=>Oi("https://tauri.app/"),Y=O=>{c(O),n(0,M=!1)};function _e(O){Yn[O?"unshift":"push"](()=>{j=O,n(4,j)})}return e.$$.update=()=>{if(e.$$.dirty[0]&1){const O=document.querySelector("#sidebar");O&&aa(O,M)}},[M,d,f,g,j,P,i,u,c,v,k,y,b,A,D,F,W,T,N,ne,Y,_e]}class ca extends we{constructor(t){super(),ye(this,t,ua,ra,me,{},null,[-1,-1])}}new ca({target:document.querySelector("#app")}); + Additionally, it has a update --background subcommand.`,n=h(),i=a("br"),l=h(),o=a("div"),o.textContent="Note that the arguments are only parsed, not implemented.",u=h(),d=a("br"),c=h(),f=a("br"),y=h(),k=a("button"),k.textContent="Get matches",r(o,"class","note"),r(k,"class","btn"),r(k,"id","cli-matches")},m(g,b){m(g,t,b),m(g,n,b),m(g,i,b),m(g,l,b),m(g,o,b),m(g,u,b),m(g,d,b),m(g,c,b),m(g,f,b),m(g,y,b),m(g,k,b),_||(v=E(k,"click",e[0]),_=!0)},p:V,i:V,o:V,d(g){g&&p(t),g&&p(n),g&&p(i),g&&p(l),g&&p(o),g&&p(u),g&&p(d),g&&p(c),g&&p(f),g&&p(y),g&&p(k),_=!1,v()}}}function Ho(e,t,n){let{onMessage:i}=t;function l(){Us().then(i).catch(i)}return e.$$set=o=>{"onMessage"in o&&n(1,i=o.onMessage)},[l,i]}class Fo extends ye{constructor(t){super(),ge(this,t,Ho,Io,he,{onMessage:1})}}function No(e){let t,n,i,l,o,u,d,c;return{c(){t=a("div"),n=a("button"),n.textContent="Call Log API",i=h(),l=a("button"),l.textContent="Call Request (async) API",o=h(),u=a("button"),u.textContent="Send event to Rust",r(n,"class","btn"),r(n,"id","log"),r(l,"class","btn"),r(l,"id","request"),r(u,"class","btn"),r(u,"id","event")},m(f,y){m(f,t,y),s(t,n),s(t,i),s(t,l),s(t,o),s(t,u),d||(c=[E(n,"click",e[0]),E(l,"click",e[1]),E(u,"click",e[2])],d=!0)},p:V,i:V,o:V,d(f){f&&p(t),d=!1,ue(c)}}}function jo(e,t,n){let{onMessage:i}=t,l;ut(async()=>{l=await Xt("rust-event",i)}),Wi(()=>{l&&l()});function o(){$n("log_operation",{event:"tauri-click",payload:"this payload is optional because we used Option in Rust"})}function u(){$n("perform_request",{endpoint:"dummy endpoint arg",body:{id:5,name:"test"}}).then(i).catch(i)}function d(){ni("js-event","this is the payload string")}return e.$$set=c=>{"onMessage"in c&&n(3,i=c.onMessage)},[o,u,d,i]}class Uo extends ye{constructor(t){super(),ge(this,t,jo,No,he,{onMessage:3})}}var qo={};we(qo,{ask:()=>Bs,confirm:()=>Vo,message:()=>Bo,open:()=>Ni,save:()=>qs});async function Ni(e={}){return typeof e=="object"&&Object.freeze(e),S({__tauriModule:"Dialog",message:{cmd:"openDialog",options:e}})}async function qs(e={}){return typeof e=="object"&&Object.freeze(e),S({__tauriModule:"Dialog",message:{cmd:"saveDialog",options:e}})}async function Bo(e,t){var i;let n=typeof t=="string"?{title:t}:t;return S({__tauriModule:"Dialog",message:{cmd:"messageDialog",message:e.toString(),title:(i=n==null?void 0:n.title)==null?void 0:i.toString(),type:n==null?void 0:n.type}})}async function Bs(e,t){var i;let n=typeof t=="string"?{title:t}:t;return S({__tauriModule:"Dialog",message:{cmd:"askDialog",message:e.toString(),title:(i=n==null?void 0:n.title)==null?void 0:i.toString(),type:n==null?void 0:n.type}})}async function Vo(e,t){var i;let n=typeof t=="string"?{title:t}:t;return S({__tauriModule:"Dialog",message:{cmd:"confirmDialog",message:e.toString(),title:(i=n==null?void 0:n.title)==null?void 0:i.toString(),type:n==null?void 0:n.type}})}var Go={};we(Go,{BaseDirectory:()=>Jt,Dir:()=>Jt,copyFile:()=>$o,createDir:()=>Yo,exists:()=>xo,readBinaryFile:()=>ji,readDir:()=>Vs,readTextFile:()=>Jo,removeDir:()=>Ko,removeFile:()=>Qo,renameFile:()=>Zo,writeBinaryFile:()=>Xo,writeFile:()=>Di,writeTextFile:()=>Di});var Jt=(e=>(e[e.Audio=1]="Audio",e[e.Cache=2]="Cache",e[e.Config=3]="Config",e[e.Data=4]="Data",e[e.LocalData=5]="LocalData",e[e.Desktop=6]="Desktop",e[e.Document=7]="Document",e[e.Download=8]="Download",e[e.Executable=9]="Executable",e[e.Font=10]="Font",e[e.Home=11]="Home",e[e.Picture=12]="Picture",e[e.Public=13]="Public",e[e.Runtime=14]="Runtime",e[e.Template=15]="Template",e[e.Video=16]="Video",e[e.Resource=17]="Resource",e[e.App=18]="App",e[e.Log=19]="Log",e[e.Temp=20]="Temp",e[e.AppConfig=21]="AppConfig",e[e.AppData=22]="AppData",e[e.AppLocalData=23]="AppLocalData",e[e.AppCache=24]="AppCache",e[e.AppLog=25]="AppLog",e))(Jt||{});async function Jo(e,t={}){return S({__tauriModule:"Fs",message:{cmd:"readTextFile",path:e,options:t}})}async function ji(e,t={}){let n=await S({__tauriModule:"Fs",message:{cmd:"readFile",path:e,options:t}});return Uint8Array.from(n)}async function Di(e,t,n){typeof n=="object"&&Object.freeze(n),typeof e=="object"&&Object.freeze(e);let i={path:"",contents:""},l=n;return typeof e=="string"?i.path=e:(i.path=e.path,i.contents=e.contents),typeof t=="string"?i.contents=t!=null?t:"":l=t,S({__tauriModule:"Fs",message:{cmd:"writeFile",path:i.path,contents:Array.from(new TextEncoder().encode(i.contents)),options:l}})}async function Xo(e,t,n){typeof n=="object"&&Object.freeze(n),typeof e=="object"&&Object.freeze(e);let i={path:"",contents:[]},l=n;return typeof e=="string"?i.path=e:(i.path=e.path,i.contents=e.contents),t&&"dir"in t?l=t:typeof e=="string"&&(i.contents=t!=null?t:[]),S({__tauriModule:"Fs",message:{cmd:"writeFile",path:i.path,contents:Array.from(i.contents instanceof ArrayBuffer?new Uint8Array(i.contents):i.contents),options:l}})}async function Vs(e,t={}){return S({__tauriModule:"Fs",message:{cmd:"readDir",path:e,options:t}})}async function Yo(e,t={}){return S({__tauriModule:"Fs",message:{cmd:"createDir",path:e,options:t}})}async function Ko(e,t={}){return S({__tauriModule:"Fs",message:{cmd:"removeDir",path:e,options:t}})}async function $o(e,t,n={}){return S({__tauriModule:"Fs",message:{cmd:"copyFile",source:e,destination:t,options:n}})}async function Qo(e,t={}){return S({__tauriModule:"Fs",message:{cmd:"removeFile",path:e,options:t}})}async function Zo(e,t,n={}){return S({__tauriModule:"Fs",message:{cmd:"renameFile",oldPath:e,newPath:t,options:n}})}async function xo(e,t={}){return S({__tauriModule:"Fs",message:{cmd:"exists",path:e,options:t}})}function ea(e){let t,n,i,l,o,u,d,c,f,y,k,_,v,g,b,A,O,I,P,F,L,T,C,W;return{c(){t=a("div"),n=a("input"),i=h(),l=a("input"),o=h(),u=a("br"),d=h(),c=a("div"),f=a("input"),y=h(),k=a("label"),k.textContent="Multiple",_=h(),v=a("div"),g=a("input"),b=h(),A=a("label"),A.textContent="Directory",O=h(),I=a("br"),P=h(),F=a("button"),F.textContent="Open dialog",L=h(),T=a("button"),T.textContent="Open save dialog",r(n,"class","input"),r(n,"id","dialog-default-path"),r(n,"placeholder","Default path"),r(l,"class","input"),r(l,"id","dialog-filter"),r(l,"placeholder","Extensions filter, comma-separated"),r(t,"class","flex gap-2 children:grow"),r(f,"type","checkbox"),r(f,"id","dialog-multiple"),r(k,"for","dialog-multiple"),r(g,"type","checkbox"),r(g,"id","dialog-directory"),r(A,"for","dialog-directory"),r(F,"class","btn"),r(F,"id","open-dialog"),r(T,"class","btn"),r(T,"id","save-dialog")},m(M,j){m(M,t,j),s(t,n),q(n,e[0]),s(t,i),s(t,l),q(l,e[1]),m(M,o,j),m(M,u,j),m(M,d,j),m(M,c,j),s(c,f),f.checked=e[2],s(c,y),s(c,k),m(M,_,j),m(M,v,j),s(v,g),g.checked=e[3],s(v,b),s(v,A),m(M,O,j),m(M,I,j),m(M,P,j),m(M,F,j),m(M,L,j),m(M,T,j),C||(W=[E(n,"input",e[8]),E(l,"input",e[9]),E(f,"change",e[10]),E(g,"change",e[11]),E(F,"click",e[4]),E(T,"click",e[5])],C=!0)},p(M,[j]){j&1&&n.value!==M[0]&&q(n,M[0]),j&2&&l.value!==M[1]&&q(l,M[1]),j&4&&(f.checked=M[2]),j&8&&(g.checked=M[3])},i:V,o:V,d(M){M&&p(t),M&&p(o),M&&p(u),M&&p(d),M&&p(c),M&&p(_),M&&p(v),M&&p(O),M&&p(I),M&&p(P),M&&p(F),M&&p(L),M&&p(T),C=!1,ue(W)}}}function ta(e,t){var n=new Blob([e],{type:"application/octet-binary"}),i=new FileReader;i.onload=function(l){var o=l.target.result;t(o.substr(o.indexOf(",")+1))},i.readAsDataURL(n)}function na(e,t,n){let{onMessage:i}=t,{insecureRenderHtml:l}=t,o=null,u=null,d=!1,c=!1;function f(){Ni({title:"My wonderful open dialog",defaultPath:o,filters:u?[{name:"Tauri Example",extensions:u.split(",").map(b=>b.trim())}]:[],multiple:d,directory:c}).then(function(b){if(Array.isArray(b))i(b);else{var A=b,O=A.match(/\S+\.\S+$/g);ji(A).then(function(I){O&&(A.includes(".png")||A.includes(".jpg"))?ta(new Uint8Array(I),function(P){var F="data:image/png;base64,"+P;l('')}):i(b)}).catch(i(b))}}).catch(i)}function y(){qs({title:"My wonderful save dialog",defaultPath:o,filters:u?[{name:"Tauri Example",extensions:u.split(",").map(b=>b.trim())}]:[]}).then(i).catch(i)}function k(){o=this.value,n(0,o)}function _(){u=this.value,n(1,u)}function v(){d=this.checked,n(2,d)}function g(){c=this.checked,n(3,c)}return e.$$set=b=>{"onMessage"in b&&n(6,i=b.onMessage),"insecureRenderHtml"in b&&n(7,l=b.insecureRenderHtml)},[o,u,d,c,f,y,i,l,k,_,v,g]}class ia extends ye{constructor(t){super(),ge(this,t,na,ea,he,{onMessage:6,insecureRenderHtml:7})}}function Bl(e,t,n){const i=e.slice();return i[9]=t[n],i}function Vl(e){let t,n=e[9][0]+"",i;return{c(){t=a("option"),i=z(n),t.__value=e[9][1],t.value=t.__value},m(l,o){m(l,t,o),s(t,i)},p:V,d(l){l&&p(t)}}}function la(e){let t,n,i,l,o,u,d,c,f,y,k,_,v,g,b,A,O,I,P,F=e[2],L=[];for(let T=0;TisNaN(parseInt(_))).map(_=>[_,Jt[_]]);function c(){const _=o.match(/\S+\.\S+$/g),v={dir:Gl()};(_?ji(o,v):Vs(o,v)).then(function(b){if(_)if(o.includes(".png")||o.includes(".jpg"))sa(new Uint8Array(b),function(A){const O="data:image/png;base64,"+A;l('')});else{const A=String.fromCharCode.apply(null,b);l(''),setTimeout(()=>{const O=document.getElementById("file-response");O.value=A,document.getElementById("file-save").addEventListener("click",function(){Di(o,O.value,{dir:Gl()}).catch(i)})})}else i(b)}).catch(i)}function f(){n(1,u.src=ks(o),u)}function y(){o=this.value,n(0,o)}function k(_){Yn[_?"unshift":"push"](()=>{u=_,n(1,u)})}return e.$$set=_=>{"onMessage"in _&&n(5,i=_.onMessage),"insecureRenderHtml"in _&&n(6,l=_.insecureRenderHtml)},[o,u,d,c,f,i,l,y,k]}class aa extends ye{constructor(t){super(),ge(this,t,oa,la,he,{onMessage:5,insecureRenderHtml:6})}}var ra={};we(ra,{Body:()=>tt,Client:()=>Js,Response:()=>Gs,ResponseType:()=>Ui,fetch:()=>ua,getClient:()=>Zn});var Ui=(e=>(e[e.JSON=1]="JSON",e[e.Text=2]="Text",e[e.Binary=3]="Binary",e))(Ui||{}),tt=class{constructor(e,t){this.type=e,this.payload=t}static form(e){let t={},n=(i,l)=>{if(l!==null){let o;typeof l=="string"?o=l:l instanceof Uint8Array||Array.isArray(l)?o=Array.from(l):l instanceof File?o={file:l.name,mime:l.type,fileName:l.name}:typeof l.file=="string"?o={file:l.file,mime:l.mime,fileName:l.fileName}:o={file:Array.from(l.file),mime:l.mime,fileName:l.fileName},t[String(i)]=o}};if(e instanceof FormData)for(let[i,l]of e)n(i,l);else for(let[i,l]of Object.entries(e))n(i,l);return new tt("Form",t)}static json(e){return new tt("Json",e)}static text(e){return new tt("Text",e)}static bytes(e){return new tt("Bytes",Array.from(e instanceof ArrayBuffer?new Uint8Array(e):e))}},Gs=class{constructor(e){this.url=e.url,this.status=e.status,this.ok=this.status>=200&&this.status<300,this.headers=e.headers,this.rawHeaders=e.rawHeaders,this.data=e.data}},Js=class{constructor(e){this.id=e}async drop(){return S({__tauriModule:"Http",message:{cmd:"dropClient",client:this.id}})}async request(e){let t=!e.responseType||e.responseType===1;return t&&(e.responseType=2),S({__tauriModule:"Http",message:{cmd:"httpRequest",client:this.id,options:e}}).then(n=>{let i=new Gs(n);if(t){try{i.data=JSON.parse(i.data)}catch(l){if(i.ok&&i.data==="")i.data={};else if(i.ok)throw Error(`Failed to parse response \`${i.data}\` as JSON: ${l}; + try setting the \`responseType\` option to \`ResponseType.Text\` or \`ResponseType.Binary\` if the API does not return a JSON response.`)}return i}return i})}async get(e,t){return this.request({method:"GET",url:e,...t})}async post(e,t,n){return this.request({method:"POST",url:e,body:t,...n})}async put(e,t,n){return this.request({method:"PUT",url:e,body:t,...n})}async patch(e,t){return this.request({method:"PATCH",url:e,...t})}async delete(e,t){return this.request({method:"DELETE",url:e,...t})}};async function Zn(e){return S({__tauriModule:"Http",message:{cmd:"createClient",options:e}}).then(t=>new Js(t))}var Li=null;async function ua(e,t){var n;return Li===null&&(Li=await Zn()),Li.request({url:e,method:(n=t==null?void 0:t.method)!=null?n:"GET",...t})}function Jl(e,t,n){const i=e.slice();return i[12]=t[n],i[14]=n,i}function Xl(e){let t,n,i,l,o,u,d,c,f,y,k,_,v,g,b,A,O,I=e[5],P=[];for(let C=0;CRe(P[C],1,1,()=>{P[C]=null});let L=!e[3]&&Ql(),T=!e[3]&&e[8]&&Zl();return{c(){t=a("span"),n=a("span"),i=z(e[6]),l=h(),o=a("ul");for(let C=0;C{y[g]=null}),ti(),o=y[l],o?o.p(_,v):(o=y[l]=f[l](_),o.c()),Me(o,1),o.m(t,u))},i(_){d||(Me(o),d=!0)},o(_){Re(o),d=!1},d(_){_&&p(t),c&&c.d(),y[l].d()}}}function Ql(e){let t;return{c(){t=a("span"),t.textContent=",",r(t,"class","comma svelte-gbh3pt")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function Zl(e){let t;return{c(){t=a("span"),t.textContent=",",r(t,"class","comma svelte-gbh3pt")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function fa(e){let t,n,i=e[5].length&&Xl(e);return{c(){i&&i.c(),t=xn()},m(l,o){i&&i.m(l,o),m(l,t,o),n=!0},p(l,[o]){l[5].length?i?(i.p(l,o),o&32&&Me(i,1)):(i=Xl(l),i.c(),Me(i,1),i.m(t.parentNode,t)):i&&(ei(),Re(i,1,1,()=>{i=null}),ti())},i(l){n||(Me(i),n=!0)},o(l){Re(i),n=!1},d(l){i&&i.d(l),l&&p(t)}}}const pa="...";function ma(e,t,n){let{json:i}=t,{depth:l=1/0}=t,{_lvl:o=0}=t,{_last:u=!0}=t;const d=b=>b===null?"null":typeof b;let c,f,y,k,_;const v=b=>{switch(d(b)){case"string":return`"${b}"`;case"function":return"f () {...}";case"symbol":return b.toString();default:return b}},g=()=>{n(8,_=!_)};return e.$$set=b=>{"json"in b&&n(0,i=b.json),"depth"in b&&n(1,l=b.depth),"_lvl"in b&&n(2,o=b._lvl),"_last"in b&&n(3,u=b._last)},e.$$.update=()=>{e.$$.dirty&17&&(n(5,c=d(i)==="object"?Object.keys(i):[]),n(4,f=Array.isArray(i)),n(6,y=f?"[":"{"),n(7,k=f?"]":"}")),e.$$.dirty&6&&n(8,_=le[9].call(n)),r(k,"class","input h-auto w-100%"),r(k,"id","request-body"),r(k,"placeholder","Request body"),r(k,"rows","5"),r(b,"class","btn"),r(b,"id","make-request"),r(T,"class","input"),r(W,"class","input"),r(L,"class","flex gap-2 children:grow"),r(ne,"type","checkbox"),r(K,"class","btn"),r(K,"type","button")},m(D,B){m(D,t,B),s(t,n),s(n,i),s(n,l),s(n,o),s(n,u),s(n,d),zt(n,e[0]),s(t,c),s(t,f),s(t,y),s(t,k),q(k,e[1]),s(t,_),s(t,v),s(t,g),s(t,b),m(D,A,B),m(D,O,B),m(D,I,B),m(D,P,B),m(D,F,B),m(D,L,B),s(L,T),q(T,e[2]),s(L,C),s(L,W),q(W,e[3]),m(D,M,B),m(D,j,B),m(D,U,B),m(D,J,B),s(J,ne),ne.checked=e[5],s(J,de),m(D,x,B),m(D,Y,B),m(D,fe,B),m(D,ee,B),m(D,H,B),m(D,K,B),m(D,te,B),m(D,pe,B),m(D,se,B),m(D,ae,B),m(D,_e,B),Vt(ce,D,B),ie=!0,Te||(Ce=[E(n,"change",e[9]),E(k,"input",e[10]),E(t,"submit",Xn(e[6])),E(T,"input",e[11]),E(W,"input",e[12]),E(ne,"change",e[13]),E(K,"click",e[7])],Te=!0)},p(D,[B]){B&1&&zt(n,D[0]),B&2&&q(k,D[1]),B&4&&T.value!==D[2]&&q(T,D[2]),B&8&&W.value!==D[3]&&q(W,D[3]),B&32&&(ne.checked=D[5]);const Ae={};B&16&&(Ae.json=D[4]),ce.$set(Ae)},i(D){ie||(Me(ce.$$.fragment,D),ie=!0)},o(D){Re(ce.$$.fragment,D),ie=!1},d(D){D&&p(t),D&&p(A),D&&p(O),D&&p(I),D&&p(P),D&&p(F),D&&p(L),D&&p(M),D&&p(j),D&&p(U),D&&p(J),D&&p(x),D&&p(Y),D&&p(fe),D&&p(ee),D&&p(H),D&&p(K),D&&p(te),D&&p(pe),D&&p(se),D&&p(ae),D&&p(_e),Gt(ce,D),Te=!1,ue(Ce)}}}function _a(e,t,n){let i="GET",l="",{onMessage:o}=t;async function u(){const O=await Zn().catch(F=>{throw o(F),F}),P={url:"http://localhost:3003",method:i||"GET"||"GET"};l.startsWith("{")&&l.endsWith("}")||l.startsWith("[")&&l.endsWith("]")?P.body=tt.json(JSON.parse(l)):l!==""&&(P.body=tt.text(l)),O.request(P).then(o).catch(o)}let d="baz",c="qux",f=null,y=!0;async function k(){const O=await Zn().catch(I=>{throw o(I),I});n(4,f=await O.request({url:"http://localhost:3003",method:"POST",body:tt.form({foo:d,bar:c}),headers:y?{"Content-Type":"multipart/form-data"}:void 0,responseType:Ui.Text}))}function _(){i=Ei(this),n(0,i)}function v(){l=this.value,n(1,l)}function g(){d=this.value,n(2,d)}function b(){c=this.value,n(3,c)}function A(){y=this.checked,n(5,y)}return e.$$set=O=>{"onMessage"in O&&n(8,o=O.onMessage)},[i,l,d,c,f,y,u,k,o,_,v,g,b,A]}class ba extends ye{constructor(t){super(),ge(this,t,_a,ha,he,{onMessage:8})}}function ga(e){let t,n,i;return{c(){t=a("button"),t.textContent="Send test notification",r(t,"class","btn"),r(t,"id","notification")},m(l,o){m(l,t,o),n||(i=E(t,"click",ya),n=!0)},p:V,i:V,o:V,d(l){l&&p(t),n=!1,i()}}}function ya(){new Notification("Notification title",{body:"This is the notification body"})}function va(e,t,n){let{onMessage:i}=t;return e.$$set=l=>{"onMessage"in l&&n(0,i=l.onMessage)},[i]}class wa extends ye{constructor(t){super(),ge(this,t,va,ga,he,{onMessage:0})}}function xl(e,t,n){const i=e.slice();return i[67]=t[n],i}function es(e,t,n){const i=e.slice();return i[70]=t[n],i}function ts(e){let t,n,i,l,o,u,d=Object.keys(e[1]),c=[];for(let f=0;fe[39].call(i))},m(f,y){m(f,t,y),m(f,n,y),m(f,i,y),s(i,l);for(let k=0;ke[57].call(je)),r($e,"class","input"),r($e,"type","number"),r(Qe,"class","input"),r(Qe,"type","number"),r(Ne,"class","flex gap-2"),r(Ze,"class","input grow"),r(Ze,"id","title"),r(Ft,"class","btn"),r(Ft,"type","submit"),r(at,"class","flex gap-1"),r(xe,"class","input grow"),r(xe,"id","url"),r(Nt,"class","btn"),r(Nt,"id","open-url"),r(rt,"class","flex gap-1"),r(ot,"class","flex flex-col gap-1")},m(w,R){m(w,t,R),m(w,n,R),m(w,i,R),s(i,l),s(i,o),s(i,u),s(i,d),s(i,c),s(i,f),s(i,y),s(i,k),s(i,_),m(w,v,R),m(w,g,R),m(w,b,R),m(w,A,R),s(A,O),s(O,I),s(O,P),P.checked=e[3],s(A,F),s(A,L),s(L,T),s(L,C),C.checked=e[2],s(A,W),s(A,M),s(M,j),s(M,U),U.checked=e[4],s(A,J),s(A,ne),s(ne,de),s(ne,x),x.checked=e[5],s(A,Y),s(A,fe),s(fe,ee),s(fe,H),H.checked=e[6],m(w,K,R),m(w,te,R),m(w,pe,R),m(w,se,R),s(se,ae),s(ae,_e),s(_e,ce),s(_e,ie),q(ie,e[13]),s(ae,Te),s(ae,Ce),s(Ce,D),s(Ce,B),q(B,e[14]),s(se,Ae),s(se,me),s(me,oe),s(oe,Le),s(oe,Q),q(Q,e[7]),s(me,Ee),s(me,Ie),s(Ie,ze),s(Ie,Z),q(Z,e[8]),s(se,N),s(se,le),s(le,G),s(G,We),s(G,Oe),q(Oe,e[9]),s(le,Yt),s(le,ht),s(ht,Kt),s(ht,Pe),q(Pe,e[10]),s(se,$t),s(se,qe),s(qe,_t),s(_t,Qt),s(_t,X),q(X,e[11]),s(qe,Wt),s(qe,nt),s(nt,Ot),s(nt,Se),q(Se,e[12]),m(w,bt,R),m(w,gt,R),m(w,yt,R),m(w,ke,R),s(ke,He),s(He,De),s(De,it),s(De,Pt),s(De,lt),s(lt,Rt),s(lt,vt),s(De,It),s(De,wt),s(wt,Bi),s(wt,ii),s(He,Vi),s(He,Be),s(Be,xt),s(Be,Gi),s(Be,en),s(en,Ji),s(en,li),s(Be,Xi),s(Be,nn),s(nn,Yi),s(nn,si),s(ke,Ki),s(ke,kt),s(kt,Ve),s(Ve,sn),s(Ve,$i),s(Ve,on),s(on,Qi),s(on,oi),s(Ve,Zi),s(Ve,rn),s(rn,xi),s(rn,ai),s(kt,el),s(kt,Ge),s(Ge,cn),s(Ge,tl),s(Ge,dn),s(dn,nl),s(dn,ri),s(Ge,il),s(Ge,pn),s(pn,ll),s(pn,ui),s(ke,sl),s(ke,Mt),s(Mt,Je),s(Je,hn),s(Je,ol),s(Je,_n),s(_n,al),s(_n,ci),s(Je,rl),s(Je,gn),s(gn,ul),s(gn,di),s(Mt,cl),s(Mt,Xe),s(Xe,vn),s(Xe,dl),s(Xe,wn),s(wn,fl),s(wn,fi),s(Xe,pl),s(Xe,Mn),s(Mn,ml),s(Mn,pi),s(ke,hl),s(ke,Tt),s(Tt,Ye),s(Ye,Cn),s(Ye,_l),s(Ye,An),s(An,bl),s(An,mi),s(Ye,gl),s(Ye,Ln),s(Ln,yl),s(Ln,hi),s(Tt,vl),s(Tt,Ke),s(Ke,zn),s(Ke,wl),s(Ke,Dn),s(Dn,kl),s(Dn,_i),s(Ke,Ml),s(Ke,On),s(On,Tl),s(On,bi),m(w,gi,R),m(w,yi,R),m(w,vi,R),m(w,Ht,R),m(w,wi,R),m(w,Fe,R),s(Fe,Rn),s(Rn,Ct),Ct.checked=e[15],s(Rn,Cl),s(Fe,Al),s(Fe,In),s(In,At),At.checked=e[16],s(In,Sl),s(Fe,Ll),s(Fe,Hn),s(Hn,St),St.checked=e[20],s(Hn,El),m(w,ki,R),m(w,Ne,R),s(Ne,Fn),s(Fn,zl),s(Fn,je);for(let be=0;be=1,y,k,_,v=f&&ts(e),g=e[1][e[0]]&&is(e);return{c(){t=a("div"),n=a("div"),i=a("input"),l=h(),o=a("button"),o.textContent="New window",u=h(),d=a("br"),c=h(),v&&v.c(),y=h(),g&&g.c(),r(i,"class","input grow"),r(i,"type","text"),r(i,"placeholder","New Window label.."),r(o,"class","btn"),r(n,"class","flex gap-1"),r(t,"class","flex flex-col children:grow gap-2")},m(b,A){m(b,t,A),s(t,n),s(n,i),q(i,e[21]),s(n,l),s(n,o),s(t,u),s(t,d),s(t,c),v&&v.m(t,null),s(t,y),g&&g.m(t,null),k||(_=[E(i,"input",e[38]),E(o,"click",e[35])],k=!0)},p(b,A){A[0]&2097152&&i.value!==b[21]&&q(i,b[21]),A[0]&2&&(f=Object.keys(b[1]).length>=1),f?v?v.p(b,A):(v=ts(b),v.c(),v.m(t,y)):v&&(v.d(1),v=null),b[1][b[0]]?g?g.p(b,A):(g=is(b),g.c(),g.m(t,null)):g&&(g.d(1),g=null)},i:V,o:V,d(b){b&&p(t),v&&v.d(),g&&g.d(),k=!1,ue(_)}}}function Ma(e,t,n){let i=Ue.label;const l={[Ue.label]:Ue},o=["default","crosshair","hand","arrow","move","text","wait","help","progress","notAllowed","contextMenu","cell","verticalText","alias","copy","noDrop","grab","grabbing","allScroll","zoomIn","zoomOut","eResize","nResize","neResize","nwResize","sResize","seResize","swResize","wResize","ewResize","nsResize","neswResize","nwseResize","colResize","rowResize"];let{onMessage:u}=t,d,c="https://tauri.app",f=!0,y=!1,k=!0,_=!1,v=!1,g=null,b=null,A=null,O=null,I=null,P=null,F=null,L=null,T=1,C=new et(F,L),W=new et(F,L),M=new dt(g,b),j=new dt(g,b),U,J,ne=!1,de=!0,x=null,Y=null,fe="default",ee=!1,H="Awesome Tauri Example!";function K(){Pi(c)}function te(){l[i].setTitle(H)}function pe(){l[i].hide(),setTimeout(l[i].show,2e3)}function se(){l[i].minimize(),setTimeout(l[i].unminimize,2e3)}function ae(){Ni({multiple:!1}).then(X=>{typeof X=="string"&&l[i].setIcon(X)})}function _e(){if(!d)return;const X=new mt(d);n(1,l[d]=X,l),X.once("tauri://error",function(){u("Error creating new webview")})}function ce(){l[i].innerSize().then(X=>{n(26,M=X),n(7,g=M.width),n(8,b=M.height)}),l[i].outerSize().then(X=>{n(27,j=X)})}function ie(){l[i].innerPosition().then(X=>{n(24,C=X)}),l[i].outerPosition().then(X=>{n(25,W=X),n(13,F=W.x),n(14,L=W.y)})}async function Te(X){!X||(U&&U(),J&&J(),J=await X.listen("tauri://move",ie),U=await X.listen("tauri://resize",ce))}async function Ce(){await l[i].minimize(),await l[i].requestUserAttention(Ii.Critical),await new Promise(X=>setTimeout(X,3e3)),await l[i].requestUserAttention(null)}function D(){d=this.value,n(21,d)}function B(){i=Ei(this),n(0,i),n(1,l)}const Ae=()=>l[i].center();function me(){y=this.checked,n(3,y)}function oe(){f=this.checked,n(2,f)}function Le(){k=this.checked,n(4,k)}function Q(){_=this.checked,n(5,_)}function Ee(){v=this.checked,n(6,v)}function Ie(){F=re(this.value),n(13,F)}function ze(){L=re(this.value),n(14,L)}function Z(){g=re(this.value),n(7,g)}function N(){b=re(this.value),n(8,b)}function le(){A=re(this.value),n(9,A)}function G(){O=re(this.value),n(10,O)}function We(){I=re(this.value),n(11,I)}function Oe(){P=re(this.value),n(12,P)}function Yt(){ne=this.checked,n(15,ne)}function ht(){de=this.checked,n(16,de)}function Kt(){ee=this.checked,n(20,ee)}function Pe(){fe=Ei(this),n(19,fe),n(29,o)}function $t(){x=re(this.value),n(17,x)}function qe(){Y=re(this.value),n(18,Y)}function _t(){H=this.value,n(28,H)}function Qt(){c=this.value,n(22,c)}return e.$$set=X=>{"onMessage"in X&&n(37,u=X.onMessage)},e.$$.update=()=>{var X,Wt,nt,Ot,Se,bt,gt,yt,ke,He,De,it,Pt,lt,Rt,st,vt,It;e.$$.dirty[0]&3&&(l[i],ie(),ce()),e.$$.dirty[0]&7&&((X=l[i])==null||X.setResizable(f)),e.$$.dirty[0]&11&&(y?(Wt=l[i])==null||Wt.maximize():(nt=l[i])==null||nt.unmaximize()),e.$$.dirty[0]&19&&((Ot=l[i])==null||Ot.setDecorations(k)),e.$$.dirty[0]&35&&((Se=l[i])==null||Se.setAlwaysOnTop(_)),e.$$.dirty[0]&67&&((bt=l[i])==null||bt.setFullscreen(v)),e.$$.dirty[0]&387&&g&&b&&((gt=l[i])==null||gt.setSize(new dt(g,b))),e.$$.dirty[0]&1539&&(A&&O?(yt=l[i])==null||yt.setMinSize(new Qn(A,O)):(ke=l[i])==null||ke.setMinSize(null)),e.$$.dirty[0]&6147&&(I>800&&P>400?(He=l[i])==null||He.setMaxSize(new Qn(I,P)):(De=l[i])==null||De.setMaxSize(null)),e.$$.dirty[0]&24579&&F!==null&&L!==null&&((it=l[i])==null||it.setPosition(new et(F,L))),e.$$.dirty[0]&3&&((Pt=l[i])==null||Pt.scaleFactor().then(wt=>n(23,T=wt))),e.$$.dirty[0]&3&&Te(l[i]),e.$$.dirty[0]&32771&&((lt=l[i])==null||lt.setCursorGrab(ne)),e.$$.dirty[0]&65539&&((Rt=l[i])==null||Rt.setCursorVisible(de)),e.$$.dirty[0]&524291&&((st=l[i])==null||st.setCursorIcon(fe)),e.$$.dirty[0]&393219&&x!==null&&Y!==null&&((vt=l[i])==null||vt.setCursorPosition(new et(x,Y))),e.$$.dirty[0]&1048579&&((It=l[i])==null||It.setIgnoreCursorEvents(ee))},[i,l,f,y,k,_,v,g,b,A,O,I,P,F,L,ne,de,x,Y,fe,ee,d,c,T,C,W,M,j,H,o,K,te,pe,se,ae,_e,Ce,u,D,B,Ae,me,oe,Le,Q,Ee,Ie,ze,Z,N,le,G,We,Oe,Yt,ht,Kt,Pe,$t,qe,_t,Qt]}class Ta extends ye{constructor(t){super(),ge(this,t,Ma,ka,he,{onMessage:37},null,[-1,-1,-1])}}var Ca={};we(Ca,{isRegistered:()=>Sa,register:()=>Ys,registerAll:()=>Aa,unregister:()=>Ks,unregisterAll:()=>$s});async function Ys(e,t){return S({__tauriModule:"GlobalShortcut",message:{cmd:"register",shortcut:e,handler:pt(t)}})}async function Aa(e,t){return S({__tauriModule:"GlobalShortcut",message:{cmd:"registerAll",shortcuts:e,handler:pt(t)}})}async function Sa(e){return S({__tauriModule:"GlobalShortcut",message:{cmd:"isRegistered",shortcut:e}})}async function Ks(e){return S({__tauriModule:"GlobalShortcut",message:{cmd:"unregister",shortcut:e}})}async function $s(){return S({__tauriModule:"GlobalShortcut",message:{cmd:"unregisterAll"}})}function ss(e,t,n){const i=e.slice();return i[9]=t[n],i}function os(e){let t,n=e[9]+"",i,l,o,u,d;function c(){return e[8](e[9])}return{c(){t=a("div"),i=z(n),l=h(),o=a("button"),o.textContent="Unregister",r(o,"class","btn"),r(o,"type","button"),r(t,"class","flex justify-between")},m(f,y){m(f,t,y),s(t,i),s(t,l),s(t,o),u||(d=E(o,"click",c),u=!0)},p(f,y){e=f,y&2&&n!==(n=e[9]+"")&&$(i,n)},d(f){f&&p(t),u=!1,d()}}}function as(e){let t,n,i,l,o;return{c(){t=a("br"),n=h(),i=a("button"),i.textContent="Unregister all",r(i,"class","btn"),r(i,"type","button")},m(u,d){m(u,t,d),m(u,n,d),m(u,i,d),l||(o=E(i,"click",e[5]),l=!0)},p:V,d(u){u&&p(t),u&&p(n),u&&p(i),l=!1,o()}}}function La(e){let t,n,i,l,o,u,d,c,f,y,k,_=e[1],v=[];for(let b=0;b<_.length;b+=1)v[b]=os(ss(e,_,b));let g=e[1].length>1&&as(e);return{c(){t=a("div"),n=a("input"),i=h(),l=a("button"),l.textContent="Register",o=h(),u=a("br"),d=h(),c=a("div");for(let b=0;b1?g?g.p(b,A):(g=as(b),g.c(),g.m(c,null)):g&&(g.d(1),g=null)},i:V,o:V,d(b){b&&p(t),b&&p(o),b&&p(u),b&&p(d),b&&p(c),ft(v,b),g&&g.d(),y=!1,ue(k)}}}function Ea(e,t,n){let i,{onMessage:l}=t;const o=ws([]);gs(e,o,_=>n(1,i=_));let u="CmdOrControl+X";function d(){const _=u;Ys(_,()=>{l(`Shortcut ${_} triggered`)}).then(()=>{o.update(v=>[...v,_]),l(`Shortcut ${_} registered successfully`)}).catch(l)}function c(_){const v=_;Ks(v).then(()=>{o.update(g=>g.filter(b=>b!==v)),l(`Shortcut ${v} unregistered`)}).catch(l)}function f(){$s().then(()=>{o.update(()=>[]),l("Unregistered all shortcuts")}).catch(l)}function y(){u=this.value,n(0,u)}const k=_=>c(_);return e.$$set=_=>{"onMessage"in _&&n(6,l=_.onMessage)},[u,i,o,d,c,f,l,y,k]}class za extends ye{constructor(t){super(),ge(this,t,Ea,La,he,{onMessage:6})}}function rs(e){let t,n,i,l,o,u,d;return{c(){t=a("br"),n=h(),i=a("input"),l=h(),o=a("button"),o.textContent="Write",r(i,"class","input"),r(i,"placeholder","write to stdin"),r(o,"class","btn")},m(c,f){m(c,t,f),m(c,n,f),m(c,i,f),q(i,e[4]),m(c,l,f),m(c,o,f),u||(d=[E(i,"input",e[14]),E(o,"click",e[8])],u=!0)},p(c,f){f&16&&i.value!==c[4]&&q(i,c[4])},d(c){c&&p(t),c&&p(n),c&&p(i),c&&p(l),c&&p(o),u=!1,ue(d)}}}function Da(e){let t,n,i,l,o,u,d,c,f,y,k,_,v,g,b,A,O,I,P,F,L,T,C,W,M=e[5]&&rs(e);return{c(){t=a("div"),n=a("div"),i=z(`Script: + `),l=a("input"),o=h(),u=a("div"),d=z(`Encoding: + `),c=a("input"),f=h(),y=a("div"),k=z(`Working directory: + `),_=a("input"),v=h(),g=a("div"),b=z(`Arguments: + `),A=a("input"),O=h(),I=a("div"),P=a("button"),P.textContent="Run",F=h(),L=a("button"),L.textContent="Kill",T=h(),M&&M.c(),r(l,"class","grow input"),r(n,"class","flex items-center gap-1"),r(c,"class","grow input"),r(u,"class","flex items-center gap-1"),r(_,"class","grow input"),r(_,"placeholder","Working directory"),r(y,"class","flex items-center gap-1"),r(A,"class","grow input"),r(A,"placeholder","Environment variables"),r(g,"class","flex items-center gap-1"),r(P,"class","btn"),r(L,"class","btn"),r(I,"class","flex children:grow gap-1"),r(t,"class","flex flex-col childre:grow gap-1")},m(j,U){m(j,t,U),s(t,n),s(n,i),s(n,l),q(l,e[0]),s(t,o),s(t,u),s(u,d),s(u,c),q(c,e[3]),s(t,f),s(t,y),s(y,k),s(y,_),q(_,e[1]),s(t,v),s(t,g),s(g,b),s(g,A),q(A,e[2]),s(t,O),s(t,I),s(I,P),s(I,F),s(I,L),s(t,T),M&&M.m(t,null),C||(W=[E(l,"input",e[10]),E(c,"input",e[11]),E(_,"input",e[12]),E(A,"input",e[13]),E(P,"click",e[6]),E(L,"click",e[7])],C=!0)},p(j,[U]){U&1&&l.value!==j[0]&&q(l,j[0]),U&8&&c.value!==j[3]&&q(c,j[3]),U&2&&_.value!==j[1]&&q(_,j[1]),U&4&&A.value!==j[2]&&q(A,j[2]),j[5]?M?M.p(j,U):(M=rs(j),M.c(),M.m(t,null)):M&&(M.d(1),M=null)},i:V,o:V,d(j){j&&p(t),M&&M.d(),C=!1,ue(W)}}}function Wa(e,t,n){const i=navigator.userAgent.includes("Windows");let l=i?"cmd":"sh",o=i?["/C"]:["-c"],{onMessage:u}=t,d='echo "hello world"',c=null,f="SOMETHING=value ANOTHER=2",y="",k="",_;function v(){return f.split(" ").reduce((T,C)=>{let[W,M]=C.split("=");return{...T,[W]:M}},{})}function g(){n(5,_=null);const T=new Oi(l,[...o,d],{cwd:c||null,env:v(),encoding:y});T.on("close",C=>{u(`command finished with code ${C.code} and signal ${C.signal}`),n(5,_=null)}),T.on("error",C=>u(`command error: "${C}"`)),T.stdout.on("data",C=>u(`command stdout: "${C}"`)),T.stderr.on("data",C=>u(`command stderr: "${C}"`)),T.spawn().then(C=>{n(5,_=C)}).catch(u)}function b(){_.kill().then(()=>u("killed child process")).catch(u)}function A(){_.write(k).catch(u)}function O(){d=this.value,n(0,d)}function I(){y=this.value,n(3,y)}function P(){c=this.value,n(1,c)}function F(){f=this.value,n(2,f)}function L(){k=this.value,n(4,k)}return e.$$set=T=>{"onMessage"in T&&n(9,u=T.onMessage)},[d,c,f,y,k,_,g,b,A,u,O,I,P,F,L]}class Oa extends ye{constructor(t){super(),ge(this,t,Wa,Da,he,{onMessage:9})}}var Pa={};we(Pa,{checkUpdate:()=>Zs,installUpdate:()=>Qs,onUpdaterEvent:()=>qi});async function qi(e){return Xt("tauri://update-status",t=>{e(t==null?void 0:t.payload)})}async function Qs(){let e;function t(){e&&e(),e=void 0}return new Promise((n,i)=>{function l(o){if(o.error)return t(),i(o.error);if(o.status==="DONE")return t(),n()}qi(l).then(o=>{e=o}).catch(o=>{throw t(),o}),ni("tauri://update-install").catch(o=>{throw t(),o})})}async function Zs(){let e;function t(){e&&e(),e=void 0}return new Promise((n,i)=>{function l(u){return t(),n({manifest:u,shouldUpdate:!0})}function o(u){if(u.error)return t(),i(u.error);if(u.status==="UPTODATE")return t(),n({shouldUpdate:!1})}Ls("tauri://update-available",u=>{l(u==null?void 0:u.payload)}).catch(u=>{throw t(),u}),qi(o).then(u=>{e=u}).catch(u=>{throw t(),u}),ni("tauri://update").catch(u=>{throw t(),u})})}function Ra(e){let t;return{c(){t=a("button"),t.innerHTML='
',r(t,"class","btn text-accentText dark:text-darkAccentText flex items-center justify-center")},m(n,i){m(n,t,i)},p:V,d(n){n&&p(t)}}}function Ia(e){let t,n,i;return{c(){t=a("button"),t.textContent="Install update",r(t,"class","btn")},m(l,o){m(l,t,o),n||(i=E(t,"click",e[4]),n=!0)},p:V,d(l){l&&p(t),n=!1,i()}}}function Ha(e){let t,n,i;return{c(){t=a("button"),t.textContent="Check update",r(t,"class","btn")},m(l,o){m(l,t,o),n||(i=E(t,"click",e[3]),n=!0)},p:V,d(l){l&&p(t),n=!1,i()}}}function Fa(e){let t;function n(o,u){return!o[0]&&!o[2]?Ha:!o[1]&&o[2]?Ia:Ra}let i=n(e),l=i(e);return{c(){t=a("div"),l.c(),r(t,"class","flex children:grow children:h10")},m(o,u){m(o,t,u),l.m(t,null)},p(o,[u]){i===(i=n(o))&&l?l.p(o,u):(l.d(1),l=i(o),l&&(l.c(),l.m(t,null)))},i:V,o:V,d(o){o&&p(t),l.d()}}}function Na(e,t,n){let{onMessage:i}=t,l;ut(async()=>{l=await Xt("tauri://update-status",i)}),Wi(()=>{l&&l()});let o,u,d;async function c(){n(0,o=!0);try{const{shouldUpdate:y,manifest:k}=await Zs();i(`Should update: ${y}`),i(k),n(2,d=y)}catch(y){i(y)}finally{n(0,o=!1)}}async function f(){n(1,u=!0);try{await Qs(),i("Installation complete, restart required."),await Fi()}catch(y){i(y)}finally{n(1,u=!1)}}return e.$$set=y=>{"onMessage"in y&&n(5,i=y.onMessage)},[o,u,d,c,f,i]}class ja extends ye{constructor(t){super(),ge(this,t,Na,Fa,he,{onMessage:5})}}var Ua={};we(Ua,{readText:()=>eo,writeText:()=>xs});async function xs(e){return S({__tauriModule:"Clipboard",message:{cmd:"writeText",data:e}})}async function eo(){return S({__tauriModule:"Clipboard",message:{cmd:"readText",data:null}})}function qa(e){let t,n,i,l,o,u,d,c;return{c(){t=a("div"),n=a("input"),i=h(),l=a("button"),l.textContent="Write",o=h(),u=a("button"),u.textContent="Read",r(n,"class","grow input"),r(n,"placeholder","Text to write to the clipboard"),r(l,"class","btn"),r(l,"type","button"),r(u,"class","btn"),r(u,"type","button"),r(t,"class","flex gap-1")},m(f,y){m(f,t,y),s(t,n),q(n,e[0]),s(t,i),s(t,l),s(t,o),s(t,u),d||(c=[E(n,"input",e[4]),E(l,"click",e[1]),E(u,"click",e[2])],d=!0)},p(f,[y]){y&1&&n.value!==f[0]&&q(n,f[0])},i:V,o:V,d(f){f&&p(t),d=!1,ue(c)}}}function Ba(e,t,n){let{onMessage:i}=t,l="clipboard message";function o(){xs(l).then(()=>{i("Wrote to the clipboard")}).catch(i)}function u(){eo().then(c=>{i(`Clipboard contents: ${c}`)}).catch(i)}function d(){l=this.value,n(0,l)}return e.$$set=c=>{"onMessage"in c&&n(3,i=c.onMessage)},[l,o,u,i,d]}class Va extends ye{constructor(t){super(),ge(this,t,Ba,qa,he,{onMessage:3})}}function Ga(e){let t;return{c(){t=a("div"),t.innerHTML=`
Not available for Linux
+ `,r(t,"class","flex flex-col gap-2")},m(n,i){m(n,t,i)},p:V,i:V,o:V,d(n){n&&p(t)}}}function Ja(e,t,n){let{onMessage:i}=t;const l=window.constraints={audio:!0,video:!0};function o(d){const c=document.querySelector("video"),f=d.getVideoTracks();i("Got stream with constraints:",l),i(`Using video device: ${f[0].label}`),window.stream=d,c.srcObject=d}function u(d){if(d.name==="ConstraintNotSatisfiedError"){const c=l.video;i(`The resolution ${c.width.exact}x${c.height.exact} px is not supported by your device.`)}else d.name==="PermissionDeniedError"&&i("Permissions have not been granted to use your camera and microphone, you need to allow the page access to your devices in order for the demo to work.");i(`getUserMedia error: ${d.name}`,d)}return ut(async()=>{try{const d=await navigator.mediaDevices.getUserMedia(l);o(d)}catch(d){u(d)}}),Wi(()=>{window.stream.getTracks().forEach(function(d){d.stop()})}),e.$$set=d=>{"onMessage"in d&&n(0,i=d.onMessage)},[i]}class Xa extends ye{constructor(t){super(),ge(this,t,Ja,Ga,he,{onMessage:0})}}function Ya(e){let t,n,i,l,o,u;return{c(){t=a("div"),n=a("button"),n.textContent="Show",i=h(),l=a("button"),l.textContent="Hide",r(n,"class","btn"),r(n,"id","show"),r(n,"title","Hides and shows the app after 2 seconds"),r(l,"class","btn"),r(l,"id","hide")},m(d,c){m(d,t,c),s(t,n),s(t,i),s(t,l),o||(u=[E(n,"click",e[0]),E(l,"click",e[1])],o=!0)},p:V,i:V,o:V,d(d){d&&p(t),o=!1,ue(u)}}}function Ka(e,t,n){let{onMessage:i}=t;function l(){o().then(()=>{setTimeout(()=>{Fs().then(()=>i("Shown app")).catch(i)},2e3)}).catch(i)}function o(){return Ns().then(()=>i("Hide app")).catch(i)}return e.$$set=u=>{"onMessage"in u&&n(2,i=u.onMessage)},[l,o,i]}class $a extends ye{constructor(t){super(),ge(this,t,Ka,Ya,he,{onMessage:2})}}function us(e,t,n){const i=e.slice();return i[32]=t[n],i}function cs(e,t,n){const i=e.slice();return i[35]=t[n],i}function ds(e){let t,n,i,l,o,u,d,c,f,y,k,_,v,g,b;function A(T,C){return T[3]?Za:Qa}let O=A(e),I=O(e);function P(T,C){return T[2]?er:xa}let F=P(e),L=F(e);return{c(){t=a("div"),n=a("span"),n.textContent="Tauri API Validation",i=h(),l=a("span"),o=a("span"),I.c(),d=h(),c=a("span"),c.innerHTML='
',f=h(),y=a("span"),L.c(),_=h(),v=a("span"),v.innerHTML='
',r(n,"class","lt-sm:pl-10 text-darkPrimaryText"),r(o,"title",u=e[3]?"Switch to Light mode":"Switch to Dark mode"),r(o,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),r(c,"title","Minimize"),r(c,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),r(y,"title",k=e[2]?"Restore":"Maximize"),r(y,"class","hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"),r(v,"title","Close"),r(v,"class","hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText "),r(l,"class","h-100% children:h-100% children:w-12 children:inline-flex children:items-center children:justify-center"),r(t,"class","w-screen select-none h-8 pl-2 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"),r(t,"data-tauri-drag-region","")},m(T,C){m(T,t,C),s(t,n),s(t,i),s(t,l),s(l,o),I.m(o,null),s(l,d),s(l,c),s(l,f),s(l,y),L.m(y,null),s(l,_),s(l,v),g||(b=[E(o,"click",e[12]),E(c,"click",e[9]),E(y,"click",e[10]),E(v,"click",e[11])],g=!0)},p(T,C){O!==(O=A(T))&&(I.d(1),I=O(T),I&&(I.c(),I.m(o,null))),C[0]&8&&u!==(u=T[3]?"Switch to Light mode":"Switch to Dark mode")&&r(o,"title",u),F!==(F=P(T))&&(L.d(1),L=F(T),L&&(L.c(),L.m(y,null))),C[0]&4&&k!==(k=T[2]?"Restore":"Maximize")&&r(y,"title",k)},d(T){T&&p(t),I.d(),L.d(),g=!1,ue(b)}}}function Qa(e){let t;return{c(){t=a("div"),r(t,"class","i-ph-moon")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function Za(e){let t;return{c(){t=a("div"),r(t,"class","i-ph-sun")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function xa(e){let t;return{c(){t=a("div"),r(t,"class","i-codicon-chrome-maximize")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function er(e){let t;return{c(){t=a("div"),r(t,"class","i-codicon-chrome-restore")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function tr(e){let t;return{c(){t=a("span"),r(t,"class","i-codicon-menu animate-duration-300ms animate-fade-in")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function nr(e){let t;return{c(){t=a("span"),r(t,"class","i-codicon-close animate-duration-300ms animate-fade-in")},m(n,i){m(n,t,i)},d(n){n&&p(t)}}}function fs(e){let t,n,i,l,o,u,d,c,f;function y(v,g){return v[3]?lr:ir}let k=y(e),_=k(e);return{c(){t=a("a"),_.c(),n=h(),i=a("br"),l=h(),o=a("div"),u=h(),d=a("br"),r(t,"href","##"),r(t,"class","nv justify-between h-8"),r(o,"class","bg-white/5 h-2px")},m(v,g){m(v,t,g),_.m(t,null),m(v,n,g),m(v,i,g),m(v,l,g),m(v,o,g),m(v,u,g),m(v,d,g),c||(f=E(t,"click",e[12]),c=!0)},p(v,g){k!==(k=y(v))&&(_.d(1),_=k(v),_&&(_.c(),_.m(t,null)))},d(v){v&&p(t),_.d(),v&&p(n),v&&p(i),v&&p(l),v&&p(o),v&&p(u),v&&p(d),c=!1,f()}}}function ir(e){let t,n;return{c(){t=z(`Switch to Dark mode + `),n=a("div"),r(n,"class","i-ph-moon")},m(i,l){m(i,t,l),m(i,n,l)},d(i){i&&p(t),i&&p(n)}}}function lr(e){let t,n;return{c(){t=z(`Switch to Light mode + `),n=a("div"),r(n,"class","i-ph-sun")},m(i,l){m(i,t,l),m(i,n,l)},d(i){i&&p(t),i&&p(n)}}}function sr(e){let t,n,i,l,o=e[35].label+"",u,d,c,f;function y(){return e[20](e[35])}return{c(){t=a("a"),n=a("div"),i=h(),l=a("p"),u=z(o),r(n,"class",e[35].icon+" mr-2"),r(t,"href","##"),r(t,"class",d="nv "+(e[1]===e[35]?"nv_selected":""))},m(k,_){m(k,t,_),s(t,n),s(t,i),s(t,l),s(l,u),c||(f=E(t,"click",y),c=!0)},p(k,_){e=k,_[0]&2&&d!==(d="nv "+(e[1]===e[35]?"nv_selected":""))&&r(t,"class",d)},d(k){k&&p(t),c=!1,f()}}}function ps(e){let t,n=e[35]&&sr(e);return{c(){n&&n.c(),t=xn()},m(i,l){n&&n.m(i,l),m(i,t,l)},p(i,l){i[35]&&n.p(i,l)},d(i){n&&n.d(i),i&&p(t)}}}function ms(e){let t,n=e[32].html+"",i;return{c(){t=new ao(!1),i=xn(),t.a=i},m(l,o){t.m(n,l,o),m(l,i,o)},p(l,o){o[0]&64&&n!==(n=l[32].html+"")&&t.p(n)},d(l){l&&p(i),l&&t.d()}}}function or(e){let t,n,i,l,o,u,d,c,f,y,k,_,v,g,b,A,O,I,P,F,L,T,C,W,M,j,U=e[1].label+"",J,ne,de,x,Y,fe,ee,H,K,te,pe,se,ae,_e,ce,ie,Te,Ce,D=e[5]&&ds(e);function B(N,le){return N[0]?nr:tr}let Ae=B(e),me=Ae(e),oe=!e[5]&&fs(e),Le=e[7],Q=[];for(let N=0;N`,k=h(),_=a("a"),_.innerHTML=`GitHub + `,v=h(),g=a("a"),g.innerHTML=`Source + `,b=h(),A=a("br"),O=h(),I=a("div"),P=h(),F=a("br"),L=h(),T=a("div");for(let N=0;N',_e=h(),ce=a("div");for(let N=0;N{Gt(G,1)}),ti()}Ee?(Y=new Ee(Ie(N)),Kn(Y.$$.fragment),Me(Y.$$.fragment,1),Vt(Y,x,null)):Y=null}if(le[0]&64){ze=N[6];let G;for(G=0;G{await confirm("Are you sure?")||H.preventDefault()}),Ue.onFileDropEvent(H=>{O(`File drop: ${JSON.stringify(H.payload)}`)});const l=navigator.userAgent.toLowerCase(),o=l.includes("android")||l.includes("iphone"),u=[{label:"Welcome",component:Po,icon:"i-ph-hand-waving"},{label:"Communication",component:Uo,icon:"i-codicon-radio-tower"},!o&&{label:"CLI",component:Fo,icon:"i-codicon-terminal"},!o&&{label:"Dialog",component:ia,icon:"i-codicon-multiple-windows"},{label:"File system",component:aa,icon:"i-codicon-files"},{label:"HTTP",component:ba,icon:"i-ph-globe-hemisphere-west"},!o&&{label:"Notifications",component:wa,icon:"i-codicon-bell-dot"},!o&&{label:"App",component:$a,icon:"i-codicon-hubot"},!o&&{label:"Window",component:Ta,icon:"i-codicon-window"},!o&&{label:"Shortcuts",component:za,icon:"i-codicon-record-keys"},{label:"Shell",component:Oa,icon:"i-codicon-terminal-bash"},!o&&{label:"Updater",component:ja,icon:"i-codicon-cloud-download"},!o&&{label:"Clipboard",component:Va,icon:"i-codicon-clippy"},{label:"WebRTC",component:Xa,icon:"i-ph-broadcast"}];let d=u[0];function c(H){n(1,d=H)}let f;ut(async()=>{const H=Ut();n(2,f=await H.isMaximized()),Xt("tauri://resize",async()=>{n(2,f=await H.isMaximized())})});function y(){Ut().minimize()}async function k(){const H=Ut();await H.isMaximized()?H.unmaximize():H.maximize()}let _=!1;async function v(){_||(_=await Bs("Are you sure that you want to close this window?",{title:"Tauri API"}),_&&Ut().close())}let g;ut(()=>{n(3,g=localStorage&&localStorage.getItem("theme")=="dark"),_s(g)});function b(){n(3,g=!g),_s(g)}let A=ws([]);gs(e,A,H=>n(6,i=H));function O(H){A.update(K=>[{html:`
[${new Date().toLocaleTimeString()}]: `+(typeof H=="string"?H:JSON.stringify(H,null,1))+"
"},...K])}function I(H){A.update(K=>[{html:`
[${new Date().toLocaleTimeString()}]: `+H+"
"},...K])}function P(){A.update(()=>[])}let F,L,T;function C(H){T=H.clientY;const K=window.getComputedStyle(F);L=parseInt(K.height,10);const te=se=>{const ae=se.clientY-T,_e=L-ae;n(4,F.style.height=`${_e{document.removeEventListener("mouseup",pe),document.removeEventListener("mousemove",te)};document.addEventListener("mouseup",pe),document.addEventListener("mousemove",te)}let W;ut(async()=>{n(5,W=await Ps()==="win32")});let M=!1,j,U,J=!1,ne=0,de=0;const x=(H,K,te)=>Math.min(Math.max(K,H),te);ut(()=>{n(18,j=document.querySelector("#sidebar")),U=document.querySelector("#sidebarToggle"),document.addEventListener("click",H=>{U.contains(H.target)?n(0,M=!M):M&&!j.contains(H.target)&&n(0,M=!1)}),document.addEventListener("touchstart",H=>{if(U.contains(H.target))return;const K=H.touches[0].clientX;(0{if(J){const K=H.touches[0].clientX;de=K;const te=(K-ne)/10;j.style.setProperty("--translate-x",`-${x(0,M?0-te:18.75-te,18.75)}rem`)}}),document.addEventListener("touchend",()=>{if(J){const H=(de-ne)/10;n(0,M=M?H>-(18.75/2):H>18.75/2)}J=!1})});const Y=()=>Pi("https://tauri.app/"),fe=H=>{c(H),n(0,M=!1)};function ee(H){Yn[H?"unshift":"push"](()=>{F=H,n(4,F)})}return e.$$.update=()=>{if(e.$$.dirty[0]&1){const H=document.querySelector("#sidebar");H&&ar(H,M)}},[M,d,f,g,F,W,i,u,c,y,k,v,b,A,O,I,P,C,j,Y,fe,ee]}class ur extends ye{constructor(t){super(),ge(this,t,rr,or,he,{},null,[-1,-1])}}new ur({target:document.querySelector("#app")}); diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 47038bde574..593aa8b2e51 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -45,9 +45,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "api" @@ -103,8 +103,6 @@ dependencies = [ "tauri-plugin-log", "tauri-plugin-sample", "tiny_http", - "window-shadows", - "window-vibrancy", ] [[package]] @@ -139,9 +137,9 @@ dependencies = [ [[package]] name = "attohttpc" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b85f766c20e6ae766956f7a2fcc4e0931e79a7e1f48b29132b5d647021114914" +checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" dependencies = [ "flate2", "http", @@ -160,7 +158,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -177,6 +175,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "bitflags" version = "1.3.2" @@ -211,9 +215,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.2" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -221,18 +225,19 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" dependencies = [ "memchr", + "serde", ] [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-unit" @@ -246,9 +251,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.12.3" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" [[package]] name = "byteorder" @@ -258,9 +263,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" dependencies = [ "serde", ] @@ -292,9 +297,9 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.13.0" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa0e3586af56b3bfa51fca452bd56e8dbbbd5d8d81cbf0b7e4e35b695b537eb8" +checksum = "497049e9477329f8f6a559972ee42e117487d01d1e8c2cc9f836ea6fa23a9e1a" dependencies = [ "serde", "toml", @@ -302,9 +307,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.74" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cesu8" @@ -330,7 +335,7 @@ checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" dependencies = [ "byteorder", "fnv", - "uuid 1.2.1", + "uuid 1.3.0", ] [[package]] @@ -350,9 +355,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chunked_transfer" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" +checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" [[package]] name = "cipher" @@ -511,9 +516,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] @@ -630,9 +635,9 @@ dependencies = [ [[package]] name = "dbus" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" dependencies = [ "libc", "libdbus-sys", @@ -654,9 +659,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", @@ -718,9 +723,9 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] @@ -765,9 +770,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" dependencies = [ "cfg-if", "libc", @@ -777,12 +782,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", - "miniz_oxide 0.5.4", + "miniz_oxide", ] [[package]] @@ -827,24 +832,24 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -853,15 +858,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -870,25 +875,28 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -962,6 +970,20 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gdkwayland-sys" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4511710212ed3020b61a8622a37aa6f0dd2a84516575da92e9b96928dcbe83ba" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + [[package]] name = "gdkx11-sys" version = "0.16.0" @@ -977,15 +999,15 @@ dependencies = [ [[package]] name = "generator" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc184cace1cea8335047a471cc1da80f18acf8a76f3bab2028d499e328948ec7" +checksum = "d266041a359dfa931b370ef684cceb84b166beb14f7f0421f4a6a3d0c446d12e" dependencies = [ "cc", "libc", "log", "rustversion", - "windows 0.32.0", + "windows 0.39.0", ] [[package]] @@ -1092,7 +1114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf" dependencies = [ "anyhow", - "heck 0.4.0", + "heck 0.4.1", "proc-macro-crate", "proc-macro-error", "proc-macro2", @@ -1112,15 +1134,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", "bstr", @@ -1231,9 +1253,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -1244,6 +1266,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "html5ever" version = "0.25.2" @@ -1266,7 +1297,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.4", + "itoa 1.0.5", ] [[package]] @@ -1300,9 +1331,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -1313,7 +1344,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.4", + "itoa 1.0.5", "pin-project-lite", "socket2", "tokio", @@ -1368,9 +1399,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.4" +version = "0.24.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd8e4fb07cf672b1642304e731ef8a6a4c7891d67bb4fd4f5ce58cd6ed86803c" +checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" dependencies = [ "bytemuck", "byteorder", @@ -1381,9 +1412,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -1418,9 +1449,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.1" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "itoa" @@ -1430,9 +1461,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "javascriptcore-rs" @@ -1479,18 +1510,18 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] [[package]] name = "json-patch" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f995a3c8f2bc3dd52a18a583e90f9ec109c047fa1603a853e46bcda14d2e279d" +checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" dependencies = [ "serde", "serde_json", @@ -1541,15 +1572,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.137" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libdbus-sys" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c185b5b7ad900923ef3a8ff594083d4d9b5aea80bb4f32b8342363138c0d456b" +checksum = "9f8d7ae751e1cb825c840ae5e682f59b098cdfd213c350ac268b61449a5f58a0" dependencies = [ "pkg-config", ] @@ -1661,9 +1692,9 @@ dependencies = [ [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" @@ -1702,15 +1733,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881" -[[package]] -name = "miniz_oxide" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.6.2" @@ -1785,11 +1807,20 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "notify-rust" -version = "4.5.10" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368e89ea58df747ce88be669ae44e79783c1d30bfd540ad0fc520b3f41f0b3b0" +checksum = "3ce656bb6d22a93ae276a23de52d1aec5ba4db3ece3c0eb79dfd5add7384db6a" dependencies = [ "dbus", "mac-notification-sys", @@ -1838,28 +1869,28 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1908,9 +1939,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -1920,19 +1951,19 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "open" -version = "3.0.3" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a3100141f1733ea40b53381b0ae3117330735ef22309a190ac57b9576ea716" +checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" dependencies = [ "pathdiff", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "os_info" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4750134fb6a5d49afc80777394ad5d95b04bc12068c6abb92fae8f43817270f" +checksum = "5c424bc68d15e0778838ac013b5b3449544d8133633d8016319e7e05a820b8c0" dependencies = [ "log", "serde", @@ -1941,19 +1972,19 @@ dependencies = [ [[package]] name = "os_pipe" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dceb7e43f59c35ee1548045b2c72945a5a3bb6ce6d6f07cdc13dc8f6bc4930a" +checksum = "a53dbb20faf34b16087a931834cba2d7a73cc74af2b7ef345a4c8324e2409a12" dependencies = [ "libc", - "winapi", + "windows-sys 0.45.0", ] [[package]] name = "os_str_bytes" -version = "6.3.1" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "oslog" @@ -2010,22 +2041,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "paste" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "pathdiff" @@ -2041,9 +2072,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.4.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" +checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" dependencies = [ "thiserror", "ucd-trie", @@ -2167,16 +2198,16 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "plist" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" +checksum = "5329b8f106a176ab0dce4aae5da86bfcb139bb74fb00882859e03745011f3635" dependencies = [ - "base64", + "base64 0.13.1", "indexmap", "line-wrap", + "quick-xml 0.26.0", "serde", "time", - "xml-rs", ] [[package]] @@ -2188,7 +2219,7 @@ dependencies = [ "bitflags", "crc32fast", "flate2", - "miniz_oxide 0.6.2", + "miniz_oxide", ] [[package]] @@ -2217,13 +2248,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -2252,15 +2282,15 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.19" +version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -2274,11 +2304,20 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -2395,9 +2434,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -2430,11 +2469,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ - "base64", + "base64 0.21.0", "bytes", "encoding_rs", "futures-core", @@ -2460,6 +2499,7 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "winreg", ] @@ -2502,20 +2542,20 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.14", + "semver 1.0.16", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "safemem" @@ -2575,9 +2615,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" dependencies = [ "serde", ] @@ -2593,18 +2633,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -2613,20 +2653,20 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" dependencies = [ - "itoa 1.0.4", + "itoa 1.0.5", "ryu", "serde", ] [[package]] name = "serde_repr" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" dependencies = [ "proc-macro2", "quote", @@ -2640,7 +2680,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.4", + "itoa 1.0.5", "ryu", "serde", ] @@ -2864,9 +2904,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.103" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -2880,7 +2920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" dependencies = [ "cfg-expr", - "heck 0.4.0", + "heck 0.4.1", "pkg-config", "toml", "version-compare", @@ -2889,8 +2929,7 @@ dependencies = [ [[package]] name = "tao" version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d021218bcb43d4bf42112b1da5f7d8454502ffb188489ba0089fe4e3e34a8f6" +source = "git+https://github.com/tauri-apps/tao?branch=dev#8971d731b02ec61a1665351c9bae11f5e4058dc4" dependencies = [ "bitflags", "cairo-rs", @@ -2904,6 +2943,7 @@ dependencies = [ "gdk", "gdk-pixbuf", "gdk-sys", + "gdkwayland-sys", "gdkx11-sys", "gio", "glib", @@ -2928,8 +2968,8 @@ dependencies = [ "serde", "tao-macros", "unicode-segmentation", - "uuid 1.2.1", - "windows 0.39.0", + "uuid 1.3.0", + "windows 0.44.0", "windows-implement", "x11-dl", ] @@ -2937,8 +2977,7 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b6fcd8245d45a39ffc8715183d92ae242750eb57b285eb3bcd63dfd512afd09" +source = "git+https://github.com/tauri-apps/tao?branch=dev#8971d731b02ec61a1665351c9bae11f5e4058dc4" dependencies = [ "proc-macro2", "quote", @@ -2962,7 +3001,7 @@ version = "2.0.0-alpha.3" dependencies = [ "anyhow", "attohttpc", - "base64", + "base64 0.13.1", "bytes", "clap", "cocoa", @@ -2974,7 +3013,7 @@ dependencies = [ "glib", "glob", "gtk", - "heck 0.4.0", + "heck 0.4.1", "http", "ico", "ignore", @@ -2997,7 +3036,7 @@ dependencies = [ "regex", "reqwest", "rfd", - "semver 1.0.14", + "semver 1.0.16", "serde", "serde_json", "serde_repr", @@ -3014,11 +3053,11 @@ dependencies = [ "time", "tokio", "url", - "uuid 1.2.1", + "uuid 1.3.0", "webkit2gtk", "webview2-com", "win7-notifications", - "windows 0.39.0", + "windows 0.44.0", "zip", ] @@ -3028,10 +3067,10 @@ version = "2.0.0-alpha.1" dependencies = [ "anyhow", "cargo_toml", - "heck 0.4.0", + "heck 0.4.1", "json-patch", "quote", - "semver 1.0.14", + "semver 1.0.16", "serde_json", "tauri-codegen", "tauri-utils", @@ -3043,7 +3082,7 @@ dependencies = [ name = "tauri-codegen" version = "2.0.0-alpha.1" dependencies = [ - "base64", + "base64 0.13.1", "brotli", "ico", "json-patch", @@ -3052,7 +3091,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "semver 1.0.14", + "semver 1.0.16", "serde", "serde_json", "sha2", @@ -3060,7 +3099,7 @@ dependencies = [ "thiserror", "time", "url", - "uuid 1.2.1", + "uuid 1.3.0", "walkdir", ] @@ -3068,7 +3107,7 @@ dependencies = [ name = "tauri-macros" version = "2.0.0-alpha.1" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "proc-macro2", "quote", "syn", @@ -3079,7 +3118,7 @@ dependencies = [ [[package]] name = "tauri-plugin-log" version = "0.1.0" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=next#37e0511020bd7d06cd29a293779efb1e2b96ba38" +source = "git+https://github.com/tauri-apps/plugins-workspace?branch=next#e151ffcf61cf2445d4a9e6cac3e44a3ff24d77d4" dependencies = [ "android_logger", "byte-unit", @@ -3115,9 +3154,9 @@ dependencies = [ "serde_json", "tauri-utils", "thiserror", - "uuid 1.2.1", + "uuid 1.3.0", "webview2-com", - "windows 0.39.0", + "windows 0.44.0", ] [[package]] @@ -3132,10 +3171,10 @@ dependencies = [ "raw-window-handle", "tauri-runtime", "tauri-utils", - "uuid 1.2.1", + "uuid 1.3.0", "webkit2gtk", "webview2-com", - "windows 0.39.0", + "windows 0.44.0", "wry", ] @@ -3148,7 +3187,7 @@ dependencies = [ "ctor", "getrandom 0.2.8", "glob", - "heck 0.4.0", + "heck 0.4.1", "html5ever", "infer 0.7.0", "json-patch", @@ -3157,7 +3196,7 @@ dependencies = [ "phf 0.10.1", "proc-macro2", "quote", - "semver 1.0.14", + "semver 1.0.16", "serde", "serde_json", "serde_with", @@ -3165,7 +3204,7 @@ dependencies = [ "thiserror", "url", "walkdir", - "windows 0.39.0", + "windows 0.44.0", ] [[package]] @@ -3174,7 +3213,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c58de036c4d2e20717024de2a3c4bf56c301f07b21bc8ef9b57189fce06f1f3b" dependencies = [ - "quick-xml", + "quick-xml 0.23.1", "strum", "windows 0.39.0", ] @@ -3206,9 +3245,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -3227,18 +3266,18 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -3260,7 +3299,7 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ - "itoa 1.0.4", + "itoa 1.0.5", "serde", "time-core", "time-macros", @@ -3305,15 +3344,15 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.21.2" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg", "bytes", @@ -3323,7 +3362,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "socket2", - "winapi", + "windows-sys 0.42.0", ] [[package]] @@ -3342,13 +3381,30 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -3428,15 +3484,15 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" @@ -3455,15 +3511,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" @@ -3476,9 +3532,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "universal-hash" @@ -3522,9 +3578,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" dependencies = [ "getrandom 0.2.8", ] @@ -3547,9 +3603,9 @@ dependencies = [ [[package]] name = "version-compare" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" [[package]] name = "version_check" @@ -3592,9 +3648,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3602,9 +3658,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -3617,9 +3673,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -3629,9 +3685,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3639,9 +3695,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -3652,15 +3708,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasm-streams" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -3712,13 +3781,13 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.19.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" +checksum = "03411e89ec447e29c08b3c086edeb88c5f8fd782cbdd4d6d316bea439be7a244" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.39.0", + "windows 0.44.0", "windows-implement", ] @@ -3735,15 +3804,15 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.19.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" +checksum = "1c0f5ce43e9611c5b2983a33156d6abe31abf39185bad84a6766c80ba1dbf1ab" dependencies = [ "regex", "serde", "serde_json", "thiserror", - "windows 0.39.0", + "windows 0.44.0", "windows-bindgen", "windows-metadata", ] @@ -3789,50 +3858,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "window-shadows" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c69eb48aac2da0199ea148c6f2c2610db8a0572b32a3dc857dec40ca22f1cec" -dependencies = [ - "cocoa", - "objc", - "raw-window-handle", - "windows-sys 0.36.1", -] - -[[package]] -name = "window-vibrancy" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe43438c9c09f934c647db9a396fc17b047b011bc7a006f0d0712f7cdf516ffa" -dependencies = [ - "cocoa", - "objc", - "raw-window-handle", - "windows-sys 0.36.1", -] - -[[package]] -name = "windows" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec" -dependencies = [ - "windows_aarch64_msvc 0.32.0", - "windows_i686_gnu 0.32.0", - "windows_i686_msvc 0.32.0", - "windows_x86_64_gnu 0.32.0", - "windows_x86_64_msvc 0.32.0", -] - [[package]] name = "windows" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" dependencies = [ - "windows-implement", "windows_aarch64_msvc 0.39.0", "windows_i686_gnu 0.39.0", "windows_i686_msvc 0.39.0", @@ -3846,14 +3877,16 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" dependencies = [ + "windows-implement", + "windows-interface", "windows-targets", ] [[package]] name = "windows-bindgen" -version = "0.39.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" +checksum = "222204ecf46521382a4d88b4a1bbefca9f8855697b4ab7d20803901425e061a3" dependencies = [ "windows-metadata", "windows-tokens", @@ -3861,19 +3894,31 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.39.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" +checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6" dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f" +dependencies = [ + "proc-macro2", + "quote", "syn", - "windows-tokens", ] [[package]] name = "windows-metadata" -version = "0.39.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" +checksum = "ee78911e3f4ce32c1ad9d3c7b0bd95389662ad8d8f1a3155688fed70bd96e2b6" [[package]] name = "windows-sys" @@ -3903,6 +3948,15 @@ dependencies = [ "windows_x86_64_msvc 0.42.1", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.42.1" @@ -3920,9 +3974,9 @@ dependencies = [ [[package]] name = "windows-tokens" -version = "0.39.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" +checksum = "fa4251900975a0d10841c5d4bde79c56681543367ef811f3fabb8d1803b0959b" [[package]] name = "windows_aarch64_gnullvm" @@ -3930,12 +3984,6 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" -[[package]] -name = "windows_aarch64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" - [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -3954,12 +4002,6 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" -[[package]] -name = "windows_i686_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" - [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -3978,12 +4020,6 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" -[[package]] -name = "windows_i686_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" - [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -4002,12 +4038,6 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" -[[package]] -name = "windows_x86_64_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" - [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -4032,12 +4062,6 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" -[[package]] -name = "windows_x86_64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" - [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -4077,9 +4101,9 @@ dependencies = [ [[package]] name = "wry" version = "0.26.0" -source = "git+https://github.com/tauri-apps/wry?branch=dev#a9e186cab4456d7ac2c265e61e71b345f7d269c4" +source = "git+https://github.com/tauri-apps/wry?branch=dev#9613a08312645bbad80de6b91c5ba33656cf6a5e" dependencies = [ - "base64", + "base64 0.13.1", "block", "cocoa", "core-graphics", @@ -4107,15 +4131,15 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.39.0", + "windows 0.44.0", "windows-implement", ] [[package]] name = "x11" -version = "2.20.0" +version = "2.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ae97874a928d821b061fce3d1fc52f08071dd53c89a6102bc06efcac3b2908" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" dependencies = [ "libc", "pkg-config", @@ -4123,12 +4147,12 @@ dependencies = [ [[package]] name = "x11-dl" -version = "2.20.0" +version = "2.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c83627bc137605acc00bb399c7b908ef460b621fc37c953db2b09f88c449ea6" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" dependencies = [ - "lazy_static", "libc", + "once_cell", "pkg-config", ] @@ -4141,17 +4165,11 @@ dependencies = [ "libc", ] -[[package]] -name = "xml-rs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" - [[package]] name = "zip" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080" +checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" dependencies = [ "byteorder", "crc32fast", diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 768b4e1433e..5d503e8b3b1 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -40,10 +40,6 @@ features = [ "updater" ] -[target."cfg(target_os = \"windows\")".dependencies] -window-vibrancy = "0.2" -window-shadows= "0.2" - [features] default = [ "custom-protocol" ] custom-protocol = [ "tauri/custom-protocol" ] diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index 173b37cf0f3..2a3391e1ec8 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -83,18 +83,14 @@ impl AppBuilder { #[cfg(target_os = "windows")] { - window_builder = window_builder.transparent(true); - window_builder = window_builder.decorations(false); + window_builder = window_builder + .transparent(true) + .shadow(true) + .decorations(false); } let window = window_builder.build().unwrap(); - #[cfg(target_os = "windows")] - { - let _ = window_shadows::set_shadow(&window, true); - let _ = window_vibrancy::apply_blur(&window, Some((0, 0, 0, 0))); - } - #[cfg(debug_assertions)] window.open_devtools(); diff --git a/examples/api/src/App.svelte b/examples/api/src/App.svelte index 423833fc27f..ae4298a2a07 100644 --- a/examples/api/src/App.svelte +++ b/examples/api/src/App.svelte @@ -368,9 +368,7 @@