From 7a32ccae67d3efcbe20c5748c1f85a23655b9294 Mon Sep 17 00:00:00 2001
From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com>
Date: Wed, 30 Sep 2026 17:34:00 +0100
Subject: [PATCH 1/4] fix(desktop): isolate blocking folder reveal from Tokio
---
.github/workflows/ci.yml | 6 +-
Cargo.lock | 4 +-
Cargo.toml | 2 +
apps/desktop/src-tauri/src/diagnostics.rs | 5 +-
vendor/tauri-plugin-opener/.gitignore | 1 +
vendor/tauri-plugin-opener/CAP-CHANGES.md | 33 ++
vendor/tauri-plugin-opener/CHANGELOG.md | 59 ++++
vendor/tauri-plugin-opener/Cargo.toml | 135 ++++++++
vendor/tauri-plugin-opener/LICENSE.spdx | 20 ++
vendor/tauri-plugin-opener/LICENSE_APACHE-2.0 | 177 +++++++++++
vendor/tauri-plugin-opener/LICENSE_MIT | 21 ++
vendor/tauri-plugin-opener/README.md | 143 +++++++++
vendor/tauri-plugin-opener/SECURITY.md | 23 ++
vendor/tauri-plugin-opener/android/.gitignore | 2 +
.../android/build.gradle.kts | 40 +++
.../android/proguard-rules.pro | 21 ++
.../android/settings.gradle | 2 +
.../android/src/main/AndroidManifest.xml | 3 +
.../android/src/main/java/OpenerPlugin.kt | 44 +++
vendor/tauri-plugin-opener/api-iife.js | 26 ++
vendor/tauri-plugin-opener/build.rs | 136 ++++++++
.../tauri-plugin-opener/ios/Package.resolved | 16 +
vendor/tauri-plugin-opener/ios/Package.swift | 34 ++
.../ios/Sources/OpenerPlugin.swift | 46 +++
.../permissions/allow-default-urls.toml | 17 +
.../autogenerated/commands/open_path.toml | 13 +
.../autogenerated/commands/open_url.toml | 13 +
.../commands/reveal_item_in_dir.toml | 13 +
.../permissions/autogenerated/reference.md | 111 +++++++
.../permissions/default.toml | 10 +
vendor/tauri-plugin-opener/src/commands.rs | 158 +++++++++
vendor/tauri-plugin-opener/src/config.rs | 19 ++
vendor/tauri-plugin-opener/src/error.rs | 57 ++++
vendor/tauri-plugin-opener/src/init-iife.js | 19 ++
vendor/tauri-plugin-opener/src/lib.rs | 238 ++++++++++++++
vendor/tauri-plugin-opener/src/open.rs | 61 ++++
.../src/reveal_item_in_dir.rs | 299 ++++++++++++++++++
vendor/tauri-plugin-opener/src/scope.rs | 141 +++++++++
vendor/tauri-plugin-opener/src/scope_entry.rs | 36 +++
39 files changed, 2198 insertions(+), 6 deletions(-)
create mode 100644 vendor/tauri-plugin-opener/.gitignore
create mode 100644 vendor/tauri-plugin-opener/CAP-CHANGES.md
create mode 100644 vendor/tauri-plugin-opener/CHANGELOG.md
create mode 100644 vendor/tauri-plugin-opener/Cargo.toml
create mode 100644 vendor/tauri-plugin-opener/LICENSE.spdx
create mode 100644 vendor/tauri-plugin-opener/LICENSE_APACHE-2.0
create mode 100644 vendor/tauri-plugin-opener/LICENSE_MIT
create mode 100644 vendor/tauri-plugin-opener/README.md
create mode 100644 vendor/tauri-plugin-opener/SECURITY.md
create mode 100644 vendor/tauri-plugin-opener/android/.gitignore
create mode 100644 vendor/tauri-plugin-opener/android/build.gradle.kts
create mode 100644 vendor/tauri-plugin-opener/android/proguard-rules.pro
create mode 100644 vendor/tauri-plugin-opener/android/settings.gradle
create mode 100644 vendor/tauri-plugin-opener/android/src/main/AndroidManifest.xml
create mode 100644 vendor/tauri-plugin-opener/android/src/main/java/OpenerPlugin.kt
create mode 100644 vendor/tauri-plugin-opener/api-iife.js
create mode 100644 vendor/tauri-plugin-opener/build.rs
create mode 100644 vendor/tauri-plugin-opener/ios/Package.resolved
create mode 100644 vendor/tauri-plugin-opener/ios/Package.swift
create mode 100644 vendor/tauri-plugin-opener/ios/Sources/OpenerPlugin.swift
create mode 100644 vendor/tauri-plugin-opener/permissions/allow-default-urls.toml
create mode 100644 vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_path.toml
create mode 100644 vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_url.toml
create mode 100644 vendor/tauri-plugin-opener/permissions/autogenerated/commands/reveal_item_in_dir.toml
create mode 100644 vendor/tauri-plugin-opener/permissions/autogenerated/reference.md
create mode 100644 vendor/tauri-plugin-opener/permissions/default.toml
create mode 100644 vendor/tauri-plugin-opener/src/commands.rs
create mode 100644 vendor/tauri-plugin-opener/src/config.rs
create mode 100644 vendor/tauri-plugin-opener/src/error.rs
create mode 100644 vendor/tauri-plugin-opener/src/init-iife.js
create mode 100644 vendor/tauri-plugin-opener/src/lib.rs
create mode 100644 vendor/tauri-plugin-opener/src/open.rs
create mode 100644 vendor/tauri-plugin-opener/src/reveal_item_in_dir.rs
create mode 100644 vendor/tauri-plugin-opener/src/scope.rs
create mode 100644 vendor/tauri-plugin-opener/src/scope_entry.rs
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f1bdd0e32d7..303be58e72d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -51,6 +51,7 @@ jobs:
- 'scripts/run-gpui-build*'
- 'scripts/sync-desktop-versions.mjs'
- 'scripts/verify-gpui-release-inputs.mjs'
+ - 'vendor/tauri-plugin-opener/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'bun.lock'
@@ -63,6 +64,7 @@ jobs:
- 'apps/desktop-gpui/**'
- 'crates/**'
- 'apps/desktop/src-tauri/**'
+ - 'vendor/tauri-plugin-opener/**'
- 'Cargo.toml'
- 'Cargo.lock'
tauri-plugins:
@@ -222,7 +224,7 @@ jobs:
- name: Test desktop installation and storage helpers
shell: bash
- run: cargo test --locked -p cap-utils -p cap-cli-install --lib
+ run: cargo test --locked -p cap-utils -p cap-cli-install -p tauri-plugin-opener --lib
- name: Build desktop binaries
shell: bash
@@ -318,7 +320,7 @@ jobs:
run: |
node --test scripts/linux-bundle-config.test.mjs scripts/finalize-linux-appimage.test.mjs
bun run --cwd apps/desktop vitest run scripts/prepare.test.js
- cargo test --locked -p cap-utils -p cap-cli-install --lib
+ cargo test --locked -p cap-utils -p cap-cli-install -p tauri-plugin-opener --lib
cargo test --locked -p cap --bin cap record::tests
cargo test --locked -p cap-recording --lib -- --test-threads=1
cargo test --locked -p cap-enc-ffmpeg
diff --git a/Cargo.lock b/Cargo.lock
index ffb628ea4f4..90c88f49856 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10802,8 +10802,6 @@ dependencies = [
[[package]]
name = "tauri-plugin-opener"
version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5"
dependencies = [
"dunce",
"glob",
@@ -10815,7 +10813,9 @@ dependencies = [
"serde_json",
"tauri",
"tauri-plugin",
+ "tempfile",
"thiserror 2.0.16",
+ "tokio",
"url",
"windows 0.61.3",
"zbus",
diff --git a/Cargo.toml b/Cargo.toml
index 8a9be0ffb64..2749ec83681 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,6 +5,7 @@ members = [
"apps/desktop/src-tauri",
"crates/*",
"crates/workspace-hack",
+ "vendor/tauri-plugin-opener",
]
[workspace.dependencies]
@@ -132,6 +133,7 @@ cidre = { git = "https://github.com/CapSoftware/cidre", rev = "bf84b67079a8" }
# wgpu = { git = "https://github.com/gfx-rs/wgpu", rev = "cd41a6e32a6239b65d1cecbeccde6a43a100914a" }
wgpu-hal = { path = "vendor/wgpu-hal" }
tao = { path = "vendor/tao" }
+tauri-plugin-opener = { path = "vendor/tauri-plugin-opener" }
# https://github.com/CapSoftware/reqwest/commit/9b5ecbd5210a9510fde766015cabb724c1e70d2e
reqwest = { git = "https://github.com/CapSoftware/reqwest", rev = "9b5ecbd5210a9510fde766015cabb724c1e70d2e" }
diff --git a/apps/desktop/src-tauri/src/diagnostics.rs b/apps/desktop/src-tauri/src/diagnostics.rs
index 286023f0add..761b4375e3a 100644
--- a/apps/desktop/src-tauri/src/diagnostics.rs
+++ b/apps/desktop/src-tauri/src/diagnostics.rs
@@ -548,8 +548,9 @@ pub async fn upload_diagnostic_report(app: AppHandle, report_path: String) -> Re
pub async fn reveal_diagnostic_report(app: AppHandle, report_path: String) -> Result<(), String> {
let path = validate_report_path(&app, &report_path)?;
- app.opener()
- .reveal_item_in_dir(&path)
+ tokio::task::spawn_blocking(move || app.opener().reveal_item_in_dir(&path))
+ .await
+ .map_err(|e| format!("Failed to reveal diagnostic report: {e}"))?
.map_err(|e| format!("Failed to reveal diagnostic report: {e}"))
}
diff --git a/vendor/tauri-plugin-opener/.gitignore b/vendor/tauri-plugin-opener/.gitignore
new file mode 100644
index 00000000000..53a0a824d96
--- /dev/null
+++ b/vendor/tauri-plugin-opener/.gitignore
@@ -0,0 +1 @@
+/permissions/schemas/
diff --git a/vendor/tauri-plugin-opener/CAP-CHANGES.md b/vendor/tauri-plugin-opener/CAP-CHANGES.md
new file mode 100644
index 00000000000..1f0056ea099
--- /dev/null
+++ b/vendor/tauri-plugin-opener/CAP-CHANGES.md
@@ -0,0 +1,33 @@
+# Cap changes to tauri-plugin-opener
+
+Vendored from the crates.io `tauri-plugin-opener` 2.5.0 archive, SHA-256
+`786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5`.
+The archive identifies upstream commit
+`2371804172fa852ef3c127b2398c54bc35f74950` in
+[tauri-apps/plugins-workspace](https://github.com/tauri-apps/plugins-workspace/tree/2371804172fa852ef3c127b2398c54bc35f74950/plugins/opener).
+
+The async `reveal_item_in_dir` IPC command previously called the synchronous
+reveal implementation on a Tokio worker. On Linux, blocking zbus can enter
+its own Tokio runtime and panic. The command now awaits a blocking worker
+and propagates both backend errors and worker failures. Its name, arguments,
+permission definitions, path canonicalization, and native backends are unchanged.
+All existing JavaScript callers retain the same IPC and permission boundary.
+
+Tests cover both Tokio runtime flavors, exact path forwarding, backend errors,
+worker panics, validation of all paths, and the real Linux zbus backend with the
+Tokio feature enabled. The Linux test permits a missing session bus or file
+manager; it does not establish successful graphical file selection.
+
+The workspace uses this source through `[patch.crates-io]`. The crate is also a
+workspace member so Cargo can run its regression tests. Dev dependencies reuse
+versions already in the workspace lockfile. No runtime dependency was upgraded.
+
+Packaging omits the upstream lockfile, original workspace manifest, npm build
+configuration, and guest TypeScript sources. The shipped JavaScript bundles are
+retained with formatting only. One trailing space in the Android build file is
+removed. Rust source is rustfmt-formatted, and
+the build regenerates the ignored permission schema. Both upstream licenses and
+all platform source files are retained.
+
+Current upstream still has the direct synchronous command call. Revisit this
+patch when upstream supplies an equivalent async boundary and retain the tests.
diff --git a/vendor/tauri-plugin-opener/CHANGELOG.md b/vendor/tauri-plugin-opener/CHANGELOG.md
new file mode 100644
index 00000000000..94c5c1bc0c3
--- /dev/null
+++ b/vendor/tauri-plugin-opener/CHANGELOG.md
@@ -0,0 +1,59 @@
+# Changelog
+
+## \[2.5.0]
+
+### enhance
+
+- [`b8056f48`](https://github.com/tauri-apps/plugins-workspace/commit/b8056f484c7144af095d4d6ded1e8adbb9b8a865) ([#2897](https://github.com/tauri-apps/plugins-workspace/pull/2897) by [@petersamokhin](https://github.com/tauri-apps/plugins-workspace/../../petersamokhin)) Allow reveal multiple items in the file explorer.
+
+## \[2.4.0]
+
+- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+
+## \[2.3.1]
+
+### feat
+
+- [`2aec8ff4`](https://github.com/tauri-apps/plugins-workspace/commit/2aec8ff4c41d178ea9804f7b6eff343c726be015) ([#2803](https://github.com/tauri-apps/plugins-workspace/pull/2803) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Add `inAppBrowser` option to open URLs in an in-app browser on Android and iOS.
+
+## \[2.3.0]
+
+- [`ce9888a2`](https://github.com/tauri-apps/plugins-workspace/commit/ce9888a2d4c9b449bd2a306f0fa6c76507fd46d3) ([#2762](https://github.com/tauri-apps/plugins-workspace/pull/2762)) Similar to the `fs` plugin the `opener` plugin now supports a `requireLiteralLeadingDot` configuration in `tauri.conf.json`.
+
+## \[2.2.7]
+
+- [`6c9e08dc`](https://github.com/tauri-apps/plugins-workspace/commit/6c9e08dccb3ac99fccfce586fa2b69717ba81b52) ([#2695](https://github.com/tauri-apps/plugins-workspace/pull/2695) by [@ShaunSHamilton](https://github.com/tauri-apps/plugins-workspace/../../ShaunSHamilton)) Adjust `open_url` url type to allow `URL`
+- [`dde6f3c3`](https://github.com/tauri-apps/plugins-workspace/commit/dde6f3c31c1b79942abb556be31757dc583f509e) ([#2549](https://github.com/tauri-apps/plugins-workspace/pull/2549) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Update `windows` crate to 0.61 to align with Tauri 2.5
+
+## \[2.2.6]
+
+- [`1a984659`](https://github.com/tauri-apps/plugins-workspace/commit/1a9846599b6a71faf330845847a30f6bf9735898) ([#2469](https://github.com/tauri-apps/plugins-workspace/pull/2469) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Update `objc2` crate to 0.6. No user facing changes.
+- [`71f95c9f`](https://github.com/tauri-apps/plugins-workspace/commit/71f95c9f05b29cf1be586849614c0b007757c15d) ([#2445](https://github.com/tauri-apps/plugins-workspace/pull/2445) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `windows` crate to 0.60 to match Tauri 2.3.0. No user facing changes.
+
+## \[2.2.5]
+
+- [`5b821181`](https://github.com/tauri-apps/plugins-workspace/commit/5b8211815825ddae2dcc0c00520e0cfdff002763) ([#2332](https://github.com/tauri-apps/plugins-workspace/pull/2332) by [@betamos](https://github.com/tauri-apps/plugins-workspace/../../betamos)) Fix broken JS commands `opener.openPath` and `opener.openUrl` on mobile.
+
+## \[2.2.4]
+
+- [`da5c59e2`](https://github.com/tauri-apps/plugins-workspace/commit/da5c59e2fe879d177e3cfd52fcacce85440423cb) ([#2271](https://github.com/tauri-apps/plugins-workspace/pull/2271) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `zbus` dependency to version 5. No API changes.
+
+## \[2.2.3]
+
+- [`a9ac1e3c`](https://github.com/tauri-apps/plugins-workspace/commit/a9ac1e3c939cec4338a9422ef02323c1d4dde6cd) ([#2253](https://github.com/tauri-apps/plugins-workspace/pull/2253) by [@betamos](https://github.com/tauri-apps/plugins-workspace/../../betamos)) Return an error in `open_path` if the file does not exist when opening with default application.
+
+## \[2.2.2]
+
+- [`ee0f65de`](https://github.com/tauri-apps/plugins-workspace/commit/ee0f65de5c645c244c5f0b638e0e0aab687cb9bf) ([#2207](https://github.com/tauri-apps/plugins-workspace/pull/2207) by [@universalappfactory](https://github.com/tauri-apps/plugins-workspace/../../universalappfactory)) Fixed OpenerPlugin packagename for android
+
+## \[2.2.1]
+
+- [`18dffc9d`](https://github.com/tauri-apps/plugins-workspace/commit/18dffc9dfecaf0c900e233e041d9ca36c92834b5) ([#2189](https://github.com/tauri-apps/plugins-workspace/pull/2189) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Fix usage on iOS.
+
+## \[2.2.0]
+
+- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+
+## \[2.0.0]
+
+- [`383e636a`](https://github.com/tauri-apps/plugins-workspace/commit/383e636a8e595aec1300999a8aeb7d9bf8c14632) ([#2019](https://github.com/tauri-apps/plugins-workspace/pull/2019) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Initial Release
diff --git a/vendor/tauri-plugin-opener/Cargo.toml b/vendor/tauri-plugin-opener/Cargo.toml
new file mode 100644
index 00000000000..35229ce3291
--- /dev/null
+++ b/vendor/tauri-plugin-opener/Cargo.toml
@@ -0,0 +1,135 @@
+[package]
+edition = "2021"
+name = "tauri-plugin-opener"
+version = "2.5.0"
+authors = ["Tauri Programme within The Commons Conservancy"]
+build = "build.rs"
+links = "tauri-plugin-opener"
+autolib = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
+description = "Open files and URLs using their default application."
+readme = "README.md"
+license = "Apache-2.0 OR MIT"
+repository = "https://github.com/tauri-apps/plugins-workspace"
+
+[package.metadata.docs.rs]
+rustc-args = [
+ "--cfg",
+ "docsrs",
+]
+rustdoc-args = [
+ "--cfg",
+ "docsrs",
+]
+
+[package.metadata.platforms.support.android]
+level = "partial"
+notes = "Only allows to open URLs via `open`"
+
+[package.metadata.platforms.support.ios]
+level = "partial"
+notes = "Only allows to open URLs via `open`"
+
+[package.metadata.platforms.support.linux]
+level = "full"
+notes = ""
+
+[package.metadata.platforms.support.macos]
+level = "full"
+notes = ""
+
+[package.metadata.platforms.support.windows]
+level = "full"
+notes = ""
+
+[lib]
+name = "tauri_plugin_opener"
+path = "src/lib.rs"
+
+[dependencies.dunce]
+version = "1"
+
+[dependencies.glob]
+version = "0.3"
+
+[dependencies.open]
+version = "5"
+features = ["shellexecute-on-windows"]
+
+[dependencies.serde]
+version = "1"
+features = ["derive"]
+
+[dependencies.serde_json]
+version = "1"
+
+[dependencies.tauri]
+version = "2.8.2"
+default-features = false
+
+[dependencies.thiserror]
+version = "2"
+
+[build-dependencies.schemars]
+version = "0.8"
+
+[build-dependencies.serde]
+version = "1"
+features = ["derive"]
+
+[build-dependencies.tauri-plugin]
+version = "2.4"
+features = ["build"]
+
+[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies.url]
+version = "2"
+
+[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies.zbus]
+version = "5.9"
+
+[target.'cfg(target_os = "ios")'.dependencies.tauri]
+version = "2.8.2"
+features = ["wry"]
+default-features = false
+
+[target.'cfg(target_os = "macos")'.dependencies.objc2-app-kit]
+version = "0.3"
+features = [
+ "std",
+ "NSWorkspace",
+]
+default-features = false
+
+[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
+version = "0.3"
+features = [
+ "std",
+ "NSURL",
+ "NSArray",
+ "NSString",
+]
+default-features = false
+
+[target."cfg(windows)".dependencies.windows]
+version = "0.61"
+features = [
+ "Win32_Foundation",
+ "Win32_UI_Shell_Common",
+ "Win32_UI_WindowsAndMessaging",
+ "Win32_System_Com",
+ "Win32_System_Registry",
+]
+
+[dev-dependencies.tokio]
+version = "1"
+features = ["macros", "rt-multi-thread"]
+
+[dev-dependencies.tempfile]
+version = "3"
+
+[target.'cfg(target_os = "linux")'.dev-dependencies.zbus]
+version = "5.9"
+features = ["tokio"]
diff --git a/vendor/tauri-plugin-opener/LICENSE.spdx b/vendor/tauri-plugin-opener/LICENSE.spdx
new file mode 100644
index 00000000000..cdd0df5ad76
--- /dev/null
+++ b/vendor/tauri-plugin-opener/LICENSE.spdx
@@ -0,0 +1,20 @@
+SPDXVersion: SPDX-2.1
+DataLicense: CC0-1.0
+PackageName: tauri
+DataFormat: SPDXRef-1
+PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy
+PackageHomePage: https://tauri.app
+PackageLicenseDeclared: Apache-2.0
+PackageLicenseDeclared: MIT
+PackageCopyrightText: 2019-2022, The Tauri Programme in the Commons Conservancy
+PackageSummary: Tauri is a rust project that enables developers to make secure
+and small desktop applications using a web frontend.
+
+PackageComment: The package includes the following libraries; see
+Relationship information.
+
+Created: 2019-05-20T09:00:00Z
+PackageDownloadLocation: git://github.com/tauri-apps/tauri
+PackageDownloadLocation: git+https://github.com/tauri-apps/tauri.git
+PackageDownloadLocation: git+ssh://github.com/tauri-apps/tauri.git
+Creator: Person: Daniel Thompson-Yvetot
\ No newline at end of file
diff --git a/vendor/tauri-plugin-opener/LICENSE_APACHE-2.0 b/vendor/tauri-plugin-opener/LICENSE_APACHE-2.0
new file mode 100644
index 00000000000..4947287f7b5
--- /dev/null
+++ b/vendor/tauri-plugin-opener/LICENSE_APACHE-2.0
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/vendor/tauri-plugin-opener/LICENSE_MIT b/vendor/tauri-plugin-opener/LICENSE_MIT
new file mode 100644
index 00000000000..4d754725663
--- /dev/null
+++ b/vendor/tauri-plugin-opener/LICENSE_MIT
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 - Present Tauri Apps Contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/vendor/tauri-plugin-opener/README.md b/vendor/tauri-plugin-opener/README.md
new file mode 100644
index 00000000000..bcb9265ca48
--- /dev/null
+++ b/vendor/tauri-plugin-opener/README.md
@@ -0,0 +1,143 @@
+
+
+
+
+| Platform | Supported |
+| -------- | --------- |
+| Linux | ✓ |
+| Windows | ✓ |
+| macOS | ✓ |
+| Android | ? |
+| iOS | ? |
+
+## Install
+
+_This plugin requires a Rust version of at least **1.77.2**_
+
+There are three general methods of installation that we can recommend.
+
+1. Use crates.io and npm (easiest, and requires you to trust that our publishing pipeline worked)
+2. Pull sources directly from Github using git tags / revision hashes (most secure)
+3. Git submodule install this repo in your tauri project and then use file protocol to ingest the source (most secure, but inconvenient to use)
+
+Install the Core plugin by adding the following to your `Cargo.toml` file:
+
+`src-tauri/Cargo.toml`
+
+```toml
+[dependencies]
+tauri-plugin-opener = "2.0.0"
+# alternatively with Git:
+tauri-plugin-opener = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
+```
+
+You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
+
+
+
+```sh
+pnpm add @tauri-apps/plugin-opener
+# or
+npm add @tauri-apps/plugin-opener
+# or
+yarn add @tauri-apps/plugin-opener
+```
+
+## Usage
+
+First you need to register the core plugin with Tauri:
+
+`src-tauri/src/lib.rs`
+
+```rust
+fn main() {
+ tauri::Builder::default()
+ .plugin(tauri_plugin_opener::init())
+ .run(tauri::generate_context!())
+ .expect("error while running tauri application");
+}
+```
+
+Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
+
+```javascript
+import { openUrl, openPath, revealItemInDir } from '@tauri-apps/plugin-opener'
+
+// Opens the URL in the default browser
+await openUrl('https://example.com')
+// Or with a specific browser/app
+await openUrl('https://example.com', 'firefox')
+
+// Opens the path with the system's default app
+await openPath('/path/to/file')
+// Or with a specific app
+await openPath('/path/to/file', 'firefox')
+
+// Reveal a path with the system's default explorer
+await revealItemInDir('/path/to/file')
+
+// Reveal multiple paths with the system's default explorer
+// Note: will be renamed to `revealItemsInDir` in the next major version
+await revealItemInDir(['/path/to/file', '/path/to/another/file'])
+```
+
+### Usage from Rust
+
+You can also use those APIs from Rust:
+
+```rust
+use tauri_plugin_opener::OpenerExt;
+
+fn main() {
+ tauri::Builder::default()
+ .plugin(tauri_plugin_opener::init())
+ .setup(|app| {
+ let opener = app.opener();
+
+ // Opens the URL in the default browser
+ opener.open_url("https://example.com", None::<&str>)?;
+ // Or with a specific browser/app
+ opener.open_url("https://example.com", Some("firefox"))?;
+
+ // Opens the path with the system's default app
+ opener.open_path("/path/to/file", None::<&str>)?;
+ // Or with a specific app
+ opener.open_path("/path/to/file", Some("firefox"))?;
+
+ // Reveal a path with the system's default explorer
+ opener.reveal_item_in_dir("/path/to/file")?;
+
+ // Reveal multiple paths with the system's default explorer
+ opener.reveal_items_in_dir(["/path/to/file"])?;
+ Ok(())
+ })
+ .run(tauri::generate_context!())
+ .expect("error while running tauri application");
+}
+```
+
+## Contributing
+
+PRs accepted. Please make sure to read the Contributing Guide before making a pull request.
+
+## Partners
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+For the complete list of sponsors please visit our [website](https://tauri.app#sponsors) and [Open Collective](https://opencollective.com/tauri).
+
+## License
+
+Code: (c) 2015 - Present - The Tauri Programme within The Commons Conservancy.
+
+MIT or MIT/Apache 2.0 where applicable.
diff --git a/vendor/tauri-plugin-opener/SECURITY.md b/vendor/tauri-plugin-opener/SECURITY.md
new file mode 100644
index 00000000000..4f09bbacd45
--- /dev/null
+++ b/vendor/tauri-plugin-opener/SECURITY.md
@@ -0,0 +1,23 @@
+# Security Policy
+
+**Do not report security vulnerabilities through public GitHub issues.**
+
+**Please use the [Private Vulnerability Disclosure](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability) feature of GitHub.**
+
+Include as much of the following information:
+
+- Type of issue (e.g. improper input parsing, privilege escalation, etc.)
+- The location of the affected source code (tag/branch/commit or direct URL)
+- Any special configuration required to reproduce the issue
+- The distribution affected or used to help us with reproduction of the issue
+- Step-by-step instructions to reproduce the issue
+- Ideally a reproduction repository
+- Impact of the issue, including how an attacker might exploit the issue
+
+We prefer to receive reports in English.
+
+## Contact
+
+Please disclose a vulnerability or security relevant issue here: [https://github.com/tauri-apps/plugins-workspace/security/advisories/new](https://github.com/tauri-apps/plugins-workspace/security/advisories/new).
+
+Alternatively, you can also contact us by email via [security@tauri.app](mailto:security@tauri.app).
diff --git a/vendor/tauri-plugin-opener/android/.gitignore b/vendor/tauri-plugin-opener/android/.gitignore
new file mode 100644
index 00000000000..c0f21ec2fd9
--- /dev/null
+++ b/vendor/tauri-plugin-opener/android/.gitignore
@@ -0,0 +1,2 @@
+/build
+/.tauri
diff --git a/vendor/tauri-plugin-opener/android/build.gradle.kts b/vendor/tauri-plugin-opener/android/build.gradle.kts
new file mode 100644
index 00000000000..0c0612a49d4
--- /dev/null
+++ b/vendor/tauri-plugin-opener/android/build.gradle.kts
@@ -0,0 +1,40 @@
+plugins {
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+}
+
+android {
+ namespace = "app.tauri.opener"
+ compileSdk = 34
+
+ defaultConfig {
+ minSdk = 24
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+dependencies {
+ implementation("androidx.core:core-ktx:1.9.0")
+ implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
+ implementation("androidx.browser:browser:1.8.0")
+ implementation(project(":tauri-android"))
+}
diff --git a/vendor/tauri-plugin-opener/android/proguard-rules.pro b/vendor/tauri-plugin-opener/android/proguard-rules.pro
new file mode 100644
index 00000000000..481bb434814
--- /dev/null
+++ b/vendor/tauri-plugin-opener/android/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/vendor/tauri-plugin-opener/android/settings.gradle b/vendor/tauri-plugin-opener/android/settings.gradle
new file mode 100644
index 00000000000..14a752e4336
--- /dev/null
+++ b/vendor/tauri-plugin-opener/android/settings.gradle
@@ -0,0 +1,2 @@
+include ':tauri-android'
+project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
diff --git a/vendor/tauri-plugin-opener/android/src/main/AndroidManifest.xml b/vendor/tauri-plugin-opener/android/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..9a40236b947
--- /dev/null
+++ b/vendor/tauri-plugin-opener/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/vendor/tauri-plugin-opener/android/src/main/java/OpenerPlugin.kt b/vendor/tauri-plugin-opener/android/src/main/java/OpenerPlugin.kt
new file mode 100644
index 00000000000..a65a2f52a13
--- /dev/null
+++ b/vendor/tauri-plugin-opener/android/src/main/java/OpenerPlugin.kt
@@ -0,0 +1,44 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+package app.tauri.opener
+
+import android.app.Activity
+import android.content.Intent
+import androidx.browser.customtabs.CustomTabsIntent
+import app.tauri.annotation.Command
+import app.tauri.annotation.TauriPlugin
+import app.tauri.plugin.Invoke
+import app.tauri.plugin.Plugin
+import androidx.core.net.toUri
+import app.tauri.annotation.InvokeArg
+
+@InvokeArg
+class OpenArgs {
+ lateinit var url: String
+ var with: String? = null
+}
+
+@TauriPlugin
+class OpenerPlugin(private val activity: Activity) : Plugin(activity) {
+ @Command
+ fun open(invoke: Invoke) {
+ try {
+ val args = invoke.parseArgs(OpenArgs::class.java)
+
+ if (args.with == "inAppBrowser") {
+ val builder = CustomTabsIntent.Builder()
+ val intent = builder.build()
+ intent.launchUrl(activity, args.url.toUri())
+ } else {
+ val intent = Intent(Intent.ACTION_VIEW, args.url.toUri())
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ activity.applicationContext?.startActivity(intent)
+ }
+ invoke.resolve()
+ } catch (ex: Exception) {
+ invoke.reject(ex.message)
+ }
+ }
+}
diff --git a/vendor/tauri-plugin-opener/api-iife.js b/vendor/tauri-plugin-opener/api-iife.js
new file mode 100644
index 00000000000..847e54ea557
--- /dev/null
+++ b/vendor/tauri-plugin-opener/api-iife.js
@@ -0,0 +1,26 @@
+if ("__TAURI__" in window) {
+ var __TAURI_PLUGIN_OPENER__ = (function (n) {
+ "use strict";
+ async function e(n, e = {}, r) {
+ return window.__TAURI_INTERNALS__.invoke(n, e, r);
+ }
+ return (
+ "function" == typeof SuppressedError && SuppressedError,
+ (n.openPath = async function (n, r) {
+ await e("plugin:opener|open_path", { path: n, with: r });
+ }),
+ (n.openUrl = async function (n, r) {
+ await e("plugin:opener|open_url", { url: n, with: r });
+ }),
+ (n.revealItemInDir = async function (n) {
+ return e("plugin:opener|reveal_item_in_dir", {
+ paths: "string" == typeof n ? [n] : n,
+ });
+ }),
+ n
+ );
+ })({});
+ Object.defineProperty(window.__TAURI__, "opener", {
+ value: __TAURI_PLUGIN_OPENER__,
+ });
+}
diff --git a/vendor/tauri-plugin-opener/build.rs b/vendor/tauri-plugin-opener/build.rs
new file mode 100644
index 00000000000..fbad4d3ac35
--- /dev/null
+++ b/vendor/tauri-plugin-opener/build.rs
@@ -0,0 +1,136 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use std::path::PathBuf;
+
+#[path = "src/scope_entry.rs"]
+#[allow(dead_code)]
+mod scope;
+
+/// Opener scope application.
+#[derive(schemars::JsonSchema)]
+#[serde(untagged)]
+#[allow(unused)]
+enum Application {
+ /// Open in default application.
+ Default,
+ /// If true, allow open with any application.
+ Enable(bool),
+ /// Allow specific application to open with.
+ App(String),
+}
+
+impl Default for Application {
+ fn default() -> Self {
+ Self::Default
+ }
+}
+
+/// Opener scope entry.
+#[derive(schemars::JsonSchema)]
+#[serde(untagged)]
+#[allow(unused)]
+enum OpenerScopeEntry {
+ Url {
+ /// A URL that can be opened by the webview when using the Opener APIs.
+ ///
+ /// Wildcards can be used following the UNIX glob pattern.
+ ///
+ /// Examples:
+ ///
+ /// - "https://*" : allows all HTTPS origin
+ ///
+ /// - "https://*.github.com/tauri-apps/tauri": allows any subdomain of "github.com" with the "tauri-apps/api" path
+ ///
+ /// - "https://myapi.service.com/users/*": allows access to any URLs that begins with "https://myapi.service.com/users/"
+ url: String,
+ /// An application to open this url with, for example: firefox.
+ #[serde(default)]
+ app: Application,
+ },
+ Path {
+ /// A path that can be opened by the webview when using the Opener APIs.
+ ///
+ /// The pattern can start with a variable that resolves to a system base directory.
+ /// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
+ /// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
+ /// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
+ /// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
+ path: PathBuf,
+ /// An application to open this path with, for example: xdg-open.
+ #[serde(default)]
+ app: Application,
+ },
+}
+
+// Ensure `OpenerScopeEntry` and `scope::EntryRaw` is kept in sync
+fn _f() {
+ match (scope::EntryRaw::Url {
+ url: String::new(),
+ app: scope::Application::Enable(true),
+ }) {
+ scope::EntryRaw::Url { url, app } => OpenerScopeEntry::Url {
+ url,
+ app: match app {
+ scope::Application::Enable(p) => Application::Enable(p),
+ scope::Application::App(p) => Application::App(p),
+ scope::Application::Default => Application::Default,
+ },
+ },
+ scope::EntryRaw::Path { path, app } => OpenerScopeEntry::Path {
+ path,
+ app: match app {
+ scope::Application::Enable(p) => Application::Enable(p),
+ scope::Application::App(p) => Application::App(p),
+ scope::Application::Default => Application::Default,
+ },
+ },
+ };
+ match (OpenerScopeEntry::Url {
+ url: String::new(),
+ app: Application::Enable(true),
+ }) {
+ OpenerScopeEntry::Url { url, app } => scope::EntryRaw::Url {
+ url,
+ app: match app {
+ Application::Enable(p) => scope::Application::Enable(p),
+ Application::App(p) => scope::Application::App(p),
+ Application::Default => scope::Application::Default,
+ },
+ },
+ OpenerScopeEntry::Path { path, app } => scope::EntryRaw::Path {
+ path,
+ app: match app {
+ Application::Enable(p) => scope::Application::Enable(p),
+ Application::App(p) => scope::Application::App(p),
+ Application::Default => scope::Application::Default,
+ },
+ },
+ };
+}
+
+const COMMANDS: &[&str] = &["open_url", "open_path", "reveal_item_in_dir"];
+
+fn main() {
+ tauri_plugin::Builder::new(COMMANDS)
+ .global_api_script_path("./api-iife.js")
+ .android_path("android")
+ .ios_path("ios")
+ .global_scope_schema(schemars::schema_for!(OpenerScopeEntry))
+ .build();
+
+ let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
+ let mobile = target_os == "ios" || target_os == "android";
+ alias("desktop", !mobile);
+ alias("mobile", mobile);
+}
+
+// creates a cfg alias if `has_feature` is true.
+// `alias` must be a snake case string.
+fn alias(alias: &str, has_feature: bool) {
+ println!("cargo:rustc-check-cfg=cfg({alias})");
+ if has_feature {
+ println!("cargo:rustc-cfg={alias}");
+ }
+}
diff --git a/vendor/tauri-plugin-opener/ios/Package.resolved b/vendor/tauri-plugin-opener/ios/Package.resolved
new file mode 100644
index 00000000000..5f998e0e66b
--- /dev/null
+++ b/vendor/tauri-plugin-opener/ios/Package.resolved
@@ -0,0 +1,16 @@
+{
+ "object": {
+ "pins": [
+ {
+ "package": "SwiftRs",
+ "repositoryURL": "https://github.com/Brendonovich/swift-rs",
+ "state": {
+ "branch": null,
+ "revision": "b5ed223fcdab165bc21219c1925dc1e77e2bef5e",
+ "version": "1.0.6"
+ }
+ }
+ ]
+ },
+ "version": 1
+}
diff --git a/vendor/tauri-plugin-opener/ios/Package.swift b/vendor/tauri-plugin-opener/ios/Package.swift
new file mode 100644
index 00000000000..8d06936e614
--- /dev/null
+++ b/vendor/tauri-plugin-opener/ios/Package.swift
@@ -0,0 +1,34 @@
+// swift-tools-version:5.3
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+import PackageDescription
+
+let package = Package(
+ name: "tauri-plugin-opener",
+ platforms: [
+ .macOS(.v10_13),
+ .iOS(.v13),
+ ],
+ products: [
+ // Products define the executables and libraries a package produces, and make them visible to other packages.
+ .library(
+ name: "tauri-plugin-opener",
+ type: .static,
+ targets: ["tauri-plugin-opener"])
+ ],
+ dependencies: [
+ .package(name: "Tauri", path: "../.tauri/tauri-api")
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package. A target can define a module or a test suite.
+ // Targets can depend on other targets in this package, and on products in packages this package depends on.
+ .target(
+ name: "tauri-plugin-opener",
+ dependencies: [
+ .byName(name: "Tauri")
+ ],
+ path: "Sources")
+ ]
+)
diff --git a/vendor/tauri-plugin-opener/ios/Sources/OpenerPlugin.swift b/vendor/tauri-plugin-opener/ios/Sources/OpenerPlugin.swift
new file mode 100644
index 00000000000..2311a3b1b74
--- /dev/null
+++ b/vendor/tauri-plugin-opener/ios/Sources/OpenerPlugin.swift
@@ -0,0 +1,46 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+import Foundation
+import SafariServices
+import SwiftRs
+import Tauri
+import UIKit
+import WebKit
+
+struct OpenArgs: Decodable {
+ let url: String
+ let with: String?
+}
+
+class OpenerPlugin: Plugin {
+ @objc public func open(_ invoke: Invoke) throws {
+ do {
+ let args = try invoke.parseArgs(OpenArgs.self)
+ if let url = URL(string: args.url) {
+ if args.with == "inAppBrowser" {
+ DispatchQueue.main.async {
+ let safariVC = SFSafariViewController(url: url)
+ self.manager.viewController?.present(safariVC, animated: true)
+ }
+ } else {
+ if #available(iOS 10, *) {
+ UIApplication.shared.open(url, options: [:])
+ } else {
+ UIApplication.shared.openURL(url)
+ }
+ }
+
+ }
+ invoke.resolve()
+ } catch {
+ invoke.reject(error.localizedDescription)
+ }
+ }
+}
+
+@_cdecl("init_plugin_opener")
+func initPlugin() -> Plugin {
+ return OpenerPlugin()
+}
diff --git a/vendor/tauri-plugin-opener/permissions/allow-default-urls.toml b/vendor/tauri-plugin-opener/permissions/allow-default-urls.toml
new file mode 100644
index 00000000000..93495b8970e
--- /dev/null
+++ b/vendor/tauri-plugin-opener/permissions/allow-default-urls.toml
@@ -0,0 +1,17 @@
+"$schema" = "schemas/schema.json"
+
+[[permission]]
+identifier = "allow-default-urls"
+description = "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application."
+
+[[permission.scope.allow]]
+url = "mailto:*"
+
+[[permission.scope.allow]]
+url = "tel:*"
+
+[[permission.scope.allow]]
+url = "http://*"
+
+[[permission.scope.allow]]
+url = "https://*"
diff --git a/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_path.toml b/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_path.toml
new file mode 100644
index 00000000000..ae67b939428
--- /dev/null
+++ b/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_path.toml
@@ -0,0 +1,13 @@
+# Automatically generated - DO NOT EDIT!
+
+"$schema" = "../../schemas/schema.json"
+
+[[permission]]
+identifier = "allow-open-path"
+description = "Enables the open_path command without any pre-configured scope."
+commands.allow = ["open_path"]
+
+[[permission]]
+identifier = "deny-open-path"
+description = "Denies the open_path command without any pre-configured scope."
+commands.deny = ["open_path"]
diff --git a/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_url.toml b/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_url.toml
new file mode 100644
index 00000000000..f1e694b1b0e
--- /dev/null
+++ b/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_url.toml
@@ -0,0 +1,13 @@
+# Automatically generated - DO NOT EDIT!
+
+"$schema" = "../../schemas/schema.json"
+
+[[permission]]
+identifier = "allow-open-url"
+description = "Enables the open_url command without any pre-configured scope."
+commands.allow = ["open_url"]
+
+[[permission]]
+identifier = "deny-open-url"
+description = "Denies the open_url command without any pre-configured scope."
+commands.deny = ["open_url"]
diff --git a/vendor/tauri-plugin-opener/permissions/autogenerated/commands/reveal_item_in_dir.toml b/vendor/tauri-plugin-opener/permissions/autogenerated/commands/reveal_item_in_dir.toml
new file mode 100644
index 00000000000..e669620ffd8
--- /dev/null
+++ b/vendor/tauri-plugin-opener/permissions/autogenerated/commands/reveal_item_in_dir.toml
@@ -0,0 +1,13 @@
+# Automatically generated - DO NOT EDIT!
+
+"$schema" = "../../schemas/schema.json"
+
+[[permission]]
+identifier = "allow-reveal-item-in-dir"
+description = "Enables the reveal_item_in_dir command without any pre-configured scope."
+commands.allow = ["reveal_item_in_dir"]
+
+[[permission]]
+identifier = "deny-reveal-item-in-dir"
+description = "Denies the reveal_item_in_dir command without any pre-configured scope."
+commands.deny = ["reveal_item_in_dir"]
diff --git a/vendor/tauri-plugin-opener/permissions/autogenerated/reference.md b/vendor/tauri-plugin-opener/permissions/autogenerated/reference.md
new file mode 100644
index 00000000000..6ad6ba1fbf6
--- /dev/null
+++ b/vendor/tauri-plugin-opener/permissions/autogenerated/reference.md
@@ -0,0 +1,111 @@
+## Default Permission
+
+This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application
+as well as reveal file in directories using default file explorer
+
+#### This default permission set includes the following:
+
+- `allow-open-url`
+- `allow-reveal-item-in-dir`
+- `allow-default-urls`
+
+## Permission Table
+
+
+
+| Identifier |
+Description |
+
+
+
+
+|
+
+`opener:allow-default-urls`
+
+ |
+
+
+This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.
+
+ |
+
+
+
+|
+
+`opener:allow-open-path`
+
+ |
+
+
+Enables the open_path command without any pre-configured scope.
+
+ |
+
+
+
+|
+
+`opener:deny-open-path`
+
+ |
+
+
+Denies the open_path command without any pre-configured scope.
+
+ |
+
+
+
+|
+
+`opener:allow-open-url`
+
+ |
+
+
+Enables the open_url command without any pre-configured scope.
+
+ |
+
+
+
+|
+
+`opener:deny-open-url`
+
+ |
+
+
+Denies the open_url command without any pre-configured scope.
+
+ |
+
+
+
+|
+
+`opener:allow-reveal-item-in-dir`
+
+ |
+
+
+Enables the reveal_item_in_dir command without any pre-configured scope.
+
+ |
+
+
+
+|
+
+`opener:deny-reveal-item-in-dir`
+
+ |
+
+
+Denies the reveal_item_in_dir command without any pre-configured scope.
+
+ |
+
+
diff --git a/vendor/tauri-plugin-opener/permissions/default.toml b/vendor/tauri-plugin-opener/permissions/default.toml
new file mode 100644
index 00000000000..846d6e51be0
--- /dev/null
+++ b/vendor/tauri-plugin-opener/permissions/default.toml
@@ -0,0 +1,10 @@
+"$schema" = "schemas/schema.json"
+
+[default]
+description = """This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application
+as well as reveal file in directories using default file explorer"""
+permissions = [
+ "allow-open-url",
+ "allow-reveal-item-in-dir",
+ "allow-default-urls",
+]
diff --git a/vendor/tauri-plugin-opener/src/commands.rs b/vendor/tauri-plugin-opener/src/commands.rs
new file mode 100644
index 00000000000..343aa441a29
--- /dev/null
+++ b/vendor/tauri-plugin-opener/src/commands.rs
@@ -0,0 +1,158 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use std::path::{Path, PathBuf};
+
+use tauri::{
+ ipc::{CommandScope, GlobalScope},
+ AppHandle, Runtime,
+};
+
+use crate::{scope::Scope, Error, OpenerExt};
+
+#[tauri::command]
+pub async fn open_url(
+ app: AppHandle,
+ command_scope: CommandScope,
+ global_scope: GlobalScope,
+ url: String,
+ with: Option,
+) -> crate::Result<()> {
+ let scope = Scope::new(
+ &app,
+ command_scope
+ .allows()
+ .iter()
+ .chain(global_scope.allows())
+ .collect(),
+ command_scope
+ .denies()
+ .iter()
+ .chain(global_scope.denies())
+ .collect(),
+ );
+
+ if scope.is_url_allowed(&url, with.as_deref()) {
+ app.opener().open_url(url, with)
+ } else {
+ Err(Error::ForbiddenUrl { url, with })
+ }
+}
+
+#[tauri::command]
+pub async fn open_path(
+ app: AppHandle,
+ command_scope: CommandScope,
+ global_scope: GlobalScope,
+ path: String,
+ with: Option,
+) -> crate::Result<()> {
+ let scope = Scope::new(
+ &app,
+ command_scope
+ .allows()
+ .iter()
+ .chain(global_scope.allows())
+ .collect(),
+ command_scope
+ .denies()
+ .iter()
+ .chain(global_scope.denies())
+ .collect(),
+ );
+
+ if scope.is_path_allowed(Path::new(&path), with.as_deref())? {
+ app.opener().open_path(path, with)
+ } else {
+ Err(Error::ForbiddenPath { path, with })
+ }
+}
+
+/// TODO: in the next major version, rename to `reveal_items_in_dir`
+#[tauri::command]
+pub async fn reveal_item_in_dir(paths: Vec) -> crate::Result<()> {
+ reveal_item_in_dir_with(paths, crate::reveal_items_in_dir).await
+}
+
+async fn reveal_item_in_dir_with(
+ paths: Vec,
+ reveal: impl FnOnce(Vec) -> crate::Result<()> + Send + 'static,
+) -> crate::Result<()> {
+ // The Linux backend uses blocking zbus, which may enter its own Tokio runtime.
+ tauri::async_runtime::spawn_blocking(move || reveal(paths)).await?
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ async fn assert_reveal_can_enter_runtime() {
+ let paths = vec![
+ PathBuf::from("recording with spaces.cap/"),
+ PathBuf::from("截图.png"),
+ ];
+ let expected = paths.clone();
+ reveal_item_in_dir_with(paths, move |actual| {
+ assert_eq!(actual, expected);
+ let runtime = tokio::runtime::Builder::new_current_thread()
+ .build()
+ .unwrap();
+ runtime.block_on(async { Ok(()) })
+ })
+ .await
+ .unwrap();
+ }
+
+ #[tokio::test]
+ async fn reveal_from_current_thread_runtime() {
+ assert_reveal_can_enter_runtime().await;
+ }
+
+ #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+ async fn reveal_from_multi_thread_runtime() {
+ assert_reveal_can_enter_runtime().await;
+ }
+
+ #[tokio::test]
+ async fn preserves_backend_errors() {
+ let error = reveal_item_in_dir_with(Vec::new(), |_| {
+ Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "reveal denied").into())
+ })
+ .await
+ .unwrap_err();
+ assert!(matches!(&error, Error::Io(e) if e.kind() == std::io::ErrorKind::PermissionDenied));
+ assert_eq!(error.to_string(), "reveal denied");
+ }
+
+ #[tokio::test]
+ async fn returns_worker_panics_as_errors() {
+ let error = reveal_item_in_dir_with(Vec::new(), |_| panic!("reveal worker failed"))
+ .await
+ .unwrap_err();
+ assert!(matches!(error, Error::Tauri(tauri::Error::JoinError(_))));
+ }
+
+ #[tokio::test]
+ async fn validates_every_path_before_revealing() {
+ let directory = tempfile::tempdir().unwrap();
+ let existing = directory.path().join("recording with spaces.cap");
+ std::fs::write(&existing, []).unwrap();
+ let missing = directory.path().join("missing.png");
+ let error = reveal_item_in_dir(vec![existing, missing])
+ .await
+ .unwrap_err();
+ assert!(matches!(error, Error::Io(e) if e.kind() == std::io::ErrorKind::NotFound));
+ }
+
+ #[cfg(target_os = "linux")]
+ #[tokio::test]
+ async fn linux_zbus_backend_from_tokio_runtime() {
+ let result = reveal_item_in_dir(Vec::new()).await;
+ // A headless host may lack a session bus or file manager; neither is a worker panic.
+ assert!(!matches!(
+ result,
+ Err(Error::Tauri(tauri::Error::JoinError(_)))
+ ));
+ }
+}
diff --git a/vendor/tauri-plugin-opener/src/config.rs b/vendor/tauri-plugin-opener/src/config.rs
new file mode 100644
index 00000000000..db3bae45e51
--- /dev/null
+++ b/vendor/tauri-plugin-opener/src/config.rs
@@ -0,0 +1,19 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use serde::Deserialize;
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct Config {
+ /// Whether or not paths that contain components that start with a `.`
+ /// will require that `.` appears literally in the pattern; `*`, `?`, `**`,
+ /// or `[...]` will not match. This is useful because such files are
+ /// conventionally considered hidden on Unix systems and it might be
+ /// desirable to skip them when listing files.
+ ///
+ /// Defaults to `true` on Unix systems and `false` on Windows
+ // dotfiles are not supposed to be exposed by default on unix
+ pub require_literal_leading_dot: Option,
+}
diff --git a/vendor/tauri-plugin-opener/src/error.rs b/vendor/tauri-plugin-opener/src/error.rs
new file mode 100644
index 00000000000..c5a4dde3cce
--- /dev/null
+++ b/vendor/tauri-plugin-opener/src/error.rs
@@ -0,0 +1,57 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use std::path::PathBuf;
+
+use serde::{Serialize, Serializer};
+
+#[derive(Debug, thiserror::Error)]
+#[non_exhaustive]
+pub enum Error {
+ #[cfg(mobile)]
+ #[error(transparent)]
+ PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
+ #[error(transparent)]
+ Tauri(#[from] tauri::Error),
+ #[error(transparent)]
+ Io(#[from] std::io::Error),
+ #[error(transparent)]
+ Json(#[from] serde_json::Error),
+ #[error("unknown program {0}")]
+ UnknownProgramName(String),
+ #[error("Not allowed to open path {}{}", .path, .with.as_ref().map(|w| format!(" with {w}")).unwrap_or_default())]
+ ForbiddenPath { path: String, with: Option },
+ #[error("Not allowed to open url {}{}", .url, .with.as_ref().map(|w| format!(" with {w}")).unwrap_or_default())]
+ ForbiddenUrl { url: String, with: Option },
+ #[error("API not supported on the current platform")]
+ UnsupportedPlatform,
+ #[error(transparent)]
+ #[cfg(windows)]
+ Win32Error(#[from] windows::core::Error),
+ #[error("Path doesn't have a parent: {0}")]
+ NoParent(PathBuf),
+ #[cfg(windows)]
+ #[error("Failed to convert path '{0}' to ITEMIDLIST")]
+ FailedToConvertPathToItemIdList(PathBuf),
+ #[error("Failed to convert path to file:// url")]
+ FailedToConvertPathToFileUrl,
+ #[error(transparent)]
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ Zbus(#[from] zbus::Error),
+}
+
+impl Serialize for Error {
+ fn serialize(&self, serializer: S) -> std::result::Result
+ where
+ S: Serializer,
+ {
+ serializer.serialize_str(self.to_string().as_ref())
+ }
+}
diff --git a/vendor/tauri-plugin-opener/src/init-iife.js b/vendor/tauri-plugin-opener/src/init-iife.js
new file mode 100644
index 00000000000..26b3118ec47
--- /dev/null
+++ b/vendor/tauri-plugin-opener/src/init-iife.js
@@ -0,0 +1,19 @@
+!(function () {
+ "use strict";
+ "function" == typeof SuppressedError && SuppressedError,
+ window.addEventListener("click", function (e) {
+ if (e.defaultPrevented || 0 !== e.button || e.metaKey || e.altKey) return;
+ const t = e
+ .composedPath()
+ .find((e) => e instanceof Node && "A" === e.nodeName.toUpperCase());
+ if (!t || !t.href || ("_blank" !== t.target && !e.ctrlKey && !e.shiftKey))
+ return;
+ const n = new URL(t.href);
+ n.origin === window.location.origin ||
+ ["http:", "https:", "mailto:", "tel:"].every((e) => n.protocol !== e) ||
+ (e.preventDefault(),
+ (async function (e, t = {}, n) {
+ window.__TAURI_INTERNALS__.invoke(e, t, n);
+ })("plugin:opener|open_url", { url: n }));
+ });
+})();
diff --git a/vendor/tauri-plugin-opener/src/lib.rs b/vendor/tauri-plugin-opener/src/lib.rs
new file mode 100644
index 00000000000..343e915580b
--- /dev/null
+++ b/vendor/tauri-plugin-opener/src/lib.rs
@@ -0,0 +1,238 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use std::path::Path;
+
+use tauri::{plugin::TauriPlugin, Manager, Runtime};
+
+#[cfg(mobile)]
+use tauri::plugin::PluginHandle;
+#[cfg(target_os = "android")]
+const PLUGIN_IDENTIFIER: &str = "app.tauri.opener";
+#[cfg(target_os = "ios")]
+tauri::ios_plugin_binding!(init_plugin_opener);
+
+mod commands;
+mod config;
+mod error;
+mod open;
+mod reveal_item_in_dir;
+mod scope;
+mod scope_entry;
+
+pub use error::Error;
+type Result = std::result::Result;
+
+pub use open::{open_path, open_url};
+pub use reveal_item_in_dir::{reveal_item_in_dir, reveal_items_in_dir};
+
+pub struct Opener {
+ // we use `fn() -> R` to silence the unused generic error
+ // while keeping this struct `Send + Sync` without requiring `R` to be
+ #[cfg(not(mobile))]
+ _marker: std::marker::PhantomData R>,
+ #[cfg(mobile)]
+ mobile_plugin_handle: PluginHandle,
+ require_literal_leading_dot: Option,
+}
+
+impl Opener {
+ /// Open a url with a default or specific program.
+ ///
+ /// # Examples
+ ///
+ /// ```rust,no_run
+ /// use tauri_plugin_opener::OpenerExt;
+ ///
+ /// tauri::Builder::default()
+ /// .setup(|app| {
+ /// // open the given URL on the system default browser
+ /// app.opener().open_url("https://github.com/tauri-apps/tauri", None::<&str>)?;
+ /// Ok(())
+ /// });
+ /// ```
+ ///
+ /// ## Platform-specific:
+ ///
+ /// - **Android / iOS**: Always opens using default program, unless `with` is provided as "inAppBrowser".
+ #[cfg(desktop)]
+ pub fn open_url(&self, url: impl Into, with: Option>) -> Result<()> {
+ crate::open::open(url.into(), with.map(Into::into))
+ }
+
+ /// Open a url with a default or specific program.
+ ///
+ /// # Examples
+ ///
+ /// ```rust,no_run
+ /// use tauri_plugin_opener::OpenerExt;
+ ///
+ /// tauri::Builder::default()
+ /// .setup(|app| {
+ /// // open the given URL on the system default browser
+ /// app.opener().open_url("https://github.com/tauri-apps/tauri", None::<&str>)?;
+ /// Ok(())
+ /// });
+ /// ```
+ ///
+ /// ## Platform-specific:
+ ///
+ /// - **Android / iOS**: Always opens using default program, unless `with` is provided as "inAppBrowser".
+ #[cfg(mobile)]
+ pub fn open_url(&self, url: impl Into, with: Option>) -> Result<()> {
+ self.mobile_plugin_handle
+ .run_mobile_plugin(
+ "open",
+ serde_json::json!({ "url": url.into(), "with": with.map(Into::into) }),
+ )
+ .map_err(Into::into)
+ }
+
+ /// Open a path with a default or specific program.
+ ///
+ /// # Examples
+ ///
+ /// ```rust,no_run
+ /// use tauri_plugin_opener::OpenerExt;
+ ///
+ /// tauri::Builder::default()
+ /// .setup(|app| {
+ /// // open the given path on the system default explorer
+ /// app.opener().open_path("/path/to/file", None::<&str>)?;
+ /// Ok(())
+ /// });
+ /// ```
+ ///
+ /// ## Platform-specific:
+ ///
+ /// - **Android / iOS**: Always opens using default program.
+ #[cfg(desktop)]
+ pub fn open_path(
+ &self,
+ path: impl Into,
+ with: Option>,
+ ) -> Result<()> {
+ crate::open::open(path.into(), with.map(Into::into))
+ }
+
+ /// Open a path with a default or specific program.
+ ///
+ /// # Examples
+ ///
+ /// ```rust,no_run
+ /// use tauri_plugin_opener::OpenerExt;
+ ///
+ /// tauri::Builder::default()
+ /// .setup(|app| {
+ /// // open the given path on the system default explorer
+ /// app.opener().open_path("/path/to/file", None::<&str>)?;
+ /// Ok(())
+ /// });
+ /// ```
+ ///
+ /// ## Platform-specific:
+ ///
+ /// - **Android / iOS**: Always opens using default program.
+ #[cfg(mobile)]
+ pub fn open_path(
+ &self,
+ path: impl Into,
+ _with: Option>,
+ ) -> Result<()> {
+ self.mobile_plugin_handle
+ .run_mobile_plugin("open", path.into())
+ .map_err(Into::into)
+ }
+
+ pub fn reveal_item_in_dir>(&self, p: P) -> Result<()> {
+ reveal_item_in_dir(p)
+ }
+
+ pub fn reveal_items_in_dir(&self, paths: I) -> Result<()>
+ where
+ I: IntoIterator- ,
+ P: AsRef,
+ {
+ reveal_items_in_dir(paths)
+ }
+}
+
+/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the opener APIs.
+pub trait OpenerExt {
+ fn opener(&self) -> &Opener;
+}
+
+impl> OpenerExt for T {
+ fn opener(&self) -> &Opener {
+ self.state::>().inner()
+ }
+}
+
+/// The opener plugin Builder.
+pub struct Builder {
+ open_js_links_on_click: bool,
+}
+
+impl Default for Builder {
+ fn default() -> Self {
+ Self {
+ open_js_links_on_click: true,
+ }
+ }
+}
+
+impl Builder {
+ /// Create a new opener plugin Builder.
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Whether the plugin should inject a JS script to open URLs in default browser
+ /// when clicking on `` elements that has `_blank` target, or when pressing `Ctrl` or `Shift` while clicking it.
+ ///
+ /// Enabled by default for `http:`, `https:`, `mailto:`, `tel:` links.
+ pub fn open_js_links_on_click(mut self, open: bool) -> Self {
+ self.open_js_links_on_click = open;
+ self
+ }
+
+ /// Build and Initializes the plugin.
+ pub fn build(self) -> TauriPlugin> {
+ let mut builder = tauri::plugin::Builder::>::new("opener")
+ .setup(|app, api| {
+ #[cfg(target_os = "android")]
+ let handle = api.register_android_plugin(PLUGIN_IDENTIFIER, "OpenerPlugin")?;
+ #[cfg(target_os = "ios")]
+ let handle = api.register_ios_plugin(init_plugin_opener)?;
+
+ app.manage(Opener {
+ #[cfg(not(mobile))]
+ _marker: std::marker::PhantomData:: R>,
+ #[cfg(mobile)]
+ mobile_plugin_handle: handle,
+ require_literal_leading_dot: api
+ .config()
+ .as_ref()
+ .and_then(|c| c.require_literal_leading_dot),
+ });
+ Ok(())
+ })
+ .invoke_handler(tauri::generate_handler![
+ commands::open_url,
+ commands::open_path,
+ commands::reveal_item_in_dir,
+ ]);
+
+ if self.open_js_links_on_click {
+ builder = builder.js_init_script(include_str!("init-iife.js").to_string());
+ }
+
+ builder.build()
+ }
+}
+
+/// Initializes the plugin.
+pub fn init() -> TauriPlugin> {
+ Builder::default().build()
+}
diff --git a/vendor/tauri-plugin-opener/src/open.rs b/vendor/tauri-plugin-opener/src/open.rs
new file mode 100644
index 00000000000..a3d46c508d4
--- /dev/null
+++ b/vendor/tauri-plugin-opener/src/open.rs
@@ -0,0 +1,61 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+//! Types and functions related to shell.
+
+use std::{ffi::OsStr, path::Path};
+
+pub(crate) fn open, S: AsRef>(path: P, with: Option
) -> crate::Result<()> {
+ match with {
+ Some(program) => ::open::with_detached(path, program.as_ref()),
+ None => ::open::that_detached(path),
+ }
+ .map_err(Into::into)
+}
+
+/// Opens URL with the program specified in `with`, or system default if `None`.
+///
+/// ## Platform-specific:
+///
+/// - **Android / iOS**: Always opens using default program.
+///
+/// # Examples
+///
+/// ```rust,no_run
+/// tauri::Builder::default()
+/// .setup(|app| {
+/// // open the given URL on the system default browser
+/// tauri_plugin_opener::open_url("https://github.com/tauri-apps/tauri", None::<&str>)?;
+/// Ok(())
+/// });
+/// ```
+pub fn open_url, S: AsRef>(url: P, with: Option) -> crate::Result<()> {
+ let url = url.as_ref();
+ open(url, with)
+}
+
+/// Opens path with the program specified in `with`, or system default if `None`.
+///
+/// ## Platform-specific:
+///
+/// - **Android / iOS**: Always opens using default program.
+///
+/// # Examples
+///
+/// ```rust,no_run
+/// tauri::Builder::default()
+/// .setup(|app| {
+/// // open the given URL on the system default explorer
+/// tauri_plugin_opener::open_path("/path/to/file", None::<&str>)?;
+/// Ok(())
+/// });
+/// ```
+pub fn open_path, S: AsRef>(path: P, with: Option) -> crate::Result<()> {
+ let path = path.as_ref();
+ if with.is_none() {
+ // Returns an IO error if not exists, and besides `exists()` is a shorthand for `metadata()`
+ _ = path.metadata()?;
+ }
+ open(path, with)
+}
diff --git a/vendor/tauri-plugin-opener/src/reveal_item_in_dir.rs b/vendor/tauri-plugin-opener/src/reveal_item_in_dir.rs
new file mode 100644
index 00000000000..6112fb8b19b
--- /dev/null
+++ b/vendor/tauri-plugin-opener/src/reveal_item_in_dir.rs
@@ -0,0 +1,299 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use std::path::Path;
+
+/// Reveal a path the system's default explorer.
+///
+/// ## Platform-specific:
+///
+/// - **Android / iOS:** Unsupported.
+pub fn reveal_item_in_dir>(path: P) -> crate::Result<()> {
+ let path = dunce::canonicalize(path.as_ref())?;
+
+ #[cfg(any(
+ windows,
+ target_os = "macos",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ return imp::reveal_items_in_dir(&[path]);
+
+ #[cfg(not(any(
+ windows,
+ target_os = "macos",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )))]
+ Err(crate::Error::UnsupportedPlatform)
+}
+
+/// Reveal the paths the system's default explorer.
+///
+/// ## Platform-specific:
+///
+/// - **Android / iOS:** Unsupported.
+pub fn reveal_items_in_dir(paths: I) -> crate::Result<()>
+where
+ I: IntoIterator- ,
+ P: AsRef,
+{
+ let mut canonicalized = vec![];
+
+ for path in paths {
+ let path = dunce::canonicalize(path.as_ref())?;
+ canonicalized.push(path);
+ }
+
+ #[cfg(any(
+ windows,
+ target_os = "macos",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ return imp::reveal_items_in_dir(&canonicalized);
+
+ #[cfg(not(any(
+ windows,
+ target_os = "macos",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )))]
+ Err(crate::Error::UnsupportedPlatform)
+}
+
+#[cfg(windows)]
+mod imp {
+ use std::collections::HashMap;
+ use std::path::{Path, PathBuf};
+
+ use windows::Win32::UI::Shell::Common::ITEMIDLIST;
+ use windows::{
+ core::{w, HSTRING, PCWSTR},
+ Win32::{
+ Foundation::ERROR_FILE_NOT_FOUND,
+ System::Com::CoInitialize,
+ UI::{
+ Shell::{
+ ILCreateFromPathW, ILFree, SHOpenFolderAndSelectItems, ShellExecuteExW,
+ SHELLEXECUTEINFOW,
+ },
+ WindowsAndMessaging::SW_SHOWNORMAL,
+ },
+ },
+ };
+
+ pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> {
+ if paths.is_empty() {
+ return Ok(());
+ }
+
+ let mut grouped_paths: HashMap<&Path, Vec<&Path>> = HashMap::new();
+ for path in paths {
+ let parent = path
+ .parent()
+ .ok_or_else(|| crate::Error::NoParent(path.to_path_buf()))?;
+ grouped_paths.entry(parent).or_default().push(path);
+ }
+
+ let _ = unsafe { CoInitialize(None) };
+
+ for (parent, to_reveals) in grouped_paths {
+ let parent_item_id_list = OwnedItemIdList::new(parent)?;
+ let to_reveals_item_id_list = to_reveals
+ .iter()
+ .map(|to_reveal| OwnedItemIdList::new(*to_reveal))
+ .collect::>>()?;
+ if let Err(e) = unsafe {
+ SHOpenFolderAndSelectItems(
+ parent_item_id_list.item,
+ Some(
+ &to_reveals_item_id_list
+ .iter()
+ .map(|item| item.item)
+ .collect::>(),
+ ),
+ 0,
+ )
+ } {
+ // from https://github.com/electron/electron/blob/10d967028af2e72382d16b7e2025d243b9e204ae/shell/common/platform_util_win.cc#L302
+ // On some systems, the above call mysteriously fails with "file not
+ // found" even though the file is there. In these cases, ShellExecute()
+ // seems to work as a fallback (although it won't select the file).
+ //
+ // Note: we only handle the first file here if multiple of are present
+ if e.code().0 == ERROR_FILE_NOT_FOUND.0 as i32 {
+ let first_path = to_reveals[0];
+ let is_dir = first_path.is_dir();
+ let mut info = SHELLEXECUTEINFOW {
+ cbSize: std::mem::size_of::() as _,
+ nShow: SW_SHOWNORMAL.0,
+ lpFile: PCWSTR(parent_item_id_list.hstring.as_ptr()),
+ lpClass: if is_dir { w!("folder") } else { PCWSTR::null() },
+ lpVerb: if is_dir {
+ w!("explore")
+ } else {
+ PCWSTR::null()
+ },
+ ..Default::default()
+ };
+
+ unsafe { ShellExecuteExW(&mut info) }?;
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ struct OwnedItemIdList {
+ hstring: HSTRING,
+ item: *const ITEMIDLIST,
+ }
+
+ impl OwnedItemIdList {
+ fn new(path: &Path) -> crate::Result {
+ let path_hstring = HSTRING::from(path);
+ let item_id_list = unsafe { ILCreateFromPathW(&path_hstring) };
+ if item_id_list.is_null() {
+ Err(crate::Error::FailedToConvertPathToItemIdList(
+ path.to_owned(),
+ ))
+ } else {
+ Ok(Self {
+ hstring: path_hstring,
+ item: item_id_list,
+ })
+ }
+ }
+ }
+
+ impl Drop for OwnedItemIdList {
+ fn drop(&mut self) {
+ if !self.item.is_null() {
+ unsafe { ILFree(Some(self.item)) };
+ }
+ }
+ }
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+mod imp {
+ use super::*;
+ use std::collections::HashMap;
+ use std::path::PathBuf;
+
+ pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> {
+ let connection = zbus::blocking::Connection::session()?;
+
+ reveal_with_filemanager1(paths, &connection).or_else(|e| {
+ // Fallback to opening the directory of the first item if revealing multiple items fails.
+ if let Some(first_path) = paths.first() {
+ reveal_with_open_uri_portal(first_path, &connection)
+ } else {
+ Err(e)
+ }
+ })
+ }
+
+ fn reveal_with_filemanager1(
+ paths: &[PathBuf],
+ connection: &zbus::blocking::Connection,
+ ) -> crate::Result<()> {
+ let uris: Result, _> = paths
+ .iter()
+ .map(|path| {
+ url::Url::from_file_path(path)
+ .map_err(|_| crate::Error::FailedToConvertPathToFileUrl)
+ })
+ .collect();
+ let uris = uris?;
+ let uri_strs: Vec<&str> = uris.iter().map(|uri| uri.as_str()).collect();
+
+ #[zbus::proxy(
+ interface = "org.freedesktop.FileManager1",
+ default_service = "org.freedesktop.FileManager1",
+ default_path = "/org/freedesktop/FileManager1"
+ )]
+ trait FileManager1 {
+ async fn ShowItems(&self, name: Vec<&str>, arg2: &str) -> crate::Result<()>;
+ }
+
+ let proxy = FileManager1ProxyBlocking::new(connection)?;
+
+ proxy.ShowItems(uri_strs, "")
+ }
+
+ fn reveal_with_open_uri_portal(
+ path: &Path,
+ connection: &zbus::blocking::Connection,
+ ) -> crate::Result<()> {
+ let uri = url::Url::from_file_path(path)
+ .map_err(|_| crate::Error::FailedToConvertPathToFileUrl)?;
+
+ #[zbus::proxy(
+ interface = "org.freedesktop.portal.Desktop",
+ default_service = "org.freedesktop.portal.OpenURI",
+ default_path = "/org/freedesktop/portal/desktop"
+ )]
+ trait PortalDesktop {
+ async fn OpenDirectory(
+ &self,
+ arg1: &str,
+ name: &str,
+ arg3: HashMap<&str, &str>,
+ ) -> crate::Result<()>;
+ }
+
+ let proxy = PortalDesktopProxyBlocking::new(connection)?;
+
+ proxy.OpenDirectory("", uri.as_str(), HashMap::new())
+ }
+}
+
+#[cfg(target_os = "macos")]
+mod imp {
+ use objc2_app_kit::NSWorkspace;
+ use objc2_foundation::{NSArray, NSString, NSURL};
+ use std::path::PathBuf;
+
+ pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> {
+ unsafe {
+ let mut urls = Vec::new();
+
+ for path in paths {
+ let path = path.to_string_lossy();
+ let path = NSString::from_str(&path);
+ let url = NSURL::fileURLWithPath(&path);
+
+ urls.push(url);
+ }
+
+ let urls = NSArray::from_retained_slice(&urls);
+
+ let workspace = NSWorkspace::new();
+ workspace.activateFileViewerSelectingURLs(&urls);
+ }
+
+ Ok(())
+ }
+}
diff --git a/vendor/tauri-plugin-opener/src/scope.rs b/vendor/tauri-plugin-opener/src/scope.rs
new file mode 100644
index 00000000000..275913d1ba5
--- /dev/null
+++ b/vendor/tauri-plugin-opener/src/scope.rs
@@ -0,0 +1,141 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use std::{
+ marker::PhantomData,
+ path::{Path, PathBuf},
+ sync::Arc,
+};
+
+use tauri::{ipc::ScopeObject, utils::acl::Value, AppHandle, Manager, Runtime};
+
+use crate::{scope_entry::EntryRaw, Error};
+
+pub use crate::scope_entry::Application;
+
+#[derive(Debug)]
+pub enum Entry {
+ Url {
+ url: glob::Pattern,
+ app: Application,
+ },
+ Path {
+ path: Option,
+ app: Application,
+ },
+}
+
+impl ScopeObject for Entry {
+ type Error = Error;
+
+ fn deserialize(
+ app_handle: &AppHandle,
+ raw: Value,
+ ) -> std::result::Result {
+ serde_json::from_value(raw.into())
+ .and_then(|raw| {
+ let entry = match raw {
+ EntryRaw::Url { url, app } => Entry::Url {
+ url: glob::Pattern::new(&url)
+ .map_err(|e| serde::de::Error::custom(e.to_string()))?,
+ app,
+ },
+ EntryRaw::Path { path, app } => {
+ let path = match app_handle.path().parse(path) {
+ Ok(path) => Some(path),
+ #[cfg(not(target_os = "android"))]
+ Err(tauri::Error::UnknownPath) => None,
+ Err(err) => return Err(serde::de::Error::custom(err.to_string())),
+ };
+
+ Entry::Path { path, app }
+ }
+ };
+
+ Ok(entry)
+ })
+ .map_err(Into::into)
+ }
+}
+
+impl Application {
+ fn matches(&self, a: Option<&str>) -> bool {
+ match self {
+ Self::Default => a.is_none(),
+ Self::Enable(enable) => *enable,
+ Self::App(program) => Some(program.as_str()) == a,
+ }
+ }
+}
+
+impl Entry {
+ fn path(&self) -> Option {
+ match self {
+ Self::Url { .. } => None,
+ Self::Path { path, .. } => path.clone(),
+ }
+ }
+
+ fn matches_url(&self, u: &str, a: Option<&str>) -> bool {
+ match self {
+ Self::Url { url, app } => url.matches(u) && app.matches(a),
+ Self::Path { .. } => false,
+ }
+ }
+
+ fn matches_path_program(&self, a: Option<&str>) -> bool {
+ match self {
+ Self::Url { .. } => false,
+ Self::Path { app, .. } => app.matches(a),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct Scope<'a, R: Runtime, M: Manager> {
+ allowed: Vec<&'a Arc>,
+ denied: Vec<&'a Arc>,
+ manager: &'a M,
+ _marker: PhantomData,
+}
+
+impl<'a, R: Runtime, M: Manager> Scope<'a, R, M> {
+ pub(crate) fn new(
+ manager: &'a M,
+ allowed: Vec<&'a Arc>,
+ denied: Vec<&'a Arc>,
+ ) -> Self {
+ Self {
+ manager,
+ allowed,
+ denied,
+ _marker: PhantomData,
+ }
+ }
+
+ pub fn is_url_allowed(&self, url: &str, with: Option<&str>) -> bool {
+ let denied = self.denied.iter().any(|e| e.matches_url(url, with));
+ if denied {
+ false
+ } else {
+ self.allowed.iter().any(|e| e.matches_url(url, with))
+ }
+ }
+
+ pub fn is_path_allowed(&self, path: &Path, with: Option<&str>) -> crate::Result {
+ let fs_scope = tauri::fs::Scope::new(
+ self.manager,
+ &tauri::utils::config::FsScope::Scope {
+ allow: self.allowed.iter().filter_map(|e| e.path()).collect(),
+ deny: self.denied.iter().filter_map(|e| e.path()).collect(),
+ require_literal_leading_dot: self
+ .manager
+ .state::>()
+ .require_literal_leading_dot,
+ },
+ )?;
+
+ Ok(fs_scope.is_allowed(path) && self.allowed.iter().any(|e| e.matches_path_program(with)))
+ }
+}
diff --git a/vendor/tauri-plugin-opener/src/scope_entry.rs b/vendor/tauri-plugin-opener/src/scope_entry.rs
new file mode 100644
index 00000000000..cf9004a2b50
--- /dev/null
+++ b/vendor/tauri-plugin-opener/src/scope_entry.rs
@@ -0,0 +1,36 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use std::path::PathBuf;
+
+use serde::Deserialize;
+
+#[derive(Deserialize, Debug)]
+#[serde(untagged)]
+pub enum Application {
+ Default,
+ Enable(bool),
+ App(String),
+}
+
+impl Default for Application {
+ fn default() -> Self {
+ Self::Default
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(untagged, rename_all = "camelCase")]
+pub(crate) enum EntryRaw {
+ Url {
+ url: String,
+ #[serde(default)]
+ app: Application,
+ },
+ Path {
+ path: PathBuf,
+ #[serde(default)]
+ app: Application,
+ },
+}
From 3858e0f26172ffeec300191fa7fb32aa1092bf7b Mon Sep 17 00:00:00 2001
From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com>
Date: Mon, 5 Oct 2026 11:02:02 +0800
Subject: [PATCH 2/4] feat(utils): run blocking native work on a dedicated
thread
---
crates/utils/Cargo.toml | 3 ++
crates/utils/src/lib.rs | 83 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 86 insertions(+)
diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml
index 622f6ca9421..fe0e2b2e33d 100644
--- a/crates/utils/Cargo.toml
+++ b/crates/utils/Cargo.toml
@@ -38,5 +38,8 @@ workspace-hack = { version = "0.1", path = "../workspace-hack" }
tracing-appender = "0.2.3"
tempfile = "3"
+[target.'cfg(target_os = "linux")'.dev-dependencies]
+zbus = { version = "5.18.0", default-features = false, features = ["tokio", "blocking-api"] }
+
[lints]
workspace = true
diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs
index 1e510081e11..9a4e9e0149b 100644
--- a/crates/utils/src/lib.rs
+++ b/crates/utils/src/lib.rs
@@ -34,6 +34,25 @@ where
tokio::spawn(future.with_current_subscriber().in_current_span())
}
+/// Runs blocking native work on a new thread outside every Tokio runtime context.
+/// Some blocking APIs, such as zbus on Linux, start their own runtime and panic when
+/// called from an async task.
+pub async fn run_on_dedicated_thread(name: &str, work: F) -> std::io::Result
+where
+ F: FnOnce() -> T + Send + 'static,
+ T: Send + 'static,
+{
+ let (sender, receiver) = tokio::sync::oneshot::channel();
+ std::thread::Builder::new()
+ .name(name.to_string())
+ .spawn(move || {
+ let _ = sender.send(work());
+ })?;
+ receiver
+ .await
+ .map_err(|_| std::io::Error::other(format!("{name} thread stopped before finishing")))
+}
+
pub fn ensure_dir(path: &PathBuf) -> Result {
std::fs::create_dir_all(path)?;
Ok(path.clone())
@@ -427,3 +446,67 @@ mod tests {
assert_eq!(result, "test (2).cap");
}
}
+
+#[cfg(test)]
+mod dedicated_thread_tests {
+ use super::run_on_dedicated_thread;
+
+ async fn assert_work_can_start_its_own_runtime() {
+ let value = run_on_dedicated_thread("runtime-test", || {
+ tokio::runtime::Builder::new_current_thread()
+ .build()
+ .unwrap()
+ .block_on(async { 7 })
+ })
+ .await
+ .unwrap();
+ assert_eq!(value, 7);
+ }
+
+ #[tokio::test]
+ async fn work_runs_outside_a_current_thread_runtime() {
+ assert_work_can_start_its_own_runtime().await;
+ }
+
+ #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+ async fn work_runs_outside_a_multi_thread_runtime() {
+ assert_work_can_start_its_own_runtime().await;
+ }
+
+ #[tokio::test]
+ async fn work_results_are_returned_unchanged() {
+ let result: Result<(), std::io::Error> = run_on_dedicated_thread("error-test", || {
+ Err(std::io::Error::new(
+ std::io::ErrorKind::PermissionDenied,
+ "denied",
+ ))
+ })
+ .await
+ .unwrap();
+ let error = result.unwrap_err();
+ assert_eq!(error.kind(), std::io::ErrorKind::PermissionDenied);
+ assert_eq!(error.to_string(), "denied");
+ }
+
+ #[tokio::test]
+ async fn panicking_work_becomes_an_error() {
+ let error = run_on_dedicated_thread::<(), _>("panic-test", || panic!("worker failed"))
+ .await
+ .unwrap_err();
+ assert_eq!(
+ error.to_string(),
+ "panic-test thread stopped before finishing"
+ );
+ }
+
+ #[cfg(target_os = "linux")]
+ #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
+ async fn blocking_zbus_runs_from_an_async_task() {
+ // A headless host may have no session bus; only a thread failure is a regression.
+ let result = run_on_dedicated_thread("zbus-test", || {
+ zbus::blocking::Connection::session().map(drop)
+ })
+ .await;
+ assert!(result.is_ok());
+ }
+}
From 03d6bf9a751ba096f4bd608ddea4a2727cf2fc0d Mon Sep 17 00:00:00 2001
From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com>
Date: Mon, 5 Oct 2026 11:12:49 +0800
Subject: [PATCH 3/4] fix(desktop): reveal items through a dedicated thread
command
---
.../src-tauri/capabilities/default.json | 3 +-
apps/desktop/src-tauri/src/diagnostics.rs | 4 +-
apps/desktop/src-tauri/src/lib.rs | 20 ++
.../src/routes/editor/EditorErrorScreen.tsx | 3 +-
apps/desktop/src/routes/editor/Header.tsx | 3 +-
.../src/routes/screenshot-editor/Header.tsx | 4 +-
apps/desktop/src/utils/recording.test.ts | 1 -
apps/desktop/src/utils/recording.ts | 3 +-
apps/desktop/src/utils/tauri.ts | 3 +
vendor/tauri-plugin-opener/.gitignore | 1 -
vendor/tauri-plugin-opener/CAP-CHANGES.md | 33 --
vendor/tauri-plugin-opener/CHANGELOG.md | 59 ----
vendor/tauri-plugin-opener/Cargo.toml | 135 --------
vendor/tauri-plugin-opener/LICENSE.spdx | 20 --
vendor/tauri-plugin-opener/LICENSE_APACHE-2.0 | 177 -----------
vendor/tauri-plugin-opener/LICENSE_MIT | 21 --
vendor/tauri-plugin-opener/README.md | 143 ---------
vendor/tauri-plugin-opener/SECURITY.md | 23 --
vendor/tauri-plugin-opener/android/.gitignore | 2 -
.../android/build.gradle.kts | 40 ---
.../android/proguard-rules.pro | 21 --
.../android/settings.gradle | 2 -
.../android/src/main/AndroidManifest.xml | 3 -
.../android/src/main/java/OpenerPlugin.kt | 44 ---
vendor/tauri-plugin-opener/api-iife.js | 26 --
vendor/tauri-plugin-opener/build.rs | 136 --------
.../tauri-plugin-opener/ios/Package.resolved | 16 -
vendor/tauri-plugin-opener/ios/Package.swift | 34 --
.../ios/Sources/OpenerPlugin.swift | 46 ---
.../permissions/allow-default-urls.toml | 17 -
.../autogenerated/commands/open_path.toml | 13 -
.../autogenerated/commands/open_url.toml | 13 -
.../commands/reveal_item_in_dir.toml | 13 -
.../permissions/autogenerated/reference.md | 111 -------
.../permissions/default.toml | 10 -
vendor/tauri-plugin-opener/src/commands.rs | 158 ---------
vendor/tauri-plugin-opener/src/config.rs | 19 --
vendor/tauri-plugin-opener/src/error.rs | 57 ----
vendor/tauri-plugin-opener/src/init-iife.js | 19 --
vendor/tauri-plugin-opener/src/lib.rs | 238 --------------
vendor/tauri-plugin-opener/src/open.rs | 61 ----
.../src/reveal_item_in_dir.rs | 299 ------------------
vendor/tauri-plugin-opener/src/scope.rs | 141 ---------
vendor/tauri-plugin-opener/src/scope_entry.rs | 36 ---
44 files changed, 30 insertions(+), 2201 deletions(-)
delete mode 100644 vendor/tauri-plugin-opener/.gitignore
delete mode 100644 vendor/tauri-plugin-opener/CAP-CHANGES.md
delete mode 100644 vendor/tauri-plugin-opener/CHANGELOG.md
delete mode 100644 vendor/tauri-plugin-opener/Cargo.toml
delete mode 100644 vendor/tauri-plugin-opener/LICENSE.spdx
delete mode 100644 vendor/tauri-plugin-opener/LICENSE_APACHE-2.0
delete mode 100644 vendor/tauri-plugin-opener/LICENSE_MIT
delete mode 100644 vendor/tauri-plugin-opener/README.md
delete mode 100644 vendor/tauri-plugin-opener/SECURITY.md
delete mode 100644 vendor/tauri-plugin-opener/android/.gitignore
delete mode 100644 vendor/tauri-plugin-opener/android/build.gradle.kts
delete mode 100644 vendor/tauri-plugin-opener/android/proguard-rules.pro
delete mode 100644 vendor/tauri-plugin-opener/android/settings.gradle
delete mode 100644 vendor/tauri-plugin-opener/android/src/main/AndroidManifest.xml
delete mode 100644 vendor/tauri-plugin-opener/android/src/main/java/OpenerPlugin.kt
delete mode 100644 vendor/tauri-plugin-opener/api-iife.js
delete mode 100644 vendor/tauri-plugin-opener/build.rs
delete mode 100644 vendor/tauri-plugin-opener/ios/Package.resolved
delete mode 100644 vendor/tauri-plugin-opener/ios/Package.swift
delete mode 100644 vendor/tauri-plugin-opener/ios/Sources/OpenerPlugin.swift
delete mode 100644 vendor/tauri-plugin-opener/permissions/allow-default-urls.toml
delete mode 100644 vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_path.toml
delete mode 100644 vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_url.toml
delete mode 100644 vendor/tauri-plugin-opener/permissions/autogenerated/commands/reveal_item_in_dir.toml
delete mode 100644 vendor/tauri-plugin-opener/permissions/autogenerated/reference.md
delete mode 100644 vendor/tauri-plugin-opener/permissions/default.toml
delete mode 100644 vendor/tauri-plugin-opener/src/commands.rs
delete mode 100644 vendor/tauri-plugin-opener/src/config.rs
delete mode 100644 vendor/tauri-plugin-opener/src/error.rs
delete mode 100644 vendor/tauri-plugin-opener/src/init-iife.js
delete mode 100644 vendor/tauri-plugin-opener/src/lib.rs
delete mode 100644 vendor/tauri-plugin-opener/src/open.rs
delete mode 100644 vendor/tauri-plugin-opener/src/reveal_item_in_dir.rs
delete mode 100644 vendor/tauri-plugin-opener/src/scope.rs
delete mode 100644 vendor/tauri-plugin-opener/src/scope_entry.rs
diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json
index 745254f6eb4..3c4d5646057 100644
--- a/apps/desktop/src-tauri/capabilities/default.json
+++ b/apps/desktop/src-tauri/capabilities/default.json
@@ -68,7 +68,6 @@
{ "url": "http://localhost:*" }
]
},
- "clipboard-manager:allow-write-text",
- "opener:allow-reveal-item-in-dir"
+ "clipboard-manager:allow-write-text"
]
}
diff --git a/apps/desktop/src-tauri/src/diagnostics.rs b/apps/desktop/src-tauri/src/diagnostics.rs
index 761b4375e3a..102c735e423 100644
--- a/apps/desktop/src-tauri/src/diagnostics.rs
+++ b/apps/desktop/src-tauri/src/diagnostics.rs
@@ -6,7 +6,6 @@ use std::{
sync::atomic::{AtomicBool, Ordering},
};
use tauri::{AppHandle, Manager};
-use tauri_plugin_opener::OpenerExt;
use tauri_specta::Event;
use tokio::io::AsyncBufReadExt;
use tracing::{error, info, warn};
@@ -548,9 +547,8 @@ pub async fn upload_diagnostic_report(app: AppHandle, report_path: String) -> Re
pub async fn reveal_diagnostic_report(app: AppHandle, report_path: String) -> Result<(), String> {
let path = validate_report_path(&app, &report_path)?;
- tokio::task::spawn_blocking(move || app.opener().reveal_item_in_dir(&path))
+ crate::reveal_in_dir(app, path)
.await
- .map_err(|e| format!("Failed to reveal diagnostic report: {e}"))?
.map_err(|e| format!("Failed to reveal diagnostic report: {e}"))
}
diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs
index a78c370641e..b97277971b9 100644
--- a/apps/desktop/src-tauri/src/lib.rs
+++ b/apps/desktop/src-tauri/src/lib.rs
@@ -4656,6 +4656,25 @@ async fn open_file_path(_app: AppHandle, path: PathBuf) -> Result<(), String> {
Ok(())
}
+/// The opener's Linux backend makes blocking zbus calls, which panic inside an async task.
+pub(crate) async fn reveal_in_dir(app: AppHandle, path: PathBuf) -> Result<(), String> {
+ cap_utils::run_on_dedicated_thread("reveal-item-in-dir", move || {
+ app.opener().reveal_item_in_dir(path)
+ })
+ .await
+ .map_err(|e| e.to_string())?
+ .map_err(|e| e.to_string())
+}
+
+#[tauri::command]
+#[specta::specta]
+#[instrument(skip(app))]
+async fn reveal_item_in_dir(app: AppHandle, path: PathBuf) -> Result<(), String> {
+ reveal_in_dir(app, path)
+ .await
+ .map_err(|e| format!("Failed to reveal item: {e}"))
+}
+
#[derive(Deserialize, specta::Type, tauri_specta::Event, Debug, Clone)]
struct RenderFrameEvent {
frame_number: u32,
@@ -6810,6 +6829,7 @@ fn specta_builder() -> tauri_specta::Builder {
copy_image_to_clipboard,
copy_rendered_screenshot_to_clipboard,
open_file_path,
+ reveal_item_in_dir,
get_video_metadata,
create_editor_instance,
editor_preparing::create_preparing_editor_frame,
diff --git a/apps/desktop/src/routes/editor/EditorErrorScreen.tsx b/apps/desktop/src/routes/editor/EditorErrorScreen.tsx
index cc2a21478c0..b22fe189a0c 100644
--- a/apps/desktop/src/routes/editor/EditorErrorScreen.tsx
+++ b/apps/desktop/src/routes/editor/EditorErrorScreen.tsx
@@ -2,7 +2,6 @@ import { Button } from "@cap/ui-solid";
import { createMutation } from "@tanstack/solid-query";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
-import { revealItemInDir } from "@tauri-apps/plugin-opener";
import { type as ostype } from "@tauri-apps/plugin-os";
import { createEffect, onCleanup, Show } from "solid-js";
import CaptionControlsWindows11 from "~/components/titlebar/controls/CaptionControlsWindows11";
@@ -165,7 +164,7 @@ export function EditorErrorScreen(props: {
});
const handleOpenFolder = () => {
- revealItemInDir(props.projectPath);
+ commands.revealItemInDir(props.projectPath);
};
return (
diff --git a/apps/desktop/src/routes/editor/Header.tsx b/apps/desktop/src/routes/editor/Header.tsx
index db72fa9ac19..a49d41889e6 100644
--- a/apps/desktop/src/routes/editor/Header.tsx
+++ b/apps/desktop/src/routes/editor/Header.tsx
@@ -1,5 +1,4 @@
import { ask } from "@tauri-apps/plugin-dialog";
-import { revealItemInDir } from "@tauri-apps/plugin-opener";
import { type as ostype } from "@tauri-apps/plugin-os";
import { cx } from "cva";
import {
@@ -129,7 +128,7 @@ export function Header(props: {
clearTimelineSelection();
console.log({ path: `${editorInstance.path}/` });
- revealItemInDir(`${editorInstance.path}/`);
+ commands.revealItemInDir(`${editorInstance.path}/`);
}}
tooltipText="Open recording bundle"
leftIcon={}
diff --git a/apps/desktop/src/routes/screenshot-editor/Header.tsx b/apps/desktop/src/routes/screenshot-editor/Header.tsx
index a41171e16a5..8489ffcf573 100644
--- a/apps/desktop/src/routes/screenshot-editor/Header.tsx
+++ b/apps/desktop/src/routes/screenshot-editor/Header.tsx
@@ -2,12 +2,12 @@ import { DropdownMenu } from "@kobalte/core/dropdown-menu";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { ask } from "@tauri-apps/plugin-dialog";
import { remove } from "@tauri-apps/plugin-fs";
-import { revealItemInDir } from "@tauri-apps/plugin-opener";
import { type as ostype } from "@tauri-apps/plugin-os";
import { cx } from "cva";
import { createEffect, onCleanup, Suspense } from "solid-js";
import CaptionControlsMacOS from "~/components/titlebar/controls/CaptionControlsMacOS";
import CaptionControlsWindows11 from "~/components/titlebar/controls/CaptionControlsWindows11";
+import { commands } from "~/utils/tauri";
import IconCapCrop from "~icons/cap/crop";
import IconCapTrash from "~icons/cap/trash";
import IconLucideCopy from "~icons/lucide/copy";
@@ -184,7 +184,7 @@ export function Header() {
>
{
- revealItemInDir(path());
+ commands.revealItemInDir(path());
}}
>
diff --git a/apps/desktop/src/utils/recording.test.ts b/apps/desktop/src/utils/recording.test.ts
index b4e37244490..8766b5a0478 100644
--- a/apps/desktop/src/utils/recording.test.ts
+++ b/apps/desktop/src/utils/recording.test.ts
@@ -8,7 +8,6 @@ import {
vi.mock("@tauri-apps/api/event", () => ({ emit: vi.fn() }));
vi.mock("@tauri-apps/plugin-dialog", () => ({ message: vi.fn() }));
-vi.mock("@tauri-apps/plugin-opener", () => ({ revealItemInDir: vi.fn() }));
vi.mock("./tauri", () => ({ commands: {} }));
describe("recording start cancellation", () => {
diff --git a/apps/desktop/src/utils/recording.ts b/apps/desktop/src/utils/recording.ts
index 4fde8b32777..4cb91207973 100644
--- a/apps/desktop/src/utils/recording.ts
+++ b/apps/desktop/src/utils/recording.ts
@@ -1,6 +1,5 @@
import { emit } from "@tauri-apps/api/event";
import * as dialog from "@tauri-apps/plugin-dialog";
-import { revealItemInDir } from "@tauri-apps/plugin-opener";
import type { createOptionsQuery } from "./queries";
import {
commands,
@@ -125,5 +124,5 @@ export async function openRecordingFolder(
if (openedContent) return;
- await revealItemInDir(`${path}/`);
+ await commands.revealItemInDir(`${path}/`);
}
diff --git a/apps/desktop/src/utils/tauri.ts b/apps/desktop/src/utils/tauri.ts
index 6f27d9856cb..767b14c177a 100644
--- a/apps/desktop/src/utils/tauri.ts
+++ b/apps/desktop/src/utils/tauri.ts
@@ -217,6 +217,9 @@ async copyRenderedScreenshotToClipboard() : Promise {
async openFilePath(path: string) : Promise {
return await TAURI_INVOKE("open_file_path", { path });
},
+async revealItemInDir(path: string) : Promise {
+ return await TAURI_INVOKE("reveal_item_in_dir", { path });
+},
async getVideoMetadata(path: string) : Promise {
return await TAURI_INVOKE("get_video_metadata", { path });
},
diff --git a/vendor/tauri-plugin-opener/.gitignore b/vendor/tauri-plugin-opener/.gitignore
deleted file mode 100644
index 53a0a824d96..00000000000
--- a/vendor/tauri-plugin-opener/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/permissions/schemas/
diff --git a/vendor/tauri-plugin-opener/CAP-CHANGES.md b/vendor/tauri-plugin-opener/CAP-CHANGES.md
deleted file mode 100644
index 1f0056ea099..00000000000
--- a/vendor/tauri-plugin-opener/CAP-CHANGES.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Cap changes to tauri-plugin-opener
-
-Vendored from the crates.io `tauri-plugin-opener` 2.5.0 archive, SHA-256
-`786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5`.
-The archive identifies upstream commit
-`2371804172fa852ef3c127b2398c54bc35f74950` in
-[tauri-apps/plugins-workspace](https://github.com/tauri-apps/plugins-workspace/tree/2371804172fa852ef3c127b2398c54bc35f74950/plugins/opener).
-
-The async `reveal_item_in_dir` IPC command previously called the synchronous
-reveal implementation on a Tokio worker. On Linux, blocking zbus can enter
-its own Tokio runtime and panic. The command now awaits a blocking worker
-and propagates both backend errors and worker failures. Its name, arguments,
-permission definitions, path canonicalization, and native backends are unchanged.
-All existing JavaScript callers retain the same IPC and permission boundary.
-
-Tests cover both Tokio runtime flavors, exact path forwarding, backend errors,
-worker panics, validation of all paths, and the real Linux zbus backend with the
-Tokio feature enabled. The Linux test permits a missing session bus or file
-manager; it does not establish successful graphical file selection.
-
-The workspace uses this source through `[patch.crates-io]`. The crate is also a
-workspace member so Cargo can run its regression tests. Dev dependencies reuse
-versions already in the workspace lockfile. No runtime dependency was upgraded.
-
-Packaging omits the upstream lockfile, original workspace manifest, npm build
-configuration, and guest TypeScript sources. The shipped JavaScript bundles are
-retained with formatting only. One trailing space in the Android build file is
-removed. Rust source is rustfmt-formatted, and
-the build regenerates the ignored permission schema. Both upstream licenses and
-all platform source files are retained.
-
-Current upstream still has the direct synchronous command call. Revisit this
-patch when upstream supplies an equivalent async boundary and retain the tests.
diff --git a/vendor/tauri-plugin-opener/CHANGELOG.md b/vendor/tauri-plugin-opener/CHANGELOG.md
deleted file mode 100644
index 94c5c1bc0c3..00000000000
--- a/vendor/tauri-plugin-opener/CHANGELOG.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Changelog
-
-## \[2.5.0]
-
-### enhance
-
-- [`b8056f48`](https://github.com/tauri-apps/plugins-workspace/commit/b8056f484c7144af095d4d6ded1e8adbb9b8a865) ([#2897](https://github.com/tauri-apps/plugins-workspace/pull/2897) by [@petersamokhin](https://github.com/tauri-apps/plugins-workspace/../../petersamokhin)) Allow reveal multiple items in the file explorer.
-
-## \[2.4.0]
-
-- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
-
-## \[2.3.1]
-
-### feat
-
-- [`2aec8ff4`](https://github.com/tauri-apps/plugins-workspace/commit/2aec8ff4c41d178ea9804f7b6eff343c726be015) ([#2803](https://github.com/tauri-apps/plugins-workspace/pull/2803) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Add `inAppBrowser` option to open URLs in an in-app browser on Android and iOS.
-
-## \[2.3.0]
-
-- [`ce9888a2`](https://github.com/tauri-apps/plugins-workspace/commit/ce9888a2d4c9b449bd2a306f0fa6c76507fd46d3) ([#2762](https://github.com/tauri-apps/plugins-workspace/pull/2762)) Similar to the `fs` plugin the `opener` plugin now supports a `requireLiteralLeadingDot` configuration in `tauri.conf.json`.
-
-## \[2.2.7]
-
-- [`6c9e08dc`](https://github.com/tauri-apps/plugins-workspace/commit/6c9e08dccb3ac99fccfce586fa2b69717ba81b52) ([#2695](https://github.com/tauri-apps/plugins-workspace/pull/2695) by [@ShaunSHamilton](https://github.com/tauri-apps/plugins-workspace/../../ShaunSHamilton)) Adjust `open_url` url type to allow `URL`
-- [`dde6f3c3`](https://github.com/tauri-apps/plugins-workspace/commit/dde6f3c31c1b79942abb556be31757dc583f509e) ([#2549](https://github.com/tauri-apps/plugins-workspace/pull/2549) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Update `windows` crate to 0.61 to align with Tauri 2.5
-
-## \[2.2.6]
-
-- [`1a984659`](https://github.com/tauri-apps/plugins-workspace/commit/1a9846599b6a71faf330845847a30f6bf9735898) ([#2469](https://github.com/tauri-apps/plugins-workspace/pull/2469) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Update `objc2` crate to 0.6. No user facing changes.
-- [`71f95c9f`](https://github.com/tauri-apps/plugins-workspace/commit/71f95c9f05b29cf1be586849614c0b007757c15d) ([#2445](https://github.com/tauri-apps/plugins-workspace/pull/2445) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `windows` crate to 0.60 to match Tauri 2.3.0. No user facing changes.
-
-## \[2.2.5]
-
-- [`5b821181`](https://github.com/tauri-apps/plugins-workspace/commit/5b8211815825ddae2dcc0c00520e0cfdff002763) ([#2332](https://github.com/tauri-apps/plugins-workspace/pull/2332) by [@betamos](https://github.com/tauri-apps/plugins-workspace/../../betamos)) Fix broken JS commands `opener.openPath` and `opener.openUrl` on mobile.
-
-## \[2.2.4]
-
-- [`da5c59e2`](https://github.com/tauri-apps/plugins-workspace/commit/da5c59e2fe879d177e3cfd52fcacce85440423cb) ([#2271](https://github.com/tauri-apps/plugins-workspace/pull/2271) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `zbus` dependency to version 5. No API changes.
-
-## \[2.2.3]
-
-- [`a9ac1e3c`](https://github.com/tauri-apps/plugins-workspace/commit/a9ac1e3c939cec4338a9422ef02323c1d4dde6cd) ([#2253](https://github.com/tauri-apps/plugins-workspace/pull/2253) by [@betamos](https://github.com/tauri-apps/plugins-workspace/../../betamos)) Return an error in `open_path` if the file does not exist when opening with default application.
-
-## \[2.2.2]
-
-- [`ee0f65de`](https://github.com/tauri-apps/plugins-workspace/commit/ee0f65de5c645c244c5f0b638e0e0aab687cb9bf) ([#2207](https://github.com/tauri-apps/plugins-workspace/pull/2207) by [@universalappfactory](https://github.com/tauri-apps/plugins-workspace/../../universalappfactory)) Fixed OpenerPlugin packagename for android
-
-## \[2.2.1]
-
-- [`18dffc9d`](https://github.com/tauri-apps/plugins-workspace/commit/18dffc9dfecaf0c900e233e041d9ca36c92834b5) ([#2189](https://github.com/tauri-apps/plugins-workspace/pull/2189) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Fix usage on iOS.
-
-## \[2.2.0]
-
-- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
-
-## \[2.0.0]
-
-- [`383e636a`](https://github.com/tauri-apps/plugins-workspace/commit/383e636a8e595aec1300999a8aeb7d9bf8c14632) ([#2019](https://github.com/tauri-apps/plugins-workspace/pull/2019) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Initial Release
diff --git a/vendor/tauri-plugin-opener/Cargo.toml b/vendor/tauri-plugin-opener/Cargo.toml
deleted file mode 100644
index 35229ce3291..00000000000
--- a/vendor/tauri-plugin-opener/Cargo.toml
+++ /dev/null
@@ -1,135 +0,0 @@
-[package]
-edition = "2021"
-name = "tauri-plugin-opener"
-version = "2.5.0"
-authors = ["Tauri Programme within The Commons Conservancy"]
-build = "build.rs"
-links = "tauri-plugin-opener"
-autolib = false
-autobins = false
-autoexamples = false
-autotests = false
-autobenches = false
-description = "Open files and URLs using their default application."
-readme = "README.md"
-license = "Apache-2.0 OR MIT"
-repository = "https://github.com/tauri-apps/plugins-workspace"
-
-[package.metadata.docs.rs]
-rustc-args = [
- "--cfg",
- "docsrs",
-]
-rustdoc-args = [
- "--cfg",
- "docsrs",
-]
-
-[package.metadata.platforms.support.android]
-level = "partial"
-notes = "Only allows to open URLs via `open`"
-
-[package.metadata.platforms.support.ios]
-level = "partial"
-notes = "Only allows to open URLs via `open`"
-
-[package.metadata.platforms.support.linux]
-level = "full"
-notes = ""
-
-[package.metadata.platforms.support.macos]
-level = "full"
-notes = ""
-
-[package.metadata.platforms.support.windows]
-level = "full"
-notes = ""
-
-[lib]
-name = "tauri_plugin_opener"
-path = "src/lib.rs"
-
-[dependencies.dunce]
-version = "1"
-
-[dependencies.glob]
-version = "0.3"
-
-[dependencies.open]
-version = "5"
-features = ["shellexecute-on-windows"]
-
-[dependencies.serde]
-version = "1"
-features = ["derive"]
-
-[dependencies.serde_json]
-version = "1"
-
-[dependencies.tauri]
-version = "2.8.2"
-default-features = false
-
-[dependencies.thiserror]
-version = "2"
-
-[build-dependencies.schemars]
-version = "0.8"
-
-[build-dependencies.serde]
-version = "1"
-features = ["derive"]
-
-[build-dependencies.tauri-plugin]
-version = "2.4"
-features = ["build"]
-
-[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies.url]
-version = "2"
-
-[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies.zbus]
-version = "5.9"
-
-[target.'cfg(target_os = "ios")'.dependencies.tauri]
-version = "2.8.2"
-features = ["wry"]
-default-features = false
-
-[target.'cfg(target_os = "macos")'.dependencies.objc2-app-kit]
-version = "0.3"
-features = [
- "std",
- "NSWorkspace",
-]
-default-features = false
-
-[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
-version = "0.3"
-features = [
- "std",
- "NSURL",
- "NSArray",
- "NSString",
-]
-default-features = false
-
-[target."cfg(windows)".dependencies.windows]
-version = "0.61"
-features = [
- "Win32_Foundation",
- "Win32_UI_Shell_Common",
- "Win32_UI_WindowsAndMessaging",
- "Win32_System_Com",
- "Win32_System_Registry",
-]
-
-[dev-dependencies.tokio]
-version = "1"
-features = ["macros", "rt-multi-thread"]
-
-[dev-dependencies.tempfile]
-version = "3"
-
-[target.'cfg(target_os = "linux")'.dev-dependencies.zbus]
-version = "5.9"
-features = ["tokio"]
diff --git a/vendor/tauri-plugin-opener/LICENSE.spdx b/vendor/tauri-plugin-opener/LICENSE.spdx
deleted file mode 100644
index cdd0df5ad76..00000000000
--- a/vendor/tauri-plugin-opener/LICENSE.spdx
+++ /dev/null
@@ -1,20 +0,0 @@
-SPDXVersion: SPDX-2.1
-DataLicense: CC0-1.0
-PackageName: tauri
-DataFormat: SPDXRef-1
-PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy
-PackageHomePage: https://tauri.app
-PackageLicenseDeclared: Apache-2.0
-PackageLicenseDeclared: MIT
-PackageCopyrightText: 2019-2022, The Tauri Programme in the Commons Conservancy
-PackageSummary: Tauri is a rust project that enables developers to make secure
-and small desktop applications using a web frontend.
-
-PackageComment: The package includes the following libraries; see
-Relationship information.
-
-Created: 2019-05-20T09:00:00Z
-PackageDownloadLocation: git://github.com/tauri-apps/tauri
-PackageDownloadLocation: git+https://github.com/tauri-apps/tauri.git
-PackageDownloadLocation: git+ssh://github.com/tauri-apps/tauri.git
-Creator: Person: Daniel Thompson-Yvetot
\ No newline at end of file
diff --git a/vendor/tauri-plugin-opener/LICENSE_APACHE-2.0 b/vendor/tauri-plugin-opener/LICENSE_APACHE-2.0
deleted file mode 100644
index 4947287f7b5..00000000000
--- a/vendor/tauri-plugin-opener/LICENSE_APACHE-2.0
+++ /dev/null
@@ -1,177 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/vendor/tauri-plugin-opener/LICENSE_MIT b/vendor/tauri-plugin-opener/LICENSE_MIT
deleted file mode 100644
index 4d754725663..00000000000
--- a/vendor/tauri-plugin-opener/LICENSE_MIT
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2017 - Present Tauri Apps Contributors
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
diff --git a/vendor/tauri-plugin-opener/README.md b/vendor/tauri-plugin-opener/README.md
deleted file mode 100644
index bcb9265ca48..00000000000
--- a/vendor/tauri-plugin-opener/README.md
+++ /dev/null
@@ -1,143 +0,0 @@
-
-
-
-
-| Platform | Supported |
-| -------- | --------- |
-| Linux | ✓ |
-| Windows | ✓ |
-| macOS | ✓ |
-| Android | ? |
-| iOS | ? |
-
-## Install
-
-_This plugin requires a Rust version of at least **1.77.2**_
-
-There are three general methods of installation that we can recommend.
-
-1. Use crates.io and npm (easiest, and requires you to trust that our publishing pipeline worked)
-2. Pull sources directly from Github using git tags / revision hashes (most secure)
-3. Git submodule install this repo in your tauri project and then use file protocol to ingest the source (most secure, but inconvenient to use)
-
-Install the Core plugin by adding the following to your `Cargo.toml` file:
-
-`src-tauri/Cargo.toml`
-
-```toml
-[dependencies]
-tauri-plugin-opener = "2.0.0"
-# alternatively with Git:
-tauri-plugin-opener = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
-```
-
-You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
-
-
-
-```sh
-pnpm add @tauri-apps/plugin-opener
-# or
-npm add @tauri-apps/plugin-opener
-# or
-yarn add @tauri-apps/plugin-opener
-```
-
-## Usage
-
-First you need to register the core plugin with Tauri:
-
-`src-tauri/src/lib.rs`
-
-```rust
-fn main() {
- tauri::Builder::default()
- .plugin(tauri_plugin_opener::init())
- .run(tauri::generate_context!())
- .expect("error while running tauri application");
-}
-```
-
-Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
-
-```javascript
-import { openUrl, openPath, revealItemInDir } from '@tauri-apps/plugin-opener'
-
-// Opens the URL in the default browser
-await openUrl('https://example.com')
-// Or with a specific browser/app
-await openUrl('https://example.com', 'firefox')
-
-// Opens the path with the system's default app
-await openPath('/path/to/file')
-// Or with a specific app
-await openPath('/path/to/file', 'firefox')
-
-// Reveal a path with the system's default explorer
-await revealItemInDir('/path/to/file')
-
-// Reveal multiple paths with the system's default explorer
-// Note: will be renamed to `revealItemsInDir` in the next major version
-await revealItemInDir(['/path/to/file', '/path/to/another/file'])
-```
-
-### Usage from Rust
-
-You can also use those APIs from Rust:
-
-```rust
-use tauri_plugin_opener::OpenerExt;
-
-fn main() {
- tauri::Builder::default()
- .plugin(tauri_plugin_opener::init())
- .setup(|app| {
- let opener = app.opener();
-
- // Opens the URL in the default browser
- opener.open_url("https://example.com", None::<&str>)?;
- // Or with a specific browser/app
- opener.open_url("https://example.com", Some("firefox"))?;
-
- // Opens the path with the system's default app
- opener.open_path("/path/to/file", None::<&str>)?;
- // Or with a specific app
- opener.open_path("/path/to/file", Some("firefox"))?;
-
- // Reveal a path with the system's default explorer
- opener.reveal_item_in_dir("/path/to/file")?;
-
- // Reveal multiple paths with the system's default explorer
- opener.reveal_items_in_dir(["/path/to/file"])?;
- Ok(())
- })
- .run(tauri::generate_context!())
- .expect("error while running tauri application");
-}
-```
-
-## Contributing
-
-PRs accepted. Please make sure to read the Contributing Guide before making a pull request.
-
-## Partners
-
-
-
-
-
-
-
-
- |
-
-
-
-
-For the complete list of sponsors please visit our [website](https://tauri.app#sponsors) and [Open Collective](https://opencollective.com/tauri).
-
-## License
-
-Code: (c) 2015 - Present - The Tauri Programme within The Commons Conservancy.
-
-MIT or MIT/Apache 2.0 where applicable.
diff --git a/vendor/tauri-plugin-opener/SECURITY.md b/vendor/tauri-plugin-opener/SECURITY.md
deleted file mode 100644
index 4f09bbacd45..00000000000
--- a/vendor/tauri-plugin-opener/SECURITY.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Security Policy
-
-**Do not report security vulnerabilities through public GitHub issues.**
-
-**Please use the [Private Vulnerability Disclosure](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability) feature of GitHub.**
-
-Include as much of the following information:
-
-- Type of issue (e.g. improper input parsing, privilege escalation, etc.)
-- The location of the affected source code (tag/branch/commit or direct URL)
-- Any special configuration required to reproduce the issue
-- The distribution affected or used to help us with reproduction of the issue
-- Step-by-step instructions to reproduce the issue
-- Ideally a reproduction repository
-- Impact of the issue, including how an attacker might exploit the issue
-
-We prefer to receive reports in English.
-
-## Contact
-
-Please disclose a vulnerability or security relevant issue here: [https://github.com/tauri-apps/plugins-workspace/security/advisories/new](https://github.com/tauri-apps/plugins-workspace/security/advisories/new).
-
-Alternatively, you can also contact us by email via [security@tauri.app](mailto:security@tauri.app).
diff --git a/vendor/tauri-plugin-opener/android/.gitignore b/vendor/tauri-plugin-opener/android/.gitignore
deleted file mode 100644
index c0f21ec2fd9..00000000000
--- a/vendor/tauri-plugin-opener/android/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/build
-/.tauri
diff --git a/vendor/tauri-plugin-opener/android/build.gradle.kts b/vendor/tauri-plugin-opener/android/build.gradle.kts
deleted file mode 100644
index 0c0612a49d4..00000000000
--- a/vendor/tauri-plugin-opener/android/build.gradle.kts
+++ /dev/null
@@ -1,40 +0,0 @@
-plugins {
- id("com.android.library")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- namespace = "app.tauri.opener"
- compileSdk = 34
-
- defaultConfig {
- minSdk = 24
-
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles("consumer-rules.pro")
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro"
- )
- }
- }
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = "1.8"
- }
-}
-
-dependencies {
- implementation("androidx.core:core-ktx:1.9.0")
- implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
- implementation("androidx.browser:browser:1.8.0")
- implementation(project(":tauri-android"))
-}
diff --git a/vendor/tauri-plugin-opener/android/proguard-rules.pro b/vendor/tauri-plugin-opener/android/proguard-rules.pro
deleted file mode 100644
index 481bb434814..00000000000
--- a/vendor/tauri-plugin-opener/android/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/vendor/tauri-plugin-opener/android/settings.gradle b/vendor/tauri-plugin-opener/android/settings.gradle
deleted file mode 100644
index 14a752e4336..00000000000
--- a/vendor/tauri-plugin-opener/android/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-include ':tauri-android'
-project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
diff --git a/vendor/tauri-plugin-opener/android/src/main/AndroidManifest.xml b/vendor/tauri-plugin-opener/android/src/main/AndroidManifest.xml
deleted file mode 100644
index 9a40236b947..00000000000
--- a/vendor/tauri-plugin-opener/android/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/vendor/tauri-plugin-opener/android/src/main/java/OpenerPlugin.kt b/vendor/tauri-plugin-opener/android/src/main/java/OpenerPlugin.kt
deleted file mode 100644
index a65a2f52a13..00000000000
--- a/vendor/tauri-plugin-opener/android/src/main/java/OpenerPlugin.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-package app.tauri.opener
-
-import android.app.Activity
-import android.content.Intent
-import androidx.browser.customtabs.CustomTabsIntent
-import app.tauri.annotation.Command
-import app.tauri.annotation.TauriPlugin
-import app.tauri.plugin.Invoke
-import app.tauri.plugin.Plugin
-import androidx.core.net.toUri
-import app.tauri.annotation.InvokeArg
-
-@InvokeArg
-class OpenArgs {
- lateinit var url: String
- var with: String? = null
-}
-
-@TauriPlugin
-class OpenerPlugin(private val activity: Activity) : Plugin(activity) {
- @Command
- fun open(invoke: Invoke) {
- try {
- val args = invoke.parseArgs(OpenArgs::class.java)
-
- if (args.with == "inAppBrowser") {
- val builder = CustomTabsIntent.Builder()
- val intent = builder.build()
- intent.launchUrl(activity, args.url.toUri())
- } else {
- val intent = Intent(Intent.ACTION_VIEW, args.url.toUri())
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- activity.applicationContext?.startActivity(intent)
- }
- invoke.resolve()
- } catch (ex: Exception) {
- invoke.reject(ex.message)
- }
- }
-}
diff --git a/vendor/tauri-plugin-opener/api-iife.js b/vendor/tauri-plugin-opener/api-iife.js
deleted file mode 100644
index 847e54ea557..00000000000
--- a/vendor/tauri-plugin-opener/api-iife.js
+++ /dev/null
@@ -1,26 +0,0 @@
-if ("__TAURI__" in window) {
- var __TAURI_PLUGIN_OPENER__ = (function (n) {
- "use strict";
- async function e(n, e = {}, r) {
- return window.__TAURI_INTERNALS__.invoke(n, e, r);
- }
- return (
- "function" == typeof SuppressedError && SuppressedError,
- (n.openPath = async function (n, r) {
- await e("plugin:opener|open_path", { path: n, with: r });
- }),
- (n.openUrl = async function (n, r) {
- await e("plugin:opener|open_url", { url: n, with: r });
- }),
- (n.revealItemInDir = async function (n) {
- return e("plugin:opener|reveal_item_in_dir", {
- paths: "string" == typeof n ? [n] : n,
- });
- }),
- n
- );
- })({});
- Object.defineProperty(window.__TAURI__, "opener", {
- value: __TAURI_PLUGIN_OPENER__,
- });
-}
diff --git a/vendor/tauri-plugin-opener/build.rs b/vendor/tauri-plugin-opener/build.rs
deleted file mode 100644
index fbad4d3ac35..00000000000
--- a/vendor/tauri-plugin-opener/build.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use std::path::PathBuf;
-
-#[path = "src/scope_entry.rs"]
-#[allow(dead_code)]
-mod scope;
-
-/// Opener scope application.
-#[derive(schemars::JsonSchema)]
-#[serde(untagged)]
-#[allow(unused)]
-enum Application {
- /// Open in default application.
- Default,
- /// If true, allow open with any application.
- Enable(bool),
- /// Allow specific application to open with.
- App(String),
-}
-
-impl Default for Application {
- fn default() -> Self {
- Self::Default
- }
-}
-
-/// Opener scope entry.
-#[derive(schemars::JsonSchema)]
-#[serde(untagged)]
-#[allow(unused)]
-enum OpenerScopeEntry {
- Url {
- /// A URL that can be opened by the webview when using the Opener APIs.
- ///
- /// Wildcards can be used following the UNIX glob pattern.
- ///
- /// Examples:
- ///
- /// - "https://*" : allows all HTTPS origin
- ///
- /// - "https://*.github.com/tauri-apps/tauri": allows any subdomain of "github.com" with the "tauri-apps/api" path
- ///
- /// - "https://myapi.service.com/users/*": allows access to any URLs that begins with "https://myapi.service.com/users/"
- url: String,
- /// An application to open this url with, for example: firefox.
- #[serde(default)]
- app: Application,
- },
- Path {
- /// A path that can be opened by the webview when using the Opener APIs.
- ///
- /// The pattern can start with a variable that resolves to a system base directory.
- /// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
- /// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
- /// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
- /// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
- path: PathBuf,
- /// An application to open this path with, for example: xdg-open.
- #[serde(default)]
- app: Application,
- },
-}
-
-// Ensure `OpenerScopeEntry` and `scope::EntryRaw` is kept in sync
-fn _f() {
- match (scope::EntryRaw::Url {
- url: String::new(),
- app: scope::Application::Enable(true),
- }) {
- scope::EntryRaw::Url { url, app } => OpenerScopeEntry::Url {
- url,
- app: match app {
- scope::Application::Enable(p) => Application::Enable(p),
- scope::Application::App(p) => Application::App(p),
- scope::Application::Default => Application::Default,
- },
- },
- scope::EntryRaw::Path { path, app } => OpenerScopeEntry::Path {
- path,
- app: match app {
- scope::Application::Enable(p) => Application::Enable(p),
- scope::Application::App(p) => Application::App(p),
- scope::Application::Default => Application::Default,
- },
- },
- };
- match (OpenerScopeEntry::Url {
- url: String::new(),
- app: Application::Enable(true),
- }) {
- OpenerScopeEntry::Url { url, app } => scope::EntryRaw::Url {
- url,
- app: match app {
- Application::Enable(p) => scope::Application::Enable(p),
- Application::App(p) => scope::Application::App(p),
- Application::Default => scope::Application::Default,
- },
- },
- OpenerScopeEntry::Path { path, app } => scope::EntryRaw::Path {
- path,
- app: match app {
- Application::Enable(p) => scope::Application::Enable(p),
- Application::App(p) => scope::Application::App(p),
- Application::Default => scope::Application::Default,
- },
- },
- };
-}
-
-const COMMANDS: &[&str] = &["open_url", "open_path", "reveal_item_in_dir"];
-
-fn main() {
- tauri_plugin::Builder::new(COMMANDS)
- .global_api_script_path("./api-iife.js")
- .android_path("android")
- .ios_path("ios")
- .global_scope_schema(schemars::schema_for!(OpenerScopeEntry))
- .build();
-
- let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
- let mobile = target_os == "ios" || target_os == "android";
- alias("desktop", !mobile);
- alias("mobile", mobile);
-}
-
-// creates a cfg alias if `has_feature` is true.
-// `alias` must be a snake case string.
-fn alias(alias: &str, has_feature: bool) {
- println!("cargo:rustc-check-cfg=cfg({alias})");
- if has_feature {
- println!("cargo:rustc-cfg={alias}");
- }
-}
diff --git a/vendor/tauri-plugin-opener/ios/Package.resolved b/vendor/tauri-plugin-opener/ios/Package.resolved
deleted file mode 100644
index 5f998e0e66b..00000000000
--- a/vendor/tauri-plugin-opener/ios/Package.resolved
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "object": {
- "pins": [
- {
- "package": "SwiftRs",
- "repositoryURL": "https://github.com/Brendonovich/swift-rs",
- "state": {
- "branch": null,
- "revision": "b5ed223fcdab165bc21219c1925dc1e77e2bef5e",
- "version": "1.0.6"
- }
- }
- ]
- },
- "version": 1
-}
diff --git a/vendor/tauri-plugin-opener/ios/Package.swift b/vendor/tauri-plugin-opener/ios/Package.swift
deleted file mode 100644
index 8d06936e614..00000000000
--- a/vendor/tauri-plugin-opener/ios/Package.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-// swift-tools-version:5.3
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-import PackageDescription
-
-let package = Package(
- name: "tauri-plugin-opener",
- platforms: [
- .macOS(.v10_13),
- .iOS(.v13),
- ],
- products: [
- // Products define the executables and libraries a package produces, and make them visible to other packages.
- .library(
- name: "tauri-plugin-opener",
- type: .static,
- targets: ["tauri-plugin-opener"])
- ],
- dependencies: [
- .package(name: "Tauri", path: "../.tauri/tauri-api")
- ],
- targets: [
- // Targets are the basic building blocks of a package. A target can define a module or a test suite.
- // Targets can depend on other targets in this package, and on products in packages this package depends on.
- .target(
- name: "tauri-plugin-opener",
- dependencies: [
- .byName(name: "Tauri")
- ],
- path: "Sources")
- ]
-)
diff --git a/vendor/tauri-plugin-opener/ios/Sources/OpenerPlugin.swift b/vendor/tauri-plugin-opener/ios/Sources/OpenerPlugin.swift
deleted file mode 100644
index 2311a3b1b74..00000000000
--- a/vendor/tauri-plugin-opener/ios/Sources/OpenerPlugin.swift
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-import Foundation
-import SafariServices
-import SwiftRs
-import Tauri
-import UIKit
-import WebKit
-
-struct OpenArgs: Decodable {
- let url: String
- let with: String?
-}
-
-class OpenerPlugin: Plugin {
- @objc public func open(_ invoke: Invoke) throws {
- do {
- let args = try invoke.parseArgs(OpenArgs.self)
- if let url = URL(string: args.url) {
- if args.with == "inAppBrowser" {
- DispatchQueue.main.async {
- let safariVC = SFSafariViewController(url: url)
- self.manager.viewController?.present(safariVC, animated: true)
- }
- } else {
- if #available(iOS 10, *) {
- UIApplication.shared.open(url, options: [:])
- } else {
- UIApplication.shared.openURL(url)
- }
- }
-
- }
- invoke.resolve()
- } catch {
- invoke.reject(error.localizedDescription)
- }
- }
-}
-
-@_cdecl("init_plugin_opener")
-func initPlugin() -> Plugin {
- return OpenerPlugin()
-}
diff --git a/vendor/tauri-plugin-opener/permissions/allow-default-urls.toml b/vendor/tauri-plugin-opener/permissions/allow-default-urls.toml
deleted file mode 100644
index 93495b8970e..00000000000
--- a/vendor/tauri-plugin-opener/permissions/allow-default-urls.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-"$schema" = "schemas/schema.json"
-
-[[permission]]
-identifier = "allow-default-urls"
-description = "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application."
-
-[[permission.scope.allow]]
-url = "mailto:*"
-
-[[permission.scope.allow]]
-url = "tel:*"
-
-[[permission.scope.allow]]
-url = "http://*"
-
-[[permission.scope.allow]]
-url = "https://*"
diff --git a/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_path.toml b/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_path.toml
deleted file mode 100644
index ae67b939428..00000000000
--- a/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_path.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-# Automatically generated - DO NOT EDIT!
-
-"$schema" = "../../schemas/schema.json"
-
-[[permission]]
-identifier = "allow-open-path"
-description = "Enables the open_path command without any pre-configured scope."
-commands.allow = ["open_path"]
-
-[[permission]]
-identifier = "deny-open-path"
-description = "Denies the open_path command without any pre-configured scope."
-commands.deny = ["open_path"]
diff --git a/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_url.toml b/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_url.toml
deleted file mode 100644
index f1e694b1b0e..00000000000
--- a/vendor/tauri-plugin-opener/permissions/autogenerated/commands/open_url.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-# Automatically generated - DO NOT EDIT!
-
-"$schema" = "../../schemas/schema.json"
-
-[[permission]]
-identifier = "allow-open-url"
-description = "Enables the open_url command without any pre-configured scope."
-commands.allow = ["open_url"]
-
-[[permission]]
-identifier = "deny-open-url"
-description = "Denies the open_url command without any pre-configured scope."
-commands.deny = ["open_url"]
diff --git a/vendor/tauri-plugin-opener/permissions/autogenerated/commands/reveal_item_in_dir.toml b/vendor/tauri-plugin-opener/permissions/autogenerated/commands/reveal_item_in_dir.toml
deleted file mode 100644
index e669620ffd8..00000000000
--- a/vendor/tauri-plugin-opener/permissions/autogenerated/commands/reveal_item_in_dir.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-# Automatically generated - DO NOT EDIT!
-
-"$schema" = "../../schemas/schema.json"
-
-[[permission]]
-identifier = "allow-reveal-item-in-dir"
-description = "Enables the reveal_item_in_dir command without any pre-configured scope."
-commands.allow = ["reveal_item_in_dir"]
-
-[[permission]]
-identifier = "deny-reveal-item-in-dir"
-description = "Denies the reveal_item_in_dir command without any pre-configured scope."
-commands.deny = ["reveal_item_in_dir"]
diff --git a/vendor/tauri-plugin-opener/permissions/autogenerated/reference.md b/vendor/tauri-plugin-opener/permissions/autogenerated/reference.md
deleted file mode 100644
index 6ad6ba1fbf6..00000000000
--- a/vendor/tauri-plugin-opener/permissions/autogenerated/reference.md
+++ /dev/null
@@ -1,111 +0,0 @@
-## Default Permission
-
-This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application
-as well as reveal file in directories using default file explorer
-
-#### This default permission set includes the following:
-
-- `allow-open-url`
-- `allow-reveal-item-in-dir`
-- `allow-default-urls`
-
-## Permission Table
-
-
-
-| Identifier |
-Description |
-
-
-
-
-|
-
-`opener:allow-default-urls`
-
- |
-
-
-This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.
-
- |
-
-
-
-|
-
-`opener:allow-open-path`
-
- |
-
-
-Enables the open_path command without any pre-configured scope.
-
- |
-
-
-
-|
-
-`opener:deny-open-path`
-
- |
-
-
-Denies the open_path command without any pre-configured scope.
-
- |
-
-
-
-|
-
-`opener:allow-open-url`
-
- |
-
-
-Enables the open_url command without any pre-configured scope.
-
- |
-
-
-
-|
-
-`opener:deny-open-url`
-
- |
-
-
-Denies the open_url command without any pre-configured scope.
-
- |
-
-
-
-|
-
-`opener:allow-reveal-item-in-dir`
-
- |
-
-
-Enables the reveal_item_in_dir command without any pre-configured scope.
-
- |
-
-
-
-|
-
-`opener:deny-reveal-item-in-dir`
-
- |
-
-
-Denies the reveal_item_in_dir command without any pre-configured scope.
-
- |
-
-
diff --git a/vendor/tauri-plugin-opener/permissions/default.toml b/vendor/tauri-plugin-opener/permissions/default.toml
deleted file mode 100644
index 846d6e51be0..00000000000
--- a/vendor/tauri-plugin-opener/permissions/default.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-"$schema" = "schemas/schema.json"
-
-[default]
-description = """This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application
-as well as reveal file in directories using default file explorer"""
-permissions = [
- "allow-open-url",
- "allow-reveal-item-in-dir",
- "allow-default-urls",
-]
diff --git a/vendor/tauri-plugin-opener/src/commands.rs b/vendor/tauri-plugin-opener/src/commands.rs
deleted file mode 100644
index 343aa441a29..00000000000
--- a/vendor/tauri-plugin-opener/src/commands.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use std::path::{Path, PathBuf};
-
-use tauri::{
- ipc::{CommandScope, GlobalScope},
- AppHandle, Runtime,
-};
-
-use crate::{scope::Scope, Error, OpenerExt};
-
-#[tauri::command]
-pub async fn open_url(
- app: AppHandle,
- command_scope: CommandScope,
- global_scope: GlobalScope,
- url: String,
- with: Option,
-) -> crate::Result<()> {
- let scope = Scope::new(
- &app,
- command_scope
- .allows()
- .iter()
- .chain(global_scope.allows())
- .collect(),
- command_scope
- .denies()
- .iter()
- .chain(global_scope.denies())
- .collect(),
- );
-
- if scope.is_url_allowed(&url, with.as_deref()) {
- app.opener().open_url(url, with)
- } else {
- Err(Error::ForbiddenUrl { url, with })
- }
-}
-
-#[tauri::command]
-pub async fn open_path(
- app: AppHandle,
- command_scope: CommandScope,
- global_scope: GlobalScope,
- path: String,
- with: Option,
-) -> crate::Result<()> {
- let scope = Scope::new(
- &app,
- command_scope
- .allows()
- .iter()
- .chain(global_scope.allows())
- .collect(),
- command_scope
- .denies()
- .iter()
- .chain(global_scope.denies())
- .collect(),
- );
-
- if scope.is_path_allowed(Path::new(&path), with.as_deref())? {
- app.opener().open_path(path, with)
- } else {
- Err(Error::ForbiddenPath { path, with })
- }
-}
-
-/// TODO: in the next major version, rename to `reveal_items_in_dir`
-#[tauri::command]
-pub async fn reveal_item_in_dir(paths: Vec) -> crate::Result<()> {
- reveal_item_in_dir_with(paths, crate::reveal_items_in_dir).await
-}
-
-async fn reveal_item_in_dir_with(
- paths: Vec,
- reveal: impl FnOnce(Vec) -> crate::Result<()> + Send + 'static,
-) -> crate::Result<()> {
- // The Linux backend uses blocking zbus, which may enter its own Tokio runtime.
- tauri::async_runtime::spawn_blocking(move || reveal(paths)).await?
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- async fn assert_reveal_can_enter_runtime() {
- let paths = vec![
- PathBuf::from("recording with spaces.cap/"),
- PathBuf::from("截图.png"),
- ];
- let expected = paths.clone();
- reveal_item_in_dir_with(paths, move |actual| {
- assert_eq!(actual, expected);
- let runtime = tokio::runtime::Builder::new_current_thread()
- .build()
- .unwrap();
- runtime.block_on(async { Ok(()) })
- })
- .await
- .unwrap();
- }
-
- #[tokio::test]
- async fn reveal_from_current_thread_runtime() {
- assert_reveal_can_enter_runtime().await;
- }
-
- #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
- async fn reveal_from_multi_thread_runtime() {
- assert_reveal_can_enter_runtime().await;
- }
-
- #[tokio::test]
- async fn preserves_backend_errors() {
- let error = reveal_item_in_dir_with(Vec::new(), |_| {
- Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "reveal denied").into())
- })
- .await
- .unwrap_err();
- assert!(matches!(&error, Error::Io(e) if e.kind() == std::io::ErrorKind::PermissionDenied));
- assert_eq!(error.to_string(), "reveal denied");
- }
-
- #[tokio::test]
- async fn returns_worker_panics_as_errors() {
- let error = reveal_item_in_dir_with(Vec::new(), |_| panic!("reveal worker failed"))
- .await
- .unwrap_err();
- assert!(matches!(error, Error::Tauri(tauri::Error::JoinError(_))));
- }
-
- #[tokio::test]
- async fn validates_every_path_before_revealing() {
- let directory = tempfile::tempdir().unwrap();
- let existing = directory.path().join("recording with spaces.cap");
- std::fs::write(&existing, []).unwrap();
- let missing = directory.path().join("missing.png");
- let error = reveal_item_in_dir(vec![existing, missing])
- .await
- .unwrap_err();
- assert!(matches!(error, Error::Io(e) if e.kind() == std::io::ErrorKind::NotFound));
- }
-
- #[cfg(target_os = "linux")]
- #[tokio::test]
- async fn linux_zbus_backend_from_tokio_runtime() {
- let result = reveal_item_in_dir(Vec::new()).await;
- // A headless host may lack a session bus or file manager; neither is a worker panic.
- assert!(!matches!(
- result,
- Err(Error::Tauri(tauri::Error::JoinError(_)))
- ));
- }
-}
diff --git a/vendor/tauri-plugin-opener/src/config.rs b/vendor/tauri-plugin-opener/src/config.rs
deleted file mode 100644
index db3bae45e51..00000000000
--- a/vendor/tauri-plugin-opener/src/config.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use serde::Deserialize;
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase", deny_unknown_fields)]
-pub struct Config {
- /// Whether or not paths that contain components that start with a `.`
- /// will require that `.` appears literally in the pattern; `*`, `?`, `**`,
- /// or `[...]` will not match. This is useful because such files are
- /// conventionally considered hidden on Unix systems and it might be
- /// desirable to skip them when listing files.
- ///
- /// Defaults to `true` on Unix systems and `false` on Windows
- // dotfiles are not supposed to be exposed by default on unix
- pub require_literal_leading_dot: Option,
-}
diff --git a/vendor/tauri-plugin-opener/src/error.rs b/vendor/tauri-plugin-opener/src/error.rs
deleted file mode 100644
index c5a4dde3cce..00000000000
--- a/vendor/tauri-plugin-opener/src/error.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use std::path::PathBuf;
-
-use serde::{Serialize, Serializer};
-
-#[derive(Debug, thiserror::Error)]
-#[non_exhaustive]
-pub enum Error {
- #[cfg(mobile)]
- #[error(transparent)]
- PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
- #[error(transparent)]
- Tauri(#[from] tauri::Error),
- #[error(transparent)]
- Io(#[from] std::io::Error),
- #[error(transparent)]
- Json(#[from] serde_json::Error),
- #[error("unknown program {0}")]
- UnknownProgramName(String),
- #[error("Not allowed to open path {}{}", .path, .with.as_ref().map(|w| format!(" with {w}")).unwrap_or_default())]
- ForbiddenPath { path: String, with: Option },
- #[error("Not allowed to open url {}{}", .url, .with.as_ref().map(|w| format!(" with {w}")).unwrap_or_default())]
- ForbiddenUrl { url: String, with: Option },
- #[error("API not supported on the current platform")]
- UnsupportedPlatform,
- #[error(transparent)]
- #[cfg(windows)]
- Win32Error(#[from] windows::core::Error),
- #[error("Path doesn't have a parent: {0}")]
- NoParent(PathBuf),
- #[cfg(windows)]
- #[error("Failed to convert path '{0}' to ITEMIDLIST")]
- FailedToConvertPathToItemIdList(PathBuf),
- #[error("Failed to convert path to file:// url")]
- FailedToConvertPathToFileUrl,
- #[error(transparent)]
- #[cfg(any(
- target_os = "linux",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd"
- ))]
- Zbus(#[from] zbus::Error),
-}
-
-impl Serialize for Error {
- fn serialize(&self, serializer: S) -> std::result::Result
- where
- S: Serializer,
- {
- serializer.serialize_str(self.to_string().as_ref())
- }
-}
diff --git a/vendor/tauri-plugin-opener/src/init-iife.js b/vendor/tauri-plugin-opener/src/init-iife.js
deleted file mode 100644
index 26b3118ec47..00000000000
--- a/vendor/tauri-plugin-opener/src/init-iife.js
+++ /dev/null
@@ -1,19 +0,0 @@
-!(function () {
- "use strict";
- "function" == typeof SuppressedError && SuppressedError,
- window.addEventListener("click", function (e) {
- if (e.defaultPrevented || 0 !== e.button || e.metaKey || e.altKey) return;
- const t = e
- .composedPath()
- .find((e) => e instanceof Node && "A" === e.nodeName.toUpperCase());
- if (!t || !t.href || ("_blank" !== t.target && !e.ctrlKey && !e.shiftKey))
- return;
- const n = new URL(t.href);
- n.origin === window.location.origin ||
- ["http:", "https:", "mailto:", "tel:"].every((e) => n.protocol !== e) ||
- (e.preventDefault(),
- (async function (e, t = {}, n) {
- window.__TAURI_INTERNALS__.invoke(e, t, n);
- })("plugin:opener|open_url", { url: n }));
- });
-})();
diff --git a/vendor/tauri-plugin-opener/src/lib.rs b/vendor/tauri-plugin-opener/src/lib.rs
deleted file mode 100644
index 343e915580b..00000000000
--- a/vendor/tauri-plugin-opener/src/lib.rs
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use std::path::Path;
-
-use tauri::{plugin::TauriPlugin, Manager, Runtime};
-
-#[cfg(mobile)]
-use tauri::plugin::PluginHandle;
-#[cfg(target_os = "android")]
-const PLUGIN_IDENTIFIER: &str = "app.tauri.opener";
-#[cfg(target_os = "ios")]
-tauri::ios_plugin_binding!(init_plugin_opener);
-
-mod commands;
-mod config;
-mod error;
-mod open;
-mod reveal_item_in_dir;
-mod scope;
-mod scope_entry;
-
-pub use error::Error;
-type Result = std::result::Result;
-
-pub use open::{open_path, open_url};
-pub use reveal_item_in_dir::{reveal_item_in_dir, reveal_items_in_dir};
-
-pub struct Opener {
- // we use `fn() -> R` to silence the unused generic error
- // while keeping this struct `Send + Sync` without requiring `R` to be
- #[cfg(not(mobile))]
- _marker: std::marker::PhantomData R>,
- #[cfg(mobile)]
- mobile_plugin_handle: PluginHandle,
- require_literal_leading_dot: Option,
-}
-
-impl Opener {
- /// Open a url with a default or specific program.
- ///
- /// # Examples
- ///
- /// ```rust,no_run
- /// use tauri_plugin_opener::OpenerExt;
- ///
- /// tauri::Builder::default()
- /// .setup(|app| {
- /// // open the given URL on the system default browser
- /// app.opener().open_url("https://github.com/tauri-apps/tauri", None::<&str>)?;
- /// Ok(())
- /// });
- /// ```
- ///
- /// ## Platform-specific:
- ///
- /// - **Android / iOS**: Always opens using default program, unless `with` is provided as "inAppBrowser".
- #[cfg(desktop)]
- pub fn open_url(&self, url: impl Into, with: Option>) -> Result<()> {
- crate::open::open(url.into(), with.map(Into::into))
- }
-
- /// Open a url with a default or specific program.
- ///
- /// # Examples
- ///
- /// ```rust,no_run
- /// use tauri_plugin_opener::OpenerExt;
- ///
- /// tauri::Builder::default()
- /// .setup(|app| {
- /// // open the given URL on the system default browser
- /// app.opener().open_url("https://github.com/tauri-apps/tauri", None::<&str>)?;
- /// Ok(())
- /// });
- /// ```
- ///
- /// ## Platform-specific:
- ///
- /// - **Android / iOS**: Always opens using default program, unless `with` is provided as "inAppBrowser".
- #[cfg(mobile)]
- pub fn open_url(&self, url: impl Into, with: Option>) -> Result<()> {
- self.mobile_plugin_handle
- .run_mobile_plugin(
- "open",
- serde_json::json!({ "url": url.into(), "with": with.map(Into::into) }),
- )
- .map_err(Into::into)
- }
-
- /// Open a path with a default or specific program.
- ///
- /// # Examples
- ///
- /// ```rust,no_run
- /// use tauri_plugin_opener::OpenerExt;
- ///
- /// tauri::Builder::default()
- /// .setup(|app| {
- /// // open the given path on the system default explorer
- /// app.opener().open_path("/path/to/file", None::<&str>)?;
- /// Ok(())
- /// });
- /// ```
- ///
- /// ## Platform-specific:
- ///
- /// - **Android / iOS**: Always opens using default program.
- #[cfg(desktop)]
- pub fn open_path(
- &self,
- path: impl Into,
- with: Option>,
- ) -> Result<()> {
- crate::open::open(path.into(), with.map(Into::into))
- }
-
- /// Open a path with a default or specific program.
- ///
- /// # Examples
- ///
- /// ```rust,no_run
- /// use tauri_plugin_opener::OpenerExt;
- ///
- /// tauri::Builder::default()
- /// .setup(|app| {
- /// // open the given path on the system default explorer
- /// app.opener().open_path("/path/to/file", None::<&str>)?;
- /// Ok(())
- /// });
- /// ```
- ///
- /// ## Platform-specific:
- ///
- /// - **Android / iOS**: Always opens using default program.
- #[cfg(mobile)]
- pub fn open_path(
- &self,
- path: impl Into,
- _with: Option>,
- ) -> Result<()> {
- self.mobile_plugin_handle
- .run_mobile_plugin("open", path.into())
- .map_err(Into::into)
- }
-
- pub fn reveal_item_in_dir>(&self, p: P) -> Result<()> {
- reveal_item_in_dir(p)
- }
-
- pub fn reveal_items_in_dir(&self, paths: I) -> Result<()>
- where
- I: IntoIterator- ,
- P: AsRef,
- {
- reveal_items_in_dir(paths)
- }
-}
-
-/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the opener APIs.
-pub trait OpenerExt {
- fn opener(&self) -> &Opener;
-}
-
-impl> OpenerExt for T {
- fn opener(&self) -> &Opener {
- self.state::>().inner()
- }
-}
-
-/// The opener plugin Builder.
-pub struct Builder {
- open_js_links_on_click: bool,
-}
-
-impl Default for Builder {
- fn default() -> Self {
- Self {
- open_js_links_on_click: true,
- }
- }
-}
-
-impl Builder {
- /// Create a new opener plugin Builder.
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Whether the plugin should inject a JS script to open URLs in default browser
- /// when clicking on `` elements that has `_blank` target, or when pressing `Ctrl` or `Shift` while clicking it.
- ///
- /// Enabled by default for `http:`, `https:`, `mailto:`, `tel:` links.
- pub fn open_js_links_on_click(mut self, open: bool) -> Self {
- self.open_js_links_on_click = open;
- self
- }
-
- /// Build and Initializes the plugin.
- pub fn build(self) -> TauriPlugin> {
- let mut builder = tauri::plugin::Builder::>::new("opener")
- .setup(|app, api| {
- #[cfg(target_os = "android")]
- let handle = api.register_android_plugin(PLUGIN_IDENTIFIER, "OpenerPlugin")?;
- #[cfg(target_os = "ios")]
- let handle = api.register_ios_plugin(init_plugin_opener)?;
-
- app.manage(Opener {
- #[cfg(not(mobile))]
- _marker: std::marker::PhantomData:: R>,
- #[cfg(mobile)]
- mobile_plugin_handle: handle,
- require_literal_leading_dot: api
- .config()
- .as_ref()
- .and_then(|c| c.require_literal_leading_dot),
- });
- Ok(())
- })
- .invoke_handler(tauri::generate_handler![
- commands::open_url,
- commands::open_path,
- commands::reveal_item_in_dir,
- ]);
-
- if self.open_js_links_on_click {
- builder = builder.js_init_script(include_str!("init-iife.js").to_string());
- }
-
- builder.build()
- }
-}
-
-/// Initializes the plugin.
-pub fn init() -> TauriPlugin> {
- Builder::default().build()
-}
diff --git a/vendor/tauri-plugin-opener/src/open.rs b/vendor/tauri-plugin-opener/src/open.rs
deleted file mode 100644
index a3d46c508d4..00000000000
--- a/vendor/tauri-plugin-opener/src/open.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-//! Types and functions related to shell.
-
-use std::{ffi::OsStr, path::Path};
-
-pub(crate) fn open, S: AsRef>(path: P, with: Option
) -> crate::Result<()> {
- match with {
- Some(program) => ::open::with_detached(path, program.as_ref()),
- None => ::open::that_detached(path),
- }
- .map_err(Into::into)
-}
-
-/// Opens URL with the program specified in `with`, or system default if `None`.
-///
-/// ## Platform-specific:
-///
-/// - **Android / iOS**: Always opens using default program.
-///
-/// # Examples
-///
-/// ```rust,no_run
-/// tauri::Builder::default()
-/// .setup(|app| {
-/// // open the given URL on the system default browser
-/// tauri_plugin_opener::open_url("https://github.com/tauri-apps/tauri", None::<&str>)?;
-/// Ok(())
-/// });
-/// ```
-pub fn open_url, S: AsRef>(url: P, with: Option) -> crate::Result<()> {
- let url = url.as_ref();
- open(url, with)
-}
-
-/// Opens path with the program specified in `with`, or system default if `None`.
-///
-/// ## Platform-specific:
-///
-/// - **Android / iOS**: Always opens using default program.
-///
-/// # Examples
-///
-/// ```rust,no_run
-/// tauri::Builder::default()
-/// .setup(|app| {
-/// // open the given URL on the system default explorer
-/// tauri_plugin_opener::open_path("/path/to/file", None::<&str>)?;
-/// Ok(())
-/// });
-/// ```
-pub fn open_path, S: AsRef>(path: P, with: Option) -> crate::Result<()> {
- let path = path.as_ref();
- if with.is_none() {
- // Returns an IO error if not exists, and besides `exists()` is a shorthand for `metadata()`
- _ = path.metadata()?;
- }
- open(path, with)
-}
diff --git a/vendor/tauri-plugin-opener/src/reveal_item_in_dir.rs b/vendor/tauri-plugin-opener/src/reveal_item_in_dir.rs
deleted file mode 100644
index 6112fb8b19b..00000000000
--- a/vendor/tauri-plugin-opener/src/reveal_item_in_dir.rs
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use std::path::Path;
-
-/// Reveal a path the system's default explorer.
-///
-/// ## Platform-specific:
-///
-/// - **Android / iOS:** Unsupported.
-pub fn reveal_item_in_dir>(path: P) -> crate::Result<()> {
- let path = dunce::canonicalize(path.as_ref())?;
-
- #[cfg(any(
- windows,
- target_os = "macos",
- target_os = "linux",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd"
- ))]
- return imp::reveal_items_in_dir(&[path]);
-
- #[cfg(not(any(
- windows,
- target_os = "macos",
- target_os = "linux",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd"
- )))]
- Err(crate::Error::UnsupportedPlatform)
-}
-
-/// Reveal the paths the system's default explorer.
-///
-/// ## Platform-specific:
-///
-/// - **Android / iOS:** Unsupported.
-pub fn reveal_items_in_dir(paths: I) -> crate::Result<()>
-where
- I: IntoIterator- ,
- P: AsRef,
-{
- let mut canonicalized = vec![];
-
- for path in paths {
- let path = dunce::canonicalize(path.as_ref())?;
- canonicalized.push(path);
- }
-
- #[cfg(any(
- windows,
- target_os = "macos",
- target_os = "linux",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd"
- ))]
- return imp::reveal_items_in_dir(&canonicalized);
-
- #[cfg(not(any(
- windows,
- target_os = "macos",
- target_os = "linux",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd"
- )))]
- Err(crate::Error::UnsupportedPlatform)
-}
-
-#[cfg(windows)]
-mod imp {
- use std::collections::HashMap;
- use std::path::{Path, PathBuf};
-
- use windows::Win32::UI::Shell::Common::ITEMIDLIST;
- use windows::{
- core::{w, HSTRING, PCWSTR},
- Win32::{
- Foundation::ERROR_FILE_NOT_FOUND,
- System::Com::CoInitialize,
- UI::{
- Shell::{
- ILCreateFromPathW, ILFree, SHOpenFolderAndSelectItems, ShellExecuteExW,
- SHELLEXECUTEINFOW,
- },
- WindowsAndMessaging::SW_SHOWNORMAL,
- },
- },
- };
-
- pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> {
- if paths.is_empty() {
- return Ok(());
- }
-
- let mut grouped_paths: HashMap<&Path, Vec<&Path>> = HashMap::new();
- for path in paths {
- let parent = path
- .parent()
- .ok_or_else(|| crate::Error::NoParent(path.to_path_buf()))?;
- grouped_paths.entry(parent).or_default().push(path);
- }
-
- let _ = unsafe { CoInitialize(None) };
-
- for (parent, to_reveals) in grouped_paths {
- let parent_item_id_list = OwnedItemIdList::new(parent)?;
- let to_reveals_item_id_list = to_reveals
- .iter()
- .map(|to_reveal| OwnedItemIdList::new(*to_reveal))
- .collect::>>()?;
- if let Err(e) = unsafe {
- SHOpenFolderAndSelectItems(
- parent_item_id_list.item,
- Some(
- &to_reveals_item_id_list
- .iter()
- .map(|item| item.item)
- .collect::>(),
- ),
- 0,
- )
- } {
- // from https://github.com/electron/electron/blob/10d967028af2e72382d16b7e2025d243b9e204ae/shell/common/platform_util_win.cc#L302
- // On some systems, the above call mysteriously fails with "file not
- // found" even though the file is there. In these cases, ShellExecute()
- // seems to work as a fallback (although it won't select the file).
- //
- // Note: we only handle the first file here if multiple of are present
- if e.code().0 == ERROR_FILE_NOT_FOUND.0 as i32 {
- let first_path = to_reveals[0];
- let is_dir = first_path.is_dir();
- let mut info = SHELLEXECUTEINFOW {
- cbSize: std::mem::size_of::() as _,
- nShow: SW_SHOWNORMAL.0,
- lpFile: PCWSTR(parent_item_id_list.hstring.as_ptr()),
- lpClass: if is_dir { w!("folder") } else { PCWSTR::null() },
- lpVerb: if is_dir {
- w!("explore")
- } else {
- PCWSTR::null()
- },
- ..Default::default()
- };
-
- unsafe { ShellExecuteExW(&mut info) }?;
- }
- }
- }
-
- Ok(())
- }
-
- struct OwnedItemIdList {
- hstring: HSTRING,
- item: *const ITEMIDLIST,
- }
-
- impl OwnedItemIdList {
- fn new(path: &Path) -> crate::Result {
- let path_hstring = HSTRING::from(path);
- let item_id_list = unsafe { ILCreateFromPathW(&path_hstring) };
- if item_id_list.is_null() {
- Err(crate::Error::FailedToConvertPathToItemIdList(
- path.to_owned(),
- ))
- } else {
- Ok(Self {
- hstring: path_hstring,
- item: item_id_list,
- })
- }
- }
- }
-
- impl Drop for OwnedItemIdList {
- fn drop(&mut self) {
- if !self.item.is_null() {
- unsafe { ILFree(Some(self.item)) };
- }
- }
- }
-}
-
-#[cfg(any(
- target_os = "linux",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd"
-))]
-mod imp {
- use super::*;
- use std::collections::HashMap;
- use std::path::PathBuf;
-
- pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> {
- let connection = zbus::blocking::Connection::session()?;
-
- reveal_with_filemanager1(paths, &connection).or_else(|e| {
- // Fallback to opening the directory of the first item if revealing multiple items fails.
- if let Some(first_path) = paths.first() {
- reveal_with_open_uri_portal(first_path, &connection)
- } else {
- Err(e)
- }
- })
- }
-
- fn reveal_with_filemanager1(
- paths: &[PathBuf],
- connection: &zbus::blocking::Connection,
- ) -> crate::Result<()> {
- let uris: Result, _> = paths
- .iter()
- .map(|path| {
- url::Url::from_file_path(path)
- .map_err(|_| crate::Error::FailedToConvertPathToFileUrl)
- })
- .collect();
- let uris = uris?;
- let uri_strs: Vec<&str> = uris.iter().map(|uri| uri.as_str()).collect();
-
- #[zbus::proxy(
- interface = "org.freedesktop.FileManager1",
- default_service = "org.freedesktop.FileManager1",
- default_path = "/org/freedesktop/FileManager1"
- )]
- trait FileManager1 {
- async fn ShowItems(&self, name: Vec<&str>, arg2: &str) -> crate::Result<()>;
- }
-
- let proxy = FileManager1ProxyBlocking::new(connection)?;
-
- proxy.ShowItems(uri_strs, "")
- }
-
- fn reveal_with_open_uri_portal(
- path: &Path,
- connection: &zbus::blocking::Connection,
- ) -> crate::Result<()> {
- let uri = url::Url::from_file_path(path)
- .map_err(|_| crate::Error::FailedToConvertPathToFileUrl)?;
-
- #[zbus::proxy(
- interface = "org.freedesktop.portal.Desktop",
- default_service = "org.freedesktop.portal.OpenURI",
- default_path = "/org/freedesktop/portal/desktop"
- )]
- trait PortalDesktop {
- async fn OpenDirectory(
- &self,
- arg1: &str,
- name: &str,
- arg3: HashMap<&str, &str>,
- ) -> crate::Result<()>;
- }
-
- let proxy = PortalDesktopProxyBlocking::new(connection)?;
-
- proxy.OpenDirectory("", uri.as_str(), HashMap::new())
- }
-}
-
-#[cfg(target_os = "macos")]
-mod imp {
- use objc2_app_kit::NSWorkspace;
- use objc2_foundation::{NSArray, NSString, NSURL};
- use std::path::PathBuf;
-
- pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> {
- unsafe {
- let mut urls = Vec::new();
-
- for path in paths {
- let path = path.to_string_lossy();
- let path = NSString::from_str(&path);
- let url = NSURL::fileURLWithPath(&path);
-
- urls.push(url);
- }
-
- let urls = NSArray::from_retained_slice(&urls);
-
- let workspace = NSWorkspace::new();
- workspace.activateFileViewerSelectingURLs(&urls);
- }
-
- Ok(())
- }
-}
diff --git a/vendor/tauri-plugin-opener/src/scope.rs b/vendor/tauri-plugin-opener/src/scope.rs
deleted file mode 100644
index 275913d1ba5..00000000000
--- a/vendor/tauri-plugin-opener/src/scope.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use std::{
- marker::PhantomData,
- path::{Path, PathBuf},
- sync::Arc,
-};
-
-use tauri::{ipc::ScopeObject, utils::acl::Value, AppHandle, Manager, Runtime};
-
-use crate::{scope_entry::EntryRaw, Error};
-
-pub use crate::scope_entry::Application;
-
-#[derive(Debug)]
-pub enum Entry {
- Url {
- url: glob::Pattern,
- app: Application,
- },
- Path {
- path: Option,
- app: Application,
- },
-}
-
-impl ScopeObject for Entry {
- type Error = Error;
-
- fn deserialize(
- app_handle: &AppHandle,
- raw: Value,
- ) -> std::result::Result {
- serde_json::from_value(raw.into())
- .and_then(|raw| {
- let entry = match raw {
- EntryRaw::Url { url, app } => Entry::Url {
- url: glob::Pattern::new(&url)
- .map_err(|e| serde::de::Error::custom(e.to_string()))?,
- app,
- },
- EntryRaw::Path { path, app } => {
- let path = match app_handle.path().parse(path) {
- Ok(path) => Some(path),
- #[cfg(not(target_os = "android"))]
- Err(tauri::Error::UnknownPath) => None,
- Err(err) => return Err(serde::de::Error::custom(err.to_string())),
- };
-
- Entry::Path { path, app }
- }
- };
-
- Ok(entry)
- })
- .map_err(Into::into)
- }
-}
-
-impl Application {
- fn matches(&self, a: Option<&str>) -> bool {
- match self {
- Self::Default => a.is_none(),
- Self::Enable(enable) => *enable,
- Self::App(program) => Some(program.as_str()) == a,
- }
- }
-}
-
-impl Entry {
- fn path(&self) -> Option {
- match self {
- Self::Url { .. } => None,
- Self::Path { path, .. } => path.clone(),
- }
- }
-
- fn matches_url(&self, u: &str, a: Option<&str>) -> bool {
- match self {
- Self::Url { url, app } => url.matches(u) && app.matches(a),
- Self::Path { .. } => false,
- }
- }
-
- fn matches_path_program(&self, a: Option<&str>) -> bool {
- match self {
- Self::Url { .. } => false,
- Self::Path { app, .. } => app.matches(a),
- }
- }
-}
-
-#[derive(Debug)]
-pub struct Scope<'a, R: Runtime, M: Manager> {
- allowed: Vec<&'a Arc>,
- denied: Vec<&'a Arc>,
- manager: &'a M,
- _marker: PhantomData,
-}
-
-impl<'a, R: Runtime, M: Manager> Scope<'a, R, M> {
- pub(crate) fn new(
- manager: &'a M,
- allowed: Vec<&'a Arc>,
- denied: Vec<&'a Arc>,
- ) -> Self {
- Self {
- manager,
- allowed,
- denied,
- _marker: PhantomData,
- }
- }
-
- pub fn is_url_allowed(&self, url: &str, with: Option<&str>) -> bool {
- let denied = self.denied.iter().any(|e| e.matches_url(url, with));
- if denied {
- false
- } else {
- self.allowed.iter().any(|e| e.matches_url(url, with))
- }
- }
-
- pub fn is_path_allowed(&self, path: &Path, with: Option<&str>) -> crate::Result {
- let fs_scope = tauri::fs::Scope::new(
- self.manager,
- &tauri::utils::config::FsScope::Scope {
- allow: self.allowed.iter().filter_map(|e| e.path()).collect(),
- deny: self.denied.iter().filter_map(|e| e.path()).collect(),
- require_literal_leading_dot: self
- .manager
- .state::>()
- .require_literal_leading_dot,
- },
- )?;
-
- Ok(fs_scope.is_allowed(path) && self.allowed.iter().any(|e| e.matches_path_program(with)))
- }
-}
diff --git a/vendor/tauri-plugin-opener/src/scope_entry.rs b/vendor/tauri-plugin-opener/src/scope_entry.rs
deleted file mode 100644
index cf9004a2b50..00000000000
--- a/vendor/tauri-plugin-opener/src/scope_entry.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use std::path::PathBuf;
-
-use serde::Deserialize;
-
-#[derive(Deserialize, Debug)]
-#[serde(untagged)]
-pub enum Application {
- Default,
- Enable(bool),
- App(String),
-}
-
-impl Default for Application {
- fn default() -> Self {
- Self::Default
- }
-}
-
-#[derive(Deserialize)]
-#[serde(untagged, rename_all = "camelCase")]
-pub(crate) enum EntryRaw {
- Url {
- url: String,
- #[serde(default)]
- app: Application,
- },
- Path {
- path: PathBuf,
- #[serde(default)]
- app: Application,
- },
-}
From e06f285fde15729a7bcc86aaad9f5b18543aea70 Mon Sep 17 00:00:00 2001
From: Richie McIlroy <33632126+richiemcilroy@users.noreply.github.com>
Date: Mon, 5 Oct 2026 11:12:49 +0800
Subject: [PATCH 4/4] revert(desktop): drop the vendored opener plugin
---
.github/workflows/ci.yml | 6 ++----
Cargo.lock | 4 ++--
Cargo.toml | 2 --
3 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0f432e406f4..dd28b8a808e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -51,7 +51,6 @@ jobs:
- 'scripts/run-gpui-build*'
- 'scripts/sync-desktop-versions.mjs'
- 'scripts/verify-gpui-release-inputs.mjs'
- - 'vendor/tauri-plugin-opener/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'bun.lock'
@@ -64,7 +63,6 @@ jobs:
- 'apps/desktop-gpui/**'
- 'crates/**'
- 'apps/desktop/src-tauri/**'
- - 'vendor/tauri-plugin-opener/**'
- 'Cargo.toml'
- 'Cargo.lock'
tauri-plugins:
@@ -225,7 +223,7 @@ jobs:
- name: Test desktop installation and storage helpers
shell: bash
- run: cargo test --locked -p cap-utils -p cap-cli-install -p tauri-plugin-opener --lib
+ run: cargo test --locked -p cap-utils -p cap-cli-install --lib
- name: Build desktop binaries
shell: bash
@@ -321,7 +319,7 @@ jobs:
run: |
node --test scripts/linux-bundle-config.test.mjs scripts/finalize-linux-appimage.test.mjs
bun run --cwd apps/desktop vitest run scripts/prepare.test.js
- cargo test --locked -p cap-utils -p cap-cli-install -p tauri-plugin-opener --lib
+ cargo test --locked -p cap-utils -p cap-cli-install --lib
cargo test --locked -p cap --bin cap record::tests
cargo test --locked -p cap-recording --lib -- --test-threads=1
cargo test --locked -p cap-enc-ffmpeg
diff --git a/Cargo.lock b/Cargo.lock
index 90c88f49856..ffb628ea4f4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10802,6 +10802,8 @@ dependencies = [
[[package]]
name = "tauri-plugin-opener"
version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5"
dependencies = [
"dunce",
"glob",
@@ -10813,9 +10815,7 @@ dependencies = [
"serde_json",
"tauri",
"tauri-plugin",
- "tempfile",
"thiserror 2.0.16",
- "tokio",
"url",
"windows 0.61.3",
"zbus",
diff --git a/Cargo.toml b/Cargo.toml
index 2749ec83681..8a9be0ffb64 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,6 @@ members = [
"apps/desktop/src-tauri",
"crates/*",
"crates/workspace-hack",
- "vendor/tauri-plugin-opener",
]
[workspace.dependencies]
@@ -133,7 +132,6 @@ cidre = { git = "https://github.com/CapSoftware/cidre", rev = "bf84b67079a8" }
# wgpu = { git = "https://github.com/gfx-rs/wgpu", rev = "cd41a6e32a6239b65d1cecbeccde6a43a100914a" }
wgpu-hal = { path = "vendor/wgpu-hal" }
tao = { path = "vendor/tao" }
-tauri-plugin-opener = { path = "vendor/tauri-plugin-opener" }
# https://github.com/CapSoftware/reqwest/commit/9b5ecbd5210a9510fde766015cabb724c1e70d2e
reqwest = { git = "https://github.com/CapSoftware/reqwest", rev = "9b5ecbd5210a9510fde766015cabb724c1e70d2e" }