From 57f9d56210454c18865dfa09c8ae6466f1c1e262 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 22 Oct 2025 14:52:02 -0300 Subject: [PATCH 01/33] feat(android): prepare for multi-window support --- Cargo.lock | 29 +++++---- Cargo.toml | 2 + crates/tauri-runtime/src/webview.rs | 4 +- .../mobile/android-codegen/TauriActivity.kt | 48 +++++++++------ .../src/main/java/app/tauri/plugin/Plugin.kt | 24 ++++++++ .../java/app/tauri/plugin/PluginManager.kt | 61 ++++++++++--------- crates/tauri/src/lib.rs | 9 +-- 7 files changed, 103 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15b6fe5b1e34..150410d75ca5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4733,12 +4733,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[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.6.0+11769913" @@ -8412,8 +8406,6 @@ dependencies = [ [[package]] name = "tao" version = "0.34.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7" dependencies = [ "bitflags 2.7.0", "block2 0.6.0", @@ -8431,7 +8423,6 @@ dependencies = [ "libc", "log", "ndk", - "ndk-context", "ndk-sys", "objc2 0.6.0", "objc2-app-kit", @@ -8440,7 +8431,7 @@ dependencies = [ "parking_lot", "raw-window-handle", "scopeguard", - "tao-macros", + "tao-macros 0.1.3", "unicode-segmentation", "url", "windows 0.61.1", @@ -8449,6 +8440,15 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "tao-macros" +version = "0.1.3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + [[package]] name = "tao-macros" version = "0.1.3" @@ -8819,9 +8819,9 @@ dependencies = [ [[package]] name = "tauri-plugin-log" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a59139183e0907cec1499dddee4e085f5a801dc659efa0848ee224f461371426" +checksum = "61c1438bc7662acd16d508c919b3c087efd63669a4c75625dff829b1c75975ec" dependencies = [ "android_logger", "byte-unit", @@ -10947,8 +10947,6 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" version = "0.53.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d78ec082b80fa088569a970d043bb3050abaabf4454101d44514ee8d9a8c9f6" dependencies = [ "base64 0.22.1", "block2 0.6.0", @@ -10964,6 +10962,7 @@ dependencies = [ "javascriptcore-rs", "jni 0.21.1", "kuchikiki", + "lazy_static", "libc", "ndk", "objc2 0.6.0", @@ -10977,7 +10976,7 @@ dependencies = [ "raw-window-handle", "sha2", "soup3", - "tao-macros", + "tao-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.12", "tracing", "url", diff --git a/Cargo.toml b/Cargo.toml index 670a647672c2..562d662718ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,3 +72,5 @@ schemars_derive = { git = 'https://github.com/tauri-apps/schemars.git', branch = tauri = { path = "./crates/tauri" } tauri-plugin = { path = "./crates/tauri-plugin" } tauri-utils = { path = "./crates/tauri-utils" } +wry = { path = "../wry" } +tao = { path = "../tao" } diff --git a/crates/tauri-runtime/src/webview.rs b/crates/tauri-runtime/src/webview.rs index f9c904ef9643..d23451d0d78b 100644 --- a/crates/tauri-runtime/src/webview.rs +++ b/crates/tauri-runtime/src/webview.rs @@ -217,7 +217,7 @@ pub struct PendingWebview> { #[cfg(target_os = "android")] #[allow(clippy::type_complexity)] pub on_webview_created: - Option) -> Result<(), jni::errors::Error> + Send>>, + Option) -> Result<(), jni::errors::Error> + Send + Sync>>, pub web_resource_request_handler: Option>, @@ -273,7 +273,7 @@ impl> PendingWebview { #[cfg(target_os = "android")] pub fn on_webview_created< - F: Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send + 'static, + F: Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send + Sync + 'static, >( mut self, f: F, diff --git a/crates/tauri/mobile/android-codegen/TauriActivity.kt b/crates/tauri/mobile/android-codegen/TauriActivity.kt index 1c96394b89a7..d4c2cbaad95f 100644 --- a/crates/tauri/mobile/android-codegen/TauriActivity.kt +++ b/crates/tauri/mobile/android-codegen/TauriActivity.kt @@ -9,43 +9,51 @@ package {{package}} import android.content.Intent import android.content.res.Configuration import app.tauri.plugin.PluginManager +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ProcessLifecycleOwner + +object TauriLifecycleObserver : DefaultLifecycleObserver { + override fun onResume(owner: LifecycleOwner) { + super.onResume(owner) + PluginManager.onResume() + } + + override fun onPause(owner: LifecycleOwner) { + super.onPause(owner) + PluginManager.onPause() + } + + override fun onStop(owner: LifecycleOwner) { + super.onStop(owner) + PluginManager.onStop() + } +} abstract class TauriActivity : WryActivity() { - var pluginManager: PluginManager = PluginManager(this) override val handleBackNavigation: Boolean = false - override fun onNewIntent(intent: Intent) { - super.onNewIntent(intent) - pluginManager.onNewIntent(intent) - } - - override fun onResume() { - super.onResume() - pluginManager.onResume() + fun getPluginManager(): PluginManager { + return PluginManager } - override fun onPause() { - super.onPause() - pluginManager.onPause() + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + PluginManager.onNewIntent(intent) } override fun onRestart() { super.onRestart() - pluginManager.onRestart() - } - - override fun onStop() { - super.onStop() - pluginManager.onStop() + PluginManager.onRestart(this) } override fun onDestroy() { super.onDestroy() - pluginManager.onDestroy() + PluginManager.onDestroy(this) } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) - pluginManager.onConfigurationChanged(newConfig) + PluginManager.onConfigurationChanged(newConfig) } } diff --git a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt index d33fa8a4d15a..fda20dd03f07 100644 --- a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt +++ b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt @@ -11,6 +11,7 @@ import android.content.pm.PackageManager import android.net.Uri import android.webkit.WebView import androidx.activity.result.IntentSenderRequest +import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import app.tauri.FsUtils import app.tauri.Logger @@ -71,10 +72,18 @@ abstract class Plugin(private val activity: Activity) { */ open fun onResume() {} + + /** + * This event is called after onStop() when the current activity is being re-displayed to the user (the user has navigated back to it). + * It will be followed by onStart() and then onResume(). + */ + open fun onRestart(activity: AppCompatActivity) {} + /** * This event is called after onStop() when the current activity is being re-displayed to the user (the user has navigated back to it). * It will be followed by onStart() and then onResume(). */ + @Deprecated("use onRestart(activity: AppCompatActivity) instead") open fun onRestart() {} /** @@ -86,8 +95,23 @@ abstract class Plugin(private val activity: Activity) { /** * This event is called before the activity is destroyed. */ + open fun onDestroy(activity: AppCompatActivity) {} + /** + * This event is called before an activity is destroyed. + */ + @Deprecated("use onDestroy(activity: AppCompatActivity) instead") open fun onDestroy() {} + internal fun triggerOnDestroy(activity: AppCompatActivity) { + onDestroy(activity) + onDestroy() + } + + internal fun triggerOnRestart(activity: AppCompatActivity) { + onRestart(activity) + onRestart() + } + /** * This event is called when a configuration change occurs but the app does not recreate the activity. */ diff --git a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt index 362896b706d0..0c64bb5f3411 100644 --- a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt +++ b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt @@ -4,7 +4,6 @@ package app.tauri.plugin -import android.app.PendingIntent import android.content.res.Configuration import android.content.Context import android.content.Intent @@ -26,7 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.module.SimpleModule import java.lang.reflect.InvocationTargetException -class PluginManager(val activity: AppCompatActivity) { +object PluginManager { fun interface RequestPermissionsCallback { fun onResult(permissions: Map) } @@ -35,16 +34,33 @@ class PluginManager(val activity: AppCompatActivity) { fun onResult(result: ActivityResult) } + lateinit var activity: AppCompatActivity private val plugins: HashMap = HashMap() - private val startActivityForResultLauncher: ActivityResultLauncher - private val startIntentSenderForResultLauncher: ActivityResultLauncher - private val requestPermissionsLauncher: ActivityResultLauncher> + private lateinit var startActivityForResultLauncher: ActivityResultLauncher + private lateinit var startIntentSenderForResultLauncher: ActivityResultLauncher + private lateinit var requestPermissionsLauncher: ActivityResultLauncher> private var requestPermissionsCallback: RequestPermissionsCallback? = null private var startActivityForResultCallback: ActivityResultCallback? = null private var startIntentSenderForResultCallback: ActivityResultCallback? = null - private var jsonMapper: ObjectMapper + private var jsonMapper: ObjectMapper = ObjectMapper() + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES) + .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) init { + val channelDeserializer = ChannelDeserializer({ channelId, payload -> + sendChannelData(channelId, payload) + }, jsonMapper) + jsonMapper + .registerModule(SimpleModule().addDeserializer(Channel::class.java, channelDeserializer)) + } + + fun onActivityCreate(activity: AppCompatActivity) { + // TODO: on destroy, we should change to a different activity + if (::activity.isInitialized) { + return + } + this.activity = activity startActivityForResultLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult() ) { result -> @@ -68,17 +84,6 @@ class PluginManager(val activity: AppCompatActivity) { requestPermissionsCallback!!.onResult(result) } } - - jsonMapper = ObjectMapper() - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - .enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES) - .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) - - val channelDeserializer = ChannelDeserializer({ channelId, payload -> - sendChannelData(channelId, payload) - }, jsonMapper) - jsonMapper - .registerModule(SimpleModule().addDeserializer(Channel::class.java, channelDeserializer)) } fun onNewIntent(intent: Intent) { @@ -99,9 +104,9 @@ class PluginManager(val activity: AppCompatActivity) { } } - fun onRestart() { + fun onRestart(activity: AppCompatActivity) { for (plugin in plugins.values) { - plugin.instance.onRestart() + plugin.instance.triggerOnRestart(activity) } } @@ -111,9 +116,9 @@ class PluginManager(val activity: AppCompatActivity) { } } - fun onDestroy() { + fun onDestroy(activity: AppCompatActivity) { for (plugin in plugins.values) { - plugin.instance.onDestroy() + plugin.instance.triggerOnDestroy(activity) } } @@ -201,14 +206,12 @@ class PluginManager(val activity: AppCompatActivity) { } } - companion object { - fun loadConfig(context: Context, plugin: String, cls: Class): T { - val tauriConfigJson = FsUtils.readAsset(context.assets, "tauri.conf.json") - val mapper = ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - val config = mapper.readValue(tauriConfigJson, Config::class.java) - return mapper.readValue(config.plugins[plugin].toString(), cls) - } + fun loadConfig(context: Context, plugin: String, cls: Class): T { + val tauriConfigJson = FsUtils.readAsset(context.assets, "tauri.conf.json") + val mapper = ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + val config = mapper.readValue(tauriConfigJson, Config::class.java) + return mapper.readValue(config.plugins[plugin].toString(), cls) } private external fun handlePluginResponse(id: Int, success: String?, error: String?) diff --git a/crates/tauri/src/lib.rs b/crates/tauri/src/lib.rs index 6f326cf1f82c..392e9272b8b8 100644 --- a/crates/tauri/src/lib.rs +++ b/crates/tauri/src/lib.rs @@ -137,14 +137,7 @@ macro_rules! android_binding { ::tauri::wry::android_binding!($domain, $app_name, $wry); - ::tauri::tao::android_binding!( - $domain, - $app_name, - WryActivity, - android_setup, - $main, - ::tauri::tao - ); + ::tauri::tao::android_binding!($domain, $app_name, Rust, android_setup, $main, ::tauri::tao); // be careful when renaming this, the `Java_app_tauri_plugin_PluginManager_handlePluginResponse` symbol is checked by the CLI ::tauri::tao::platform::android::prelude::android_fn!( From a71c7078b857046eb0fac46e77cc53ac4e54404c Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 23 Oct 2025 15:28:11 -0300 Subject: [PATCH 02/33] add android multiwindow apis --- crates/tauri-runtime-wry/src/lib.rs | 15 +++++++++++++++ crates/tauri-runtime/src/window.rs | 10 ++++++++++ crates/tauri/src/test/mock_runtime.rs | 10 ++++++++++ crates/tauri/src/webview/webview_window.rs | 18 ++++++++++++++++++ crates/tauri/src/window/mod.rs | 18 ++++++++++++++++++ examples/api/src-tauri/src/lib.rs | 12 ++++++++++++ 6 files changed, 83 insertions(+) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 6b919e746700..b45f64992544 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -35,6 +35,8 @@ use tauri_runtime::{ #[cfg(target_vendor = "apple")] use objc2::rc::Retained; +#[cfg(target_os = "android")] +use tao::platform::android::WindowBuilderExtAndroid; #[cfg(target_os = "macos")] use tao::platform::macos::{EventLoopWindowTargetExtMacOS, WindowBuilderExtMacOS}; #[cfg(target_os = "linux")] @@ -1238,6 +1240,18 @@ impl WindowBuilder for WindowBuilderWrapper { fn window_classname>(self, _window_classname: S) -> Self { self } + + #[cfg(target_os = "android")] + fn activity_name>(mut self, class_name: S) -> Self { + self.inner = self.inner.activity_name(class_name.into()); + self + } + + #[cfg(target_os = "android")] + fn created_by_activity_name>(mut self, class_name: S) -> Self { + self.inner = self.inner.created_by_activity_name(class_name.into()); + self + } } #[cfg(any( @@ -4401,6 +4415,7 @@ fn create_window( let window = window_builder .inner .build(event_loop) + .inspect_err(|e| log::error!("Error creating window: {e:?}")) .map_err(|_| Error::CreateWindow)?; #[cfg(feature = "tracing")] diff --git a/crates/tauri-runtime/src/window.rs b/crates/tauri-runtime/src/window.rs index cdda268832d5..1444efef61f2 100644 --- a/crates/tauri-runtime/src/window.rs +++ b/crates/tauri-runtime/src/window.rs @@ -477,6 +477,16 @@ pub trait WindowBuilder: WindowBuilderBase { /// Sets custom name for Windows' window class. **Windows only**. #[must_use] fn window_classname>(self, window_classname: S) -> Self; + + /// The name of the activity to create for this webview window. + #[cfg(target_os = "android")] + fn activity_name>(self, class_name: S) -> Self; + + /// Sets the name of the activity that is creating this webview window. + /// + /// This is important to determine which stack the activity will belong to. + #[cfg(target_os = "android")] + fn created_by_activity_name>(self, class_name: S) -> Self; } /// A window that has yet to be built. diff --git a/crates/tauri/src/test/mock_runtime.rs b/crates/tauri/src/test/mock_runtime.rs index 16b136ef7544..3ad101edf2c1 100644 --- a/crates/tauri/src/test/mock_runtime.rs +++ b/crates/tauri/src/test/mock_runtime.rs @@ -534,6 +534,16 @@ impl WindowBuilder for MockWindowBuilder { fn background_color(self, _color: tauri_utils::config::Color) -> Self { self } + + #[cfg(target_os = "android")] + fn activity_name(self, _class_name: String) -> Self { + self + } + + #[cfg(target_os = "android")] + fn created_by_activity_name(self, _class_name: String) -> Self { + self + } } impl WebviewDispatch for MockWebviewDispatcher { diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs index 24232993a7d5..05da93ac847d 100644 --- a/crates/tauri/src/webview/webview_window.rs +++ b/crates/tauri/src/webview/webview_window.rs @@ -1376,6 +1376,24 @@ impl> WebviewWindowBuilder<'_, R, M> { } } +// Android specific APIs +#[cfg(target_os = "android")] +impl> WebviewWindowBuilder<'_, R, M> { + /// The name of the activity to create for this webview window. + pub fn activity_name>(mut self, class_name: S) -> Self { + self.window_builder = self.window_builder.activity_name(class_name); + self + } + + /// Sets the name of the activity that is creating this webview window. + /// + /// This is important to determine which stack the activity will belong to. + pub fn created_by_activity_name>(mut self, class_name: S) -> Self { + self.window_builder = self.window_builder.created_by_activity_name(class_name); + self + } +} + /// A type that wraps a [`Window`] together with a [`Webview`]. #[default_runtime(crate::Wry, wry)] #[derive(Debug)] diff --git a/crates/tauri/src/window/mod.rs b/crates/tauri/src/window/mod.rs index 9e74e694b354..00f9894b3dc3 100644 --- a/crates/tauri/src/window/mod.rs +++ b/crates/tauri/src/window/mod.rs @@ -904,6 +904,24 @@ impl> WindowBuilder<'_, R, M> { self } } + +#[cfg(target_os = "android")] +impl> WindowBuilder<'_, R, M> { + /// The name of the activity to create for this webview window. + pub fn activity_name>(mut self, class_name: S) -> Self { + self.window_builder = self.window_builder.activity_name(class_name); + self + } + + /// Sets the name of the activity that is creating this webview window. + /// + /// This is important to determine which stack the activity will belong to. + pub fn created_by_activity_name>(mut self, class_name: S) -> Self { + self.window_builder = self.window_builder.created_by_activity_name(class_name); + self + } +} + /// A wrapper struct to hold the window menu state /// and whether it is global per-app or specific to this window. #[cfg(desktop)] diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index 73a573672fab..a2a097ad4c61 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -104,6 +104,18 @@ pub fn run_app) + Send + 'static>( let webview = window_builder.build()?; + let app_ = app.handle().clone(); + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_secs(3)); + let mut w = WebviewWindowBuilder::new(&app_, "main-2", WebviewUrl::default()); + #[cfg(target_os = "android")] + { + w = w.activity_name("Activity3"); + } + let w = w.build().unwrap(); + w.open_devtools(); + }); + #[cfg(debug_assertions)] webview.open_devtools(); From f12df40b0077cd8304df908977035197c09eebb4 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Fri, 24 Oct 2025 14:52:36 -0300 Subject: [PATCH 03/33] activity_name getter, JS API, auto inherit from manager on builder::new --- crates/tauri-cli/config.schema.json | 14 +++++++++ crates/tauri-runtime-wry/src/lib.rs | 14 ++++++++- crates/tauri-runtime/src/lib.rs | 4 +++ .../schemas/config.schema.json | 14 +++++++++ crates/tauri-utils/src/config.rs | 16 +++++++++- crates/tauri/src/app.rs | 10 +++++++ crates/tauri/src/lib.rs | 2 ++ crates/tauri/src/test/mock_runtime.rs | 5 ++++ crates/tauri/src/webview/mod.rs | 5 ++++ crates/tauri/src/webview/webview_window.rs | 11 +++++++ crates/tauri/src/window/mod.rs | 30 ++++++++++++++++++- examples/api/src-tauri/src/lib.rs | 12 -------- packages/api/src/window.ts | 10 +++++++ 13 files changed, 132 insertions(+), 15 deletions(-) diff --git a/crates/tauri-cli/config.schema.json b/crates/tauri-cli/config.schema.json index 672ace507f23..646fd1e5d808 100644 --- a/crates/tauri-cli/config.schema.json +++ b/crates/tauri-cli/config.schema.json @@ -603,6 +603,20 @@ "$ref": "#/definitions/ScrollBarStyle" } ] + }, + "activityName": { + "description": "The name of the Android activity to create for this window.", + "type": [ + "string", + "null" + ] + }, + "createdByActivityName": { + "description": "The name of the Android activity that is creating this webview window.\n\n This is important to determine which stack the activity will belong to.", + "type": [ + "string", + "null" + ] } }, "additionalProperties": false diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index b45f64992544..4c9862a9c8cf 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -36,7 +36,7 @@ use tauri_runtime::{ #[cfg(target_vendor = "apple")] use objc2::rc::Retained; #[cfg(target_os = "android")] -use tao::platform::android::WindowBuilderExtAndroid; +use tao::platform::android::{WindowBuilderExtAndroid, WindowExtAndroid}; #[cfg(target_os = "macos")] use tao::platform::macos::{EventLoopWindowTargetExtMacOS, WindowBuilderExtMacOS}; #[cfg(target_os = "linux")] @@ -1343,6 +1343,8 @@ pub enum WindowMessage { target_os = "openbsd" ))] GtkBox(Sender), + #[cfg(target_os = "android")] + ActivityName(Sender), RawWindowHandle(Sender>), Theme(Sender), IsEnabled(Sender), @@ -2004,6 +2006,12 @@ impl WindowDispatch for WryWindowDispatcher { window_getter!(self, WindowMessage::GtkBox).map(|w| w.0) } + /// Returns the name of the Android activity associated with this window. + #[cfg(target_os = "android")] + fn activity_name(&self) -> Result { + window_getter!(self, WindowMessage::ActivityName) + } + fn window_handle( &self, ) -> std::result::Result, raw_window_handle::HandleError> { @@ -3300,6 +3308,10 @@ fn handle_user_message( WindowMessage::GtkBox(tx) => tx .send(GtkBox(window.default_vbox().unwrap().clone())) .unwrap(), + #[cfg(target_os = "android")] + WindowMessage::ActivityName(tx) => { + tx.send(window.activity_name()).unwrap(); + } WindowMessage::RawWindowHandle(tx) => tx .send( window diff --git a/crates/tauri-runtime/src/lib.rs b/crates/tauri-runtime/src/lib.rs index a8d17181eb53..9e9571e2cad0 100644 --- a/crates/tauri-runtime/src/lib.rs +++ b/crates/tauri-runtime/src/lib.rs @@ -735,6 +735,10 @@ pub trait WindowDispatch: Debug + Clone + Send + Sync + Sized + 's ))] fn default_vbox(&self) -> Result; + /// Returns the name of the Android activity associated with this window. + #[cfg(target_os = "android")] + fn activity_name(&self) -> Result; + /// Raw window handle. fn window_handle( &self, diff --git a/crates/tauri-schema-generator/schemas/config.schema.json b/crates/tauri-schema-generator/schemas/config.schema.json index 672ace507f23..646fd1e5d808 100644 --- a/crates/tauri-schema-generator/schemas/config.schema.json +++ b/crates/tauri-schema-generator/schemas/config.schema.json @@ -603,6 +603,20 @@ "$ref": "#/definitions/ScrollBarStyle" } ] + }, + "activityName": { + "description": "The name of the Android activity to create for this window.", + "type": [ + "string", + "null" + ] + }, + "createdByActivityName": { + "description": "The name of the Android activity that is creating this webview window.\n\n This is important to determine which stack the activity will belong to.", + "type": [ + "string", + "null" + ] } }, "additionalProperties": false diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index 40d33292a17e..14761b871fc4 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -1995,6 +1995,14 @@ pub struct WindowConfig { /// - **Linux / Android / iOS / macOS**: Unsupported. Only supports `Default` and performs no operation. #[serde(default, alias = "scroll-bar-style")] pub scroll_bar_style: ScrollBarStyle, + /// The name of the Android activity to create for this window. + #[serde(default, alias = "activity-name")] + pub activity_name: Option, + /// The name of the Android activity that is creating this webview window. + /// + /// This is important to determine which stack the activity will belong to. + #[serde(default, alias = "created-by-activity-name")] + pub created_by_activity_name: Option, } impl Default for WindowConfig { @@ -2057,6 +2065,8 @@ impl Default for WindowConfig { data_directory: None, data_store_identifier: None, scroll_bar_style: ScrollBarStyle::Default, + activity_name: None, + created_by_activity_name: None, } } } @@ -3594,6 +3604,8 @@ mod build { let data_directory = opt_lit(self.data_directory.as_ref().map(path_buf_lit).as_ref()); let data_store_identifier = opt_vec_lit(self.data_store_identifier, identity); let scroll_bar_style = &self.scroll_bar_style; + let activity_name = opt_lit(self.activity_name.as_ref()); + let created_by_activity_name = opt_lit(self.created_by_activity_name.as_ref()); literal_struct!( tokens, @@ -3654,7 +3666,9 @@ mod build { disable_input_accessory_view, data_directory, data_store_identifier, - scroll_bar_style + scroll_bar_style, + activity_name, + created_by_activity_name ); } } diff --git a/crates/tauri/src/app.rs b/crates/tauri/src/app.rs index b950895bc002..229205faf953 100644 --- a/crates/tauri/src/app.rs +++ b/crates/tauri/src/app.rs @@ -658,6 +658,11 @@ impl ManagerBase for AppHandle { fn managed_app_handle(&self) -> &AppHandle { self } + + #[cfg(target_os = "android")] + fn activity_name(&self) -> Option> { + None + } } /// The instance of the currently running application. @@ -708,6 +713,11 @@ impl ManagerBase for App { fn managed_app_handle(&self) -> &AppHandle { self.handle() } + + #[cfg(target_os = "android")] + fn activity_name(&self) -> Option> { + None + } } /// APIs specific to the wry runtime. diff --git a/crates/tauri/src/lib.rs b/crates/tauri/src/lib.rs index 392e9272b8b8..6393565b4b87 100644 --- a/crates/tauri/src/lib.rs +++ b/crates/tauri/src/lib.rs @@ -1088,6 +1088,8 @@ pub(crate) mod sealed { fn manager_owned(&self) -> Arc>; fn runtime(&self) -> RuntimeOrDispatch<'_, R>; fn managed_app_handle(&self) -> &AppHandle; + #[cfg(target_os = "android")] + fn activity_name(&self) -> Option>; } } diff --git a/crates/tauri/src/test/mock_runtime.rs b/crates/tauri/src/test/mock_runtime.rs index 3ad101edf2c1..2ebe947472c5 100644 --- a/crates/tauri/src/test/mock_runtime.rs +++ b/crates/tauri/src/test/mock_runtime.rs @@ -806,6 +806,11 @@ impl WindowDispatch for MockWindowDispatcher { unimplemented!() } + #[cfg(target_os = "android")] + fn activity_name(&self) -> Result { + unimplemented!() + } + fn window_handle( &self, ) -> std::result::Result, raw_window_handle::HandleError> { diff --git a/crates/tauri/src/webview/mod.rs b/crates/tauri/src/webview/mod.rs index 9c4a6e0ea79c..63f071c45b31 100644 --- a/crates/tauri/src/webview/mod.rs +++ b/crates/tauri/src/webview/mod.rs @@ -2278,6 +2278,11 @@ impl ManagerBase for Webview { fn managed_app_handle(&self) -> &AppHandle { &self.app_handle } + + #[cfg(target_os = "android")] + fn activity_name(&self) -> Option> { + Some(self.window().activity_name()) + } } impl<'de, R: Runtime> CommandArg<'de, R> for Webview { diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs index 05da93ac847d..26a6d84ce19f 100644 --- a/crates/tauri/src/webview/webview_window.rs +++ b/crates/tauri/src/webview/webview_window.rs @@ -1823,6 +1823,12 @@ impl WebviewWindow { self.window.default_vbox() } + /// Returns the name of the Android activity associated with this window. + #[cfg(target_os = "android")] + pub fn activity_name(&self) -> crate::Result { + self.window.activity_name() + } + /// Returns the current window theme. /// /// ## Platform-specific @@ -2600,4 +2606,9 @@ impl ManagerBase for WebviewWindow { fn managed_app_handle(&self) -> &AppHandle { self.webview.managed_app_handle() } + + #[cfg(target_os = "android")] + fn activity_name(&self) -> Option> { + Some(self.window.activity_name()) + } } diff --git a/crates/tauri/src/window/mod.rs b/crates/tauri/src/window/mod.rs index 00f9894b3dc3..183e09f19058 100644 --- a/crates/tauri/src/window/mod.rs +++ b/crates/tauri/src/window/mod.rs @@ -129,6 +129,8 @@ unstable_struct!( #[cfg(desktop)] on_menu_event: Option>>, window_effects: Option, + #[cfg(target_os = "android")] + created_by_activity_name_set: bool, } ); @@ -215,6 +217,8 @@ async fn create_window(app: tauri::AppHandle) { #[cfg(desktop)] on_menu_event: None, window_effects: None, + #[cfg(target_os = "android")] + created_by_activity_name_set: false, } } @@ -250,6 +254,8 @@ async fn reopen_window(app: tauri::AppHandle) { pub fn from_config(manager: &'a M, config: &WindowConfig) -> crate::Result { #[cfg_attr(not(windows), allow(unused_mut))] let mut builder = Self { + #[cfg(target_os = "android")] + created_by_activity_name_set: config.created_by_activity_name.is_some(), manager, label: config.label.clone(), window_effects: config.window_effects.clone(), @@ -345,12 +351,22 @@ tauri::Builder::default() /// Creates a new window with an optional webview. fn build_internal( - self, + // mutable on Android + #[allow(unused_mut)] mut self, webview: Option>, ) -> crate::Result> { #[cfg(desktop)] let theme = self.window_builder.get_theme(); + #[cfg(target_os = "android")] + if !self.created_by_activity_name_set { + if let Some(manager_window_activity_name) = self.manager.activity_name() { + self.window_builder = self + .window_builder + .created_by_activity_name(manager_window_activity_name?); + } + } + let mut pending = PendingWindow::new(self.window_builder, self.label)?; if let Some(webview) = webview { pending.set_webview(webview); @@ -917,6 +933,7 @@ impl> WindowBuilder<'_, R, M> { /// /// This is important to determine which stack the activity will belong to. pub fn created_by_activity_name>(mut self, class_name: S) -> Self { + self.created_by_activity_name_set = true; self.window_builder = self.window_builder.created_by_activity_name(class_name); self } @@ -1027,6 +1044,11 @@ impl ManagerBase for Window { fn managed_app_handle(&self) -> &AppHandle { &self.app_handle } + + #[cfg(target_os = "android")] + fn activity_name(&self) -> Option> { + Some(self.activity_name()) + } } impl<'de, R: Runtime> CommandArg<'de, R> for Window { @@ -1637,6 +1659,12 @@ impl Window { self.window.dispatcher.default_vbox().map_err(Into::into) } + /// Returns the name of the Android activity associated with this window. + #[cfg(target_os = "android")] + pub fn activity_name(&self) -> crate::Result { + self.window.dispatcher.activity_name().map_err(Into::into) + } + /// Returns the current window theme. /// /// ## Platform-specific diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index a2a097ad4c61..73a573672fab 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -104,18 +104,6 @@ pub fn run_app) + Send + 'static>( let webview = window_builder.build()?; - let app_ = app.handle().clone(); - std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_secs(3)); - let mut w = WebviewWindowBuilder::new(&app_, "main-2", WebviewUrl::default()); - #[cfg(target_os = "android")] - { - w = w.activity_name("Activity3"); - } - let w = w.build().unwrap(); - w.open_devtools(); - }); - #[cfg(debug_assertions)] webview.open_devtools(); diff --git a/packages/api/src/window.ts b/packages/api/src/window.ts index 090108337e5e..06f42a832120 100644 --- a/packages/api/src/window.ts +++ b/packages/api/src/window.ts @@ -2511,6 +2511,16 @@ interface WindowOptions { * - **Linux / Android / iOS / macOS**: Unsupported. Only supports `Default` and performs no operation. */ scrollBarStyle?: ScrollBarStyle + /** + * The name of the Android activity to create for this window. + */ + activityName?: string + /** + * The name of the Android activity that is creating this webview window. + * + * This is important to determine which stack the activity will belong to. + */ + createdByActivityName?: string } function mapMonitor(m: Monitor | null): Monitor | null { From c389ba427fa4c3e2dbfe5c3061e14eedb8a12119 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 28 Oct 2025 14:12:11 -0300 Subject: [PATCH 04/33] refactor(core): improve iOS log messages from stdout/stderr move the stdout/stderr forward logic to Swift so it does not consume a Rust thread and never deadlocks on the simulator I had to work on this because i'm facing #12172 again on latest Xcode (26.1) --- .changes/refactor-ios-stdout-stderr.md | 6 ++ .../mobile/ios-api/Sources/Tauri/Logger.swift | 83 ++++++++++++++++++- .../mobile/ios-api/Sources/Tauri/Tauri.swift | 8 ++ crates/tauri/src/ios.rs | 1 + crates/tauri/src/lib.rs | 32 +------ 5 files changed, 99 insertions(+), 31 deletions(-) create mode 100644 .changes/refactor-ios-stdout-stderr.md diff --git a/.changes/refactor-ios-stdout-stderr.md b/.changes/refactor-ios-stdout-stderr.md new file mode 100644 index 000000000000..3993ebfe0fe2 --- /dev/null +++ b/.changes/refactor-ios-stdout-stderr.md @@ -0,0 +1,6 @@ +--- +"tauri": path:bug +"tauri-macros": patch:bug +--- + +Fix iOS deadlock when running on the simulator from Xcode by properly piping stdout/stderr messages through the Xcode console and OSLog. diff --git a/crates/tauri/mobile/ios-api/Sources/Tauri/Logger.swift b/crates/tauri/mobile/ios-api/Sources/Tauri/Logger.swift index 9fa6e3fe71fd..d055090dccf3 100644 --- a/crates/tauri/mobile/ios-api/Sources/Tauri/Logger.swift +++ b/crates/tauri/mobile/ios-api/Sources/Tauri/Logger.swift @@ -4,6 +4,86 @@ import os.log import UIKit +import Foundation + +class StdoutRedirector { + private var originalStdout: Int32 = -1 + private var originalStderr: Int32 = -1 + private var stdoutPipe: [Int32] = [-1, -1] + private var stderrPipe: [Int32] = [-1, -1] + private var stdoutReadSource: DispatchSourceRead? + private var stderrReadSource: DispatchSourceRead? + + func start() { + originalStdout = dup(STDOUT_FILENO) + originalStderr = dup(STDERR_FILENO) + + guard Darwin.pipe(&stdoutPipe) == 0, + Darwin.pipe(&stderrPipe) == 0 else { + Logger.error("Failed to create stdout/stderr pipes") + return + } + + dup2(stdoutPipe[1], STDOUT_FILENO) + dup2(stderrPipe[1], STDERR_FILENO) + close(stdoutPipe[1]) + close(stderrPipe[1]) + + stdoutReadSource = createReader( + readPipe: stdoutPipe[0], + writeToOriginal: originalStdout, + label: "stdout" + ) + + stderrReadSource = createReader( + readPipe: stderrPipe[0], + writeToOriginal: originalStderr, + label: "stderr" + ) + } + + private func createReader( + readPipe: Int32, + writeToOriginal: Int32, + label: String + ) -> DispatchSourceRead { + let source = DispatchSource.makeReadSource( + fileDescriptor: readPipe, + queue: .global(qos: .utility) + ) + + source.setEventHandler { + let bufferSize = 4096 + var buffer = [UInt8](repeating: 0, count: bufferSize) + let bytesRead = read(readPipe, &buffer, bufferSize) + + if bytesRead > 0 { + let output = String( + bytes: buffer[0.. Date: Tue, 28 Oct 2025 14:13:23 -0300 Subject: [PATCH 05/33] patch --- .changes/refactor-ios-stdout-stderr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/refactor-ios-stdout-stderr.md b/.changes/refactor-ios-stdout-stderr.md index 3993ebfe0fe2..766cd9fb5492 100644 --- a/.changes/refactor-ios-stdout-stderr.md +++ b/.changes/refactor-ios-stdout-stderr.md @@ -1,5 +1,5 @@ --- -"tauri": path:bug +"tauri": patch:bug "tauri-macros": patch:bug --- From b5d874d4347d6be5a06fa346f478c6e742ac7e16 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 29 Oct 2025 07:22:16 -0300 Subject: [PATCH 06/33] use context (created_by_activity_name) from caller on TS API --- crates/tauri/src/window/plugin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/tauri/src/window/plugin.rs b/crates/tauri/src/window/plugin.rs index e1fecd9b732d..db5326303130 100644 --- a/crates/tauri/src/window/plugin.rs +++ b/crates/tauri/src/window/plugin.rs @@ -31,8 +31,8 @@ mod desktop_commands { } #[command(root = "crate")] - pub async fn create(app: AppHandle, options: WindowConfig) -> crate::Result<()> { - WindowBuilder::from_config(&app, &options)?.build()?; + pub async fn create(window: Window, options: WindowConfig) -> crate::Result<()> { + WindowBuilder::from_config(&window, &options)?.build()?; Ok(()) } From bcc25c007141ac967b27ceee8747aec160e385d8 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 29 Oct 2025 08:36:26 -0300 Subject: [PATCH 07/33] add scene identifier getter and setter --- crates/tauri-cli/config.schema.json | 7 +++ crates/tauri-runtime-wry/src/lib.rs | 26 +++++++-- crates/tauri-runtime/src/lib.rs | 4 ++ crates/tauri-runtime/src/window.rs | 7 +++ .../schemas/config.schema.json | 7 +++ crates/tauri-utils/src/config.rs | 12 ++++- crates/tauri/build.rs | 2 + .../window/autogenerated/reference.md | 54 +++++++++++++++++++ crates/tauri/src/app.rs | 10 ++++ crates/tauri/src/lib.rs | 2 + crates/tauri/src/test/mock_runtime.rs | 10 ++++ crates/tauri/src/webview/mod.rs | 5 ++ crates/tauri/src/webview/webview_window.rs | 21 ++++++++ crates/tauri/src/window/mod.rs | 47 ++++++++++++++++ crates/tauri/src/window/plugin.rs | 4 ++ packages/api/src/window.ts | 19 +++++++ 16 files changed, 233 insertions(+), 4 deletions(-) diff --git a/crates/tauri-cli/config.schema.json b/crates/tauri-cli/config.schema.json index 8ac3570fcb28..6fb5cab9949b 100644 --- a/crates/tauri-cli/config.schema.json +++ b/crates/tauri-cli/config.schema.json @@ -617,6 +617,13 @@ "string", "null" ] + }, + "requestedBySceneIdentifier": { + "description": "Sets the identifier of the scene that is requesting the new scene,\n establishing a relationship between the two scenes.\n\n By default the system uses the foreground scene.", + "type": [ + "string", + "null" + ] } }, "additionalProperties": false diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 4c9862a9c8cf..48bf17d2676a 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -111,7 +111,7 @@ use wry::{ use wry::{WebViewBuilderExtUnix, WebViewExtUnix}; #[cfg(target_os = "ios")] -pub use tao::platform::ios::WindowExtIOS; +pub use tao::platform::ios::{WindowBuilderExtIOS, WindowExtIOS}; #[cfg(target_os = "macos")] pub use tao::platform::macos::{ ActivationPolicy as TaoActivationPolicy, EventLoopExtMacOS, WindowExtMacOS, @@ -1243,13 +1243,21 @@ impl WindowBuilder for WindowBuilderWrapper { #[cfg(target_os = "android")] fn activity_name>(mut self, class_name: S) -> Self { - self.inner = self.inner.activity_name(class_name.into()); + self.inner = self.inner.with_activity_name(class_name.into()); self } #[cfg(target_os = "android")] fn created_by_activity_name>(mut self, class_name: S) -> Self { - self.inner = self.inner.created_by_activity_name(class_name.into()); + self.inner = self.inner.with_created_by_activity_name(class_name.into()); + self + } + + #[cfg(target_os = "ios")] + fn requested_by_scene_identifier>(mut self, identifier: S) -> Self { + self.inner = self + .inner + .with_requesting_scene_identifier(identifier.into()); self } } @@ -1345,6 +1353,8 @@ pub enum WindowMessage { GtkBox(Sender), #[cfg(target_os = "android")] ActivityName(Sender), + #[cfg(target_os = "ios")] + SceneIdentifier(Sender), RawWindowHandle(Sender>), Theme(Sender), IsEnabled(Sender), @@ -2012,6 +2022,12 @@ impl WindowDispatch for WryWindowDispatcher { window_getter!(self, WindowMessage::ActivityName) } + /// Returns the identifier of the UIScene tied to this UIWindow. + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Result { + window_getter!(self, WindowMessage::SceneIdentifier) + } + fn window_handle( &self, ) -> std::result::Result, raw_window_handle::HandleError> { @@ -3312,6 +3328,10 @@ fn handle_user_message( WindowMessage::ActivityName(tx) => { tx.send(window.activity_name()).unwrap(); } + #[cfg(target_os = "ios")] + WindowMessage::SceneIdentifier(tx) => { + tx.send(window.scene_identifier()).unwrap(); + } WindowMessage::RawWindowHandle(tx) => tx .send( window diff --git a/crates/tauri-runtime/src/lib.rs b/crates/tauri-runtime/src/lib.rs index 9e9571e2cad0..5da3a2a3ef06 100644 --- a/crates/tauri-runtime/src/lib.rs +++ b/crates/tauri-runtime/src/lib.rs @@ -739,6 +739,10 @@ pub trait WindowDispatch: Debug + Clone + Send + Sync + Sized + 's #[cfg(target_os = "android")] fn activity_name(&self) -> Result; + /// Returns the identifier of the UIScene tied to this UIWindow. + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Result; + /// Raw window handle. fn window_handle( &self, diff --git a/crates/tauri-runtime/src/window.rs b/crates/tauri-runtime/src/window.rs index 1444efef61f2..a1ccd922eb2e 100644 --- a/crates/tauri-runtime/src/window.rs +++ b/crates/tauri-runtime/src/window.rs @@ -487,6 +487,13 @@ pub trait WindowBuilder: WindowBuilderBase { /// This is important to determine which stack the activity will belong to. #[cfg(target_os = "android")] fn created_by_activity_name>(self, class_name: S) -> Self; + + /// Sets the identifier of the UIScene that is requesting the creation of this new scene, + /// establishing a relationship between the two scenes. + /// + /// By default the system uses the foreground scene. + #[cfg(target_os = "ios")] + fn requested_by_scene_identifier>(self, identifier: S) -> Self; } /// A window that has yet to be built. diff --git a/crates/tauri-schema-generator/schemas/config.schema.json b/crates/tauri-schema-generator/schemas/config.schema.json index 8ac3570fcb28..6fb5cab9949b 100644 --- a/crates/tauri-schema-generator/schemas/config.schema.json +++ b/crates/tauri-schema-generator/schemas/config.schema.json @@ -617,6 +617,13 @@ "string", "null" ] + }, + "requestedBySceneIdentifier": { + "description": "Sets the identifier of the scene that is requesting the new scene,\n establishing a relationship between the two scenes.\n\n By default the system uses the foreground scene.", + "type": [ + "string", + "null" + ] } }, "additionalProperties": false diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index 14761b871fc4..30b2377b757a 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -2003,6 +2003,13 @@ pub struct WindowConfig { /// This is important to determine which stack the activity will belong to. #[serde(default, alias = "created-by-activity-name")] pub created_by_activity_name: Option, + + /// Sets the identifier of the scene that is requesting the new scene, + /// establishing a relationship between the two scenes. + /// + /// By default the system uses the foreground scene. + #[serde(default, alias = "requested-by-scene-identifier")] + pub requested_by_scene_identifier: Option, } impl Default for WindowConfig { @@ -2067,6 +2074,7 @@ impl Default for WindowConfig { scroll_bar_style: ScrollBarStyle::Default, activity_name: None, created_by_activity_name: None, + requested_by_scene_identifier: None, } } } @@ -3606,6 +3614,7 @@ mod build { let scroll_bar_style = &self.scroll_bar_style; let activity_name = opt_lit(self.activity_name.as_ref()); let created_by_activity_name = opt_lit(self.created_by_activity_name.as_ref()); + let requested_by_scene_identifier = opt_lit(self.requested_by_scene_identifier.as_ref()); literal_struct!( tokens, @@ -3668,7 +3677,8 @@ mod build { data_store_identifier, scroll_bar_style, activity_name, - created_by_activity_name + created_by_activity_name, + requested_by_scene_identifier ); } } diff --git a/crates/tauri/build.rs b/crates/tauri/build.rs index d63401ed099e..4a886abad524 100644 --- a/crates/tauri/build.rs +++ b/crates/tauri/build.rs @@ -69,6 +69,8 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[ ("cursor_position", true), ("theme", true), ("is_always_on_top", true), + ("activity_name", true), + ("scene_identifier", true), // setters ("center", false), ("request_user_attention", false), diff --git a/crates/tauri/permissions/window/autogenerated/reference.md b/crates/tauri/permissions/window/autogenerated/reference.md index 00158661d0ed..9d591ba38e3d 100644 --- a/crates/tauri/permissions/window/autogenerated/reference.md +++ b/crates/tauri/permissions/window/autogenerated/reference.md @@ -29,6 +29,8 @@ Default permissions for the plugin. - `allow-cursor-position` - `allow-theme` - `allow-is-always-on-top` +- `allow-activity-name` +- `allow-scene-identifier` - `allow-internal-toggle-maximize` ## Permission Table @@ -40,6 +42,32 @@ Default permissions for the plugin. + + + +`core:window:allow-activity-name` + + + + +Enables the activity_name command without any pre-configured scope. + + + + + + + +`core:window:deny-activity-name` + + + + +Denies the activity_name command without any pre-configured scope. + + + + @@ -875,6 +903,32 @@ Denies the scale_factor command without any pre-configured scope. +`core:window:allow-scene-identifier` + + + + +Enables the scene_identifier command without any pre-configured scope. + + + + + + + +`core:window:deny-scene-identifier` + + + + +Denies the scene_identifier command without any pre-configured scope. + + + + + + + `core:window:allow-set-always-on-bottom` diff --git a/crates/tauri/src/app.rs b/crates/tauri/src/app.rs index 229205faf953..cacdaea4e381 100644 --- a/crates/tauri/src/app.rs +++ b/crates/tauri/src/app.rs @@ -663,6 +663,11 @@ impl ManagerBase for AppHandle { fn activity_name(&self) -> Option> { None } + + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Option> { + None + } } /// The instance of the currently running application. @@ -718,6 +723,11 @@ impl ManagerBase for App { fn activity_name(&self) -> Option> { None } + + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Option> { + None + } } /// APIs specific to the wry runtime. diff --git a/crates/tauri/src/lib.rs b/crates/tauri/src/lib.rs index db4752464ba5..88214fad1657 100644 --- a/crates/tauri/src/lib.rs +++ b/crates/tauri/src/lib.rs @@ -1062,6 +1062,8 @@ pub(crate) mod sealed { fn managed_app_handle(&self) -> &AppHandle; #[cfg(target_os = "android")] fn activity_name(&self) -> Option>; + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Option>; } } diff --git a/crates/tauri/src/test/mock_runtime.rs b/crates/tauri/src/test/mock_runtime.rs index 2ebe947472c5..7d81a804fa27 100644 --- a/crates/tauri/src/test/mock_runtime.rs +++ b/crates/tauri/src/test/mock_runtime.rs @@ -544,6 +544,11 @@ impl WindowBuilder for MockWindowBuilder { fn created_by_activity_name(self, _class_name: String) -> Self { self } + + #[cfg(target_os = "ios")] + fn requested_by_scene_identifier(self, _identifier: String) -> Self { + self + } } impl WebviewDispatch for MockWebviewDispatcher { @@ -811,6 +816,11 @@ impl WindowDispatch for MockWindowDispatcher { unimplemented!() } + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Result { + unimplemented!() + } + fn window_handle( &self, ) -> std::result::Result, raw_window_handle::HandleError> { diff --git a/crates/tauri/src/webview/mod.rs b/crates/tauri/src/webview/mod.rs index 63f071c45b31..5122bdce9535 100644 --- a/crates/tauri/src/webview/mod.rs +++ b/crates/tauri/src/webview/mod.rs @@ -2283,6 +2283,11 @@ impl ManagerBase for Webview { fn activity_name(&self) -> Option> { Some(self.window().activity_name()) } + + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Option> { + Some(self.window().scene_identifier()) + } } impl<'de, R: Runtime> CommandArg<'de, R> for Webview { diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs index 26a6d84ce19f..6c6d025f2637 100644 --- a/crates/tauri/src/webview/webview_window.rs +++ b/crates/tauri/src/webview/webview_window.rs @@ -1394,6 +1394,22 @@ impl> WebviewWindowBuilder<'_, R, M> { } } +/// iOS specific APIs +#[cfg(target_os = "ios")] +impl> WebviewWindowBuilder<'_, R, M> { + /// Sets the identifier of the scene that is requesting the new scene, + /// establishing a relationship between the two scenes. + /// + /// By default the system uses the foreground scene. + #[cfg(target_os = "ios")] + pub fn requested_by_scene_identifier(mut self, identifier: String) -> Self { + self.window_builder = self + .window_builder + .requested_by_scene_identifier(identifier); + self + } +} + /// A type that wraps a [`Window`] together with a [`Webview`]. #[default_runtime(crate::Wry, wry)] #[derive(Debug)] @@ -2611,4 +2627,9 @@ impl ManagerBase for WebviewWindow { fn activity_name(&self) -> Option> { Some(self.window.activity_name()) } + + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Option> { + Some(self.window.scene_identifier()) + } } diff --git a/crates/tauri/src/window/mod.rs b/crates/tauri/src/window/mod.rs index 183e09f19058..d9d4a930437c 100644 --- a/crates/tauri/src/window/mod.rs +++ b/crates/tauri/src/window/mod.rs @@ -131,6 +131,8 @@ unstable_struct!( window_effects: Option, #[cfg(target_os = "android")] created_by_activity_name_set: bool, + #[cfg(target_os = "ios")] + requested_by_scene_identifier_set: bool, } ); @@ -219,6 +221,8 @@ async fn create_window(app: tauri::AppHandle) { window_effects: None, #[cfg(target_os = "android")] created_by_activity_name_set: false, + #[cfg(target_os = "ios")] + requested_by_scene_identifier_set: false, } } @@ -256,6 +260,8 @@ async fn reopen_window(app: tauri::AppHandle) { let mut builder = Self { #[cfg(target_os = "android")] created_by_activity_name_set: config.created_by_activity_name.is_some(), + #[cfg(target_os = "ios")] + requested_by_scene_identifier_set: config.requested_by_scene_identifier.is_some(), manager, label: config.label.clone(), window_effects: config.window_effects.clone(), @@ -367,6 +373,15 @@ tauri::Builder::default() } } + #[cfg(target_os = "ios")] + if !self.requested_by_scene_identifier_set { + if let Some(manager_window_scene_identifier) = self.manager.scene_identifier() { + self.window_builder = self + .window_builder + .requested_by_scene_identifier(manager_window_scene_identifier?); + } + } + let mut pending = PendingWindow::new(self.window_builder, self.label)?; if let Some(webview) = webview { pending.set_webview(webview); @@ -939,6 +954,23 @@ impl> WindowBuilder<'_, R, M> { } } +/// iOS specific APIs +#[cfg(target_os = "ios")] +impl> WindowBuilder<'_, R, M> { + /// Sets the identifier of the scene that is requesting the new scene, + /// establishing a relationship between the two scenes. + /// + /// By default the system uses the foreground scene. + #[cfg(target_os = "ios")] + pub fn requested_by_scene_identifier(mut self, identifier: String) -> Self { + self.requested_by_scene_identifier_set = true; + self.window_builder = self + .window_builder + .requested_by_scene_identifier(identifier); + self + } +} + /// A wrapper struct to hold the window menu state /// and whether it is global per-app or specific to this window. #[cfg(desktop)] @@ -1049,6 +1081,11 @@ impl ManagerBase for Window { fn activity_name(&self) -> Option> { Some(self.activity_name()) } + + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Option> { + Some(self.scene_identifier()) + } } impl<'de, R: Runtime> CommandArg<'de, R> for Window { @@ -1665,6 +1702,16 @@ impl Window { self.window.dispatcher.activity_name().map_err(Into::into) } + /// Returns the identifier of the UIScene tied to this window. + #[cfg(target_os = "ios")] + pub fn scene_identifier(&self) -> crate::Result { + self + .window + .dispatcher + .scene_identifier() + .map_err(Into::into) + } + /// Returns the current window theme. /// /// ## Platform-specific diff --git a/crates/tauri/src/window/plugin.rs b/crates/tauri/src/window/plugin.rs index db5326303130..f8066092274b 100644 --- a/crates/tauri/src/window/plugin.rs +++ b/crates/tauri/src/window/plugin.rs @@ -101,6 +101,10 @@ mod desktop_commands { getter!(cursor_position, PhysicalPosition); getter!(theme, Theme); getter!(is_always_on_top, bool); + #[cfg(target_os = "android")] + getter!(activity_name, String); + #[cfg(target_os = "ios")] + getter!(scene_identifier, String); setter!(center); setter!(request_user_attention, Option); diff --git a/packages/api/src/window.ts b/packages/api/src/window.ts index 06f42a832120..8d7994462930 100644 --- a/packages/api/src/window.ts +++ b/packages/api/src/window.ts @@ -820,6 +820,18 @@ class Window { }) } + async activityName(): Promise { + return invoke('plugin:window|activity_name', { + label: this.label + }) + } + + async sceneIdentifier(): Promise { + return invoke('plugin:window|scene_identifier', { + label: this.label + }) + } + // Setters /** @@ -2521,6 +2533,13 @@ interface WindowOptions { * This is important to determine which stack the activity will belong to. */ createdByActivityName?: string + /** + * Sets the identifier of the UIScene that is requesting the creation of this new scene, + * establishing a relationship between the two scenes. + * + * By default the system uses the foreground scene. + */ + requestedBySceneIdentifier?: string } function mapMonitor(m: Monitor | null): Monitor | null { From e4ccc322302b07d1306e26b530466d7c300d955f Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 29 Oct 2025 13:53:41 -0300 Subject: [PATCH 08/33] expose apis --- crates/tauri/src/webview/plugin.rs | 200 ++++--- crates/tauri/src/webview/webview_window.rs | 410 +++++++------- crates/tauri/src/window/mod.rs | 599 +++++++++++---------- crates/tauri/src/window/plugin.rs | 347 ++++++------ 4 files changed, 775 insertions(+), 781 deletions(-) diff --git a/crates/tauri/src/webview/plugin.rs b/crates/tauri/src/webview/plugin.rs index 78bfd80210e2..dbcca60ad675 100644 --- a/crates/tauri/src/webview/plugin.rs +++ b/crates/tauri/src/webview/plugin.rs @@ -5,85 +5,45 @@ //! The tauri plugin to create and manipulate windows from JS. use crate::{ + command, plugin::{Builder, TauriPlugin}, - Runtime, + sealed::ManagerBase, + utils::config::WindowConfig, + AppHandle, Runtime, WebviewWindowBuilder, }; -#[cfg(desktop)] -mod desktop_commands { - - use serde::Serialize; - use tauri_runtime::dpi::{Position, Size}; - use tauri_utils::config::WindowConfig; - - use super::*; - use crate::{ - command, sealed::ManagerBase, webview::Color, AppHandle, Webview, WebviewWindowBuilder, - }; - - #[derive(Serialize)] - pub struct WebviewRef { - window_label: String, - label: String, - } - - #[command(root = "crate")] - pub async fn get_all_webviews(app: AppHandle) -> Vec { - app - .manager() - .webviews() - .values() - .map(|webview| WebviewRef { - window_label: webview.window_ref().label().into(), - label: webview.label().into(), - }) - .collect() - } - - #[command(root = "crate")] - pub async fn create_webview_window( - app: AppHandle, - options: WindowConfig, - ) -> crate::Result<()> { - WebviewWindowBuilder::from_config(&app, &options)?.build()?; - Ok(()) - } - - #[cfg(not(feature = "unstable"))] - #[command(root = "crate")] - pub async fn create_webview() -> crate::Result<()> { - Err(crate::Error::UnstableFeatureNotSupported) - } - - #[cfg(feature = "unstable")] - #[command(root = "crate")] - pub async fn create_webview( - app: AppHandle, - window_label: String, - options: WindowConfig, - ) -> crate::Result<()> { - use anyhow::Context; - - let window = app - .manager() - .get_window(&window_label) - .ok_or(crate::Error::WindowNotFound)?; - - let x = options.x.context("missing parameter `options.x`")?; - let y = options.y.context("missing parameter `options.y`")?; - let width = options.width; - let height = options.height; +#[derive(serde::Serialize)] +struct WebviewRef { + window_label: String, + label: String, +} - let builder = crate::webview::WebviewBuilder::from_config(&options); +#[command(root = "crate")] +async fn get_all_webviews(app: AppHandle) -> Vec { + app + .manager() + .webviews() + .values() + .map(|webview| WebviewRef { + window_label: webview.window_ref().label().into(), + label: webview.label().into(), + }) + .collect() +} - window.add_child( - builder, - tauri_runtime::dpi::LogicalPosition::new(x, y), - tauri_runtime::dpi::LogicalSize::new(width, height), - )?; +#[command(root = "crate")] +async fn create_webview_window( + app: AppHandle, + options: WindowConfig, +) -> crate::Result<()> { + WebviewWindowBuilder::from_config(&app, &options)?.build()?; + Ok(()) +} - Ok(()) - } +#[cfg(desktop)] +mod desktop_commands { + use super::*; + use crate::{command, AppHandle}; fn get_webview( webview: Webview, @@ -163,6 +123,42 @@ mod desktop_commands { ); setter!(clear_all_browsing_data, clear_all_browsing_data); + #[cfg(not(feature = "unstable"))] + #[command(root = "crate")] + pub async fn create_webview() -> crate::Result<()> { + Err(crate::Error::UnstableFeatureNotSupported) + } + + #[cfg(feature = "unstable")] + #[command(root = "crate")] + pub async fn create_webview( + app: AppHandle, + window_label: String, + options: WindowConfig, + ) -> crate::Result<()> { + use anyhow::Context; + + let window = app + .manager() + .get_window(&window_label) + .ok_or(crate::Error::WindowNotFound)?; + + let x = options.x.context("missing parameter `options.x`")?; + let y = options.y.context("missing parameter `options.y`")?; + let width = options.width; + let height = options.height; + + let builder = crate::webview::WebviewBuilder::from_config(&options); + + window.add_child( + builder, + tauri_runtime::dpi::LogicalPosition::new(x, y), + tauri_runtime::dpi::LogicalSize::new(width, height), + )?; + + Ok(()) + } + #[command(root = "crate")] pub async fn reparent( webview: crate::Webview, @@ -228,39 +224,29 @@ pub fn init() -> TauriPlugin { } builder - .invoke_handler( - #[cfg(desktop)] - crate::generate_handler![ - #![plugin(webview)] - desktop_commands::create_webview, - desktop_commands::create_webview_window, - // getters - desktop_commands::get_all_webviews, - desktop_commands::webview_position, - desktop_commands::webview_size, - // setters - desktop_commands::webview_close, - desktop_commands::set_webview_size, - desktop_commands::set_webview_position, - desktop_commands::set_webview_focus, - desktop_commands::set_webview_auto_resize, - desktop_commands::set_webview_background_color, - desktop_commands::set_webview_zoom, - desktop_commands::webview_hide, - desktop_commands::webview_show, - desktop_commands::print, - desktop_commands::reparent, - desktop_commands::clear_all_browsing_data, - #[cfg(any(debug_assertions, feature = "devtools"))] - desktop_commands::internal_toggle_devtools, - ], - #[cfg(mobile)] - |invoke| { - invoke - .resolver - .reject("Webview API not available on mobile"); - true - }, - ) + .invoke_handler(crate::generate_handler![ + #![plugin(webview)] + create_webview_window, + get_all_webviews, + #[cfg(desktop)] desktop_commands::create_webview, + // getters + #[cfg(desktop)] desktop_commands::webview_position, + #[cfg(desktop)] desktop_commands::webview_size, + // setters + #[cfg(desktop)] desktop_commands::webview_close, + #[cfg(desktop)] desktop_commands::set_webview_size, + #[cfg(desktop)] desktop_commands::set_webview_position, + #[cfg(desktop)] desktop_commands::set_webview_focus, + #[cfg(desktop)] desktop_commands::set_webview_auto_resize, + #[cfg(desktop)] desktop_commands::set_webview_background_color, + #[cfg(desktop)] desktop_commands::set_webview_zoom, + #[cfg(desktop)] desktop_commands::webview_hide, + #[cfg(desktop)] desktop_commands::webview_show, + #[cfg(desktop)] desktop_commands::print, + #[cfg(desktop)] desktop_commands::clear_all_browsing_data, + #[cfg(desktop)] desktop_commands::reparent, + #[cfg(all(desktop, any(debug_assertions, feature = "devtools")))] + desktop_commands::internal_toggle_devtools, + ]) .build() } diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs index 6c6d025f2637..f649758421d2 100644 --- a/crates/tauri/src/webview/webview_window.rs +++ b/crates/tauri/src/webview/webview_window.rs @@ -13,7 +13,7 @@ use std::{ use crate::{ event::EventTarget, ipc::ScopeObject, - runtime::dpi::{PhysicalPosition, PhysicalSize}, + runtime::dpi::{PhysicalPosition, PhysicalSize, Position, Size}, webview::{NewWindowResponse, ScrollBarStyle}, window::Monitor, Emitter, EventName, Listener, ResourceTable, Window, @@ -22,11 +22,7 @@ use crate::{ use crate::{ image::Image, menu::{ContextMenu, Menu}, - runtime::{ - dpi::{Position, Size}, - window::CursorIcon, - UserAttentionType, - }, + runtime::{window::CursorIcon, UserAttentionType}, }; use tauri_runtime::webview::NewWindowFeatures; use tauri_utils::config::{BackgroundThrottlingPolicy, Color, WebviewUrl, WindowConfig}; @@ -465,44 +461,6 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { self } - /// The initial position of the window's. - #[must_use] - pub fn position(mut self, x: f64, y: f64) -> Self { - self.window_builder = self.window_builder.position(x, y); - self - } - - /// Window size. - #[must_use] - pub fn inner_size(mut self, width: f64, height: f64) -> Self { - self.window_builder = self.window_builder.inner_size(width, height); - self - } - - /// Window min inner size. - #[must_use] - pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self { - self.window_builder = self.window_builder.min_inner_size(min_width, min_height); - self - } - - /// Window max inner size. - #[must_use] - pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self { - self.window_builder = self.window_builder.max_inner_size(max_width, max_height); - self - } - - /// Window inner size constraints. - #[must_use] - pub fn inner_size_constraints( - mut self, - constraints: tauri_runtime::window::WindowSizeConstraints, - ) -> Self { - self.window_builder = self.window_builder.inner_size_constraints(constraints); - self - } - /// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size) /// on creation, which means the window size will be limited to `monitor size - taskbar size` /// @@ -531,14 +489,6 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { self } - /// Whether the window is resizable or not. - /// When resizable is set to false, native window's maximize button is automatically disabled. - #[must_use] - pub fn resizable(mut self, resizable: bool) -> Self { - self.window_builder = self.window_builder.resizable(resizable); - self - } - /// Whether the window's native maximize button is enabled or not. /// If resizable is set to false, this setting is ignored. /// @@ -576,13 +526,6 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { self } - /// The title of the window in the title bar. - #[must_use] - pub fn title>(mut self, title: S) -> Self { - self.window_builder = self.window_builder.title(title); - self - } - /// Whether to start the window in fullscreen or not. #[must_use] pub fn fullscreen(mut self, fullscreen: bool) -> Self { @@ -590,25 +533,6 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { self } - /// Sets the window to be initially focused. - #[must_use] - #[deprecated( - since = "1.2.0", - note = "The window is automatically focused by default. This function Will be removed in 3.0.0. Use `focused` instead." - )] - pub fn focus(mut self) -> Self { - self.window_builder = self.window_builder.focused(true); - self.webview_builder = self.webview_builder.focused(true); - self - } - - /// Whether the window will be focusable or not. - #[must_use] - pub fn focusable(mut self, focusable: bool) -> Self { - self.window_builder = self.window_builder.focusable(focusable); - self - } - /// Whether the window will be initially focused or not. #[must_use] pub fn focused(mut self, focused: bool) -> Self { @@ -624,24 +548,6 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { self } - /// Whether the window should be immediately visible upon creation. - #[must_use] - pub fn visible(mut self, visible: bool) -> Self { - self.window_builder = self.window_builder.visible(visible); - self - } - - /// Forces a theme or uses the system settings if None was provided. - /// - /// ## Platform-specific - /// - /// - **macOS**: Only supported on macOS 10.14+. - #[must_use] - pub fn theme(mut self, theme: Option) -> Self { - self.window_builder = self.window_builder.theme(theme); - self - } - /// Whether the window should have borders and bars. #[must_use] pub fn decorations(mut self, decorations: bool) -> Self { @@ -672,13 +578,6 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { self } - /// Prevents the window contents from being captured by other apps. - #[must_use] - pub fn content_protected(mut self, protected: bool) -> Self { - self.window_builder = self.window_builder.content_protected(protected); - self - } - /// Sets the window icon. pub fn icon(mut self, icon: Image<'a>) -> crate::Result { self.window_builder = self.window_builder.icon(icon)?; @@ -896,6 +795,106 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { } } +/// Window APIs. +impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { + /// The initial position of the window's. + #[must_use] + pub fn position(mut self, x: f64, y: f64) -> Self { + self.window_builder = self.window_builder.position(x, y); + self + } + + /// Window size. + #[must_use] + pub fn inner_size(mut self, width: f64, height: f64) -> Self { + self.window_builder = self.window_builder.inner_size(width, height); + self + } + + /// Window min inner size. + #[must_use] + pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self { + self.window_builder = self.window_builder.min_inner_size(min_width, min_height); + self + } + + /// Window max inner size. + #[must_use] + pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self { + self.window_builder = self.window_builder.max_inner_size(max_width, max_height); + self + } + + /// Window inner size constraints. + #[must_use] + pub fn inner_size_constraints( + mut self, + constraints: tauri_runtime::window::WindowSizeConstraints, + ) -> Self { + self.window_builder = self.window_builder.inner_size_constraints(constraints); + self + } + + /// Whether the window is resizable or not. + /// When resizable is set to false, native window's maximize button is automatically disabled. + #[must_use] + pub fn resizable(mut self, resizable: bool) -> Self { + self.window_builder = self.window_builder.resizable(resizable); + self + } + + /// The title of the window in the title bar. + #[must_use] + pub fn title>(mut self, title: S) -> Self { + self.window_builder = self.window_builder.title(title); + self + } + + /// Sets the window to be initially focused. + #[must_use] + #[deprecated( + since = "1.2.0", + note = "The window is automatically focused by default. This function Will be removed in 3.0.0. Use `focused` instead." + )] + pub fn focus(mut self) -> Self { + self.window_builder = self.window_builder.focused(true); + self.webview_builder = self.webview_builder.focused(true); + self + } + + /// Whether the window will be focusable or not. + #[must_use] + pub fn focusable(mut self, focusable: bool) -> Self { + self.window_builder = self.window_builder.focusable(focusable); + self + } + + /// Whether the window should be immediately visible upon creation. + #[must_use] + pub fn visible(mut self, visible: bool) -> Self { + self.window_builder = self.window_builder.visible(visible); + self + } + + /// Forces a theme or uses the system settings if None was provided. + /// + /// ## Platform-specific + /// + /// - **macOS**: Only supported on macOS 10.14+. + #[must_use] + pub fn theme(mut self, theme: Option) -> Self { + self.window_builder = self.window_builder.theme(theme); + self + } + + /// Prevents the window contents from being captured by other apps. + #[must_use] + pub fn content_protected(mut self, protected: bool) -> Self { + self.window_builder = self.window_builder.content_protected(protected); + self + } +} + /// Webview attributes. impl> WebviewWindowBuilder<'_, R, M> { /// Sets whether clicking an inactive window also clicks through to the webview. @@ -1897,17 +1896,6 @@ impl WebviewWindow { self.window.request_user_attention(request_type) } - /// Determines if this window should be resizable. - /// When resizable is set to false, native window's maximize button is automatically disabled. - pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> { - self.window.set_resizable(resizable) - } - - /// Enable or disable the window. - pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> { - self.webview.window().set_enabled(enabled) - } - /// Determines if this window's native maximize button should be enabled. /// If resizable is set to false, this setting is ignored. /// @@ -1939,11 +1927,6 @@ impl WebviewWindow { self.window.set_closable(closable) } - /// Set this window's title. - pub fn set_title(&self, title: &str) -> crate::Result<()> { - self.window.set_title(title) - } - /// Maximizes this window. pub fn maximize(&self) -> crate::Result<()> { self.window.maximize() @@ -1964,26 +1947,6 @@ impl WebviewWindow { self.window.unminimize() } - /// Show this window. - pub fn show(&self) -> crate::Result<()> { - self.window.show() - } - - /// Hide this window. - pub fn hide(&self) -> crate::Result<()> { - self.window.hide() - } - - /// Closes this window. It emits [`crate::RunEvent::CloseRequested`] first like a user-initiated close request so you can intercept it. - pub fn close(&self) -> crate::Result<()> { - self.window.close() - } - - /// Destroys this window. Similar to [`Self::close`] but does not emit any events and force close the window instead. - pub fn destroy(&self) -> crate::Result<()> { - self.window.destroy() - } - /// Determines if this window should be [decorated]. /// /// [decorated]: https://en.wikipedia.org/wiki/Window_(computing)#Window_decoration @@ -2059,79 +2022,16 @@ impl WebviewWindow { .set_visible_on_all_workspaces(visible_on_all_workspaces) } - /// Prevents the window contents from being captured by other apps. - pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> { - self.window.set_content_protected(protected) - } - - /// Resizes this window. - pub fn set_size>(&self, size: S) -> crate::Result<()> { - self.window.set_size(size.into()) - } - - /// Sets this window's minimum inner size. - pub fn set_min_size>(&self, size: Option) -> crate::Result<()> { - self.window.set_min_size(size.map(|s| s.into())) - } - - /// Sets this window's maximum inner size. - pub fn set_max_size>(&self, size: Option) -> crate::Result<()> { - self.window.set_max_size(size.map(|s| s.into())) - } - - /// Sets this window's minimum inner width. - pub fn set_size_constraints( - &self, - constraints: tauri_runtime::window::WindowSizeConstraints, - ) -> crate::Result<()> { - self.window.set_size_constraints(constraints) - } - - /// Sets this window's position. - pub fn set_position>(&self, position: Pos) -> crate::Result<()> { - self.window.set_position(position) - } - /// Determines if this window should be fullscreen. pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> { self.window.set_fullscreen(fullscreen) } - /// Bring the window to front and focus. - pub fn set_focus(&self) -> crate::Result<()> { - self.window.set_focus() - } - - /// Sets whether the window can be focused. - /// - /// ## Platform-specific - /// - /// - **macOS**: If the window is already focused, it is not possible to unfocus it after calling `set_focusable(false)`. - /// In this case, you might consider calling [`Window::set_focus`] but it will move the window to the back i.e. at the bottom in terms of z-order. - pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> { - self.window.set_focusable(focusable) - } - /// Sets this window' icon. pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> { self.window.set_icon(icon) } - /// Sets the window background color. - /// - /// ## Platform-specific: - /// - /// - **iOS / Android:** Unsupported. - /// - **macOS**: Not implemented for the webview layer.. - /// - **Windows**: - /// - alpha channel is ignored for the window layer. - /// - On Windows 7, transparency is not supported and the alpha value will be ignored for the webview layer.. - /// - On Windows 8 and newer: translucent colors are not supported so any alpha value other than `0` will be replaced by `255` for the webview layer. - pub fn set_background_color(&self, color: Option) -> crate::Result<()> { - self.window.set_background_color(color)?; - self.webview.set_background_color(color) - } - /// Whether to hide the window icon from the taskbar or not. /// /// ## Platform-specific @@ -2231,6 +2131,108 @@ impl WebviewWindow { pub fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> crate::Result<()> { self.window.set_title_bar_style(style) } +} + +/// Desktop window setters and actions. +impl WebviewWindow { + /// Determines if this window should be resizable. + /// When resizable is set to false, native window's maximize button is automatically disabled. + pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> { + self.window.set_resizable(resizable) + } + + /// Enable or disable the window. + pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> { + self.webview.window().set_enabled(enabled) + } + + /// Set this window's title. + pub fn set_title(&self, title: &str) -> crate::Result<()> { + self.window.set_title(title) + } + + /// Show this window. + pub fn show(&self) -> crate::Result<()> { + self.window.show() + } + + /// Hide this window. + pub fn hide(&self) -> crate::Result<()> { + self.window.hide() + } + + /// Closes this window. It emits [`crate::RunEvent::CloseRequested`] first like a user-initiated close request so you can intercept it. + pub fn close(&self) -> crate::Result<()> { + self.window.close() + } + + /// Destroys this window. Similar to [`Self::close`] but does not emit any events and force close the window instead. + pub fn destroy(&self) -> crate::Result<()> { + self.window.destroy() + } + + /// Prevents the window contents from being captured by other apps. + pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> { + self.window.set_content_protected(protected) + } + + /// Resizes this window. + pub fn set_size>(&self, size: S) -> crate::Result<()> { + self.window.set_size(size.into()) + } + + /// Sets this window's minimum inner size. + pub fn set_min_size>(&self, size: Option) -> crate::Result<()> { + self.window.set_min_size(size.map(|s| s.into())) + } + + /// Sets this window's maximum inner size. + pub fn set_max_size>(&self, size: Option) -> crate::Result<()> { + self.window.set_max_size(size.map(|s| s.into())) + } + + /// Sets this window's minimum inner width. + pub fn set_size_constraints( + &self, + constraints: tauri_runtime::window::WindowSizeConstraints, + ) -> crate::Result<()> { + self.window.set_size_constraints(constraints) + } + + /// Sets this window's position. + pub fn set_position>(&self, position: Pos) -> crate::Result<()> { + self.window.set_position(position) + } + + /// Bring the window to front and focus. + pub fn set_focus(&self) -> crate::Result<()> { + self.window.set_focus() + } + + /// Sets whether the window can be focused. + /// + /// ## Platform-specific + /// + /// - **macOS**: If the window is already focused, it is not possible to unfocus it after calling `set_focusable(false)`. + /// In this case, you might consider calling [`Window::set_focus`] but it will move the window to the back i.e. at the bottom in terms of z-order. + pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> { + self.window.set_focusable(focusable) + } + + /// Sets the window background color. + /// + /// ## Platform-specific: + /// + /// - **iOS / Android:** Unsupported. + /// - **macOS**: Not implemented for the webview layer.. + /// - **Windows**: + /// - alpha channel is ignored for the window layer. + /// - On Windows 7, transparency is not supported and the alpha value will be ignored for the webview layer.. + /// - On Windows 8 and newer: translucent colors are not supported so any alpha value other than `0` will be replaced by `255` for the webview layer. + pub fn set_background_color(&self, color: Option) -> crate::Result<()> { + self.window.set_background_color(color)?; + self.webview.set_background_color(color) + } /// Sets the theme for this window. /// @@ -2243,7 +2245,7 @@ impl WebviewWindow { } } -/// Desktop webview setters and actions. +/// Desktop webview APIs. #[cfg(desktop)] impl WebviewWindow { /// Opens the dialog to prints the contents of the webview. @@ -2616,7 +2618,7 @@ impl ManagerBase for WebviewWindow { } fn runtime(&self) -> RuntimeOrDispatch<'_, R> { - self.webview.runtime() + self.window.runtime() } fn managed_app_handle(&self) -> &AppHandle { diff --git a/crates/tauri/src/window/mod.rs b/crates/tauri/src/window/mod.rs index d9d4a930437c..b1b367d53a9b 100644 --- a/crates/tauri/src/window/mod.rs +++ b/crates/tauri/src/window/mod.rs @@ -21,6 +21,7 @@ use crate::{ ipc::{CommandArg, CommandItem, InvokeError}, manager::{AppManager, EmitPayload}, runtime::{ + dpi::{Position, Size}, monitor::Monitor as RuntimeMonitor, window::{DetachedWindow, PendingWindow, WindowBuilder as _}, RuntimeHandle, WindowDispatch, @@ -35,10 +36,7 @@ use crate::{ use crate::{ image::Image, menu::{ContextMenu, Menu, MenuId}, - runtime::{ - dpi::{Position, Size}, - UserAttentionType, - }, + runtime::UserAttentionType, CursorIcon, }; @@ -456,7 +454,7 @@ tauri::Builder::default() } } -/// Desktop APIs. +/// Desktop APIs #[cfg(desktop)] #[cfg_attr(not(feature = "unstable"), allow(dead_code))] impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { @@ -474,44 +472,6 @@ impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { self } - /// The initial position of the window's. - #[must_use] - pub fn position(mut self, x: f64, y: f64) -> Self { - self.window_builder = self.window_builder.position(x, y); - self - } - - /// Window size. - #[must_use] - pub fn inner_size(mut self, width: f64, height: f64) -> Self { - self.window_builder = self.window_builder.inner_size(width, height); - self - } - - /// Window min inner size. - #[must_use] - pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self { - self.window_builder = self.window_builder.min_inner_size(min_width, min_height); - self - } - - /// Window max inner size. - #[must_use] - pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self { - self.window_builder = self.window_builder.max_inner_size(max_width, max_height); - self - } - - /// Window inner size constraints. - #[must_use] - pub fn inner_size_constraints( - mut self, - constraints: tauri_runtime::window::WindowSizeConstraints, - ) -> Self { - self.window_builder = self.window_builder.inner_size_constraints(constraints); - self - } - /// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size) /// on creation, which means the window size will be limited to `monitor size - taskbar size` /// @@ -542,14 +502,6 @@ impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { self } - /// Whether the window is resizable or not. - /// When resizable is set to false, native window's maximize button is automatically disabled. - #[must_use] - pub fn resizable(mut self, resizable: bool) -> Self { - self.window_builder = self.window_builder.resizable(resizable); - self - } - /// Whether the window's native maximize button is enabled or not. /// If resizable is set to false, this setting is ignored. /// @@ -587,13 +539,6 @@ impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { self } - /// The title of the window in the title bar. - #[must_use] - pub fn title>(mut self, title: S) -> Self { - self.window_builder = self.window_builder.title(title); - self - } - /// Whether to start the window in fullscreen or not. #[must_use] pub fn fullscreen(mut self, fullscreen: bool) -> Self { @@ -601,31 +546,6 @@ impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { self } - /// Sets the window to be initially focused. - #[must_use] - #[deprecated( - since = "1.2.0", - note = "The window is automatically focused by default. This function Will be removed in 3.0.0. Use `focused` instead." - )] - pub fn focus(mut self) -> Self { - self.window_builder = self.window_builder.focused(true); - self - } - - /// Whether the window will be initially focused or not. - #[must_use] - pub fn focused(mut self, focused: bool) -> Self { - self.window_builder = self.window_builder.focused(focused); - self - } - - /// Whether the window will be focusable or not. - #[must_use] - pub fn focusable(mut self, focusable: bool) -> Self { - self.window_builder = self.window_builder.focusable(focusable); - self - } - /// Whether the window should be maximized upon creation. #[must_use] pub fn maximized(mut self, maximized: bool) -> Self { @@ -633,37 +553,6 @@ impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { self } - /// Whether the window should be immediately visible upon creation. - #[must_use] - pub fn visible(mut self, visible: bool) -> Self { - self.window_builder = self.window_builder.visible(visible); - self - } - - /// Forces a theme or uses the system settings if None was provided. - /// - /// ## Platform-specific - /// - /// - **macOS**: Only supported on macOS 10.14+. - #[must_use] - pub fn theme(mut self, theme: Option) -> Self { - self.window_builder = self.window_builder.theme(theme); - self - } - - /// Whether the window should be transparent. If this is true, writing colors - /// with alpha values different than `1.0` will produce a transparent window. - #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))] - #[cfg_attr( - docsrs, - doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api"))) - )] - #[must_use] - pub fn transparent(mut self, transparent: bool) -> Self { - self.window_builder = self.window_builder.transparent(transparent); - self - } - /// Whether the window should have borders and bars. #[must_use] pub fn decorations(mut self, decorations: bool) -> Self { @@ -698,13 +587,6 @@ impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { self } - /// Prevents the window contents from being captured by other apps. - #[must_use] - pub fn content_protected(mut self, protected: bool) -> Self { - self.window_builder = self.window_builder.content_protected(protected); - self - } - /// Sets the window icon. pub fn icon(mut self, icon: Image<'a>) -> crate::Result { self.window_builder = self.window_builder.icon(icon.into())?; @@ -923,7 +805,125 @@ impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { } } -impl> WindowBuilder<'_, R, M> { +/// Window APIs. +#[cfg_attr(not(feature = "unstable"), allow(dead_code))] +impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { + /// The initial position of the window's. + #[must_use] + pub fn position(mut self, x: f64, y: f64) -> Self { + self.window_builder = self.window_builder.position(x, y); + self + } + + /// Window size. + #[must_use] + pub fn inner_size(mut self, width: f64, height: f64) -> Self { + self.window_builder = self.window_builder.inner_size(width, height); + self + } + + /// Window min inner size. + #[must_use] + pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self { + self.window_builder = self.window_builder.min_inner_size(min_width, min_height); + self + } + + /// Window max inner size. + #[must_use] + pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self { + self.window_builder = self.window_builder.max_inner_size(max_width, max_height); + self + } + + /// Window inner size constraints. + #[must_use] + pub fn inner_size_constraints( + mut self, + constraints: tauri_runtime::window::WindowSizeConstraints, + ) -> Self { + self.window_builder = self.window_builder.inner_size_constraints(constraints); + self + } + + /// Whether the window is resizable or not. + /// When resizable is set to false, native window's maximize button is automatically disabled. + #[must_use] + pub fn resizable(mut self, resizable: bool) -> Self { + self.window_builder = self.window_builder.resizable(resizable); + self + } + + /// The title of the window in the title bar. + #[must_use] + pub fn title>(mut self, title: S) -> Self { + self.window_builder = self.window_builder.title(title); + self + } + + /// Sets the window to be initially focused. + #[must_use] + #[deprecated( + since = "1.2.0", + note = "The window is automatically focused by default. This function Will be removed in 3.0.0. Use `focused` instead." + )] + pub fn focus(mut self) -> Self { + self.window_builder = self.window_builder.focused(true); + self + } + + /// Whether the window will be initially focused or not. + #[must_use] + pub fn focused(mut self, focused: bool) -> Self { + self.window_builder = self.window_builder.focused(focused); + self + } + + /// Whether the window will be focusable or not. + #[must_use] + pub fn focusable(mut self, focusable: bool) -> Self { + self.window_builder = self.window_builder.focusable(focusable); + self + } + + /// Whether the window should be immediately visible upon creation. + #[must_use] + pub fn visible(mut self, visible: bool) -> Self { + self.window_builder = self.window_builder.visible(visible); + self + } + + /// Forces a theme or uses the system settings if None was provided. + /// + /// ## Platform-specific + /// + /// - **macOS**: Only supported on macOS 10.14+. + #[must_use] + pub fn theme(mut self, theme: Option) -> Self { + self.window_builder = self.window_builder.theme(theme); + self + } + + /// Whether the window should be transparent. If this is true, writing colors + /// with alpha values different than `1.0` will produce a transparent window. + #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))] + #[cfg_attr( + docsrs, + doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api"))) + )] + #[must_use] + pub fn transparent(mut self, transparent: bool) -> Self { + self.window_builder = self.window_builder.transparent(transparent); + self + } + + /// Prevents the window contents from being captured by other apps. + #[must_use] + pub fn content_protected(mut self, protected: bool) -> Self { + self.window_builder = self.window_builder.content_protected(protected); + self + } + /// Set the window and webview background color. /// /// ## Platform-specific: @@ -1738,18 +1738,188 @@ impl Window { } } -/// Desktop window setters and actions. -#[cfg(desktop)] +/// Window setters and actions. impl Window { - /// Centers the window. - pub fn center(&self) -> crate::Result<()> { - self.window.dispatcher.center().map_err(Into::into) + /// Determines if this window should be resizable. + /// When resizable is set to false, native window's maximize button is automatically disabled. + pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> { + self + .window + .dispatcher + .set_resizable(resizable) + .map_err(Into::into) } - /// Requests user attention to the window, this has no effect if the application - /// is already focused. How requesting for user attention manifests is platform dependent, - /// see `UserAttentionType` for details. - /// + /// Set this window's title. + pub fn set_title(&self, title: &str) -> crate::Result<()> { + self + .window + .dispatcher + .set_title(title.to_string()) + .map_err(Into::into) + } + + /// Enable or disable the window. + pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> { + self + .window + .dispatcher + .set_enabled(enabled) + .map_err(Into::into) + } + + /// Show this window. + pub fn show(&self) -> crate::Result<()> { + self.window.dispatcher.show().map_err(Into::into) + } + + /// Hide this window. + pub fn hide(&self) -> crate::Result<()> { + self.window.dispatcher.hide().map_err(Into::into) + } + + /// Closes this window. It emits [`crate::RunEvent::CloseRequested`] first like a user-initiated close request so you can intercept it. + pub fn close(&self) -> crate::Result<()> { + self.window.dispatcher.close().map_err(Into::into) + } + + /// Destroys this window. Similar to [`Self::close`] but does not emit any events and force close the window instead. + pub fn destroy(&self) -> crate::Result<()> { + self.window.dispatcher.destroy().map_err(Into::into) + } + + /// Sets the window background color. + /// + /// ## Platform-specific: + /// + /// - **Windows:** alpha channel is ignored. + /// - **iOS / Android:** Unsupported. + pub fn set_background_color(&self, color: Option) -> crate::Result<()> { + self + .window + .dispatcher + .set_background_color(color) + .map_err(Into::into) + } + + /// Prevents the window contents from being captured by other apps. + pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> { + self + .window + .dispatcher + .set_content_protected(protected) + .map_err(Into::into) + } + + /// Resizes this window. + pub fn set_size>(&self, size: S) -> crate::Result<()> { + self + .window + .dispatcher + .set_size(size.into()) + .map_err(Into::into) + } + + /// Sets this window's minimum inner size. + pub fn set_min_size>(&self, size: Option) -> crate::Result<()> { + self + .window + .dispatcher + .set_min_size(size.map(|s| s.into())) + .map_err(Into::into) + } + + /// Sets this window's maximum inner size. + pub fn set_max_size>(&self, size: Option) -> crate::Result<()> { + self + .window + .dispatcher + .set_max_size(size.map(|s| s.into())) + .map_err(Into::into) + } + + /// Sets this window's minimum inner width. + pub fn set_size_constraints( + &self, + constraints: tauri_runtime::window::WindowSizeConstraints, + ) -> crate::Result<()> { + self + .window + .dispatcher + .set_size_constraints(constraints) + .map_err(Into::into) + } + + /// Sets this window's position. + pub fn set_position>(&self, position: Pos) -> crate::Result<()> { + self + .window + .dispatcher + .set_position(position.into()) + .map_err(Into::into) + } + + /// Bring the window to front and focus. + pub fn set_focus(&self) -> crate::Result<()> { + self.window.dispatcher.set_focus().map_err(Into::into) + } + + /// Sets whether the window can be focused. + /// + /// ## Platform-specific + /// + /// - **macOS**: If the window is already focused, it is not possible to unfocus it after calling `set_focusable(false)`. + /// In this case, you might consider calling [`Window::set_focus`] but it will move the window to the back i.e. at the bottom in terms of z-order. + pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> { + self + .window + .dispatcher + .set_focusable(focusable) + .map_err(Into::into) + } + + /// Sets the theme for this window. + /// + /// ## Platform-specific + /// + /// - **Linux / macOS**: Theme is app-wide and not specific to this window. + /// - **iOS / Android:** Unsupported. + pub fn set_theme(&self, theme: Option) -> crate::Result<()> { + self + .window + .dispatcher + .set_theme(theme) + .map_err(Into::::into)?; + #[cfg(windows)] + if let (Some(menu), Ok(hwnd)) = (self.menu(), self.hwnd()) { + let raw_hwnd = hwnd.0 as isize; + self.run_on_main_thread(move || { + let _ = unsafe { + menu.inner().set_theme_for_hwnd( + raw_hwnd, + theme + .map(crate::menu::map_to_menu_theme) + .unwrap_or(muda::MenuTheme::Auto), + ) + }; + })?; + }; + Ok(()) + } +} + +/// Desktop window setters and actions. +#[cfg(desktop)] +impl Window { + /// Centers the window. + pub fn center(&self) -> crate::Result<()> { + self.window.dispatcher.center().map_err(Into::into) + } + + /// Requests user attention to the window, this has no effect if the application + /// is already focused. How requesting for user attention manifests is platform dependent, + /// see `UserAttentionType` for details. + /// /// Providing `None` will unset the request for user attention. Unsetting the request for /// user attention might not be done automatically by the WM when the window receives input. /// @@ -1768,16 +1938,6 @@ impl Window { .map_err(Into::into) } - /// Determines if this window should be resizable. - /// When resizable is set to false, native window's maximize button is automatically disabled. - pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> { - self - .window - .dispatcher - .set_resizable(resizable) - .map_err(Into::into) - } - /// Determines if this window's native maximize button should be enabled. /// If resizable is set to false, this setting is ignored. /// @@ -1821,24 +1981,6 @@ impl Window { .map_err(Into::into) } - /// Set this window's title. - pub fn set_title(&self, title: &str) -> crate::Result<()> { - self - .window - .dispatcher - .set_title(title.to_string()) - .map_err(Into::into) - } - - /// Enable or disable the window. - pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> { - self - .window - .dispatcher - .set_enabled(enabled) - .map_err(Into::into) - } - /// Maximizes this window. pub fn maximize(&self) -> crate::Result<()> { self.window.dispatcher.maximize().map_err(Into::into) @@ -1859,26 +2001,6 @@ impl Window { self.window.dispatcher.unminimize().map_err(Into::into) } - /// Show this window. - pub fn show(&self) -> crate::Result<()> { - self.window.dispatcher.show().map_err(Into::into) - } - - /// Hide this window. - pub fn hide(&self) -> crate::Result<()> { - self.window.dispatcher.hide().map_err(Into::into) - } - - /// Closes this window. It emits [`crate::RunEvent::CloseRequested`] first like a user-initiated close request so you can intercept it. - pub fn close(&self) -> crate::Result<()> { - self.window.dispatcher.close().map_err(Into::into) - } - - /// Destroys this window. Similar to [`Self::close`] but does not emit any events and force close the window instead. - pub fn destroy(&self) -> crate::Result<()> { - self.window.dispatcher.destroy().map_err(Into::into) - } - /// Determines if this window should be [decorated]. /// /// [decorated]: https://en.wikipedia.org/wiki/Window_(computing)#Window_decoration @@ -1981,77 +2103,6 @@ tauri::Builder::default() .map_err(Into::into) } - /// Sets the window background color. - /// - /// ## Platform-specific: - /// - /// - **Windows:** alpha channel is ignored. - /// - **iOS / Android:** Unsupported. - pub fn set_background_color(&self, color: Option) -> crate::Result<()> { - self - .window - .dispatcher - .set_background_color(color) - .map_err(Into::into) - } - - /// Prevents the window contents from being captured by other apps. - pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> { - self - .window - .dispatcher - .set_content_protected(protected) - .map_err(Into::into) - } - - /// Resizes this window. - pub fn set_size>(&self, size: S) -> crate::Result<()> { - self - .window - .dispatcher - .set_size(size.into()) - .map_err(Into::into) - } - - /// Sets this window's minimum inner size. - pub fn set_min_size>(&self, size: Option) -> crate::Result<()> { - self - .window - .dispatcher - .set_min_size(size.map(|s| s.into())) - .map_err(Into::into) - } - - /// Sets this window's maximum inner size. - pub fn set_max_size>(&self, size: Option) -> crate::Result<()> { - self - .window - .dispatcher - .set_max_size(size.map(|s| s.into())) - .map_err(Into::into) - } - - /// Sets this window's minimum inner width. - pub fn set_size_constraints( - &self, - constraints: tauri_runtime::window::WindowSizeConstraints, - ) -> crate::Result<()> { - self - .window - .dispatcher - .set_size_constraints(constraints) - .map_err(Into::into) - } - - /// Sets this window's position. - pub fn set_position>(&self, position: Pos) -> crate::Result<()> { - self - .window - .dispatcher - .set_position(position.into()) - .map_err(Into::into) - } - /// Determines if this window should be fullscreen. pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> { self @@ -2082,25 +2133,6 @@ tauri::Builder::default() self.set_fullscreen(fullscreen) } - /// Bring the window to front and focus. - pub fn set_focus(&self) -> crate::Result<()> { - self.window.dispatcher.set_focus().map_err(Into::into) - } - - /// Sets whether the window can be focused. - /// - /// ## Platform-specific - /// - /// - **macOS**: If the window is already focused, it is not possible to unfocus it after calling `set_focusable(false)`. - /// In this case, you might consider calling [`Window::set_focus`] but it will move the window to the back i.e. at the bottom in terms of z-order. - pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> { - self - .window - .dispatcher - .set_focusable(focusable) - .map_err(Into::into) - } - /// Sets this window' icon. pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> { self @@ -2266,35 +2298,6 @@ tauri::Builder::default() .set_title_bar_style(style) .map_err(Into::into) } - - /// Sets the theme for this window. - /// - /// ## Platform-specific - /// - /// - **Linux / macOS**: Theme is app-wide and not specific to this window. - /// - **iOS / Android:** Unsupported. - pub fn set_theme(&self, theme: Option) -> crate::Result<()> { - self - .window - .dispatcher - .set_theme(theme) - .map_err(Into::::into)?; - #[cfg(windows)] - if let (Some(menu), Ok(hwnd)) = (self.menu(), self.hwnd()) { - let raw_hwnd = hwnd.0 as isize; - self.run_on_main_thread(move || { - let _ = unsafe { - menu.inner().set_theme_for_hwnd( - raw_hwnd, - theme - .map(crate::menu::map_to_menu_theme) - .unwrap_or(muda::MenuTheme::Auto), - ) - }; - })?; - }; - Ok(()) - } } /// Progress bar state. diff --git a/crates/tauri/src/window/plugin.rs b/crates/tauri/src/window/plugin.rs index f8066092274b..4fba9e72e9cd 100644 --- a/crates/tauri/src/window/plugin.rs +++ b/crates/tauri/src/window/plugin.rs @@ -6,23 +6,56 @@ use crate::{ plugin::{Builder, TauriPlugin}, - Runtime, + sealed::ManagerBase, + Runtime, Window, }; -#[cfg(desktop)] -mod desktop_commands { - use tauri_runtime::{window::WindowSizeConstraints, ResizeDirection}; - use tauri_utils::TitleBarStyle; +fn get_window(window: Window, label: Option) -> crate::Result> { + match label { + Some(l) if !l.is_empty() => window + .manager() + .get_window(&l) + .ok_or(crate::Error::WindowNotFound), + _ => Ok(window), + } +} +macro_rules! getter { + ($cmd: ident, $ret: ty) => { + #[command(root = "crate")] + pub async fn $cmd(window: Window, label: Option) -> crate::Result<$ret> { + get_window(window, label)?.$cmd().map_err(Into::into) + } + }; +} + +macro_rules! setter { + ($cmd: ident) => { + #[command(root = "crate")] + pub async fn $cmd(window: Window, label: Option) -> crate::Result<()> { + get_window(window, label)?.$cmd().map_err(Into::into) + } + }; + + ($cmd: ident, $input: ty) => { + #[command(root = "crate")] + pub async fn $cmd( + window: Window, + label: Option, + value: $input, + ) -> crate::Result<()> { + get_window(window, label)?.$cmd(value).map_err(Into::into) + } + }; +} + +mod commands { + use tauri_runtime::window::WindowSizeConstraints; use super::*; use crate::{ - command, - sealed::ManagerBase, - utils::config::{WindowConfig, WindowEffectsConfig}, - window::Color, - window::{ProgressBarState, WindowBuilder}, - AppHandle, CursorIcon, Manager, Monitor, PhysicalPosition, PhysicalSize, Position, Size, Theme, - UserAttentionType, Webview, Window, + command, sealed::ManagerBase, utils::config::WindowConfig, window::Color, + window::WindowBuilder, AppHandle, PhysicalPosition, PhysicalSize, Position, Size, Theme, + Window, }; #[command(root = "crate")] @@ -36,109 +69,84 @@ mod desktop_commands { Ok(()) } - fn get_window(window: Window, label: Option) -> crate::Result> { - match label { - Some(l) if !l.is_empty() => window - .manager() - .get_window(&l) - .ok_or(crate::Error::WindowNotFound), - _ => Ok(window), - } - } - - macro_rules! getter { - ($cmd: ident, $ret: ty) => { - #[command(root = "crate")] - pub async fn $cmd( - window: Window, - label: Option, - ) -> crate::Result<$ret> { - get_window(window, label)?.$cmd().map_err(Into::into) - } - }; - } - - macro_rules! setter { - ($cmd: ident) => { - #[command(root = "crate")] - pub async fn $cmd(window: Window, label: Option) -> crate::Result<()> { - get_window(window, label)?.$cmd().map_err(Into::into) - } - }; - - ($cmd: ident, $input: ty) => { - #[command(root = "crate")] - pub async fn $cmd( - window: Window, - label: Option, - value: $input, - ) -> crate::Result<()> { - get_window(window, label)?.$cmd(value).map_err(Into::into) - } - }; - } - getter!(scale_factor, f64); getter!(inner_position, PhysicalPosition); getter!(outer_position, PhysicalPosition); getter!(inner_size, PhysicalSize); getter!(outer_size, PhysicalSize); + getter!(is_focused, bool); + getter!(is_resizable, bool); + getter!(is_visible, bool); + getter!(is_enabled, bool); + getter!(title, String); + getter!(theme, Theme); + #[cfg(target_os = "android")] + getter!(activity_name, String); + #[cfg(target_os = "ios")] + getter!(scene_identifier, String); + + setter!(set_resizable, bool); + setter!(set_title, &str); + setter!(show); + setter!(hide); + setter!(close); + setter!(destroy); + setter!(set_content_protected, bool); + setter!(set_size, Size); + setter!(set_min_size, Option); + setter!(set_max_size, Option); + setter!(set_position, Position); + setter!(set_focus); + setter!(set_focusable, bool); + setter!(set_background_color, Option); + setter!(set_size_constraints, WindowSizeConstraints); + setter!(set_theme, Option); + setter!(set_enabled, bool); +} + +#[cfg(desktop)] +mod desktop_commands { + use tauri_runtime::ResizeDirection; + use tauri_utils::TitleBarStyle; + + use super::*; + use crate::{ + command, utils::config::WindowEffectsConfig, window::ProgressBarState, CursorIcon, Manager, + Monitor, PhysicalPosition, Position, UserAttentionType, Webview, + }; + getter!(is_fullscreen, bool); getter!(is_minimized, bool); getter!(is_maximized, bool); - getter!(is_focused, bool); getter!(is_decorated, bool); - getter!(is_resizable, bool); getter!(is_maximizable, bool); getter!(is_minimizable, bool); getter!(is_closable, bool); - getter!(is_visible, bool); - getter!(is_enabled, bool); - getter!(title, String); getter!(current_monitor, Option); getter!(primary_monitor, Option); getter!(available_monitors, Vec); getter!(cursor_position, PhysicalPosition); - getter!(theme, Theme); getter!(is_always_on_top, bool); - #[cfg(target_os = "android")] - getter!(activity_name, String); - #[cfg(target_os = "ios")] - getter!(scene_identifier, String); setter!(center); setter!(request_user_attention, Option); - setter!(set_resizable, bool); setter!(set_maximizable, bool); setter!(set_minimizable, bool); setter!(set_closable, bool); - setter!(set_title, &str); setter!(maximize); setter!(unmaximize); setter!(minimize); setter!(unminimize); - setter!(show); - setter!(hide); - setter!(close); - setter!(destroy); setter!(set_decorations, bool); setter!(set_shadow, bool); setter!(set_effects, Option); setter!(set_always_on_top, bool); setter!(set_always_on_bottom, bool); - setter!(set_content_protected, bool); - setter!(set_size, Size); - setter!(set_min_size, Option); - setter!(set_max_size, Option); - setter!(set_position, Position); setter!(set_fullscreen, bool); setter!(set_simple_fullscreen, bool); - setter!(set_focus); - setter!(set_focusable, bool); setter!(set_skip_taskbar, bool); setter!(set_cursor_grab, bool); setter!(set_cursor_visible, bool); - setter!(set_background_color, Option); setter!(set_cursor_icon, CursorIcon); setter!(set_cursor_position, Position); setter!(set_ignore_cursor_events, bool); @@ -150,9 +158,6 @@ mod desktop_commands { setter!(set_badge_label, Option); setter!(set_visible_on_all_workspaces, bool); setter!(set_title_bar_style, TitleBarStyle); - setter!(set_size_constraints, WindowSizeConstraints); - setter!(set_theme, Option); - setter!(set_enabled, bool); #[command(root = "crate")] #[cfg(target_os = "windows")] @@ -248,96 +253,94 @@ pub fn init() -> TauriPlugin { Builder::new("window") .js_init_script(init_script) - .invoke_handler( - #[cfg(desktop)] - crate::generate_handler![ - #![plugin(window)] - desktop_commands::create, - // getters - desktop_commands::get_all_windows, - desktop_commands::scale_factor, - desktop_commands::inner_position, - desktop_commands::outer_position, - desktop_commands::inner_size, - desktop_commands::outer_size, - desktop_commands::is_fullscreen, - desktop_commands::is_minimized, - desktop_commands::is_maximized, - desktop_commands::is_focused, - desktop_commands::is_decorated, - desktop_commands::is_resizable, - desktop_commands::is_maximizable, - desktop_commands::is_minimizable, - desktop_commands::is_closable, - desktop_commands::is_visible, - desktop_commands::is_enabled, - desktop_commands::title, - desktop_commands::current_monitor, - desktop_commands::primary_monitor, - desktop_commands::monitor_from_point, - desktop_commands::available_monitors, - desktop_commands::cursor_position, - desktop_commands::theme, - desktop_commands::is_always_on_top, - // setters - desktop_commands::center, - desktop_commands::request_user_attention, - desktop_commands::set_resizable, - desktop_commands::set_maximizable, - desktop_commands::set_minimizable, - desktop_commands::set_closable, - desktop_commands::set_title, - desktop_commands::maximize, - desktop_commands::unmaximize, - desktop_commands::minimize, - desktop_commands::unminimize, - desktop_commands::show, - desktop_commands::hide, - desktop_commands::close, - desktop_commands::destroy, - desktop_commands::set_decorations, - desktop_commands::set_shadow, - desktop_commands::set_effects, - desktop_commands::set_always_on_top, - desktop_commands::set_always_on_bottom, - desktop_commands::set_content_protected, - desktop_commands::set_size, - desktop_commands::set_min_size, - desktop_commands::set_max_size, - desktop_commands::set_size_constraints, - desktop_commands::set_position, - desktop_commands::set_fullscreen, - desktop_commands::set_simple_fullscreen, - desktop_commands::set_focus, - desktop_commands::set_focusable, - desktop_commands::set_enabled, - desktop_commands::set_skip_taskbar, - desktop_commands::set_cursor_grab, - desktop_commands::set_cursor_visible, - desktop_commands::set_cursor_icon, - desktop_commands::set_cursor_position, - desktop_commands::set_ignore_cursor_events, - desktop_commands::start_dragging, - desktop_commands::start_resize_dragging, - desktop_commands::set_badge_count, - #[cfg(target_os = "macos")] - desktop_commands::set_badge_label, - desktop_commands::set_progress_bar, - #[cfg(target_os = "windows")] - desktop_commands::set_overlay_icon, - desktop_commands::set_icon, - desktop_commands::set_visible_on_all_workspaces, - desktop_commands::set_background_color, - desktop_commands::set_title_bar_style, - desktop_commands::set_theme, - desktop_commands::toggle_maximize, - desktop_commands::internal_toggle_maximize, - ], - #[cfg(mobile)] - |invoke| { - invoke.resolver.reject("Window API not available on mobile"); - true - }, - ) + .invoke_handler(crate::generate_handler![ + #![plugin(window)] + commands::create, + // getters + commands::get_all_windows, + commands::scale_factor, + commands::inner_position, + commands::outer_position, + commands::inner_size, + commands::outer_size, + commands::is_focused, + commands::is_resizable, + commands::is_visible, + commands::is_enabled, + commands::title, + commands::theme, + #[cfg(target_os = "android")] + commands::activity_name, + #[cfg(target_os = "ios")] + commands::scene_identifier, + + commands::set_resizable, + commands::set_title, + commands::show, + commands::hide, + commands::close, + commands::destroy, + commands::set_content_protected, + commands::set_size, + commands::set_min_size, + commands::set_max_size, + commands::set_position, + commands::set_size_constraints, + commands::set_focus, + commands::set_focusable, + commands::set_enabled, + commands::set_background_color, + commands::set_theme, + + #[cfg(desktop)] desktop_commands::is_fullscreen, + #[cfg(desktop)] desktop_commands::is_minimized, + #[cfg(desktop)] desktop_commands::is_maximized, + #[cfg(desktop)] desktop_commands::is_decorated, + #[cfg(desktop)] desktop_commands::is_maximizable, + #[cfg(desktop)] desktop_commands::is_minimizable, + #[cfg(desktop)] desktop_commands::is_closable, + #[cfg(desktop)] desktop_commands::current_monitor, + #[cfg(desktop)] desktop_commands::primary_monitor, + #[cfg(desktop)] desktop_commands::monitor_from_point, + #[cfg(desktop)] desktop_commands::available_monitors, + #[cfg(desktop)] desktop_commands::cursor_position, + #[cfg(desktop)] desktop_commands::is_always_on_top, + // setters + #[cfg(desktop)] desktop_commands::center, + #[cfg(desktop)] desktop_commands::request_user_attention, + #[cfg(desktop)] desktop_commands::set_maximizable, + #[cfg(desktop)] desktop_commands::set_minimizable, + #[cfg(desktop)] desktop_commands::set_closable, + #[cfg(desktop)] desktop_commands::maximize, + #[cfg(desktop)] desktop_commands::unmaximize, + #[cfg(desktop)] desktop_commands::minimize, + #[cfg(desktop)] desktop_commands::unminimize, + #[cfg(desktop)] desktop_commands::set_decorations, + #[cfg(desktop)] desktop_commands::set_shadow, + #[cfg(desktop)] desktop_commands::set_effects, + #[cfg(desktop)] desktop_commands::set_always_on_top, + #[cfg(desktop)] desktop_commands::set_always_on_bottom, + #[cfg(desktop)] desktop_commands::set_fullscreen, + #[cfg(desktop)] desktop_commands::set_simple_fullscreen, + #[cfg(desktop)] desktop_commands::set_skip_taskbar, + #[cfg(desktop)] desktop_commands::set_cursor_grab, + #[cfg(desktop)] desktop_commands::set_cursor_visible, + #[cfg(desktop)] desktop_commands::set_cursor_icon, + #[cfg(desktop)] desktop_commands::set_cursor_position, + #[cfg(desktop)] desktop_commands::set_ignore_cursor_events, + #[cfg(desktop)] desktop_commands::start_dragging, + #[cfg(desktop)] desktop_commands::start_resize_dragging, + #[cfg(desktop)] desktop_commands::set_badge_count, + #[cfg(target_os = "macos")] + #[cfg(desktop)] desktop_commands::set_badge_label, + #[cfg(desktop)] desktop_commands::set_progress_bar, + #[cfg(target_os = "windows")] + #[cfg(desktop)] desktop_commands::set_overlay_icon, + #[cfg(desktop)] desktop_commands::set_icon, + #[cfg(desktop)] desktop_commands::set_visible_on_all_workspaces, + #[cfg(desktop)] desktop_commands::set_title_bar_style, + #[cfg(desktop)] desktop_commands::toggle_maximize, + #[cfg(desktop)] desktop_commands::internal_toggle_maximize, + ]) .build() } From 9a360af214fd22b30e69d37541c4f9f90e89ba7c Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 29 Oct 2025 13:56:04 -0300 Subject: [PATCH 09/33] fix desktop build --- crates/tauri/src/webview/plugin.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/tauri/src/webview/plugin.rs b/crates/tauri/src/webview/plugin.rs index dbcca60ad675..c4d14c7d4aa3 100644 --- a/crates/tauri/src/webview/plugin.rs +++ b/crates/tauri/src/webview/plugin.rs @@ -43,7 +43,12 @@ async fn create_webview_window( #[cfg(desktop)] mod desktop_commands { use super::*; - use crate::{command, AppHandle}; + use crate::{ + command, + runtime::dpi::{Position, Size}, + utils::config::Color, + AppHandle, Webview, + }; fn get_webview( webview: Webview, From 6b941162c55d54f076c2742a5d76bebc51d5d6cb Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 30 Oct 2025 08:17:39 -0300 Subject: [PATCH 10/33] fix window from config --- crates/tauri-runtime-wry/src/lib.rs | 127 ++++++++++++++++------------ 1 file changed, 72 insertions(+), 55 deletions(-) diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 48bf17d2676a..7532208e9ed9 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -74,7 +74,6 @@ use tao::{ UserAttentionType as TaoUserAttentionType, }, }; -#[cfg(desktop)] use tauri_utils::config::PreventOverflowConfig; #[cfg(target_os = "macos")] use tauri_utils::TitleBarStyle; @@ -870,68 +869,86 @@ impl WindowBuilder for WindowBuilderWrapper { window.inner = window.inner.with_cursor_moved_event(false); } - #[cfg(desktop)] + #[cfg(target_os = "android")] { - window = window - .title(config.title.to_string()) - .inner_size(config.width, config.height) - .focused(config.focus) - .focusable(config.focusable) - .visible(config.visible) - .resizable(config.resizable) - .fullscreen(config.fullscreen) - .decorations(config.decorations) - .maximized(config.maximized) - .always_on_bottom(config.always_on_bottom) - .always_on_top(config.always_on_top) - .visible_on_all_workspaces(config.visible_on_all_workspaces) - .content_protected(config.content_protected) - .skip_taskbar(config.skip_taskbar) - .theme(config.theme) - .closable(config.closable) - .maximizable(config.maximizable) - .minimizable(config.minimizable) - .shadow(config.shadow); - - let mut constraints = WindowSizeConstraints::default(); - - if let Some(min_width) = config.min_width { - constraints.min_width = Some(tao::dpi::LogicalUnit::new(min_width).into()); - } - if let Some(min_height) = config.min_height { - constraints.min_height = Some(tao::dpi::LogicalUnit::new(min_height).into()); + if let Some(activity_name) = &config.activity_name { + window.inner = window.inner.with_activity_name(activity_name.clone()); } - if let Some(max_width) = config.max_width { - constraints.max_width = Some(tao::dpi::LogicalUnit::new(max_width).into()); + if let Some(activity_name) = &config.created_by_activity_name { + window.inner = window + .inner + .with_created_by_activity_name(activity_name.clone()); } - if let Some(max_height) = config.max_height { - constraints.max_height = Some(tao::dpi::LogicalUnit::new(max_height).into()); - } - if let Some(color) = config.background_color { - window = window.background_color(color); - } - window = window.inner_size_constraints(constraints); + } - if let (Some(x), Some(y)) = (config.x, config.y) { - window = window.position(x, y); + #[cfg(target_os = "ios")] + { + if let Some(scene_identifier) = &config.requested_by_scene_identifier { + window.inner = window + .inner + .with_requesting_scene_identifier(scene_identifier.clone()); } + } - if config.center { - window = window.center(); - } + window = window + .title(config.title.to_string()) + .inner_size(config.width, config.height) + .focused(config.focus) + .focusable(config.focusable) + .visible(config.visible) + .resizable(config.resizable) + .fullscreen(config.fullscreen) + .decorations(config.decorations) + .maximized(config.maximized) + .always_on_bottom(config.always_on_bottom) + .always_on_top(config.always_on_top) + .visible_on_all_workspaces(config.visible_on_all_workspaces) + .content_protected(config.content_protected) + .skip_taskbar(config.skip_taskbar) + .theme(config.theme) + .closable(config.closable) + .maximizable(config.maximizable) + .minimizable(config.minimizable) + .shadow(config.shadow); + + let mut constraints = WindowSizeConstraints::default(); + + if let Some(min_width) = config.min_width { + constraints.min_width = Some(tao::dpi::LogicalUnit::new(min_width).into()); + } + if let Some(min_height) = config.min_height { + constraints.min_height = Some(tao::dpi::LogicalUnit::new(min_height).into()); + } + if let Some(max_width) = config.max_width { + constraints.max_width = Some(tao::dpi::LogicalUnit::new(max_width).into()); + } + if let Some(max_height) = config.max_height { + constraints.max_height = Some(tao::dpi::LogicalUnit::new(max_height).into()); + } + if let Some(color) = config.background_color { + window = window.background_color(color); + } + window = window.inner_size_constraints(constraints); - if let Some(window_classname) = &config.window_classname { - window = window.window_classname(window_classname); - } + if let (Some(x), Some(y)) = (config.x, config.y) { + window = window.position(x, y); + } - if let Some(prevent_overflow) = &config.prevent_overflow { - window = match prevent_overflow { - PreventOverflowConfig::Enable(true) => window.prevent_overflow(), - PreventOverflowConfig::Margin(margin) => window - .prevent_overflow_with_margin(TaoPhysicalSize::new(margin.width, margin.height).into()), - _ => window, - }; - } + if config.center { + window = window.center(); + } + + if let Some(window_classname) = &config.window_classname { + window = window.window_classname(window_classname); + } + + if let Some(prevent_overflow) = &config.prevent_overflow { + window = match prevent_overflow { + PreventOverflowConfig::Enable(true) => window.prevent_overflow(), + PreventOverflowConfig::Margin(margin) => window + .prevent_overflow_with_margin(TaoPhysicalSize::new(margin.width, margin.height).into()), + _ => window, + }; } window From a6020088babec0ca66a6d5ddb76d51c5489853dd Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 30 Oct 2025 08:17:52 -0300 Subject: [PATCH 11/33] update lock --- Cargo.lock | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc4d675a3de8..d01d1f76642e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,7 +1319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1972,7 +1972,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -4311,7 +4311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -5115,6 +5115,16 @@ dependencies = [ "objc2-foundation 0.3.0", ] +[[package]] +name = "objc2-core-location" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d31f4c5b5192304996badc466aeadffe1411d73a9bbd3b18b6b2ee9d048b07bd" +dependencies = [ + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + [[package]] name = "objc2-encode" version = "4.1.0" @@ -5199,6 +5209,7 @@ checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" dependencies = [ "bitflags 2.7.0", "objc2 0.6.0", + "objc2-core-foundation", "objc2-foundation 0.3.0", ] @@ -5220,8 +5231,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1" dependencies = [ "bitflags 2.7.0", + "block2 0.6.0", "objc2 0.6.0", + "objc2-cloud-kit", + "objc2-core-data", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation 0.3.0", + "objc2-quartz-core 0.3.0", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670fe793adbf3b5e93686d48a05a7ed7ee53dfa65d106ced4805fae8969059b2" +dependencies = [ + "objc2 0.6.0", "objc2-foundation 0.3.0", ] @@ -5810,7 +5839,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.22.1", + "base64 0.21.7", "bitfield", "block-padding", "blowfish", @@ -8427,6 +8456,7 @@ dependencies = [ "objc2 0.6.0", "objc2-app-kit", "objc2-foundation 0.3.0", + "objc2-ui-kit", "once_cell", "parking_lot", "raw-window-handle", @@ -10314,7 +10344,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -10946,7 +10976,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.53.4" +version = "0.53.5" dependencies = [ "base64 0.22.1", "block2 0.6.0", From eb51b1329f26bf7a5f026e8852c65ade5f695f17 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 30 Oct 2025 08:44:42 -0300 Subject: [PATCH 12/33] size is now respected on iOS, so default to None --- Cargo.lock | 14 +++++------ crates/tauri-cli/config.schema.json | 10 ++++++-- crates/tauri-runtime-wry/src/lib.rs | 5 +++- .../schemas/config.schema.json | 10 ++++++-- crates/tauri-utils/src/config.rs | 24 ++++++++++++------- crates/tauri/src/webview/plugin.rs | 4 ++-- 6 files changed, 45 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d01d1f76642e..0149641e7049 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,7 +1319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1972,7 +1972,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5839,7 +5839,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.21.7", + "base64 0.22.1", "bitfield", "block-padding", "blowfish", @@ -8849,9 +8849,9 @@ dependencies = [ [[package]] name = "tauri-plugin-log" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c1438bc7662acd16d508c919b3c087efd63669a4c75625dff829b1c75975ec" +checksum = "d5709c792b8630290b5d9811a1f8fe983dd925fc87c7fc7f4923616458cd00b6" dependencies = [ "android_logger", "byte-unit", @@ -10344,7 +10344,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -10976,7 +10976,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.53.5" +version = "0.53.4" dependencies = [ "base64 0.22.1", "block2 0.6.0", diff --git a/crates/tauri-cli/config.schema.json b/crates/tauri-cli/config.schema.json index f884489b72c0..3deb2e3405dd 100644 --- a/crates/tauri-cli/config.schema.json +++ b/crates/tauri-cli/config.schema.json @@ -280,13 +280,19 @@ "width": { "description": "The window width.", "default": 800.0, - "type": "number", + "type": [ + "number", + "null" + ], "format": "double" }, "height": { "description": "The window height.", "default": 600.0, - "type": "number", + "type": [ + "number", + "null" + ], "format": "double" }, "minWidth": { diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 7532208e9ed9..70f0a0a7bfb8 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -890,9 +890,12 @@ impl WindowBuilder for WindowBuilderWrapper { } } + if let (Some(width), Some(height)) = (config.width, config.height) { + window = window.inner_size(width, height); + } + window = window .title(config.title.to_string()) - .inner_size(config.width, config.height) .focused(config.focus) .focusable(config.focusable) .visible(config.visible) diff --git a/crates/tauri-schema-generator/schemas/config.schema.json b/crates/tauri-schema-generator/schemas/config.schema.json index f884489b72c0..3deb2e3405dd 100644 --- a/crates/tauri-schema-generator/schemas/config.schema.json +++ b/crates/tauri-schema-generator/schemas/config.schema.json @@ -280,13 +280,19 @@ "width": { "description": "The window width.", "default": 800.0, - "type": "number", + "type": [ + "number", + "null" + ], "format": "double" }, "height": { "description": "The window height.", "default": 600.0, - "type": "number", + "type": [ + "number", + "null" + ], "format": "double" }, "minWidth": { diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index 30b2377b757a..477da33afd89 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -1694,10 +1694,10 @@ pub struct WindowConfig { pub y: Option, /// The window width. #[serde(default = "default_width")] - pub width: f64, + pub width: Option, /// The window height. #[serde(default = "default_height")] - pub height: f64, + pub height: Option, /// The min window width. #[serde(alias = "min-width")] pub min_width: Option, @@ -2083,12 +2083,20 @@ fn default_window_label() -> String { "main".to_string() } -fn default_width() -> f64 { - 800f64 +fn default_width() -> Option { + if cfg!(any(target_os = "ios", target_os = "android")) { + None + } else { + Some(800.) + } } -fn default_height() -> f64 { - 600f64 +fn default_height() -> Option { + if cfg!(any(target_os = "ios", target_os = "android")) { + None + } else { + Some(600.) + } } fn default_title() -> String { @@ -3563,8 +3571,8 @@ mod build { let center = self.center; let x = opt_lit(self.x.as_ref()); let y = opt_lit(self.y.as_ref()); - let width = self.width; - let height = self.height; + let width = opt_lit(self.width.as_ref()); + let height = opt_lit(self.height.as_ref()); let min_width = opt_lit(self.min_width.as_ref()); let min_height = opt_lit(self.min_height.as_ref()); let max_width = opt_lit(self.max_width.as_ref()); diff --git a/crates/tauri/src/webview/plugin.rs b/crates/tauri/src/webview/plugin.rs index c4d14c7d4aa3..dce13e4c3db8 100644 --- a/crates/tauri/src/webview/plugin.rs +++ b/crates/tauri/src/webview/plugin.rs @@ -150,8 +150,8 @@ mod desktop_commands { let x = options.x.context("missing parameter `options.x`")?; let y = options.y.context("missing parameter `options.y`")?; - let width = options.width; - let height = options.height; + let width = options.width.unwrap_or(800.); + let height = options.height.unwrap_or(600.); let builder = crate::webview::WebviewBuilder::from_config(&options); From f5f9b3dfab993a98b03c1c369d2c68c8f132a8d7 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 6 Nov 2025 12:51:15 -0300 Subject: [PATCH 13/33] add SceneRequested event --- Cargo.lock | 20 ++++++++++---------- crates/tauri-runtime-wry/src/lib.rs | 4 ++++ crates/tauri-runtime/src/lib.rs | 14 ++++++++++++++ crates/tauri/src/app.rs | 18 ++++++++++++++++++ examples/api/src-tauri/src/lib.rs | 19 +++++++++++++++++-- 5 files changed, 63 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0149641e7049..39864424bd05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,7 +1319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -2342,7 +2342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4311,7 +4311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -5839,7 +5839,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.22.1", + "base64 0.21.7", "bitfield", "block-padding", "blowfish", @@ -6419,7 +6419,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7138,7 +7138,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7151,7 +7151,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -9028,7 +9028,7 @@ dependencies = [ "getrandom 0.2.15", "once_cell", "rustix 0.38.43", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -10344,7 +10344,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -10976,7 +10976,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.53.4" +version = "0.53.5" dependencies = [ "base64 0.22.1", "block2 0.6.0", diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 70f0a0a7bfb8..b8f9a4765fd5 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -4283,6 +4283,10 @@ fn handle_event_loop( } => callback(RunEvent::Reopen { has_visible_windows, }), + #[cfg(target_os = "ios")] + Event::SceneRequested { scene, options } => { + callback(RunEvent::SceneRequested { scene, options }); + } _ => (), } } diff --git a/crates/tauri-runtime/src/lib.rs b/crates/tauri-runtime/src/lib.rs index 5da3a2a3ef06..73ed2d0cc8a1 100644 --- a/crates/tauri-runtime/src/lib.rs +++ b/crates/tauri-runtime/src/lib.rs @@ -238,6 +238,20 @@ pub enum RunEvent { }, /// A custom event defined by the user. UserEvent(T), + /// Emitted when a scene is requested by the system. + /// + /// This event is emitted when a scene is requested by the system. + /// Scenes created by [`Window::new`] are not emitted with this event. + /// It is also not emitted for the main scene. + #[cfg(target_os = "ios")] + SceneRequested { + /// Scene that was requested by the system. + scene: objc2::rc::Retained, + /// Options that were used to request the scene. + /// + /// This lets you determine why the scene was requested. + options: objc2::rc::Retained, + }, } /// Action to take when the event loop is about to exit diff --git a/crates/tauri/src/app.rs b/crates/tauri/src/app.rs index cacdaea4e381..a1fded17a1ce 100644 --- a/crates/tauri/src/app.rs +++ b/crates/tauri/src/app.rs @@ -252,6 +252,20 @@ pub enum RunEvent { /// Indicates whether the NSApplication object found any visible windows in your application. has_visible_windows: bool, }, + /// Emitted when a scene is requested by the system. + /// + /// This event is emitted when a scene is requested by the system. + /// Scenes created by [`Window::new`] are not emitted with this event. + /// It is also not emitted for the main scene. + #[cfg(target_os = "ios")] + SceneRequested { + /// Scene that was requested by the system. + scene: objc2::rc::Retained, + /// Options that were used to request the scene. + /// + /// This lets you determine why the scene was requested. + options: objc2::rc::Retained, + }, } impl From for RunEvent { @@ -2505,6 +2519,10 @@ fn on_event_loop_event( } => RunEvent::Reopen { has_visible_windows, }, + #[cfg(target_os = "ios")] + RuntimeRunEvent::SceneRequested { scene, options } => { + RunEvent::SceneRequested { scene, options } + } _ => unimplemented!(), }; diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index 73a573672fab..47cc753f4c8b 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -86,7 +86,7 @@ pub fn run_app) + Send + 'static>( let number = created_window_count.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - let builder = tauri::WebviewWindowBuilder::new( + let builder = WebviewWindowBuilder::new( &app_, format!("new-{number}"), tauri::WebviewUrl::External("about:blank".parse().unwrap()), @@ -181,9 +181,12 @@ pub fn run_app) + Send + 'static>( #[cfg(target_os = "macos")] app.set_activation_policy(tauri::ActivationPolicy::Regular); + #[cfg(target_os = "ios")] + let mut counter = 0; app.run(move |_app_handle, _event| { - #[cfg(all(desktop, not(test)))] + #[cfg(not(test))] match &_event { + #[cfg(desktop)] RunEvent::ExitRequested { api, code, .. } => { // Keep the event loop running even if all windows are closed // This allow us to catch tray icon events when there is no window @@ -192,6 +195,7 @@ pub fn run_app) + Send + 'static>( api.prevent_exit(); } } + #[cfg(desktop)] RunEvent::WindowEvent { event: tauri::WindowEvent::CloseRequested { api, .. }, label, @@ -207,6 +211,17 @@ pub fn run_app) + Send + 'static>( .destroy() .unwrap(); } + #[cfg(target_os = "ios")] + RunEvent::SceneRequested { .. } => { + counter += 1; + WebviewWindowBuilder::new( + _app_handle, + format!("main-from-scene-{counter}"), + WebviewUrl::default(), + ) + .build() + .unwrap(); + } _ => (), } }) From 6eb2dee24cee08a44ba05d30fc9c19c6ba3417f5 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 12 Nov 2025 15:06:58 -0300 Subject: [PATCH 14/33] update features --- crates/tauri-runtime/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/tauri-runtime/Cargo.toml b/crates/tauri-runtime/Cargo.toml index b269b8d329a0..57d68bd576a5 100644 --- a/crates/tauri-runtime/Cargo.toml +++ b/crates/tauri-runtime/Cargo.toml @@ -54,6 +54,8 @@ objc2 = "0.6" objc2-ui-kit = { version = "0.3.0", default-features = false, features = [ "UIView", "UIResponder", + "UIScene", + "UISceneOptions", ] } [target."cfg(target_os = \"macos\")".dependencies] From bfc74a75b6f4adb4e2483d276d26abe3e9fe620a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 12 Nov 2025 15:17:41 -0300 Subject: [PATCH 15/33] fix default size --- crates/tauri-utils/src/config.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index 15da73a780ba..f5b4c05aec8d 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -2069,7 +2069,10 @@ fn default_window_label() -> String { } fn default_width() -> Option { - if cfg!(any(target_os = "ios", target_os = "android")) { + // the config is generated by a proc macro, so we have to check CARGO_CFG_TARGET_OS since proc macros run on the host target + if cfg!(any(target_os = "ios", target_os = "android")) + || std::env::var("CARGO_CFG_TARGET_OS").map_or(false, |os| os == "ios" || os == "android") + { None } else { Some(800.) @@ -2077,7 +2080,10 @@ fn default_width() -> Option { } fn default_height() -> Option { - if cfg!(any(target_os = "ios", target_os = "android")) { + // the config is generated by a proc macro, so we have to check CARGO_CFG_TARGET_OS since proc macros run on the host target + if cfg!(any(target_os = "ios", target_os = "android")) + || std::env::var("CARGO_CFG_TARGET_OS").map_or(false, |os| os == "ios" || os == "android") + { None } else { Some(600.) From e97e2e5229d94916f15f2cac49a1ac0d841f9b1e Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 12 Nov 2025 15:19:41 -0300 Subject: [PATCH 16/33] Revert "fix default size" This reverts commit bfc74a75b6f4adb4e2483d276d26abe3e9fe620a. --- crates/tauri-utils/src/config.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index f5b4c05aec8d..15da73a780ba 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -2069,10 +2069,7 @@ fn default_window_label() -> String { } fn default_width() -> Option { - // the config is generated by a proc macro, so we have to check CARGO_CFG_TARGET_OS since proc macros run on the host target - if cfg!(any(target_os = "ios", target_os = "android")) - || std::env::var("CARGO_CFG_TARGET_OS").map_or(false, |os| os == "ios" || os == "android") - { + if cfg!(any(target_os = "ios", target_os = "android")) { None } else { Some(800.) @@ -2080,10 +2077,7 @@ fn default_width() -> Option { } fn default_height() -> Option { - // the config is generated by a proc macro, so we have to check CARGO_CFG_TARGET_OS since proc macros run on the host target - if cfg!(any(target_os = "ios", target_os = "android")) - || std::env::var("CARGO_CFG_TARGET_OS").map_or(false, |os| os == "ios" || os == "android") - { + if cfg!(any(target_os = "ios", target_os = "android")) { None } else { Some(600.) From 11fc78bf160451b92e5f4f0769ad1e64ad75fe67 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 12 Nov 2025 15:20:58 -0300 Subject: [PATCH 17/33] ignore size from config --- crates/tauri-cli/config.schema.json | 10 ++------ crates/tauri-runtime-wry/src/lib.rs | 6 +++-- .../schemas/config.schema.json | 10 ++------ crates/tauri-utils/src/config.rs | 24 +++++++------------ 4 files changed, 16 insertions(+), 34 deletions(-) diff --git a/crates/tauri-cli/config.schema.json b/crates/tauri-cli/config.schema.json index 3deb2e3405dd..f884489b72c0 100644 --- a/crates/tauri-cli/config.schema.json +++ b/crates/tauri-cli/config.schema.json @@ -280,19 +280,13 @@ "width": { "description": "The window width.", "default": 800.0, - "type": [ - "number", - "null" - ], + "type": "number", "format": "double" }, "height": { "description": "The window height.", "default": 600.0, - "type": [ - "number", - "null" - ], + "type": "number", "format": "double" }, "minWidth": { diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index b8f9a4765fd5..4fa682c41ba7 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -890,8 +890,10 @@ impl WindowBuilder for WindowBuilderWrapper { } } - if let (Some(width), Some(height)) = (config.width, config.height) { - window = window.inner_size(width, height); + // ignore size from config for mobile for backward compatibility + #[cfg(not(any(target_os = "ios", target_os = "android")))] + { + window = window.inner_size(config.width, config.height); } window = window diff --git a/crates/tauri-schema-generator/schemas/config.schema.json b/crates/tauri-schema-generator/schemas/config.schema.json index 3deb2e3405dd..f884489b72c0 100644 --- a/crates/tauri-schema-generator/schemas/config.schema.json +++ b/crates/tauri-schema-generator/schemas/config.schema.json @@ -280,19 +280,13 @@ "width": { "description": "The window width.", "default": 800.0, - "type": [ - "number", - "null" - ], + "type": "number", "format": "double" }, "height": { "description": "The window height.", "default": 600.0, - "type": [ - "number", - "null" - ], + "type": "number", "format": "double" }, "minWidth": { diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index 15da73a780ba..a8595d0bf52d 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -1679,10 +1679,10 @@ pub struct WindowConfig { pub y: Option, /// The window width. #[serde(default = "default_width")] - pub width: Option, + pub width: f64, /// The window height. #[serde(default = "default_height")] - pub height: Option, + pub height: f64, /// The min window width. #[serde(alias = "min-width")] pub min_width: Option, @@ -2068,20 +2068,12 @@ fn default_window_label() -> String { "main".to_string() } -fn default_width() -> Option { - if cfg!(any(target_os = "ios", target_os = "android")) { - None - } else { - Some(800.) - } +fn default_width() -> f64 { + 800. } -fn default_height() -> Option { - if cfg!(any(target_os = "ios", target_os = "android")) { - None - } else { - Some(600.) - } +fn default_height() -> f64 { + 600. } fn default_title() -> String { @@ -3551,8 +3543,8 @@ mod build { let center = self.center; let x = opt_lit(self.x.as_ref()); let y = opt_lit(self.y.as_ref()); - let width = opt_lit(self.width.as_ref()); - let height = opt_lit(self.height.as_ref()); + let width = self.width; + let height = self.height; let min_width = opt_lit(self.min_width.as_ref()); let min_height = opt_lit(self.min_height.as_ref()); let max_width = opt_lit(self.max_width.as_ref()); From e5f65a6707174191ef2532f774e2d797af48d02e Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 12 Nov 2025 15:21:22 -0300 Subject: [PATCH 18/33] lint --- crates/tauri/src/webview/plugin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/tauri/src/webview/plugin.rs b/crates/tauri/src/webview/plugin.rs index dce13e4c3db8..02c68fca72af 100644 --- a/crates/tauri/src/webview/plugin.rs +++ b/crates/tauri/src/webview/plugin.rs @@ -47,7 +47,7 @@ mod desktop_commands { command, runtime::dpi::{Position, Size}, utils::config::Color, - AppHandle, Webview, + Webview, }; fn get_webview( @@ -137,7 +137,7 @@ mod desktop_commands { #[cfg(feature = "unstable")] #[command(root = "crate")] pub async fn create_webview( - app: AppHandle, + app: crate::AppHandle, window_label: String, options: WindowConfig, ) -> crate::Result<()> { From 2172252b7848d02e7f093d6e53999526656c0f40 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 17 Nov 2025 08:50:39 -0300 Subject: [PATCH 19/33] use git --- Cargo.lock | 27 +++++++++++++++------------ Cargo.toml | 4 ++-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b8335989326..9e81809d968c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,7 +1319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -2348,7 +2348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4319,7 +4319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -5848,7 +5848,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.21.7", + "base64 0.22.1", "bitfield", "block-padding", "blowfish", @@ -6428,7 +6428,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7147,7 +7147,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7160,7 +7160,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8444,6 +8444,7 @@ dependencies = [ [[package]] name = "tao" version = "0.34.5" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#e07caa1ea2edf0a8a675fadc53f301a66aa60900" dependencies = [ "bitflags 2.7.0", "block2 0.6.0", @@ -8470,7 +8471,7 @@ dependencies = [ "parking_lot", "raw-window-handle", "scopeguard", - "tao-macros 0.1.3", + "tao-macros 0.1.3 (git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window)", "unicode-segmentation", "url", "windows 0.61.1", @@ -8482,6 +8483,8 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", @@ -8491,8 +8494,7 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#e07caa1ea2edf0a8a675fadc53f301a66aa60900" dependencies = [ "proc-macro2", "quote", @@ -9038,7 +9040,7 @@ dependencies = [ "getrandom 0.2.15", "once_cell", "rustix 0.38.43", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -10354,7 +10356,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -10987,6 +10989,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" version = "0.53.5" +source = "git+https://github.com/tauri-apps/wry?branch=feat/mobile-multi-webview#5861318d87e8ac3b71ec7aae64df931923bbbc39" dependencies = [ "base64 0.22.1", "block2 0.6.0", diff --git a/Cargo.toml b/Cargo.toml index 562d662718ea..d5e6e4228884 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,5 +72,5 @@ schemars_derive = { git = 'https://github.com/tauri-apps/schemars.git', branch = tauri = { path = "./crates/tauri" } tauri-plugin = { path = "./crates/tauri-plugin" } tauri-utils = { path = "./crates/tauri-utils" } -wry = { path = "../wry" } -tao = { path = "../tao" } +wry = { git = "https://github.com/tauri-apps/wry", branch = "feat/mobile-multi-webview" } +tao = { git = "https://github.com/tauri-apps/tao", branch = "feat/mobile-multi-window" } From 7f953fceb232d76a529aa492f4d7f5b22a4c429e Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 17 Nov 2025 09:03:18 -0300 Subject: [PATCH 20/33] change file --- .changes/multi-window-mobile.md | 8 ++++++++ .changes/refactor-ios-stdout-stderr.md | 6 ------ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 .changes/multi-window-mobile.md delete mode 100644 .changes/refactor-ios-stdout-stderr.md diff --git a/.changes/multi-window-mobile.md b/.changes/multi-window-mobile.md new file mode 100644 index 000000000000..57af55470ac8 --- /dev/null +++ b/.changes/multi-window-mobile.md @@ -0,0 +1,8 @@ +--- +"tauri": minor:feat +"tauri-runtime-wry": minor:feat +"tauri-runtime": minor:feat +"tauri-utils": minor:feat +--- + +Support creating multiple windows on Android (activity embedding) and iOS (scenes). diff --git a/.changes/refactor-ios-stdout-stderr.md b/.changes/refactor-ios-stdout-stderr.md deleted file mode 100644 index 766cd9fb5492..000000000000 --- a/.changes/refactor-ios-stdout-stderr.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"tauri": patch:bug -"tauri-macros": patch:bug ---- - -Fix iOS deadlock when running on the simulator from Xcode by properly piping stdout/stderr messages through the Xcode console and OSLog. From 7f02cf5863143e81bb4fec887c78f5cb5030ebed Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 2 Dec 2025 10:01:18 -0300 Subject: [PATCH 21/33] add supports_multiple_windows API --- Cargo.lock | 22 ++++---- crates/tauri/build.rs | 1 + .../app/autogenerated/reference.md | 27 +++++++++ crates/tauri/scripts/bundle.global.js | 2 +- crates/tauri/src/app.rs | 56 +++++++++++++++++++ crates/tauri/src/app/plugin.rs | 6 ++ packages/api/src/app.ts | 7 ++- 7 files changed, 108 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e81809d968c..3be7d57f4026 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,7 +1319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2348,7 +2348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5848,7 +5848,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.22.1", + "base64 0.21.7", "bitfield", "block-padding", "blowfish", @@ -6428,7 +6428,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7147,7 +7147,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7160,7 +7160,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8444,7 +8444,7 @@ dependencies = [ [[package]] name = "tao" version = "0.34.5" -source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#e07caa1ea2edf0a8a675fadc53f301a66aa60900" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#fb52c343d759521dbf9277bf8257b1323b2077a0" dependencies = [ "bitflags 2.7.0", "block2 0.6.0", @@ -8494,7 +8494,7 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.3" -source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#e07caa1ea2edf0a8a675fadc53f301a66aa60900" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#fb52c343d759521dbf9277bf8257b1323b2077a0" dependencies = [ "proc-macro2", "quote", @@ -9040,7 +9040,7 @@ dependencies = [ "getrandom 0.2.15", "once_cell", "rustix 0.38.43", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -10356,7 +10356,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -10989,7 +10989,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" version = "0.53.5" -source = "git+https://github.com/tauri-apps/wry?branch=feat/mobile-multi-webview#5861318d87e8ac3b71ec7aae64df931923bbbc39" +source = "git+https://github.com/tauri-apps/wry?branch=feat/mobile-multi-webview#8d8836116aaaef271775e2958f3e38c4b5d18b04" dependencies = [ "base64 0.22.1", "block2 0.6.0", diff --git a/crates/tauri/build.rs b/crates/tauri/build.rs index 4a886abad524..f2efb35cf1e2 100644 --- a/crates/tauri/build.rs +++ b/crates/tauri/build.rs @@ -168,6 +168,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[ ("bundle_type", true), ("register_listener", true), ("remove_listener", true), + ("supports_multiple_windows", true), ], ), ( diff --git a/crates/tauri/permissions/app/autogenerated/reference.md b/crates/tauri/permissions/app/autogenerated/reference.md index 178a632d93ab..581bd8da85e8 100644 --- a/crates/tauri/permissions/app/autogenerated/reference.md +++ b/crates/tauri/permissions/app/autogenerated/reference.md @@ -11,6 +11,7 @@ Default permissions for the plugin. - `allow-bundle-type` - `allow-register-listener` - `allow-remove-listener` +- `allow-supports-multiple-windows` ## Permission Table @@ -336,6 +337,32 @@ Denies the set_dock_visibility command without any pre-configured scope. +`core:app:allow-supports-multiple-windows` + + + + +Enables the supports_multiple_windows command without any pre-configured scope. + + + + + + + +`core:app:deny-supports-multiple-windows` + + + + +Denies the supports_multiple_windows command without any pre-configured scope. + + + + + + + `core:app:allow-tauri-version` diff --git a/crates/tauri/scripts/bundle.global.js b/crates/tauri/scripts/bundle.global.js index 7f8eff34b263..33d685365b4d 100644 --- a/crates/tauri/scripts/bundle.global.js +++ b/crates/tauri/scripts/bundle.global.js @@ -1 +1 @@ -var __TAURI_IIFE__=function(e){"use strict";function n(e,n,t,i){if("a"===t&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof n?e!==n||!i:!n.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?i:"a"===t?i.call(e):i?i.value:n.get(e)}function t(e,n,t,i,r){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!r)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof n?e!==n||!r:!n.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?r.call(e,t):r?r.value=t:n.set(e,t),t}var i,r,s,a,l;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";function u(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}class c{constructor(e){i.set(this,void 0),r.set(this,0),s.set(this,[]),a.set(this,void 0),t(this,i,e||(()=>{}),"f"),this.id=u(e=>{const l=e.index;if("end"in e)return void(l==n(this,r,"f")?this.cleanupCallback():t(this,a,l,"f"));const o=e.message;if(l==n(this,r,"f")){for(n(this,i,"f").call(this,o),t(this,r,n(this,r,"f")+1,"f");n(this,r,"f")in n(this,s,"f");){const e=n(this,s,"f")[n(this,r,"f")];n(this,i,"f").call(this,e),delete n(this,s,"f")[n(this,r,"f")],t(this,r,n(this,r,"f")+1,"f")}n(this,r,"f")===n(this,a,"f")&&this.cleanupCallback()}else n(this,s,"f")[l]=o})}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(e){t(this,i,e,"f")}get onmessage(){return n(this,i,"f")}[(i=new WeakMap,r=new WeakMap,s=new WeakMap,a=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}class d{constructor(e,n,t){this.plugin=e,this.event=n,this.channelId=t}async unregister(){return h(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function p(e,n,t){const i=new c(t);try{return await h(`plugin:${e}|register_listener`,{event:n,handler:i}),new d(e,n,i.id)}catch{return await h(`plugin:${e}|registerListener`,{event:n,handler:i}),new d(e,n,i.id)}}async function h(e,n={},t){return window.__TAURI_INTERNALS__.invoke(e,n,t)}class w{get rid(){return n(this,l,"f")}constructor(e){l.set(this,void 0),t(this,l,e,"f")}async close(){return h("plugin:resources|close",{rid:this.rid})}}l=new WeakMap;var _=Object.freeze({__proto__:null,Channel:c,PluginListener:d,Resource:w,SERIALIZE_TO_IPC_FN:o,addPluginListener:p,checkPermissions:async function(e){return h(`plugin:${e}|check_permissions`)},convertFileSrc:function(e,n="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(e,n)},invoke:h,isTauri:function(){return!!(globalThis||window).isTauri},requestPermissions:async function(e){return h(`plugin:${e}|request_permissions`)},transformCallback:u});class y extends w{constructor(e){super(e)}static async new(e,n,t){return h("plugin:image|new",{rgba:g(e),width:n,height:t}).then(e=>new y(e))}static async fromBytes(e){return h("plugin:image|from_bytes",{bytes:g(e)}).then(e=>new y(e))}static async fromPath(e){return h("plugin:image|from_path",{path:e}).then(e=>new y(e))}async rgba(){return h("plugin:image|rgba",{rid:this.rid}).then(e=>new Uint8Array(e))}async size(){return h("plugin:image|size",{rid:this.rid})}}function g(e){return null==e?null:"string"==typeof e?e:e instanceof y?e.rid:e}var b,m=Object.freeze({__proto__:null,Image:y,transformImage:g});!function(e){e.Nsis="nsis",e.Msi="msi",e.Deb="deb",e.Rpm="rpm",e.AppImage="appimage",e.App="app"}(b||(b={}));var f=Object.freeze({__proto__:null,get BundleType(){return b},defaultWindowIcon:async function(){return h("plugin:app|default_window_icon").then(e=>e?new y(e):null)},fetchDataStoreIdentifiers:async function(){return h("plugin:app|fetch_data_store_identifiers")},getBundleType:async function(){return h("plugin:app|bundle_type")},getIdentifier:async function(){return h("plugin:app|identifier")},getName:async function(){return h("plugin:app|name")},getTauriVersion:async function(){return h("plugin:app|tauri_version")},getVersion:async function(){return h("plugin:app|version")},hide:async function(){return h("plugin:app|app_hide")},onBackButtonPress:async function(e){return p("app","back-button",e)},removeDataStore:async function(e){return h("plugin:app|remove_data_store",{uuid:e})},setDockVisibility:async function(e){return h("plugin:app|set_dock_visibility",{visible:e})},setTheme:async function(e){return h("plugin:app|set_app_theme",{theme:e})},show:async function(){return h("plugin:app|app_show")}});class v{constructor(...e){this.type="Logical",1===e.length?"Logical"in e[0]?(this.width=e[0].Logical.width,this.height=e[0].Logical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toPhysical(e){return new k(this.width*e,this.height*e)}[o](){return{width:this.width,height:this.height}}toJSON(){return this[o]()}}class k{constructor(...e){this.type="Physical",1===e.length?"Physical"in e[0]?(this.width=e[0].Physical.width,this.height=e[0].Physical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toLogical(e){return new v(this.width/e,this.height/e)}[o](){return{width:this.width,height:this.height}}toJSON(){return this[o]()}}class A{constructor(e){this.size=e}toLogical(e){return this.size instanceof v?this.size:this.size.toLogical(e)}toPhysical(e){return this.size instanceof k?this.size:this.size.toPhysical(e)}[o](){return{[`${this.size.type}`]:{width:this.size.width,height:this.size.height}}}toJSON(){return this[o]()}}class T{constructor(...e){this.type="Logical",1===e.length?"Logical"in e[0]?(this.x=e[0].Logical.x,this.y=e[0].Logical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toPhysical(e){return new I(this.x*e,this.y*e)}[o](){return{x:this.x,y:this.y}}toJSON(){return this[o]()}}class I{constructor(...e){this.type="Physical",1===e.length?"Physical"in e[0]?(this.x=e[0].Physical.x,this.y=e[0].Physical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toLogical(e){return new T(this.x/e,this.y/e)}[o](){return{x:this.x,y:this.y}}toJSON(){return this[o]()}}class E{constructor(e){this.position=e}toLogical(e){return this.position instanceof T?this.position:this.position.toLogical(e)}toPhysical(e){return this.position instanceof I?this.position:this.position.toPhysical(e)}[o](){return{[`${this.position.type}`]:{x:this.position.x,y:this.position.y}}}toJSON(){return this[o]()}}var R,D=Object.freeze({__proto__:null,LogicalPosition:T,LogicalSize:v,PhysicalPosition:I,PhysicalSize:k,Position:E,Size:A});async function S(e,n){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(e,n),await h("plugin:event|unlisten",{event:e,eventId:n})}async function N(e,n,t){var i;const r="string"==typeof(null==t?void 0:t.target)?{kind:"AnyLabel",label:t.target}:null!==(i=null==t?void 0:t.target)&&void 0!==i?i:{kind:"Any"};return h("plugin:event|listen",{event:e,target:r,handler:u(n)}).then(n=>async()=>S(e,n))}async function L(e,n,t){return N(e,t=>{S(e,t.id),n(t)},t)}async function C(e,n){await h("plugin:event|emit",{event:e,payload:n})}async function x(e,n,t){const i="string"==typeof e?{kind:"AnyLabel",label:e}:e;await h("plugin:event|emit_to",{target:i,event:n,payload:t})}!function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",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_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(R||(R={}));var P,z,W,O=Object.freeze({__proto__:null,get TauriEvent(){return R},emit:C,emitTo:x,listen:N,once:L});function U(e){var n;if("items"in e)e.items=null===(n=e.items)||void 0===n?void 0:n.map(e=>"rid"in e?e:U(e));else if("action"in e&&e.action){const n=new c;return n.onmessage=e.action,delete e.action,{...e,handler:n}}return e}async function F(e,n){const t=new c;if(n&&"object"==typeof n&&("action"in n&&n.action&&(t.onmessage=n.action,delete n.action),"item"in n&&n.item&&"object"==typeof n.item&&"About"in n.item&&n.item.About&&"object"==typeof n.item.About&&"icon"in n.item.About&&n.item.About.icon&&(n.item.About.icon=g(n.item.About.icon)),"icon"in n&&n.icon&&(n.icon=g(n.icon)),"items"in n&&n.items)){function i(e){var n;return"rid"in e?[e.rid,e.kind]:("item"in e&&"object"==typeof e.item&&(null===(n=e.item.About)||void 0===n?void 0:n.icon)&&(e.item.About.icon=g(e.item.About.icon)),"icon"in e&&e.icon&&(e.icon=g(e.icon)),"items"in e&&e.items&&(e.items=e.items.map(i)),U(e))}n.items=n.items.map(i)}return h("plugin:menu|new",{kind:e,options:n,handler:t})}class M extends w{get id(){return n(this,P,"f")}get kind(){return n(this,z,"f")}constructor(e,n,i){super(e),P.set(this,void 0),z.set(this,void 0),t(this,P,n,"f"),t(this,z,i,"f")}}P=new WeakMap,z=new WeakMap;class B extends M{constructor(e,n){super(e,n,"MenuItem")}static async new(e){return F("MenuItem",e).then(([e,n])=>new B(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return h("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return h("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return h("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}}class V extends M{constructor(e,n){super(e,n,"Check")}static async new(e){return F("Check",e).then(([e,n])=>new V(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return h("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return h("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return h("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async isChecked(){return h("plugin:menu|is_checked",{rid:this.rid})}async setChecked(e){return h("plugin:menu|set_checked",{rid:this.rid,checked:e})}}!function(e){e.Add="Add",e.Advanced="Advanced",e.Bluetooth="Bluetooth",e.Bookmarks="Bookmarks",e.Caution="Caution",e.ColorPanel="ColorPanel",e.ColumnView="ColumnView",e.Computer="Computer",e.EnterFullScreen="EnterFullScreen",e.Everyone="Everyone",e.ExitFullScreen="ExitFullScreen",e.FlowView="FlowView",e.Folder="Folder",e.FolderBurnable="FolderBurnable",e.FolderSmart="FolderSmart",e.FollowLinkFreestanding="FollowLinkFreestanding",e.FontPanel="FontPanel",e.GoLeft="GoLeft",e.GoRight="GoRight",e.Home="Home",e.IChatTheater="IChatTheater",e.IconView="IconView",e.Info="Info",e.InvalidDataFreestanding="InvalidDataFreestanding",e.LeftFacingTriangle="LeftFacingTriangle",e.ListView="ListView",e.LockLocked="LockLocked",e.LockUnlocked="LockUnlocked",e.MenuMixedState="MenuMixedState",e.MenuOnState="MenuOnState",e.MobileMe="MobileMe",e.MultipleDocuments="MultipleDocuments",e.Network="Network",e.Path="Path",e.PreferencesGeneral="PreferencesGeneral",e.QuickLook="QuickLook",e.RefreshFreestanding="RefreshFreestanding",e.Refresh="Refresh",e.Remove="Remove",e.RevealFreestanding="RevealFreestanding",e.RightFacingTriangle="RightFacingTriangle",e.Share="Share",e.Slideshow="Slideshow",e.SmartBadge="SmartBadge",e.StatusAvailable="StatusAvailable",e.StatusNone="StatusNone",e.StatusPartiallyAvailable="StatusPartiallyAvailable",e.StatusUnavailable="StatusUnavailable",e.StopProgressFreestanding="StopProgressFreestanding",e.StopProgress="StopProgress",e.TrashEmpty="TrashEmpty",e.TrashFull="TrashFull",e.User="User",e.UserAccounts="UserAccounts",e.UserGroup="UserGroup",e.UserGuest="UserGuest"}(W||(W={}));class G extends M{constructor(e,n){super(e,n,"Icon")}static async new(e){return F("Icon",e).then(([e,n])=>new G(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return h("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return h("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return h("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async setIcon(e){return h("plugin:menu|set_icon",{rid:this.rid,kind:this.kind,icon:g(e)})}}class j extends M{constructor(e,n){super(e,n,"Predefined")}static async new(e){return F("Predefined",e).then(([e,n])=>new j(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}}function H([e,n,t]){switch(t){case"Submenu":return new $(e,n);case"Predefined":return new j(e,n);case"Check":return new V(e,n);case"Icon":return new G(e,n);default:return new B(e,n)}}class $ extends M{constructor(e,n){super(e,n,"Submenu")}static async new(e){return F("Submenu",e).then(([e,n])=>new $(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return h("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return h("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async append(e){return h("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e)})}async prepend(e){return h("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e)})}async insert(e,n){return h("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e),position:n})}async remove(e){return h("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return h("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(H)}async items(){return h("plugin:menu|items",{rid:this.rid,kind:this.kind}).then(e=>e.map(H))}async get(e){return h("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then(e=>e?H(e):null)}async popup(e,n){var t;return h("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:null!==(t=null==n?void 0:n.label)&&void 0!==t?t:null,at:e instanceof E?e:e?new E(e):null})}async setAsWindowsMenuForNSApp(){return h("plugin:menu|set_as_windows_menu_for_nsapp",{rid:this.rid})}async setAsHelpMenuForNSApp(){return h("plugin:menu|set_as_help_menu_for_nsapp",{rid:this.rid})}async setIcon(e){return h("plugin:menu|set_icon",{rid:this.rid,kind:this.kind,icon:g(e)})}}class q extends M{constructor(e,n){super(e,n,"Menu")}static async new(e){return F("Menu",e).then(([e,n])=>new q(e,n))}static async default(){return h("plugin:menu|create_default").then(([e,n])=>new q(e,n))}async append(e){return h("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e)})}async prepend(e){return h("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e)})}async insert(e,n){return h("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e),position:n})}async remove(e){return h("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return h("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(H)}async items(){return h("plugin:menu|items",{rid:this.rid,kind:this.kind}).then(e=>e.map(H))}async get(e){return h("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then(e=>e?H(e):null)}async popup(e,n){var t;return h("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:null!==(t=null==n?void 0:n.label)&&void 0!==t?t:null,at:e instanceof E?e:e?new E(e):null})}async setAsAppMenu(){return h("plugin:menu|set_as_app_menu",{rid:this.rid}).then(e=>e?new q(e[0],e[1]):null)}async setAsWindowMenu(e){var n;return h("plugin:menu|set_as_window_menu",{rid:this.rid,window:null!==(n=null==e?void 0:e.label)&&void 0!==n?n:null}).then(e=>e?new q(e[0],e[1]):null)}}var J=Object.freeze({__proto__:null,CheckMenuItem:V,IconMenuItem:G,Menu:q,MenuItem:B,get NativeIcon(){return W},PredefinedMenuItem:j,Submenu:$,itemFromKind:H});function Q(){var e,n;window.__TAURI_INTERNALS__=null!==(e=window.__TAURI_INTERNALS__)&&void 0!==e?e:{},window.__TAURI_EVENT_PLUGIN_INTERNALS__=null!==(n=window.__TAURI_EVENT_PLUGIN_INTERNALS__)&&void 0!==n?n:{}}var Z,K=Object.freeze({__proto__:null,clearMocks:function(){"object"==typeof window.__TAURI_INTERNALS__&&(delete window.__TAURI_INTERNALS__.invoke,delete window.__TAURI_INTERNALS__.transformCallback,delete window.__TAURI_INTERNALS__.unregisterCallback,delete window.__TAURI_INTERNALS__.runCallback,delete window.__TAURI_INTERNALS__.callbacks,delete window.__TAURI_INTERNALS__.convertFileSrc,delete window.__TAURI_INTERNALS__.metadata,"object"==typeof window.__TAURI_EVENT_PLUGIN_INTERNALS__&&delete window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener)},mockConvertFileSrc:function(e){Q(),window.__TAURI_INTERNALS__.convertFileSrc=function(n,t="asset"){const i=encodeURIComponent(n);return"windows"===e?`http://${t}.localhost/${i}`:`${t}://localhost/${i}`}},mockIPC:function(e,n){function t(e,n){switch(e){case"plugin:event|listen":return function(e){i.has(e.event)||i.set(e.event,[]);return i.get(e.event).push(e.handler),e.handler}(n);case"plugin:event|emit":return function(e){const n=i.get(e.event)||[];for(const t of n)a(t,e);return null}(n);case"plugin:event|unlisten":return function(e){const n=i.get(e.event);if(n){const t=n.indexOf(e.id);-1!==t&&n.splice(t,1)}}(n)}}Q();const i=new Map,r=new Map;function s(e){r.delete(e)}function a(e,n){const t=r.get(e);t?t(n):console.warn(`[TAURI] Couldn't find callback id ${e}. This might happen when the app is reloaded while Rust is running an asynchronous operation.`)}window.__TAURI_INTERNALS__.invoke=async function(i,r,s){return(null==n?void 0:n.shouldMockEvents)&&function(e){return e.startsWith("plugin:event|")}(i)?t(i,r):e(i,r)},window.__TAURI_INTERNALS__.transformCallback=function(e,n=!1){const t=window.crypto.getRandomValues(new Uint32Array(1))[0];return r.set(t,i=>(n&&s(t),e&&e(i))),t},window.__TAURI_INTERNALS__.unregisterCallback=s,window.__TAURI_INTERNALS__.runCallback=a,window.__TAURI_INTERNALS__.callbacks=r,window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener=function(e,n){s(n)}},mockWindows:function(e,...n){Q(),window.__TAURI_INTERNALS__.metadata={currentWindow:{label:e},currentWebview:{windowLabel:e,label:e}}}});!function(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.Document=6]="Document",e[e.Download=7]="Download",e[e.Picture=8]="Picture",e[e.Public=9]="Public",e[e.Video=10]="Video",e[e.Resource=11]="Resource",e[e.Temp=12]="Temp",e[e.AppConfig=13]="AppConfig",e[e.AppData=14]="AppData",e[e.AppLocalData=15]="AppLocalData",e[e.AppCache=16]="AppCache",e[e.AppLog=17]="AppLog",e[e.Desktop=18]="Desktop",e[e.Executable=19]="Executable",e[e.Font=20]="Font",e[e.Home=21]="Home",e[e.Runtime=22]="Runtime",e[e.Template=23]="Template"}(Z||(Z={}));var Y=Object.freeze({__proto__:null,get BaseDirectory(){return Z},appCacheDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppCache})},appConfigDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppConfig})},appDataDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppData})},appLocalDataDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppLocalData})},appLogDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppLog})},audioDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Audio})},basename:async function(e,n){return h("plugin:path|basename",{path:e,ext:n})},cacheDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Cache})},configDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Config})},dataDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Data})},delimiter:function(){return window.__TAURI_INTERNALS__.plugins.path.delimiter},desktopDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Desktop})},dirname:async function(e){return h("plugin:path|dirname",{path:e})},documentDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Document})},downloadDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Download})},executableDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Executable})},extname:async function(e){return h("plugin:path|extname",{path:e})},fontDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Font})},homeDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Home})},isAbsolute:async function(e){return h("plugin:path|is_absolute",{path:e})},join:async function(...e){return h("plugin:path|join",{paths:e})},localDataDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.LocalData})},normalize:async function(e){return h("plugin:path|normalize",{path:e})},pictureDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Picture})},publicDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Public})},resolve:async function(...e){return h("plugin:path|resolve",{paths:e})},resolveResource:async function(e){return h("plugin:path|resolve_directory",{directory:Z.Resource,path:e})},resourceDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Resource})},runtimeDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Runtime})},sep:function(){return window.__TAURI_INTERNALS__.plugins.path.sep},tempDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Temp})},templateDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Template})},videoDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Video})}});class X extends w{constructor(e,n){super(e),this.id=n}static async getById(e){return h("plugin:tray|get_by_id",{id:e}).then(n=>n?new X(n,e):null)}static async removeById(e){return h("plugin:tray|remove_by_id",{id:e})}static async new(e){(null==e?void 0:e.menu)&&(e.menu=[e.menu.rid,e.menu.kind]),(null==e?void 0:e.icon)&&(e.icon=g(e.icon));const n=new c;if(null==e?void 0:e.action){const t=e.action;n.onmessage=e=>t(function(e){const n=e;return n.position=new I(e.position),n.rect.position=new I(e.rect.position),n.rect.size=new k(e.rect.size),n}(e)),delete e.action}return h("plugin:tray|new",{options:null!=e?e:{},handler:n}).then(([e,n])=>new X(e,n))}async setIcon(e){let n=null;return e&&(n=g(e)),h("plugin:tray|set_icon",{rid:this.rid,icon:n})}async setMenu(e){return e&&(e=[e.rid,e.kind]),h("plugin:tray|set_menu",{rid:this.rid,menu:e})}async setTooltip(e){return h("plugin:tray|set_tooltip",{rid:this.rid,tooltip:e})}async setTitle(e){return h("plugin:tray|set_title",{rid:this.rid,title:e})}async setVisible(e){return h("plugin:tray|set_visible",{rid:this.rid,visible:e})}async setTempDirPath(e){return h("plugin:tray|set_temp_dir_path",{rid:this.rid,path:e})}async setIconAsTemplate(e){return h("plugin:tray|set_icon_as_template",{rid:this.rid,asTemplate:e})}async setMenuOnLeftClick(e){return h("plugin:tray|set_show_menu_on_left_click",{rid:this.rid,onLeft:e})}async setShowMenuOnLeftClick(e){return h("plugin:tray|set_show_menu_on_left_click",{rid:this.rid,onLeft:e})}}var ee,ne,te=Object.freeze({__proto__:null,TrayIcon:X});!function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"}(ee||(ee={}));class ie{constructor(e){this._preventDefault=!1,this.event=e.event,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}}function re(){return new le(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}async function se(){return h("plugin:window|get_all_windows").then(e=>e.map(e=>new le(e,{skip:!0})))}!function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"}(ne||(ne={}));const ae=["tauri://created","tauri://error"];class le{constructor(e,n={}){var t;this.label=e,this.listeners=Object.create(null),(null==n?void 0:n.skip)||h("plugin:window|create",{options:{...n,parent:"string"==typeof n.parent?n.parent:null===(t=n.parent)||void 0===t?void 0:t.label,label:e}}).then(async()=>this.emit("tauri://created")).catch(async e=>this.emit("tauri://error",e))}static async getByLabel(e){var n;return null!==(n=(await se()).find(n=>n.label===e))&&void 0!==n?n:null}static getCurrent(){return re()}static async getAll(){return se()}static async getFocusedWindow(){for(const e of await se())if(await e.isFocused())return e;return null}async listen(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:N(e,n,{target:{kind:"Window",label:this.label}})}async once(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:L(e,n,{target:{kind:"Window",label:this.label}})}async emit(e,n){if(!ae.includes(e))return C(e,n);for(const t of this.listeners[e]||[])t({event:e,id:-1,payload:n})}async emitTo(e,n,t){if(!ae.includes(n))return x(e,n,t);for(const e of this.listeners[n]||[])e({event:n,id:-1,payload:t})}_handleTauriEvent(e,n){return!!ae.includes(e)&&(e in this.listeners?this.listeners[e].push(n):this.listeners[e]=[n],!0)}async scaleFactor(){return h("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return h("plugin:window|inner_position",{label:this.label}).then(e=>new I(e))}async outerPosition(){return h("plugin:window|outer_position",{label:this.label}).then(e=>new I(e))}async innerSize(){return h("plugin:window|inner_size",{label:this.label}).then(e=>new k(e))}async outerSize(){return h("plugin:window|outer_size",{label:this.label}).then(e=>new k(e))}async isFullscreen(){return h("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return h("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return h("plugin:window|is_maximized",{label:this.label})}async isFocused(){return h("plugin:window|is_focused",{label:this.label})}async isDecorated(){return h("plugin:window|is_decorated",{label:this.label})}async isResizable(){return h("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return h("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return h("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return h("plugin:window|is_closable",{label:this.label})}async isVisible(){return h("plugin:window|is_visible",{label:this.label})}async title(){return h("plugin:window|title",{label:this.label})}async theme(){return h("plugin:window|theme",{label:this.label})}async isAlwaysOnTop(){return h("plugin:window|is_always_on_top",{label:this.label})}async center(){return h("plugin:window|center",{label:this.label})}async requestUserAttention(e){let n=null;return e&&(n=e===ee.Critical?{type:"Critical"}:{type:"Informational"}),h("plugin:window|request_user_attention",{label:this.label,value:n})}async setResizable(e){return h("plugin:window|set_resizable",{label:this.label,value:e})}async setEnabled(e){return h("plugin:window|set_enabled",{label:this.label,value:e})}async isEnabled(){return h("plugin:window|is_enabled",{label:this.label})}async setMaximizable(e){return h("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return h("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return h("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return h("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return h("plugin:window|maximize",{label:this.label})}async unmaximize(){return h("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return h("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return h("plugin:window|minimize",{label:this.label})}async unminimize(){return h("plugin:window|unminimize",{label:this.label})}async show(){return h("plugin:window|show",{label:this.label})}async hide(){return h("plugin:window|hide",{label:this.label})}async close(){return h("plugin:window|close",{label:this.label})}async destroy(){return h("plugin:window|destroy",{label:this.label})}async setDecorations(e){return h("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return h("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return h("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return h("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return h("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return h("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return h("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){return h("plugin:window|set_size",{label:this.label,value:e instanceof A?e:new A(e)})}async setMinSize(e){return h("plugin:window|set_min_size",{label:this.label,value:e instanceof A?e:e?new A(e):null})}async setMaxSize(e){return h("plugin:window|set_max_size",{label:this.label,value:e instanceof A?e:e?new A(e):null})}async setSizeConstraints(e){function n(e){return e?{Logical:e}:null}return h("plugin:window|set_size_constraints",{label:this.label,value:{minWidth:n(null==e?void 0:e.minWidth),minHeight:n(null==e?void 0:e.minHeight),maxWidth:n(null==e?void 0:e.maxWidth),maxHeight:n(null==e?void 0:e.maxHeight)}})}async setPosition(e){return h("plugin:window|set_position",{label:this.label,value:e instanceof E?e:new E(e)})}async setFullscreen(e){return h("plugin:window|set_fullscreen",{label:this.label,value:e})}async setSimpleFullscreen(e){return h("plugin:window|set_simple_fullscreen",{label:this.label,value:e})}async setFocus(){return h("plugin:window|set_focus",{label:this.label})}async setFocusable(e){return h("plugin:window|set_focusable",{label:this.label,value:e})}async setIcon(e){return h("plugin:window|set_icon",{label:this.label,value:g(e)})}async setSkipTaskbar(e){return h("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return h("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return h("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return h("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setBackgroundColor(e){return h("plugin:window|set_background_color",{color:e})}async setCursorPosition(e){return h("plugin:window|set_cursor_position",{label:this.label,value:e instanceof E?e:new E(e)})}async setIgnoreCursorEvents(e){return h("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return h("plugin:window|start_dragging",{label:this.label})}async startResizeDragging(e){return h("plugin:window|start_resize_dragging",{label:this.label,value:e})}async setBadgeCount(e){return h("plugin:window|set_badge_count",{label:this.label,value:e})}async setBadgeLabel(e){return h("plugin:window|set_badge_label",{label:this.label,value:e})}async setOverlayIcon(e){return h("plugin:window|set_overlay_icon",{label:this.label,value:e?g(e):void 0})}async setProgressBar(e){return h("plugin:window|set_progress_bar",{label:this.label,value:e})}async setVisibleOnAllWorkspaces(e){return h("plugin:window|set_visible_on_all_workspaces",{label:this.label,value:e})}async setTitleBarStyle(e){return h("plugin:window|set_title_bar_style",{label:this.label,value:e})}async setTheme(e){return h("plugin:window|set_theme",{label:this.label,value:e})}async onResized(e){return this.listen(R.WINDOW_RESIZED,n=>{n.payload=new k(n.payload),e(n)})}async onMoved(e){return this.listen(R.WINDOW_MOVED,n=>{n.payload=new I(n.payload),e(n)})}async onCloseRequested(e){return this.listen(R.WINDOW_CLOSE_REQUESTED,async n=>{const t=new ie(n);await e(t),t.isPreventDefault()||await this.destroy()})}async onDragDropEvent(e){const n=await this.listen(R.DRAG_ENTER,n=>{e({...n,payload:{type:"enter",paths:n.payload.paths,position:new I(n.payload.position)}})}),t=await this.listen(R.DRAG_OVER,n=>{e({...n,payload:{type:"over",position:new I(n.payload.position)}})}),i=await this.listen(R.DRAG_DROP,n=>{e({...n,payload:{type:"drop",paths:n.payload.paths,position:new I(n.payload.position)}})}),r=await this.listen(R.DRAG_LEAVE,n=>{e({...n,payload:{type:"leave"}})});return()=>{n(),i(),t(),r()}}async onFocusChanged(e){const n=await this.listen(R.WINDOW_FOCUS,n=>{e({...n,payload:!0})}),t=await this.listen(R.WINDOW_BLUR,n=>{e({...n,payload:!1})});return()=>{n(),t()}}async onScaleChanged(e){return this.listen(R.WINDOW_SCALE_FACTOR_CHANGED,e)}async onThemeChanged(e){return this.listen(R.WINDOW_THEME_CHANGED,e)}}var oe,ue,ce,de;function pe(e){return null===e?null:{name:e.name,scaleFactor:e.scaleFactor,position:new I(e.position),size:new k(e.size),workArea:{position:new I(e.workArea.position),size:new k(e.workArea.size)}}}!function(e){e.Disabled="disabled",e.Throttle="throttle",e.Suspend="suspend"}(oe||(oe={})),function(e){e.Default="default",e.FluentOverlay="fluentOverlay"}(ue||(ue={})),function(e){e.AppearanceBased="appearanceBased",e.Light="light",e.Dark="dark",e.MediumLight="mediumLight",e.UltraDark="ultraDark",e.Titlebar="titlebar",e.Selection="selection",e.Menu="menu",e.Popover="popover",e.Sidebar="sidebar",e.HeaderView="headerView",e.Sheet="sheet",e.WindowBackground="windowBackground",e.HudWindow="hudWindow",e.FullScreenUI="fullScreenUI",e.Tooltip="tooltip",e.ContentBackground="contentBackground",e.UnderWindowBackground="underWindowBackground",e.UnderPageBackground="underPageBackground",e.Mica="mica",e.Blur="blur",e.Acrylic="acrylic",e.Tabbed="tabbed",e.TabbedDark="tabbedDark",e.TabbedLight="tabbedLight"}(ce||(ce={})),function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"}(de||(de={}));var he=Object.freeze({__proto__:null,CloseRequestedEvent:ie,get Effect(){return ce},get EffectState(){return de},LogicalPosition:T,LogicalSize:v,PhysicalPosition:I,PhysicalSize:k,get ProgressBarStatus(){return ne},get UserAttentionType(){return ee},Window:le,availableMonitors:async function(){return h("plugin:window|available_monitors").then(e=>e.map(pe))},currentMonitor:async function(){return h("plugin:window|current_monitor").then(pe)},cursorPosition:async function(){return h("plugin:window|cursor_position").then(e=>new I(e))},getAllWindows:se,getCurrentWindow:re,monitorFromPoint:async function(e,n){return h("plugin:window|monitor_from_point",{x:e,y:n}).then(pe)},primaryMonitor:async function(){return h("plugin:window|primary_monitor").then(pe)}});function we(){return new ge(re(),window.__TAURI_INTERNALS__.metadata.currentWebview.label,{skip:!0})}async function _e(){return h("plugin:webview|get_all_webviews").then(e=>e.map(e=>new ge(new le(e.windowLabel,{skip:!0}),e.label,{skip:!0})))}const ye=["tauri://created","tauri://error"];class ge{constructor(e,n,t){this.window=e,this.label=n,this.listeners=Object.create(null),(null==t?void 0:t.skip)||h("plugin:webview|create_webview",{windowLabel:e.label,options:{...t,label:n}}).then(async()=>this.emit("tauri://created")).catch(async e=>this.emit("tauri://error",e))}static async getByLabel(e){var n;return null!==(n=(await _e()).find(n=>n.label===e))&&void 0!==n?n:null}static getCurrent(){return we()}static async getAll(){return _e()}async listen(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:N(e,n,{target:{kind:"Webview",label:this.label}})}async once(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:L(e,n,{target:{kind:"Webview",label:this.label}})}async emit(e,n){if(!ye.includes(e))return C(e,n);for(const t of this.listeners[e]||[])t({event:e,id:-1,payload:n})}async emitTo(e,n,t){if(!ye.includes(n))return x(e,n,t);for(const e of this.listeners[n]||[])e({event:n,id:-1,payload:t})}_handleTauriEvent(e,n){return!!ye.includes(e)&&(e in this.listeners?this.listeners[e].push(n):this.listeners[e]=[n],!0)}async position(){return h("plugin:webview|webview_position",{label:this.label}).then(e=>new I(e))}async size(){return h("plugin:webview|webview_size",{label:this.label}).then(e=>new k(e))}async close(){return h("plugin:webview|webview_close",{label:this.label})}async setSize(e){return h("plugin:webview|set_webview_size",{label:this.label,value:e instanceof A?e:new A(e)})}async setPosition(e){return h("plugin:webview|set_webview_position",{label:this.label,value:e instanceof E?e:new E(e)})}async setFocus(){return h("plugin:webview|set_webview_focus",{label:this.label})}async setAutoResize(e){return h("plugin:webview|set_webview_auto_resize",{label:this.label,value:e})}async hide(){return h("plugin:webview|webview_hide",{label:this.label})}async show(){return h("plugin:webview|webview_show",{label:this.label})}async setZoom(e){return h("plugin:webview|set_webview_zoom",{label:this.label,value:e})}async reparent(e){return h("plugin:webview|reparent",{label:this.label,window:"string"==typeof e?e:e.label})}async clearAllBrowsingData(){return h("plugin:webview|clear_all_browsing_data")}async setBackgroundColor(e){return h("plugin:webview|set_webview_background_color",{color:e})}async onDragDropEvent(e){const n=await this.listen(R.DRAG_ENTER,n=>{e({...n,payload:{type:"enter",paths:n.payload.paths,position:new I(n.payload.position)}})}),t=await this.listen(R.DRAG_OVER,n=>{e({...n,payload:{type:"over",position:new I(n.payload.position)}})}),i=await this.listen(R.DRAG_DROP,n=>{e({...n,payload:{type:"drop",paths:n.payload.paths,position:new I(n.payload.position)}})}),r=await this.listen(R.DRAG_LEAVE,n=>{e({...n,payload:{type:"leave"}})});return()=>{n(),i(),t(),r()}}}var be,me,fe=Object.freeze({__proto__:null,Webview:ge,getAllWebviews:_e,getCurrentWebview:we});function ve(){const e=we();return new Ae(e.label,{skip:!0})}async function ke(){return h("plugin:window|get_all_windows").then(e=>e.map(e=>new Ae(e,{skip:!0})))}class Ae{constructor(e,n={}){var t;this.label=e,this.listeners=Object.create(null),(null==n?void 0:n.skip)||h("plugin:webview|create_webview_window",{options:{...n,parent:"string"==typeof n.parent?n.parent:null===(t=n.parent)||void 0===t?void 0:t.label,label:e}}).then(async()=>this.emit("tauri://created")).catch(async e=>this.emit("tauri://error",e))}static async getByLabel(e){var n;const t=null!==(n=(await ke()).find(n=>n.label===e))&&void 0!==n?n:null;return t?new Ae(t.label,{skip:!0}):null}static getCurrent(){return ve()}static async getAll(){return ke()}async listen(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:N(e,n,{target:{kind:"WebviewWindow",label:this.label}})}async once(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:L(e,n,{target:{kind:"WebviewWindow",label:this.label}})}async setBackgroundColor(e){return h("plugin:window|set_background_color",{color:e}).then(()=>h("plugin:webview|set_webview_background_color",{color:e}))}}be=Ae,me=[le,ge],(Array.isArray(me)?me:[me]).forEach(e=>{Object.getOwnPropertyNames(e.prototype).forEach(n=>{var t;"object"==typeof be.prototype&&be.prototype&&n in be.prototype||Object.defineProperty(be.prototype,n,null!==(t=Object.getOwnPropertyDescriptor(e.prototype,n))&&void 0!==t?t:Object.create(null))})});var Te=Object.freeze({__proto__:null,WebviewWindow:Ae,getAllWebviewWindows:ke,getCurrentWebviewWindow:ve});return e.app=f,e.core=_,e.dpi=D,e.event=O,e.image=m,e.menu=J,e.mocks=K,e.path=Y,e.tray=te,e.webview=fe,e.webviewWindow=Te,e.window=he,e}({});window.__TAURI__=__TAURI_IIFE__; +var __TAURI_IIFE__=function(e){"use strict";function n(e,n,t,i){if("a"===t&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof n?e!==n||!i:!n.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?i:"a"===t?i.call(e):i?i.value:n.get(e)}function t(e,n,t,i,r){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!r)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof n?e!==n||!r:!n.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?r.call(e,t):r?r.value=t:n.set(e,t),t}var i,r,s,a,l;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";function u(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}class c{constructor(e){i.set(this,void 0),r.set(this,0),s.set(this,[]),a.set(this,void 0),t(this,i,e||(()=>{}),"f"),this.id=u(e=>{const l=e.index;if("end"in e)return void(l==n(this,r,"f")?this.cleanupCallback():t(this,a,l,"f"));const o=e.message;if(l==n(this,r,"f")){for(n(this,i,"f").call(this,o),t(this,r,n(this,r,"f")+1,"f");n(this,r,"f")in n(this,s,"f");){const e=n(this,s,"f")[n(this,r,"f")];n(this,i,"f").call(this,e),delete n(this,s,"f")[n(this,r,"f")],t(this,r,n(this,r,"f")+1,"f")}n(this,r,"f")===n(this,a,"f")&&this.cleanupCallback()}else n(this,s,"f")[l]=o})}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(e){t(this,i,e,"f")}get onmessage(){return n(this,i,"f")}[(i=new WeakMap,r=new WeakMap,s=new WeakMap,a=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}class d{constructor(e,n,t){this.plugin=e,this.event=n,this.channelId=t}async unregister(){return h(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function p(e,n,t){const i=new c(t);try{return await h(`plugin:${e}|register_listener`,{event:n,handler:i}),new d(e,n,i.id)}catch{return await h(`plugin:${e}|registerListener`,{event:n,handler:i}),new d(e,n,i.id)}}async function h(e,n={},t){return window.__TAURI_INTERNALS__.invoke(e,n,t)}class w{get rid(){return n(this,l,"f")}constructor(e){l.set(this,void 0),t(this,l,e,"f")}async close(){return h("plugin:resources|close",{rid:this.rid})}}l=new WeakMap;var _=Object.freeze({__proto__:null,Channel:c,PluginListener:d,Resource:w,SERIALIZE_TO_IPC_FN:o,addPluginListener:p,checkPermissions:async function(e){return h(`plugin:${e}|check_permissions`)},convertFileSrc:function(e,n="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(e,n)},invoke:h,isTauri:function(){return!!(globalThis||window).isTauri},requestPermissions:async function(e){return h(`plugin:${e}|request_permissions`)},transformCallback:u});class y extends w{constructor(e){super(e)}static async new(e,n,t){return h("plugin:image|new",{rgba:g(e),width:n,height:t}).then(e=>new y(e))}static async fromBytes(e){return h("plugin:image|from_bytes",{bytes:g(e)}).then(e=>new y(e))}static async fromPath(e){return h("plugin:image|from_path",{path:e}).then(e=>new y(e))}async rgba(){return h("plugin:image|rgba",{rid:this.rid}).then(e=>new Uint8Array(e))}async size(){return h("plugin:image|size",{rid:this.rid})}}function g(e){return null==e?null:"string"==typeof e?e:e instanceof y?e.rid:e}var b,m=Object.freeze({__proto__:null,Image:y,transformImage:g});!function(e){e.Nsis="nsis",e.Msi="msi",e.Deb="deb",e.Rpm="rpm",e.AppImage="appimage",e.App="app"}(b||(b={}));var f=Object.freeze({__proto__:null,get BundleType(){return b},defaultWindowIcon:async function(){return h("plugin:app|default_window_icon").then(e=>e?new y(e):null)},fetchDataStoreIdentifiers:async function(){return h("plugin:app|fetch_data_store_identifiers")},getBundleType:async function(){return h("plugin:app|bundle_type")},getIdentifier:async function(){return h("plugin:app|identifier")},getName:async function(){return h("plugin:app|name")},getTauriVersion:async function(){return h("plugin:app|tauri_version")},getVersion:async function(){return h("plugin:app|version")},hide:async function(){return h("plugin:app|app_hide")},onBackButtonPress:async function(e){return p("app","back-button",e)},removeDataStore:async function(e){return h("plugin:app|remove_data_store",{uuid:e})},setDockVisibility:async function(e){return h("plugin:app|set_dock_visibility",{visible:e})},setTheme:async function(e){return h("plugin:app|set_app_theme",{theme:e})},show:async function(){return h("plugin:app|app_show")},supportsMultipleWindows:async function(){return h("plugin:app|supports_multiple_windows")}});class v{constructor(...e){this.type="Logical",1===e.length?"Logical"in e[0]?(this.width=e[0].Logical.width,this.height=e[0].Logical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toPhysical(e){return new k(this.width*e,this.height*e)}[o](){return{width:this.width,height:this.height}}toJSON(){return this[o]()}}class k{constructor(...e){this.type="Physical",1===e.length?"Physical"in e[0]?(this.width=e[0].Physical.width,this.height=e[0].Physical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toLogical(e){return new v(this.width/e,this.height/e)}[o](){return{width:this.width,height:this.height}}toJSON(){return this[o]()}}class A{constructor(e){this.size=e}toLogical(e){return this.size instanceof v?this.size:this.size.toLogical(e)}toPhysical(e){return this.size instanceof k?this.size:this.size.toPhysical(e)}[o](){return{[`${this.size.type}`]:{width:this.size.width,height:this.size.height}}}toJSON(){return this[o]()}}class T{constructor(...e){this.type="Logical",1===e.length?"Logical"in e[0]?(this.x=e[0].Logical.x,this.y=e[0].Logical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toPhysical(e){return new I(this.x*e,this.y*e)}[o](){return{x:this.x,y:this.y}}toJSON(){return this[o]()}}class I{constructor(...e){this.type="Physical",1===e.length?"Physical"in e[0]?(this.x=e[0].Physical.x,this.y=e[0].Physical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toLogical(e){return new T(this.x/e,this.y/e)}[o](){return{x:this.x,y:this.y}}toJSON(){return this[o]()}}class E{constructor(e){this.position=e}toLogical(e){return this.position instanceof T?this.position:this.position.toLogical(e)}toPhysical(e){return this.position instanceof I?this.position:this.position.toPhysical(e)}[o](){return{[`${this.position.type}`]:{x:this.position.x,y:this.position.y}}}toJSON(){return this[o]()}}var R,D=Object.freeze({__proto__:null,LogicalPosition:T,LogicalSize:v,PhysicalPosition:I,PhysicalSize:k,Position:E,Size:A});async function S(e,n){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(e,n),await h("plugin:event|unlisten",{event:e,eventId:n})}async function N(e,n,t){var i;const r="string"==typeof(null==t?void 0:t.target)?{kind:"AnyLabel",label:t.target}:null!==(i=null==t?void 0:t.target)&&void 0!==i?i:{kind:"Any"};return h("plugin:event|listen",{event:e,target:r,handler:u(n)}).then(n=>async()=>S(e,n))}async function L(e,n,t){return N(e,t=>{S(e,t.id),n(t)},t)}async function C(e,n){await h("plugin:event|emit",{event:e,payload:n})}async function x(e,n,t){const i="string"==typeof e?{kind:"AnyLabel",label:e}:e;await h("plugin:event|emit_to",{target:i,event:n,payload:t})}!function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",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_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(R||(R={}));var P,z,W,O=Object.freeze({__proto__:null,get TauriEvent(){return R},emit:C,emitTo:x,listen:N,once:L});function U(e){var n;if("items"in e)e.items=null===(n=e.items)||void 0===n?void 0:n.map(e=>"rid"in e?e:U(e));else if("action"in e&&e.action){const n=new c;return n.onmessage=e.action,delete e.action,{...e,handler:n}}return e}async function F(e,n){const t=new c;if(n&&"object"==typeof n&&("action"in n&&n.action&&(t.onmessage=n.action,delete n.action),"item"in n&&n.item&&"object"==typeof n.item&&"About"in n.item&&n.item.About&&"object"==typeof n.item.About&&"icon"in n.item.About&&n.item.About.icon&&(n.item.About.icon=g(n.item.About.icon)),"icon"in n&&n.icon&&(n.icon=g(n.icon)),"items"in n&&n.items)){function i(e){var n;return"rid"in e?[e.rid,e.kind]:("item"in e&&"object"==typeof e.item&&(null===(n=e.item.About)||void 0===n?void 0:n.icon)&&(e.item.About.icon=g(e.item.About.icon)),"icon"in e&&e.icon&&(e.icon=g(e.icon)),"items"in e&&e.items&&(e.items=e.items.map(i)),U(e))}n.items=n.items.map(i)}return h("plugin:menu|new",{kind:e,options:n,handler:t})}class M extends w{get id(){return n(this,P,"f")}get kind(){return n(this,z,"f")}constructor(e,n,i){super(e),P.set(this,void 0),z.set(this,void 0),t(this,P,n,"f"),t(this,z,i,"f")}}P=new WeakMap,z=new WeakMap;class B extends M{constructor(e,n){super(e,n,"MenuItem")}static async new(e){return F("MenuItem",e).then(([e,n])=>new B(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return h("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return h("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return h("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}}class V extends M{constructor(e,n){super(e,n,"Check")}static async new(e){return F("Check",e).then(([e,n])=>new V(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return h("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return h("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return h("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async isChecked(){return h("plugin:menu|is_checked",{rid:this.rid})}async setChecked(e){return h("plugin:menu|set_checked",{rid:this.rid,checked:e})}}!function(e){e.Add="Add",e.Advanced="Advanced",e.Bluetooth="Bluetooth",e.Bookmarks="Bookmarks",e.Caution="Caution",e.ColorPanel="ColorPanel",e.ColumnView="ColumnView",e.Computer="Computer",e.EnterFullScreen="EnterFullScreen",e.Everyone="Everyone",e.ExitFullScreen="ExitFullScreen",e.FlowView="FlowView",e.Folder="Folder",e.FolderBurnable="FolderBurnable",e.FolderSmart="FolderSmart",e.FollowLinkFreestanding="FollowLinkFreestanding",e.FontPanel="FontPanel",e.GoLeft="GoLeft",e.GoRight="GoRight",e.Home="Home",e.IChatTheater="IChatTheater",e.IconView="IconView",e.Info="Info",e.InvalidDataFreestanding="InvalidDataFreestanding",e.LeftFacingTriangle="LeftFacingTriangle",e.ListView="ListView",e.LockLocked="LockLocked",e.LockUnlocked="LockUnlocked",e.MenuMixedState="MenuMixedState",e.MenuOnState="MenuOnState",e.MobileMe="MobileMe",e.MultipleDocuments="MultipleDocuments",e.Network="Network",e.Path="Path",e.PreferencesGeneral="PreferencesGeneral",e.QuickLook="QuickLook",e.RefreshFreestanding="RefreshFreestanding",e.Refresh="Refresh",e.Remove="Remove",e.RevealFreestanding="RevealFreestanding",e.RightFacingTriangle="RightFacingTriangle",e.Share="Share",e.Slideshow="Slideshow",e.SmartBadge="SmartBadge",e.StatusAvailable="StatusAvailable",e.StatusNone="StatusNone",e.StatusPartiallyAvailable="StatusPartiallyAvailable",e.StatusUnavailable="StatusUnavailable",e.StopProgressFreestanding="StopProgressFreestanding",e.StopProgress="StopProgress",e.TrashEmpty="TrashEmpty",e.TrashFull="TrashFull",e.User="User",e.UserAccounts="UserAccounts",e.UserGroup="UserGroup",e.UserGuest="UserGuest"}(W||(W={}));class G extends M{constructor(e,n){super(e,n,"Icon")}static async new(e){return F("Icon",e).then(([e,n])=>new G(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return h("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return h("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return h("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async setIcon(e){return h("plugin:menu|set_icon",{rid:this.rid,kind:this.kind,icon:g(e)})}}class j extends M{constructor(e,n){super(e,n,"Predefined")}static async new(e){return F("Predefined",e).then(([e,n])=>new j(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}}function H([e,n,t]){switch(t){case"Submenu":return new $(e,n);case"Predefined":return new j(e,n);case"Check":return new V(e,n);case"Icon":return new G(e,n);default:return new B(e,n)}}class $ extends M{constructor(e,n){super(e,n,"Submenu")}static async new(e){return F("Submenu",e).then(([e,n])=>new $(e,n))}async text(){return h("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return h("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return h("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return h("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async append(e){return h("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e)})}async prepend(e){return h("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e)})}async insert(e,n){return h("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e),position:n})}async remove(e){return h("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return h("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(H)}async items(){return h("plugin:menu|items",{rid:this.rid,kind:this.kind}).then(e=>e.map(H))}async get(e){return h("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then(e=>e?H(e):null)}async popup(e,n){var t;return h("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:null!==(t=null==n?void 0:n.label)&&void 0!==t?t:null,at:e instanceof E?e:e?new E(e):null})}async setAsWindowsMenuForNSApp(){return h("plugin:menu|set_as_windows_menu_for_nsapp",{rid:this.rid})}async setAsHelpMenuForNSApp(){return h("plugin:menu|set_as_help_menu_for_nsapp",{rid:this.rid})}async setIcon(e){return h("plugin:menu|set_icon",{rid:this.rid,kind:this.kind,icon:g(e)})}}class q extends M{constructor(e,n){super(e,n,"Menu")}static async new(e){return F("Menu",e).then(([e,n])=>new q(e,n))}static async default(){return h("plugin:menu|create_default").then(([e,n])=>new q(e,n))}async append(e){return h("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e)})}async prepend(e){return h("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e)})}async insert(e,n){return h("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map(e=>"rid"in e?[e.rid,e.kind]:e),position:n})}async remove(e){return h("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return h("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(H)}async items(){return h("plugin:menu|items",{rid:this.rid,kind:this.kind}).then(e=>e.map(H))}async get(e){return h("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then(e=>e?H(e):null)}async popup(e,n){var t;return h("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:null!==(t=null==n?void 0:n.label)&&void 0!==t?t:null,at:e instanceof E?e:e?new E(e):null})}async setAsAppMenu(){return h("plugin:menu|set_as_app_menu",{rid:this.rid}).then(e=>e?new q(e[0],e[1]):null)}async setAsWindowMenu(e){var n;return h("plugin:menu|set_as_window_menu",{rid:this.rid,window:null!==(n=null==e?void 0:e.label)&&void 0!==n?n:null}).then(e=>e?new q(e[0],e[1]):null)}}var J=Object.freeze({__proto__:null,CheckMenuItem:V,IconMenuItem:G,Menu:q,MenuItem:B,get NativeIcon(){return W},PredefinedMenuItem:j,Submenu:$,itemFromKind:H});function Q(){var e,n;window.__TAURI_INTERNALS__=null!==(e=window.__TAURI_INTERNALS__)&&void 0!==e?e:{},window.__TAURI_EVENT_PLUGIN_INTERNALS__=null!==(n=window.__TAURI_EVENT_PLUGIN_INTERNALS__)&&void 0!==n?n:{}}var Z,K=Object.freeze({__proto__:null,clearMocks:function(){"object"==typeof window.__TAURI_INTERNALS__&&(delete window.__TAURI_INTERNALS__.invoke,delete window.__TAURI_INTERNALS__.transformCallback,delete window.__TAURI_INTERNALS__.unregisterCallback,delete window.__TAURI_INTERNALS__.runCallback,delete window.__TAURI_INTERNALS__.callbacks,delete window.__TAURI_INTERNALS__.convertFileSrc,delete window.__TAURI_INTERNALS__.metadata,"object"==typeof window.__TAURI_EVENT_PLUGIN_INTERNALS__&&delete window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener)},mockConvertFileSrc:function(e){Q(),window.__TAURI_INTERNALS__.convertFileSrc=function(n,t="asset"){const i=encodeURIComponent(n);return"windows"===e?`http://${t}.localhost/${i}`:`${t}://localhost/${i}`}},mockIPC:function(e,n){function t(e,n){switch(e){case"plugin:event|listen":return function(e){i.has(e.event)||i.set(e.event,[]);return i.get(e.event).push(e.handler),e.handler}(n);case"plugin:event|emit":return function(e){const n=i.get(e.event)||[];for(const t of n)a(t,e);return null}(n);case"plugin:event|unlisten":return function(e){const n=i.get(e.event);if(n){const t=n.indexOf(e.id);-1!==t&&n.splice(t,1)}}(n)}}Q();const i=new Map,r=new Map;function s(e){r.delete(e)}function a(e,n){const t=r.get(e);t?t(n):console.warn(`[TAURI] Couldn't find callback id ${e}. This might happen when the app is reloaded while Rust is running an asynchronous operation.`)}window.__TAURI_INTERNALS__.invoke=async function(i,r,s){return(null==n?void 0:n.shouldMockEvents)&&function(e){return e.startsWith("plugin:event|")}(i)?t(i,r):e(i,r)},window.__TAURI_INTERNALS__.transformCallback=function(e,n=!1){const t=window.crypto.getRandomValues(new Uint32Array(1))[0];return r.set(t,i=>(n&&s(t),e&&e(i))),t},window.__TAURI_INTERNALS__.unregisterCallback=s,window.__TAURI_INTERNALS__.runCallback=a,window.__TAURI_INTERNALS__.callbacks=r,window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener=function(e,n){s(n)}},mockWindows:function(e,...n){Q(),window.__TAURI_INTERNALS__.metadata={currentWindow:{label:e},currentWebview:{windowLabel:e,label:e}}}});!function(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.Document=6]="Document",e[e.Download=7]="Download",e[e.Picture=8]="Picture",e[e.Public=9]="Public",e[e.Video=10]="Video",e[e.Resource=11]="Resource",e[e.Temp=12]="Temp",e[e.AppConfig=13]="AppConfig",e[e.AppData=14]="AppData",e[e.AppLocalData=15]="AppLocalData",e[e.AppCache=16]="AppCache",e[e.AppLog=17]="AppLog",e[e.Desktop=18]="Desktop",e[e.Executable=19]="Executable",e[e.Font=20]="Font",e[e.Home=21]="Home",e[e.Runtime=22]="Runtime",e[e.Template=23]="Template"}(Z||(Z={}));var Y=Object.freeze({__proto__:null,get BaseDirectory(){return Z},appCacheDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppCache})},appConfigDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppConfig})},appDataDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppData})},appLocalDataDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppLocalData})},appLogDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.AppLog})},audioDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Audio})},basename:async function(e,n){return h("plugin:path|basename",{path:e,ext:n})},cacheDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Cache})},configDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Config})},dataDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Data})},delimiter:function(){return window.__TAURI_INTERNALS__.plugins.path.delimiter},desktopDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Desktop})},dirname:async function(e){return h("plugin:path|dirname",{path:e})},documentDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Document})},downloadDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Download})},executableDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Executable})},extname:async function(e){return h("plugin:path|extname",{path:e})},fontDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Font})},homeDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Home})},isAbsolute:async function(e){return h("plugin:path|is_absolute",{path:e})},join:async function(...e){return h("plugin:path|join",{paths:e})},localDataDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.LocalData})},normalize:async function(e){return h("plugin:path|normalize",{path:e})},pictureDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Picture})},publicDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Public})},resolve:async function(...e){return h("plugin:path|resolve",{paths:e})},resolveResource:async function(e){return h("plugin:path|resolve_directory",{directory:Z.Resource,path:e})},resourceDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Resource})},runtimeDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Runtime})},sep:function(){return window.__TAURI_INTERNALS__.plugins.path.sep},tempDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Temp})},templateDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Template})},videoDir:async function(){return h("plugin:path|resolve_directory",{directory:Z.Video})}});class X extends w{constructor(e,n){super(e),this.id=n}static async getById(e){return h("plugin:tray|get_by_id",{id:e}).then(n=>n?new X(n,e):null)}static async removeById(e){return h("plugin:tray|remove_by_id",{id:e})}static async new(e){(null==e?void 0:e.menu)&&(e.menu=[e.menu.rid,e.menu.kind]),(null==e?void 0:e.icon)&&(e.icon=g(e.icon));const n=new c;if(null==e?void 0:e.action){const t=e.action;n.onmessage=e=>t(function(e){const n=e;return n.position=new I(e.position),n.rect.position=new I(e.rect.position),n.rect.size=new k(e.rect.size),n}(e)),delete e.action}return h("plugin:tray|new",{options:null!=e?e:{},handler:n}).then(([e,n])=>new X(e,n))}async setIcon(e){let n=null;return e&&(n=g(e)),h("plugin:tray|set_icon",{rid:this.rid,icon:n})}async setMenu(e){return e&&(e=[e.rid,e.kind]),h("plugin:tray|set_menu",{rid:this.rid,menu:e})}async setTooltip(e){return h("plugin:tray|set_tooltip",{rid:this.rid,tooltip:e})}async setTitle(e){return h("plugin:tray|set_title",{rid:this.rid,title:e})}async setVisible(e){return h("plugin:tray|set_visible",{rid:this.rid,visible:e})}async setTempDirPath(e){return h("plugin:tray|set_temp_dir_path",{rid:this.rid,path:e})}async setIconAsTemplate(e){return h("plugin:tray|set_icon_as_template",{rid:this.rid,asTemplate:e})}async setMenuOnLeftClick(e){return h("plugin:tray|set_show_menu_on_left_click",{rid:this.rid,onLeft:e})}async setShowMenuOnLeftClick(e){return h("plugin:tray|set_show_menu_on_left_click",{rid:this.rid,onLeft:e})}}var ee,ne,te=Object.freeze({__proto__:null,TrayIcon:X});!function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"}(ee||(ee={}));class ie{constructor(e){this._preventDefault=!1,this.event=e.event,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}}function re(){return new le(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}async function se(){return h("plugin:window|get_all_windows").then(e=>e.map(e=>new le(e,{skip:!0})))}!function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"}(ne||(ne={}));const ae=["tauri://created","tauri://error"];class le{constructor(e,n={}){var t;this.label=e,this.listeners=Object.create(null),(null==n?void 0:n.skip)||h("plugin:window|create",{options:{...n,parent:"string"==typeof n.parent?n.parent:null===(t=n.parent)||void 0===t?void 0:t.label,label:e}}).then(async()=>this.emit("tauri://created")).catch(async e=>this.emit("tauri://error",e))}static async getByLabel(e){var n;return null!==(n=(await se()).find(n=>n.label===e))&&void 0!==n?n:null}static getCurrent(){return re()}static async getAll(){return se()}static async getFocusedWindow(){for(const e of await se())if(await e.isFocused())return e;return null}async listen(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:N(e,n,{target:{kind:"Window",label:this.label}})}async once(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:L(e,n,{target:{kind:"Window",label:this.label}})}async emit(e,n){if(!ae.includes(e))return C(e,n);for(const t of this.listeners[e]||[])t({event:e,id:-1,payload:n})}async emitTo(e,n,t){if(!ae.includes(n))return x(e,n,t);for(const e of this.listeners[n]||[])e({event:n,id:-1,payload:t})}_handleTauriEvent(e,n){return!!ae.includes(e)&&(e in this.listeners?this.listeners[e].push(n):this.listeners[e]=[n],!0)}async scaleFactor(){return h("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return h("plugin:window|inner_position",{label:this.label}).then(e=>new I(e))}async outerPosition(){return h("plugin:window|outer_position",{label:this.label}).then(e=>new I(e))}async innerSize(){return h("plugin:window|inner_size",{label:this.label}).then(e=>new k(e))}async outerSize(){return h("plugin:window|outer_size",{label:this.label}).then(e=>new k(e))}async isFullscreen(){return h("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return h("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return h("plugin:window|is_maximized",{label:this.label})}async isFocused(){return h("plugin:window|is_focused",{label:this.label})}async isDecorated(){return h("plugin:window|is_decorated",{label:this.label})}async isResizable(){return h("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return h("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return h("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return h("plugin:window|is_closable",{label:this.label})}async isVisible(){return h("plugin:window|is_visible",{label:this.label})}async title(){return h("plugin:window|title",{label:this.label})}async theme(){return h("plugin:window|theme",{label:this.label})}async isAlwaysOnTop(){return h("plugin:window|is_always_on_top",{label:this.label})}async activityName(){return h("plugin:window|activity_name",{label:this.label})}async sceneIdentifier(){return h("plugin:window|scene_identifier",{label:this.label})}async center(){return h("plugin:window|center",{label:this.label})}async requestUserAttention(e){let n=null;return e&&(n=e===ee.Critical?{type:"Critical"}:{type:"Informational"}),h("plugin:window|request_user_attention",{label:this.label,value:n})}async setResizable(e){return h("plugin:window|set_resizable",{label:this.label,value:e})}async setEnabled(e){return h("plugin:window|set_enabled",{label:this.label,value:e})}async isEnabled(){return h("plugin:window|is_enabled",{label:this.label})}async setMaximizable(e){return h("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return h("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return h("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return h("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return h("plugin:window|maximize",{label:this.label})}async unmaximize(){return h("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return h("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return h("plugin:window|minimize",{label:this.label})}async unminimize(){return h("plugin:window|unminimize",{label:this.label})}async show(){return h("plugin:window|show",{label:this.label})}async hide(){return h("plugin:window|hide",{label:this.label})}async close(){return h("plugin:window|close",{label:this.label})}async destroy(){return h("plugin:window|destroy",{label:this.label})}async setDecorations(e){return h("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return h("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return h("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return h("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return h("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return h("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return h("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){return h("plugin:window|set_size",{label:this.label,value:e instanceof A?e:new A(e)})}async setMinSize(e){return h("plugin:window|set_min_size",{label:this.label,value:e instanceof A?e:e?new A(e):null})}async setMaxSize(e){return h("plugin:window|set_max_size",{label:this.label,value:e instanceof A?e:e?new A(e):null})}async setSizeConstraints(e){function n(e){return e?{Logical:e}:null}return h("plugin:window|set_size_constraints",{label:this.label,value:{minWidth:n(null==e?void 0:e.minWidth),minHeight:n(null==e?void 0:e.minHeight),maxWidth:n(null==e?void 0:e.maxWidth),maxHeight:n(null==e?void 0:e.maxHeight)}})}async setPosition(e){return h("plugin:window|set_position",{label:this.label,value:e instanceof E?e:new E(e)})}async setFullscreen(e){return h("plugin:window|set_fullscreen",{label:this.label,value:e})}async setSimpleFullscreen(e){return h("plugin:window|set_simple_fullscreen",{label:this.label,value:e})}async setFocus(){return h("plugin:window|set_focus",{label:this.label})}async setFocusable(e){return h("plugin:window|set_focusable",{label:this.label,value:e})}async setIcon(e){return h("plugin:window|set_icon",{label:this.label,value:g(e)})}async setSkipTaskbar(e){return h("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return h("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return h("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return h("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setBackgroundColor(e){return h("plugin:window|set_background_color",{color:e})}async setCursorPosition(e){return h("plugin:window|set_cursor_position",{label:this.label,value:e instanceof E?e:new E(e)})}async setIgnoreCursorEvents(e){return h("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return h("plugin:window|start_dragging",{label:this.label})}async startResizeDragging(e){return h("plugin:window|start_resize_dragging",{label:this.label,value:e})}async setBadgeCount(e){return h("plugin:window|set_badge_count",{label:this.label,value:e})}async setBadgeLabel(e){return h("plugin:window|set_badge_label",{label:this.label,value:e})}async setOverlayIcon(e){return h("plugin:window|set_overlay_icon",{label:this.label,value:e?g(e):void 0})}async setProgressBar(e){return h("plugin:window|set_progress_bar",{label:this.label,value:e})}async setVisibleOnAllWorkspaces(e){return h("plugin:window|set_visible_on_all_workspaces",{label:this.label,value:e})}async setTitleBarStyle(e){return h("plugin:window|set_title_bar_style",{label:this.label,value:e})}async setTheme(e){return h("plugin:window|set_theme",{label:this.label,value:e})}async onResized(e){return this.listen(R.WINDOW_RESIZED,n=>{n.payload=new k(n.payload),e(n)})}async onMoved(e){return this.listen(R.WINDOW_MOVED,n=>{n.payload=new I(n.payload),e(n)})}async onCloseRequested(e){return this.listen(R.WINDOW_CLOSE_REQUESTED,async n=>{const t=new ie(n);await e(t),t.isPreventDefault()||await this.destroy()})}async onDragDropEvent(e){const n=await this.listen(R.DRAG_ENTER,n=>{e({...n,payload:{type:"enter",paths:n.payload.paths,position:new I(n.payload.position)}})}),t=await this.listen(R.DRAG_OVER,n=>{e({...n,payload:{type:"over",position:new I(n.payload.position)}})}),i=await this.listen(R.DRAG_DROP,n=>{e({...n,payload:{type:"drop",paths:n.payload.paths,position:new I(n.payload.position)}})}),r=await this.listen(R.DRAG_LEAVE,n=>{e({...n,payload:{type:"leave"}})});return()=>{n(),i(),t(),r()}}async onFocusChanged(e){const n=await this.listen(R.WINDOW_FOCUS,n=>{e({...n,payload:!0})}),t=await this.listen(R.WINDOW_BLUR,n=>{e({...n,payload:!1})});return()=>{n(),t()}}async onScaleChanged(e){return this.listen(R.WINDOW_SCALE_FACTOR_CHANGED,e)}async onThemeChanged(e){return this.listen(R.WINDOW_THEME_CHANGED,e)}}var oe,ue,ce,de;function pe(e){return null===e?null:{name:e.name,scaleFactor:e.scaleFactor,position:new I(e.position),size:new k(e.size),workArea:{position:new I(e.workArea.position),size:new k(e.workArea.size)}}}!function(e){e.Disabled="disabled",e.Throttle="throttle",e.Suspend="suspend"}(oe||(oe={})),function(e){e.Default="default",e.FluentOverlay="fluentOverlay"}(ue||(ue={})),function(e){e.AppearanceBased="appearanceBased",e.Light="light",e.Dark="dark",e.MediumLight="mediumLight",e.UltraDark="ultraDark",e.Titlebar="titlebar",e.Selection="selection",e.Menu="menu",e.Popover="popover",e.Sidebar="sidebar",e.HeaderView="headerView",e.Sheet="sheet",e.WindowBackground="windowBackground",e.HudWindow="hudWindow",e.FullScreenUI="fullScreenUI",e.Tooltip="tooltip",e.ContentBackground="contentBackground",e.UnderWindowBackground="underWindowBackground",e.UnderPageBackground="underPageBackground",e.Mica="mica",e.Blur="blur",e.Acrylic="acrylic",e.Tabbed="tabbed",e.TabbedDark="tabbedDark",e.TabbedLight="tabbedLight"}(ce||(ce={})),function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"}(de||(de={}));var he=Object.freeze({__proto__:null,CloseRequestedEvent:ie,get Effect(){return ce},get EffectState(){return de},LogicalPosition:T,LogicalSize:v,PhysicalPosition:I,PhysicalSize:k,get ProgressBarStatus(){return ne},get UserAttentionType(){return ee},Window:le,availableMonitors:async function(){return h("plugin:window|available_monitors").then(e=>e.map(pe))},currentMonitor:async function(){return h("plugin:window|current_monitor").then(pe)},cursorPosition:async function(){return h("plugin:window|cursor_position").then(e=>new I(e))},getAllWindows:se,getCurrentWindow:re,monitorFromPoint:async function(e,n){return h("plugin:window|monitor_from_point",{x:e,y:n}).then(pe)},primaryMonitor:async function(){return h("plugin:window|primary_monitor").then(pe)}});function we(){return new ge(re(),window.__TAURI_INTERNALS__.metadata.currentWebview.label,{skip:!0})}async function _e(){return h("plugin:webview|get_all_webviews").then(e=>e.map(e=>new ge(new le(e.windowLabel,{skip:!0}),e.label,{skip:!0})))}const ye=["tauri://created","tauri://error"];class ge{constructor(e,n,t){this.window=e,this.label=n,this.listeners=Object.create(null),(null==t?void 0:t.skip)||h("plugin:webview|create_webview",{windowLabel:e.label,options:{...t,label:n}}).then(async()=>this.emit("tauri://created")).catch(async e=>this.emit("tauri://error",e))}static async getByLabel(e){var n;return null!==(n=(await _e()).find(n=>n.label===e))&&void 0!==n?n:null}static getCurrent(){return we()}static async getAll(){return _e()}async listen(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:N(e,n,{target:{kind:"Webview",label:this.label}})}async once(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:L(e,n,{target:{kind:"Webview",label:this.label}})}async emit(e,n){if(!ye.includes(e))return C(e,n);for(const t of this.listeners[e]||[])t({event:e,id:-1,payload:n})}async emitTo(e,n,t){if(!ye.includes(n))return x(e,n,t);for(const e of this.listeners[n]||[])e({event:n,id:-1,payload:t})}_handleTauriEvent(e,n){return!!ye.includes(e)&&(e in this.listeners?this.listeners[e].push(n):this.listeners[e]=[n],!0)}async position(){return h("plugin:webview|webview_position",{label:this.label}).then(e=>new I(e))}async size(){return h("plugin:webview|webview_size",{label:this.label}).then(e=>new k(e))}async close(){return h("plugin:webview|webview_close",{label:this.label})}async setSize(e){return h("plugin:webview|set_webview_size",{label:this.label,value:e instanceof A?e:new A(e)})}async setPosition(e){return h("plugin:webview|set_webview_position",{label:this.label,value:e instanceof E?e:new E(e)})}async setFocus(){return h("plugin:webview|set_webview_focus",{label:this.label})}async setAutoResize(e){return h("plugin:webview|set_webview_auto_resize",{label:this.label,value:e})}async hide(){return h("plugin:webview|webview_hide",{label:this.label})}async show(){return h("plugin:webview|webview_show",{label:this.label})}async setZoom(e){return h("plugin:webview|set_webview_zoom",{label:this.label,value:e})}async reparent(e){return h("plugin:webview|reparent",{label:this.label,window:"string"==typeof e?e:e.label})}async clearAllBrowsingData(){return h("plugin:webview|clear_all_browsing_data")}async setBackgroundColor(e){return h("plugin:webview|set_webview_background_color",{color:e})}async onDragDropEvent(e){const n=await this.listen(R.DRAG_ENTER,n=>{e({...n,payload:{type:"enter",paths:n.payload.paths,position:new I(n.payload.position)}})}),t=await this.listen(R.DRAG_OVER,n=>{e({...n,payload:{type:"over",position:new I(n.payload.position)}})}),i=await this.listen(R.DRAG_DROP,n=>{e({...n,payload:{type:"drop",paths:n.payload.paths,position:new I(n.payload.position)}})}),r=await this.listen(R.DRAG_LEAVE,n=>{e({...n,payload:{type:"leave"}})});return()=>{n(),i(),t(),r()}}}var be,me,fe=Object.freeze({__proto__:null,Webview:ge,getAllWebviews:_e,getCurrentWebview:we});function ve(){const e=we();return new Ae(e.label,{skip:!0})}async function ke(){return h("plugin:window|get_all_windows").then(e=>e.map(e=>new Ae(e,{skip:!0})))}class Ae{constructor(e,n={}){var t;this.label=e,this.listeners=Object.create(null),(null==n?void 0:n.skip)||h("plugin:webview|create_webview_window",{options:{...n,parent:"string"==typeof n.parent?n.parent:null===(t=n.parent)||void 0===t?void 0:t.label,label:e}}).then(async()=>this.emit("tauri://created")).catch(async e=>this.emit("tauri://error",e))}static async getByLabel(e){var n;const t=null!==(n=(await ke()).find(n=>n.label===e))&&void 0!==n?n:null;return t?new Ae(t.label,{skip:!0}):null}static getCurrent(){return ve()}static async getAll(){return ke()}async listen(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:N(e,n,{target:{kind:"WebviewWindow",label:this.label}})}async once(e,n){return this._handleTauriEvent(e,n)?()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)}:L(e,n,{target:{kind:"WebviewWindow",label:this.label}})}async setBackgroundColor(e){return h("plugin:window|set_background_color",{color:e}).then(()=>h("plugin:webview|set_webview_background_color",{color:e}))}}be=Ae,me=[le,ge],(Array.isArray(me)?me:[me]).forEach(e=>{Object.getOwnPropertyNames(e.prototype).forEach(n=>{var t;"object"==typeof be.prototype&&be.prototype&&n in be.prototype||Object.defineProperty(be.prototype,n,null!==(t=Object.getOwnPropertyDescriptor(e.prototype,n))&&void 0!==t?t:Object.create(null))})});var Te=Object.freeze({__proto__:null,WebviewWindow:Ae,getAllWebviewWindows:ke,getCurrentWebviewWindow:ve});return e.app=f,e.core=_,e.dpi=D,e.event=O,e.image=m,e.menu=J,e.mocks=K,e.path=Y,e.tray=te,e.webview=fe,e.webviewWindow=Te,e.window=he,e}({});window.__TAURI__=__TAURI_IIFE__; diff --git a/crates/tauri/src/app.rs b/crates/tauri/src/app.rs index a1fded17a1ce..d713e600bcd9 100644 --- a/crates/tauri/src/app.rs +++ b/crates/tauri/src/app.rs @@ -648,6 +648,18 @@ impl AppHandle { pub fn set_device_event_filter(&self, filter: DeviceEventFilter) { self.runtime_handle.set_device_event_filter(filter); } + + /// Whether the application supports multiple windows. + #[cfg(target_os = "ios")] + pub fn supports_multiple_windows(&self) -> bool { + let (tx, rx) = std::sync::mpsc::channel(); + self.run_on_main_thread(move || unsafe { + let mtm = objc2::MainThreadMarker::new().unwrap(); + let ui_application = objc2_ui_kit::UIApplication::sharedApplication(mtm); + tx.send(ui_application.supportsMultipleScenes()).unwrap(); + }); + rx.recv().unwrap() + } } impl Manager for AppHandle { @@ -1081,6 +1093,40 @@ macro_rules! shared_app_impl { pub fn invoke_key(&self) -> &str { self.manager.invoke_key() } + + /// Whether the application supports multiple windows. + #[cfg(desktop)] + pub fn supports_multiple_windows(&self) -> bool { + true + } + + /// Whether the application supports multiple windows. + #[cfg(target_os = "android")] + pub fn supports_multiple_windows(&self) -> bool { + let runtime_handle = match self.runtime() { + RuntimeOrDispatch::Runtime(runtime) => runtime.handle(), + RuntimeOrDispatch::RuntimeHandle(handle) => handle, + _ => unreachable!(), + }; + + let (tx, rx) = std::sync::mpsc::channel(); + + runtime_handle.run_on_android_context(move |env, _activity, _webview| { + let supports = (|| { + let version_class = env.find_class("android/os/Build$VERSION")?; + let sdk = env + .get_static_field(version_class, "SDK_INT", "I")? + .i() + .unwrap_or_default(); + crate::Result::Ok(sdk >= 32) + })() + .unwrap_or(false); + + let _ = tx.send(supports); + }); + + rx.recv().unwrap_or(false) + } } impl Listener for $app { @@ -1180,6 +1226,16 @@ impl App { &self.handle } + /// Whether the application supports multiple windows. + #[cfg(target_os = "ios")] + pub fn supports_multiple_windows(&self) -> bool { + unsafe { + let mtm = objc2::MainThreadMarker::new().unwrap(); + let ui_application = objc2_ui_kit::UIApplication::sharedApplication(mtm); + ui_application.supportsMultipleScenes() + } + } + /// Sets the activation policy for the application. It is set to `NSApplicationActivationPolicyRegular` by default. /// /// # Examples diff --git a/crates/tauri/src/app/plugin.rs b/crates/tauri/src/app/plugin.rs index 8b9d3f358daf..20d07fc67736 100644 --- a/crates/tauri/src/app/plugin.rs +++ b/crates/tauri/src/app/plugin.rs @@ -115,6 +115,11 @@ pub fn bundle_type() -> Option { tauri_utils::platform::bundle_type() } +#[command(root = "crate")] +pub fn supports_multiple_windows(app: AppHandle) -> bool { + app.supports_multiple_windows() +} + pub fn init() -> TauriPlugin { Builder::new("app") .invoke_handler(crate::generate_handler![ @@ -131,6 +136,7 @@ pub fn init() -> TauriPlugin { set_app_theme, set_dock_visibility, bundle_type, + supports_multiple_windows, ]) .setup(|_app, _api| { #[cfg(target_os = "android")] diff --git a/packages/api/src/app.ts b/packages/api/src/app.ts index 55b28043631f..82cc51859224 100644 --- a/packages/api/src/app.ts +++ b/packages/api/src/app.ts @@ -274,6 +274,10 @@ async function onBackButtonPress( ) } +async function supportsMultipleWindows(): Promise { + return invoke('plugin:app|supports_multiple_windows') +} + export { getName, getVersion, @@ -288,5 +292,6 @@ export { setDockVisibility, getBundleType, type OnBackButtonPressPayload, - onBackButtonPress + onBackButtonPress, + supportsMultipleWindows } From 12aaef662004b61e7605ebd3d9f9c05512673c92 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 3 Dec 2025 13:06:27 -0300 Subject: [PATCH 22/33] add androidx.lifecycle:lifecycle-process --- crates/tauri-build/src/mobile.rs | 3 ++- crates/tauri-cli/templates/mobile/android/app/build.gradle.kts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/tauri-build/src/mobile.rs b/crates/tauri-build/src/mobile.rs index 9acef2e917be..3dcd4136c8c4 100644 --- a/crates/tauri-build/src/mobile.rs +++ b/crates/tauri-build/src/mobile.rs @@ -15,7 +15,8 @@ pub fn generate_gradle_files(project_dir: PathBuf) -> Result<()> { "// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n".to_string(); let mut app_build_gradle = "// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. val implementation by configurations -dependencies {" +dependencies { + implementation(\"androidx.lifecycle:lifecycle-process:2.10.0\")" .to_string(); for (env, value) in std::env::vars_os() { diff --git a/crates/tauri-cli/templates/mobile/android/app/build.gradle.kts b/crates/tauri-cli/templates/mobile/android/app/build.gradle.kts index 621f7bcaeabe..b1485863abf9 100644 --- a/crates/tauri-cli/templates/mobile/android/app/build.gradle.kts +++ b/crates/tauri-cli/templates/mobile/android/app/build.gradle.kts @@ -68,6 +68,7 @@ dependencies { implementation("androidx.appcompat:appcompat:1.7.1") implementation("androidx.activity:activity-ktx:1.10.1") implementation("com.google.android.material:material:1.12.0") + implementation("androidx.lifecycle:lifecycle-process:2.10.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.4") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") From 00dcedc908c2a715c9f863897c96f9b28f63d930 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 3 Dec 2025 14:11:50 -0300 Subject: [PATCH 23/33] update wry --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3be7d57f4026..e6178519f037 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1319,7 +1319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -1978,7 +1978,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -2348,7 +2348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4319,7 +4319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -6428,7 +6428,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7147,7 +7147,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7160,7 +7160,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -9040,7 +9040,7 @@ dependencies = [ "getrandom 0.2.15", "once_cell", "rustix 0.38.43", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -10356,7 +10356,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -10989,7 +10989,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" version = "0.53.5" -source = "git+https://github.com/tauri-apps/wry?branch=feat/mobile-multi-webview#8d8836116aaaef271775e2958f3e38c4b5d18b04" +source = "git+https://github.com/tauri-apps/wry?branch=feat/mobile-multi-webview#0904dc98d630157d344178dfb7349e8bb34741ab" dependencies = [ "base64 0.22.1", "block2 0.6.0", From 5ffb144a71baaf9d5a20a5a86c4dd9dab9128738 Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Wed, 18 Mar 2026 12:09:18 +0100 Subject: [PATCH 24/33] update lockfile --- Cargo.lock | 444 +++++++++++++++++---- crates/tauri/src/webview/webview_window.rs | 15 - 2 files changed, 369 insertions(+), 90 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22c897595dc2..7e340fe401a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -815,11 +815,11 @@ dependencies = [ [[package]] name = "block2" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.4", ] [[package]] @@ -1461,6 +1461,19 @@ dependencies = [ "libc", ] +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.7.0", + "core-foundation 0.10.0", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + [[package]] name = "core-graphics-types" version = "0.2.0" @@ -1632,6 +1645,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.13.1", + "smallvec", +] + [[package]] name = "cssparser-macros" version = "0.6.1" @@ -1867,7 +1893,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl", + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", ] [[package]] @@ -1882,6 +1917,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "des" version = "0.8.1" @@ -1977,10 +2023,16 @@ dependencies = [ ] [[package]] -name = "dispatch" -version = "0.2.0" +name = "dispatch2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.7.0", + "block2 0.6.2", + "libc", + "objc2 0.6.4", +] [[package]] name = "displaydoc" @@ -2022,6 +2074,21 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser 0.36.0", + "foldhash", + "html5ever 0.38.0", + "precomputed-hash", + "selectors 0.36.0", + "tendril 0.5.0", +] + [[package]] name = "dpi" version = "0.1.1" @@ -2504,6 +2571,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "fontconfig-parser" version = "0.5.7" @@ -3244,10 +3317,20 @@ checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.14.1", "match_token", ] +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever 0.38.0", +] + [[package]] name = "http" version = "0.2.12" @@ -4231,10 +4314,10 @@ version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ - "cssparser", - "html5ever", + "cssparser 0.29.6", + "html5ever 0.29.1", "indexmap 2.11.4", - "selectors", + "selectors 0.24.0", ] [[package]] @@ -4482,9 +4565,20 @@ dependencies = [ "log", "phf 0.11.3", "phf_codegen 0.11.3", - "string_cache", - "string_cache_codegen", - "tendril", + "string_cache 0.8.7", + "string_cache_codegen 0.5.2", + "tendril 0.4.3", +] + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril 0.5.0", + "web_atoms", ] [[package]] @@ -4642,7 +4736,7 @@ dependencies = [ "gtk", "keyboard-types", "libxdo", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", @@ -4741,12 +4835,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[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.6.0+11769913" @@ -5047,9 +5135,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", "objc2-exception-helper", @@ -5062,12 +5150,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" dependencies = [ "bitflags 2.7.0", - "block2 0.6.0", - "objc2 0.6.0", + "block2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.0", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c1948a9be5f469deadbd6bcb86ad7ff9e47b4f632380139722f7d9840c0d42c" +dependencies = [ + "bitflags 2.7.0", + "objc2 0.6.4", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f860f8e841f6d32f754836f51e6bc7777cd7e7053cf18528233f6811d3eceb4" +dependencies = [ + "objc2 0.6.4", + "objc2-foundation 0.3.0", +] + [[package]] name = "objc2-core-foundation" version = "0.3.0" @@ -5075,7 +5184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" dependencies = [ "bitflags 2.7.0", - "objc2 0.6.0", + "objc2 0.6.4", ] [[package]] @@ -5085,7 +5194,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dca602628b65356b6513290a21a6405b4d4027b8b250f0b98dddbb28b7de02" dependencies = [ "bitflags 2.7.0", + "objc2 0.6.4", "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffa6bea72bf42c78b0b34e89c0bafac877d5f80bf91e159a5d96ea7f693ca56" +dependencies = [ + "objc2 0.6.4", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d31f4c5b5192304996badc466aeadffe1411d73a9bbd3b18b6b2ee9d048b07bd" +dependencies = [ + "objc2 0.6.4", + "objc2-foundation 0.3.0", ] [[package]] @@ -5122,8 +5253,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" dependencies = [ "bitflags 2.7.0", - "block2 0.6.0", - "objc2 0.6.0", + "block2 0.6.2", + "objc2 0.6.4", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161a8b87e32610086e1a7a9e9ec39f84459db7b3a0881c1f16ca5a2605581c19" +dependencies = [ + "bitflags 2.7.0", + "objc2 0.6.4", "objc2-core-foundation", ] @@ -5152,6 +5294,18 @@ dependencies = [ "objc2-metal", ] +[[package]] +name = "objc2-quartz-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" +dependencies = [ + "bitflags 2.7.0", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.0", +] + [[package]] name = "objc2-ui-kit" version = "0.3.0" @@ -5159,8 +5313,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1" dependencies = [ "bitflags 2.7.0", - "objc2 0.6.0", + "block2 0.6.2", + "objc2 0.6.4", + "objc2-cloud-kit", + "objc2-core-data", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation 0.3.0", + "objc2-quartz-core 0.3.0", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670fe793adbf3b5e93686d48a05a7ed7ee53dfa65d106ced4805fae8969059b2" +dependencies = [ + "objc2 0.6.4", "objc2-foundation 0.3.0", ] @@ -5171,8 +5343,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b717127e4014b0f9f3e8bba3d3f2acec81f1bde01f656823036e823ed2c94dce" dependencies = [ "bitflags 2.7.0", - "block2 0.6.0", - "objc2 0.6.0", + "block2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", @@ -5754,7 +5926,7 @@ dependencies = [ "aes-gcm", "aes-kw", "argon2", - "base64 0.22.1", + "base64 0.21.7", "bitfield", "block-padding", "blowfish", @@ -5841,6 +6013,17 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + [[package]] name = "phf_codegen" version = "0.8.0" @@ -5861,6 +6044,16 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", +] + [[package]] name = "phf_generator" version = "0.8.0" @@ -5891,6 +6084,16 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.10.0" @@ -5918,6 +6121,19 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "phf_shared" version = "0.8.0" @@ -5945,6 +6161,15 @@ dependencies = [ "siphasher 1.0.1", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -6154,9 +6379,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -7405,14 +7630,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", - "cssparser", + "cssparser 0.29.6", "derive_more 0.99.18", "fxhash", "log", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", - "servo_arc", + "servo_arc 0.2.0", + "smallvec", +] + +[[package]] +name = "selectors" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223b960be86f7d302b7168cdbab137f9bdbf7e04a90c185312fab14dff49dc5f" +dependencies = [ + "bitflags 2.7.0", + "cssparser 0.36.0", + "derive_more 2.0.1", + "log", + "new_debug_unreachable", + "phf 0.13.1", + "phf_codegen 0.13.1", + "precomputed-hash", + "rustc-hash", + "servo_arc 0.4.3", "smallvec", ] @@ -7690,6 +7934,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.10.6" @@ -7954,13 +8207,13 @@ checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ "bytemuck", "cfg_aliases", - "core-graphics", + "core-graphics 0.24.0", "foreign-types 0.5.0", "js-sys", "log", "objc2 0.5.2", "objc2-foundation 0.2.2", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "raw-window-handle", "redox_syscall", "wasm-bindgen", @@ -8108,6 +8361,18 @@ dependencies = [ "serde", ] +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.13.1", + "precomputed-hash", +] + [[package]] name = "string_cache_codegen" version = "0.5.2" @@ -8120,6 +8385,18 @@ dependencies = [ "quote", ] +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", +] + [[package]] name = "strsim" version = "0.11.1" @@ -8346,36 +8623,33 @@ dependencies = [ [[package]] name = "tao" -version = "0.34.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7" +version = "0.34.6" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#ef64b23ca7c65525fb059d9c50664e3c1c1ff0f5" dependencies = [ "bitflags 2.7.0", - "block2 0.6.0", + "block2 0.6.2", "core-foundation 0.10.0", - "core-graphics", + "core-graphics 0.25.0", "crossbeam-channel", - "dispatch", + "dispatch2", "dlopen2", "dpi", "gdkwayland-sys", "gdkx11-sys", "gtk", "jni", - "lazy_static", "libc", "log", "ndk", - "ndk-context", "ndk-sys", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-foundation 0.3.0", + "objc2-ui-kit", "once_cell", "parking_lot", "raw-window-handle", - "scopeguard", - "tao-macros", + "tao-macros 0.1.3 (git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window)", "unicode-segmentation", "url", "windows 0.61.1", @@ -8395,6 +8669,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#ef64b23ca7c65525fb059d9c50664e3c1c1ff0f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "tap" version = "1.0.1" @@ -8442,7 +8726,7 @@ dependencies = [ "log", "mime", "muda", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-foundation 0.3.0", "objc2-ui-kit", @@ -8570,7 +8854,7 @@ dependencies = [ "glob", "handlebars", "heck 0.5.0", - "html5ever", + "html5ever 0.29.1", "ignore", "image", "include_dir", @@ -8766,7 +9050,7 @@ dependencies = [ "byte-unit", "fern", "log", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-foundation 0.3.0", "serde", "serde_json", @@ -8798,7 +9082,7 @@ dependencies = [ "gtk", "http 1.3.1", "jni", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-ui-kit", "objc2-web-kit", "raw-window-handle", @@ -8820,7 +9104,7 @@ dependencies = [ "http 1.3.1", "jni", "log", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "once_cell", "percent-encoding", @@ -8874,7 +9158,7 @@ dependencies = [ "dunce", "getrandom 0.3.3", "glob", - "html5ever", + "html5ever 0.29.1", "http 1.3.1", "infer", "json-patch", @@ -8951,6 +9235,16 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tendril" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" +dependencies = [ + "new_debug_unreachable", + "utf-8", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -9440,7 +9734,7 @@ dependencies = [ "dirs 6.0.0", "libappindicator", "muda", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", @@ -10087,6 +10381,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" +dependencies = [ + "phf 0.13.1", + "phf_codegen 0.13.1", + "string_cache 0.9.0", + "string_cache_codegen 0.6.1", +] + [[package]] name = "webkit2gtk" version = "2.0.2" @@ -10261,7 +10567,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", @@ -10832,27 +11138,25 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e456eeaf7f09413fdc16799782879b2b9f1d264dfdbce4cf7e924df0ef36afb9" +version = "0.54.4" +source = "git+https://github.com/tauri-apps/wry?branch=feat/mobile-multi-webview#5c83d7bba62a4fc8ef9d76d829b4961a36d0255d" dependencies = [ "base64 0.22.1", - "block2 0.6.0", + "block2 0.6.2", "cookie", "crossbeam-channel", "dirs 6.0.0", + "dom_query", "dpi", "dunce", "gdkx11", "gtk", - "html5ever", "http 1.3.1", "javascriptcore-rs", "jni", - "kuchikiki", "libc", "ndk", - "objc2 0.6.0", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", @@ -10863,7 +11167,7 @@ dependencies = [ "raw-window-handle", "sha2", "soup3", - "tao-macros", + "tao-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.12", "tracing", "url", @@ -11237,13 +11541,3 @@ checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" dependencies = [ "zune-core", ] - -[[patch.unused]] -name = "tao" -version = "0.34.6" -source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#d21a9ceee6bc78a6ba9b358df1ed4c35b31d6847" - -[[patch.unused]] -name = "wry" -version = "0.54.4" -source = "git+https://github.com/tauri-apps/wry?branch=feat/mobile-multi-webview#499516fada8ea30b5fcf0daae0c3b247ec2874ca" diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs index 51587e535ee2..86ad050b582b 100644 --- a/crates/tauri/src/webview/webview_window.rs +++ b/crates/tauri/src/webview/webview_window.rs @@ -2041,21 +2041,6 @@ impl WebviewWindow { self.window.set_simple_fullscreen(enable) } - /// Bring the window to front and focus. - pub fn set_focus(&self) -> crate::Result<()> { - self.window.set_focus() - } - - /// Sets whether the window can be focused. - /// - /// ## Platform-specific - /// - /// - **macOS**: If the window is already focused, it is not possible to unfocus it after calling `set_focusable(false)`. - /// In this case, you might consider calling [`Window::set_focus`] but it will move the window to the back i.e. at the bottom in terms of z-order. - pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> { - self.window.set_focusable(focusable) - } - /// Sets this window' icon. pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> { self.window.set_icon(icon) From 38b28ba578e2cd7a7df49433371519465bd552d3 Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Wed, 18 Mar 2026 12:29:15 +0100 Subject: [PATCH 25/33] compile error --- crates/tauri/src/webview/plugin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/tauri/src/webview/plugin.rs b/crates/tauri/src/webview/plugin.rs index 02c68fca72af..5999112eb894 100644 --- a/crates/tauri/src/webview/plugin.rs +++ b/crates/tauri/src/webview/plugin.rs @@ -150,8 +150,8 @@ mod desktop_commands { let x = options.x.context("missing parameter `options.x`")?; let y = options.y.context("missing parameter `options.y`")?; - let width = options.width.unwrap_or(800.); - let height = options.height.unwrap_or(600.); + let width = options.width; + let height = options.height; let builder = crate::webview::WebviewBuilder::from_config(&options); From d9ba0d4449fbf9c64898327b84fcbd17e9f6980a Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Wed, 18 Mar 2026 12:48:47 +0100 Subject: [PATCH 26/33] update tao --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e340fe401a1..8526432c04ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1320,7 +1320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2019,7 +2019,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -4402,7 +4402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -7358,7 +7358,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -8624,7 +8624,7 @@ dependencies = [ [[package]] name = "tao" version = "0.34.6" -source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#ef64b23ca7c65525fb059d9c50664e3c1c1ff0f5" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#64d6c8d948460b4cc41eb4628a518407fb05154e" dependencies = [ "bitflags 2.7.0", "block2 0.6.2", @@ -8672,7 +8672,7 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.3" -source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#ef64b23ca7c65525fb059d9c50664e3c1c1ff0f5" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#64d6c8d948460b4cc41eb4628a518407fb05154e" dependencies = [ "proc-macro2", "quote", @@ -10552,7 +10552,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] From bb064d6ad3e0411be7598aa9d80ff3265446b0d2 Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Wed, 18 Mar 2026 13:21:27 +0100 Subject: [PATCH 27/33] update tao again --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8526432c04ce..de3c22da241b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1320,7 +1320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -2019,7 +2019,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4402,7 +4402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -7358,7 +7358,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -8624,7 +8624,7 @@ dependencies = [ [[package]] name = "tao" version = "0.34.6" -source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#64d6c8d948460b4cc41eb4628a518407fb05154e" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#3d0e11657397eb21f260ffbc9822eedb2102a0b9" dependencies = [ "bitflags 2.7.0", "block2 0.6.2", @@ -8672,7 +8672,7 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.3" -source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#64d6c8d948460b4cc41eb4628a518407fb05154e" +source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#3d0e11657397eb21f260ffbc9822eedb2102a0b9" dependencies = [ "proc-macro2", "quote", @@ -10552,7 +10552,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] From 4058801a79bfaaaf565e055a15379f8b912b6de0 Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Wed, 18 Mar 2026 13:37:17 +0100 Subject: [PATCH 28/33] ios --- crates/tauri/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml index cdc4bc697197..1a2c9419a73d 100644 --- a/crates/tauri/Cargo.toml +++ b/crates/tauri/Cargo.toml @@ -159,6 +159,8 @@ jni = "0.21" libc = "0.2" swift-rs = "1" objc2-ui-kit = { version = "0.3.0", default-features = false, features = [ + "UIApplication", + "UIResponder", "UIView", ] } From d34a185fb2f0917882b2a35cf712cceb55473c0c Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Wed, 18 Mar 2026 14:11:42 +0100 Subject: [PATCH 29/33] ios mock runtime --- crates/tauri/src/test/mock_runtime.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/tauri/src/test/mock_runtime.rs b/crates/tauri/src/test/mock_runtime.rs index d1974fcafd32..722d3e4bb73b 100644 --- a/crates/tauri/src/test/mock_runtime.rs +++ b/crates/tauri/src/test/mock_runtime.rs @@ -546,7 +546,7 @@ impl WindowBuilder for MockWindowBuilder { } #[cfg(target_os = "ios")] - fn requested_by_scene_identifier(self, _identifier: String) -> Self { + fn requested_by_scene_identifier>(self, _identifier: S) -> Self { self } } From 3fecd4e17e05d66e30b76859b9cff70a7683d813 Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Wed, 18 Mar 2026 14:36:27 +0100 Subject: [PATCH 30/33] android mock runtime --- crates/tauri/src/test/mock_runtime.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/tauri/src/test/mock_runtime.rs b/crates/tauri/src/test/mock_runtime.rs index 722d3e4bb73b..7913e9ac014c 100644 --- a/crates/tauri/src/test/mock_runtime.rs +++ b/crates/tauri/src/test/mock_runtime.rs @@ -536,12 +536,12 @@ impl WindowBuilder for MockWindowBuilder { } #[cfg(target_os = "android")] - fn activity_name(self, _class_name: String) -> Self { + fn activity_name>(self, _class_name: S) -> Self { self } #[cfg(target_os = "android")] - fn created_by_activity_name(self, _class_name: String) -> Self { + fn created_by_activity_name>(self, _class_name: S) -> Self { self } From abd327d3d92540d89ebf6fd84a0731d8c782725e Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:29:01 +0100 Subject: [PATCH 31/33] wry and tao from dev branch --- Cargo.lock | 21 +++++++++++---------- Cargo.toml | 4 ++-- crates/tauri-runtime-wry/Cargo.toml | 1 - 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9df8e2ac94b..1774f350ef59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2086,7 +2086,7 @@ dependencies = [ "foldhash", "html5ever 0.38.0", "precomputed-hash", - "selectors 0.36.0", + "selectors 0.36.1", "tendril 0.5.0", ] @@ -5509,7 +5509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.48.0", ] [[package]] @@ -7358,7 +7358,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -7643,9 +7643,9 @@ dependencies = [ [[package]] name = "selectors" -version = "0.36.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223b960be86f7d302b7168cdbab137f9bdbf7e04a90c185312fab14dff49dc5f" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" dependencies = [ "bitflags 2.7.0", "cssparser 0.36.0", @@ -8645,8 +8645,8 @@ dependencies = [ [[package]] name = "tao" -version = "0.34.6" -source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#3d0e11657397eb21f260ffbc9822eedb2102a0b9" +version = "0.34.8" +source = "git+https://github.com/tauri-apps/tao#58781691571b01f18734722feb254a855d51e34b" dependencies = [ "bitflags 2.7.0", "block2 0.6.2", @@ -8670,8 +8670,9 @@ dependencies = [ "objc2-ui-kit", "once_cell", "parking_lot", + "percent-encoding", "raw-window-handle", - "tao-macros 0.1.3 (git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window)", + "tao-macros 0.1.3 (git+https://github.com/tauri-apps/tao)", "unicode-segmentation", "url", "windows 0.61.1", @@ -8694,7 +8695,7 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.3" -source = "git+https://github.com/tauri-apps/tao?branch=feat/mobile-multi-window#3d0e11657397eb21f260ffbc9822eedb2102a0b9" +source = "git+https://github.com/tauri-apps/tao#58781691571b01f18734722feb254a855d51e34b" dependencies = [ "proc-macro2", "quote", @@ -11183,7 +11184,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" version = "0.54.4" -source = "git+https://github.com/tauri-apps/wry?branch=feat/mobile-multi-webview#5c83d7bba62a4fc8ef9d76d829b4961a36d0255d" +source = "git+https://github.com/tauri-apps/wry#a535fd95025d126dd1d6ad10b101b81ed9ae40fe" dependencies = [ "base64 0.22.1", "block2 0.6.2", diff --git a/Cargo.toml b/Cargo.toml index d5e6e4228884..2853f9576c24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,5 +72,5 @@ schemars_derive = { git = 'https://github.com/tauri-apps/schemars.git', branch = tauri = { path = "./crates/tauri" } tauri-plugin = { path = "./crates/tauri-plugin" } tauri-utils = { path = "./crates/tauri-utils" } -wry = { git = "https://github.com/tauri-apps/wry", branch = "feat/mobile-multi-webview" } -tao = { git = "https://github.com/tauri-apps/tao", branch = "feat/mobile-multi-window" } +wry = { git = "https://github.com/tauri-apps/wry" } +tao = { git = "https://github.com/tauri-apps/tao" } diff --git a/crates/tauri-runtime-wry/Cargo.toml b/crates/tauri-runtime-wry/Cargo.toml index 10ad16be189c..298f8930bc33 100644 --- a/crates/tauri-runtime-wry/Cargo.toml +++ b/crates/tauri-runtime-wry/Cargo.toml @@ -14,7 +14,6 @@ rust-version.workspace = true [dependencies] wry = { version = "0.54.0", default-features = false, features = [ - "drag-drop", "protocol", "os-webview", "linux-body", From d96f1cca1b6f759906c0f3fcdcc48338ba1537c3 Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:42:37 +0100 Subject: [PATCH 32/33] tao/wry cratesio --- .changes/tauri-dbus.md | 5 ++++ Cargo.lock | 45 +++++++++++++++++++---------- Cargo.toml | 2 -- crates/tauri-runtime-wry/Cargo.toml | 7 +++-- crates/tauri/Cargo.toml | 10 ++++++- 5 files changed, 47 insertions(+), 22 deletions(-) create mode 100644 .changes/tauri-dbus.md diff --git a/.changes/tauri-dbus.md b/.changes/tauri-dbus.md new file mode 100644 index 000000000000..cab4b325103c --- /dev/null +++ b/.changes/tauri-dbus.md @@ -0,0 +1,5 @@ +--- +tauri: minor:feat +--- + +Added `dbus` feature flag (enabled by default) which is required for theme detection on Linux. diff --git a/Cargo.lock b/Cargo.lock index 1774f350ef59..3ee41ee22db5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1812,6 +1812,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "dbus" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b3aa68d7e7abee336255bd7248ea965cc393f3e70411135a6f6a4b651345d4" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.59.0", +] + [[package]] name = "der" version = "0.7.9" @@ -4376,6 +4387,15 @@ version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + [[package]] name = "libfuzzer-sys" version = "0.4.8" @@ -8645,14 +8665,16 @@ dependencies = [ [[package]] name = "tao" -version = "0.34.8" -source = "git+https://github.com/tauri-apps/tao#58781691571b01f18734722feb254a855d51e34b" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cf65722394c2ac443e80120064987f8914ee1d4e4e36e63cdf10f2990f01159" dependencies = [ "bitflags 2.7.0", "block2 0.6.2", "core-foundation 0.10.0", "core-graphics 0.25.0", "crossbeam-channel", + "dbus", "dispatch2", "dlopen2", "dpi", @@ -8672,7 +8694,7 @@ dependencies = [ "parking_lot", "percent-encoding", "raw-window-handle", - "tao-macros 0.1.3 (git+https://github.com/tauri-apps/tao)", + "tao-macros", "unicode-segmentation", "url", "windows 0.61.1", @@ -8692,16 +8714,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "tao-macros" -version = "0.1.3" -source = "git+https://github.com/tauri-apps/tao#58781691571b01f18734722feb254a855d51e34b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "tap" version = "1.0.1" @@ -11183,8 +11195,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.54.4" -source = "git+https://github.com/tauri-apps/wry#a535fd95025d126dd1d6ad10b101b81ed9ae40fe" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3013fd6116aac351dd2e18f349b28b2cfef3a5ff3253a9d0ce2d7193bb1b4429" dependencies = [ "base64 0.22.1", "block2 0.6.2", @@ -11212,7 +11225,7 @@ dependencies = [ "raw-window-handle", "sha2", "soup3", - "tao-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tao-macros", "thiserror 2.0.12", "tracing", "url", diff --git a/Cargo.toml b/Cargo.toml index 2853f9576c24..670a647672c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,5 +72,3 @@ schemars_derive = { git = 'https://github.com/tauri-apps/schemars.git', branch = tauri = { path = "./crates/tauri" } tauri-plugin = { path = "./crates/tauri-plugin" } tauri-utils = { path = "./crates/tauri-utils" } -wry = { git = "https://github.com/tauri-apps/wry" } -tao = { git = "https://github.com/tauri-apps/tao" } diff --git a/crates/tauri-runtime-wry/Cargo.toml b/crates/tauri-runtime-wry/Cargo.toml index 298f8930bc33..675f7fd8c876 100644 --- a/crates/tauri-runtime-wry/Cargo.toml +++ b/crates/tauri-runtime-wry/Cargo.toml @@ -13,12 +13,12 @@ edition.workspace = true rust-version.workspace = true [dependencies] -wry = { version = "0.54.0", default-features = false, features = [ +wry = { version = "0.55.0", default-features = false, features = [ "protocol", "os-webview", "linux-body", ] } -tao = { version = "0.34.5", default-features = false, features = ["rwh_06"] } +tao = { version = "0.35.0", default-features = false, features = ["rwh_06"] } tauri-runtime = { version = "2.10.1", path = "../tauri-runtime" } tauri-utils = { version = "2.8.3", path = "../tauri-utils" } raw-window-handle = "0.6" @@ -59,7 +59,7 @@ objc2-app-kit = { version = "0.3", default-features = false, features = [ jni = "0.21" [features] -default = ["x11"] +default = ["x11", "dbus"] devtools = ["wry/devtools", "tauri-runtime/devtools"] x11 = ["tao/x11", "wry/x11"] macos-private-api = [ @@ -73,3 +73,4 @@ tracing = ["dep:tracing", "wry/tracing"] macos-proxy = ["wry/mac-proxy"] unstable = [] common-controls-v6 = [] +dbus = ["tao/dbus"] diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml index 1a2c9419a73d..9326713e5da1 100644 --- a/crates/tauri/Cargo.toml +++ b/crates/tauri/Cargo.toml @@ -184,9 +184,17 @@ cargo_toml = "0.22" http-range = "0.1.5" [features] -default = ["wry", "compression", "common-controls-v6", "dynamic-acl", "x11"] +default = [ + "wry", + "compression", + "common-controls-v6", + "dynamic-acl", + "x11", + "dbus", +] unstable = ["tauri-runtime-wry?/unstable"] x11 = ["tauri-runtime-wry?/x11"] +dbus = ["tauri-runtime-wry?/dbus"] common-controls-v6 = [ "tray-icon?/common-controls-v6", "muda/common-controls-v6", From 865dcedc5eaa6f98f68710af8d24596fd5c9e00b Mon Sep 17 00:00:00 2001 From: FabianLars <30730186+FabianLars@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:55:33 +0100 Subject: [PATCH 33/33] dbus docs --- crates/tauri/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/tauri/src/lib.rs b/crates/tauri/src/lib.rs index 88214fad1657..38cbbc3a58ec 100644 --- a/crates/tauri/src/lib.rs +++ b/crates/tauri/src/lib.rs @@ -13,6 +13,7 @@ //! - **wry** *(enabled by default)*: Enables the [wry](https://github.com/tauri-apps/wry) runtime. Only disable it if you want a custom runtime. //! - **common-controls-v6** *(enabled by default)*: Enables [Common Controls v6](https://learn.microsoft.com/en-us/windows/win32/controls/common-control-versions) support on Windows, mainly for the predefined `about` menu item. //! - **x11** *(enabled by default)*: Enables X11 support. Disable this if you only target Wayland. +//! - **dbus** *(enabled by default)*: Enables dbus dependency for theme support on Linux. Disable this if you do not need theme support or don't want to build the dbus rust crate. The WebView dependencies use dbus either way. //! - **unstable**: Enables unstable features. Be careful, it might introduce breaking changes in future minor releases. //! - **tracing**: Enables [`tracing`](https://docs.rs/tracing/latest/tracing) for window startup, plugins, `Window::eval`, events, IPC, updater and custom protocol request handlers. //! - **test**: Enables the [`mod@test`] module exposing unit test helpers.