Note that the arguments are only parsed, not implemented.
diff --git a/examples/api/src/views/Haptics.svelte b/examples/api/src/views/Haptics.svelte
new file mode 100644
index 00000000..9ddf15c1
--- /dev/null
+++ b/examples/api/src/views/Haptics.svelte
@@ -0,0 +1,46 @@
+
+
+
+ vibrate(300).then(onMessage).catch(onMessage)}
+ >vibrate short
+ vibrate(1500).then(onMessage).catch(onMessage)}
+ >vibrate long
+ impactFeedback('medium').then(onMessage).catch(onMessage)}
+ >impact medium
+
+ notificationFeedback('warning').then(onMessage).catch(onMessage)}
+ >notification warning
+ selectionFeedback().then(onMessage).catch(onMessage)}
+ >selection
+
+
+
+
+
+ Depending on your device settings for haptic feedback some of the buttons may
+ not work.
+
diff --git a/examples/api/src/views/Http.svelte b/examples/api/src/views/Http.svelte
index e1848803..5c7c3c49 100644
--- a/examples/api/src/views/Http.svelte
+++ b/examples/api/src/views/Http.svelte
@@ -1,69 +1,69 @@
@@ -82,7 +82,7 @@
placeholder="Request body"
rows="5"
bind:value={httpBody}
- />
+ >
Make request
diff --git a/examples/api/src/views/Opener.svelte b/examples/api/src/views/Opener.svelte
new file mode 100644
index 00000000..eca634ac
--- /dev/null
+++ b/examples/api/src/views/Opener.svelte
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
diff --git a/examples/api/src/views/Scanner.svelte b/examples/api/src/views/Scanner.svelte
index 57ff904c..ea0609e3 100644
--- a/examples/api/src/views/Scanner.svelte
+++ b/examples/api/src/views/Scanner.svelte
@@ -1,38 +1,44 @@
@@ -59,11 +65,12 @@
Aim your camera at a QR code
-
Cancel
+
Cancel
@@ -111,7 +118,7 @@
transition: 0.3s;
}
.square:after {
- content: "";
+ content: '';
top: 0;
display: block;
padding-bottom: 100%;
@@ -141,7 +148,8 @@
width: 100%;
margin: 1rem;
border: 2px solid #fff;
- box-shadow: 0px 0px 2px 1px rgb(0 0 0 / 0.5),
+ box-shadow:
+ 0px 0px 2px 1px rgb(0 0 0 / 0.5),
inset 0px 0px 2px 1px rgb(0 0 0 / 0.5);
border-radius: 1rem;
}
diff --git a/examples/api/src/views/Store.svelte b/examples/api/src/views/Store.svelte
index d8e6653b..6248b009 100644
--- a/examples/api/src/views/Store.svelte
+++ b/examples/api/src/views/Store.svelte
@@ -1,5 +1,5 @@
@@ -44,7 +81,12 @@
-
diff --git a/examples/api/src/views/Updater.svelte b/examples/api/src/views/Updater.svelte
index 819c65e0..2fa5e436 100644
--- a/examples/api/src/views/Updater.svelte
+++ b/examples/api/src/views/Updater.svelte
@@ -1,56 +1,56 @@
@@ -61,7 +61,7 @@
{:else}
{/if}
diff --git a/package.json b/package.json
index c74b41d4..54215699 100644
--- a/package.json
+++ b/package.json
@@ -7,23 +7,24 @@
"build": "pnpm run -r --parallel --filter !plugins-workspace --filter !\"./plugins/*/examples/**\" --filter !\"./examples/*\" build",
"lint": "eslint .",
"format": "prettier --write .",
- "format:check": "prettier --check ."
+ "format:check": "prettier --check .",
+ "example:api:dev": "pnpm run --filter \"api\" tauri dev"
},
"devDependencies": {
- "@eslint/js": "9.12.0",
- "@rollup/plugin-node-resolve": "15.3.0",
+ "@eslint/js": "9.19.0",
+ "@rollup/plugin-node-resolve": "16.0.0",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "11.1.6",
"@types/eslint__js": "8.42.3",
"covector": "^0.12.3",
- "eslint": "9.12.0",
- "eslint-config-prettier": "9.1.0",
+ "eslint": "9.19.0",
+ "eslint-config-prettier": "10.0.1",
"eslint-plugin-security": "3.0.1",
- "prettier": "3.3.3",
- "rollup": "4.22.4",
- "tslib": "2.7.0",
- "typescript": "5.6.2",
- "typescript-eslint": "8.8.0"
+ "prettier": "3.4.2",
+ "rollup": "4.34.6",
+ "tslib": "2.8.1",
+ "typescript": "5.7.3",
+ "typescript-eslint": "8.23.0"
},
"resolutions": {
"semver": ">=7.5.2",
diff --git a/plugins/autostart/CHANGELOG.md b/plugins/autostart/CHANGELOG.md
index cd26fad7..85b3d5c1 100644
--- a/plugins/autostart/CHANGELOG.md
+++ b/plugins/autostart/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## \[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.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@@ -88,11 +92,3 @@
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- te to alpha.11.
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- ae67\`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
diff --git a/plugins/autostart/Cargo.toml b/plugins/autostart/Cargo.toml
index 77f555e6..d0a80ddf 100644
--- a/plugins/autostart/Cargo.toml
+++ b/plugins/autostart/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-autostart"
-version = "2.0.1"
+version = "2.2.0"
description = "Automatically launch your application at startup."
authors = { workspace = true }
license = { workspace = true }
@@ -27,6 +27,5 @@ tauri-plugin = { workspace = true, features = ["build"] }
serde = { workspace = true }
serde_json = { workspace = true }
tauri = { workspace = true }
-log = { workspace = true }
thiserror = { workspace = true }
auto-launch = "0.5"
diff --git a/plugins/autostart/README.md b/plugins/autostart/README.md
index 0a1415db..76d68f73 100644
--- a/plugins/autostart/README.md
+++ b/plugins/autostart/README.md
@@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-autostart#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
use tauri_plugin_autostart::MacosLauncher;
diff --git a/plugins/autostart/package.json b/plugins/autostart/package.json
index 9ea68630..876d9295 100644
--- a/plugins/autostart/package.json
+++ b/plugins/autostart/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-autostart",
- "version": "2.0.0",
+ "version": "2.2.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
diff --git a/plugins/autostart/src/lib.rs b/plugins/autostart/src/lib.rs
index bd3866b3..5550bfa1 100644
--- a/plugins/autostart/src/lib.rs
+++ b/plugins/autostart/src/lib.rs
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-//! [](https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/autostart)
-//!
//! Automatically launch your application at startup. Supports Windows, Mac (via AppleScript or Launch Agent), and Linux.
#![doc(
@@ -13,8 +11,6 @@
#![cfg(not(any(target_os = "android", target_os = "ios")))]
use auto_launch::{AutoLaunch, AutoLaunchBuilder};
-#[cfg(target_os = "macos")]
-use log::info;
use serde::{ser::Serializer, Serialize};
use tauri::{
command,
@@ -135,7 +131,6 @@ pub fn init
(
} else {
exe_path
};
- info!("auto_start path {}", &app_path);
builder.set_app_path(&app_path);
}
#[cfg(target_os = "linux")]
diff --git a/plugins/barcode-scanner/CHANGELOG.md b/plugins/barcode-scanner/CHANGELOG.md
index 447499bc..3fa878fd 100644
--- a/plugins/barcode-scanner/CHANGELOG.md
+++ b/plugins/barcode-scanner/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## \[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.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
diff --git a/plugins/barcode-scanner/Cargo.toml b/plugins/barcode-scanner/Cargo.toml
index 8fabe711..018b4908 100644
--- a/plugins/barcode-scanner/Cargo.toml
+++ b/plugins/barcode-scanner/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-barcode-scanner"
-version = "2.0.1"
+version = "2.2.0"
description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
edition = { workspace = true }
authors = { workspace = true }
diff --git a/plugins/barcode-scanner/README.md b/plugins/barcode-scanner/README.md
index eba7ca9e..4abbef0a 100644
--- a/plugins/barcode-scanner/README.md
+++ b/plugins/barcode-scanner/README.md
@@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
diff --git a/plugins/barcode-scanner/package.json b/plugins/barcode-scanner/package.json
index b90be3d7..9e8c8b56 100644
--- a/plugins/barcode-scanner/package.json
+++ b/plugins/barcode-scanner/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-barcode-scanner",
- "version": "2.0.0",
+ "version": "2.2.0",
"description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
"license": "MIT OR Apache-2.0",
"authors": [
diff --git a/plugins/biometric/CHANGELOG.md b/plugins/biometric/CHANGELOG.md
index 1eac8a95..4cd2731d 100644
--- a/plugins/biometric/CHANGELOG.md
+++ b/plugins/biometric/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## \[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.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
diff --git a/plugins/biometric/Cargo.toml b/plugins/biometric/Cargo.toml
index 816c5ef1..b96f55b3 100644
--- a/plugins/biometric/Cargo.toml
+++ b/plugins/biometric/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-biometric"
-version = "2.0.1"
+version = "2.2.0"
description = "Prompt the user for biometric authentication on Android and iOS."
edition = { workspace = true }
authors = { workspace = true }
diff --git a/plugins/biometric/README.md b/plugins/biometric/README.md
index 2028595d..c7844f7b 100644
--- a/plugins/biometric/README.md
+++ b/plugins/biometric/README.md
@@ -56,7 +56,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-biometric#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
diff --git a/plugins/biometric/package.json b/plugins/biometric/package.json
index 5d0cd5c0..fe689d45 100644
--- a/plugins/biometric/package.json
+++ b/plugins/biometric/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-biometric",
- "version": "2.0.0",
+ "version": "2.2.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
diff --git a/plugins/cli/CHANGELOG.md b/plugins/cli/CHANGELOG.md
index 879c20b6..c2c011e2 100644
--- a/plugins/cli/CHANGELOG.md
+++ b/plugins/cli/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## \[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.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@@ -89,10 +93,3 @@
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
te to alpha.11.
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- ae67\`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- om/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
diff --git a/plugins/cli/Cargo.toml b/plugins/cli/Cargo.toml
index b4db6991..500ba957 100644
--- a/plugins/cli/Cargo.toml
+++ b/plugins/cli/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-cli"
-version = "2.0.1"
+version = "2.2.0"
description = "Parse arguments from your Tauri application's command line interface."
edition = { workspace = true }
authors = { workspace = true }
diff --git a/plugins/cli/README.md b/plugins/cli/README.md
index 05d5bd35..91a8080b 100644
--- a/plugins/cli/README.md
+++ b/plugins/cli/README.md
@@ -55,7 +55,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-cli#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
diff --git a/plugins/cli/package.json b/plugins/cli/package.json
index 481082bb..e5ff8b73 100644
--- a/plugins/cli/package.json
+++ b/plugins/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-cli",
- "version": "2.0.0",
+ "version": "2.2.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
diff --git a/plugins/cli/src/lib.rs b/plugins/cli/src/lib.rs
index 38f64f83..3e144376 100644
--- a/plugins/cli/src/lib.rs
+++ b/plugins/cli/src/lib.rs
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-//! [](https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/cli)
-//!
//! Parse arguments from your Command Line Interface.
//!
//! - Supported platforms: Windows, Linux and macOS.
diff --git a/plugins/clipboard-manager/CHANGELOG.md b/plugins/clipboard-manager/CHANGELOG.md
index 68dee776..91cfc44d 100644
--- a/plugins/clipboard-manager/CHANGELOG.md
+++ b/plugins/clipboard-manager/CHANGELOG.md
@@ -1,5 +1,21 @@
# Changelog
+## \[2.2.1]
+
+- [`ce11079f`](https://github.com/tauri-apps/plugins-workspace/commit/ce11079f19852fbefdecf0e4c7d947af3624fee0) ([#2280](https://github.com/tauri-apps/plugins-workspace/pull/2280) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Explicitly drop `arboard::Clipboard` on exit. Add recommendation to not use read methods on the mainthread.
+
+## \[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.1]
+
+- [`3fa0fc09`](https://github.com/tauri-apps/plugins-workspace/commit/3fa0fc09bbee0d619801e5757af9fb3c09883c97) ([#2099](https://github.com/tauri-apps/plugins-workspace/pull/2099) by [@rasteiner](https://github.com/tauri-apps/plugins-workspace/../../rasteiner)) Fix clipboard manager client side api not copying fallback alternative text when calling `writeHtml`.
+
+## \[2.0.2]
+
+- [`d57df4de`](https://github.com/tauri-apps/plugins-workspace/commit/d57df4debe7c75cfbd6d6558fff1beb07dbee54c) ([#1986](https://github.com/tauri-apps/plugins-workspace/pull/1986) by [@RikaKagurasaka](https://github.com/tauri-apps/plugins-workspace/../../RikaKagurasaka)) Fix that `read_image` wrongly set the image rgba data with binary PNG data.
+
## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@@ -108,38 +124,3 @@
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- te to alpha.11.
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- \`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- te to alpha.11.
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- hub.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- \`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- te to alpha.11.
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- te to alpha.11.
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- !
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- ps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- !
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- om/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
diff --git a/plugins/clipboard-manager/Cargo.toml b/plugins/clipboard-manager/Cargo.toml
index 3c086574..5b486fcf 100644
--- a/plugins/clipboard-manager/Cargo.toml
+++ b/plugins/clipboard-manager/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-clipboard-manager"
-version = "2.0.1"
+version = "2.2.1"
description = "Read and write to the system clipboard."
edition = { workspace = true }
authors = { workspace = true }
@@ -37,4 +37,3 @@ tauri = { workspace = true, features = ["wry"] }
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
arboard = "3"
-image = "0.25"
diff --git a/plugins/clipboard-manager/README.md b/plugins/clipboard-manager/README.md
index daec970d..57b89526 100644
--- a/plugins/clipboard-manager/README.md
+++ b/plugins/clipboard-manager/README.md
@@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-clipboard-manager#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
@@ -72,7 +72,6 @@ import {
writeText,
readText,
writeHtml,
- readHtml,
clear
} from '@tauri-apps/plugin-clipboard-manager'
await writeText('Tauri is awesome!')
diff --git a/plugins/clipboard-manager/api-iife.js b/plugins/clipboard-manager/api-iife.js
index 7750e01d..fb245a4e 100644
--- a/plugins/clipboard-manager/api-iife.js
+++ b/plugins/clipboard-manager/api-iife.js
@@ -1 +1 @@
-if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var t;async function r(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;class n{get rid(){return function(e,t,r,n){if("a"===r&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}(this,t,"f")}constructor(e){t.set(this,void 0),function(e,t,r,n,a){if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");t.set(e,r)}(this,t,e)}async close(){return r("plugin:resources|close",{rid:this.rid})}}t=new WeakMap;class a extends n{constructor(e){super(e)}static async new(e,t,n){return r("plugin:image|new",{rgba:i(e),width:t,height:n}).then((e=>new a(e)))}static async fromBytes(e){return r("plugin:image|from_bytes",{bytes:i(e)}).then((e=>new a(e)))}static async fromPath(e){return r("plugin:image|from_path",{path:e}).then((e=>new a(e)))}async rgba(){return r("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return r("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await r("plugin:clipboard-manager|clear")},e.readImage=async function(){return await r("plugin:clipboard-manager|read_image").then((e=>new a(e)))},e.readText=async function(){return await r("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,t){await r("plugin:clipboard-manager|write_html",{html:e,altHtml:t})},e.writeImage=async function(e){await r("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,t){await r("plugin:clipboard-manager|write_text",{label:t?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
+if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var t;async function r(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;class n{get rid(){return function(e,t,r,n){if("a"===r&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}(this,t,"f")}constructor(e){t.set(this,void 0),function(e,t,r){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");t.set(e,r)}(this,t,e)}async close(){return r("plugin:resources|close",{rid:this.rid})}}t=new WeakMap;class a extends n{constructor(e){super(e)}static async new(e,t,n){return r("plugin:image|new",{rgba:i(e),width:t,height:n}).then((e=>new a(e)))}static async fromBytes(e){return r("plugin:image|from_bytes",{bytes:i(e)}).then((e=>new a(e)))}static async fromPath(e){return r("plugin:image|from_path",{path:e}).then((e=>new a(e)))}async rgba(){return r("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return r("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await r("plugin:clipboard-manager|clear")},e.readImage=async function(){return await r("plugin:clipboard-manager|read_image").then((e=>new a(e)))},e.readText=async function(){return await r("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,t){await r("plugin:clipboard-manager|write_html",{html:e,altText:t})},e.writeImage=async function(e){await r("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,t){await r("plugin:clipboard-manager|write_text",{label:t?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
diff --git a/plugins/clipboard-manager/guest-js/index.ts b/plugins/clipboard-manager/guest-js/index.ts
index f7f31842..19851fe0 100644
--- a/plugins/clipboard-manager/guest-js/index.ts
+++ b/plugins/clipboard-manager/guest-js/index.ts
@@ -65,6 +65,7 @@ async function readText(): Promise {
* 0, 255, 0, 255,
* ];
* await writeImage(buffer);
+ * ```
*
* @returns A promise indicating the success or failure of the operation.
*
@@ -90,7 +91,7 @@ async function writeImage(
* import { readImage } from '@tauri-apps/plugin-clipboard-manager';
*
* const clipboardImage = await readImage();
- * const blob = new Blob([clipboardImage.bytes], { type: 'image' })
+ * const blob = new Blob([await clipboardImage.rbga()], { type: 'image' })
* const url = URL.createObjectURL(blob)
* ```
* @since 2.0.0
@@ -110,9 +111,11 @@ async function readImage(): Promise {
*
* @example
* ```typescript
- * import { writeHtml, readHtml } from '@tauri-apps/plugin-clipboard-manager';
+ * import { writeHtml } from '@tauri-apps/plugin-clipboard-manager';
* await writeHtml('Tauri is awesome! ', 'plaintext');
- * await writeHtml('Tauri is awesome! ', 'Tauri is awesome '); // Will write "Tauri is awesome " as plain text
+ * // The following will write "Tauri is awesome " as plain text
+ * await writeHtml('Tauri is awesome! ', 'Tauri is awesome ');
+ * // we can read html data only as a string so there's just readText(), no readHtml()
* assert(await readText(), 'Tauri is awesome! ');
* ```
*
@@ -120,10 +123,10 @@ async function readImage(): Promise {
*
* @since 2.0.0
*/
-async function writeHtml(html: string, altHtml?: string): Promise {
+async function writeHtml(html: string, altText?: string): Promise {
await invoke('plugin:clipboard-manager|write_html', {
html,
- altHtml
+ altText
})
}
diff --git a/plugins/clipboard-manager/package.json b/plugins/clipboard-manager/package.json
index f4897994..ff1ba9e7 100644
--- a/plugins/clipboard-manager/package.json
+++ b/plugins/clipboard-manager/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-clipboard-manager",
- "version": "2.0.0",
+ "version": "2.2.1",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
diff --git a/plugins/clipboard-manager/src/desktop.rs b/plugins/clipboard-manager/src/desktop.rs
index bb8ba318..f3570cc0 100644
--- a/plugins/clipboard-manager/src/desktop.rs
+++ b/plugins/clipboard-manager/src/desktop.rs
@@ -3,7 +3,6 @@
// SPDX-License-Identifier: MIT
use arboard::ImageData;
-use image::ImageEncoder;
use serde::de::DeserializeOwned;
use tauri::{image::Image, plugin::PluginApi, AppHandle, Runtime};
@@ -15,7 +14,7 @@ pub fn init(
) -> crate::Result> {
Ok(Clipboard {
app: app.clone(),
- clipboard: arboard::Clipboard::new().map(Mutex::new),
+ clipboard: arboard::Clipboard::new().map(|c| Mutex::new(Some(c))),
})
}
@@ -23,13 +22,21 @@ pub fn init(
pub struct Clipboard {
#[allow(dead_code)]
app: AppHandle,
- clipboard: Result, arboard::Error>,
+ // According to arboard docs the clipboard must be dropped before exit.
+ // Since tauri doesn't call drop on exit we'll use an Option to take() on RunEvent::Exit.
+ clipboard: Result>, arboard::Error>,
}
impl Clipboard {
pub fn write_text<'a, T: Into>>(&self, text: T) -> crate::Result<()> {
match &self.clipboard {
- Ok(clipboard) => clipboard.lock().unwrap().set_text(text).map_err(Into::into),
+ Ok(clipboard) => clipboard
+ .lock()
+ .unwrap()
+ .as_mut()
+ .unwrap()
+ .set_text(text)
+ .map_err(Into::into),
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}
@@ -39,6 +46,8 @@ impl Clipboard {
Ok(clipboard) => clipboard
.lock()
.unwrap()
+ .as_mut()
+ .unwrap()
.set_image(ImageData {
bytes: Cow::Borrowed(image.rgba()),
width: image.width() as usize,
@@ -49,10 +58,11 @@ impl Clipboard {
}
}
+ /// Warning: This method should not be used on the main thread! Otherwise the underlying libraries may deadlock on Linux, freezing the whole app, when trying to copy data copied from this app, for example if the user copies text from the WebView.
pub fn read_text(&self) -> crate::Result {
match &self.clipboard {
Ok(clipboard) => {
- let text = clipboard.lock().unwrap().get_text()?;
+ let text = clipboard.lock().unwrap().as_mut().unwrap().get_text()?;
Ok(text)
}
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
@@ -68,6 +78,8 @@ impl Clipboard {
Ok(clipboard) => clipboard
.lock()
.unwrap()
+ .as_mut()
+ .unwrap()
.set_html(html, alt_text)
.map_err(Into::into),
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
@@ -76,28 +88,36 @@ impl Clipboard {
pub fn clear(&self) -> crate::Result<()> {
match &self.clipboard {
- Ok(clipboard) => clipboard.lock().unwrap().clear().map_err(Into::into),
+ Ok(clipboard) => clipboard
+ .lock()
+ .unwrap()
+ .as_mut()
+ .unwrap()
+ .clear()
+ .map_err(Into::into),
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}
+ /// Warning: This method should not be used on the main thread! Otherwise the underlying libraries may deadlock on Linux, freezing the whole app, when trying to copy data copied from this app, for example if the user copies text from the WebView.
pub fn read_image(&self) -> crate::Result> {
match &self.clipboard {
Ok(clipboard) => {
- let image = clipboard.lock().unwrap().get_image()?;
-
- let mut buffer: Vec = Vec::new();
- image::codecs::png::PngEncoder::new(&mut buffer).write_image(
- &image.bytes,
+ let image = clipboard.lock().unwrap().as_mut().unwrap().get_image()?;
+ let image = Image::new_owned(
+ image.bytes.to_vec(),
image.width as u32,
image.height as u32,
- image::ExtendedColorType::Rgba8,
- )?;
-
- let image = Image::new_owned(buffer, image.width as u32, image.height as u32);
+ );
Ok(image)
}
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}
+
+ pub(crate) fn cleanup(&self) {
+ if let Ok(clipboard) = &self.clipboard {
+ clipboard.lock().unwrap().take();
+ }
+ }
}
diff --git a/plugins/clipboard-manager/src/error.rs b/plugins/clipboard-manager/src/error.rs
index 7e36a11b..1b8cf482 100644
--- a/plugins/clipboard-manager/src/error.rs
+++ b/plugins/clipboard-manager/src/error.rs
@@ -15,9 +15,6 @@ pub enum Error {
Clipboard(String),
#[error(transparent)]
Tauri(#[from] tauri::Error),
- #[cfg(desktop)]
- #[error("invalid image: {0}")]
- Image(#[from] image::ImageError),
}
impl Serialize for Error {
diff --git a/plugins/clipboard-manager/src/lib.rs b/plugins/clipboard-manager/src/lib.rs
index 3924d7f1..0cbb4e41 100644
--- a/plugins/clipboard-manager/src/lib.rs
+++ b/plugins/clipboard-manager/src/lib.rs
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-//! [](https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/clipboard-manager)
-//!
//! Read and write to the system clipboard.
#![doc(
@@ -13,7 +11,7 @@
use tauri::{
plugin::{Builder, TauriPlugin},
- Manager, Runtime,
+ Manager, RunEvent, Runtime,
};
#[cfg(desktop)]
@@ -61,5 +59,11 @@ pub fn init() -> TauriPlugin {
app.manage(clipboard);
Ok(())
})
+ .on_event(|_app, _event| {
+ #[cfg(desktop)]
+ if let RunEvent::Exit = _event {
+ _app.clipboard().cleanup();
+ }
+ })
.build()
}
diff --git a/plugins/deep-link/CHANGELOG.md b/plugins/deep-link/CHANGELOG.md
index 2f2da36d..4c3c9b9a 100644
--- a/plugins/deep-link/CHANGELOG.md
+++ b/plugins/deep-link/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## \[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.1]
+
+- [`b2aea045`](https://github.com/tauri-apps/plugins-workspace/commit/b2aea0456799775a7243706fdd7a5abf9a193992) ([#2008](https://github.com/tauri-apps/plugins-workspace/pull/2008) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) `onOpenUrl()` will now not call `getCurrent()` anymore, matching the documented behavior.
+
## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
diff --git a/plugins/deep-link/Cargo.toml b/plugins/deep-link/Cargo.toml
index db5c0247..b43b1dfa 100644
--- a/plugins/deep-link/Cargo.toml
+++ b/plugins/deep-link/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-deep-link"
-version = "2.0.1"
+version = "2.2.0"
description = "Set your Tauri application as the default handler for an URL"
authors = { workspace = true }
license = { workspace = true }
@@ -32,14 +32,14 @@ serde = { workspace = true }
serde_json = { workspace = true }
tauri = { workspace = true }
tauri-utils = { workspace = true }
-log = { workspace = true }
+tracing = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true }
[target."cfg(windows)".dependencies]
dunce = "1"
-windows-registry = "0.3"
-windows-result = "0.2"
+windows-registry = "0.4"
+windows-result = "0.3"
[target."cfg(target_os = \"linux\")".dependencies]
rust-ini = "0.21"
diff --git a/plugins/deep-link/README.md b/plugins/deep-link/README.md
index 77fae2c8..61a36a80 100644
--- a/plugins/deep-link/README.md
+++ b/plugins/deep-link/README.md
@@ -133,7 +133,7 @@ Under `tauri.conf.json > plugins > deep-link`, configure the domains (mobile) an
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
diff --git a/plugins/deep-link/api-iife.js b/plugins/deep-link/api-iife.js
index eba7152f..6d9e3e18 100644
--- a/plugins/deep-link/api-iife.js
+++ b/plugins/deep-link/api-iife.js
@@ -1 +1 @@
-if("__TAURI__"in window){var __TAURI_PLUGIN_DEEP_LINK__=function(e){"use strict";function n(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}async function r(e,n={},r){return window.__TAURI_INTERNALS__.invoke(e,n,r)}var t;async function i(e,t,i){const a={kind:"Any"};return r("plugin:event|listen",{event:e,target:a,handler:n(t)}).then((n=>async()=>async function(e,n){await r("plugin:event|unlisten",{event:e,eventId:n})}(e,n)))}async function a(){return await r("plugin:deep-link|get_current")}return"function"==typeof SuppressedError&&SuppressedError,function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(t||(t={})),e.getCurrent=a,e.isRegistered=async function(e){return await r("plugin:deep-link|is_registered",{protocol:e})},e.onOpenUrl=async function(e){const n=await a();return n&&e(n),await i("deep-link://new-url",(n=>{e(n.payload)}))},e.register=async function(e){return await r("plugin:deep-link|register",{protocol:e})},e.unregister=async function(e){return await r("plugin:deep-link|unregister",{protocol:e})},e}({});Object.defineProperty(window.__TAURI__,"deepLink",{value:__TAURI_PLUGIN_DEEP_LINK__})}
+if("__TAURI__"in window){var __TAURI_PLUGIN_DEEP_LINK__=function(e){"use strict";function n(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}async function r(e,n={},r){return window.__TAURI_INTERNALS__.invoke(e,n,r)}var t;async function i(e,t,i){const a={kind:"Any"};return r("plugin:event|listen",{event:e,target:a,handler:n(t)}).then((n=>async()=>async function(e,n){await r("plugin:event|unlisten",{event:e,eventId:n})}(e,n)))}return"function"==typeof SuppressedError&&SuppressedError,function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(t||(t={})),e.getCurrent=async function(){return await r("plugin:deep-link|get_current")},e.isRegistered=async function(e){return await r("plugin:deep-link|is_registered",{protocol:e})},e.onOpenUrl=async function(e){return await i("deep-link://new-url",(n=>{e(n.payload)}))},e.register=async function(e){return await r("plugin:deep-link|register",{protocol:e})},e.unregister=async function(e){return await r("plugin:deep-link|unregister",{protocol:e})},e}({});Object.defineProperty(window.__TAURI__,"deepLink",{value:__TAURI_PLUGIN_DEEP_LINK__})}
diff --git a/plugins/deep-link/examples/app/CHANGELOG.md b/plugins/deep-link/examples/app/CHANGELOG.md
index ba903aea..5e89d0f4 100644
--- a/plugins/deep-link/examples/app/CHANGELOG.md
+++ b/plugins/deep-link/examples/app/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## \[2.2.0]
+
+### Dependencies
+
+- Upgraded to `deep-link-js@2.1.0`
+
+## \[2.0.1]
+
+### Dependencies
+
+- Upgraded to `deep-link-js@2.0.1`
+
## \[2.0.0]
- [`e2c4dfb6`](https://github.com/tauri-apps/plugins-workspace/commit/e2c4dfb6af43e5dd8d9ceba232c315f5febd55c1) Update to tauri v2 stable release.
diff --git a/plugins/deep-link/examples/app/package.json b/plugins/deep-link/examples/app/package.json
index 98864087..8cd45fa1 100644
--- a/plugins/deep-link/examples/app/package.json
+++ b/plugins/deep-link/examples/app/package.json
@@ -1,7 +1,7 @@
{
"name": "deep-link-example",
"private": true,
- "version": "2.0.0",
+ "version": "2.2.0",
"type": "module",
"scripts": {
"dev": "vite",
@@ -10,12 +10,12 @@
"tauri": "tauri"
},
"dependencies": {
- "@tauri-apps/api": "2.0.1",
- "@tauri-apps/plugin-deep-link": "2.0.0"
+ "@tauri-apps/api": "2.2.0",
+ "@tauri-apps/plugin-deep-link": "2.2.0"
},
"devDependencies": {
- "@tauri-apps/cli": "2.0.0",
+ "@tauri-apps/cli": "2.2.7",
"typescript": "^5.2.2",
- "vite": "^5.4.7"
+ "vite": "^6.0.0"
}
}
diff --git a/plugins/deep-link/guest-js/index.ts b/plugins/deep-link/guest-js/index.ts
index 49afbab7..461bec8a 100644
--- a/plugins/deep-link/guest-js/index.ts
+++ b/plugins/deep-link/guest-js/index.ts
@@ -73,7 +73,7 @@ export async function unregister(protocol: string): Promise {
* await isRegistered("my-scheme");
* ```
*
- * #### - **macOS / Android / iOS**: Unsupported, always returns `true`.
+ * #### - **macOS / Android / iOS**: Unsupported.
*
* @since 2.0.0
*/
@@ -92,18 +92,13 @@ export async function isRegistered(protocol: string): Promise {
* await onOpenUrl((urls) => { console.log(urls) });
* ```
*
- * #### - **Windows / Linux**: Unsupported, the OS will spawn a new app instance passing the URL as a CLI argument.
+ * #### - **Windows / Linux**: Unsupported without the single-instance plugin. The OS will spawn a new app instance passing the URL as a CLI argument.
*
* @since 2.0.0
*/
export async function onOpenUrl(
handler: (urls: string[]) => void
): Promise {
- const current = await getCurrent()
- if (current) {
- handler(current)
- }
-
return await listen('deep-link://new-url', (event) => {
handler(event.payload)
})
diff --git a/plugins/deep-link/package.json b/plugins/deep-link/package.json
index c2ca9aa2..0b05dd86 100644
--- a/plugins/deep-link/package.json
+++ b/plugins/deep-link/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-deep-link",
- "version": "2.0.0",
+ "version": "2.2.0",
"description": "Set your Tauri application as the default handler for an URL",
"license": "MIT OR Apache-2.0",
"authors": [
diff --git a/plugins/deep-link/src/lib.rs b/plugins/deep-link/src/lib.rs
index 25cdd317..c259e6b2 100644
--- a/plugins/deep-link/src/lib.rs
+++ b/plugins/deep-link/src/lib.rs
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-use std::sync::Arc;
-
use tauri::{
plugin::{Builder, PluginApi, TauriPlugin},
AppHandle, EventId, Listener, Manager, Runtime,
@@ -217,7 +215,7 @@ mod imp {
current.replace(vec![url.clone()]);
let _ = self.app.emit("deep-link://new-url", vec![url]);
} else if cfg!(debug_assertions) {
- log::warn!("argument {url} does not match any configured deep link scheme; skipping it");
+ tracing::warn!("argument {url} does not match any configured deep link scheme; skipping it");
}
}
}
@@ -478,13 +476,10 @@ impl OpenUrlEvent {
}
impl DeepLink {
- /// Handle a new deep link being triggered to open the app.
+ /// Helper function for the `deep-link://new-url` event to run a function each time the protocol is triggered while the app is running.
///
- /// To avoid race conditions, if the app was started with a deep link,
- /// the closure gets immediately called with the deep link URL.
+ /// Use `get_current` on app load to check whether your app was started via a deep link.
pub fn on_open_url(&self, f: F) -> EventId {
- let f = Arc::new(f);
- let f_ = f.clone();
let event_id = self.app.listen("deep-link://new-url", move |event| {
if let Ok(urls) = serde_json::from_str(event.payload()) {
f(OpenUrlEvent {
@@ -494,13 +489,6 @@ impl DeepLink {
}
});
- if let Ok(Some(current)) = self.get_current() {
- f_(OpenUrlEvent {
- id: event_id,
- urls: current,
- })
- }
-
event_id
}
}
diff --git a/plugins/dialog/CHANGELOG.md b/plugins/dialog/CHANGELOG.md
index 150e092e..07989c79 100644
--- a/plugins/dialog/CHANGELOG.md
+++ b/plugins/dialog/CHANGELOG.md
@@ -1,5 +1,38 @@
# Changelog
+## \[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.
+
+### Dependencies
+
+- Upgraded to `fs-js@2.1.0`
+
+## \[2.0.2]
+
+### Dependencies
+
+- Upgraded to `fs-js@2.0.4`
+
+## \[2.0.4]
+
+- [`76f99ce9`](https://github.com/tauri-apps/plugins-workspace/commit/76f99ce999a2ff9e40235c1675e3eb6570b5e1e2) ([#2108](https://github.com/tauri-apps/plugins-workspace/pull/2108) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) The `Dialog` struct is now correctly exported, primarily to fix the documentation on `docs.rs`.
+
+### Dependencies
+
+- Upgraded to `fs@2.1.0`
+
+## \[2.0.3]
+
+### Dependencies
+
+- Upgraded to `fs@2.0.3`
+
+## \[2.0.1]
+
+- [`2302c2db`](https://github.com/tauri-apps/plugins-workspace/commit/2302c2db1c49673e61dcbda8cdb01b2c57e9ba6f) ([#1910](https://github.com/tauri-apps/plugins-workspace/pull/1910) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Fix `ask` and `confirm` not using system button texts
+- [`aee14ed4`](https://github.com/tauri-apps/plugins-workspace/commit/aee14ed4261cdedc4ed7cc2686f01f437859a5c7) ([#1892](https://github.com/tauri-apps/plugins-workspace/pull/1892) by [@nashaofu](https://github.com/tauri-apps/plugins-workspace/../../nashaofu)) Set `save` dialog mime type from the `filters` extensions on Android.
+
## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@@ -205,86 +238,3 @@
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
d6e80b)([#545](https://github.com/tauri-apps/plugins-workspace/pull/545)) Fixes docs.rs build by enabling the `tauri/dox` feature flag.
-- [`d74fc0a`](https://github.com/tauri-apps/plugins-workspace/commit/d74fc0a097996e90a37be8f57d50b7d1f6ca616f)([#555](https://github.com/tauri-apps/plugins-workspace/pull/555)) Update to alpha.11.
-
-### Dependencies
-
-- Upgraded to `fs@2.0.0-alpha.1`
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- \`
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- ri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- \`
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- hub.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- ri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- \`
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- alpha release!
- pull/371)) First v2 alpha release!
- ri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- \`
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- kspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- 71]\(https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- kspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- 7ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- 71]\(https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- kspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
- lpha release!
- pull/371)) First v2 alpha release!
diff --git a/plugins/dialog/Cargo.toml b/plugins/dialog/Cargo.toml
index eec855bd..3ca0df2d 100644
--- a/plugins/dialog/Cargo.toml
+++ b/plugins/dialog/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-dialog"
-version = "2.0.1"
+version = "2.2.0"
description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
@@ -34,7 +34,7 @@ tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true }
-tauri-plugin-fs = { path = "../fs", version = "2.0.1" }
+tauri-plugin-fs = { path = "../fs", version = "2.2.0" }
[target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] }
diff --git a/plugins/dialog/README.md b/plugins/dialog/README.md
index 2259e35b..63d71767 100644
--- a/plugins/dialog/README.md
+++ b/plugins/dialog/README.md
@@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-dialog#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
diff --git a/plugins/dialog/api-iife.js b/plugins/dialog/api-iife.js
index ee604570..c2e0870c 100644
--- a/plugins/dialog/api-iife.js
+++ b/plugins/dialog/api-iife.js
@@ -1 +1 @@
-if("__TAURI__"in window){var __TAURI_PLUGIN_DIALOG__=function(t){"use strict";async function n(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}return"function"==typeof SuppressedError&&SuppressedError,t.ask=async function(t,e){const i="string"==typeof e?{title:e}:e;return await n("plugin:dialog|ask",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString()??"Yes",cancelButtonLabel:i?.cancelLabel?.toString()??"No"})},t.confirm=async function(t,e){const i="string"==typeof e?{title:e}:e;return await n("plugin:dialog|confirm",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString()??"Ok",cancelButtonLabel:i?.cancelLabel?.toString()??"Cancel"})},t.message=async function(t,e){const i="string"==typeof e?{title:e}:e;await n("plugin:dialog|message",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString()})},t.open=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|open",{options:t})},t.save=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|save",{options:t})},t}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_PLUGIN_DIALOG__})}
+if("__TAURI__"in window){var __TAURI_PLUGIN_DIALOG__=function(t){"use strict";async function n(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}return"function"==typeof SuppressedError&&SuppressedError,t.ask=async function(t,e){const i="string"==typeof e?{title:e}:e;return await n("plugin:dialog|ask",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,yesButtonLabel:i?.okLabel?.toString(),noButtonLabel:i?.cancelLabel?.toString()})},t.confirm=async function(t,e){const i="string"==typeof e?{title:e}:e;return await n("plugin:dialog|confirm",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString(),cancelButtonLabel:i?.cancelLabel?.toString()})},t.message=async function(t,e){const i="string"==typeof e?{title:e}:e;await n("plugin:dialog|message",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString()})},t.open=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|open",{options:t})},t.save=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|save",{options:t})},t}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_PLUGIN_DIALOG__})}
diff --git a/plugins/dialog/guest-js/index.ts b/plugins/dialog/guest-js/index.ts
index a6301ebe..150be95a 100644
--- a/plugins/dialog/guest-js/index.ts
+++ b/plugins/dialog/guest-js/index.ts
@@ -257,8 +257,8 @@ async function ask(
message: message.toString(),
title: opts?.title?.toString(),
kind: opts?.kind,
- okButtonLabel: opts?.okLabel?.toString() ?? 'Yes',
- cancelButtonLabel: opts?.cancelLabel?.toString() ?? 'No'
+ yesButtonLabel: opts?.okLabel?.toString(),
+ noButtonLabel: opts?.cancelLabel?.toString()
})
}
@@ -287,8 +287,8 @@ async function confirm(
message: message.toString(),
title: opts?.title?.toString(),
kind: opts?.kind,
- okButtonLabel: opts?.okLabel?.toString() ?? 'Ok',
- cancelButtonLabel: opts?.cancelLabel?.toString() ?? 'Cancel'
+ okButtonLabel: opts?.okLabel?.toString(),
+ cancelButtonLabel: opts?.cancelLabel?.toString()
})
}
diff --git a/plugins/dialog/package.json b/plugins/dialog/package.json
index 72ebd27a..52fef579 100644
--- a/plugins/dialog/package.json
+++ b/plugins/dialog/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-dialog",
- "version": "2.0.0",
+ "version": "2.2.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
diff --git a/plugins/dialog/src/commands.rs b/plugins/dialog/src/commands.rs
index 8690a8b0..c3caf027 100644
--- a/plugins/dialog/src/commands.rs
+++ b/plugins/dialog/src/commands.rs
@@ -10,7 +10,7 @@ use tauri_plugin_fs::FsExt;
use crate::{
Dialog, FileDialogBuilder, FilePath, MessageDialogButtons, MessageDialogKind, Result, CANCEL,
- OK,
+ NO, OK, YES,
};
#[derive(Serialize)]
@@ -143,7 +143,7 @@ pub(crate) async fn open(
for folder in folders {
if let Ok(path) = folder.clone().into_path() {
if let Some(s) = window.try_fs_scope() {
- s.allow_directory(&path, options.recursive);
+ s.allow_directory(&path, options.recursive)?;
}
tauri_scope.allow_directory(&path, options.directory)?;
}
@@ -157,7 +157,7 @@ pub(crate) async fn open(
if let Some(folder) = &folder {
if let Ok(path) = folder.clone().into_path() {
if let Some(s) = window.try_fs_scope() {
- s.allow_directory(&path, options.recursive);
+ s.allow_directory(&path, options.recursive)?;
}
tauri_scope.allow_directory(&path, options.directory)?;
}
@@ -175,7 +175,7 @@ pub(crate) async fn open(
for file in files {
if let Ok(path) = file.clone().into_path() {
if let Some(s) = window.try_fs_scope() {
- s.allow_file(&path);
+ s.allow_file(&path)?;
}
tauri_scope.allow_file(&path)?;
@@ -190,7 +190,7 @@ pub(crate) async fn open(
if let Some(file) = &file {
if let Ok(path) = file.clone().into_path() {
if let Some(s) = window.try_fs_scope() {
- s.allow_file(&path);
+ s.allow_file(&path)?;
}
tauri_scope.allow_file(&path)?;
}
@@ -232,7 +232,7 @@ pub(crate) async fn save(
if let Some(p) = &path {
if let Ok(path) = p.clone().into_path() {
if let Some(s) = window.try_fs_scope() {
- s.allow_file(&path);
+ s.allow_file(&path)?;
}
tauri_scope.allow_file(&path)?;
}
@@ -299,8 +299,8 @@ pub(crate) async fn ask(
title: Option,
message: String,
kind: Option,
- ok_button_label: Option,
- cancel_button_label: Option,
+ yes_button_label: Option,
+ no_button_label: Option,
) -> Result {
Ok(message_dialog(
window,
@@ -308,7 +308,16 @@ pub(crate) async fn ask(
title,
message,
kind,
- get_ok_cancel_type(ok_button_label, cancel_button_label),
+ if let Some(yes_button_label) = yes_button_label {
+ MessageDialogButtons::OkCancelCustom(
+ yes_button_label,
+ no_button_label.unwrap_or(NO.to_string()),
+ )
+ } else if let Some(no_button_label) = no_button_label {
+ MessageDialogButtons::OkCancelCustom(YES.to_string(), no_button_label)
+ } else {
+ MessageDialogButtons::YesNo
+ },
))
}
@@ -328,22 +337,15 @@ pub(crate) async fn confirm(
title,
message,
kind,
- get_ok_cancel_type(ok_button_label, cancel_button_label),
+ if let Some(ok_button_label) = ok_button_label {
+ MessageDialogButtons::OkCancelCustom(
+ ok_button_label,
+ cancel_button_label.unwrap_or(CANCEL.to_string()),
+ )
+ } else if let Some(cancel_button_label) = cancel_button_label {
+ MessageDialogButtons::OkCancelCustom(OK.to_string(), cancel_button_label)
+ } else {
+ MessageDialogButtons::OkCancel
+ },
))
}
-
-fn get_ok_cancel_type(
- ok_button_label: Option,
- cancel_button_label: Option,
-) -> MessageDialogButtons {
- if let Some(ok_button_label) = ok_button_label {
- MessageDialogButtons::OkCancelCustom(
- ok_button_label,
- cancel_button_label.unwrap_or(CANCEL.to_string()),
- )
- } else if let Some(cancel_button_label) = cancel_button_label {
- MessageDialogButtons::OkCancelCustom(OK.to_string(), cancel_button_label)
- } else {
- MessageDialogButtons::OkCancel
- }
-}
diff --git a/plugins/dialog/src/desktop.rs b/plugins/dialog/src/desktop.rs
index d30f6bfe..d1a3e8b2 100644
--- a/plugins/dialog/src/desktop.rs
+++ b/plugins/dialog/src/desktop.rs
@@ -112,6 +112,7 @@ impl From for rfd::MessageButtons {
match value {
MessageDialogButtons::Ok => Self::Ok,
MessageDialogButtons::OkCancel => Self::OkCancel,
+ MessageDialogButtons::YesNo => Self::YesNo,
MessageDialogButtons::OkCustom(ok) => Self::OkCustom(ok),
MessageDialogButtons::OkCancelCustom(ok, cancel) => Self::OkCancelCustom(ok, cancel),
}
diff --git a/plugins/dialog/src/lib.rs b/plugins/dialog/src/lib.rs
index a7538e1b..2ef1c1ea 100644
--- a/plugins/dialog/src/lib.rs
+++ b/plugins/dialog/src/lib.rs
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-//! [](https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/dialog)
-//!
//! Native system dialogs for opening and saving files along with message dialogs.
#![doc(
@@ -41,8 +39,15 @@ use desktop::*;
#[cfg(mobile)]
use mobile::*;
+#[cfg(desktop)]
+pub use desktop::Dialog;
+#[cfg(mobile)]
+pub use mobile::Dialog;
+
pub(crate) const OK: &str = "Ok";
pub(crate) const CANCEL: &str = "Cancel";
+pub(crate) const YES: &str = "Yes";
+pub(crate) const NO: &str = "No";
macro_rules! blocking_fn {
($self:ident, $fn:ident) => {{
@@ -236,6 +241,7 @@ impl MessageDialogBuilder {
let (ok_button_label, cancel_button_label) = match &self.buttons {
MessageDialogButtons::Ok => (Some(OK), None),
MessageDialogButtons::OkCancel => (Some(OK), Some(CANCEL)),
+ MessageDialogButtons::YesNo => (Some(YES), Some(NO)),
MessageDialogButtons::OkCustom(ok) => (Some(ok.as_str()), Some(CANCEL)),
MessageDialogButtons::OkCancelCustom(ok, cancel) => {
(Some(ok.as_str()), Some(cancel.as_str()))
diff --git a/plugins/dialog/src/models.rs b/plugins/dialog/src/models.rs
index 3f9eb6c1..d6452bce 100644
--- a/plugins/dialog/src/models.rs
+++ b/plugins/dialog/src/models.rs
@@ -59,6 +59,8 @@ pub enum MessageDialogButtons {
Ok,
/// 2 buttons `Ok` and `Cancel` with OS default dialog texts
OkCancel,
+ /// 2 buttons `Yes` and `No` with OS default dialog texts
+ YesNo,
/// A single `Ok` button with custom text
OkCustom(String),
/// 2 buttons `Ok` and `Cancel` with custom texts
diff --git a/plugins/fs/CHANGELOG.md b/plugins/fs/CHANGELOG.md
index 41e6411a..533714ad 100644
--- a/plugins/fs/CHANGELOG.md
+++ b/plugins/fs/CHANGELOG.md
@@ -1,5 +1,31 @@
# Changelog
+## \[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.4]
+
+- [`77b85507`](https://github.com/tauri-apps/plugins-workspace/commit/77b855074aad612f2b28e6a3b5881fac767a05ae) ([#2171](https://github.com/tauri-apps/plugins-workspace/pull/2171) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed docs.rs build.
+
+## \[2.0.3]
+
+- [`ed981027`](https://github.com/tauri-apps/plugins-workspace/commit/ed981027dd4fba7d0e2f836eb5db34d344388d73) ([#1962](https://github.com/tauri-apps/plugins-workspace/pull/1962) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Improve performance of `readTextFile` and `readTextFileLines` APIs
+- [`3e78173d`](https://github.com/tauri-apps/plugins-workspace/commit/3e78173df9ce90aa3b19e1f36d1f8712c5020fb6) ([#2018](https://github.com/tauri-apps/plugins-workspace/pull/2018) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Fix `readDir` function failing to read directories that contain broken symlinks.
+- [`5092ea5e`](https://github.com/tauri-apps/plugins-workspace/commit/5092ea5e89817c0550d09b0a4ad17bf1253b23df) ([#1964](https://github.com/tauri-apps/plugins-workspace/pull/1964) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Add support for using `ReadableStream` with `writeFile` API.
+
+## \[2.0.2]
+
+- [`77149dc4`](https://github.com/tauri-apps/plugins-workspace/commit/77149dc4320d26b413e4a6bbe82c654367c51b32) ([#1965](https://github.com/tauri-apps/plugins-workspace/pull/1965) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Fix `writeTextFile` converting UTF-8 characters (for example `äöü`) in the given path into replacement character (`�`)
+
+## \[2.0.3]
+
+- [`14cee64c`](https://github.com/tauri-apps/plugins-workspace/commit/14cee64c82a72655ae6a4ac0892736a2959dbda5) ([#1958](https://github.com/tauri-apps/plugins-workspace/pull/1958) by [@bWanShiTong](https://github.com/tauri-apps/plugins-workspace/../../bWanShiTong)) Fix compilation on targets with pointer width of `16` or `32`
+
+## \[2.0.1]
+
+- [`ae802456`](https://github.com/tauri-apps/plugins-workspace/commit/ae8024565f074f313084777c8b10d1b5e3bbe220) ([#1950](https://github.com/tauri-apps/plugins-workspace/pull/1950) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Improve performance of the `FileHandle.read` and `writeTextFile` APIs.
+
## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@@ -149,33 +175,3 @@
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- /pull/454)) Fix `writeBinaryFile` crashing with `command 'write_binary_file' not found`
-- [`d74fc0a`](https://github.com/tauri-apps/plugins-workspace/commit/d74fc0a097996e90a37be8f57d50b7d1f6ca616f)([#555](https://github.com/tauri-apps/plugins-workspace/pull/555)) Update to alpha.11.
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- ae67\`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- .com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- ac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- .com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- s/plugins-workspace/pull/371)) First v2 alpha release!
- ac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- .com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- kspace/pull/371)) First v2 alpha release!
- s/plugins-workspace/pull/371)) First v2 alpha release!
- ac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- .com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- uri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- .com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- kspace/pull/371)) First v2 alpha release!
- s/plugins-workspace/pull/371)) First v2 alpha release!
- ac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- .com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
diff --git a/plugins/fs/Cargo.toml b/plugins/fs/Cargo.toml
index 1d9b5f34..5768da73 100644
--- a/plugins/fs/Cargo.toml
+++ b/plugins/fs/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-fs"
-version = "2.0.1"
+version = "2.2.0"
description = "Access the file system."
authors = { workspace = true }
license = { workspace = true }
@@ -14,7 +14,7 @@ rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.platforms.support]
-windows = { level = "full", notes = "" }
+windows = { level = "full", notes = "Apps installed via MSI or NSIS in `perMachine` and `both` mode require admin permissions for write acces in `$RESOURCES` folder" }
linux = { level = "full", notes = "No write access to `$RESOURCES` folder" }
macos = { level = "full", notes = "No write access to `$RESOURCES` folder" }
android = { level = "partial", notes = "Access is restricted to Application folder by default" }
@@ -24,6 +24,8 @@ ios = { level = "partial", notes = "Access is restricted to Application folder b
tauri-plugin = { workspace = true, features = ["build"] }
schemars = { workspace = true }
serde = { workspace = true }
+toml = "0.8"
+tauri-utils = { workspace = true, features = ["build"] }
[dependencies]
serde = { workspace = true }
@@ -34,9 +36,13 @@ thiserror = { workspace = true }
url = { workspace = true }
anyhow = "1"
uuid = { version = "1", features = ["v4"] }
-glob = "0.3"
-notify = { version = "6", optional = true, features = ["serde"] }
-notify-debouncer-full = { version = "0.3", optional = true }
+glob = { workspace = true }
+# TODO: Remove `serialization-compat-6` in v3
+notify = { version = "8", optional = true, features = [
+ "serde",
+ "serialization-compat-6",
+] }
+notify-debouncer-full = { version = "0.5", optional = true }
dunce = { workspace = true }
percent-encoding = "2"
diff --git a/plugins/fs/README.md b/plugins/fs/README.md
index af5c63a8..dea88824 100644
--- a/plugins/fs/README.md
+++ b/plugins/fs/README.md
@@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-fs#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
diff --git a/plugins/fs/api-iife.js b/plugins/fs/api-iife.js
index b855277d..1edae3de 100644
--- a/plugins/fs/api-iife.js
+++ b/plugins/fs/api-iife.js
@@ -1 +1 @@
-if("__TAURI__"in window){var __TAURI_PLUGIN_FS__=function(t){"use strict";function e(t,e,n,i){if("a"===n&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?i:"a"===n?i.call(t):i?i.value:e.get(t)}function n(t,e,n,i,o){if("function"==typeof e?t!==e||!o:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,n),n}var i,o,r,a,s,c;"function"==typeof SuppressedError&&SuppressedError;class f{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),o.set(this,0),r.set(this,{}),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:a})=>{if(a===e(this,o,"f")){n(this,o,a+1),e(this,i,"f").call(this,t);const s=Object.keys(e(this,r,"f"));if(s.length>0){let t=a+1;for(const n of s.sort()){if(parseInt(n)!==t)break;{const o=e(this,r,"f")[n];delete e(this,r,"f")[n],e(this,i,"f").call(this,o),t+=1}}n(this,o,t)}}else e(this,r,"f")[a.toString()]=t}))}set onmessage(t){n(this,i,t)}get onmessage(){return e(this,i,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}async function l(t,e={},n){return window.__TAURI_INTERNALS__.invoke(t,e,n)}i=new WeakMap,o=new WeakMap,r=new WeakMap;class u{get rid(){return e(this,a,"f")}constructor(t){a.set(this,void 0),n(this,a,t)}async close(){return l("plugin:resources|close",{rid:this.rid})}}function p(t){return{isFile:t.isFile,isDirectory:t.isDirectory,isSymlink:t.isSymlink,size:t.size,mtime:null!==t.mtime?new Date(t.mtime):null,atime:null!==t.atime?new Date(t.atime):null,birthtime:null!==t.birthtime?new Date(t.birthtime):null,readonly:t.readonly,fileAttributes:t.fileAttributes,dev:t.dev,ino:t.ino,mode:t.mode,nlink:t.nlink,uid:t.uid,gid:t.gid,rdev:t.rdev,blksize:t.blksize,blocks:t.blocks}}a=new WeakMap,t.BaseDirectory=void 0,(s=t.BaseDirectory||(t.BaseDirectory={}))[s.Audio=1]="Audio",s[s.Cache=2]="Cache",s[s.Config=3]="Config",s[s.Data=4]="Data",s[s.LocalData=5]="LocalData",s[s.Document=6]="Document",s[s.Download=7]="Download",s[s.Picture=8]="Picture",s[s.Public=9]="Public",s[s.Video=10]="Video",s[s.Resource=11]="Resource",s[s.Temp=12]="Temp",s[s.AppConfig=13]="AppConfig",s[s.AppData=14]="AppData",s[s.AppLocalData=15]="AppLocalData",s[s.AppCache=16]="AppCache",s[s.AppLog=17]="AppLog",s[s.Desktop=18]="Desktop",s[s.Executable=19]="Executable",s[s.Font=20]="Font",s[s.Home=21]="Home",s[s.Runtime=22]="Runtime",s[s.Template=23]="Template",t.SeekMode=void 0,(c=t.SeekMode||(t.SeekMode={}))[c.Start=0]="Start",c[c.Current=1]="Current",c[c.End=2]="End";class w extends u{async read(t){if(0===t.byteLength)return 0;const[e,n]=await l("plugin:fs|read",{rid:this.rid,len:t.byteLength});return t.set(e),0===n?null:n}async seek(t,e){return await l("plugin:fs|seek",{rid:this.rid,offset:t,whence:e})}async stat(){return p(await l("plugin:fs|fstat",{rid:this.rid}))}async truncate(t){await l("plugin:fs|ftruncate",{rid:this.rid,len:t})}async write(t){return await l("plugin:fs|write",{rid:this.rid,data:t})}}async function h(t){await l("plugin:fs|unwatch",{rid:t})}return t.FileHandle=w,t.copyFile=async function(t,e,n){if(t instanceof URL&&"file:"!==t.protocol||e instanceof URL&&"file:"!==e.protocol)throw new TypeError("Must be a file URL.");await l("plugin:fs|copy_file",{fromPath:t instanceof URL?t.toString():t,toPath:e instanceof URL?e.toString():e,options:n})},t.create=async function(t,e){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");const n=await l("plugin:fs|create",{path:t instanceof URL?t.toString():t,options:e});return new w(n)},t.exists=async function(t,e){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");return await l("plugin:fs|exists",{path:t instanceof URL?t.toString():t,options:e})},t.lstat=async function(t,e){return p(await l("plugin:fs|lstat",{path:t instanceof URL?t.toString():t,options:e}))},t.mkdir=async function(t,e){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");await l("plugin:fs|mkdir",{path:t instanceof URL?t.toString():t,options:e})},t.open=async function(t,e){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");const n=await l("plugin:fs|open",{path:t instanceof URL?t.toString():t,options:e});return new w(n)},t.readDir=async function(t,e){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");return await l("plugin:fs|read_dir",{path:t instanceof URL?t.toString():t,options:e})},t.readFile=async function(t,e){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");const n=await l("plugin:fs|read_file",{path:t instanceof URL?t.toString():t,options:e});return n instanceof ArrayBuffer?new Uint8Array(n):Uint8Array.from(n)},t.readTextFile=async function(t,e){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");return await l("plugin:fs|read_text_file",{path:t instanceof URL?t.toString():t,options:e})},t.readTextFileLines=async function(t,e){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");const n=t instanceof URL?t.toString():t;return await Promise.resolve({path:n,rid:null,async next(){null===this.rid&&(this.rid=await l("plugin:fs|read_text_file_lines",{path:n,options:e}));const[t,i]=await l("plugin:fs|read_text_file_lines_next",{rid:this.rid});return i&&(this.rid=null),{value:i?"":t,done:i}},[Symbol.asyncIterator](){return this}})},t.remove=async function(t,e){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");await l("plugin:fs|remove",{path:t instanceof URL?t.toString():t,options:e})},t.rename=async function(t,e,n){if(t instanceof URL&&"file:"!==t.protocol||e instanceof URL&&"file:"!==e.protocol)throw new TypeError("Must be a file URL.");await l("plugin:fs|rename",{oldPath:t instanceof URL?t.toString():t,newPath:e instanceof URL?e.toString():e,options:n})},t.stat=async function(t,e){return p(await l("plugin:fs|stat",{path:t instanceof URL?t.toString():t,options:e}))},t.truncate=async function(t,e,n){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");await l("plugin:fs|truncate",{path:t instanceof URL?t.toString():t,len:e,options:n})},t.watch=async function(t,e,n){const i={recursive:!1,delayMs:2e3,...n},o=Array.isArray(t)?t:[t];for(const t of o)if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");const r=new f;r.onmessage=e;const a=await l("plugin:fs|watch",{paths:o.map((t=>t instanceof URL?t.toString():t)),options:i,onEvent:r});return()=>{h(a)}},t.watchImmediate=async function(t,e,n){const i={recursive:!1,...n,delayMs:null},o=Array.isArray(t)?t:[t];for(const t of o)if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");const r=new f;r.onmessage=e;const a=await l("plugin:fs|watch",{paths:o.map((t=>t instanceof URL?t.toString():t)),options:i,onEvent:r});return()=>{h(a)}},t.writeFile=async function(t,e,n){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");await l("plugin:fs|write_file",e,{headers:{path:encodeURIComponent(t instanceof URL?t.toString():t),options:JSON.stringify(n)}})},t.writeTextFile=async function(t,e,n){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");await l("plugin:fs|write_text_file",{path:t instanceof URL?t.toString():t,data:e,options:n})},t}({});Object.defineProperty(window.__TAURI__,"fs",{value:__TAURI_PLUGIN_FS__})}
+if("__TAURI__"in window){var __TAURI_PLUGIN_FS__=function(t){"use strict";function e(t,e,n,i){if("a"===n&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?i:"a"===n?i.call(t):i?i.value:e.get(t)}function n(t,e,n,i,o){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,n),n}var i,o,r,a;"function"==typeof SuppressedError&&SuppressedError;const s="__TAURI_TO_IPC_KEY__";class f{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),o.set(this,0),r.set(this,[]),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:a})=>{if(a==e(this,o,"f"))for(e(this,i,"f").call(this,t),n(this,o,e(this,o,"f")+1);e(this,o,"f")in e(this,r,"f");){const t=e(this,r,"f")[e(this,o,"f")];e(this,i,"f").call(this,t),delete e(this,r,"f")[e(this,o,"f")],n(this,o,e(this,o,"f")+1)}else e(this,r,"f")[a]=t}))}set onmessage(t){n(this,i,t)}get onmessage(){return e(this,i,"f")}[(i=new WeakMap,o=new WeakMap,r=new WeakMap,s)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[s]()}}async function c(t,e={},n){return window.__TAURI_INTERNALS__.invoke(t,e,n)}class l{get rid(){return e(this,a,"f")}constructor(t){a.set(this,void 0),n(this,a,t)}async close(){return c("plugin:resources|close",{rid:this.rid})}}var u,p;function w(t){return{isFile:t.isFile,isDirectory:t.isDirectory,isSymlink:t.isSymlink,size:t.size,mtime:null!==t.mtime?new Date(t.mtime):null,atime:null!==t.atime?new Date(t.atime):null,birthtime:null!==t.birthtime?new Date(t.birthtime):null,readonly:t.readonly,fileAttributes:t.fileAttributes,dev:t.dev,ino:t.ino,mode:t.mode,nlink:t.nlink,uid:t.uid,gid:t.gid,rdev:t.rdev,blksize:t.blksize,blocks:t.blocks}}a=new WeakMap,t.BaseDirectory=void 0,(u=t.BaseDirectory||(t.BaseDirectory={}))[u.Audio=1]="Audio",u[u.Cache=2]="Cache",u[u.Config=3]="Config",u[u.Data=4]="Data",u[u.LocalData=5]="LocalData",u[u.Document=6]="Document",u[u.Download=7]="Download",u[u.Picture=8]="Picture",u[u.Public=9]="Public",u[u.Video=10]="Video",u[u.Resource=11]="Resource",u[u.Temp=12]="Temp",u[u.AppConfig=13]="AppConfig",u[u.AppData=14]="AppData",u[u.AppLocalData=15]="AppLocalData",u[u.AppCache=16]="AppCache",u[u.AppLog=17]="AppLog",u[u.Desktop=18]="Desktop",u[u.Executable=19]="Executable",u[u.Font=20]="Font",u[u.Home=21]="Home",u[u.Runtime=22]="Runtime",u[u.Template=23]="Template",t.SeekMode=void 0,(p=t.SeekMode||(t.SeekMode={}))[p.Start=0]="Start",p[p.Current=1]="Current",p[p.End=2]="End";class h extends l{async read(t){if(0===t.byteLength)return 0;const e=await c("plugin:fs|read",{rid:this.rid,len:t.byteLength}),n=function(t){const e=new Uint8ClampedArray(t),n=e.byteLength;let i=0;for(let t=0;tt instanceof URL?t.toString():t)),options:i,onEvent:r});return()=>{y(a)}},t.watchImmediate=async function(t,e,n){const i={recursive:!1,...n,delayMs:null},o=Array.isArray(t)?t:[t];for(const t of o)if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");const r=new f;r.onmessage=e;const a=await c("plugin:fs|watch",{paths:o.map((t=>t instanceof URL?t.toString():t)),options:i,onEvent:r});return()=>{y(a)}},t.writeFile=async function(t,e,n){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");if(e instanceof ReadableStream){const i=await d(t,n);for await(const t of e)await i.write(t);await i.close()}else await c("plugin:fs|write_file",e,{headers:{path:encodeURIComponent(t instanceof URL?t.toString():t),options:JSON.stringify(n)}})},t.writeTextFile=async function(t,e,n){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");const i=new TextEncoder;await c("plugin:fs|write_text_file",i.encode(e),{headers:{path:encodeURIComponent(t instanceof URL?t.toString():t),options:JSON.stringify(n)}})},t}({});Object.defineProperty(window.__TAURI__,"fs",{value:__TAURI_PLUGIN_FS__})}
diff --git a/plugins/fs/build.rs b/plugins/fs/build.rs
index cb9d00da..9c34586e 100644
--- a/plugins/fs/build.rs
+++ b/plugins/fs/build.rs
@@ -7,6 +7,8 @@ use std::{
path::{Path, PathBuf},
};
+use tauri_utils::acl::manifest::PermissionFile;
+
#[path = "src/scope.rs"]
#[allow(dead_code)]
mod scope;
@@ -16,10 +18,23 @@ mod scope;
#[serde(untagged)]
#[allow(unused)]
enum FsScopeEntry {
- /// FS scope path.
+ /// A path that can be accessed by the webview when using the fs APIs.
+ /// FS scope path pattern.
+ ///
+ /// 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`.
Value(PathBuf),
Object {
- /// FS scope path.
+ /// A path that can be accessed by the webview when using the fs 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,
},
}
@@ -62,31 +77,32 @@ const BASE_DIR_VARS: &[&str] = &[
"APPCACHE",
"APPLOG",
];
-const COMMANDS: &[&str] = &[
- "mkdir",
- "create",
- "copy_file",
- "remove",
- "rename",
- "truncate",
- "ftruncate",
- "write",
- "write_file",
- "write_text_file",
- "read_dir",
- "read_file",
- "read",
- "open",
- "read_text_file",
- "read_text_file_lines",
- "read_text_file_lines_next",
- "seek",
- "stat",
- "lstat",
- "fstat",
- "exists",
- "watch",
- "unwatch",
+const COMMANDS: &[(&str, &[&str])] = &[
+ ("mkdir", &[]),
+ ("create", &[]),
+ ("copy_file", &[]),
+ ("remove", &[]),
+ ("rename", &[]),
+ ("truncate", &[]),
+ ("ftruncate", &[]),
+ ("write", &[]),
+ ("write_file", &["open", "write"]),
+ ("write_text_file", &[]),
+ ("read_dir", &[]),
+ ("read_file", &[]),
+ ("read", &[]),
+ ("open", &[]),
+ ("read_text_file", &[]),
+ ("read_text_file_lines", &["read_text_file_lines_next"]),
+ ("read_text_file_lines_next", &[]),
+ ("seek", &[]),
+ ("stat", &[]),
+ ("lstat", &[]),
+ ("fstat", &[]),
+ ("exists", &[]),
+ ("watch", &[]),
+ ("unwatch", &[]),
+ ("size", &[]),
];
fn main() {
@@ -192,9 +208,59 @@ permissions = [
}
}
- tauri_plugin::Builder::new(COMMANDS)
- .global_api_script_path("./api-iife.js")
- .global_scope_schema(schemars::schema_for!(FsScopeEntry))
- .android_path("android")
- .build();
+ tauri_plugin::Builder::new(
+ &COMMANDS
+ .iter()
+ // FIXME: https://docs.rs/crate/tauri-plugin-fs/2.1.0/builds/1571296
+ .filter(|c| c.1.is_empty())
+ .map(|c| c.0)
+ .collect::>(),
+ )
+ .global_api_script_path("./api-iife.js")
+ .global_scope_schema(schemars::schema_for!(FsScopeEntry))
+ .android_path("android")
+ .build();
+
+ // workaround to include nested permissions as `tauri_plugin` doesn't support it
+ let permissions_dir = autogenerated.join("commands");
+ for (command, nested_commands) in COMMANDS {
+ if nested_commands.is_empty() {
+ continue;
+ }
+
+ let permission_path = permissions_dir.join(format!("{command}.toml"));
+
+ let content = std::fs::read_to_string(&permission_path)
+ .unwrap_or_else(|_| panic!("failed to read {command}.toml"));
+
+ let mut permission_file = toml::from_str::(&content)
+ .unwrap_or_else(|_| panic!("failed to deserialize {command}.toml"));
+
+ for p in permission_file
+ .permission
+ .iter_mut()
+ .filter(|p| p.identifier.starts_with("allow"))
+ {
+ for c in nested_commands.iter().map(|s| s.to_string()) {
+ if !p.commands.allow.contains(&c) {
+ p.commands.allow.push(c);
+ }
+ }
+ }
+
+ let out = toml::to_string_pretty(&permission_file)
+ .unwrap_or_else(|_| panic!("failed to serialize {command}.toml"));
+ let out = format!(
+ r#"# Automatically generated - DO NOT EDIT!
+
+"$schema" = "../../schemas/schema.json"
+
+{out}"#
+ );
+
+ if content != out {
+ std::fs::write(permission_path, out)
+ .unwrap_or_else(|_| panic!("failed to write {command}.toml"));
+ }
+ }
}
diff --git a/plugins/fs/guest-js/index.ts b/plugins/fs/guest-js/index.ts
index 1f314f71..9d894bc5 100644
--- a/plugins/fs/guest-js/index.ts
+++ b/plugins/fs/guest-js/index.ts
@@ -10,20 +10,33 @@
* This module prevents path traversal, not allowing parent directory accessors to be used
* (i.e. "/usr/path/to/../file" or "../path/to/file" paths are not allowed).
* Paths accessed with this API must be either relative to one of the {@link BaseDirectory | base directories}
- * or created with the {@link https://v2.tauri.app/reference/javascript/api/namespacepath | path API}.
+ * or created with the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/ | path API}.
*
* The API has a scope configuration that forces you to restrict the paths that can be accessed using glob patterns.
*
- * The scope configuration is an array of glob patterns describing folder paths that are allowed.
- * For instance, this scope configuration only allows accessing files on the
- * *databases* folder of the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/#appdatadir | `$APPDATA` directory}:
+ * The scope configuration is an array of glob patterns describing file/directory paths that are allowed.
+ * For instance, this scope configuration allows **all** enabled `fs` APIs to (only) access files in the
+ * *databases* directory of the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/#appdatadir | `$APPDATA` directory}:
* ```json
* {
- * "plugins": {
- * "fs": {
- * "scope": ["$APPDATA/databases/*"]
+ * "permissions": [
+ * {
+ * "identifier": "fs:scope",
+ * "allow": [{ "path": "$APPDATA/databases/*" }]
* }
- * }
+ * ]
+ * }
+ * ```
+ *
+ * Scopes can also be applied to specific `fs` APIs by using the API's identifier instead of `fs:scope`:
+ * ```json
+ * {
+ * "permissions": [
+ * {
+ * "identifier": "fs:allow-exists",
+ * "allow": [{ "path": "$APPDATA/databases/*" }]
+ * }
+ * ]
* }
* ```
*
@@ -56,8 +69,6 @@
*
* Trying to execute any API with a URL not configured on the scope results in a promise rejection due to denied access.
*
- * Note that this scope applies to **all** APIs on this module.
- *
* @module
*/
@@ -243,6 +254,26 @@ function parseFileInfo(r: UnparsedFileInfo): FileInfo {
}
}
+// https://mstn.github.io/2018/06/08/fixed-size-arrays-in-typescript/
+type FixedSizeArray = ReadonlyArray & {
+ length: N
+}
+
+// https://gist.github.com/zapthedingbat/38ebfbedd98396624e5b5f2ff462611d
+/** Converts a big-endian eight byte array to number */
+function fromBytes(buffer: FixedSizeArray): number {
+ const bytes = new Uint8ClampedArray(buffer)
+ const size = bytes.byteLength
+ let x = 0
+ for (let i = 0; i < size; i++) {
+ // eslint-disable-next-line security/detect-object-injection
+ const byte = bytes[i]
+ x *= 0x100
+ x += byte
+ }
+ return x
+}
+
/**
* The Tauri abstraction for reading and writing files.
*
@@ -285,12 +316,20 @@ class FileHandle extends Resource {
return 0
}
- const [data, nread] = await invoke<[number[], number]>('plugin:fs|read', {
+ const data = await invoke('plugin:fs|read', {
rid: this.rid,
len: buffer.byteLength
})
- buffer.set(data)
+ // Rust side will never return an empty array for this command and
+ // ensure there is at least 8 elements there.
+ //
+ // This is an optimization to include the number of read bytes (as bigendian bytes)
+ // at the end of returned array to avoid serialization overhead of separate values.
+ const nread = fromBytes(data.slice(-8) as FixedSizeArray)
+
+ const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data
+ buffer.set(bytes.slice(0, bytes.length - 8))
return nread === 0 ? null : nread
}
@@ -389,11 +428,11 @@ class FileHandle extends Resource {
}
/**
- * Writes `p.byteLength` bytes from `p` to the underlying data stream. It
- * resolves to the number of bytes written from `p` (`0` <= `n` <=
- * `p.byteLength`) or reject with the error encountered that caused the
+ * Writes `data.byteLength` bytes from `data` to the underlying data stream. It
+ * resolves to the number of bytes written from `data` (`0` <= `n` <=
+ * `data.byteLength`) or reject with the error encountered that caused the
* write to stop early. `write()` must reject with a non-null error if
- * would resolve to `n` < `p.byteLength`. `write()` must not modify the
+ * would resolve to `n` < `data.byteLength`. `write()` must not modify the
* slice data, even temporarily.
*
* @example
@@ -731,10 +770,14 @@ async function readTextFile(
throw new TypeError('Must be a file URL.')
}
- return await invoke('plugin:fs|read_text_file', {
+ const arr = await invoke('plugin:fs|read_text_file', {
path: path instanceof URL ? path.toString() : path,
options
})
+
+ const bytes = arr instanceof ArrayBuffer ? arr : Uint8Array.from(arr)
+
+ return new TextDecoder().decode(bytes)
}
/**
@@ -765,6 +808,7 @@ async function readTextFileLines(
return await Promise.resolve({
path: pathStr,
rid: null as number | null,
+
async next(): Promise> {
if (this.rid === null) {
this.rid = await invoke('plugin:fs|read_text_file_lines', {
@@ -773,19 +817,35 @@ async function readTextFileLines(
})
}
- const [line, done] = await invoke<[string | null, boolean]>(
+ const arr = await invoke(
'plugin:fs|read_text_file_lines_next',
{ rid: this.rid }
)
- // an iteration is over, reset rid for next iteration
- if (done) this.rid = null
+ const bytes =
+ arr instanceof ArrayBuffer ? new Uint8Array(arr) : Uint8Array.from(arr)
+
+ // Rust side will never return an empty array for this command and
+ // ensure there is at least one elements there.
+ //
+ // This is an optimization to include whether we finished iteration or not (1 or 0)
+ // at the end of returned array to avoid serialization overhead of separate values.
+ const done = bytes[bytes.byteLength - 1] === 1
+
+ if (done) {
+ // a full iteration is over, reset rid for next iteration
+ this.rid = null
+ return { value: null, done }
+ }
+
+ const line = new TextDecoder().decode(bytes.slice(0, bytes.byteLength))
return {
- value: done ? '' : line!,
+ value: line,
done
}
},
+
[Symbol.asyncIterator](): AsyncIterableIterator {
return this
}
@@ -1006,19 +1066,27 @@ interface WriteFileOptions {
*/
async function writeFile(
path: string | URL,
- data: Uint8Array,
+ data: Uint8Array | ReadableStream,
options?: WriteFileOptions
): Promise {
if (path instanceof URL && path.protocol !== 'file:') {
throw new TypeError('Must be a file URL.')
}
- await invoke('plugin:fs|write_file', data, {
- headers: {
- path: encodeURIComponent(path instanceof URL ? path.toString() : path),
- options: JSON.stringify(options)
+ if (data instanceof ReadableStream) {
+ const file = await open(path, options)
+ for await (const chunk of data) {
+ await file.write(chunk)
}
- })
+ await file.close()
+ } else {
+ await invoke('plugin:fs|write_file', data, {
+ headers: {
+ path: encodeURIComponent(path instanceof URL ? path.toString() : path),
+ options: JSON.stringify(options)
+ }
+ })
+ }
}
/**
@@ -1041,10 +1109,13 @@ async function writeTextFile(
throw new TypeError('Must be a file URL.')
}
- await invoke('plugin:fs|write_text_file', {
- path: path instanceof URL ? path.toString() : path,
- data,
- options
+ const encoder = new TextEncoder()
+
+ await invoke('plugin:fs|write_text_file', encoder.encode(data), {
+ headers: {
+ path: encodeURIComponent(path instanceof URL ? path.toString() : path),
+ options: JSON.stringify(options)
+ }
})
}
@@ -1251,6 +1322,31 @@ async function watchImmediate(
}
}
+/**
+ * Get the size of a file or directory. For files, the `stat` functions can be used as well.
+ *
+ * If `path` is a directory, this function will recursively iterate over every file and every directory inside of `path` and therefore will be very time consuming if used on larger directories.
+ *
+ * @example
+ * ```typescript
+ * import { size, BaseDirectory } from '@tauri-apps/plugin-fs';
+ * // Get the size of the `$APPDATA/tauri` directory.
+ * const dirSize = await size('tauri', { baseDir: BaseDirectory.AppData });
+ * console.log(dirSize); // 1024
+ * ```
+ *
+ * @since 2.1.0
+ */
+async function size(path: string | URL): Promise {
+ if (path instanceof URL && path.protocol !== 'file:') {
+ throw new TypeError('Must be a file URL.')
+ }
+
+ return await invoke('plugin:fs|size', {
+ path: path instanceof URL ? path.toString() : path
+ })
+}
+
export type {
CreateOptions,
OpenOptions,
@@ -1298,5 +1394,6 @@ export {
writeTextFile,
exists,
watch,
- watchImmediate
+ watchImmediate,
+ size
}
diff --git a/plugins/fs/package.json b/plugins/fs/package.json
index 7a6cf527..09efc93d 100644
--- a/plugins/fs/package.json
+++ b/plugins/fs/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-fs",
- "version": "2.0.0",
+ "version": "2.2.0",
"description": "Access the file system.",
"license": "MIT OR Apache-2.0",
"authors": [
diff --git a/plugins/fs/permissions/autogenerated/commands/read_text_file_lines.toml b/plugins/fs/permissions/autogenerated/commands/read_text_file_lines.toml
index 1ba629cb..84b4ebb2 100644
--- a/plugins/fs/permissions/autogenerated/commands/read_text_file_lines.toml
+++ b/plugins/fs/permissions/autogenerated/commands/read_text_file_lines.toml
@@ -5,9 +5,18 @@
[[permission]]
identifier = "allow-read-text-file-lines"
description = "Enables the read_text_file_lines command without any pre-configured scope."
-commands.allow = ["read_text_file_lines"]
+
+[permission.commands]
+allow = [
+ "read_text_file_lines",
+ "read_text_file_lines_next",
+]
+deny = []
[[permission]]
identifier = "deny-read-text-file-lines"
description = "Denies the read_text_file_lines command without any pre-configured scope."
-commands.deny = ["read_text_file_lines"]
+
+[permission.commands]
+allow = []
+deny = ["read_text_file_lines"]
diff --git a/plugins/fs/permissions/autogenerated/commands/size.toml b/plugins/fs/permissions/autogenerated/commands/size.toml
new file mode 100644
index 00000000..8a0ea55c
--- /dev/null
+++ b/plugins/fs/permissions/autogenerated/commands/size.toml
@@ -0,0 +1,13 @@
+# Automatically generated - DO NOT EDIT!
+
+"$schema" = "../../schemas/schema.json"
+
+[[permission]]
+identifier = "allow-size"
+description = "Enables the size command without any pre-configured scope."
+commands.allow = ["size"]
+
+[[permission]]
+identifier = "deny-size"
+description = "Denies the size command without any pre-configured scope."
+commands.deny = ["size"]
diff --git a/plugins/fs/permissions/autogenerated/commands/write_file.toml b/plugins/fs/permissions/autogenerated/commands/write_file.toml
index cb0450fc..ea7d5136 100644
--- a/plugins/fs/permissions/autogenerated/commands/write_file.toml
+++ b/plugins/fs/permissions/autogenerated/commands/write_file.toml
@@ -5,9 +5,19 @@
[[permission]]
identifier = "allow-write-file"
description = "Enables the write_file command without any pre-configured scope."
-commands.allow = ["write_file"]
+
+[permission.commands]
+allow = [
+ "write_file",
+ "open",
+ "write",
+]
+deny = []
[[permission]]
identifier = "deny-write-file"
description = "Denies the write_file command without any pre-configured scope."
-commands.deny = ["write_file"]
+
+[permission.commands]
+allow = []
+deny = ["write_file"]
diff --git a/plugins/fs/permissions/autogenerated/reference.md b/plugins/fs/permissions/autogenerated/reference.md
index 79ec2f55..3cec32ed 100644
--- a/plugins/fs/permissions/autogenerated/reference.md
+++ b/plugins/fs/permissions/autogenerated/reference.md
@@ -24,6 +24,7 @@ This default permission set prevents access to critical components
of the Tauri application by default.
On Windows the webview data folder access is denied.
+#### Included permissions within this default permission set:
- `create-app-specific-dirs`
@@ -3409,6 +3410,32 @@ Denies the seek command without any pre-configured scope.
+`fs:allow-size`
+
+
+
+
+Enables the size command without any pre-configured scope.
+
+
+
+
+
+
+
+`fs:deny-size`
+
+
+
+
+Denies the size command without any pre-configured scope.
+
+
+
+
+
+
+
`fs:allow-stat`
diff --git a/plugins/fs/permissions/default.toml b/plugins/fs/permissions/default.toml
index d519d758..4cc7bd51 100644
--- a/plugins/fs/permissions/default.toml
+++ b/plugins/fs/permissions/default.toml
@@ -26,6 +26,7 @@ This default permission set prevents access to critical components
of the Tauri application by default.
On Windows the webview data folder access is denied.
+#### Included permissions within this default permission set:
"""
permissions = [
"create-app-specific-dirs",
diff --git a/plugins/fs/permissions/read-meta.toml b/plugins/fs/permissions/read-meta.toml
index 09c731ad..83024b0c 100644
--- a/plugins/fs/permissions/read-meta.toml
+++ b/plugins/fs/permissions/read-meta.toml
@@ -3,4 +3,4 @@
[[permission]]
identifier = "read-meta"
description = "This enables all index or metadata related commands without any pre-configured accessible paths."
-commands.allow = ["read_dir", "stat", "lstat", "fstat", "exists"]
+commands.allow = ["read_dir", "stat", "lstat", "fstat", "exists", "size"]
diff --git a/plugins/fs/permissions/schemas/schema.json b/plugins/fs/permissions/schemas/schema.json
index 57ab0634..2c13d5c6 100644
--- a/plugins/fs/permissions/schemas/schema.json
+++ b/plugins/fs/permissions/schemas/schema.json
@@ -1589,6 +1589,16 @@
"type": "string",
"const": "deny-seek"
},
+ {
+ "description": "Enables the size command without any pre-configured scope.",
+ "type": "string",
+ "const": "allow-size"
+ },
+ {
+ "description": "Denies the size command without any pre-configured scope.",
+ "type": "string",
+ "const": "deny-size"
+ },
{
"description": "Enables the stat command without any pre-configured scope.",
"type": "string",
@@ -1665,7 +1675,7 @@
"const": "create-app-specific-dirs"
},
{
- "description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n",
+ "description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n",
"type": "string",
"const": "default"
},
diff --git a/plugins/fs/src/commands.rs b/plugins/fs/src/commands.rs
index cb40c3ee..29d1104f 100644
--- a/plugins/fs/src/commands.rs
+++ b/plugins/fs/src/commands.rs
@@ -9,20 +9,20 @@ use tauri::{
ipc::{CommandScope, GlobalScope},
path::BaseDirectory,
utils::config::FsScope,
- AppHandle, Manager, Resource, ResourceId, Runtime, Webview,
+ Manager, Resource, ResourceId, Runtime, Webview,
};
use std::{
borrow::Cow,
fs::File,
- io::{BufReader, Lines, Read, Write},
+ io::{BufRead, BufReader, Read, Write},
path::{Path, PathBuf},
str::FromStr,
sync::Mutex,
time::{SystemTime, UNIX_EPOCH},
};
-use crate::{scope::Entry, Error, FsExt, SafeFilePath};
+use crate::{scope::Entry, Error, SafeFilePath};
#[derive(Debug, thiserror::Error)]
pub enum CommandError {
@@ -245,32 +245,12 @@ pub fn mkdir(
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DirEntry {
- pub name: Option,
+ pub name: String,
pub is_directory: bool,
pub is_file: bool,
pub is_symlink: bool,
}
-fn read_dir_inner>(path: P) -> crate::Result> {
- let mut files_and_dirs: Vec = vec![];
- for entry in std::fs::read_dir(path)? {
- let path = entry?.path();
- let file_type = path.metadata()?.file_type();
- files_and_dirs.push(DirEntry {
- is_directory: file_type.is_dir(),
- is_file: file_type.is_file(),
- is_symlink: std::fs::symlink_metadata(&path)
- .map(|md| md.file_type().is_symlink())
- .unwrap_or(false),
- name: path
- .file_name()
- .map(|name| name.to_string_lossy())
- .map(|name| name.to_string()),
- });
- }
- Result::Ok(files_and_dirs)
-}
-
#[tauri::command]
pub async fn read_dir(
webview: Webview,
@@ -287,27 +267,73 @@ pub async fn read_dir(
options.as_ref().and_then(|o| o.base_dir),
)?;
- read_dir_inner(&resolved_path)
- .map_err(|e| {
- format!(
- "failed to read directory at path: {} with error: {e}",
- resolved_path.display()
- )
+ let entries = std::fs::read_dir(&resolved_path).map_err(|e| {
+ format!(
+ "failed to read directory at path: {} with error: {e}",
+ resolved_path.display()
+ )
+ })?;
+
+ let entries = entries
+ .filter_map(|entry| {
+ let entry = entry.ok()?;
+ let name = entry.file_name().into_string().ok()?;
+ let metadata = entry.file_type();
+ macro_rules! method_or_false {
+ ($method:ident) => {
+ if let Ok(metadata) = &metadata {
+ metadata.$method()
+ } else {
+ false
+ }
+ };
+ }
+ Some(DirEntry {
+ name,
+ is_file: method_or_false!(is_file),
+ is_directory: method_or_false!(is_dir),
+ is_symlink: method_or_false!(is_symlink),
+ })
})
- .map_err(Into::into)
+ .collect();
+
+ Ok(entries)
}
#[tauri::command]
pub async fn read(
webview: Webview,
rid: ResourceId,
- len: u32,
-) -> CommandResult<(Vec, usize)> {
- let mut data = vec![0; len as usize];
+ len: usize,
+) -> CommandResult {
+ let mut data = vec![0; len];
let file = webview.resources_table().get::(rid)?;
let nread = StdFileResource::with_lock(&file, |mut file| file.read(&mut data))
.map_err(|e| format!("faied to read bytes from file with error: {e}"))?;
- Ok((data, nread))
+
+ // This is an optimization to include the number of read bytes (as bigendian bytes)
+ // at the end of returned vector so we can use `tauri::ipc::Response`
+ // and avoid serialization overhead of separate values.
+ #[cfg(target_pointer_width = "16")]
+ let nread = {
+ let nread = nread.to_be_bytes();
+ let mut out = [0; 8];
+ out[6..].copy_from_slice(&nread);
+ out
+ };
+ #[cfg(target_pointer_width = "32")]
+ let nread = {
+ let nread = nread.to_be_bytes();
+ let mut out = [0; 8];
+ out[4..].copy_from_slice(&nread);
+ out
+ };
+ #[cfg(target_pointer_width = "64")]
+ let nread = nread.to_be_bytes();
+
+ data.extend(nread);
+
+ Ok(tauri::ipc::Response::new(data))
}
#[tauri::command]
@@ -346,6 +372,7 @@ pub async fn read_file(
Ok(tauri::ipc::Response::new(contents))
}
+// TODO, remove in v3, rely on `read_file` command instead
#[tauri::command]
pub async fn read_text_file(
webview: Webview,
@@ -353,33 +380,8 @@ pub async fn read_text_file(
command_scope: CommandScope,
path: SafeFilePath,
options: Option,
-) -> CommandResult {
- let (mut file, path) = resolve_file(
- &webview,
- &global_scope,
- &command_scope,
- path,
- OpenOptions {
- base: BaseOptions {
- base_dir: options.as_ref().and_then(|o| o.base_dir),
- },
- options: crate::OpenOptions {
- read: true,
- ..Default::default()
- },
- },
- )?;
-
- let mut contents = String::new();
-
- file.read_to_string(&mut contents).map_err(|e| {
- format!(
- "failed to read file as text at path: {} with error: {e}",
- path.display()
- )
- })?;
-
- Ok(contents)
+) -> CommandResult {
+ read_file(webview, global_scope, command_scope, path, options).await
}
#[tauri::command]
@@ -390,8 +392,6 @@ pub fn read_text_file_lines(
path: SafeFilePath,
options: Option,
) -> CommandResult {
- use std::io::BufRead;
-
let resolved_path = resolve_path(
&webview,
&global_scope,
@@ -407,7 +407,7 @@ pub fn read_text_file_lines(
)
})?;
- let lines = BufReader::new(file).lines();
+ let lines = BufReader::new(file);
let rid = webview.resources_table().add(StdLinesResource::new(lines));
Ok(rid)
@@ -417,18 +417,28 @@ pub fn read_text_file_lines(
pub async fn read_text_file_lines_next(
webview: Webview,
rid: ResourceId,
-) -> CommandResult<(Option, bool)> {
+) -> CommandResult {
let mut resource_table = webview.resources_table();
let lines = resource_table.get::(rid)?;
- let ret = StdLinesResource::with_lock(&lines, |lines| {
- lines.next().map(|a| (a.ok(), false)).unwrap_or_else(|| {
- let _ = resource_table.close(rid);
- (None, true)
- })
+ let ret = StdLinesResource::with_lock(&lines, |lines| -> CommandResult> {
+ // This is an optimization to include wether we finished iteration or not (1 or 0)
+ // at the end of returned vector so we can use `tauri::ipc::Response`
+ // and avoid serialization overhead of separate values.
+ match lines.next() {
+ Some(Ok(mut bytes)) => {
+ bytes.push(false as u8);
+ Ok(bytes)
+ }
+ Some(Err(_)) => Ok(vec![false as u8]),
+ None => {
+ resource_table.close(rid)?;
+ Ok(vec![true as u8])
+ }
+ }
});
- Ok(ret)
+ ret.map(tauri::ipc::Response::new)
}
#[derive(Debug, Clone, Deserialize)]
@@ -779,18 +789,43 @@ fn default_create_value() -> bool {
true
}
-fn write_file_inner(
+#[tauri::command]
+pub async fn write_file(
webview: Webview,
- global_scope: &GlobalScope,
- command_scope: &CommandScope,
- path: SafeFilePath,
- data: &[u8],
- options: Option,
+ global_scope: GlobalScope,
+ command_scope: CommandScope,
+ request: tauri::ipc::Request<'_>,
) -> CommandResult<()> {
+ let data = match request.body() {
+ tauri::ipc::InvokeBody::Raw(data) => Cow::Borrowed(data),
+ tauri::ipc::InvokeBody::Json(serde_json::Value::Array(data)) => Cow::Owned(
+ data.iter()
+ .flat_map(|v| v.as_number().and_then(|v| v.as_u64().map(|v| v as u8)))
+ .collect(),
+ ),
+ _ => return Err(anyhow::anyhow!("unexpected invoke body").into()),
+ };
+
+ let path = request
+ .headers()
+ .get("path")
+ .ok_or_else(|| anyhow::anyhow!("missing file path").into())
+ .and_then(|p| {
+ percent_encoding::percent_decode(p.as_ref())
+ .decode_utf8()
+ .map_err(|_| anyhow::anyhow!("path is not a valid UTF-8").into())
+ })
+ .and_then(|p| SafeFilePath::from_str(&p).map_err(CommandError::from))?;
+ let options: Option = request
+ .headers()
+ .get("options")
+ .and_then(|p| p.to_str().ok())
+ .and_then(|opts| serde_json::from_str(opts).ok());
+
let (mut file, path) = resolve_file(
&webview,
- global_scope,
- command_scope,
+ &global_scope,
+ &command_scope,
path,
if let Some(opts) = options {
OpenOptions {
@@ -823,7 +858,7 @@ fn write_file_inner(
},
)?;
- file.write_all(data)
+ file.write_all(&data)
.map_err(|e| {
format!(
"failed to write bytes to file at path: {} with error: {e}",
@@ -833,69 +868,43 @@ fn write_file_inner(
.map_err(Into::into)
}
+// TODO, remove in v3, rely on `write_file` command instead
#[tauri::command]
-pub async fn write_file(
+pub async fn write_text_file(
webview: Webview,
global_scope: GlobalScope,
command_scope: CommandScope,
request: tauri::ipc::Request<'_>,
) -> CommandResult<()> {
- let data = match request.body() {
- tauri::ipc::InvokeBody::Raw(data) => Cow::Borrowed(data),
- tauri::ipc::InvokeBody::Json(serde_json::Value::Array(data)) => Cow::Owned(
- data.iter()
- .flat_map(|v| v.as_number().and_then(|v| v.as_u64().map(|v| v as u8)))
- .collect(),
- ),
- _ => return Err(anyhow::anyhow!("unexpected invoke body").into()),
- };
-
- let path = request
- .headers()
- .get("path")
- .ok_or_else(|| anyhow::anyhow!("missing file path").into())
- .and_then(|p| {
- percent_encoding::percent_decode(p.as_ref())
- .decode_utf8()
- .map_err(|_| anyhow::anyhow!("path is not a valid UTF-8").into())
- })
- .and_then(|p| SafeFilePath::from_str(&p).map_err(CommandError::from))?;
- let options = request
- .headers()
- .get("options")
- .and_then(|p| p.to_str().ok())
- .and_then(|opts| serde_json::from_str(opts).ok());
- write_file_inner(webview, &global_scope, &command_scope, path, &data, options)
+ write_file(webview, global_scope, command_scope, request).await
}
#[tauri::command]
-pub async fn write_text_file(
- #[allow(unused)] app: AppHandle,
- #[allow(unused)] webview: Webview,
- #[allow(unused)] global_scope: GlobalScope,
- #[allow(unused)] command_scope: CommandScope,
+pub fn exists(
+ webview: Webview,
+ global_scope: GlobalScope,
+ command_scope: CommandScope,
path: SafeFilePath,
- data: String,
- #[allow(unused)] options: Option,
-) -> CommandResult<()> {
- write_file_inner(
- webview,
+ options: Option,
+) -> CommandResult {
+ let resolved_path = resolve_path(
+ &webview,
&global_scope,
&command_scope,
path,
- data.as_bytes(),
- options,
- )
+ options.as_ref().and_then(|o| o.base_dir),
+ )?;
+ Ok(resolved_path.exists())
}
#[tauri::command]
-pub fn exists(
+pub async fn size(
webview: Webview,
global_scope: GlobalScope,
command_scope: CommandScope,
path: SafeFilePath,
options: Option,
-) -> CommandResult {
+) -> CommandResult {
let resolved_path = resolve_path(
&webview,
&global_scope,
@@ -903,7 +912,38 @@ pub fn exists(
path,
options.as_ref().and_then(|o| o.base_dir),
)?;
- Ok(resolved_path.exists())
+
+ let metadata = resolved_path.metadata()?;
+
+ if metadata.is_file() {
+ Ok(metadata.len())
+ } else {
+ let size = get_dir_size(&resolved_path).map_err(|e| {
+ format!(
+ "failed to get size at path: {} with error: {e}",
+ resolved_path.display()
+ )
+ })?;
+
+ Ok(size)
+ }
+}
+
+fn get_dir_size(path: &PathBuf) -> CommandResult {
+ let mut size = 0;
+
+ for entry in std::fs::read_dir(path)? {
+ let entry = entry?;
+ let metadata = entry.metadata()?;
+
+ if metadata.is_file() {
+ size += metadata.len();
+ } else if metadata.is_dir() {
+ size += get_dir_size(&entry.path())?;
+ }
+ }
+
+ Ok(size)
}
#[cfg(not(target_os = "android"))]
@@ -951,6 +991,8 @@ pub fn resolve_file(
path: SafeFilePath,
open_options: OpenOptions,
) -> CommandResult<(File, PathBuf)> {
+ use crate::FsExt;
+
match path {
SafeFilePath::Url(url) => {
let path = url.as_str().into();
@@ -983,40 +1025,81 @@ pub fn resolve_path(
path
};
+ let fs_scope = webview.state::();
+
let scope = tauri::scope::fs::Scope::new(
webview,
&FsScope::Scope {
- allow: webview
- .fs_scope()
- .allowed
- .lock()
- .unwrap()
- .clone()
- .into_iter()
- .chain(global_scope.allows().iter().filter_map(|e| e.path.clone()))
+ allow: global_scope
+ .allows()
+ .iter()
+ .filter_map(|e| e.path.clone())
.chain(command_scope.allows().iter().filter_map(|e| e.path.clone()))
.collect(),
- deny: webview
- .fs_scope()
- .denied
- .lock()
- .unwrap()
- .clone()
- .into_iter()
- .chain(global_scope.denies().iter().filter_map(|e| e.path.clone()))
+ deny: global_scope
+ .denies()
+ .iter()
+ .filter_map(|e| e.path.clone())
.chain(command_scope.denies().iter().filter_map(|e| e.path.clone()))
.collect(),
- require_literal_leading_dot: webview.fs_scope().require_literal_leading_dot,
+ require_literal_leading_dot: fs_scope.require_literal_leading_dot,
},
)?;
- if scope.is_allowed(&path) {
+ let require_literal_leading_dot = fs_scope.require_literal_leading_dot.unwrap_or(cfg!(unix));
+
+ if is_forbidden(&fs_scope.scope, &path, require_literal_leading_dot)
+ || is_forbidden(&scope, &path, require_literal_leading_dot)
+ {
+ return Err(CommandError::Plugin(Error::PathForbidden(path)));
+ }
+
+ if fs_scope.scope.is_allowed(&path) || scope.is_allowed(&path) {
Ok(path)
} else {
Err(CommandError::Plugin(Error::PathForbidden(path)))
}
}
+fn is_forbidden>(
+ scope: &tauri::fs::Scope,
+ path: P,
+ require_literal_leading_dot: bool,
+) -> bool {
+ let path = path.as_ref();
+ let path = if path.is_symlink() {
+ match std::fs::read_link(path) {
+ Ok(p) => p,
+ Err(_) => return false,
+ }
+ } else {
+ path.to_path_buf()
+ };
+ let path = if !path.exists() {
+ crate::Result::Ok(path)
+ } else {
+ std::fs::canonicalize(path).map_err(Into::into)
+ };
+
+ if let Ok(path) = path {
+ let path: PathBuf = path.components().collect();
+ scope.forbidden_patterns().iter().any(|p| {
+ p.matches_path_with(
+ &path,
+ glob::MatchOptions {
+ // this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt`
+ // see:
+ require_literal_separator: true,
+ require_literal_leading_dot,
+ ..Default::default()
+ },
+ )
+ })
+ } else {
+ false
+ }
+}
+
struct StdFileResource(Mutex);
impl StdFileResource {
@@ -1032,14 +1115,38 @@ impl StdFileResource {
impl Resource for StdFileResource {}
-struct StdLinesResource(Mutex>>);
+/// Same as [std::io::Lines] but with bytes
+struct LinesBytes(T);
+
+impl Iterator for LinesBytes {
+ type Item = std::io::Result>;
+
+ fn next(&mut self) -> Option>> {
+ let mut buf = Vec::new();
+ match self.0.read_until(b'\n', &mut buf) {
+ Ok(0) => None,
+ Ok(_n) => {
+ if buf.last() == Some(&b'\n') {
+ buf.pop();
+ if buf.last() == Some(&b'\r') {
+ buf.pop();
+ }
+ }
+ Some(Ok(buf))
+ }
+ Err(e) => Some(Err(e)),
+ }
+ }
+}
+
+struct StdLinesResource(Mutex>>);
impl StdLinesResource {
- fn new(lines: Lines>) -> Self {
- Self(Mutex::new(lines))
+ fn new(lines: BufReader) -> Self {
+ Self(Mutex::new(LinesBytes(lines)))
}
- fn with_lock>) -> R>(&self, mut f: F) -> R {
+ fn with_lock>) -> R>(&self, mut f: F) -> R {
let mut lines = self.0.lock().unwrap();
f(&mut lines)
}
@@ -1138,7 +1245,12 @@ fn get_stat(metadata: std::fs::Metadata) -> FileInfo {
}
}
+#[cfg(test)]
mod test {
+ use std::io::{BufRead, BufReader};
+
+ use super::LinesBytes;
+
#[test]
fn safe_file_path_parse() {
use super::SafeFilePath;
@@ -1152,4 +1264,24 @@ mod test {
Ok(SafeFilePath::Url(_))
));
}
+
+ #[test]
+ fn test_lines_bytes() {
+ let base = String::from("line 1\nline2\nline 3\nline 4");
+ let bytes = base.as_bytes();
+
+ let string1 = base.lines().collect::();
+ let string2 = BufReader::new(bytes)
+ .lines()
+ .map_while(Result::ok)
+ .collect::();
+ let string3 = LinesBytes(BufReader::new(bytes))
+ .flatten()
+ .flat_map(String::from_utf8)
+ .collect::();
+
+ assert_eq!(string1, string2);
+ assert_eq!(string1, string3);
+ assert_eq!(string2, string3);
+ }
}
diff --git a/plugins/fs/src/file_path.rs b/plugins/fs/src/file_path.rs
index 9ff7a947..735fc105 100644
--- a/plugins/fs/src/file_path.rs
+++ b/plugins/fs/src/file_path.rs
@@ -138,7 +138,7 @@ impl<'de> serde::Deserialize<'de> for FilePath {
{
struct FilePathVisitor;
- impl<'de> serde::de::Visitor<'de> for FilePathVisitor {
+ impl serde::de::Visitor<'_> for FilePathVisitor {
type Value = FilePath;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
@@ -169,7 +169,7 @@ impl<'de> serde::Deserialize<'de> for SafeFilePath {
{
struct SafeFilePathVisitor;
- impl<'de> serde::de::Visitor<'de> for SafeFilePathVisitor {
+ impl serde::de::Visitor<'_> for SafeFilePathVisitor {
type Value = SafeFilePath;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
diff --git a/plugins/fs/src/lib.rs b/plugins/fs/src/lib.rs
index a1cf2766..731c7040 100644
--- a/plugins/fs/src/lib.rs
+++ b/plugins/fs/src/lib.rs
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-//! [](https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/fs)
-//!
//! Access the file system.
#![doc(
@@ -17,7 +15,7 @@ use serde::Deserialize;
use tauri::{
ipc::ScopeObject,
plugin::{Builder as PluginBuilder, TauriPlugin},
- utils::acl::Value,
+ utils::{acl::Value, config::FsScope},
AppHandle, DragDropEvent, Manager, RunEvent, Runtime, WindowEvent,
};
@@ -41,7 +39,6 @@ pub use desktop::Fs;
pub use mobile::Fs;
pub use error::Error;
-pub use scope::{Event as ScopeEvent, Scope};
pub use file_path::FilePath;
pub use file_path::SafeFilePath;
@@ -367,21 +364,26 @@ impl ScopeObject for scope::Entry {
}
}
+pub(crate) struct Scope {
+ pub(crate) scope: tauri::fs::Scope,
+ pub(crate) require_literal_leading_dot: Option,
+}
+
pub trait FsExt {
- fn fs_scope(&self) -> &Scope;
- fn try_fs_scope(&self) -> Option<&Scope>;
+ fn fs_scope(&self) -> tauri::fs::Scope;
+ fn try_fs_scope(&self) -> Option;
/// Cross platform file system APIs that also support manipulating Android files.
fn fs(&self) -> &Fs;
}
impl> FsExt for T {
- fn fs_scope(&self) -> &Scope {
- self.state::().inner()
+ fn fs_scope(&self) -> tauri::fs::Scope {
+ self.state::().scope.clone()
}
- fn try_fs_scope(&self) -> Option<&Scope> {
- self.try_state::().map(|s| s.inner())
+ fn try_fs_scope(&self) -> Option {
+ self.try_state::().map(|s| s.scope.clone())
}
fn fs(&self) -> &Fs {
@@ -415,17 +417,20 @@ pub fn init() -> TauriPlugin> {
commands::write_file,
commands::write_text_file,
commands::exists,
+ commands::size,
#[cfg(feature = "watch")]
watcher::watch,
#[cfg(feature = "watch")]
watcher::unwatch
])
.setup(|app, api| {
- let mut scope = Scope::default();
- scope.require_literal_leading_dot = api
- .config()
- .as_ref()
- .and_then(|c| c.require_literal_leading_dot);
+ let scope = Scope {
+ require_literal_leading_dot: api
+ .config()
+ .as_ref()
+ .and_then(|c| c.require_literal_leading_dot),
+ scope: tauri::fs::Scope::new(app, &FsScope::default())?,
+ };
#[cfg(target_os = "android")]
{
@@ -448,9 +453,9 @@ pub fn init() -> TauriPlugin> {
let scope = app.fs_scope();
for path in paths {
if path.is_file() {
- scope.allow_file(path);
+ let _ = scope.allow_file(path);
} else {
- scope.allow_directory(path, true);
+ let _ = scope.allow_directory(path, true);
}
}
}
diff --git a/plugins/fs/src/scope.rs b/plugins/fs/src/scope.rs
index e8361d51..7914706a 100644
--- a/plugins/fs/src/scope.rs
+++ b/plugins/fs/src/scope.rs
@@ -2,130 +2,18 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-use std::{
- collections::HashMap,
- path::{Path, PathBuf},
- sync::{
- atomic::{AtomicU32, Ordering},
- Mutex,
- },
-};
+use std::path::PathBuf;
use serde::Deserialize;
-#[derive(Deserialize)]
-#[serde(untagged)]
-pub(crate) enum EntryRaw {
- Value(PathBuf),
- Object { path: PathBuf },
-}
-
#[derive(Debug)]
pub struct Entry {
pub path: Option,
}
-pub type EventId = u32;
-type EventListener = Box;
-
-/// Scope change event.
-#[derive(Debug, Clone)]
-pub enum Event {
- /// A path has been allowed.
- PathAllowed(PathBuf),
- /// A path has been forbidden.
- PathForbidden(PathBuf),
-}
-
-#[derive(Default)]
-pub struct Scope {
- pub(crate) allowed: Mutex>,
- pub(crate) denied: Mutex>,
- event_listeners: Mutex>,
- next_event_id: AtomicU32,
- pub(crate) require_literal_leading_dot: Option,
-}
-
-impl Scope {
- /// Extend the allowed patterns with the given directory.
- ///
- /// After this function has been called, the frontend will be able to use the Tauri API to read
- /// the directory and all of its files. If `recursive` is `true`, subdirectories will be accessible too.
- pub fn allow_directory>(&self, path: P, recursive: bool) {
- let path = path.as_ref();
-
- {
- let mut allowed = self.allowed.lock().unwrap();
- allowed.push(path.to_path_buf());
- allowed.push(path.join(if recursive { "**" } else { "*" }));
- }
-
- self.emit(Event::PathAllowed(path.to_path_buf()));
- }
-
- /// Extend the allowed patterns with the given file path.
- ///
- /// After this function has been called, the frontend will be able to use the Tauri API to read the contents of this file.
- pub fn allow_file>(&self, path: P) {
- let path = path.as_ref();
-
- self.allowed.lock().unwrap().push(path.to_path_buf());
-
- self.emit(Event::PathAllowed(path.to_path_buf()));
- }
-
- /// Set the given directory path to be forbidden by this scope.
- ///
- /// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
- pub fn forbid_directory>(&self, path: P, recursive: bool) {
- let path = path.as_ref();
-
- {
- let mut denied = self.denied.lock().unwrap();
- denied.push(path.to_path_buf());
- denied.push(path.join(if recursive { "**" } else { "*" }));
- }
-
- self.emit(Event::PathForbidden(path.to_path_buf()));
- }
-
- /// Set the given file path to be forbidden by this scope.
- ///
- /// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
- pub fn forbid_file>(&self, path: P) {
- let path = path.as_ref();
-
- self.denied.lock().unwrap().push(path.to_path_buf());
-
- self.emit(Event::PathForbidden(path.to_path_buf()));
- }
-
- /// List of allowed paths.
- pub fn allowed(&self) -> Vec {
- self.allowed.lock().unwrap().clone()
- }
-
- /// List of forbidden paths.
- pub fn forbidden(&self) -> Vec {
- self.denied.lock().unwrap().clone()
- }
-
- fn next_event_id(&self) -> u32 {
- self.next_event_id.fetch_add(1, Ordering::Relaxed)
- }
-
- fn emit(&self, event: Event) {
- let listeners = self.event_listeners.lock().unwrap();
- let handlers = listeners.values();
- for listener in handlers {
- listener(&event);
- }
- }
-
- /// Listen to an event on this scope.
- pub fn listen(&self, f: F) -> EventId {
- let id = self.next_event_id();
- self.event_listeners.lock().unwrap().insert(id, Box::new(f));
- id
- }
+#[derive(Deserialize)]
+#[serde(untagged)]
+pub(crate) enum EntryRaw {
+ Value(PathBuf),
+ Object { path: PathBuf },
}
diff --git a/plugins/fs/src/watcher.rs b/plugins/fs/src/watcher.rs
index cf2af503..7d851822 100644
--- a/plugins/fs/src/watcher.rs
+++ b/plugins/fs/src/watcher.rs
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
-use notify_debouncer_full::{new_debouncer, DebounceEventResult, Debouncer, FileIdMap};
+use notify_debouncer_full::{new_debouncer, DebounceEventResult, Debouncer, RecommendedCache};
use serde::Deserialize;
use tauri::{
ipc::{Channel, CommandScope, GlobalScope},
@@ -47,7 +47,7 @@ impl WatcherResource {
impl Resource for WatcherResource {}
enum WatcherKind {
- Debouncer(Debouncer),
+ Debouncer(Debouncer),
Watcher(RecommendedWatcher),
}
@@ -111,8 +111,7 @@ pub async fn watch(
let (tx, rx) = channel();
let mut debouncer = new_debouncer(Duration::from_millis(delay), None, tx)?;
for path in &resolved_paths {
- debouncer.watcher().watch(path.as_ref(), recursive_mode)?;
- debouncer.cache().add_root(path, recursive_mode);
+ debouncer.watch(path, recursive_mode)?;
}
watch_debounced(on_event, rx);
WatcherKind::Debouncer(debouncer)
@@ -120,7 +119,7 @@ pub async fn watch(
let (tx, rx) = channel();
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
for path in &resolved_paths {
- watcher.watch(path.as_ref(), recursive_mode)?;
+ watcher.watch(path, recursive_mode)?;
}
watch_raw(on_event, rx);
WatcherKind::Watcher(watcher)
@@ -140,14 +139,14 @@ pub async fn unwatch(webview: Webview, rid: ResourceId) -> Comman
match &mut watcher.kind {
WatcherKind::Debouncer(ref mut debouncer) => {
for path in &watcher.paths {
- debouncer.watcher().unwatch(path.as_ref()).map_err(|e| {
+ debouncer.unwatch(path).map_err(|e| {
format!("failed to unwatch path: {} with error: {e}", path.display())
})?;
}
}
WatcherKind::Watcher(ref mut w) => {
for path in &watcher.paths {
- w.unwatch(path.as_ref()).map_err(|e| {
+ w.unwatch(path).map_err(|e| {
format!("failed to unwatch path: {} with error: {e}", path.display())
})?;
}
diff --git a/plugins/geolocation/CHANGELOG.md b/plugins/geolocation/CHANGELOG.md
index 32952ac7..26bd66b6 100644
--- a/plugins/geolocation/CHANGELOG.md
+++ b/plugins/geolocation/CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog
+## \[2.2.3]
+
+- [`406e6f48`](https://github.com/tauri-apps/plugins-workspace/commit/406e6f484cdc13d35c50fb949f7489ca9eeccc44) ([#2323](https://github.com/tauri-apps/plugins-workspace/pull/2323) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused build failures when the `haptics` or `geolocation` plugin was used without their `specta` feature flag enabled.
+
+## \[2.2.2]
+
+- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) **Breaking change:** `specta` integration is now behind a `specta` feature flag like in Tauri.
+- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Unlock and widen `specta` version range to match Tauri. No API changes.
+
+## \[2.2.1]
+
+- [`fb67ab2b`](https://github.com/tauri-apps/plugins-workspace/commit/fb67ab2b926502bfc20d6b43fbdd156691ea8526) ([#2281](https://github.com/tauri-apps/plugins-workspace/pull/2281) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Added `specta-util` to fix a "dependency not found" compilation error.
+
+## \[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.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
diff --git a/plugins/geolocation/Cargo.toml b/plugins/geolocation/Cargo.toml
index 550a66cb..3fc3baaf 100644
--- a/plugins/geolocation/Cargo.toml
+++ b/plugins/geolocation/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "tauri-plugin-geolocation"
description = "Get and track the device's current position"
-version = "2.0.1"
+version = "2.2.3"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
@@ -26,10 +26,13 @@ tauri-plugin = { workspace = true, features = ["build"] }
[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
-tauri = { workspace = true, features = ["specta"] }
+tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
-specta = { workspace = true }
+specta = { workspace = true, optional = true }
[target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] }
+
+[features]
+specta = ["dep:specta", "tauri/specta"]
diff --git a/plugins/geolocation/README.md b/plugins/geolocation/README.md
index 0557cc98..bf8e1741 100644
--- a/plugins/geolocation/README.md
+++ b/plugins/geolocation/README.md
@@ -81,7 +81,7 @@ The Google Play Store uses this property to decide whether it should show the ap
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
@@ -92,6 +92,20 @@ fn main() {
}
```
+Then, for instance, grant the plugin the permission to check or request permissions from the user and to read the device position
+
+`src-tauri/capabilities/default.json`
+
+```json
+ "permissions": [
+ "core:default",
+ "geolocation:allow-check-permissions",
+ "geolocation:allow-request-permissions",
+ "geolocation:allow-get-current-position",
+ "geolocation:allow-watch-position",
+ ]
+```
+
Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
```javascript
@@ -100,7 +114,7 @@ import {
requestPermissions,
getCurrentPosition,
watchPosition
-} from '@tauri-apps/plugin-log'
+} from '@tauri-apps/plugin-geolocation'
let permissions = await checkPermissions()
if (
diff --git a/plugins/geolocation/api-iife.js b/plugins/geolocation/api-iife.js
index 912c073e..34316795 100644
--- a/plugins/geolocation/api-iife.js
+++ b/plugins/geolocation/api-iife.js
@@ -1 +1 @@
-if("__TAURI__"in window){var __TAURI_PLUGIN_GEOLOCATION__=function(t){"use strict";function e(t,e,n,i){if("a"===n&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?i:"a"===n?i.call(t):i?i.value:e.get(t)}function n(t,e,n,i,o){if("function"==typeof e?t!==e||!o:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,n),n}var i,o,s;"function"==typeof SuppressedError&&SuppressedError;class r{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),o.set(this,0),s.set(this,{}),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:r})=>{if(r===e(this,o,"f")){n(this,o,r+1),e(this,i,"f").call(this,t);const a=Object.keys(e(this,s,"f"));if(a.length>0){let t=r+1;for(const n of a.sort()){if(parseInt(n)!==t)break;{const o=e(this,s,"f")[n];delete e(this,s,"f")[n],e(this,i,"f").call(this,o),t+=1}}n(this,o,t)}}else e(this,s,"f")[r.toString()]=t}))}set onmessage(t){n(this,i,t)}get onmessage(){return e(this,i,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}async function a(t,e={},n){return window.__TAURI_INTERNALS__.invoke(t,e,n)}return i=new WeakMap,o=new WeakMap,s=new WeakMap,t.checkPermissions=async function(){return await async function(t){return a(`plugin:${t}|check_permissions`)}("geolocation")},t.clearWatch=async function(t){await a("plugin:geolocation|clear_watch",{channelId:t})},t.getCurrentPosition=async function(t){return await a("plugin:geolocation|get_current_position",{options:t})},t.requestPermissions=async function(t){return await a("plugin:geolocation|request_permissions",{permissions:t})},t.watchPosition=async function(t,e){const n=new r;return n.onmessage=t=>{"string"==typeof t?e(null,t):e(t)},await a("plugin:geolocation|watch_position",{options:t,channel:n}),n.id},t}({});Object.defineProperty(window.__TAURI__,"geolocation",{value:__TAURI_PLUGIN_GEOLOCATION__})}
+if("__TAURI__"in window){var __TAURI_PLUGIN_GEOLOCATION__=function(t){"use strict";function n(t,n,e,i){if("a"===e&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof n?t!==n||!i:!n.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===e?i:"a"===e?i.call(t):i?i.value:n.get(t)}function e(t,n,e,i,s){if("function"==typeof n||!n.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return n.set(t,e),e}var i,s,o;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),s.set(this,0),o.set(this,[]),this.id=function(t,n=!1){return window.__TAURI_INTERNALS__.transformCallback(t,n)}((({message:t,id:r})=>{if(r==n(this,s,"f"))for(n(this,i,"f").call(this,t),e(this,s,n(this,s,"f")+1);n(this,s,"f")in n(this,o,"f");){const t=n(this,o,"f")[n(this,s,"f")];n(this,i,"f").call(this,t),delete n(this,o,"f")[n(this,s,"f")],e(this,s,n(this,s,"f")+1)}else n(this,o,"f")[r]=t}))}set onmessage(t){e(this,i,t)}get onmessage(){return n(this,i,"f")}[(i=new WeakMap,s=new WeakMap,o=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}async function c(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}return t.checkPermissions=async function(){return await async function(t){return c(`plugin:${t}|check_permissions`)}("geolocation")},t.clearWatch=async function(t){await c("plugin:geolocation|clear_watch",{channelId:t})},t.getCurrentPosition=async function(t){return await c("plugin:geolocation|get_current_position",{options:t})},t.requestPermissions=async function(t){return await c("plugin:geolocation|request_permissions",{permissions:t})},t.watchPosition=async function(t,n){const e=new a;return e.onmessage=t=>{"string"==typeof t?n(null,t):n(t)},await c("plugin:geolocation|watch_position",{options:t,channel:e}),e.id},t}({});Object.defineProperty(window.__TAURI__,"geolocation",{value:__TAURI_PLUGIN_GEOLOCATION__})}
diff --git a/plugins/geolocation/package.json b/plugins/geolocation/package.json
index 998d2456..c52b7e08 100644
--- a/plugins/geolocation/package.json
+++ b/plugins/geolocation/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-geolocation",
- "version": "2.0.0",
+ "version": "2.2.3",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
diff --git a/plugins/geolocation/src/commands.rs b/plugins/geolocation/src/commands.rs
index 9f9b7aa0..d2cae848 100644
--- a/plugins/geolocation/src/commands.rs
+++ b/plugins/geolocation/src/commands.rs
@@ -7,7 +7,6 @@ use tauri::{command, ipc::Channel, AppHandle, Runtime};
use crate::{GeolocationExt, PermissionStatus, PermissionType, Position, PositionOptions, Result};
#[command]
-#[specta::specta]
pub(crate) async fn get_current_position(
app: AppHandle,
options: Option,
@@ -16,7 +15,6 @@ pub(crate) async fn get_current_position(
}
#[command]
-#[specta::specta]
pub(crate) async fn watch_position(
app: AppHandle,
options: PositionOptions,
@@ -26,19 +24,16 @@ pub(crate) async fn watch_position(
}
#[command]
-#[specta::specta]
pub(crate) async fn clear_watch(app: AppHandle, channel_id: u32) -> Result<()> {
app.geolocation().clear_watch(channel_id)
}
#[command]
-#[specta::specta]
pub(crate) async fn check_permissions(app: AppHandle) -> Result {
app.geolocation().check_permissions()
}
#[command]
-#[specta::specta]
pub(crate) async fn request_permissions(
app: AppHandle,
permissions: Option>,
diff --git a/plugins/geolocation/src/error.rs b/plugins/geolocation/src/error.rs
index 30ff7f44..0fba5445 100644
--- a/plugins/geolocation/src/error.rs
+++ b/plugins/geolocation/src/error.rs
@@ -3,18 +3,18 @@
// SPDX-License-Identifier: MIT
use serde::{ser::Serializer, Serialize};
-use specta::Type;
pub type Result = std::result::Result;
// TODO: Improve Error handling (different typed errors instead of one (stringified) PluginInvokeError for all mobile errors)
-#[derive(Debug, thiserror::Error, Type)]
+#[derive(Debug, thiserror::Error)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
pub enum Error {
#[cfg(mobile)]
#[error(transparent)]
PluginInvoke(
- #[serde(skip)]
+ #[cfg_attr(feature = "specta", serde(skip))]
#[from]
tauri::plugin::mobile::PluginInvokeError,
),
diff --git a/plugins/geolocation/src/lib.rs b/plugins/geolocation/src/lib.rs
index eed4a475..55f50aa0 100644
--- a/plugins/geolocation/src/lib.rs
+++ b/plugins/geolocation/src/lib.rs
@@ -7,8 +7,6 @@ use tauri::{
Manager, Runtime,
};
-//use tauri_specta::*;
-
pub use models::*;
#[cfg(desktop)]
@@ -27,24 +25,6 @@ use desktop::Geolocation;
#[cfg(mobile)]
use mobile::Geolocation;
-/* macro_rules! specta_builder {
- () => {
- ts::builder()
- .commands(collect_commands![
- commands::get_current_position,
- commands::watch_position,
- commands::clear_watch,
- commands::check_permissions,
- commands::request_permissions
- ])
- .header("// @ts-nocheck")
- .config(
- specta::ts::ExportConfig::default()
- .bigint(specta::ts::BigIntExportBehavior::Number),
- )
- };
-} */
-
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the geolocation APIs.
pub trait GeolocationExt {
fn geolocation(&self) -> &Geolocation;
@@ -58,9 +38,6 @@ impl> crate::GeolocationExt for T {
/// Initializes the plugin.
pub fn init() -> TauriPlugin {
- /* let (invoke_handler, register_events) =
- specta_builder!().build_plugin_utils("geolocation").unwrap(); */
-
Builder::new("geolocation")
.invoke_handler(tauri::generate_handler![
commands::get_current_position,
@@ -79,22 +56,3 @@ pub fn init() -> TauriPlugin {
})
.build()
}
-
-/* #[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn export_types() {
- specta_builder!()
- .path("./guest-js/bindings.ts")
- .config(
- specta::ts::ExportConfig::default()
- .formatter(specta::ts::formatter::prettier)
- .bigint(specta::ts::BigIntExportBehavior::Number),
- )
- .export_for_plugin("geolocation")
- .expect("failed to export specta types");
- }
-}
- */
diff --git a/plugins/geolocation/src/models.rs b/plugins/geolocation/src/models.rs
index 165c036f..c08bb27a 100644
--- a/plugins/geolocation/src/models.rs
+++ b/plugins/geolocation/src/models.rs
@@ -3,10 +3,10 @@
// SPDX-License-Identifier: MIT
use serde::{Deserialize, Serialize};
-use specta::Type;
use tauri::plugin::PermissionState;
-#[derive(Debug, Clone, Default, Serialize, Deserialize, Type)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub struct PermissionStatus {
/// Permission state for the location alias.
@@ -25,7 +25,8 @@ pub struct PermissionStatus {
pub coarse_location: PermissionState,
}
-#[derive(Debug, Clone, Default, Serialize, Deserialize, Type)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub struct PositionOptions {
/// High accuracy mode (such as GPS, if available)
@@ -46,14 +47,16 @@ pub struct PositionOptions {
pub maximum_age: u32,
}
-#[derive(Debug, Clone, Serialize, Deserialize, Type)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub enum PermissionType {
Location,
CoarseLocation,
}
-#[derive(Debug, Clone, Default, Serialize, Deserialize, Type)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub struct Coordinates {
/// Latitude in decimal degrees.
@@ -73,7 +76,8 @@ pub struct Coordinates {
pub heading: Option,
}
-#[derive(Debug, Clone, Default, Serialize, Deserialize, Type)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub struct Position {
/// Creation time for these coordinates.
@@ -83,7 +87,8 @@ pub struct Position {
pub coords: Coordinates,
}
-#[derive(Debug, Clone, Serialize, Deserialize, Type)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(untagged)]
pub enum WatchEvent {
Position(Position),
diff --git a/plugins/global-shortcut/CHANGELOG.md b/plugins/global-shortcut/CHANGELOG.md
index 5cd07c7d..32f8748d 100644
--- a/plugins/global-shortcut/CHANGELOG.md
+++ b/plugins/global-shortcut/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## \[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.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@@ -104,22 +108,3 @@
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- te to alpha.11.
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- \-apps/plugins-workspace/pull/371)) First v2 alpha release!
- te to alpha.11.
-
-## \[2.0.0-alpha.0]
-
-- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- ]\(https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- om/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- st v2 alpha release!
- ]\(https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- om/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
diff --git a/plugins/global-shortcut/Cargo.toml b/plugins/global-shortcut/Cargo.toml
index a26be3fb..a9ff1fe0 100644
--- a/plugins/global-shortcut/Cargo.toml
+++ b/plugins/global-shortcut/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-global-shortcut"
-version = "2.0.1"
+version = "2.2.0"
description = "Register global hotkeys listeners on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
diff --git a/plugins/global-shortcut/README.md b/plugins/global-shortcut/README.md
index 12a9b08d..53e7b19f 100644
--- a/plugins/global-shortcut/README.md
+++ b/plugins/global-shortcut/README.md
@@ -55,7 +55,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-global-shortcut#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
diff --git a/plugins/global-shortcut/api-iife.js b/plugins/global-shortcut/api-iife.js
index 67ec9e47..b172d68d 100644
--- a/plugins/global-shortcut/api-iife.js
+++ b/plugins/global-shortcut/api-iife.js
@@ -1 +1 @@
-if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBAL_SHORTCUT__=function(t){"use strict";function e(t,e,r,s){if("a"===r&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function r(t,e,r,s,n){if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,r),r}var s,n,i;"function"==typeof SuppressedError&&SuppressedError;class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,s.set(this,(()=>{})),n.set(this,0),i.set(this,{}),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:o})=>{if(o===e(this,n,"f")){r(this,n,o+1),e(this,s,"f").call(this,t);const a=Object.keys(e(this,i,"f"));if(a.length>0){let t=o+1;for(const r of a.sort()){if(parseInt(r)!==t)break;{const n=e(this,i,"f")[r];delete e(this,i,"f")[r],e(this,s,"f").call(this,n),t+=1}}r(this,n,t)}}else e(this,i,"f")[o.toString()]=t}))}set onmessage(t){r(this,s,t)}get onmessage(){return e(this,s,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}async function a(t,e={},r){return window.__TAURI_INTERNALS__.invoke(t,e,r)}return s=new WeakMap,n=new WeakMap,i=new WeakMap,t.isRegistered=async function(t){return await a("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const r=new o;return r.onmessage=e,await a("plugin:global-shortcut|register",{shortcuts:Array.isArray(t)?t:[t],handler:r})},t.unregister=async function(t){return await a("plugin:global-shortcut|unregister",{shortcuts:Array.isArray(t)?t:[t]})},t.unregisterAll=async function(){return await a("plugin:global-shortcut|unregister_all",{})},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBAL_SHORTCUT__})}
+if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBAL_SHORTCUT__=function(t){"use strict";function e(t,e,r,s){if("a"===r&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function r(t,e,r,s,i){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,r),r}var s,i,n;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,s.set(this,(()=>{})),i.set(this,0),n.set(this,[]),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:o})=>{if(o==e(this,i,"f"))for(e(this,s,"f").call(this,t),r(this,i,e(this,i,"f")+1);e(this,i,"f")in e(this,n,"f");){const t=e(this,n,"f")[e(this,i,"f")];e(this,s,"f").call(this,t),delete e(this,n,"f")[e(this,i,"f")],r(this,i,e(this,i,"f")+1)}else e(this,n,"f")[o]=t}))}set onmessage(t){r(this,s,t)}get onmessage(){return e(this,s,"f")}[(s=new WeakMap,i=new WeakMap,n=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function _(t,e={},r){return window.__TAURI_INTERNALS__.invoke(t,e,r)}return t.isRegistered=async function(t){return await _("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const r=new a;return r.onmessage=e,await _("plugin:global-shortcut|register",{shortcuts:Array.isArray(t)?t:[t],handler:r})},t.unregister=async function(t){return await _("plugin:global-shortcut|unregister",{shortcuts:Array.isArray(t)?t:[t]})},t.unregisterAll=async function(){return await _("plugin:global-shortcut|unregister_all",{})},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBAL_SHORTCUT__})}
diff --git a/plugins/global-shortcut/package.json b/plugins/global-shortcut/package.json
index a247f93b..13f014b7 100644
--- a/plugins/global-shortcut/package.json
+++ b/plugins/global-shortcut/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-global-shortcut",
- "version": "2.0.0",
+ "version": "2.2.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
diff --git a/plugins/global-shortcut/src/lib.rs b/plugins/global-shortcut/src/lib.rs
index 5868ff9d..450bb598 100644
--- a/plugins/global-shortcut/src/lib.rs
+++ b/plugins/global-shortcut/src/lib.rs
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-//! [](https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/global-shortcut)
-//!
//! Register global shortcuts.
//!
//! - Supported platforms: Windows, Linux and macOS.
diff --git a/plugins/haptics/CHANGELOG.md b/plugins/haptics/CHANGELOG.md
index f8e3fad7..246fb18e 100644
--- a/plugins/haptics/CHANGELOG.md
+++ b/plugins/haptics/CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog
+## \[2.2.3]
+
+- [`406e6f48`](https://github.com/tauri-apps/plugins-workspace/commit/406e6f484cdc13d35c50fb949f7489ca9eeccc44) ([#2323](https://github.com/tauri-apps/plugins-workspace/pull/2323) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused build failures when the `haptics` or `geolocation` plugin was used without their `specta` feature flag enabled.
+
+## \[2.2.2]
+
+- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) **Breaking change:** `specta` integration is now behind a `specta` feature flag like in Tauri.
+- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Unlock and widen `specta` version range to match Tauri. No API changes.
+
+## \[2.2.1]
+
+- [`fb67ab2b`](https://github.com/tauri-apps/plugins-workspace/commit/fb67ab2b926502bfc20d6b43fbdd156691ea8526) ([#2281](https://github.com/tauri-apps/plugins-workspace/pull/2281) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Added `specta-util` to fix a "dependency not found" compilation error.
+
+## \[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.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
diff --git a/plugins/haptics/Cargo.toml b/plugins/haptics/Cargo.toml
index 8335475f..4215130e 100644
--- a/plugins/haptics/Cargo.toml
+++ b/plugins/haptics/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "tauri-plugin-haptics"
description = "Haptic feedback and vibrations on Android and iOS"
-version = "2.0.1"
+version = "2.2.3"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
@@ -26,10 +26,13 @@ tauri-plugin = { workspace = true, features = ["build"] }
[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
-tauri = { workspace = true, features = ["specta"] }
+tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
-specta = { workspace = true }
+specta = { workspace = true, optional = true }
[target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] }
+
+[features]
+specta = ["dep:specta", "tauri/specta"]
diff --git a/plugins/haptics/README.md b/plugins/haptics/README.md
index b96e267c..44118812 100644
--- a/plugins/haptics/README.md
+++ b/plugins/haptics/README.md
@@ -58,7 +58,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-haptics#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
@@ -69,6 +69,19 @@ fn main() {
}
```
+Second, add the required permissions in the project:
+
+`src-tauri/capabilities/default.json`
+
+```json
+ "permissions": [
+ "haptics:allow-impact-feedback",
+ "haptics:allow-notification-feedback",
+ "haptics:allow-selection-feedback",
+ "haptics:allow-vibrate"
+ ]
+```
+
Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
```javascript
diff --git a/plugins/haptics/package.json b/plugins/haptics/package.json
index f29bdc24..1c70aae1 100644
--- a/plugins/haptics/package.json
+++ b/plugins/haptics/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-haptics",
- "version": "2.0.0",
+ "version": "2.2.3",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
diff --git a/plugins/haptics/src/commands.rs b/plugins/haptics/src/commands.rs
index b2bbd1b0..76f097c0 100644
--- a/plugins/haptics/src/commands.rs
+++ b/plugins/haptics/src/commands.rs
@@ -7,13 +7,11 @@ use tauri::{command, AppHandle, Runtime};
use crate::{HapticsExt, ImpactFeedbackStyle, NotificationFeedbackType, Result};
#[command]
-#[specta::specta]
pub(crate) async fn vibrate(app: AppHandle, duration: u32) -> Result<()> {
app.haptics().vibrate(duration)
}
#[command]
-#[specta::specta]
pub(crate) async fn impact_feedback(
app: AppHandle,
style: ImpactFeedbackStyle,
@@ -22,7 +20,6 @@ pub(crate) async fn impact_feedback(
}
#[command]
-#[specta::specta]
pub(crate) async fn notification_feedback(
app: AppHandle,
r#type: NotificationFeedbackType,
@@ -31,7 +28,6 @@ pub(crate) async fn notification_feedback(
}
#[command]
-#[specta::specta]
pub(crate) async fn selection_feedback(app: AppHandle) -> Result<()> {
app.haptics().selection_feedback()
}
diff --git a/plugins/haptics/src/error.rs b/plugins/haptics/src/error.rs
index 30ff7f44..0fba5445 100644
--- a/plugins/haptics/src/error.rs
+++ b/plugins/haptics/src/error.rs
@@ -3,18 +3,18 @@
// SPDX-License-Identifier: MIT
use serde::{ser::Serializer, Serialize};
-use specta::Type;
pub type Result = std::result::Result;
// TODO: Improve Error handling (different typed errors instead of one (stringified) PluginInvokeError for all mobile errors)
-#[derive(Debug, thiserror::Error, Type)]
+#[derive(Debug, thiserror::Error)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
pub enum Error {
#[cfg(mobile)]
#[error(transparent)]
PluginInvoke(
- #[serde(skip)]
+ #[cfg_attr(feature = "specta", serde(skip))]
#[from]
tauri::plugin::mobile::PluginInvokeError,
),
diff --git a/plugins/haptics/src/lib.rs b/plugins/haptics/src/lib.rs
index 0a727e14..f56e5212 100644
--- a/plugins/haptics/src/lib.rs
+++ b/plugins/haptics/src/lib.rs
@@ -7,8 +7,6 @@ use tauri::{
Manager, Runtime,
};
-//use tauri_specta::*;
-
pub use models::*;
#[cfg(desktop)]
@@ -27,23 +25,6 @@ use desktop::Haptics;
#[cfg(mobile)]
use mobile::Haptics;
-/* macro_rules! specta_builder {
- () => {
- ts::builder()
- .commands(collect_commands![
- commands::vibrate,
- commands::impact_feedback,
- commands::notification_feedback,
- commands::selection_feedback
- ])
- .header("// @ts-nocheck")
- .config(
- specta::ts::ExportConfig::default()
- .bigint(specta::ts::BigIntExportBehavior::Number),
- )
- };
-} */
-
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the haptics APIs.
pub trait HapticsExt {
fn haptics(&self) -> &Haptics;
@@ -57,9 +38,6 @@ impl> crate::HapticsExt for T {
/// Initializes the plugin.
pub fn init() -> TauriPlugin {
- /* let (invoke_handler, register_events) =
- specta_builder!().build_plugin_utils("haptics").unwrap(); */
-
Builder::new("haptics")
.invoke_handler(tauri::generate_handler![
commands::vibrate,
@@ -77,22 +55,3 @@ pub fn init() -> TauriPlugin {
})
.build()
}
-
-/* #[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn export_types() {
- specta_builder!()
- .path("./guest-js/bindings.ts")
- .config(
- specta::ts::ExportConfig::default()
- .formatter(specta::ts::formatter::prettier)
- .bigint(specta::ts::BigIntExportBehavior::Number),
- )
- .export_for_plugin("haptics")
- .expect("failed to export specta types");
- }
-}
- */
diff --git a/plugins/haptics/src/models.rs b/plugins/haptics/src/models.rs
index 9f3b2035..50a1fb16 100644
--- a/plugins/haptics/src/models.rs
+++ b/plugins/haptics/src/models.rs
@@ -3,9 +3,9 @@
// SPDX-License-Identifier: MIT
use serde::{Deserialize, Serialize};
-use specta::Type;
/*
-#[derive(Debug, Clone, Default, Serialize, Deserialize, Type)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub struct HapticsOptions {
// TODO: support array to match web api
@@ -13,7 +13,8 @@ pub struct HapticsOptions {
}
*/
-#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Type)]
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub enum ImpactFeedbackStyle {
Light,
@@ -24,7 +25,8 @@ pub enum ImpactFeedbackStyle {
Rigid,
}
-#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Type)]
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")]
pub enum NotificationFeedbackType {
#[default]
diff --git a/plugins/http/CHANGELOG.md b/plugins/http/CHANGELOG.md
index 508780fa..e9611f4e 100644
--- a/plugins/http/CHANGELOG.md
+++ b/plugins/http/CHANGELOG.md
@@ -1,5 +1,42 @@
# Changelog
+## \[2.3.0]
+
+- [`10513649`](https://github.com/tauri-apps/plugins-workspace/commit/105136494c5a5bf4b1f1cc06cc71815412d17ec8) ([#2204](https://github.com/tauri-apps/plugins-workspace/pull/2204) by [@RickeyWard](https://github.com/tauri-apps/plugins-workspace/../../RickeyWard)) Add `dangerous-settings` feature flag and new JS `danger` option to disable tls hostname/certificate validation.
+
+## \[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.
+
+### Dependencies
+
+- Upgraded to `fs@2.2.0`
+
+## \[2.0.2]
+
+### Dependencies
+
+- Upgraded to `fs-js@2.0.4`
+
+## \[2.0.4]
+
+- [`a3b553dd`](https://github.com/tauri-apps/plugins-workspace/commit/a3b553ddb403771aa699362c4e69a064b7731da5) ([#2079](https://github.com/tauri-apps/plugins-workspace/pull/2079) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Add tracing logs for requestes and responses behind `tracing` feature flag.
+
+### Dependencies
+
+- Upgraded to `fs@2.1.0`
+
+## \[2.0.3]
+
+### Dependencies
+
+- Upgraded to `fs@2.0.3`
+
+## \[2.0.1]
+
+- [`cfd48b3b`](https://github.com/tauri-apps/plugins-workspace/commit/cfd48b3b2ec0fccfc162197518694ed59ceda22c) ([#1941](https://github.com/tauri-apps/plugins-workspace/pull/1941) by [@Nipsuli](https://github.com/tauri-apps/plugins-workspace/../../Nipsuli)) Allow skipping sending `Origin` header in HTTP requests by setting `Origin` header to an empty string when calling `fetch`.
+- [`9b2840db`](https://github.com/tauri-apps/plugins-workspace/commit/9b2840db9464cf08db75806270e4441f0af81e5d) ([#1884](https://github.com/tauri-apps/plugins-workspace/pull/1884) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Retain headers order.
+
## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@@ -189,100 +226,3 @@
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- /717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- ace/pull/371)) First v2 alpha release!
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- 2 alpha release!
- !
- 371\)) First v2 alpha release!
- /717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- ace/pull/371)) First v2 alpha release!
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- lpha release!
- !
- 371\)) First v2 alpha release!
- ub.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- ace/pull/371)) First v2 alpha release!
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- lpha release!
- !
- 371\)) First v2 alpha release!
- lpha release!
- !
- 371\)) First v2 alpha release!
- t v2 alpha release!
- !
- 371\)) First v2 alpha release!
- ace/pull/371)) First v2 alpha release!
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- !
- 371\)) First v2 alpha release!
- lpha release!
- !
- 371\)) First v2 alpha release!
- lpha release!
- !
- 371\)) First v2 alpha release!
- lpha release!
- !
- 371\)) First v2 alpha release!
- lpha release!
- lpha release!
- !
- 371\)) First v2 alpha release!
- lpha release!
- !
- 371\)) First v2 alpha release!
- ha release!
- !
- 371\)) First v2 alpha release!
diff --git a/plugins/http/Cargo.toml b/plugins/http/Cargo.toml
index 64b9aa75..9aa49e0e 100644
--- a/plugins/http/Cargo.toml
+++ b/plugins/http/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-http"
-version = "2.0.1"
+version = "2.3.0"
description = "Access an HTTP client written in Rust."
edition = { workspace = true }
authors = { workspace = true }
@@ -34,13 +34,14 @@ serde_json = { workspace = true }
tauri = { workspace = true }
thiserror = { workspace = true }
tokio = { version = "1", features = ["sync", "macros"] }
-tauri-plugin-fs = { path = "../fs", version = "2.0.1" }
+tauri-plugin-fs = { path = "../fs", version = "2.2.0" }
urlpattern = "0.3"
regex = "1"
http = "1"
reqwest = { version = "0.12", default-features = false }
url = { workspace = true }
data-url = "0.3"
+tracing = { workspace = true, optional = true }
[features]
default = [
@@ -71,3 +72,5 @@ http2 = ["reqwest/http2"]
charset = ["reqwest/charset"]
macos-system-configuration = ["reqwest/macos-system-configuration"]
unsafe-headers = []
+tracing = ["dep:tracing"]
+dangerous-settings = []
diff --git a/plugins/http/README.md b/plugins/http/README.md
index c3fe92c0..5c3cd9c2 100644
--- a/plugins/http/README.md
+++ b/plugins/http/README.md
@@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-http#v2
First you need to register the core plugin with Tauri:
-`src-tauri/src/main.rs`
+`src-tauri/src/lib.rs`
```rust
fn main() {
diff --git a/plugins/http/api-iife.js b/plugins/http/api-iife.js
index 0cfeb063..76b498ad 100644
--- a/plugins/http/api-iife.js
+++ b/plugins/http/api-iife.js
@@ -1 +1 @@
-if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";async function t(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;const r="Request canceled";return e.fetch=async function(e,n){const a=n?.signal;if(a?.aborted)throw new Error(r);const o=n?.maxRedirections,s=n?.connectTimeout,i=n?.proxy;n&&(delete n.maxRedirections,delete n.connectTimeout,delete n.proxy);const d=n?.headers?n.headers instanceof Headers?n.headers:new Headers(n.headers):new Headers,c=new Request(e,n),u=await c.arrayBuffer(),f=0!==u.byteLength?Array.from(new Uint8Array(u)):null;for(const[e,t]of c.headers)d.get(e)||d.set(e,t);const _=(d instanceof Headers?Array.from(d.entries()):Array.isArray(d)?d:Object.entries(d)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(a?.aborted)throw new Error(r);const h=await t("plugin:http|fetch",{clientConfig:{method:c.method,url:c.url,headers:_,data:f,maxRedirections:o,connectTimeout:s,proxy:i}}),l=()=>t("plugin:http|fetch_cancel",{rid:h});if(a?.aborted)throw l(),new Error(r);a?.addEventListener("abort",(()=>{l()}));const{status:p,statusText:w,url:y,headers:T,rid:A}=await t("plugin:http|fetch_send",{rid:h}),g=await t("plugin:http|fetch_read_body",{rid:A}),R=new Response(g instanceof ArrayBuffer&&0!==g.byteLength?g:g instanceof Array&&g.length>0?new Uint8Array(g):null,{status:p,statusText:w});return Object.defineProperty(R,"url",{value:y}),Object.defineProperty(R,"headers",{value:new Headers(T)}),R},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})}
+if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";async function t(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;const r="Request canceled";return e.fetch=async function(e,n){const a=n?.signal;if(a?.aborted)throw new Error(r);const o=n?.maxRedirections,s=n?.connectTimeout,i=n?.proxy,d=n?.danger;n&&(delete n.maxRedirections,delete n.connectTimeout,delete n.proxy,delete n.danger);const c=n?.headers?n.headers instanceof Headers?n.headers:new Headers(n.headers):new Headers,u=new Request(e,n),f=await u.arrayBuffer(),_=0!==f.byteLength?Array.from(new Uint8Array(f)):null;for(const[e,t]of u.headers)c.get(e)||c.set(e,t);const h=(c instanceof Headers?Array.from(c.entries()):Array.isArray(c)?c:Object.entries(c)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(a?.aborted)throw new Error(r);const l=await t("plugin:http|fetch",{clientConfig:{method:u.method,url:u.url,headers:h,data:_,maxRedirections:o,connectTimeout:s,proxy:i,danger:d}}),p=()=>t("plugin:http|fetch_cancel",{rid:l});if(a?.aborted)throw p(),new Error(r);a?.addEventListener("abort",(()=>{p()}));const{status:w,statusText:y,url:g,headers:T,rid:A}=await t("plugin:http|fetch_send",{rid:l}),R=await t("plugin:http|fetch_read_body",{rid:A}),b=new Response(R instanceof ArrayBuffer&&0!==R.byteLength?R:R instanceof Array&&R.length>0?new Uint8Array(R):null,{status:w,statusText:y});return Object.defineProperty(b,"url",{value:g}),Object.defineProperty(b,"headers",{value:new Headers(T)}),b},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})}
diff --git a/plugins/http/guest-js/index.ts b/plugins/http/guest-js/index.ts
index 4362e893..bea18e44 100644
--- a/plugins/http/guest-js/index.ts
+++ b/plugins/http/guest-js/index.ts
@@ -84,6 +84,26 @@ export interface ClientOptions {
* Configuration of a proxy that a Client should pass requests to.
*/
proxy?: Proxy
+ /**
+ * Configuration for dangerous settings on the client such as disabling SSL verification.
+ */
+ danger?: DangerousSettings
+}
+
+/**
+ * Configuration for dangerous settings on the client such as disabling SSL verification.
+ *
+ * @since 2.3.0
+ */
+export interface DangerousSettings {
+ /**
+ * Disables SSL verification.
+ */
+ acceptInvalidCerts?: boolean
+ /**
+ * Disables hostname verification.
+ */
+ acceptInvalidHostnames?: boolean
}
const ERROR_REQUEST_CANCELLED = 'Request canceled'
@@ -115,12 +135,14 @@ export async function fetch(
const maxRedirections = init?.maxRedirections
const connectTimeout = init?.connectTimeout
const proxy = init?.proxy
+ const danger = init?.danger
// Remove these fields before creating the request
if (init) {
delete init.maxRedirections
delete init.connectTimeout
delete init.proxy
+ delete init.danger
}
const headers = init?.headers
@@ -172,7 +194,8 @@ export async function fetch(
data,
maxRedirections,
connectTimeout,
- proxy
+ proxy,
+ danger
}
})
diff --git a/plugins/http/package.json b/plugins/http/package.json
index 24fb27f7..02ea80bf 100644
--- a/plugins/http/package.json
+++ b/plugins/http/package.json
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-http",
- "version": "2.0.0",
+ "version": "2.3.0",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
diff --git a/plugins/http/src/commands.rs b/plugins/http/src/commands.rs
index b9c1cae2..3dc0297e 100644
--- a/plugins/http/src/commands.rs
+++ b/plugins/http/src/commands.rs
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc, time::Duration};
+use std::{future::Future, pin::Pin, str::FromStr, sync::Arc, time::Duration};
-use http::{header, HeaderName, Method, StatusCode};
+use http::{header, HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
use reqwest::{redirect::Policy, NoProxy};
use serde::{Deserialize, Serialize};
use tauri::{
@@ -75,6 +75,14 @@ pub struct FetchResponse {
rid: ResourceId,
}
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+#[allow(dead_code)] //feature flags shoudln't affect api
+pub struct DangerousSettings {
+ accept_invalid_certs: bool,
+ accept_invalid_hostnames: bool,
+}
+
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientConfig {
@@ -85,6 +93,7 @@ pub struct ClientConfig {
connect_timeout: Option,
max_redirections: Option,
proxy: Option,
+ danger: Option