diff --git a/.changes/deep-link-fix-different-exec.md b/.changes/deep-link-fix-different-exec.md new file mode 100644 index 000000000..1f5b5cda8 --- /dev/null +++ b/.changes/deep-link-fix-different-exec.md @@ -0,0 +1,6 @@ +--- +"deep-link": patch +"deep-link-js": patch +--- + +Fix Exec= field in desktop handler if executable path changes diff --git a/.changes/enhance-fs-error-message.md b/.changes/enhance-fs-error-message.md new file mode 100644 index 000000000..540e65a50 --- /dev/null +++ b/.changes/enhance-fs-error-message.md @@ -0,0 +1,6 @@ +--- +"fs": patch +"fs-js": patch +--- + +Enhance error messages. diff --git a/.changes/geolocation-android-timeout.md b/.changes/geolocation-android-timeout.md new file mode 100644 index 000000000..dc3d952e2 --- /dev/null +++ b/.changes/geolocation-android-timeout.md @@ -0,0 +1,6 @@ +--- +geolocation: patch +geolocation-js: patch +--- + +On Android, use the `timeout` value for `setMinUpdateIntervalMillis`, `setMaxUpdateDelayMillis` and `setIntervalMillis` instead of just `minUpdateInterval`. diff --git a/.changes/nfc-close-session.md b/.changes/nfc-close-session.md deleted file mode 100644 index 70735b34f..000000000 --- a/.changes/nfc-close-session.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -nfc: patch -nfc-js: patch ---- - -On iOS, the reader session will now get closed properly on errors, preventing dangling invalid sessions that could prevent subsequent write attempts. diff --git a/.changes/opener-same-origin-link.md b/.changes/opener-same-origin-link.md new file mode 100644 index 000000000..5bed71c4a --- /dev/null +++ b/.changes/opener-same-origin-link.md @@ -0,0 +1,6 @@ +--- +"opener": patch +"opener-js": patch +--- + +Fix opener doesn't open same origin links in the browser diff --git a/.changes/store-defaults-js.md b/.changes/store-defaults-js.md deleted file mode 100644 index 122ef8195..000000000 --- a/.changes/store-defaults-js.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -store: minor -store-js: minor ---- - -Allow setting defaults from the JavaScript API diff --git a/.changes/store-load-override-defaults.md b/.changes/store-load-override-defaults.md deleted file mode 100644 index 1fb8376e0..000000000 --- a/.changes/store-load-override-defaults.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -store: minor -store-js: minor ---- - -Add an new option `overrideDefaults` for creating/loading and reloading the store that overrides the store with the on-disk state, ignoring defaults diff --git a/.changes/updater-new-bundle-support.md b/.changes/updater-new-bundle-support.md new file mode 100644 index 000000000..99cf2e889 --- /dev/null +++ b/.changes/updater-new-bundle-support.md @@ -0,0 +1,6 @@ +--- +"updater": minor +"updater-js": minor +--- + +Updater plugin now supports all bundle types: Deb, Rpm and AppImage for Linux; NSiS, MSI for Windows. diff --git a/.gitignore b/.gitignore index 38051f657..41022b01c 100644 --- a/.gitignore +++ b/.gitignore @@ -58,4 +58,4 @@ pids .idea debug.log TODO.md -.aider* +.aider.* diff --git a/Cargo.lock b/Cargo.lock index 7d504d866..afef03afa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,7 +220,7 @@ checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "api" -version = "2.0.32" +version = "2.0.36" dependencies = [ "log", "serde", @@ -903,7 +903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" dependencies = [ "serde", - "toml", + "toml 0.8.20", ] [[package]] @@ -1608,9 +1608,9 @@ dependencies = [ [[package]] name = "dlopen2" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff" dependencies = [ "dlopen2_derive", "libc", @@ -1770,7 +1770,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml", + "toml 0.8.20", "vswhom", "winreg 0.52.0", ] @@ -3648,9 +3648,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" dependencies = [ "crossbeam-channel", "dpi", @@ -3664,7 +3664,7 @@ dependencies = [ "png", "serde", "thiserror 2.0.12", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4140,6 +4140,17 @@ dependencies = [ "objc2-foundation 0.3.0", ] +[[package]] +name = "objc2-security" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3126341c65c5d5728423ae95d788e1b660756486ad0592307ab87ba02d9a7268" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", +] + [[package]] name = "objc2-ui-kit" version = "0.3.0" @@ -4164,6 +4175,7 @@ dependencies = [ "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.0", + "objc2-security", ] [[package]] @@ -5668,6 +5680,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -5712,9 +5733,9 @@ dependencies = [ [[package]] name = "serialize-to-javascript" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" dependencies = [ "serde", "serde_json", @@ -5723,13 +5744,13 @@ dependencies = [ [[package]] name = "serialize-to-javascript-impl" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.100", ] [[package]] @@ -6364,17 +6385,18 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.20", "version-compare", ] [[package]] name = "tao" -version = "0.34.0" +version = "0.34.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c380ca75a231b87b6c9dd86948f035012e7171d1a7c40a9c2890489a7ffd8a" +checksum = "4daa814018fecdfb977b59a094df4bd43b42e8e21f88fddfc05807e6f46efaaf" dependencies = [ "bitflags 2.9.0", + "block2 0.6.0", "core-foundation 0.10.0", "core-graphics", "crossbeam-channel", @@ -6443,16 +6465,17 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.6.0" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7a0f4019c80391d143ee26cd7cd1ed271ac241d3087d333f99f3269ba90812" +checksum = "5d545ccf7b60dcd44e07c6fb5aeb09140966f0aabd5d2aa14a6821df7bc99348" dependencies = [ "anyhow", "bytes", + "cookie", "dirs 6.0.0", "dunce", "embed_plist", - "getrandom 0.2.15", + "getrandom 0.3.2", "glob", "gtk", "heck 0.5.0", @@ -6468,6 +6491,7 @@ dependencies = [ "objc2-app-kit", "objc2-foundation 0.3.0", "objc2-ui-kit", + "objc2-web-kit", "percent-encoding", "plist", "raw-window-handle", @@ -6497,9 +6521,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f025c389d3adb83114bec704da973142e82fc6ec799c7c750c5e21cefaec83" +checksum = "67945dbaf8920dbe3a1e56721a419a0c3d085254ab24cff5b9ad55e2b0016e0b" dependencies = [ "anyhow", "cargo_toml", @@ -6515,15 +6539,15 @@ dependencies = [ "tauri-codegen", "tauri-utils", "tauri-winres", - "toml", + "toml 0.9.5", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df493a1075a241065bc865ed5ef8d0fbc1e76c7afdc0bf0eccfaa7d4f0e406" +checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a" dependencies = [ "base64 0.22.1", "ico", @@ -6547,9 +6571,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f59e1d1fa9651212dcb890a0c66226d819b716490b0cf43c078514da3591705" +checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -6561,9 +6585,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9a0bd00bf1930ad1a604d08b0eb6b2a9c1822686d65d7f4731a7723b8901d3" +checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f" dependencies = [ "anyhow", "glob", @@ -6572,7 +6596,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml", + "toml 0.9.5", "walkdir", ] @@ -6641,9 +6665,10 @@ dependencies = [ [[package]] name = "tauri-plugin-deep-link" -version = "2.4.1" +version = "2.4.3" dependencies = [ "dunce", + "plist", "rust-ini", "serde", "serde_json", @@ -6659,7 +6684,7 @@ dependencies = [ [[package]] name = "tauri-plugin-dialog" -version = "2.3.2" +version = "2.4.0" dependencies = [ "log", "raw-window-handle", @@ -6675,7 +6700,7 @@ dependencies = [ [[package]] name = "tauri-plugin-fs" -version = "2.4.1" +version = "2.4.2" dependencies = [ "anyhow", "dunce", @@ -6691,7 +6716,7 @@ dependencies = [ "tauri-plugin", "tauri-utils", "thiserror 2.0.12", - "toml", + "toml 0.9.5", "url", ] @@ -6736,7 +6761,7 @@ dependencies = [ [[package]] name = "tauri-plugin-http" -version = "2.5.1" +version = "2.5.2" dependencies = [ "bytes", "cookie_store", @@ -6772,7 +6797,7 @@ dependencies = [ [[package]] name = "tauri-plugin-log" -version = "2.6.0" +version = "2.7.0" dependencies = [ "android_logger", "byte-unit", @@ -6793,7 +6818,7 @@ dependencies = [ [[package]] name = "tauri-plugin-nfc" -version = "2.3.0" +version = "2.3.1" dependencies = [ "log", "serde", @@ -6806,7 +6831,7 @@ dependencies = [ [[package]] name = "tauri-plugin-notification" -version = "2.3.0" +version = "2.3.1" dependencies = [ "color-backtrace", "ctor", @@ -6828,7 +6853,7 @@ dependencies = [ [[package]] name = "tauri-plugin-opener" -version = "2.4.0" +version = "2.5.0" dependencies = [ "dunce", "glob", @@ -6848,7 +6873,7 @@ dependencies = [ [[package]] name = "tauri-plugin-os" -version = "2.3.0" +version = "2.3.1" dependencies = [ "gethostname 1.0.1", "log", @@ -6864,7 +6889,7 @@ dependencies = [ [[package]] name = "tauri-plugin-persisted-scope" -version = "2.3.1" +version = "2.3.2" dependencies = [ "aho-corasick", "bincode", @@ -6916,7 +6941,7 @@ dependencies = [ [[package]] name = "tauri-plugin-shell" -version = "2.3.0" +version = "2.3.1" dependencies = [ "encoding_rs", "log", @@ -6935,7 +6960,7 @@ dependencies = [ [[package]] name = "tauri-plugin-single-instance" -version = "2.3.2" +version = "2.3.4" dependencies = [ "semver", "serde", @@ -6967,7 +6992,7 @@ dependencies = [ [[package]] name = "tauri-plugin-store" -version = "2.3.0" +version = "2.4.0" dependencies = [ "dunce", "serde", @@ -7080,9 +7105,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7bb73d1bceac06c20b3f755b2c8a2cb13b20b50083084a8cf3700daf397ba4" +checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846" dependencies = [ "cookie", "dpi", @@ -7091,20 +7116,23 @@ dependencies = [ "jni", "objc2 0.6.0", "objc2-ui-kit", + "objc2-web-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", "thiserror 2.0.12", "url", + "webkit2gtk", + "webview2-com", "windows 0.61.1", ] [[package]] name = "tauri-runtime-wry" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe52ed0ef40fd7ad51a620ecb3018e32eba3040bb95025216a962a37f6f050c5" +checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807" dependencies = [ "gtk", "http", @@ -7129,16 +7157,16 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41743bbbeb96c3a100d234e5a0b60a46d5aa068f266160862c7afdbf828ca02e" +checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212" dependencies = [ "aes-gcm", "anyhow", "cargo_metadata", "ctor", "dunce", - "getrandom 0.2.15", + "getrandom 0.3.2", "glob", "html5ever", "http", @@ -7160,7 +7188,7 @@ dependencies = [ "serialize-to-javascript", "swift-rs", "thiserror 2.0.12", - "toml", + "toml 0.9.5", "url", "urlpattern", "uuid", @@ -7174,7 +7202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56eaa45f707bedf34d19312c26d350bc0f3c59a47e58e8adbeecdc850d2c13a0" dependencies = [ "embed-resource", - "toml", + "toml 0.8.20", ] [[package]] @@ -7476,11 +7504,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.8", + "toml_datetime 0.6.8", "toml_edit 0.22.24", ] +[[package]] +name = "toml" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +dependencies = [ + "indexmap 2.9.0", + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow 0.7.12", +] + [[package]] name = "toml_datetime" version = "0.6.8" @@ -7490,6 +7533,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -7497,7 +7549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.9.0", - "toml_datetime", + "toml_datetime 0.6.8", "winnow 0.5.40", ] @@ -7508,7 +7560,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ "indexmap 2.9.0", - "toml_datetime", + "toml_datetime 0.6.8", "winnow 0.5.40", ] @@ -7520,11 +7572,26 @@ checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap 2.9.0", "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.7.6", + "serde_spanned 0.6.8", + "toml_datetime 0.6.8", + "winnow 0.7.12", ] +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow 0.7.12", +] + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + [[package]] name = "tower" version = "0.5.2" @@ -7586,9 +7653,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d433764348e7084bad2c5ea22c96c71b61b17afe3a11645710f533bd72b6a2b5" +checksum = "a0d92153331e7d02ec09137538996a7786fe679c629c279e82a6be762b7e6fe2" dependencies = [ "crossbeam-channel", "dirs 6.0.0", @@ -8833,9 +8900,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -8901,14 +8968,15 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.52.0" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08db04817a654a7e3339647d9cf8b497ed9ddcd4ec7cfda5a3a220c10a3bba3" +checksum = "e3b6763512fe4b51c80b3ce9b50939d682acb4de335dfabbdb20d7a2642199b7" dependencies = [ "base64 0.22.1", "block2 0.6.0", "cookie", "crossbeam-channel", + "dirs 6.0.0", "dpi", "dunce", "gdkx11", @@ -9069,7 +9137,7 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.60.2", - "winnow 0.7.6", + "winnow 0.7.12", "zbus_macros", "zbus_names", "zvariant", @@ -9108,7 +9176,7 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.6", + "winnow 0.7.12", "zvariant", ] @@ -9295,7 +9363,7 @@ dependencies = [ "enumflags2", "serde", "url", - "winnow 0.7.6", + "winnow 0.7.12", "zvariant_derive", "zvariant_utils", ] @@ -9324,5 +9392,5 @@ dependencies = [ "serde", "static_assertions", "syn 2.0.100", - "winnow 0.7.6", + "winnow 0.7.12", ] diff --git a/Cargo.toml b/Cargo.toml index 2d1b4c0e6..071f54ffb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,10 @@ resolver = "2" serde = { version = "1", features = ["derive"] } tracing = "0.1" log = "0.4" -tauri = { version = "2.6", default-features = false } -tauri-build = "2.3" -tauri-plugin = "2.3" -tauri-utils = "2.5" +tauri = { version = "2.8.2", default-features = false } +tauri-build = "2.4" +tauri-plugin = "2.4" +tauri-utils = "2.7" serde_json = "1" thiserror = "2" url = "2" diff --git a/eslint.config.js b/eslint.config.js index f34103ee4..2500c686e 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -3,11 +3,12 @@ // SPDX-License-Identifier: MIT import eslint from '@eslint/js' +import { defineConfig } from 'eslint/config' import eslintConfigPrettier from 'eslint-config-prettier' import eslintPluginSecurity from 'eslint-plugin-security' import tseslint from 'typescript-eslint' -export default tseslint.config( +export default defineConfig( { ignores: [ '**/target', diff --git a/examples/api/CHANGELOG.md b/examples/api/CHANGELOG.md index ea5d611d4..cd296557c 100644 --- a/examples/api/CHANGELOG.md +++ b/examples/api/CHANGELOG.md @@ -1,5 +1,36 @@ # Changelog +## \[2.0.32] + +### Dependencies + +- Upgraded to `dialog-js@2.4.0` +- Upgraded to `log-js@2.7.0` + +## \[2.0.31] + +### Dependencies + +- Upgraded to `shell-js@2.3.1` + +## \[2.0.30] + +### Dependencies + +- Upgraded to `notification-js@2.3.1` + +## \[2.0.29] + +### Dependencies + +- Upgraded to `fs-js@2.4.2` +- Upgraded to `nfc-js@2.3.1` +- Upgraded to `opener-js@2.5.0` +- Upgraded to `os-js@2.3.1` +- Upgraded to `store-js@2.4.0` +- Upgraded to `dialog-js@2.3.3` +- Upgraded to `http-js@2.5.2` + ## \[2.0.28] ### Dependencies diff --git a/examples/api/index.html b/examples/api/index.html index 919cfd2a7..655165ea0 100644 --- a/examples/api/index.html +++ b/examples/api/index.html @@ -4,7 +4,7 @@ Svelte + Vite App diff --git a/examples/api/package.json b/examples/api/package.json index 200ac8f89..91763334f 100644 --- a/examples/api/package.json +++ b/examples/api/package.json @@ -1,7 +1,7 @@ { "name": "api", "private": true, - "version": "2.0.28", + "version": "2.0.32", "type": "module", "scripts": { "dev": "vite --clearScreen false", @@ -10,25 +10,25 @@ "tauri": "tauri" }, "dependencies": { - "@tauri-apps/api": "2.7.0", + "@tauri-apps/api": "2.8.0", "@tauri-apps/plugin-barcode-scanner": "^2.4.0", "@tauri-apps/plugin-biometric": "^2.3.0", "@tauri-apps/plugin-cli": "^2.4.0", "@tauri-apps/plugin-clipboard-manager": "^2.3.0", - "@tauri-apps/plugin-dialog": "^2.3.2", - "@tauri-apps/plugin-fs": "^2.4.1", + "@tauri-apps/plugin-dialog": "^2.4.0", + "@tauri-apps/plugin-fs": "^2.4.2", "@tauri-apps/plugin-geolocation": "^2.2.0", "@tauri-apps/plugin-global-shortcut": "^2.3.0", "@tauri-apps/plugin-haptics": "^2.2.0", - "@tauri-apps/plugin-http": "^2.5.1", - "@tauri-apps/plugin-nfc": "^2.3.0", - "@tauri-apps/plugin-notification": "^2.3.0", - "@tauri-apps/plugin-opener": "^2.4.0", - "@tauri-apps/plugin-os": "^2.3.0", + "@tauri-apps/plugin-http": "^2.5.2", + "@tauri-apps/plugin-nfc": "^2.3.1", + "@tauri-apps/plugin-notification": "^2.3.1", + "@tauri-apps/plugin-opener": "^2.5.0", + "@tauri-apps/plugin-os": "^2.3.1", "@tauri-apps/plugin-process": "^2.3.0", - "@tauri-apps/plugin-shell": "^2.3.0", "@tauri-apps/plugin-secure-storage": "file:../../plugins/secure-storage", - "@tauri-apps/plugin-store": "^2.3.0", + "@tauri-apps/plugin-shell": "^2.3.1", + "@tauri-apps/plugin-store": "^2.4.0", "@tauri-apps/plugin-updater": "^2.9.0", "@tauri-apps/plugin-upload": "^2.3.0", "@zerodevx/svelte-json-view": "1.0.11" @@ -37,10 +37,10 @@ "@iconify-json/codicon": "^1.2.12", "@iconify-json/ph": "^1.2.2", "@sveltejs/vite-plugin-svelte": "^6.0.0", - "@tauri-apps/cli": "2.7.1", + "@tauri-apps/cli": "2.8.4", "@unocss/extractor-svelte": "^66.3.3", "svelte": "^5.20.4", "unocss": "^66.3.3", - "vite": "^7.0.4" + "vite": "^7.0.7" } } diff --git a/examples/api/src-tauri/CHANGELOG.md b/examples/api/src-tauri/CHANGELOG.md index 9fdae693e..7e4eaae8d 100644 --- a/examples/api/src-tauri/CHANGELOG.md +++ b/examples/api/src-tauri/CHANGELOG.md @@ -1,5 +1,36 @@ # Changelog +## \[2.0.36] + +### Dependencies + +- Upgraded to `dialog@2.4.0` +- Upgraded to `log@2.7.0` + +## \[2.0.35] + +### Dependencies + +- Upgraded to `shell@2.3.1` + +## \[2.0.34] + +### Dependencies + +- Upgraded to `notification@2.3.1` + +## \[2.0.33] + +### Dependencies + +- Upgraded to `fs@2.4.2` +- Upgraded to `nfc@2.3.1` +- Upgraded to `opener@2.5.0` +- Upgraded to `os@2.3.1` +- Upgraded to `store@2.4.0` +- Upgraded to `dialog@2.3.3` +- Upgraded to `http@2.5.2` + ## \[2.0.32] ### Dependencies diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 5557e09bd..1134fc410 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "api" publish = false -version = "2.0.32" +version = "2.0.36" description = "An example Tauri Application showcasing the api" edition = "2021" rust-version = { workspace = true } @@ -20,25 +20,25 @@ serde = { workspace = true } tiny_http = "0.12" time = "0.3" log = { workspace = true } -tauri-plugin-log = { path = "../../../plugins/log", version = "2.6.0" } -tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.4.1", features = [ +tauri-plugin-log = { path = "../../../plugins/log", version = "2.7.0" } +tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.4.2", features = [ "watch", ] } tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.0" } -tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.3.2" } +tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.4.0" } tauri-plugin-http = { path = "../../../plugins/http", features = [ "multipart", "cookies", -], version = "2.5.1" } -tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.0", features = [ +], version = "2.5.2" } +tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.1", features = [ "windows7-compat", ] } -tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.0" } +tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.1" } tauri-plugin-process = { path = "../../../plugins/process", version = "2.3.0" } -tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.4.0" } +tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.5.0" } tauri-plugin-secure-storage = { path = "../../../plugins/secure-storage" } -tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.0" } -tauri-plugin-store = { path = "../../../plugins/store", version = "2.3.0" } +tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.1" } +tauri-plugin-store = { path = "../../../plugins/store", version = "2.4.0" } tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" } [dependencies.tauri] @@ -63,7 +63,7 @@ tauri-plugin-window-state = { path = "../../../plugins/window-state", version = [target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies] tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.4.0" } -tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.0" } +tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.1" } tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.0" } tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.0" } tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.3.0" } diff --git a/examples/api/src-tauri/capabilities/base.json b/examples/api/src-tauri/capabilities/base.json index e17b3c3af..80d18a90b 100644 --- a/examples/api/src-tauri/capabilities/base.json +++ b/examples/api/src-tauri/capabilities/base.json @@ -68,6 +68,9 @@ "fs:allow-rename", "fs:allow-mkdir", "fs:allow-remove", + "fs:allow-stat", + "fs:allow-fstat", + "fs:allow-lstat", "fs:allow-write-text-file", "fs:read-meta", "fs:scope-download-recursive", @@ -75,6 +78,9 @@ { "identifier": "fs:scope-appdata-recursive", "allow": [ + { + "path": "$APPDATA/db/" + }, { "path": "$APPDATA/db/**" } diff --git a/examples/api/src/views/Dialog.svelte b/examples/api/src/views/Dialog.svelte index 462eecff7..5aadad5a9 100644 --- a/examples/api/src/views/Dialog.svelte +++ b/examples/api/src/views/Dialog.svelte @@ -44,6 +44,13 @@ await message("Tauri is awesome!"); } + async function msgCustom(result) { + const buttons = { yes: "awesome", no: "amazing", cancel: "stunning" }; + await message(`Tauri is: `, { buttons }) + .then((res) => onMessage(`Tauri is ${res}`)) + .catch(onMessage); + } + function openDialog() { open({ title: "My wonderful open dialog", @@ -136,12 +143,17 @@
- - - - - + +
+ + + + + + + +
\ No newline at end of file diff --git a/examples/api/src/views/FileSystem.svelte b/examples/api/src/views/FileSystem.svelte index baf9f6072..fbec86282 100644 --- a/examples/api/src/views/FileSystem.svelte +++ b/examples/api/src/views/FileSystem.svelte @@ -1,194 +1,214 @@
+ {#if isMobile} +
+ On mobile, paths outside of App* paths require the use of dialogs + regardless of Tauri's scope mechanism. +
+
+ {/if}
- + + {#each dirOptions as dir} + {/each}
- - - - + + + +
- +
- +
{#if file}
- - + + +
{/if} @@ -241,8 +262,8 @@

- - + +
diff --git a/examples/api/src/views/Notifications.svelte b/examples/api/src/views/Notifications.svelte index e01d57fcd..11273fdc3 100644 --- a/examples/api/src/views/Notifications.svelte +++ b/examples/api/src/views/Notifications.svelte @@ -1,16 +1,21 @@ - diff --git a/package.json b/package.json index 55a7d97fe..89c5909e7 100644 --- a/package.json +++ b/package.json @@ -11,20 +11,21 @@ "example:api:dev": "pnpm run --filter \"api\" tauri dev" }, "devDependencies": { - "@eslint/js": "9.32.0", + "@eslint/js": "9.36.0", "@rollup/plugin-node-resolve": "16.0.1", "@rollup/plugin-terser": "0.4.4", "@rollup/plugin-typescript": "12.1.4", "covector": "^0.12.4", - "eslint": "9.32.0", + "eslint": "9.36.0", "eslint-config-prettier": "10.1.8", "eslint-plugin-security": "3.0.1", "prettier": "3.6.2", - "rollup": "4.46.2", + "rollup": "4.52.3", "tslib": "2.8.1", "typescript": "5.9.2", - "typescript-eslint": "8.39.0" + "typescript-eslint": "8.44.1" }, + "minimumReleaseAge": 4320, "pnpm": { "overrides": { "esbuild@<0.25.0": ">=0.25.0" @@ -34,6 +35,6 @@ ] }, "engines": { - "pnpm": "^10.0.0" + "pnpm": "^10.16.0" } } diff --git a/plugins/autostart/package.json b/plugins/autostart/package.json index 172c022f0..5f3d2adf4 100644 --- a/plugins/autostart/package.json +++ b/plugins/autostart/package.json @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/barcode-scanner/package.json b/plugins/barcode-scanner/package.json index b9194415c..87b6dc718 100644 --- a/plugins/barcode-scanner/package.json +++ b/plugins/barcode-scanner/package.json @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/biometric/package.json b/plugins/biometric/package.json index 559010f3f..e6c4d9881 100644 --- a/plugins/biometric/package.json +++ b/plugins/biometric/package.json @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/cli/package.json b/plugins/cli/package.json index 2177132ae..4aeeb8ec9 100644 --- a/plugins/cli/package.json +++ b/plugins/cli/package.json @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/clipboard-manager/package.json b/plugins/clipboard-manager/package.json index abe2a4fbd..ac6ac49c7 100644 --- a/plugins/clipboard-manager/package.json +++ b/plugins/clipboard-manager/package.json @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/deep-link/CHANGELOG.md b/plugins/deep-link/CHANGELOG.md index e55810a55..d9163a4f3 100644 --- a/plugins/deep-link/CHANGELOG.md +++ b/plugins/deep-link/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## \[2.4.3] + +- [`2522b71f`](https://github.com/tauri-apps/plugins-workspace/commit/2522b71f6bcae65c03b24415eb9295c9e7c84ffc) ([#2970](https://github.com/tauri-apps/plugins-workspace/pull/2970) by [@WSH032](https://github.com/tauri-apps/plugins-workspace/../../WSH032)) Revert the breaking change introduced by [#2928](https://github.com/tauri-apps/plugins-workspace/pull/2928). + +## \[2.4.2] + +- [`21d721a0`](https://github.com/tauri-apps/plugins-workspace/commit/21d721a0c2731fc201872f5b99ea8bbdc61b0b60) ([#2928](https://github.com/tauri-apps/plugins-workspace/pull/2928) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Linux, improved error messages when OS commands fail. + ## \[2.4.1] - [`d4f8299b`](https://github.com/tauri-apps/plugins-workspace/commit/d4f8299b12f107718c70692840a63768d65baaf9) ([#2844](https://github.com/tauri-apps/plugins-workspace/pull/2844) by [@yobson1](https://github.com/tauri-apps/plugins-workspace/../../yobson1)) Fix deep link protocol handler not set as default on linux diff --git a/plugins/deep-link/Cargo.toml b/plugins/deep-link/Cargo.toml index 7c8524bfc..631e056ba 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.4.1" +version = "2.4.3" description = "Set your Tauri application as the default handler for an URL" authors = { workspace = true } license = { workspace = true } @@ -27,6 +27,9 @@ serde_json = { workspace = true } tauri-utils = { workspace = true } tauri-plugin = { workspace = true, features = ["build"] } +[target."cfg(target_os = \"macos\")".build-dependencies] +plist = "1" + [dependencies] serde = { workspace = true } serde_json = { workspace = true } diff --git a/plugins/deep-link/build.rs b/plugins/deep-link/build.rs index 418746b23..16d2a96e5 100644 --- a/plugins/deep-link/build.rs +++ b/plugins/deep-link/build.rs @@ -10,50 +10,64 @@ const COMMANDS: &[&str] = &["get_current", "register", "unregister", "is_registe // TODO: Consider using activity-alias in case users may have multiple activities in their app. fn intent_filter(domain: &AssociatedDomain) -> String { + let host = domain + .host + .as_ref() + .map(|h| format!(r#""#)) + .unwrap_or_default(); + + let auto_verify = if domain.is_app_link() { + r#"android:autoVerify="true" "#.to_string() + } else { + String::new() + }; + format!( - r#" + r#" - {} - - {} - {} - {} - {} + {schemes} + {host} + {domains} + {path_patterns} + {path_prefixes} + {path_suffixes} "#, - domain + schemes = domain .scheme .iter() .map(|scheme| format!(r#""#)) .collect::>() .join("\n "), - domain.host, - domain + host = host, + domains = domain .path .iter() .map(|path| format!(r#""#)) .collect::>() .join("\n "), - domain + path_patterns = domain .path_pattern .iter() .map(|pattern| format!(r#""#)) .collect::>() .join("\n "), - domain + path_prefixes = domain .path_prefix .iter() .map(|prefix| format!(r#""#)) .collect::>() .join("\n "), - domain + path_suffixes = domain .path_suffix .iter() .map(|suffix| format!(r#""#)) .collect::>() .join("\n "), ) + .trim() + .to_string() } fn main() { @@ -68,6 +82,16 @@ fn main() { } if let Some(config) = tauri_plugin::plugin_config::("deep-link") { + let errors: Vec = config + .mobile + .iter() + .filter_map(|d| d.validate().err()) + .collect(); + + if !errors.is_empty() { + panic!("Deep link config validation failed:\n{}", errors.join("\n")); + } + tauri_plugin::mobile::update_android_manifest( "DEEP LINK PLUGIN", "activity", @@ -80,20 +104,89 @@ fn main() { ) .expect("failed to rewrite AndroidManifest.xml"); - #[cfg(target_os = "macos")] + #[cfg(any(target_os = "macos", target_os = "ios"))] { - tauri_plugin::mobile::update_entitlements(|entitlements| { - entitlements.insert( - "com.apple.developer.associated-domains".into(), - config - .mobile - .into_iter() - .map(|d| format!("applinks:{}", d.host).into()) - .collect::>() - .into(), - ); - }) - .expect("failed to update entitlements"); + // we need to ensure that the entitlements are only + // generated for explicit app links and not + // other deep links because then they + // are just going to complain and not be built or signed + let has_app_links = config.mobile.iter().any(|d| d.is_app_link()); + + if !has_app_links { + tauri_plugin::mobile::update_entitlements(|entitlements| { + entitlements.remove("com.apple.developer.associated-domains"); + }) + .expect("failed to update entitlements"); + } else { + tauri_plugin::mobile::update_entitlements(|entitlements| { + entitlements.insert( + "com.apple.developer.associated-domains".into(), + config + .mobile + .iter() + .filter(|d| d.is_app_link()) + .filter_map(|d| d.host.as_ref()) + .map(|host| format!("applinks:{}", host).into()) + .collect::>() + .into(), + ); + }) + .expect("failed to update entitlements"); + } + + let deep_link_domains = config + .mobile + .iter() + .filter_map(|domain| { + if domain.is_app_link() { + return None; + } + + Some(domain) + }) + .collect::>(); + + if deep_link_domains.is_empty() { + tauri_plugin::mobile::update_info_plist(|info_plist| { + info_plist.remove("CFBundleURLTypes"); + }) + .expect("failed to update Info.plist"); + } else { + tauri_plugin::mobile::update_info_plist(|info_plist| { + info_plist.insert( + "CFBundleURLTypes".into(), + deep_link_domains + .iter() + .map(|domain| { + let schemes = domain + .scheme + .iter() + .filter(|scheme| { + scheme.as_str() != "https" && scheme.as_str() != "http" + }) + .collect::>(); + + let mut dict = plist::Dictionary::new(); + dict.insert( + "CFBundleURLSchemes".into(), + schemes + .iter() + .map(|s| s.to_string().into()) + .collect::>() + .into(), + ); + dict.insert( + "CFBundleURLName".into(), + format!("{}", domain.scheme[0]).into(), + ); + plist::Value::Dictionary(dict) + }) + .collect::>() + .into(), + ); + }) + .expect("failed to update Info.plist"); + } } } } diff --git a/plugins/deep-link/examples/app/CHANGELOG.md b/plugins/deep-link/examples/app/CHANGELOG.md index 773bef385..a95225512 100644 --- a/plugins/deep-link/examples/app/CHANGELOG.md +++ b/plugins/deep-link/examples/app/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## \[2.2.6] + +### Dependencies + +- Upgraded to `deep-link-js@2.4.3` + +## \[2.2.5] + +### Dependencies + +- Upgraded to `deep-link-js@2.4.2` + ## \[2.2.4] ### Dependencies diff --git a/plugins/deep-link/examples/app/package.json b/plugins/deep-link/examples/app/package.json index 0df59d850..f7da42f96 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.2.4", + "version": "2.2.6", "type": "module", "scripts": { "dev": "vite", @@ -10,12 +10,12 @@ "tauri": "tauri" }, "dependencies": { - "@tauri-apps/api": "2.7.0", - "@tauri-apps/plugin-deep-link": "2.4.1" + "@tauri-apps/api": "2.8.0", + "@tauri-apps/plugin-deep-link": "2.4.3" }, "devDependencies": { - "@tauri-apps/cli": "2.7.1", + "@tauri-apps/cli": "2.8.4", "typescript": "^5.7.3", - "vite": "^7.0.4" + "vite": "^7.0.7" } } diff --git a/plugins/deep-link/examples/app/src-tauri/gen/android/app/build.gradle.kts b/plugins/deep-link/examples/app/src-tauri/gen/android/app/build.gradle.kts index f434bbfb0..13ec1ffd6 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/android/app/build.gradle.kts +++ b/plugins/deep-link/examples/app/src-tauri/gen/android/app/build.gradle.kts @@ -14,13 +14,13 @@ val tauriProperties = Properties().apply { } android { - compileSdk = 34 + compileSdk = 36 namespace = "com.tauri.deep_link_example" defaultConfig { manifestPlaceholders["usesCleartextTraffic"] = "false" applicationId = "com.tauri.deep_link_example" minSdk = 24 - targetSdk = 34 + targetSdk = 36 versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") } @@ -58,9 +58,10 @@ rust { } dependencies { - implementation("androidx.webkit:webkit:1.6.1") - implementation("androidx.appcompat:appcompat:1.6.1") - implementation("com.google.android.material:material:1.8.0") + implementation("androidx.webkit:webkit:1.14.0") + implementation("androidx.appcompat:appcompat:1.7.1") + implementation("androidx.activity:activity-ktx:1.10.1") + implementation("com.google.android.material:material:1.12.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.4") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") diff --git a/plugins/deep-link/examples/app/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/plugins/deep-link/examples/app/src-tauri/gen/android/app/src/main/AndroidManifest.xml index 05265e325..591f3c61e 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/android/app/src/main/AndroidManifest.xml +++ b/plugins/deep-link/examples/app/src-tauri/gen/android/app/src/main/AndroidManifest.xml @@ -23,23 +23,40 @@ - + - + + + + - + - + + + + + + + + + + + + + + + diff --git a/plugins/deep-link/examples/app/src-tauri/gen/android/app/src/main/java/com/tauri/deep_link_example/MainActivity.kt b/plugins/deep-link/examples/app/src-tauri/gen/android/app/src/main/java/com/tauri/deep_link_example/MainActivity.kt index e13fb9956..313161c22 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/android/app/src/main/java/com/tauri/deep_link_example/MainActivity.kt +++ b/plugins/deep-link/examples/app/src-tauri/gen/android/app/src/main/java/com/tauri/deep_link_example/MainActivity.kt @@ -1,7 +1,11 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - package com.tauri.deep_link_example -class MainActivity : TauriActivity() +import android.os.Bundle +import androidx.activity.enableEdgeToEdge + +class MainActivity : TauriActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + } +} diff --git a/plugins/deep-link/examples/app/src-tauri/gen/android/build.gradle.kts b/plugins/deep-link/examples/app/src-tauri/gen/android/build.gradle.kts index c5ef452a6..607240bc8 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/android/build.gradle.kts +++ b/plugins/deep-link/examples/app/src-tauri/gen/android/build.gradle.kts @@ -4,7 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:8.5.1") + classpath("com.android.tools.build:gradle:8.11.0") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25") } } diff --git a/plugins/deep-link/examples/app/src-tauri/gen/android/buildSrc/build.gradle.kts b/plugins/deep-link/examples/app/src-tauri/gen/android/buildSrc/build.gradle.kts index 39e90b054..5c55bba71 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/android/buildSrc/build.gradle.kts +++ b/plugins/deep-link/examples/app/src-tauri/gen/android/buildSrc/build.gradle.kts @@ -18,6 +18,6 @@ repositories { dependencies { compileOnly(gradleApi()) - implementation("com.android.tools.build:gradle:8.5.1") + implementation("com.android.tools.build:gradle:8.11.0") } diff --git a/plugins/deep-link/examples/app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties b/plugins/deep-link/examples/app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties index 0df10d556..c5f9a53c2 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties +++ b/plugins/deep-link/examples/app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue May 10 19:22:52 CST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/plugins/deep-link/examples/app/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json b/plugins/deep-link/examples/app/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json index dd3b8bcc5..90eea7ec7 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/plugins/deep-link/examples/app/src-tauri/gen/apple/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,116 +1,116 @@ { - "images": [ + "images" : [ { - "size": "20x20", - "idiom": "iphone", - "filename": "AppIcon-20x20@2x.png", - "scale": "2x" + "size" : "20x20", + "idiom" : "iphone", + "filename" : "AppIcon-20x20@2x.png", + "scale" : "2x" }, { - "size": "20x20", - "idiom": "iphone", - "filename": "AppIcon-20x20@3x.png", - "scale": "3x" + "size" : "20x20", + "idiom" : "iphone", + "filename" : "AppIcon-20x20@3x.png", + "scale" : "3x" }, { - "size": "29x29", - "idiom": "iphone", - "filename": "AppIcon-29x29@2x-1.png", - "scale": "2x" + "size" : "29x29", + "idiom" : "iphone", + "filename" : "AppIcon-29x29@2x-1.png", + "scale" : "2x" }, { - "size": "29x29", - "idiom": "iphone", - "filename": "AppIcon-29x29@3x.png", - "scale": "3x" + "size" : "29x29", + "idiom" : "iphone", + "filename" : "AppIcon-29x29@3x.png", + "scale" : "3x" }, { - "size": "40x40", - "idiom": "iphone", - "filename": "AppIcon-40x40@2x.png", - "scale": "2x" + "size" : "40x40", + "idiom" : "iphone", + "filename" : "AppIcon-40x40@2x.png", + "scale" : "2x" }, { - "size": "40x40", - "idiom": "iphone", - "filename": "AppIcon-40x40@3x.png", - "scale": "3x" + "size" : "40x40", + "idiom" : "iphone", + "filename" : "AppIcon-40x40@3x.png", + "scale" : "3x" }, { - "size": "60x60", - "idiom": "iphone", - "filename": "AppIcon-60x60@2x.png", - "scale": "2x" + "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppIcon-60x60@2x.png", + "scale" : "2x" }, { - "size": "60x60", - "idiom": "iphone", - "filename": "AppIcon-60x60@3x.png", - "scale": "3x" + "size" : "60x60", + "idiom" : "iphone", + "filename" : "AppIcon-60x60@3x.png", + "scale" : "3x" }, { - "size": "20x20", - "idiom": "ipad", - "filename": "AppIcon-20x20@1x.png", - "scale": "1x" + "size" : "20x20", + "idiom" : "ipad", + "filename" : "AppIcon-20x20@1x.png", + "scale" : "1x" }, { - "size": "20x20", - "idiom": "ipad", - "filename": "AppIcon-20x20@2x-1.png", - "scale": "2x" + "size" : "20x20", + "idiom" : "ipad", + "filename" : "AppIcon-20x20@2x-1.png", + "scale" : "2x" }, { - "size": "29x29", - "idiom": "ipad", - "filename": "AppIcon-29x29@1x.png", - "scale": "1x" + "size" : "29x29", + "idiom" : "ipad", + "filename" : "AppIcon-29x29@1x.png", + "scale" : "1x" }, { - "size": "29x29", - "idiom": "ipad", - "filename": "AppIcon-29x29@2x.png", - "scale": "2x" + "size" : "29x29", + "idiom" : "ipad", + "filename" : "AppIcon-29x29@2x.png", + "scale" : "2x" }, { - "size": "40x40", - "idiom": "ipad", - "filename": "AppIcon-40x40@1x.png", - "scale": "1x" + "size" : "40x40", + "idiom" : "ipad", + "filename" : "AppIcon-40x40@1x.png", + "scale" : "1x" }, { - "size": "40x40", - "idiom": "ipad", - "filename": "AppIcon-40x40@2x-1.png", - "scale": "2x" + "size" : "40x40", + "idiom" : "ipad", + "filename" : "AppIcon-40x40@2x-1.png", + "scale" : "2x" }, { - "size": "76x76", - "idiom": "ipad", - "filename": "AppIcon-76x76@1x.png", - "scale": "1x" + "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppIcon-76x76@1x.png", + "scale" : "1x" }, { - "size": "76x76", - "idiom": "ipad", - "filename": "AppIcon-76x76@2x.png", - "scale": "2x" + "size" : "76x76", + "idiom" : "ipad", + "filename" : "AppIcon-76x76@2x.png", + "scale" : "2x" }, { - "size": "83.5x83.5", - "idiom": "ipad", - "filename": "AppIcon-83.5x83.5@2x.png", - "scale": "2x" + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "AppIcon-83.5x83.5@2x.png", + "scale" : "2x" }, { - "size": "1024x1024", - "idiom": "ios-marketing", - "filename": "AppIcon-512@2x.png", - "scale": "1x" + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "AppIcon-512@2x.png", + "scale" : "1x" } ], - "info": { - "version": 1, - "author": "xcode" + "info" : { + "version" : 1, + "author" : "xcode" } -} +} \ No newline at end of file diff --git a/plugins/deep-link/examples/app/src-tauri/gen/apple/Assets.xcassets/Contents.json b/plugins/deep-link/examples/app/src-tauri/gen/apple/Assets.xcassets/Contents.json index 97a8662eb..da4a164c9 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/apple/Assets.xcassets/Contents.json +++ b/plugins/deep-link/examples/app/src-tauri/gen/apple/Assets.xcassets/Contents.json @@ -1,6 +1,6 @@ { - "info": { - "version": 1, - "author": "xcode" + "info" : { + "version" : 1, + "author" : "xcode" } -} +} \ No newline at end of file diff --git a/plugins/deep-link/examples/app/src-tauri/gen/apple/LaunchScreen.storyboard b/plugins/deep-link/examples/app/src-tauri/gen/apple/LaunchScreen.storyboard index dd79351ec..81b5f90e2 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/apple/LaunchScreen.storyboard +++ b/plugins/deep-link/examples/app/src-tauri/gen/apple/LaunchScreen.storyboard @@ -1,5 +1,5 @@ - + diff --git a/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example.xcodeproj/project.pbxproj b/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example.xcodeproj/project.pbxproj index 450bd8470..4c01a958a 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example.xcodeproj/project.pbxproj +++ b/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 63; objects = { /* Begin PBXBuildFile section */ @@ -167,6 +167,8 @@ dependencies = ( ); name = "deep-link-example_iOS"; + packageProductDependencies = ( + ); productName = "deep-link-example_iOS"; productReference = 1CAAFA750FD735A285DC1238 /* deep-link-example.app */; productType = "com.apple.product-type.application"; @@ -189,6 +191,7 @@ en, ); mainGroup = 1DC58B1705AA3ECC6B887FE7; + minimizedProjectReferenceProxies = 1; projectDirPath = ""; projectRoot = ""; targets = ( @@ -227,7 +230,6 @@ outputPaths = ( "$(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a", "$(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a", - "$(SRCROOT)/Externals/arm64-sim/${CONFIGURATION}/libapp.a", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -314,18 +316,13 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ARCHS = ( - arm64, - "arm64-sim", - ); + ARCHS = arm64; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "deep-link-example_iOS/deep-link-example_iOS.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = Q93MBH6S2F; + DEVELOPMENT_TEAM = "Q93MBH6S2F"; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64"; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; + "EXCLUDED_ARCHS[sdk=iphoneos*]" = x86_64; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\".\"", @@ -335,13 +332,6 @@ "$(inherited)", "@executable_path/Frameworks", ); - "LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = ( - "$(inherited)", - "$(PROJECT_DIR)/Externals/arm64-sim/$(CONFIGURATION)", - "$(SDKROOT)/usr/lib/swift", - "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", - "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", - ); "LIBRARY_SEARCH_PATHS[arch=arm64]" = ( "$(inherited)", "$(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION)", @@ -360,7 +350,7 @@ PRODUCT_NAME = "deep-link-example"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = "arm64 arm64-sim"; + VALID_ARCHS = arm64; }; name = debug; }; @@ -424,18 +414,13 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ARCHS = ( - arm64, - "arm64-sim", - ); + ARCHS = arm64; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "deep-link-example_iOS/deep-link-example_iOS.entitlements"; CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = Q93MBH6S2F; + DEVELOPMENT_TEAM = "Q93MBH6S2F"; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64"; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; + "EXCLUDED_ARCHS[sdk=iphoneos*]" = x86_64; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "\".\"", @@ -445,13 +430,6 @@ "$(inherited)", "@executable_path/Frameworks", ); - "LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = ( - "$(inherited)", - "$(PROJECT_DIR)/Externals/arm64-sim/$(CONFIGURATION)", - "$(SDKROOT)/usr/lib/swift", - "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", - "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", - ); "LIBRARY_SEARCH_PATHS[arch=arm64]" = ( "$(inherited)", "$(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION)", @@ -470,7 +448,7 @@ PRODUCT_NAME = "deep-link-example"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; - VALID_ARCHS = "arm64 arm64-sim"; + VALID_ARCHS = arm64; }; name = release; }; diff --git a/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example_iOS/Info.plist b/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example_iOS/Info.plist index 7ce866140..bc74b01bc 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example_iOS/Info.plist +++ b/plugins/deep-link/examples/app/src-tauri/gen/apple/deep-link-example_iOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.0.0 + 0.1.0 CFBundleVersion 0.1.0 LSRequiresIPhoneOS @@ -40,5 +40,16 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + CFBundleURLTypes + + + CFBundleURLSchemes + + taurideeplink + + CFBundleURLName + taurideeplink + + \ No newline at end of file diff --git a/plugins/deep-link/examples/app/src-tauri/gen/apple/project.yml b/plugins/deep-link/examples/app/src-tauri/gen/apple/project.yml index c924ca77b..74d0ab491 100644 --- a/plugins/deep-link/examples/app/src-tauri/gen/apple/project.yml +++ b/plugins/deep-link/examples/app/src-tauri/gen/apple/project.yml @@ -1,7 +1,3 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy -# SPDX-License-Identifier: Apache-2.0 -# SPDX-License-Identifier: MIT - name: deep-link-example options: bundleIdPrefix: com.tauri.deep-link-example @@ -16,7 +12,6 @@ settingGroups: base: PRODUCT_NAME: deep-link-example PRODUCT_BUNDLE_IDENTIFIER: com.tauri.deep-link-example - DEVELOPMENT_TEAM: Q93MBH6S2F targetTemplates: app: type: application @@ -56,8 +51,8 @@ targets: - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - CFBundleShortVersionString: 0.0.0 - CFBundleVersion: 0.0.0 + CFBundleShortVersionString: 0.1.0 + CFBundleVersion: '0.1.0' entitlements: path: deep-link-example_iOS/deep-link-example_iOS.entitlements scheme: @@ -67,14 +62,12 @@ targets: settings: base: ENABLE_BITCODE: false - ARCHS: [arm64, arm64-sim] - VALID_ARCHS: arm64 arm64-sim + ARCHS: [arm64] + VALID_ARCHS: arm64 LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) - LIBRARY_SEARCH_PATHS[arch=arm64-sim]: $(inherited) $(PROJECT_DIR)/Externals/arm64-sim/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true - EXCLUDED_ARCHS[sdk=iphonesimulator*]: arm64 - EXCLUDED_ARCHS[sdk=iphoneos*]: arm64-sim x86_64 + EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64 groups: [app] dependencies: - framework: libapp.a @@ -93,4 +86,3 @@ targets: outputFiles: - $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a - $(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a - - $(SRCROOT)/Externals/arm64-sim/${CONFIGURATION}/libapp.a diff --git a/plugins/deep-link/examples/app/src-tauri/tauri.conf.json b/plugins/deep-link/examples/app/src-tauri/tauri.conf.json index ac1c292b4..61f7394df 100644 --- a/plugins/deep-link/examples/app/src-tauri/tauri.conf.json +++ b/plugins/deep-link/examples/app/src-tauri/tauri.conf.json @@ -35,6 +35,9 @@ }, { "host": "tauri.app" + }, + { + "scheme": ["taurideeplink"] } ], "desktop": { diff --git a/plugins/deep-link/package.json b/plugins/deep-link/package.json index b316c481d..7eebc79ef 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.4.1", + "version": "2.4.3", "description": "Set your Tauri application as the default handler for an URL", "license": "MIT OR Apache-2.0", "authors": [ @@ -25,6 +25,9 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" + }, + "devDependencies": { + "@tauri-apps/cli": "2.8.4" } } diff --git a/plugins/deep-link/src/config.rs b/plugins/deep-link/src/config.rs index 88222a246..49de68e3d 100644 --- a/plugins/deep-link/src/config.rs +++ b/plugins/deep-link/src/config.rs @@ -11,10 +11,8 @@ use tauri_utils::config::DeepLinkProtocol; pub struct AssociatedDomain { #[serde(default = "default_schemes")] pub scheme: Vec, - - #[serde(deserialize_with = "deserialize_associated_host")] - pub host: String, - + #[serde(default, deserialize_with = "deserialize_associated_host")] + pub host: Option, // Optional custom uri schemes dont NEED a host (may have one still), but required for https/http schemes #[serde(default)] pub path: Vec, #[serde(default, alias = "path-pattern", rename = "pathPattern")] @@ -23,6 +21,41 @@ pub struct AssociatedDomain { pub path_prefix: Vec, #[serde(default, alias = "path-suffix", rename = "pathSuffix")] pub path_suffix: Vec, + #[serde(default, alias = "app-link", rename = "appLink")] + pub app_link: Option, +} + +impl AssociatedDomain { + /// Returns true if the domain uses http or https scheme. + pub fn is_web_link(&self) -> bool { + self.scheme.iter().any(|s| s == "https" || s == "http") + } + + /// Returns true if the domain uses http or https scheme and has proper host configuration. + pub fn is_app_link(&self) -> bool { + self.app_link + .unwrap_or_else(|| self.is_web_link() && self.host.is_some()) + } + + pub fn validate(&self) -> Result<(), String> { + // Rule 1: All web links require a host. + if self.is_web_link() && self.host.is_none() { + return Err("Web link requires a host".into()); + } + + // Rule 2: If it's an App Link, ensure http(s) and host. + if self.is_app_link() { + if !self.is_web_link() { + return Err("AppLink must be a valid web link (https/http + host)".into()); + } + if self.scheme.iter().any(|s| s == "http") && !self.scheme.iter().any(|s| s == "https") + { + eprintln!("Warning: AppLink uses only 'http' — allowed on Android but not secure for production."); + } + } + + Ok(()) + } } // TODO: Consider removing this in v3 @@ -30,18 +63,19 @@ fn default_schemes() -> Vec { vec!["https".to_string(), "http".to_string()] } -fn deserialize_associated_host<'de, D>(deserializer: D) -> Result +fn deserialize_associated_host<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { - let host = String::deserialize(deserializer)?; - if let Some((scheme, _host)) = host.split_once("://") { - Err(serde::de::Error::custom(format!( - "host `{host}` cannot start with a scheme, please remove the `{scheme}://` prefix" - ))) - } else { - Ok(host) + let opt = Option::::deserialize(deserializer)?; + if let Some(ref host) = opt { + if let Some((scheme, _)) = host.split_once("://") { + return Err(serde::de::Error::custom(format!( + "host `{host}` cannot start with a scheme, please remove the `{scheme}://` prefix" + ))); + } } + Ok(opt) } #[derive(Deserialize, Clone)] diff --git a/plugins/deep-link/src/error.rs b/plugins/deep-link/src/error.rs index 88c71e8a5..41eb764f1 100644 --- a/plugins/deep-link/src/error.rs +++ b/plugins/deep-link/src/error.rs @@ -28,6 +28,16 @@ pub enum Error { PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError), } +// TODO(v3): change this into an error in v3, +// see . +#[inline] +#[cfg(target_os = "linux")] +pub(crate) fn inspect_command_error<'a>(command: &'a str) -> impl Fn(&std::io::Error) + 'a { + move |e| { + tracing::error!("Failed to run OS command `{command}`: {e}"); + } +} + impl Serialize for Error { fn serialize(&self, serializer: S) -> std::result::Result where diff --git a/plugins/deep-link/src/lib.rs b/plugins/deep-link/src/lib.rs index 1cd13b580..00757bf2a 100644 --- a/plugins/deep-link/src/lib.rs +++ b/plugins/deep-link/src/lib.rs @@ -254,6 +254,7 @@ mod imp { /// /// ## Platform-specific: /// + /// - **Linux**: Needs the `xdg-mime` and `update-desktop-database` commands available on the system. /// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`). pub fn register>(&self, _protocol: S) -> crate::Result<()> { #[cfg(windows)] @@ -292,6 +293,7 @@ mod imp { .unwrap_or_else(|| bin.into_os_string()) .to_string_lossy() .to_string(); + let qualified_exec = format!("{} %u", exec); let target = self.app.path().data_dir()?.join("applications"); @@ -303,12 +305,28 @@ mod imp { if let Ok(mut desktop_file) = ini::Ini::load_from_file(&target_file) { if let Some(section) = desktop_file.section_mut(Some("Desktop Entry")) { - // it's ok to remove it - we only write to the file if it's missing - // and in that case we include old_mimes let old_mimes = section.remove("MimeType").unwrap_or_default(); + let mut change = false; + // if the mime type is not present, append it to the list if !old_mimes.split(';').any(|mime| mime == mime_type) { section.append("MimeType", format!("{mime_type};{old_mimes}")); + change = true; + } else { + section.insert("MimeType".to_string(), old_mimes); + } + + // if the exec command doesnt match, update to the new one + let old_exec = section.remove("Exec").unwrap_or_default(); + if old_exec != qualified_exec { + section.append("Exec", qualified_exec); + change = true; + } else { + section.insert("Exec".to_string(), old_exec.to_string()); + } + + // if any property has changed, rewrite the .desktop file + if change { desktop_file.write_to_file(&target_file)?; } } @@ -323,7 +341,7 @@ mod imp { .product_name .clone() .unwrap_or_else(|| file_name.clone()), - exec = exec, + qualified_exec = qualified_exec, mime_type = mime_type ) .as_bytes(), @@ -332,11 +350,15 @@ mod imp { Command::new("update-desktop-database") .arg(target) - .status()?; + .status() + .inspect_err(crate::error::inspect_command_error( + "update-desktop-database", + ))?; Command::new("xdg-mime") .args(["default", &file_name, mime_type.as_str()]) - .status()?; + .status() + .inspect_err(crate::error::inspect_command_error("xdg-mime"))?; Ok(()) } @@ -405,6 +427,7 @@ mod imp { /// /// ## Platform-specific: /// + /// - **Linux**: Needs the `xdg-mime` command available on the system. /// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`). pub fn is_registered>(&self, _protocol: S) -> crate::Result { #[cfg(windows)] @@ -439,7 +462,8 @@ mod imp { "default", &format!("x-scheme-handler/{}", _protocol.as_ref()), ]) - .output()?; + .output() + .inspect_err(crate::error::inspect_command_error("xdg-mime"))?; Ok(String::from_utf8_lossy(&output.stdout).contains(&file_name)) } diff --git a/plugins/deep-link/src/template.desktop b/plugins/deep-link/src/template.desktop index 0fb89abb4..068beb103 100644 --- a/plugins/deep-link/src/template.desktop +++ b/plugins/deep-link/src/template.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Type=Application Name={name} -Exec={exec} %u +Exec={qualified_exec} Terminal=false MimeType={mime_type} -NoDisplay=true \ No newline at end of file +NoDisplay=true diff --git a/plugins/dialog/CHANGELOG.md b/plugins/dialog/CHANGELOG.md index b5fe23475..fd5f8dd2d 100644 --- a/plugins/dialog/CHANGELOG.md +++ b/plugins/dialog/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## \[2.4.0] + +- [`509eba8d`](https://github.com/tauri-apps/plugins-workspace/commit/509eba8d441c4f6ecf0af77b572cb2afd69a752d) ([#2641](https://github.com/tauri-apps/plugins-workspace/pull/2641) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Add support for showing a message dialog with 3 buttons. + +## \[2.3.3] + +### Dependencies + +- Upgraded to `fs-js@2.4.2` + ## \[2.3.2] - [`af08c66f`](https://github.com/tauri-apps/plugins-workspace/commit/af08c66faafe0dffc4b0a80aef030cd3f0f89a9c) ([#2871](https://github.com/tauri-apps/plugins-workspace/pull/2871) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused the file picker not to open on Android when extension filters were set. diff --git a/plugins/dialog/Cargo.toml b/plugins/dialog/Cargo.toml index c57ad66b9..7856e68db 100644 --- a/plugins/dialog/Cargo.toml +++ b/plugins/dialog/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-dialog" -version = "2.3.2" +version = "2.4.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.4.1" } +tauri-plugin-fs = { path = "../fs", version = "2.4.2" } [target.'cfg(target_os = "ios")'.dependencies] tauri = { workspace = true, features = ["wry"] } diff --git a/plugins/dialog/android/src/main/java/DialogPlugin.kt b/plugins/dialog/android/src/main/java/DialogPlugin.kt index 1d87a6e91..b93596353 100644 --- a/plugins/dialog/android/src/main/java/DialogPlugin.kt +++ b/plugins/dialog/android/src/main/java/DialogPlugin.kt @@ -38,6 +38,7 @@ class MessageOptions { var title: String? = null lateinit var message: String var okButtonLabel: String? = null + var noButtonLabel: String? = null var cancelButtonLabel: String? = null } @@ -139,9 +140,8 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) { return } - val handler = { cancelled: Boolean, value: Boolean -> + val handler = { value: String -> val ret = JSObject() - ret.put("cancelled", cancelled) ret.put("value", value) invoke.resolve(ret) } @@ -153,24 +153,34 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) { if (args.title != null) { builder.setTitle(args.title) } + + val okButtonLabel = args.okButtonLabel ?: "Ok" + builder .setMessage(args.message) - .setPositiveButton( - args.okButtonLabel ?: "OK" - ) { dialog, _ -> + .setPositiveButton(okButtonLabel) { dialog, _ -> dialog.dismiss() - handler(false, true) + handler(okButtonLabel) } .setOnCancelListener { dialog -> dialog.dismiss() - handler(true, false) + handler(args.cancelButtonLabel ?: "Cancel") } + + if (args.noButtonLabel != null) { + builder.setNeutralButton(args.noButtonLabel) { dialog, _ -> + dialog.dismiss() + handler(args.noButtonLabel!!) + } + } + if (args.cancelButtonLabel != null) { builder.setNegativeButton( args.cancelButtonLabel) { dialog, _ -> dialog.dismiss() - handler(false, false) + handler(args.cancelButtonLabel!!) } } + val dialog = builder.create() dialog.show() } diff --git a/plugins/dialog/api-iife.js b/plugins/dialog/api-iife.js index c2e0870c8..a357f2c0b 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,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__})} +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)}function e(t){if(void 0!==t)return"string"==typeof t?t:"ok"in t&&"cancel"in t?{OkCancelCustom:[t.ok,t.cancel]}:"yes"in t&&"no"in t&&"cancel"in t?{YesNoCancelCustom:[t.yes,t.no,t.cancel]}:"ok"in t?{OkCustom:t.ok}:void 0}return"function"==typeof SuppressedError&&SuppressedError,t.ask=async function(t,e){const o="string"==typeof e?{title:e}:e;return await n("plugin:dialog|ask",{message:t.toString(),title:o?.title?.toString(),kind:o?.kind,yesButtonLabel:o?.okLabel?.toString(),noButtonLabel:o?.cancelLabel?.toString()})},t.confirm=async function(t,e){const o="string"==typeof e?{title:e}:e;return await n("plugin:dialog|confirm",{message:t.toString(),title:o?.title?.toString(),kind:o?.kind,okButtonLabel:o?.okLabel?.toString(),cancelButtonLabel:o?.cancelLabel?.toString()})},t.message=async function(t,o){const i="string"==typeof o?{title:o}:o;return n("plugin:dialog|message",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString(),buttons:e(i?.buttons)})},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 150be95a0..a77857545 100644 --- a/plugins/dialog/guest-js/index.ts +++ b/plugins/dialog/guest-js/index.ts @@ -77,6 +77,80 @@ interface SaveDialogOptions { canCreateDirectories?: boolean } +/** + * Default buttons for a message dialog. + * + * @since 2.4.0 + */ +export type MessageDialogDefaultButtons = + | 'Ok' + | 'OkCancel' + | 'YesNo' + | 'YesNoCancel' + +/** All possible button keys. */ +type ButtonKey = 'ok' | 'cancel' | 'yes' | 'no' + +/** Ban everything except a set of keys. */ +type BanExcept = Partial< + Record, never> +> + +/** + * The Yes, No and Cancel buttons of a message dialog. + * + * @since 2.4.0 + */ +export type MessageDialogButtonsYesNoCancel = { + /** The Yes button. */ + yes: string + /** The No button. */ + no: string + /** The Cancel button. */ + cancel: string +} & BanExcept<'yes' | 'no' | 'cancel'> + +/** + * The Ok and Cancel buttons of a message dialog. + * + * @since 2.4.0 + */ +export type MessageDialogButtonsOkCancel = { + /** The Ok button. */ + ok: string + /** The Cancel button. */ + cancel: string +} & BanExcept<'ok' | 'cancel'> + +/** + * The Ok button of a message dialog. + * + * @since 2.4.0 + */ +export type MessageDialogButtonsOk = { + /** The Ok button. */ + ok: string +} & BanExcept<'ok'> + +/** + * Custom buttons for a message dialog. + * + * @since 2.4.0 + */ +export type MessageDialogCustomButtons = + | MessageDialogButtonsYesNoCancel + | MessageDialogButtonsOkCancel + | MessageDialogButtonsOk + +/** + * The buttons of a message dialog. + * + * @since 2.4.0 + */ +export type MessageDialogButtons = + | MessageDialogDefaultButtons + | MessageDialogCustomButtons + /** * @since 2.0.0 */ @@ -85,8 +159,58 @@ interface MessageDialogOptions { title?: string /** The kind of the dialog. Defaults to `info`. */ kind?: 'info' | 'warning' | 'error' - /** The label of the confirm button. */ + /** + * The label of the Ok button. + * + * @deprecated Use {@linkcode MessageDialogOptions.buttons} instead. + */ okLabel?: string + /** + * The buttons of the dialog. + * + * @example + * + * ```ts + * // Use system default buttons texts + * await message('Hello World!', { buttons: 'Ok' }) + * await message('Hello World!', { buttons: 'OkCancel' }) + * + * // Or with custom button texts + * await message('Hello World!', { buttons: { ok: 'Yes!' } }) + * await message('Take on the task?', { + * buttons: { ok: 'Accept', cancel: 'Cancel' } + * }) + * await message('Show the file content?', { + * buttons: { yes: 'Show content', no: 'Show in folder', cancel: 'Cancel' } + * }) + * ``` + * + * @since 2.4.0 + */ + buttons?: MessageDialogButtons +} + +/** + * Internal function to convert the buttons to the Rust type. + */ +function buttonsToRust(buttons: MessageDialogButtons | undefined) { + if (buttons === undefined) { + return undefined + } + + if (typeof buttons === 'string') { + return buttons + } else if ('ok' in buttons && 'cancel' in buttons) { + return { OkCancelCustom: [buttons.ok, buttons.cancel] } + } else if ('yes' in buttons && 'no' in buttons && 'cancel' in buttons) { + return { + YesNoCancelCustom: [buttons.yes, buttons.no, buttons.cancel] + } + } else if ('ok' in buttons) { + return { OkCustom: buttons.ok } + } + + return undefined } interface ConfirmDialogOptions { @@ -202,6 +326,16 @@ async function save(options: SaveDialogOptions = {}): Promise { return await invoke('plugin:dialog|save', { options }) } +/** + * The result of a message dialog. + * + * The result is a string if the dialog has custom buttons, + * otherwise it is one of the default buttons. + * + * @since 2.4.0 + */ +export type MessageDialogResult = 'Yes' | 'No' | 'Ok' | 'Cancel' | (string & {}) + /** * Shows a message dialog with an `Ok` button. * @example @@ -222,13 +356,15 @@ async function save(options: SaveDialogOptions = {}): Promise { async function message( message: string, options?: string | MessageDialogOptions -): Promise { +): Promise { const opts = typeof options === 'string' ? { title: options } : options - await invoke('plugin:dialog|message', { + + return invoke('plugin:dialog|message', { message: message.toString(), title: opts?.title?.toString(), kind: opts?.kind, - okButtonLabel: opts?.okLabel?.toString() + okButtonLabel: opts?.okLabel?.toString(), + buttons: buttonsToRust(opts?.buttons) }) } diff --git a/plugins/dialog/ios/Sources/DialogPlugin.swift b/plugins/dialog/ios/Sources/DialogPlugin.swift index b3f7e7da6..710fd0bb2 100644 --- a/plugins/dialog/ios/Sources/DialogPlugin.swift +++ b/plugins/dialog/ios/Sources/DialogPlugin.swift @@ -20,6 +20,7 @@ struct MessageDialogOptions: Decodable { var title: String? let message: String var okButtonLabel: String? + var noButtonLabel: String? var cancelButtonLabel: String? } @@ -200,36 +201,38 @@ class DialogPlugin: Plugin { let alert = UIAlertController( title: args.title, message: args.message, preferredStyle: UIAlertController.Style.alert) - let cancelButtonLabel = args.cancelButtonLabel ?? "" - if !cancelButtonLabel.isEmpty { + if let cancelButtonLabel = args.cancelButtonLabel { alert.addAction( UIAlertAction( title: cancelButtonLabel, style: UIAlertAction.Style.default, handler: { (_) -> Void in - Logger.error("cancel") - - invoke.resolve([ - "value": false, - "cancelled": false, - ]) - })) + invoke.resolve(["value": cancelButtonLabel]) + } + ) + ) } - let okButtonLabel = args.okButtonLabel ?? (cancelButtonLabel.isEmpty ? "OK" : "") - if !okButtonLabel.isEmpty { + if let noButtonLabel = args.noButtonLabel { alert.addAction( UIAlertAction( - title: okButtonLabel, style: UIAlertAction.Style.default, + title: noButtonLabel, style: UIAlertAction.Style.default, handler: { (_) -> Void in - Logger.error("ok") - - invoke.resolve([ - "value": true, - "cancelled": false, - ]) - })) + invoke.resolve(["value": noButtonLabel]) + } + ) + ) } + let okButtonLabel = args.okButtonLabel ?? "Ok" + alert.addAction( + UIAlertAction( + title: okButtonLabel, style: UIAlertAction.Style.default, + handler: { (_) -> Void in + invoke.resolve(["value": okButtonLabel]) + } + ) + ) + manager.viewController?.present(alert, animated: true, completion: nil) } } diff --git a/plugins/dialog/package.json b/plugins/dialog/package.json index 4412461cc..ac363a667 100644 --- a/plugins/dialog/package.json +++ b/plugins/dialog/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-dialog", - "version": "2.3.2", + "version": "2.4.0", "license": "MIT OR Apache-2.0", "authors": [ "Tauri Programme within The Commons Conservancy" @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/dialog/src/commands.rs b/plugins/dialog/src/commands.rs index c3caf027f..5298de9d0 100644 --- a/plugins/dialog/src/commands.rs +++ b/plugins/dialog/src/commands.rs @@ -9,8 +9,8 @@ use tauri::{command, Manager, Runtime, State, Window}; use tauri_plugin_fs::FsExt; use crate::{ - Dialog, FileDialogBuilder, FilePath, MessageDialogButtons, MessageDialogKind, Result, CANCEL, - NO, OK, YES, + Dialog, FileDialogBuilder, FilePath, MessageDialogBuilder, MessageDialogButtons, + MessageDialogKind, MessageDialogResult, Result, CANCEL, NO, OK, YES, }; #[derive(Serialize)] @@ -248,7 +248,7 @@ fn message_dialog( message: String, kind: Option, buttons: MessageDialogButtons, -) -> bool { +) -> MessageDialogBuilder { let mut builder = dialog.message(message); builder = builder.buttons(buttons); @@ -266,7 +266,7 @@ fn message_dialog( builder = builder.kind(kind); } - builder.blocking_show() + builder } #[command] @@ -277,19 +277,15 @@ pub(crate) async fn message( message: String, kind: Option, ok_button_label: Option, -) -> Result { - Ok(message_dialog( - window, - dialog, - title, - message, - kind, - if let Some(ok_button_label) = ok_button_label { - MessageDialogButtons::OkCustom(ok_button_label) - } else { - MessageDialogButtons::Ok - }, - )) + buttons: Option, +) -> Result { + let buttons = buttons.unwrap_or(if let Some(ok_button_label) = ok_button_label { + MessageDialogButtons::OkCustom(ok_button_label) + } else { + MessageDialogButtons::Ok + }); + + Ok(message_dialog(window, dialog, title, message, kind, buttons).blocking_show_with_result()) } #[command] @@ -302,7 +298,7 @@ pub(crate) async fn ask( yes_button_label: Option, no_button_label: Option, ) -> Result { - Ok(message_dialog( + let dialog = message_dialog( window, dialog, title, @@ -318,7 +314,9 @@ pub(crate) async fn ask( } else { MessageDialogButtons::YesNo }, - )) + ); + + Ok(dialog.blocking_show()) } #[command] @@ -331,7 +329,7 @@ pub(crate) async fn confirm( ok_button_label: Option, cancel_button_label: Option, ) -> Result { - Ok(message_dialog( + let dialog = message_dialog( window, dialog, title, @@ -347,5 +345,7 @@ pub(crate) async fn confirm( } else { MessageDialogButtons::OkCancel }, - )) + ); + + Ok(dialog.blocking_show()) } diff --git a/plugins/dialog/src/desktop.rs b/plugins/dialog/src/desktop.rs index d1a3e8b21..8d3b08f95 100644 --- a/plugins/dialog/src/desktop.rs +++ b/plugins/dialog/src/desktop.rs @@ -13,7 +13,7 @@ use rfd::{AsyncFileDialog, AsyncMessageDialog}; use serde::de::DeserializeOwned; use tauri::{plugin::PluginApi, AppHandle, Runtime}; -use crate::{models::*, FileDialogBuilder, FilePath, MessageDialogBuilder, OK}; +use crate::{models::*, FileDialogBuilder, FilePath, MessageDialogBuilder}; pub fn init( app: &AppHandle, @@ -115,6 +115,10 @@ impl From for rfd::MessageButtons { MessageDialogButtons::YesNo => Self::YesNo, MessageDialogButtons::OkCustom(ok) => Self::OkCustom(ok), MessageDialogButtons::OkCancelCustom(ok, cancel) => Self::OkCancelCustom(ok, cancel), + MessageDialogButtons::YesNoCancel => Self::YesNoCancel, + MessageDialogButtons::YesNoCancelCustom(yes, no, cancel) => { + Self::YesNoCancelCustom(yes, no, cancel) + } } } } @@ -208,28 +212,46 @@ pub fn save_file) + Send + 'static>( } /// Shows a message dialog -pub fn show_message_dialog( +pub fn show_message_dialog( dialog: MessageDialogBuilder, - f: F, + callback: F, ) { - use rfd::MessageDialogResult; - - let ok_label = match &dialog.buttons { - MessageDialogButtons::OkCustom(ok) => Some(ok.clone()), - MessageDialogButtons::OkCancelCustom(ok, _) => Some(ok.clone()), - _ => None, - }; - let f = move |res| { - f(match res { - MessageDialogResult::Ok | MessageDialogResult::Yes => true, - MessageDialogResult::Custom(s) => ok_label.map_or(s == OK, |ok_label| ok_label == s), - _ => false, - }); - }; + let f = move |res: rfd::MessageDialogResult| callback(res.into()); let handle = dialog.dialog.app_handle().to_owned(); let _ = handle.run_on_main_thread(move || { + let buttons = dialog.buttons.clone(); let dialog = AsyncMessageDialog::from(dialog).show(); - std::thread::spawn(move || f(tauri::async_runtime::block_on(dialog))); + std::thread::spawn(move || { + let result = tauri::async_runtime::block_on(dialog); + // on Linux rfd does not return rfd::MessageDialogResult::Custom, so we must map manually + let result = match (result, buttons) { + (rfd::MessageDialogResult::Ok, MessageDialogButtons::OkCustom(s)) => { + rfd::MessageDialogResult::Custom(s) + } + ( + rfd::MessageDialogResult::Ok, + MessageDialogButtons::OkCancelCustom(ok, _cancel), + ) => rfd::MessageDialogResult::Custom(ok), + ( + rfd::MessageDialogResult::Cancel, + MessageDialogButtons::OkCancelCustom(_ok, cancel), + ) => rfd::MessageDialogResult::Custom(cancel), + ( + rfd::MessageDialogResult::Yes, + MessageDialogButtons::YesNoCancelCustom(yes, _no, _cancel), + ) => rfd::MessageDialogResult::Custom(yes), + ( + rfd::MessageDialogResult::No, + MessageDialogButtons::YesNoCancelCustom(_yes, no, _cancel), + ) => rfd::MessageDialogResult::Custom(no), + ( + rfd::MessageDialogResult::Cancel, + MessageDialogButtons::YesNoCancelCustom(_yes, _no, cancel), + ) => rfd::MessageDialogResult::Custom(cancel), + (result, _) => result, + }; + f(result); + }); }); } diff --git a/plugins/dialog/src/lib.rs b/plugins/dialog/src/lib.rs index 2ef1c1ead..17d9a829d 100644 --- a/plugins/dialog/src/lib.rs +++ b/plugins/dialog/src/lib.rs @@ -216,6 +216,7 @@ pub(crate) struct MessageDialogPayload<'a> { message: &'a String, kind: &'a MessageDialogKind, ok_button_label: Option<&'a str>, + no_button_label: Option<&'a str>, cancel_button_label: Option<&'a str>, } @@ -238,13 +239,17 @@ impl MessageDialogBuilder { #[cfg(mobile)] pub(crate) fn payload(&self) -> MessageDialogPayload<'_> { - 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)), + let (ok_button_label, no_button_label, cancel_button_label) = match &self.buttons { + MessageDialogButtons::Ok => (Some(OK), None, None), + MessageDialogButtons::OkCancel => (Some(OK), None, Some(CANCEL)), + MessageDialogButtons::YesNo => (Some(YES), Some(NO), None), + MessageDialogButtons::YesNoCancel => (Some(YES), Some(NO), Some(CANCEL)), + MessageDialogButtons::OkCustom(ok) => (Some(ok.as_str()), None, None), MessageDialogButtons::OkCancelCustom(ok, cancel) => { - (Some(ok.as_str()), Some(cancel.as_str())) + (Some(ok.as_str()), None, Some(cancel.as_str())) + } + MessageDialogButtons::YesNoCancelCustom(yes, no, cancel) => { + (Some(yes.as_str()), Some(no.as_str()), Some(cancel.as_str())) } }; MessageDialogPayload { @@ -252,6 +257,7 @@ impl MessageDialogBuilder { message: &self.message, kind: &self.kind, ok_button_label, + no_button_label, cancel_button_label, } } @@ -295,16 +301,55 @@ impl MessageDialogBuilder { } /// Shows a message dialog + /// + /// Returns `true` if the user pressed the OK/Yes button, pub fn show(self, f: F) { + let ok_label = match &self.buttons { + MessageDialogButtons::OkCustom(ok) => Some(ok.clone()), + MessageDialogButtons::OkCancelCustom(ok, _) => Some(ok.clone()), + MessageDialogButtons::YesNoCancelCustom(yes, _, _) => Some(yes.clone()), + _ => None, + }; + + show_message_dialog(self, move |res| { + let sucess = match res { + MessageDialogResult::Ok | MessageDialogResult::Yes => true, + MessageDialogResult::Custom(s) => { + ok_label.map_or(s == OK, |ok_label| ok_label == s) + } + _ => false, + }; + + f(sucess) + }) + } + + /// Shows a message dialog and returns the button that was pressed. + /// + /// Returns a [`MessageDialogResult`] enum that indicates which button was pressed. + pub fn show_with_result(self, f: F) { show_message_dialog(self, f) } /// Shows a message dialog. + /// + /// Returns `true` if the user pressed the OK/Yes button, + /// /// This is a blocking operation, /// and should *NOT* be used when running on the main thread context. pub fn blocking_show(self) -> bool { blocking_fn!(self, show) } + + /// Shows a message dialog and returns the button that was pressed. + /// + /// Returns a [`MessageDialogResult`] enum that indicates which button was pressed. + /// + /// This is a blocking operation, + /// and should *NOT* be used when running on the main thread context. + pub fn blocking_show_with_result(self) -> MessageDialogResult { + blocking_fn!(self, show_with_result) + } } #[derive(Debug, Serialize)] pub(crate) struct Filter { diff --git a/plugins/dialog/src/mobile.rs b/plugins/dialog/src/mobile.rs index b73def4f9..46ea3a276 100644 --- a/plugins/dialog/src/mobile.rs +++ b/plugins/dialog/src/mobile.rs @@ -8,7 +8,7 @@ use tauri::{ AppHandle, Runtime, }; -use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder}; +use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder, MessageDialogResult}; #[cfg(target_os = "android")] const PLUGIN_IDENTIFIER: &str = "app.tauri.dialog"; @@ -107,13 +107,11 @@ pub fn save_file) + Send + 'static>( #[derive(Debug, Deserialize)] struct ShowMessageDialogResponse { - #[allow(dead_code)] - cancelled: bool, - value: bool, + value: String, } /// Shows a message dialog -pub fn show_message_dialog( +pub fn show_message_dialog( dialog: MessageDialogBuilder, f: F, ) { @@ -122,6 +120,8 @@ pub fn show_message_dialog( .dialog .0 .run_mobile_plugin::("showMessageDialog", dialog.payload()); - f(res.map(|r| r.value).unwrap_or_default()) + + let res = res.map(|res| res.value.into()); + f(res.unwrap_or_default()) }); } diff --git a/plugins/dialog/src/models.rs b/plugins/dialog/src/models.rs index d6452bce7..0b2de2c9a 100644 --- a/plugins/dialog/src/models.rs +++ b/plugins/dialog/src/models.rs @@ -52,7 +52,7 @@ impl Serialize for MessageDialogKind { /// Set of button that will be displayed on the dialog #[non_exhaustive] -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Serialize, Deserialize)] pub enum MessageDialogButtons { #[default] /// A single `Ok` button with OS default dialog text @@ -61,8 +61,49 @@ pub enum MessageDialogButtons { OkCancel, /// 2 buttons `Yes` and `No` with OS default dialog texts YesNo, + /// 3 buttons `Yes`, `No` and `Cancel` with OS default dialog texts + YesNoCancel, /// A single `Ok` button with custom text OkCustom(String), /// 2 buttons `Ok` and `Cancel` with custom texts OkCancelCustom(String, String), + /// 3 buttons `Yes`, `No` and `Cancel` with custom texts + YesNoCancelCustom(String, String, String), +} + +/// Result of a message dialog +#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub enum MessageDialogResult { + Yes, + No, + Ok, + #[default] + Cancel, + #[serde(untagged)] + Custom(String), +} + +#[cfg(desktop)] +impl From for MessageDialogResult { + fn from(result: rfd::MessageDialogResult) -> Self { + match result { + rfd::MessageDialogResult::Yes => Self::Yes, + rfd::MessageDialogResult::No => Self::No, + rfd::MessageDialogResult::Ok => Self::Ok, + rfd::MessageDialogResult::Cancel => Self::Cancel, + rfd::MessageDialogResult::Custom(s) => Self::Custom(s), + } + } +} + +impl From for MessageDialogResult { + fn from(value: String) -> Self { + match value.as_str() { + "Yes" => Self::Yes, + "No" => Self::No, + "Ok" => Self::Ok, + "Cancel" => Self::Cancel, + _ => Self::Custom(value), + } + } } diff --git a/plugins/fs/CHANGELOG.md b/plugins/fs/CHANGELOG.md index a46c067e9..e1e1ad40a 100644 --- a/plugins/fs/CHANGELOG.md +++ b/plugins/fs/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## \[2.4.2] + +- [`4eb36b0f`](https://github.com/tauri-apps/plugins-workspace/commit/4eb36b0ff57acb0bb1b911c583efa3bf2f56aa32) ([#2907](https://github.com/tauri-apps/plugins-workspace/pull/2907) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Fixed calling `writeFile` with `data: ReadableStream` throws `Invalid argument` +- [`515182a1`](https://github.com/tauri-apps/plugins-workspace/commit/515182a179d4439079b2b7f6927555ba5ab0b035) ([#2915](https://github.com/tauri-apps/plugins-workspace/pull/2915) by [@samhinshaw](https://github.com/tauri-apps/plugins-workspace/../../samhinshaw)) `readFile` now returns a more specific type `Promise>` instead of the default `Promise` + ## \[2.4.1] - [`44a1f659`](https://github.com/tauri-apps/plugins-workspace/commit/44a1f659125a341191420e650608b0b6ff316a0e) ([#2846](https://github.com/tauri-apps/plugins-workspace/pull/2846) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Fix `writeFile` doesn't create a new file by default when the data is a `ReadableStream` diff --git a/plugins/fs/Cargo.toml b/plugins/fs/Cargo.toml index 73df3d0e2..bff3baeac 100644 --- a/plugins/fs/Cargo.toml +++ b/plugins/fs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-fs" -version = "2.4.1" +version = "2.4.2" description = "Access the file system." authors = { workspace = true } license = { workspace = true } @@ -24,7 +24,7 @@ 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" +toml = "0.9" tauri-utils = { workspace = true, features = ["build"] } [dependencies] diff --git a/plugins/fs/api-iife.js b/plugins/fs/api-iife.js index aab5a9691..5fdd186d9 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("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,s;"function"==typeof SuppressedError&&SuppressedError;const c="__TAURI_TO_IPC_KEY__";class f{constructor(t){i.set(this,void 0),o.set(this,0),r.set(this,[]),a.set(this,void 0),n(this,i,t||(()=>{})),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((t=>{const s=t.index;if("end"in t)return void(s==e(this,o,"f")?this.cleanupCallback():n(this,a,s));const c=t.message;if(s==e(this,o,"f")){for(e(this,i,"f").call(this,c),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)}e(this,o,"f")===e(this,a,"f")&&this.cleanupCallback()}else e(this,r,"f")[s]=c}))}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(t){n(this,i,t)}get onmessage(){return e(this,i,"f")}[(i=new WeakMap,o=new WeakMap,r=new WeakMap,a=new WeakMap,c)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[c]()}}async function l(t,e={},n){return window.__TAURI_INTERNALS__.invoke(t,e,n)}class u{get rid(){return e(this,s,"f")}constructor(t){s.set(this,void 0),n(this,s,t)}async close(){return l("plugin:resources|close",{rid:this.rid})}}var p,w;function d(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}}s=new WeakMap,t.BaseDirectory=void 0,(p=t.BaseDirectory||(t.BaseDirectory={}))[p.Audio=1]="Audio",p[p.Cache=2]="Cache",p[p.Config=3]="Config",p[p.Data=4]="Data",p[p.LocalData=5]="LocalData",p[p.Document=6]="Document",p[p.Download=7]="Download",p[p.Picture=8]="Picture",p[p.Public=9]="Public",p[p.Video=10]="Video",p[p.Resource=11]="Resource",p[p.Temp=12]="Temp",p[p.AppConfig=13]="AppConfig",p[p.AppData=14]="AppData",p[p.AppLocalData=15]="AppLocalData",p[p.AppCache=16]="AppCache",p[p.AppLog=17]="AppLog",p[p.Desktop=18]="Desktop",p[p.Executable=19]="Executable",p[p.Font=20]="Font",p[p.Home=21]="Home",p[p.Runtime=22]="Runtime",p[p.Template=23]="Template",t.SeekMode=void 0,(w=t.SeekMode||(t.SeekMode={}))[w.Start=0]="Start",w[w.Current=1]="Current",w[w.End=2]="End";class h extends u{async read(t){if(0===t.byteLength)return 0;const e=await l("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:n,onEvent:o}),a=new L(r);return()=>{a.close()}}return t.FileHandle=h,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 h(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 d(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=y,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.");const n=await l("plugin:fs|read_text_file",{path:t instanceof URL?t.toString():t,options:e}),i=n instanceof ArrayBuffer?n:Uint8Array.from(n);return(new TextDecoder).decode(i)},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=await l("plugin:fs|read_text_file_lines_next",{rid:this.rid}),i=t instanceof ArrayBuffer?new Uint8Array(t):Uint8Array.from(t),o=1===i[i.byteLength-1];if(o)return this.rid=null,{value:null,done:o};return{value:(new TextDecoder).decode(i.slice(0,i.byteLength)),done:o}},[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.size=async function(t){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");return await l("plugin:fs|size",{path:t instanceof URL?t.toString():t})},t.stat=async function(t,e){return d(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){return await R(t,e,{delayMs:2e3,...n})},t.watchImmediate=async function(t,e,n){return await R(t,e,{...n,delayMs:void 0})},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 y(t,{create:!0,...n}),o=e.getReader();try{for(;;){const{done:t,value:e}=await o.read();if(t)break;await i.write(e)}}finally{o.releaseLock(),await i.close()}}else 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.");const i=new TextEncoder;await l("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__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_FS__=function(t){"use strict";function e(t,e,n,i){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,s;"function"==typeof SuppressedError&&SuppressedError;const c="__TAURI_TO_IPC_KEY__";class f{constructor(t){i.set(this,void 0),o.set(this,0),r.set(this,[]),a.set(this,void 0),n(this,i,t||(()=>{})),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((t=>{const s=t.index;if("end"in t)return void(s==e(this,o,"f")?this.cleanupCallback():n(this,a,s));const c=t.message;if(s==e(this,o,"f")){for(e(this,i,"f").call(this,c),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)}e(this,o,"f")===e(this,a,"f")&&this.cleanupCallback()}else e(this,r,"f")[s]=c}))}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(t){n(this,i,t)}get onmessage(){return e(this,i,"f")}[(i=new WeakMap,o=new WeakMap,r=new WeakMap,a=new WeakMap,c)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[c]()}}async function l(t,e={},n){return window.__TAURI_INTERNALS__.invoke(t,e,n)}class u{get rid(){return e(this,s,"f")}constructor(t){s.set(this,void 0),n(this,s,t)}async close(){return l("plugin:resources|close",{rid:this.rid})}}var p,w;function d(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}}s=new WeakMap,t.BaseDirectory=void 0,(p=t.BaseDirectory||(t.BaseDirectory={}))[p.Audio=1]="Audio",p[p.Cache=2]="Cache",p[p.Config=3]="Config",p[p.Data=4]="Data",p[p.LocalData=5]="LocalData",p[p.Document=6]="Document",p[p.Download=7]="Download",p[p.Picture=8]="Picture",p[p.Public=9]="Public",p[p.Video=10]="Video",p[p.Resource=11]="Resource",p[p.Temp=12]="Temp",p[p.AppConfig=13]="AppConfig",p[p.AppData=14]="AppData",p[p.AppLocalData=15]="AppLocalData",p[p.AppCache=16]="AppCache",p[p.AppLog=17]="AppLog",p[p.Desktop=18]="Desktop",p[p.Executable=19]="Executable",p[p.Font=20]="Font",p[p.Home=21]="Home",p[p.Runtime=22]="Runtime",p[p.Template=23]="Template",t.SeekMode=void 0,(w=t.SeekMode||(t.SeekMode={}))[w.Start=0]="Start",w[w.Current=1]="Current",w[w.End=2]="End";class h extends u{async read(t){if(0===t.byteLength)return 0;const e=await l("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:n,onEvent:o}),a=new L(r);return()=>{a.close()}}return t.FileHandle=h,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 h(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 d(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=y,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.");const n=await l("plugin:fs|read_text_file",{path:t instanceof URL?t.toString():t,options:e}),i=n instanceof ArrayBuffer?n:Uint8Array.from(n);return(new TextDecoder).decode(i)},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=await l("plugin:fs|read_text_file_lines_next",{rid:this.rid}),i=t instanceof ArrayBuffer?new Uint8Array(t):Uint8Array.from(t),o=1===i[i.byteLength-1];if(o)return this.rid=null,{value:null,done:o};return{value:(new TextDecoder).decode(i.slice(0,i.byteLength)),done:o}},[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.size=async function(t){if(t instanceof URL&&"file:"!==t.protocol)throw new TypeError("Must be a file URL.");return await l("plugin:fs|size",{path:t instanceof URL?t.toString():t})},t.stat=async function(t,e){return d(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){return await R(t,e,{delayMs:2e3,...n})},t.watchImmediate=async function(t,e,n){return await R(t,e,{...n,delayMs:void 0})},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 y(t,{read:!1,create:!0,write:!0,...n}),o=e.getReader();try{for(;;){const{done:t,value:e}=await o.read();if(t)break;await i.write(e)}}finally{o.releaseLock(),await i.close()}}else 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.");const i=new TextEncoder;await l("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/guest-js/index.ts b/plugins/fs/guest-js/index.ts index a78fa17b8..572cc1e85 100644 --- a/plugins/fs/guest-js/index.ts +++ b/plugins/fs/guest-js/index.ts @@ -739,7 +739,7 @@ interface ReadFileOptions { async function readFile( path: string | URL, options?: ReadFileOptions -): Promise { +): Promise> { if (path instanceof URL && path.protocol !== 'file:') { throw new TypeError('Must be a file URL.') } @@ -1074,7 +1074,12 @@ async function writeFile( } if (data instanceof ReadableStream) { - const file = await open(path, { create: true, ...options }) + const file = await open(path, { + read: false, + create: true, + write: true, + ...options + }) const reader = data.getReader() try { diff --git a/plugins/fs/package.json b/plugins/fs/package.json index 1ce3cb57e..203fb2adc 100644 --- a/plugins/fs/package.json +++ b/plugins/fs/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-fs", - "version": "2.4.1", + "version": "2.4.2", "description": "Access the file system.", "license": "MIT OR Apache-2.0", "authors": [ @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/fs/permissions/autogenerated/reference.md b/plugins/fs/permissions/autogenerated/reference.md index c1a603194..7f021a7f3 100644 --- a/plugins/fs/permissions/autogenerated/reference.md +++ b/plugins/fs/permissions/autogenerated/reference.md @@ -3748,6 +3748,28 @@ This enables all index or metadata related commands without any pre-configured a An empty permission you can use to modify the global scope. +## Example + +```json +{ + "identifier": "read-documents", + "windows": ["main"], + "permissions": [ + "fs:allow-read", + { + "identifier": "fs:scope", + "allow": [ + "$APPDATA/documents/**/*" + ], + "deny": [ + "$APPDATA/documents/secret.txt" + ] + } + ] +} +``` + + diff --git a/plugins/fs/permissions/schemas/schema.json b/plugins/fs/permissions/schemas/schema.json index 54c6798b3..e1c051f70 100644 --- a/plugins/fs/permissions/schemas/schema.json +++ b/plugins/fs/permissions/schemas/schema.json @@ -2005,10 +2005,10 @@ "markdownDescription": "This enables all index or metadata related commands without any pre-configured accessible paths." }, { - "description": "An empty permission you can use to modify the global scope.", + "description": "An empty permission you can use to modify the global scope.\n\n## Example\n\n```json\n{\n \"identifier\": \"read-documents\",\n \"windows\": [\"main\"],\n \"permissions\": [\n \"fs:allow-read\",\n {\n \"identifier\": \"fs:scope\",\n \"allow\": [\n \"$APPDATA/documents/**/*\"\n ],\n \"deny\": [\n \"$APPDATA/documents/secret.txt\"\n ]\n }\n ]\n}\n```\n", "type": "string", "const": "scope", - "markdownDescription": "An empty permission you can use to modify the global scope." + "markdownDescription": "An empty permission you can use to modify the global scope.\n\n## Example\n\n```json\n{\n \"identifier\": \"read-documents\",\n \"windows\": [\"main\"],\n \"permissions\": [\n \"fs:allow-read\",\n {\n \"identifier\": \"fs:scope\",\n \"allow\": [\n \"$APPDATA/documents/**/*\"\n ],\n \"deny\": [\n \"$APPDATA/documents/secret.txt\"\n ]\n }\n ]\n}\n```\n" }, { "description": "This enables all write related commands without any pre-configured accessible paths.", diff --git a/plugins/fs/permissions/scope.toml b/plugins/fs/permissions/scope.toml index 7e945aa8b..03ba275a4 100644 --- a/plugins/fs/permissions/scope.toml +++ b/plugins/fs/permissions/scope.toml @@ -2,4 +2,27 @@ [[permission]] identifier = "scope" -description = "An empty permission you can use to modify the global scope." +description = """ +An empty permission you can use to modify the global scope. + +## Example + +```json +{ + "identifier": "read-documents", + "windows": ["main"], + "permissions": [ + "fs:allow-read", + { + "identifier": "fs:scope", + "allow": [ + "$APPDATA/documents/**/*" + ], + "deny": [ + "$APPDATA/documents/secret.txt" + ] + } + ] +} +``` +""" diff --git a/plugins/fs/src/commands.rs b/plugins/fs/src/commands.rs index bd1400ea1..ad837a4c0 100644 --- a/plugins/fs/src/commands.rs +++ b/plugins/fs/src/commands.rs @@ -85,6 +85,7 @@ pub fn create( options: Option, ) -> CommandResult { let resolved_path = resolve_path( + "create", &webview, &global_scope, &command_scope, @@ -119,6 +120,7 @@ pub fn open( options: Option, ) -> CommandResult { let (file, _path) = resolve_file( + "open", &webview, &global_scope, &command_scope, @@ -167,6 +169,7 @@ pub async fn copy_file( options: Option, ) -> CommandResult<()> { let resolved_from_path = resolve_path( + "copy-file", &webview, &global_scope, &command_scope, @@ -174,6 +177,7 @@ pub async fn copy_file( options.as_ref().and_then(|o| o.from_path_base_dir), )?; let resolved_to_path = resolve_path( + "copy-file", &webview, &global_scope, &command_scope, @@ -208,6 +212,7 @@ pub fn mkdir( options: Option, ) -> CommandResult<()> { let resolved_path = resolve_path( + "mkdir", &webview, &global_scope, &command_scope, @@ -255,6 +260,7 @@ pub async fn read_dir( options: Option, ) -> CommandResult> { let resolved_path = resolve_path( + "read-dir", &webview, &global_scope, &command_scope, @@ -331,8 +337,8 @@ pub async fn read( Ok(tauri::ipc::Response::new(data)) } -#[tauri::command] -pub async fn read_file( +async fn read_file_inner( + permission: &str, webview: Webview, global_scope: GlobalScope, command_scope: CommandScope, @@ -340,6 +346,7 @@ pub async fn read_file( options: Option, ) -> CommandResult { let (mut file, path) = resolve_file( + permission, &webview, &global_scope, &command_scope, @@ -367,6 +374,25 @@ pub async fn read_file( Ok(tauri::ipc::Response::new(contents)) } +#[tauri::command] +pub async fn read_file( + webview: Webview, + global_scope: GlobalScope, + command_scope: CommandScope, + path: SafeFilePath, + options: Option, +) -> CommandResult { + read_file_inner( + "read-file", + webview, + global_scope, + command_scope, + path, + options, + ) + .await +} + // TODO, remove in v3, rely on `read_file` command instead #[tauri::command] pub async fn read_text_file( @@ -376,7 +402,15 @@ pub async fn read_text_file( path: SafeFilePath, options: Option, ) -> CommandResult { - read_file(webview, global_scope, command_scope, path, options).await + read_file_inner( + "read-text-file", + webview, + global_scope, + command_scope, + path, + options, + ) + .await } #[tauri::command] @@ -388,6 +422,7 @@ pub fn read_text_file_lines( options: Option, ) -> CommandResult { let resolved_path = resolve_path( + "read-text-file-lines", &webview, &global_scope, &command_scope, @@ -452,6 +487,7 @@ pub fn remove( options: Option, ) -> CommandResult<()> { let resolved_path = resolve_path( + "remove", &webview, &global_scope, &command_scope, @@ -521,6 +557,7 @@ pub fn rename( options: Option, ) -> CommandResult<()> { let resolved_old_path = resolve_path( + "rename", &webview, &global_scope, &command_scope, @@ -528,6 +565,7 @@ pub fn rename( options.as_ref().and_then(|o| o.old_path_base_dir), )?; let resolved_new_path = resolve_path( + "rename", &webview, &global_scope, &command_scope, @@ -575,6 +613,7 @@ pub async fn seek( #[cfg(target_os = "android")] fn get_metadata std::io::Result>( + permission: &str, metadata_fn: F, webview: &Webview, global_scope: &GlobalScope, @@ -585,6 +624,7 @@ fn get_metadata std::io::Result { let (file, path) = resolve_file( + permission, webview, global_scope, command_scope, @@ -606,6 +646,7 @@ fn get_metadata std::io::Result get_fs_metadata( + permission, metadata_fn, webview, global_scope, @@ -618,6 +659,7 @@ fn get_metadata std::io::Result std::io::Result>( + permission: &str, metadata_fn: F, webview: &Webview, global_scope: &GlobalScope, @@ -626,6 +668,7 @@ fn get_metadata std::io::Result, ) -> CommandResult { get_fs_metadata( + permission, metadata_fn, webview, global_scope, @@ -636,6 +679,7 @@ fn get_metadata std::io::Result std::io::Result>( + permission: &str, metadata_fn: F, webview: &Webview, global_scope: &GlobalScope, @@ -644,6 +688,7 @@ fn get_fs_metadata std::io::Result, ) -> CommandResult { let resolved_path = resolve_path( + permission, webview, global_scope, command_scope, @@ -668,6 +713,7 @@ pub fn stat( options: Option, ) -> CommandResult { let metadata = get_metadata( + "stat", |p| std::fs::metadata(p), &webview, &global_scope, @@ -688,6 +734,7 @@ pub fn lstat( options: Option, ) -> CommandResult { let metadata = get_metadata( + "lstat", |p| std::fs::symlink_metadata(p), &webview, &global_scope, @@ -716,6 +763,7 @@ pub async fn truncate( options: Option, ) -> CommandResult<()> { let resolved_path = resolve_path( + "truncate", &webview, &global_scope, &command_scope, @@ -784,23 +832,13 @@ fn default_create_value() -> bool { true } -#[tauri::command] -pub async fn write_file( +async fn write_file_inner( + permission: &str, 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") @@ -811,6 +849,7 @@ pub async fn write_file( .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") @@ -818,6 +857,7 @@ pub async fn write_file( .and_then(|opts| serde_json::from_str(opts).ok()); let (mut file, path) = resolve_file( + permission, &webview, &global_scope, &command_scope, @@ -853,6 +893,16 @@ pub async fn write_file( }, )?; + 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()), + }; + file.write_all(&data) .map_err(|e| { format!( @@ -863,6 +913,16 @@ pub async fn write_file( .map_err(Into::into) } +#[tauri::command] +pub async fn write_file( + webview: Webview, + global_scope: GlobalScope, + command_scope: CommandScope, + request: tauri::ipc::Request<'_>, +) -> CommandResult<()> { + write_file_inner("write-file", webview, global_scope, command_scope, request).await +} + // TODO, remove in v3, rely on `write_file` command instead #[tauri::command] pub async fn write_text_file( @@ -871,7 +931,14 @@ pub async fn write_text_file( command_scope: CommandScope, request: tauri::ipc::Request<'_>, ) -> CommandResult<()> { - write_file(webview, global_scope, command_scope, request).await + write_file_inner( + "write-text-file", + webview, + global_scope, + command_scope, + request, + ) + .await } #[tauri::command] @@ -883,6 +950,7 @@ pub fn exists( options: Option, ) -> CommandResult { let resolved_path = resolve_path( + "exists", &webview, &global_scope, &command_scope, @@ -901,6 +969,7 @@ pub async fn size( options: Option, ) -> CommandResult { let resolved_path = resolve_path( + "size", &webview, &global_scope, &command_scope, @@ -943,16 +1012,25 @@ fn get_dir_size(path: &PathBuf) -> CommandResult { #[cfg(not(target_os = "android"))] pub fn resolve_file( + permission: &str, webview: &Webview, global_scope: &GlobalScope, command_scope: &CommandScope, path: SafeFilePath, open_options: OpenOptions, ) -> CommandResult<(File, PathBuf)> { - resolve_file_in_fs(webview, global_scope, command_scope, path, open_options) + resolve_file_in_fs( + permission, + webview, + global_scope, + command_scope, + path, + open_options, + ) } fn resolve_file_in_fs( + permission: &str, webview: &Webview, global_scope: &GlobalScope, command_scope: &CommandScope, @@ -960,6 +1038,7 @@ fn resolve_file_in_fs( open_options: OpenOptions, ) -> CommandResult<(File, PathBuf)> { let path = resolve_path( + permission, webview, global_scope, command_scope, @@ -980,6 +1059,7 @@ fn resolve_file_in_fs( #[cfg(target_os = "android")] pub fn resolve_file( + permission: &str, webview: &Webview, global_scope: &GlobalScope, command_scope: &CommandScope, @@ -997,6 +1077,7 @@ pub fn resolve_file( Ok((file, path)) } SafeFilePath::Path(path) => resolve_file_in_fs( + permission, webview, global_scope, command_scope, @@ -1007,6 +1088,7 @@ pub fn resolve_file( } pub fn resolve_path( + permission: &str, webview: &Webview, global_scope: &GlobalScope, command_scope: &CommandScope, @@ -1052,7 +1134,17 @@ pub fn resolve_path( if fs_scope.scope.is_allowed(&path) || scope.is_allowed(&path) { Ok(path) } else { - Err(CommandError::Plugin(Error::PathForbidden(path))) + #[cfg(not(debug_assertions))] + return Err(CommandError::Plugin(Error::PathForbidden(path))); + + #[cfg(debug_assertions)] + Err( + anyhow::anyhow!( + "forbidden path: {}, maybe it is not allowed on the scope for `allow-{permission}` permission in your capability file", + path.display() + ) + ) + .map_err(Into::into) } } diff --git a/plugins/fs/src/mobile.rs b/plugins/fs/src/mobile.rs index 06422be6b..472f2c8a9 100644 --- a/plugins/fs/src/mobile.rs +++ b/plugins/fs/src/mobile.rs @@ -89,7 +89,7 @@ impl Fs { std::fs::File::from_raw_fd(fd) }) } else { - todo!() + unimplemented!() } } } diff --git a/plugins/fs/src/watcher.rs b/plugins/fs/src/watcher.rs index 89446b884..de9a85d31 100644 --- a/plugins/fs/src/watcher.rs +++ b/plugins/fs/src/watcher.rs @@ -49,6 +49,7 @@ pub fn watch( .into_iter() .map(|path| { resolve_path( + "watch", &webview, &global_scope, &command_scope, diff --git a/plugins/geolocation/android/src/main/java/Geolocation.kt b/plugins/geolocation/android/src/main/java/Geolocation.kt index b16a44825..444711a4c 100644 --- a/plugins/geolocation/android/src/main/java/Geolocation.kt +++ b/plugins/geolocation/android/src/main/java/Geolocation.kt @@ -91,11 +91,9 @@ public class Geolocation(private val context: Context) { val lowPrio = if (networkEnabled) Priority.PRIORITY_BALANCED_POWER_ACCURACY else Priority.PRIORITY_LOW_POWER val prio = if (enableHighAccuracy) Priority.PRIORITY_HIGH_ACCURACY else lowPrio - Logger.error(prio.toString()) - - val locationRequest = LocationRequest.Builder(10000) + val locationRequest = LocationRequest.Builder(timeout) .setMaxUpdateDelayMillis(timeout) - .setMinUpdateIntervalMillis(5000) + .setMinUpdateIntervalMillis(timeout) .setPriority(prio) .build() @@ -145,4 +143,4 @@ public class Geolocation(private val context: Context) { return lastLoc } -} \ No newline at end of file +} diff --git a/plugins/geolocation/package.json b/plugins/geolocation/package.json index 272943b28..937b96ddb 100644 --- a/plugins/geolocation/package.json +++ b/plugins/geolocation/package.json @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/global-shortcut/package.json b/plugins/global-shortcut/package.json index f33653280..2b796c7a5 100644 --- a/plugins/global-shortcut/package.json +++ b/plugins/global-shortcut/package.json @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/haptics/package.json b/plugins/haptics/package.json index b4f485a2a..6111cef45 100644 --- a/plugins/haptics/package.json +++ b/plugins/haptics/package.json @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/http/CHANGELOG.md b/plugins/http/CHANGELOG.md index 9544fae25..95bc6a40d 100644 --- a/plugins/http/CHANGELOG.md +++ b/plugins/http/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## \[2.5.2] + +### Dependencies + +- Upgraded to `fs-js@2.4.2` + ## \[2.5.1] ### Dependencies diff --git a/plugins/http/Cargo.toml b/plugins/http/Cargo.toml index 6f9ec1d84..66530117b 100644 --- a/plugins/http/Cargo.toml +++ b/plugins/http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-http" -version = "2.5.1" +version = "2.5.2" description = "Access an HTTP client written in Rust." edition = { workspace = true } authors = { workspace = true } @@ -34,7 +34,7 @@ serde_json = { workspace = true } tauri = { workspace = true } thiserror = { workspace = true } tokio = { version = "1", features = ["sync", "macros"] } -tauri-plugin-fs = { path = "../fs", version = "2.4.1" } +tauri-plugin-fs = { path = "../fs", version = "2.4.2" } urlpattern = "0.3" regex = "1" http = "1" diff --git a/plugins/http/package.json b/plugins/http/package.json index 130057710..e897de2fb 100644 --- a/plugins/http/package.json +++ b/plugins/http/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-http", - "version": "2.5.1", + "version": "2.5.2", "license": "MIT OR Apache-2.0", "authors": [ "Tauri Programme within The Commons Conservancy" @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/log/CHANGELOG.md b/plugins/log/CHANGELOG.md index 2d18252fa..138f3c4f9 100644 --- a/plugins/log/CHANGELOG.md +++ b/plugins/log/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## \[2.7.0] + +- [`625bb1c0`](https://github.com/tauri-apps/plugins-workspace/commit/625bb1c0965394b88522643731f78ccbcca84add) ([#2965](https://github.com/tauri-apps/plugins-workspace/pull/2965) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Re-export the log crate. + ## \[2.6.0] - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 diff --git a/plugins/log/Cargo.toml b/plugins/log/Cargo.toml index 68dedd8a8..1cbe906ec 100644 --- a/plugins/log/Cargo.toml +++ b/plugins/log/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-log" -version = "2.6.0" +version = "2.7.0" description = "Configurable logging for your Tauri app." authors = { workspace = true } license = { workspace = true } diff --git a/plugins/log/package.json b/plugins/log/package.json index a71b2f44b..d96fed282 100644 --- a/plugins/log/package.json +++ b/plugins/log/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-log", - "version": "2.6.0", + "version": "2.7.0", "description": "Configurable logging for your Tauri app.", "license": "MIT OR Apache-2.0", "authors": [ @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/log/src/commands.rs b/plugins/log/src/commands.rs new file mode 100644 index 000000000..c402db76e --- /dev/null +++ b/plugins/log/src/commands.rs @@ -0,0 +1,73 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use std::collections::HashMap; + +use log::RecordBuilder; + +use crate::{LogLevel, WEBVIEW_TARGET}; + +#[tauri::command] +pub fn log( + level: LogLevel, + message: String, + location: Option<&str>, + file: Option<&str>, + line: Option, + key_values: Option>, +) { + let level = log::Level::from(level); + + let target = if let Some(location) = location { + format!("{WEBVIEW_TARGET}:{location}") + } else { + WEBVIEW_TARGET.to_string() + }; + + let mut builder = RecordBuilder::new(); + builder.level(level).target(&target).file(file).line(line); + + let key_values = key_values.unwrap_or_default(); + let mut kv = HashMap::new(); + for (k, v) in key_values.iter() { + kv.insert(k.as_str(), v.as_str()); + } + builder.key_values(&kv); + #[cfg(feature = "tracing")] + emit_trace(level, &message, location, file, line, &kv); + + log::logger().log(&builder.args(format_args!("{message}")).build()); +} + +// Target becomes default and location is added as a parameter +#[cfg(feature = "tracing")] +fn emit_trace( + level: log::Level, + message: &String, + location: Option<&str>, + file: Option<&str>, + line: Option, + kv: &HashMap<&str, &str>, +) { + macro_rules! emit_event { + ($level:expr) => { + tracing::event!( + target: WEBVIEW_TARGET, + $level, + message = %message, + location = location, + file, + line, + ?kv + ) + }; + } + match level { + log::Level::Error => emit_event!(tracing::Level::ERROR), + log::Level::Warn => emit_event!(tracing::Level::WARN), + log::Level::Info => emit_event!(tracing::Level::INFO), + log::Level::Debug => emit_event!(tracing::Level::DEBUG), + log::Level::Trace => emit_event!(tracing::Level::TRACE), + } +} diff --git a/plugins/log/src/lib.rs b/plugins/log/src/lib.rs index de5c5d54e..c0642d417 100644 --- a/plugins/log/src/lib.rs +++ b/plugins/log/src/lib.rs @@ -10,12 +10,10 @@ )] use fern::{Filter, FormatCallback}; -use log::{logger, RecordBuilder}; use log::{LevelFilter, Record}; use serde::Serialize; use serde_repr::{Deserialize_repr, Serialize_repr}; use std::borrow::Cow; -use std::collections::HashMap; use std::{ fmt::Arguments, fs::{self, File}, @@ -30,6 +28,9 @@ use tauri::{AppHandle, Emitter}; use time::{macros::format_description, OffsetDateTime}; pub use fern; +pub use log; + +mod commands; pub const WEBVIEW_TARGET: &str = "webview"; @@ -206,70 +207,6 @@ impl Target { } } -// Target becomes default and location is added as a parameter -#[cfg(feature = "tracing")] -fn emit_trace( - level: log::Level, - message: &String, - location: Option<&str>, - file: Option<&str>, - line: Option, - kv: &HashMap<&str, &str>, -) { - macro_rules! emit_event { - ($level:expr) => { - tracing::event!( - target: WEBVIEW_TARGET, - $level, - message = %message, - location = location, - file, - line, - ?kv - ) - }; - } - match level { - log::Level::Error => emit_event!(tracing::Level::ERROR), - log::Level::Warn => emit_event!(tracing::Level::WARN), - log::Level::Info => emit_event!(tracing::Level::INFO), - log::Level::Debug => emit_event!(tracing::Level::DEBUG), - log::Level::Trace => emit_event!(tracing::Level::TRACE), - } -} - -#[tauri::command] -fn log( - level: LogLevel, - message: String, - location: Option<&str>, - file: Option<&str>, - line: Option, - key_values: Option>, -) { - let level = log::Level::from(level); - - let target = if let Some(location) = location { - format!("{WEBVIEW_TARGET}:{location}") - } else { - WEBVIEW_TARGET.to_string() - }; - - let mut builder = RecordBuilder::new(); - builder.level(level).target(&target).file(file).line(line); - - let key_values = key_values.unwrap_or_default(); - let mut kv = HashMap::new(); - for (k, v) in key_values.iter() { - kv.insert(k.as_str(), v.as_str()); - } - builder.key_values(&kv); - #[cfg(feature = "tracing")] - emit_trace(level, &message, location, file, line, &kv); - - logger().log(&builder.args(format_args!("{message}")).build()); -} - pub struct Builder { dispatch: fern::Dispatch, rotation_strategy: RotationStrategy, @@ -528,7 +465,7 @@ impl Builder { } fn plugin_builder() -> plugin::Builder { - plugin::Builder::new("log").invoke_handler(tauri::generate_handler![log]) + plugin::Builder::new("log").invoke_handler(tauri::generate_handler![commands::log]) } #[allow(clippy::type_complexity)] diff --git a/plugins/nfc/CHANGELOG.md b/plugins/nfc/CHANGELOG.md index 23faf01a4..70a9b3f07 100644 --- a/plugins/nfc/CHANGELOG.md +++ b/plugins/nfc/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## \[2.3.1] + +- [`fe23a5e0`](https://github.com/tauri-apps/plugins-workspace/commit/fe23a5e01399a6ad61426bf8a94a6bb97227cf88) ([#2885](https://github.com/tauri-apps/plugins-workspace/pull/2885) by [@zaphim12](https://github.com/tauri-apps/plugins-workspace/../../zaphim12)) On iOS, the reader session will now get closed properly on errors, preventing dangling invalid sessions that could prevent subsequent write attempts. + ## \[2.3.0] - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 diff --git a/plugins/nfc/Cargo.toml b/plugins/nfc/Cargo.toml index 8111239c7..1cf4f020f 100644 --- a/plugins/nfc/Cargo.toml +++ b/plugins/nfc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-nfc" -version = "2.3.0" +version = "2.3.1" description = "Read and write NFC tags on Android and iOS." edition = { workspace = true } authors = { workspace = true } diff --git a/plugins/nfc/package.json b/plugins/nfc/package.json index 97ee99782..423f45e20 100644 --- a/plugins/nfc/package.json +++ b/plugins/nfc/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-nfc", - "version": "2.3.0", + "version": "2.3.1", "license": "MIT OR Apache-2.0", "authors": [ "Tauri Programme within The Commons Conservancy" @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/notification/CHANGELOG.md b/plugins/notification/CHANGELOG.md index dd4dcdee3..3adc96e50 100644 --- a/plugins/notification/CHANGELOG.md +++ b/plugins/notification/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## \[2.3.1] + +- [`8abb31ee`](https://github.com/tauri-apps/plugins-workspace/commit/8abb31ee59c68197102c0aa699d690b34646ec3c) ([#2905](https://github.com/tauri-apps/plugins-workspace/pull/2905) by [@ChristianPavilonis](https://github.com/tauri-apps/plugins-workspace/../../ChristianPavilonis)) Fix notification scheduling on iOS. +- [`2d03e2ea`](https://github.com/tauri-apps/plugins-workspace/commit/2d03e2eac2c19ad997d81d23836ab6a219252ffb) ([#2678](https://github.com/tauri-apps/plugins-workspace/pull/2678) by [@Keerthi421](https://github.com/tauri-apps/plugins-workspace/../../Keerthi421)) Added sound support for desktop notifications which was previously only available on mobile platforms. + ## \[2.3.0] - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 diff --git a/plugins/notification/Cargo.toml b/plugins/notification/Cargo.toml index 10ef6267a..ad51b2655 100644 --- a/plugins/notification/Cargo.toml +++ b/plugins/notification/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-notification" -version = "2.3.0" +version = "2.3.1" description = "Send desktop and mobile notifications on your Tauri application." edition = { workspace = true } authors = { workspace = true } diff --git a/plugins/notification/README.md b/plugins/notification/README.md index b42d6f2f1..e17efe028 100644 --- a/plugins/notification/README.md +++ b/plugins/notification/README.md @@ -95,6 +95,45 @@ export async function enqueueNotification(title, body) { } ``` +### Notification with Sound + +You can add sound to your notifications on all platforms (desktop and mobile): + +```javascript +import { sendNotification } from '@tauri-apps/plugin-notification' +import { platform } from '@tauri-apps/api/os' + +// Basic notification with sound +sendNotification({ + title: 'New Message', + body: 'You have a new message', + sound: 'notification.wav' // Path to sound file +}) + +// Platform-specific sounds +async function sendPlatformSpecificNotification() { + const platformName = platform() + + let soundPath + if (platformName === 'darwin') { + // On macOS: use system sounds or sound files in the app bundle + soundPath = 'Ping' // macOS system sound + } else if (platformName === 'linux') { + // On Linux: use XDG theme sounds or file paths + soundPath = 'message-new-instant' // XDG theme sound + } else { + // On Windows: use file paths + soundPath = 'notification.wav' + } + + sendNotification({ + title: 'Platform-specific Notification', + body: 'This notification uses platform-specific sound', + sound: soundPath + }) +} +``` + ## Contributing PRs accepted. Please make sure to read the Contributing Guide before making a pull request. diff --git a/plugins/notification/guest-js/index.ts b/plugins/notification/guest-js/index.ts index 9f81a1e18..685c60c20 100644 --- a/plugins/notification/guest-js/index.ts +++ b/plugins/notification/guest-js/index.ts @@ -71,7 +71,13 @@ interface Options { */ groupSummary?: boolean /** - * The sound resource name. Only available on mobile. + * The sound resource name or file path for the notification. + * + * Platform specific behavior: + * - On macOS: use system sounds (e.g., "Ping", "Blow") or sound files in the app bundle + * - On Linux: use XDG theme sounds (e.g., "message-new-instant") or file paths + * - On Windows: use file paths to sound files (.wav format) + * - On Mobile: use resource names */ sound?: string /** diff --git a/plugins/notification/ios/Sources/Notification.swift b/plugins/notification/ios/Sources/Notification.swift index adba05ec2..259399b8d 100644 --- a/plugins/notification/ios/Sources/Notification.swift +++ b/plugins/notification/ios/Sources/Notification.swift @@ -38,10 +38,17 @@ func makeNotificationContent(_ notification: Notification) throws -> UNNotificat arguments: nil) } - content.userInfo = [ - "__EXTRA__": notification.extra as Any, - "__SCHEDULE__": notification.schedule as Any, - ] + var userInfo: [String: Any] = [:] + + if let extra = notification.extra { + userInfo["__EXTRA__"] = extra + } + + if let schedule = notification.schedule { + userInfo["__SCHEDULE__"] = scheduleToDictionary(schedule) + } + + content.userInfo = userInfo if let actionTypeId = notification.actionTypeId { content.categoryIdentifier = actionTypeId @@ -66,6 +73,56 @@ func makeNotificationContent(_ notification: Notification) throws -> UNNotificat return content } +func scheduleToDictionary(_ schedule: NotificationSchedule) -> [String: Any] { + switch schedule { + case .at(let date, let repeating): + return [ + "type": "at", + "date": date, + "repeating": repeating + ] + case .interval(let interval): + return [ + "type": "interval", + "interval": scheduleIntervalToDictionary(interval) + ] + case .every(let interval, let count): + return [ + "type": "every", + "interval": interval.rawValue, + "count": count + ] + } +} + +func scheduleIntervalToDictionary(_ interval: ScheduleInterval) -> [String: Any] { + var dict: [String: Any] = [:] + + if let year = interval.year { + dict["year"] = year + } + if let month = interval.month { + dict["month"] = month + } + if let day = interval.day { + dict["day"] = day + } + if let weekday = interval.weekday { + dict["weekday"] = weekday + } + if let hour = interval.hour { + dict["hour"] = hour + } + if let minute = interval.minute { + dict["minute"] = minute + } + if let second = interval.second { + dict["second"] = second + } + + return dict +} + func makeAttachments(_ attachments: [NotificationAttachment]) throws -> [UNNotificationAttachment] { var createdAttachments = [UNNotificationAttachment]() diff --git a/plugins/notification/package.json b/plugins/notification/package.json index d1908ebbf..52a7ac604 100644 --- a/plugins/notification/package.json +++ b/plugins/notification/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-notification", - "version": "2.3.0", + "version": "2.3.1", "license": "MIT OR Apache-2.0", "authors": [ "Tauri Programme within The Commons Conservancy" @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/notification/src/desktop.rs b/plugins/notification/src/desktop.rs index 47279225a..4ceb83088 100644 --- a/plugins/notification/src/desktop.rs +++ b/plugins/notification/src/desktop.rs @@ -39,6 +39,9 @@ impl crate::NotificationBuilder { if let Some(icon) = self.data.icon { notification = notification.icon(icon); } + if let Some(sound) = self.data.sound { + notification = notification.sound(sound); + } #[cfg(feature = "windows7-compat")] { notification.notify(&self.app)?; @@ -102,6 +105,8 @@ mod imp { title: Option, /// The notification icon. icon: Option, + /// The notification sound. + sound: Option, /// The notification identifier identifier: String, } @@ -136,6 +141,13 @@ mod imp { self } + /// Sets the notification sound file. + #[must_use] + pub fn sound(mut self, sound: impl Into) -> Self { + self.sound = Some(sound.into()); + self + } + /// Shows the notification. /// /// # Examples @@ -177,6 +189,9 @@ mod imp { } else { notification.auto_icon(); } + if let Some(sound) = self.sound { + notification.sound_name(&sound); + } #[cfg(windows)] { let exe = tauri::utils::platform::current_exe()?; @@ -250,6 +265,7 @@ mod imp { } } + /// Shows the notification on Windows 7. #[cfg(all(windows, feature = "windows7-compat"))] fn notify_win7(self, app: &tauri::AppHandle) -> crate::Result<()> { let app_ = app.clone(); diff --git a/plugins/notification/src/lib.rs b/plugins/notification/src/lib.rs index 9ca33d63a..8b79c8730 100644 --- a/plugins/notification/src/lib.rs +++ b/plugins/notification/src/lib.rs @@ -132,7 +132,7 @@ impl NotificationBuilder { self } - /// The sound resource name. Only available on mobile. + /// The sound resource name for the notification. pub fn sound(mut self, sound: impl Into) -> Self { self.data.sound.replace(sound.into()); self diff --git a/plugins/opener/CHANGELOG.md b/plugins/opener/CHANGELOG.md index 6244fa1ce..94c5c1bc0 100644 --- a/plugins/opener/CHANGELOG.md +++ b/plugins/opener/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## \[2.5.0] + +### enhance + +- [`b8056f48`](https://github.com/tauri-apps/plugins-workspace/commit/b8056f484c7144af095d4d6ded1e8adbb9b8a865) ([#2897](https://github.com/tauri-apps/plugins-workspace/pull/2897) by [@petersamokhin](https://github.com/tauri-apps/plugins-workspace/../../petersamokhin)) Allow reveal multiple items in the file explorer. + ## \[2.4.0] - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 diff --git a/plugins/opener/Cargo.toml b/plugins/opener/Cargo.toml index 323914e5e..bccbcec75 100644 --- a/plugins/opener/Cargo.toml +++ b/plugins/opener/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-opener" -version = "2.4.0" +version = "2.5.0" description = "Open files and URLs using their default application." edition = { workspace = true } authors = { workspace = true } @@ -35,8 +35,6 @@ tauri = { workspace = true } thiserror = { workspace = true } open = { version = "5", features = ["shellexecute-on-windows"] } glob = { workspace = true } - -[target."cfg(windows)".dependencies] dunce = { workspace = true } [target."cfg(windows)".dependencies.windows] diff --git a/plugins/opener/README.md b/plugins/opener/README.md index 02050e932..bcb9265ca 100644 --- a/plugins/opener/README.md +++ b/plugins/opener/README.md @@ -75,6 +75,10 @@ await openPath('/path/to/file', 'firefox') // Reveal a path with the system's default explorer await revealItemInDir('/path/to/file') + +// Reveal multiple paths with the system's default explorer +// Note: will be renamed to `revealItemsInDir` in the next major version +await revealItemInDir(['/path/to/file', '/path/to/another/file']) ``` ### Usage from Rust @@ -102,6 +106,9 @@ fn main() { // Reveal a path with the system's default explorer opener.reveal_item_in_dir("/path/to/file")?; + + // Reveal multiple paths with the system's default explorer + opener.reveal_items_in_dir(["/path/to/file"])?; Ok(()) }) .run(tauri::generate_context!()) diff --git a/plugins/opener/api-iife.js b/plugins/opener/api-iife.js index 30415a61e..dd976e576 100644 --- a/plugins/opener/api-iife.js +++ b/plugins/opener/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_OPENER__=function(n){"use strict";async function e(n,e={},_){return window.__TAURI_INTERNALS__.invoke(n,e,_)}return"function"==typeof SuppressedError&&SuppressedError,n.openPath=async function(n,_){await e("plugin:opener|open_path",{path:n,with:_})},n.openUrl=async function(n,_){await e("plugin:opener|open_url",{url:n,with:_})},n.revealItemInDir=async function(n){return e("plugin:opener|reveal_item_in_dir",{path:n})},n}({});Object.defineProperty(window.__TAURI__,"opener",{value:__TAURI_PLUGIN_OPENER__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_OPENER__=function(n){"use strict";async function e(n,e={},r){return window.__TAURI_INTERNALS__.invoke(n,e,r)}return"function"==typeof SuppressedError&&SuppressedError,n.openPath=async function(n,r){await e("plugin:opener|open_path",{path:n,with:r})},n.openUrl=async function(n,r){await e("plugin:opener|open_url",{url:n,with:r})},n.revealItemInDir=async function(n){return e("plugin:opener|reveal_item_in_dir",{paths:"string"==typeof n?[n]:n})},n}({});Object.defineProperty(window.__TAURI__,"opener",{value:__TAURI_PLUGIN_OPENER__})} diff --git a/plugins/opener/guest-js/index.ts b/plugins/opener/guest-js/index.ts index b73ef5381..6b40da19c 100644 --- a/plugins/opener/guest-js/index.ts +++ b/plugins/opener/guest-js/index.ts @@ -86,12 +86,14 @@ export async function openPath(path: string, openWith?: string): Promise { * ```typescript * import { revealItemInDir } from '@tauri-apps/plugin-opener'; * await revealItemInDir('/path/to/file'); + * await revealItemInDir([ '/path/to/file', '/path/to/another/file' ]); * ``` * * @param path The path to reveal. * * @since 2.0.0 */ -export async function revealItemInDir(path: string) { - return invoke('plugin:opener|reveal_item_in_dir', { path }) +export async function revealItemInDir(path: string | string[]): Promise { + const paths = typeof path === 'string' ? [path] : path + return invoke('plugin:opener|reveal_item_in_dir', { paths }) } diff --git a/plugins/opener/guest-js/init.ts b/plugins/opener/guest-js/init.ts index 6f81141a2..4c2b631fb 100644 --- a/plugins/opener/guest-js/init.ts +++ b/plugins/opener/guest-js/init.ts @@ -46,10 +46,8 @@ window.addEventListener('click', function (evt) { // return early if if ( - // same origin (internal navigation) - url.origin === window.location.origin // not default protocols - || ['http:', 'https:', 'mailto:', 'tel:'].every((p) => url.protocol !== p) + ['http:', 'https:', 'mailto:', 'tel:'].every((p) => url.protocol !== p) ) return diff --git a/plugins/opener/package.json b/plugins/opener/package.json index 274c5e031..b979726d0 100644 --- a/plugins/opener/package.json +++ b/plugins/opener/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-opener", - "version": "2.4.0", + "version": "2.5.0", "description": "Open files and URLs using their default application.", "license": "MIT OR Apache-2.0", "authors": [ @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/opener/src/commands.rs b/plugins/opener/src/commands.rs index b00d5306e..e22b41d41 100644 --- a/plugins/opener/src/commands.rs +++ b/plugins/opener/src/commands.rs @@ -69,7 +69,8 @@ pub async fn open_path( } } +/// TODO: in the next major version, rename to `reveal_items_in_dir` #[tauri::command] -pub async fn reveal_item_in_dir(path: PathBuf) -> crate::Result<()> { - crate::reveal_item_in_dir(path) +pub async fn reveal_item_in_dir(paths: Vec) -> crate::Result<()> { + crate::reveal_items_in_dir(&paths) } diff --git a/plugins/opener/src/error.rs b/plugins/opener/src/error.rs index 157922fc6..c5a4dde3c 100644 --- a/plugins/opener/src/error.rs +++ b/plugins/opener/src/error.rs @@ -31,6 +31,9 @@ pub enum Error { Win32Error(#[from] windows::core::Error), #[error("Path doesn't have a parent: {0}")] NoParent(PathBuf), + #[cfg(windows)] + #[error("Failed to convert path '{0}' to ITEMIDLIST")] + FailedToConvertPathToItemIdList(PathBuf), #[error("Failed to convert path to file:// url")] FailedToConvertPathToFileUrl, #[error(transparent)] diff --git a/plugins/opener/src/init-iife.js b/plugins/opener/src/init-iife.js index 51f6f0684..3a9f95b7e 100644 --- a/plugins/opener/src/init-iife.js +++ b/plugins/opener/src/init-iife.js @@ -1 +1 @@ -!function(){"use strict";"function"==typeof SuppressedError&&SuppressedError,window.addEventListener("click",(function(e){if(e.defaultPrevented||0!==e.button||e.metaKey||e.altKey)return;const t=e.composedPath().find((e=>e instanceof Node&&"A"===e.nodeName.toUpperCase()));if(!t||!t.href||"_blank"!==t.target&&!e.ctrlKey&&!e.shiftKey)return;const n=new URL(t.href);n.origin===window.location.origin||["http:","https:","mailto:","tel:"].every((e=>n.protocol!==e))||(e.preventDefault(),async function(e,t={},n){window.__TAURI_INTERNALS__.invoke(e,t,n)}("plugin:opener|open_url",{url:n}))}))}(); +!function(){"use strict";"function"==typeof SuppressedError&&SuppressedError,window.addEventListener("click",(function(e){if(e.defaultPrevented||0!==e.button||e.metaKey||e.altKey)return;const t=e.composedPath().find((e=>e instanceof Node&&"A"===e.nodeName.toUpperCase()));if(!t||!t.href||"_blank"!==t.target&&!e.ctrlKey&&!e.shiftKey)return;const n=new URL(t.href);["http:","https:","mailto:","tel:"].every((e=>n.protocol!==e))||(e.preventDefault(),async function(e,t={},n){window.__TAURI_INTERNALS__.invoke(e,t,n)}("plugin:opener|open_url",{url:n}))}))}(); diff --git a/plugins/opener/src/lib.rs b/plugins/opener/src/lib.rs index 6bf0e5b28..343e91558 100644 --- a/plugins/opener/src/lib.rs +++ b/plugins/opener/src/lib.rs @@ -25,7 +25,7 @@ pub use error::Error; type Result = std::result::Result; pub use open::{open_path, open_url}; -pub use reveal_item_in_dir::reveal_item_in_dir; +pub use reveal_item_in_dir::{reveal_item_in_dir, reveal_items_in_dir}; pub struct Opener { // we use `fn() -> R` to silence the unused generic error @@ -146,7 +146,15 @@ impl Opener { } pub fn reveal_item_in_dir>(&self, p: P) -> Result<()> { - crate::reveal_item_in_dir::reveal_item_in_dir(p) + reveal_item_in_dir(p) + } + + pub fn reveal_items_in_dir(&self, paths: I) -> Result<()> + where + I: IntoIterator, + P: AsRef, + { + reveal_items_in_dir(paths) } } @@ -213,7 +221,7 @@ impl Builder { .invoke_handler(tauri::generate_handler![ commands::open_url, commands::open_path, - commands::reveal_item_in_dir + commands::reveal_item_in_dir, ]); if self.open_js_links_on_click { diff --git a/plugins/opener/src/reveal_item_in_dir.rs b/plugins/opener/src/reveal_item_in_dir.rs index 6e3dfc2c8..6112fb8b1 100644 --- a/plugins/opener/src/reveal_item_in_dir.rs +++ b/plugins/opener/src/reveal_item_in_dir.rs @@ -10,7 +10,7 @@ use std::path::Path; /// /// - **Android / iOS:** Unsupported. pub fn reveal_item_in_dir>(path: P) -> crate::Result<()> { - let path = path.as_ref().canonicalize()?; + let path = dunce::canonicalize(path.as_ref())?; #[cfg(any( windows, @@ -21,7 +21,47 @@ pub fn reveal_item_in_dir>(path: P) -> crate::Result<()> { target_os = "netbsd", target_os = "openbsd" ))] - return imp::reveal_item_in_dir(&path); + return imp::reveal_items_in_dir(&[path]); + + #[cfg(not(any( + windows, + target_os = "macos", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + )))] + Err(crate::Error::UnsupportedPlatform) +} + +/// Reveal the paths the system's default explorer. +/// +/// ## Platform-specific: +/// +/// - **Android / iOS:** Unsupported. +pub fn reveal_items_in_dir(paths: I) -> crate::Result<()> +where + I: IntoIterator, + P: AsRef, +{ + let mut canonicalized = vec![]; + + for path in paths { + let path = dunce::canonicalize(path.as_ref())?; + canonicalized.push(path); + } + + #[cfg(any( + windows, + target_os = "macos", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + return imp::reveal_items_in_dir(&canonicalized); #[cfg(not(any( windows, @@ -37,8 +77,10 @@ pub fn reveal_item_in_dir>(path: P) -> crate::Result<()> { #[cfg(windows)] mod imp { - use super::*; + use std::collections::HashMap; + use std::path::{Path, PathBuf}; + use windows::Win32::UI::Shell::Common::ITEMIDLIST; use windows::{ core::{w, HSTRING, PCWSTR}, Win32::{ @@ -54,57 +96,98 @@ mod imp { }, }; - pub fn reveal_item_in_dir(path: &Path) -> crate::Result<()> { - let file = dunce::simplified(path); + pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> { + if paths.is_empty() { + return Ok(()); + } + + let mut grouped_paths: HashMap<&Path, Vec<&Path>> = HashMap::new(); + for path in paths { + let parent = path + .parent() + .ok_or_else(|| crate::Error::NoParent(path.to_path_buf()))?; + grouped_paths.entry(parent).or_default().push(path); + } let _ = unsafe { CoInitialize(None) }; - let dir = file - .parent() - .ok_or_else(|| crate::Error::NoParent(file.to_path_buf()))?; - - let dir = HSTRING::from(dir); - let dir_item = unsafe { ILCreateFromPathW(&dir) }; - - let file_h = HSTRING::from(file); - let file_item = unsafe { ILCreateFromPathW(&file_h) }; - - unsafe { - if let Err(e) = SHOpenFolderAndSelectItems(dir_item, Some(&[file_item]), 0) { + for (parent, to_reveals) in grouped_paths { + let parent_item_id_list = OwnedItemIdList::new(parent)?; + let to_reveals_item_id_list = to_reveals + .iter() + .map(|to_reveal| OwnedItemIdList::new(*to_reveal)) + .collect::>>()?; + if let Err(e) = unsafe { + SHOpenFolderAndSelectItems( + parent_item_id_list.item, + Some( + &to_reveals_item_id_list + .iter() + .map(|item| item.item) + .collect::>(), + ), + 0, + ) + } { // from https://github.com/electron/electron/blob/10d967028af2e72382d16b7e2025d243b9e204ae/shell/common/platform_util_win.cc#L302 // On some systems, the above call mysteriously fails with "file not // found" even though the file is there. In these cases, ShellExecute() // seems to work as a fallback (although it won't select the file). + // + // Note: we only handle the first file here if multiple of are present if e.code().0 == ERROR_FILE_NOT_FOUND.0 as i32 { - let is_dir = file.is_dir(); + let first_path = to_reveals[0]; + let is_dir = first_path.is_dir(); let mut info = SHELLEXECUTEINFOW { cbSize: std::mem::size_of::() as _, nShow: SW_SHOWNORMAL.0, - lpFile: PCWSTR(dir.as_ptr()), + lpFile: PCWSTR(parent_item_id_list.hstring.as_ptr()), lpClass: if is_dir { w!("folder") } else { PCWSTR::null() }, lpVerb: if is_dir { w!("explore") } else { PCWSTR::null() }, - ..std::mem::zeroed() + ..Default::default() }; - ShellExecuteExW(&mut info).inspect_err(|_| { - ILFree(Some(dir_item)); - ILFree(Some(file_item)); - })?; + unsafe { ShellExecuteExW(&mut info) }?; } } } - unsafe { - ILFree(Some(dir_item)); - ILFree(Some(file_item)); - } - Ok(()) } + + struct OwnedItemIdList { + hstring: HSTRING, + item: *const ITEMIDLIST, + } + + impl OwnedItemIdList { + fn new(path: &Path) -> crate::Result { + let path_hstring = HSTRING::from(path); + let item_id_list = unsafe { ILCreateFromPathW(&path_hstring) }; + if item_id_list.is_null() { + Err(crate::Error::FailedToConvertPathToItemIdList( + path.to_owned(), + )) + } else { + Ok(Self { + hstring: path_hstring, + item: item_id_list, + }) + } + } + } + + impl Drop for OwnedItemIdList { + fn drop(&mut self) { + if !self.item.is_null() { + unsafe { ILFree(Some(self.item)) }; + } + } + } } #[cfg(any( @@ -115,24 +198,36 @@ mod imp { target_os = "openbsd" ))] mod imp { - - use std::collections::HashMap; - use super::*; + use std::collections::HashMap; + use std::path::PathBuf; - pub fn reveal_item_in_dir(path: &Path) -> crate::Result<()> { + pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> { let connection = zbus::blocking::Connection::session()?; - reveal_with_filemanager1(path, &connection) - .or_else(|_| reveal_with_open_uri_portal(path, &connection)) + reveal_with_filemanager1(paths, &connection).or_else(|e| { + // Fallback to opening the directory of the first item if revealing multiple items fails. + if let Some(first_path) = paths.first() { + reveal_with_open_uri_portal(first_path, &connection) + } else { + Err(e) + } + }) } fn reveal_with_filemanager1( - path: &Path, + paths: &[PathBuf], connection: &zbus::blocking::Connection, ) -> crate::Result<()> { - let uri = url::Url::from_file_path(path) - .map_err(|_| crate::Error::FailedToConvertPathToFileUrl)?; + let uris: Result, _> = paths + .iter() + .map(|path| { + url::Url::from_file_path(path) + .map_err(|_| crate::Error::FailedToConvertPathToFileUrl) + }) + .collect(); + let uris = uris?; + let uri_strs: Vec<&str> = uris.iter().map(|uri| uri.as_str()).collect(); #[zbus::proxy( interface = "org.freedesktop.FileManager1", @@ -145,7 +240,7 @@ mod imp { let proxy = FileManager1ProxyBlocking::new(connection)?; - proxy.ShowItems(vec![uri.as_str()], "") + proxy.ShowItems(uri_strs, "") } fn reveal_with_open_uri_portal( @@ -177,14 +272,22 @@ mod imp { #[cfg(target_os = "macos")] mod imp { - use super::*; use objc2_app_kit::NSWorkspace; use objc2_foundation::{NSArray, NSString, NSURL}; - pub fn reveal_item_in_dir(path: &Path) -> crate::Result<()> { + use std::path::PathBuf; + + pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> { unsafe { - let path = path.to_string_lossy(); - let path = NSString::from_str(&path); - let urls = vec![NSURL::fileURLWithPath(&path)]; + let mut urls = Vec::new(); + + for path in paths { + let path = path.to_string_lossy(); + let path = NSString::from_str(&path); + let url = NSURL::fileURLWithPath(&path); + + urls.push(url); + } + let urls = NSArray::from_retained_slice(&urls); let workspace = NSWorkspace::new(); diff --git a/plugins/os/CHANGELOG.md b/plugins/os/CHANGELOG.md index 207832aba..0bce65325 100644 --- a/plugins/os/CHANGELOG.md +++ b/plugins/os/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## \[2.3.1] + +- [`d3d290ab`](https://github.com/tauri-apps/plugins-workspace/commit/d3d290ab8a8913981a98e2eb7f2c5d4aba3bc36c) ([#2912](https://github.com/tauri-apps/plugins-workspace/pull/2912) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Unlocked version of `serialize-to-javascript` from `=0.1.1` to `^0.1.1` for compatibility with Tauri's upcoming version `2.8`. + ## \[2.3.0] - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 diff --git a/plugins/os/Cargo.toml b/plugins/os/Cargo.toml index c9be33304..09ee3d905 100644 --- a/plugins/os/Cargo.toml +++ b/plugins/os/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-os" -version = "2.3.0" +version = "2.3.1" description = "Read information about the operating system." edition = { workspace = true } authors = { workspace = true } @@ -32,4 +32,4 @@ thiserror = { workspace = true } os_info = "3" sys-locale = "0.3" gethostname = "1.0" -serialize-to-javascript = "=0.1.1" +serialize-to-javascript = "0.1.1" diff --git a/plugins/os/package.json b/plugins/os/package.json index cf4897e31..4e332a377 100644 --- a/plugins/os/package.json +++ b/plugins/os/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-os", - "version": "2.3.0", + "version": "2.3.1", "license": "MIT OR Apache-2.0", "authors": [ "Tauri Programme within The Commons Conservancy" @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/persisted-scope/CHANGELOG.md b/plugins/persisted-scope/CHANGELOG.md index 3b239419b..ca40e97a7 100644 --- a/plugins/persisted-scope/CHANGELOG.md +++ b/plugins/persisted-scope/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## \[2.3.2] + +### Dependencies + +- Upgraded to `fs@2.4.2` + ## \[2.3.1] ### Dependencies diff --git a/plugins/persisted-scope/Cargo.toml b/plugins/persisted-scope/Cargo.toml index eccff8662..5c8f08cd2 100644 --- a/plugins/persisted-scope/Cargo.toml +++ b/plugins/persisted-scope/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-persisted-scope" -version = "2.3.1" +version = "2.3.2" description = "Save filesystem and asset scopes and restore them when the app is reopened." authors = { workspace = true } license = { workspace = true } @@ -27,7 +27,7 @@ log = { workspace = true } thiserror = { workspace = true } aho-corasick = "1" bincode = "1" -tauri-plugin-fs = { path = "../fs", version = "2.4.1" } +tauri-plugin-fs = { path = "../fs", version = "2.4.2" } [features] protocol-asset = ["tauri/protocol-asset"] diff --git a/plugins/positioner/package.json b/plugins/positioner/package.json index b3ecae5d1..dd75462a4 100644 --- a/plugins/positioner/package.json +++ b/plugins/positioner/package.json @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/process/package.json b/plugins/process/package.json index 1864ad5cb..ba40619e3 100644 --- a/plugins/process/package.json +++ b/plugins/process/package.json @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/shell/CHANGELOG.md b/plugins/shell/CHANGELOG.md index 39afdc9c3..5bbd3d8ea 100644 --- a/plugins/shell/CHANGELOG.md +++ b/plugins/shell/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## \[2.3.1] + +- [`d865ed47`](https://github.com/tauri-apps/plugins-workspace/commit/d865ed47685c3923e894f7d10ee4c037507037e6) ([#2950](https://github.com/tauri-apps/plugins-workspace/pull/2950) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Fix sidecar with dots in the filename not working on Windows. + ## \[2.3.0] - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 diff --git a/plugins/shell/Cargo.toml b/plugins/shell/Cargo.toml index 24d27ff36..fee2ce2c3 100644 --- a/plugins/shell/Cargo.toml +++ b/plugins/shell/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-shell" -version = "2.3.0" +version = "2.3.1" description = "Access the system shell. Allows you to spawn child processes and manage files and URLs using their default application." edition = { workspace = true } authors = { workspace = true } diff --git a/plugins/shell/package.json b/plugins/shell/package.json index 95d720d9f..839c86b62 100644 --- a/plugins/shell/package.json +++ b/plugins/shell/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-shell", - "version": "2.3.0", + "version": "2.3.1", "license": "MIT OR Apache-2.0", "authors": [ "Tauri Programme within The Commons Conservancy" @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/shell/src/commands.rs b/plugins/shell/src/commands.rs index 5275e9b9c..0facce719 100644 --- a/plugins/shell/src/commands.rs +++ b/plugins/shell/src/commands.rs @@ -115,7 +115,12 @@ fn prepare_cmd( let mut command = if options.sidecar { let program = PathBuf::from(program); let program_as_string = program.display().to_string(); - let program_no_ext_as_string = program.with_extension("").display().to_string(); + let has_extension = program.extension().is_some_and(|ext| ext == "exe"); + let program_no_ext_as_string = if has_extension { + program.with_extension("").display().to_string() + } else { + program_as_string.clone() + }; let configured_sidecar = window .config() .bundle diff --git a/plugins/shell/src/process/mod.rs b/plugins/shell/src/process/mod.rs index 44f037b01..1384bbb8b 100644 --- a/plugins/shell/src/process/mod.rs +++ b/plugins/shell/src/process/mod.rs @@ -118,9 +118,23 @@ pub struct Output { fn relative_command_path(command: &Path) -> crate::Result { match platform::current_exe()?.parent() { #[cfg(windows)] - Some(exe_dir) => Ok(exe_dir.join(command).with_extension("exe")), + Some(exe_dir) => { + let mut command_path = exe_dir.join(command); + let already_exe = command_path.extension().is_some_and(|ext| ext == "exe"); + if !already_exe { + // do not use with_extension to retain dots in the command filename + command_path.as_mut_os_string().push(".exe"); + } + Ok(command_path) + } #[cfg(not(windows))] - Some(exe_dir) => Ok(exe_dir.join(command)), + Some(exe_dir) => { + let mut command_path = exe_dir.join(command); + if command_path.extension().is_some_and(|ext| ext == "exe") { + command_path.set_extension(""); + } + Ok(command_path) + } None => Err(crate::Error::CurrentExeHasNoParent), } } @@ -133,6 +147,10 @@ impl From for StdCommand { impl Command { pub(crate) fn new>(program: S) -> Self { + log::debug!( + "Creating sidecar {}", + program.as_ref().to_str().unwrap_or("") + ); let mut command = StdCommand::new(program); command.stdout(Stdio::piped()); @@ -451,9 +469,33 @@ fn spawn_pipe_reader) -> CommandEvent + Send + Copy + 'static>( // tests for the commands functions. #[cfg(test)] mod tests { - #[cfg(not(windows))] use super::*; + #[test] + fn relative_command_path_resolves() { + let cwd_parent = platform::current_exe() + .unwrap() + .parent() + .unwrap() + .to_owned(); + assert_eq!( + relative_command_path(Path::new("Tauri.Example")).unwrap(), + cwd_parent.join(if cfg!(windows) { + "Tauri.Example.exe" + } else { + "Tauri.Example" + }) + ); + assert_eq!( + relative_command_path(Path::new("Tauri.Example.exe")).unwrap(), + cwd_parent.join(if cfg!(windows) { + "Tauri.Example.exe" + } else { + "Tauri.Example" + }) + ); + } + #[cfg(not(windows))] #[test] fn test_cmd_spawn_output() { diff --git a/plugins/single-instance/CHANGELOG.md b/plugins/single-instance/CHANGELOG.md index f2ac4031f..e0c65a475 100644 --- a/plugins/single-instance/CHANGELOG.md +++ b/plugins/single-instance/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## \[2.3.4] + +### Dependencies + +- Upgraded to `deep-link@2.4.3` + +## \[2.3.3] + +### Dependencies + +- Upgraded to `deep-link@2.4.2` + ## \[2.3.2] ### Dependencies diff --git a/plugins/single-instance/Cargo.toml b/plugins/single-instance/Cargo.toml index 24ff572bd..5486fd642 100644 --- a/plugins/single-instance/Cargo.toml +++ b/plugins/single-instance/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-single-instance" -version = "2.3.2" +version = "2.3.4" description = "Ensure a single instance of your tauri app is running." authors = { workspace = true } license = { workspace = true } @@ -26,7 +26,7 @@ serde_json = { workspace = true } tauri = { workspace = true } tracing = { workspace = true } thiserror = { workspace = true } -tauri-plugin-deep-link = { path = "../deep-link", version = "2.4.1", optional = true } +tauri-plugin-deep-link = { path = "../deep-link", version = "2.4.3", optional = true } semver = { version = "1", optional = true } [target."cfg(target_os = \"windows\")".dependencies.windows-sys] diff --git a/plugins/single-instance/examples/vanilla/package.json b/plugins/single-instance/examples/vanilla/package.json index b7d778ccb..fe9120dd2 100644 --- a/plugins/single-instance/examples/vanilla/package.json +++ b/plugins/single-instance/examples/vanilla/package.json @@ -9,6 +9,6 @@ "author": "", "license": "MIT", "devDependencies": { - "@tauri-apps/cli": "2.7.1" + "@tauri-apps/cli": "2.8.4" } } diff --git a/plugins/sql/package.json b/plugins/sql/package.json index be6d89ed2..afbb6bd99 100644 --- a/plugins/sql/package.json +++ b/plugins/sql/package.json @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/store/CHANGELOG.md b/plugins/store/CHANGELOG.md index f9c466e4a..7e7d5de4f 100644 --- a/plugins/store/CHANGELOG.md +++ b/plugins/store/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## \[2.4.0] + +- [`5ac8fbb1`](https://github.com/tauri-apps/plugins-workspace/commit/5ac8fbb1fa76714aa8cc9c0d74e0aebab12f9755) ([#2857](https://github.com/tauri-apps/plugins-workspace/pull/2857) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Allow setting defaults from the JavaScript API +- [`5ac8fbb1`](https://github.com/tauri-apps/plugins-workspace/commit/5ac8fbb1fa76714aa8cc9c0d74e0aebab12f9755) ([#2857](https://github.com/tauri-apps/plugins-workspace/pull/2857) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Add an new option `overrideDefaults` for creating/loading and reloading the store that overrides the store with the on-disk state, ignoring defaults + ## \[2.3.0] - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 diff --git a/plugins/store/Cargo.toml b/plugins/store/Cargo.toml index 4b27a8e47..b82ffa33d 100644 --- a/plugins/store/Cargo.toml +++ b/plugins/store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin-store" -version = "2.3.0" +version = "2.4.0" description = "Simple, persistent key-value store." authors = { workspace = true } license = { workspace = true } diff --git a/plugins/store/examples/AppSettingsManager/package.json b/plugins/store/examples/AppSettingsManager/package.json index fab6f1aa4..0b74f6801 100644 --- a/plugins/store/examples/AppSettingsManager/package.json +++ b/plugins/store/examples/AppSettingsManager/package.json @@ -8,8 +8,8 @@ "tauri": "tauri" }, "devDependencies": { - "@tauri-apps/cli": "2.7.1", + "@tauri-apps/cli": "2.8.4", "typescript": "^5.7.3", - "vite": "^7.0.4" + "vite": "^7.0.7" } } diff --git a/plugins/store/guest-js/index.ts b/plugins/store/guest-js/index.ts index 49853f11e..776894a1b 100644 --- a/plugins/store/guest-js/index.ts +++ b/plugins/store/guest-js/index.ts @@ -407,7 +407,7 @@ interface IStore { * Note: * - This method loads the data and merges it with the current store, * this behavior will be changed to resetting to default first and then merging with the on-disk state in v3, - * to fully match the store with the on-disk state, set {@linkcode ReloadOptions.ignoreDefaults} to `true` + * to fully match the store with the on-disk state, set {@linkcode ReloadOptions | ignoreDefaults} to `true` * - This method does not emit change events. * * @returns diff --git a/plugins/store/package.json b/plugins/store/package.json index d26973d16..288e1495c 100644 --- a/plugins/store/package.json +++ b/plugins/store/package.json @@ -1,6 +1,6 @@ { "name": "@tauri-apps/plugin-store", - "version": "2.3.0", + "version": "2.4.0", "description": "Simple, persistent key-value store.", "license": "MIT OR Apache-2.0", "authors": [ @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/stronghold/package.json b/plugins/stronghold/package.json index 58d474648..f5f01aa1d 100644 --- a/plugins/stronghold/package.json +++ b/plugins/stronghold/package.json @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/updater/package.json b/plugins/updater/package.json index 3da537828..d3f314d42 100644 --- a/plugins/updater/package.json +++ b/plugins/updater/package.json @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/updater/src/error.rs b/plugins/updater/src/error.rs index b82e7d551..c44f98627 100644 --- a/plugins/updater/src/error.rs +++ b/plugins/updater/src/error.rs @@ -39,9 +39,14 @@ pub enum Error { /// `reqwest` crate errors. #[error(transparent)] Reqwest(#[from] reqwest::Error), - /// The platform was not found on the updater JSON response. - #[error("the platform `{0}` was not found on the response `platforms` object")] + /// The platform was not found in the updater JSON response. + #[error("the platform `{0}` was not found in the response `platforms` object")] TargetNotFound(String), + /// Neither the platform nor the fallback platform was found in the updater JSON response. + #[error( + "None of the fallback platforms `{0:?}` were found in the response `platforms` object" + )] + TargetsNotFound(Vec), /// Download failed #[error("`{0}`")] Network(String), @@ -69,6 +74,8 @@ pub enum Error { AuthenticationFailed, #[error("Failed to install .deb package")] DebInstallFailed, + #[error("Failed to install package")] + PackageInstallFailed, #[error("invalid updater binary format")] InvalidUpdaterFormat, #[error(transparent)] diff --git a/plugins/updater/src/updater.rs b/plugins/updater/src/updater.rs index 707c14895..28c420ca8 100644 --- a/plugins/updater/src/updater.rs +++ b/plugins/updater/src/updater.rs @@ -26,7 +26,13 @@ use reqwest::{ }; use semver::Version; use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize}; -use tauri::{utils::platform::current_exe, AppHandle, Resource, Runtime}; +use tauri::{ + utils::{ + config::BundleType, + platform::{bundle_type, current_exe}, + }, + AppHandle, Resource, Runtime, +}; use time::OffsetDateTime; use url::Url; @@ -37,6 +43,31 @@ use crate::{ const UPDATER_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); +#[derive(Copy, Clone)] +pub enum Installer { + AppImage, + Deb, + Rpm, + + App, + + Msi, + Nsis, +} + +impl Installer { + fn name(self) -> &'static str { + match self { + Self::AppImage => "appimage", + Self::Deb => "deb", + Self::Rpm => "rpm", + Self::App => "app", + Self::Msi => "msi", + Self::Nsis => "nsis", + } + } +} + #[derive(Debug, Deserialize, Serialize, Clone)] pub struct ReleaseManifestPlatform { /// Download URL for the platform @@ -265,13 +296,7 @@ impl UpdaterBuilder { return Err(Error::EmptyEndpoints); }; - let arch = get_updater_arch().ok_or(Error::UnsupportedArch)?; - let (target, json_target) = if let Some(target) = self.target { - (target.clone(), target) - } else { - let target = get_updater_target().ok_or(Error::UnsupportedOs)?; - (target.to_string(), format!("{target}-{arch}")) - }; + let arch = updater_arch().ok_or(Error::UnsupportedArch)?; let executable_path = self.executable_path.clone().unwrap_or(current_exe()?); @@ -294,8 +319,7 @@ impl UpdaterBuilder { installer_args: self.installer_args, current_exe_args: self.current_exe_args, arch, - target, - json_target, + target: self.target, headers: self.headers, extract_path, on_before_exit: self.on_before_exit, @@ -327,10 +351,9 @@ pub struct Updater { proxy: Option, endpoints: Vec, arch: &'static str, - // The `{{target}}` variable we replace in the endpoint - target: String, - // The value we search if the updater server returns a JSON with the `platforms` object - json_target: String, + // The `{{target}}` variable we replace in the endpoint and serach for in the JSON, + // this is either the user provided target or the current operating system by default + target: Option, headers: HeaderMap, extract_path: PathBuf, on_before_exit: Option, @@ -359,12 +382,17 @@ impl Updater { std::env::set_var("SSL_CERT_DIR", "/etc/ssl/certs"); } } + let target = if let Some(target) = &self.target { + target + } else { + updater_os().ok_or(Error::UnsupportedOs)? + }; let mut remote_release: Option = None; let mut raw_json: Option = None; let mut last_error: Option = None; for url in &self.endpoints { - // replace {{current_version}}, {{target}} and {{arch}} in the provided URL + // replace {{current_version}}, {{target}}, {{arch}} and {{bundle_type}} in the provided URL // this is useful if we need to query example // https://releases.myapp.com/update/{{target}}/{{arch}}/{{current_version}} // will be translated into -> @@ -376,17 +404,22 @@ impl Updater { const CONTROLS_ADD: &AsciiSet = &CONTROLS.add(b'+'); let encoded_version = percent_encoding::percent_encode(version, CONTROLS_ADD); let encoded_version = encoded_version.to_string(); + let installer = installer_for_bundle_type(bundle_type()) + .map(|i| i.name()) + .unwrap_or("unknown"); let url: Url = url .to_string() // url::Url automatically url-encodes the path components .replace("%7B%7Bcurrent_version%7D%7D", &encoded_version) - .replace("%7B%7Btarget%7D%7D", &self.target) + .replace("%7B%7Btarget%7D%7D", target) .replace("%7B%7Barch%7D%7D", self.arch) + .replace("%7B%7Bbundle_type%7D%7D", installer) // but not query parameters .replace("{{current_version}}", &encoded_version) - .replace("{{target}}", &self.target) + .replace("{{target}}", target) .replace("{{arch}}", self.arch) + .replace("{{bundle_type}}", installer) .parse()?; log::debug!("checking for updates {url}"); @@ -466,6 +499,9 @@ impl Updater { None => release.version > self.current_version, }; + let installer = installer_for_bundle_type(bundle_type()); + let (download_url, signature) = self.get_urls(&release, &installer)?; + let update = if should_update { Some(Update { run_on_main_thread: self.run_on_main_thread.clone(), @@ -473,12 +509,12 @@ impl Updater { on_before_exit: self.on_before_exit.clone(), app_name: self.app_name.clone(), current_version: self.current_version.to_string(), - target: self.target.clone(), + target: target.to_owned(), extract_path: self.extract_path.clone(), version: release.version.to_string(), date: release.pub_date, - download_url: release.download_url(&self.json_target)?.to_owned(), - signature: release.signature(&self.json_target)?.to_owned(), + download_url: download_url.clone(), + signature: signature.to_owned(), body: release.notes, raw_json: raw_json.unwrap(), timeout: None, @@ -494,6 +530,38 @@ impl Updater { Ok(update) } + + fn get_urls<'a>( + &self, + release: &'a RemoteRelease, + installer: &Option, + ) -> Result<(&'a Url, &'a String)> { + // Use the user provided target + if let Some(target) = &self.target { + return Ok((release.download_url(target)?, release.signature(target)?)); + } + + // Or else we search for [`{os}-{arch}-{installer}`, `{os}-{arch}`] in order + let os = updater_os().ok_or(Error::UnsupportedOs)?; + let arch = self.arch; + let mut targets = Vec::new(); + if let Some(installer) = installer { + let installer = installer.name(); + targets.push(format!("{os}-{arch}-{installer}")); + } + targets.push(format!("{os}-{arch}")); + + for target in &targets { + log::debug!("Searching for updater target '{target}' in release data"); + if let (Ok(download_url), Ok(signature)) = + (release.download_url(target), release.signature(target)) + { + return Ok((download_url, signature)); + }; + } + + Err(Error::TargetsNotFound(targets)) + } } #[derive(Clone)] @@ -511,7 +579,8 @@ pub struct Update { pub version: String, /// Update publish date pub date: Option, - /// Target + /// The `{{target}}` variable we replace in the endpoint and search for in the JSON, + /// this is either the user provided target or the current operating system by default pub target: String, /// Download URL announced pub download_url: Url, @@ -852,11 +921,10 @@ impl Update { /// └── ... /// fn install_inner(&self, bytes: &[u8]) -> Result<()> { - if self.is_deb_package() { - self.install_deb(bytes) - } else { - // Handle AppImage or other formats - self.install_appimage(bytes) + match installer_for_bundle_type(bundle_type()) { + Some(Installer::Deb) => self.install_deb(bytes), + Some(Installer::Rpm) => self.install_rpm(bytes), + _ => self.install_appimage(bytes), } } @@ -933,39 +1001,6 @@ impl Update { Err(Error::TempDirNotOnSameMountPoint) } - fn is_deb_package(&self) -> bool { - // First check if we're in a typical Debian installation path - let in_system_path = self - .extract_path - .to_str() - .map(|p| p.starts_with("/usr")) - .unwrap_or(false); - - if !in_system_path { - return false; - } - - // Then verify it's actually a Debian-based system by checking for dpkg - let dpkg_exists = std::path::Path::new("/var/lib/dpkg").exists(); - let apt_exists = std::path::Path::new("/etc/apt").exists(); - - // Additional check for the package in dpkg database - let package_in_dpkg = if let Ok(output) = std::process::Command::new("dpkg") - .args(["-S", &self.extract_path.to_string_lossy()]) - .output() - { - output.status.success() - } else { - false - }; - - // Consider it a deb package only if: - // 1. We're in a system path AND - // 2. We have Debian package management tools AND - // 3. The binary is tracked by dpkg - dpkg_exists && apt_exists && package_in_dpkg - } - fn install_deb(&self, bytes: &[u8]) -> Result<()> { // First verify the bytes are actually a .deb package if !infer::archive::is_deb(bytes) { @@ -973,6 +1008,18 @@ impl Update { return Err(Error::InvalidUpdaterFormat); } + self.try_tmp_locations(bytes, "dpkg", "-i") + } + + fn install_rpm(&self, bytes: &[u8]) -> Result<()> { + // First verify the bytes are actually a .rpm package + if !infer::archive::is_rpm(bytes) { + return Err(Error::InvalidUpdaterFormat); + } + self.try_tmp_locations(bytes, "rpm", "-U") + } + + fn try_tmp_locations(&self, bytes: &[u8], install_cmd: &str, install_arg: &str) -> Result<()> { // Try different temp directories let tmp_dir_locations = vec![ Box::new(|| Some(std::env::temp_dir())) as Box Option>, @@ -984,15 +1031,19 @@ impl Update { for tmp_dir_location in tmp_dir_locations { if let Some(path) = tmp_dir_location() { if let Ok(tmp_dir) = tempfile::Builder::new() - .prefix("tauri_deb_update") + .prefix("tauri_rpm_update") .tempdir_in(path) { - let deb_path = tmp_dir.path().join("package.deb"); + let pkg_path = tmp_dir.path().join("package.rpm"); // Try writing the .deb file - if std::fs::write(&deb_path, bytes).is_ok() { + if std::fs::write(&pkg_path, bytes).is_ok() { // If write succeeds, proceed with installation - return self.try_install_with_privileges(&deb_path); + return self.try_install_with_privileges( + &pkg_path, + install_cmd, + install_arg, + ); } // If write fails, continue to next temp location } @@ -1003,12 +1054,17 @@ impl Update { Err(Error::TempDirNotFound) } - fn try_install_with_privileges(&self, deb_path: &Path) -> Result<()> { + fn try_install_with_privileges( + &self, + pkg_path: &Path, + install_cmd: &str, + install_arg: &str, + ) -> Result<()> { // 1. First try using pkexec (graphical sudo prompt) if let Ok(status) = std::process::Command::new("pkexec") - .arg("dpkg") - .arg("-i") - .arg(deb_path) + .arg(install_cmd) + .arg(install_arg) + .arg(pkg_path) .status() { if status.success() { @@ -1019,7 +1075,7 @@ impl Update { // 2. Try zenity or kdialog for a graphical sudo experience if let Ok(password) = self.get_password_graphically() { - if self.install_with_sudo(deb_path, &password)? { + if self.install_with_sudo(pkg_path, &password, install_cmd, install_arg)? { log::debug!("installed deb with GUI sudo"); return Ok(()); } @@ -1027,16 +1083,16 @@ impl Update { // 3. Final fallback: terminal sudo let status = std::process::Command::new("sudo") - .arg("dpkg") - .arg("-i") - .arg(deb_path) + .arg(install_cmd) + .arg(install_arg) + .arg(pkg_path) .status()?; if status.success() { log::debug!("installed deb with sudo"); Ok(()) } else { - Err(Error::DebInstallFailed) + Err(Error::PackageInstallFailed) } } @@ -1070,15 +1126,21 @@ impl Update { Err(Error::AuthenticationFailed) } - fn install_with_sudo(&self, deb_path: &Path, password: &str) -> Result { + fn install_with_sudo( + &self, + pkg_path: &Path, + password: &str, + install_cmd: &str, + install_arg: &str, + ) -> Result { use std::io::Write; use std::process::{Command, Stdio}; let mut child = Command::new("sudo") .arg("-S") // read password from stdin - .arg("dpkg") - .arg("-i") - .arg(deb_path) + .arg(install_cmd) + .arg(install_arg) + .arg(pkg_path) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) @@ -1086,7 +1148,7 @@ impl Update { if let Some(mut stdin) = child.stdin.take() { // Write password to stdin - writeln!(stdin, "{}", password)?; + writeln!(stdin, "{password}")?; } let status = child.wait()?; @@ -1199,16 +1261,18 @@ impl Update { } } -/// Gets the target string used on the updater. +/// Gets the base target string used by the updater. If bundle type is available it +/// will be added to this string when selecting the download URL and signature. +/// `tauri::utils::platform::bundle_type` method is used to obtain current bundle type. pub fn target() -> Option { - if let (Some(target), Some(arch)) = (get_updater_target(), get_updater_arch()) { + if let (Some(target), Some(arch)) = (updater_os(), updater_arch()) { Some(format!("{target}-{arch}")) } else { None } } -pub(crate) fn get_updater_target() -> Option<&'static str> { +fn updater_os() -> Option<&'static str> { if cfg!(target_os = "linux") { Some("linux") } else if cfg!(target_os = "macos") { @@ -1221,7 +1285,7 @@ pub(crate) fn get_updater_target() -> Option<&'static str> { } } -pub(crate) fn get_updater_arch() -> Option<&'static str> { +fn updater_arch() -> Option<&'static str> { if cfg!(target_arch = "x86") { Some("i686") } else if cfg!(target_arch = "x86_64") { @@ -1315,6 +1379,18 @@ impl<'de> Deserialize<'de> for RemoteRelease { } } +fn installer_for_bundle_type(bundle: Option) -> Option { + match bundle? { + BundleType::Deb => Some(Installer::Deb), + BundleType::Rpm => Some(Installer::Rpm), + BundleType::AppImage => Some(Installer::AppImage), + BundleType::Msi => Some(Installer::Msi), + BundleType::Nsis => Some(Installer::Nsis), + BundleType::App => Some(Installer::App), // App is also returned for Dmg type + _ => None, + } +} + fn parse_version<'de, D>(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, diff --git a/plugins/updater/tests/app-updater/tauri.conf.json b/plugins/updater/tests/app-updater/tauri.conf.json index f2c6df21b..fc70993eb 100644 --- a/plugins/updater/tests/app-updater/tauri.conf.json +++ b/plugins/updater/tests/app-updater/tauri.conf.json @@ -2,6 +2,7 @@ "identifier": "com.tauri.updater", "plugins": { "updater": { + "dangerousInsecureTransportProtocol": true, "endpoints": ["http://localhost:3007"], "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUwNDRGMjkwRjg2MDhCRDAKUldUUWkyRDRrUEpFNEQ4SmdwcU5PaXl6R2ZRUUNvUnhIaVkwVUltV0NMaEx6VTkrWVhpT0ZqeEEK", "windows": { diff --git a/plugins/updater/tests/app-updater/tests/update.rs b/plugins/updater/tests/app-updater/tests/update.rs index d308b3173..f962eff9e 100644 --- a/plugins/updater/tests/app-updater/tests/update.rs +++ b/plugins/updater/tests/app-updater/tests/update.rs @@ -17,6 +17,7 @@ use tauri::utils::config::{Updater, V1Compatible}; const UPDATER_PRIVATE_KEY: &str = "dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5TlFOMFpXYzJFOUdjeHJEVXY4WE1TMUxGNDJVUjNrMmk1WlR3UVJVUWwva0FBQkFBQUFBQUFBQUFBQUlBQUFBQUpVK3ZkM3R3eWhyN3hiUXhQb2hvWFVzUW9FbEs3NlNWYjVkK1F2VGFRU1FEaGxuRUtlell5U0gxYS9DbVRrS0YyZVJGblhjeXJibmpZeGJjS0ZKSUYwYndYc2FCNXpHalM3MHcrODMwN3kwUG9SOWpFNVhCSUd6L0E4TGRUT096TEtLR1JwT1JEVFU9Cg=="; const UPDATED_EXIT_CODE: i32 = 0; +const ERROR_EXIT_CODE: i32 = 1; const UP_TO_DATE_EXIT_CODE: i32 = 2; #[derive(Serialize)] @@ -48,7 +49,7 @@ struct Update { fn build_app(cwd: &Path, config: &Config, bundle_updater: bool, target: BundleTarget) { let mut command = Command::new("cargo"); command - .args(["tauri", "build", "--debug", "--verbose"]) + .args(["tauri", "build", "--verbose"]) .arg("--config") .arg(serde_json::to_string(config).unwrap()) .env("TAURI_SIGNING_PRIVATE_KEY", UPDATER_PRIVATE_KEY) @@ -80,6 +81,8 @@ fn build_app(cwd: &Path, config: &Config, bundle_updater: bool, target: BundleTa #[derive(Copy, Clone)] enum BundleTarget { AppImage, + Deb, + Rpm, App, @@ -91,6 +94,8 @@ impl BundleTarget { fn name(self) -> &'static str { match self { Self::AppImage => "appimage", + Self::Deb => "deb", + Self::Rpm => "rpm", Self::App => "app", Self::Msi => "msi", Self::Nsis => "nsis", @@ -109,57 +114,168 @@ impl Default for BundleTarget { } } +fn target_to_platforms( + update_platform: Option, + signature: String, +) -> HashMap { + let mut platforms = HashMap::new(); + if let Some(platform) = update_platform { + platforms.insert( + platform, + PlatformUpdate { + signature, + url: "http://localhost:3007/download", + with_elevated_task: false, + }, + ); + } + + platforms +} + #[cfg(target_os = "linux")] -fn bundle_paths(root_dir: &Path, version: &str) -> Vec<(BundleTarget, PathBuf)> { - vec![( - BundleTarget::AppImage, - root_dir.join(format!( - "target/debug/bundle/appimage/app-updater_{version}_amd64.AppImage" - )), - )] +fn test_cases( + root_dir: &Path, + version: &str, + target: String, +) -> Vec<(BundleTarget, PathBuf, Option, Vec)> { + vec![ + // update using fallback + ( + BundleTarget::AppImage, + root_dir.join(format!( + "target/release/bundle/appimage/app-updater_{version}_amd64.AppImage" + )), + Some(target.clone()), + vec![UPDATED_EXIT_CODE, UP_TO_DATE_EXIT_CODE], + ), + // update using full name + ( + BundleTarget::AppImage, + root_dir.join(format!( + "target/release/bundle/appimage/app-updater_{version}_amd64.AppImage" + )), + Some(format!("{target}-{}", BundleTarget::AppImage.name())), + vec![UPDATED_EXIT_CODE, UP_TO_DATE_EXIT_CODE], + ), + // no update + ( + BundleTarget::AppImage, + root_dir.join(format!( + "target/release/bundle/appimage/app-updater_{version}_amd64.AppImage" + )), + None, + vec![ERROR_EXIT_CODE], + ), + ] } #[cfg(target_os = "macos")] -fn bundle_paths(root_dir: &Path, _version: &str) -> Vec<(BundleTarget, PathBuf)> { - vec![( - BundleTarget::App, - root_dir.join("target/debug/bundle/macos/app-updater.app"), - )] +fn test_cases( + root_dir: &Path, + _version: &str, + target: String, +) -> Vec<(BundleTarget, PathBuf, Option, Vec)> { + vec![ + ( + BundleTarget::App, + root_dir.join("target/release/bundle/macos/app-updater.app"), + Some(target.clone()), + vec![UPDATED_EXIT_CODE, UP_TO_DATE_EXIT_CODE], + ), + // update with installer + ( + BundleTarget::App, + root_dir.join("target/release/bundle/macos/app-updater.app"), + Some(format!("{target}-{}", BundleTarget::App.name())), + vec![UPDATED_EXIT_CODE, UP_TO_DATE_EXIT_CODE], + ), + // no update + ( + BundleTarget::App, + root_dir.join("target/release/bundle/macos/app-updater.app"), + None, + vec![ERROR_EXIT_CODE], + ), + ] } #[cfg(target_os = "ios")] -fn bundle_paths(root_dir: &Path, _version: &str) -> Vec<(BundleTarget, PathBuf)> { +fn bundle_paths( + root_dir: &Path, + _version: &str, + v1compatible: bool, +) -> Vec<(BundleTarget, PathBuf)> { vec![( BundleTarget::App, - root_dir.join("target/debug/bundle/ios/app-updater.ipa"), + root_dir.join("target/release/bundle/ios/app-updater.ipa"), )] } #[cfg(target_os = "android")] -fn bundle_path(root_dir: &Path, _version: &str) -> PathBuf { - root_dir.join("target/debug/bundle/android/app-updater.apk") +fn bundle_path(root_dir: &Path, _version: &str, v1compatible: bool) -> PathBuf { + root_dir.join("target/release/bundle/android/app-updater.apk") } #[cfg(windows)] -fn bundle_paths(root_dir: &Path, version: &str) -> Vec<(BundleTarget, PathBuf)> { +fn test_cases( + root_dir: &Path, + version: &str, + target: String, +) -> Vec<(BundleTarget, PathBuf, Option, Vec)> { vec![ ( BundleTarget::Nsis, root_dir.join(format!( - "target/debug/bundle/nsis/app-updater_{version}_x64-setup.exe" + "target/release/bundle/nsis/app-updater_{version}_x64-setup.exe" )), + Some(target.clone()), + vec![UPDATED_EXIT_CODE], + ), + ( + BundleTarget::Nsis, + root_dir.join(format!( + "target/release/bundle/nsis/app-updater_{version}_x64-setup.exe" + )), + Some(format!("{target}-{}", BundleTarget::Nsis.name())), + vec![UPDATED_EXIT_CODE], + ), + ( + BundleTarget::Nsis, + root_dir.join(format!( + "target/release/bundle/nsis/app-updater_{version}_x64-setup.exe" + )), + None, + vec![ERROR_EXIT_CODE], ), ( BundleTarget::Msi, root_dir.join(format!( - "target/debug/bundle/msi/app-updater_{version}_x64_en-US.msi" + "target/release/bundle/msi/app-updater_{version}_x64_en-US.msi" )), + Some(target.clone()), + vec![UPDATED_EXIT_CODE], + ), + ( + BundleTarget::Msi, + root_dir.join(format!( + "target/release/bundle/msi/app-updater_{version}_x64_en-US.msi" + )), + Some(format!("{target}-{}", BundleTarget::Msi.name())), + vec![UPDATED_EXIT_CODE], + ), + ( + BundleTarget::Msi, + root_dir.join(format!( + "target/release/bundle/msi/app-updater_{version}_x64_en-US.msi" + )), + None, + vec![ERROR_EXIT_CODE], ), ] } #[test] -#[ignore] fn update_app() { let target = tauri_plugin_updater::target().expect("running updater test in an unsupported platform"); @@ -185,9 +301,6 @@ fn update_app() { Updater::String(V1Compatible::V1Compatible) ); - // bundle app update - build_app(&manifest_dir, &config, true, Default::default()); - let updater_zip_ext = if v1_compatible { if cfg!(windows) { Some("zip") @@ -200,7 +313,13 @@ fn update_app() { None }; - for (bundle_target, out_bundle_path) in bundle_paths(&root_dir, "1.0.0") { + for (bundle_target, out_bundle_path, update_platform, status_checks) in + test_cases(&root_dir, "1.0.0", target.clone()) + { + // bundle app update + config.version = "1.0.0"; + build_app(&manifest_dir, &config, true, BundleTarget::default()); + let bundle_updater_ext = if v1_compatible { out_bundle_path .extension() @@ -228,13 +347,11 @@ fn update_app() { }); let out_updater_path = out_bundle_path.with_extension(updater_extension); let updater_path = root_dir.join(format!( - "target/debug/{}", + "target/release/{}", out_updater_path.file_name().unwrap().to_str().unwrap() )); std::fs::rename(&out_updater_path, &updater_path).expect("failed to rename bundle"); - let target = target.clone(); - // start the updater server let server = Arc::new( tiny_http::Server::http("localhost:3007").expect("failed to start updater server"), @@ -245,16 +362,9 @@ fn update_app() { for request in server_.incoming_requests() { match request.url() { "/" => { - let mut platforms = HashMap::new(); + let platforms = + target_to_platforms(update_platform.clone(), signature.clone()); - platforms.insert( - target.clone(), - PlatformUpdate { - signature: signature.clone(), - url: "http://localhost:3007/download", - with_elevated_task: false, - }, - ); let body = serde_json::to_vec(&Update { version: "1.0.0", date: time::OffsetDateTime::now_utc() @@ -293,19 +403,12 @@ fn update_app() { // bundle initial app version build_app(&manifest_dir, &config, false, bundle_target); - let status_checks = if matches!(bundle_target, BundleTarget::Msi) { - // for msi we can't really check if the app was updated, because we can't change the install path - vec![UPDATED_EXIT_CODE] - } else { - vec![UPDATED_EXIT_CODE, UP_TO_DATE_EXIT_CODE] - }; - for expected_exit_code in status_checks { let mut binary_cmd = if cfg!(windows) { - Command::new(root_dir.join("target/debug/app-updater.exe")) + Command::new(root_dir.join("target/release/app-updater.exe")) } else if cfg!(target_os = "macos") { Command::new( - bundle_paths(&root_dir, "0.1.0") + test_cases(&root_dir, "0.1.0", target.clone()) .first() .unwrap() .1 @@ -313,11 +416,20 @@ fn update_app() { ) } else if std::env::var("CI").map(|v| v == "true").unwrap_or_default() { let mut c = Command::new("xvfb-run"); - c.arg("--auto-servernum") - .arg(&bundle_paths(&root_dir, "0.1.0").first().unwrap().1); + c.arg("--auto-servernum").arg( + &test_cases(&root_dir, "0.1.0", target.clone()) + .first() + .unwrap() + .1, + ); c } else { - Command::new(&bundle_paths(&root_dir, "0.1.0").first().unwrap().1) + Command::new( + &test_cases(&root_dir, "0.1.0", target.clone()) + .first() + .unwrap() + .1, + ) }; binary_cmd.env("TARGET", bundle_target.name()); @@ -327,7 +439,7 @@ fn update_app() { if code != expected_exit_code { panic!( - "failed to run app, expected exit code {expected_exit_code}, got {code}" + "failed to run app bundled as {}, expected exit code {expected_exit_code}, got {code}", bundle_target.name() ); } #[cfg(windows)] diff --git a/plugins/upload/package.json b/plugins/upload/package.json index 2d687f591..249cc01ba 100644 --- a/plugins/upload/package.json +++ b/plugins/upload/package.json @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/websocket/examples/tauri-app/package.json b/plugins/websocket/examples/tauri-app/package.json index b9c6f38ac..073e4166d 100644 --- a/plugins/websocket/examples/tauri-app/package.json +++ b/plugins/websocket/examples/tauri-app/package.json @@ -9,9 +9,9 @@ "preview": "vite preview" }, "devDependencies": { - "@tauri-apps/cli": "2.7.1", + "@tauri-apps/cli": "2.8.4", "typescript": "^5.7.3", - "vite": "^7.0.4" + "vite": "^7.0.7" }, "dependencies": { "tauri-plugin-websocket-api": "link:..\\.." diff --git a/plugins/websocket/package.json b/plugins/websocket/package.json index 60cbe80b9..901752df6 100644 --- a/plugins/websocket/package.json +++ b/plugins/websocket/package.json @@ -24,6 +24,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/plugins/window-state/api-iife.js b/plugins/window-state/api-iife.js index d87a4adef..b9586faa5 100644 --- a/plugins/window-state/api-iife.js +++ b/plugins/window-state/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_WINDOW_STATE__=function(e){"use strict";var t;"function"==typeof SuppressedError&&SuppressedError;const i="__TAURI_TO_IPC_KEY__";function n(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}async function a(e,t={},i){return window.__TAURI_INTERNALS__.invoke(e,t,i)}class s{get rid(){return function(e,t,i,n){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"===i?n:"a"===i?n.call(e):n?n.value:t.get(e)}(this,t,"f")}constructor(e){t.set(this,void 0),function(e,t,i){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,i)}(this,t,e)}async close(){return a("plugin:resources|close",{rid:this.rid})}}t=new WeakMap;class l{constructor(...e){this.type="Logical",1===e.length?"Logical"in e[0]?(this.width=e[0].Logical.width,this.height=e[0].Logical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toPhysical(e){return new r(this.width*e,this.height*e)}[i](){return{width:this.width,height:this.height}}toJSON(){return this[i]()}}class r{constructor(...e){this.type="Physical",1===e.length?"Physical"in e[0]?(this.width=e[0].Physical.width,this.height=e[0].Physical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toLogical(e){return new l(this.width/e,this.height/e)}[i](){return{width:this.width,height:this.height}}toJSON(){return this[i]()}}class o{constructor(e){this.size=e}toLogical(e){return this.size instanceof l?this.size:this.size.toLogical(e)}toPhysical(e){return this.size instanceof r?this.size:this.size.toPhysical(e)}[i](){return{[`${this.size.type}`]:{width:this.size.width,height:this.size.height}}}toJSON(){return this[i]()}}class u{constructor(...e){this.type="Logical",1===e.length?"Logical"in e[0]?(this.x=e[0].Logical.x,this.y=e[0].Logical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toPhysical(e){return new c(this.x*e,this.y*e)}[i](){return{x:this.x,y:this.y}}toJSON(){return this[i]()}}class c{constructor(...e){this.type="Physical",1===e.length?"Physical"in e[0]?(this.x=e[0].Physical.x,this.y=e[0].Physical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toLogical(e){return new u(this.x/e,this.y/e)}[i](){return{x:this.x,y:this.y}}toJSON(){return this[i]()}}class h{constructor(e){this.position=e}toLogical(e){return this.position instanceof u?this.position:this.position.toLogical(e)}toPhysical(e){return this.position instanceof c?this.position:this.position.toPhysical(e)}[i](){return{[`${this.position.type}`]:{x:this.position.x,y:this.position.y}}}toJSON(){return this[i]()}}var d,w,b;async function g(e,t){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(e,t),await a("plugin:event|unlisten",{event:e,eventId:t})}async function y(e,t,i){var s;const l="string"==typeof(null==i?void 0:i.target)?{kind:"AnyLabel",label:i.target}:null!==(s=null==i?void 0:i.target)&&void 0!==s?s:{kind:"Any"};return a("plugin:event|listen",{event:e,target:l,handler:n(t)}).then((t=>async()=>g(e,t)))}!function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(d||(d={}));class p extends s{constructor(e){super(e)}static async new(e,t,i){return a("plugin:image|new",{rgba:_(e),width:t,height:i}).then((e=>new p(e)))}static async fromBytes(e){return a("plugin:image|from_bytes",{bytes:_(e)}).then((e=>new p(e)))}static async fromPath(e){return a("plugin:image|from_path",{path:e}).then((e=>new p(e)))}async rgba(){return a("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return a("plugin:image|size",{rid:this.rid})}}function _(e){return null==e?null:"string"==typeof e?e:e instanceof p?e.rid:e}!function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"}(w||(w={}));class v{constructor(e){this._preventDefault=!1,this.event=e.event,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}}function f(){return new I(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}async function m(){return a("plugin:window|get_all_windows").then((e=>e.map((e=>new I(e,{skip:!0})))))}!function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"}(b||(b={}));const E=["tauri://created","tauri://error"];class I{constructor(e,t={}){var i;this.label=e,this.listeners=Object.create(null),(null==t?void 0:t.skip)||a("plugin:window|create",{options:{...t,parent:"string"==typeof t.parent?t.parent:null===(i=t.parent)||void 0===i?void 0:i.label,label:e}}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static async getByLabel(e){var t;return null!==(t=(await m()).find((t=>t.label===e)))&&void 0!==t?t:null}static getCurrent(){return f()}static async getAll(){return m()}static async getFocusedWindow(){for(const e of await m())if(await e.isFocused())return e;return null}async listen(e,t){return this._handleTauriEvent(e,t)?()=>{const i=this.listeners[e];i.splice(i.indexOf(t),1)}:y(e,t,{target:{kind:"Window",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?()=>{const i=this.listeners[e];i.splice(i.indexOf(t),1)}:async function(e,t,i){return y(e,(i=>{g(e,i.id),t(i)}),i)}(e,t,{target:{kind:"Window",label:this.label}})}async emit(e,t){if(!E.includes(e))return async function(e,t){await a("plugin:event|emit",{event:e,payload:t})}(e,t);for(const i of this.listeners[e]||[])i({event:e,id:-1,payload:t})}async emitTo(e,t,i){if(!E.includes(t))return async function(e,t,i){const n="string"==typeof e?{kind:"AnyLabel",label:e}:e;await a("plugin:event|emit_to",{target:n,event:t,payload:i})}(e,t,i);for(const e of this.listeners[t]||[])e({event:t,id:-1,payload:i})}_handleTauriEvent(e,t){return!!E.includes(e)&&(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0)}async scaleFactor(){return a("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return a("plugin:window|inner_position",{label:this.label}).then((e=>new c(e)))}async outerPosition(){return a("plugin:window|outer_position",{label:this.label}).then((e=>new c(e)))}async innerSize(){return a("plugin:window|inner_size",{label:this.label}).then((e=>new r(e)))}async outerSize(){return a("plugin:window|outer_size",{label:this.label}).then((e=>new r(e)))}async isFullscreen(){return a("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return a("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return a("plugin:window|is_maximized",{label:this.label})}async isFocused(){return a("plugin:window|is_focused",{label:this.label})}async isDecorated(){return a("plugin:window|is_decorated",{label:this.label})}async isResizable(){return a("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return a("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return a("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return a("plugin:window|is_closable",{label:this.label})}async isVisible(){return a("plugin:window|is_visible",{label:this.label})}async title(){return a("plugin:window|title",{label:this.label})}async theme(){return a("plugin:window|theme",{label:this.label})}async isAlwaysOnTop(){return a("plugin:window|is_always_on_top",{label:this.label})}async center(){return a("plugin:window|center",{label:this.label})}async requestUserAttention(e){let t=null;return e&&(t=e===w.Critical?{type:"Critical"}:{type:"Informational"}),a("plugin:window|request_user_attention",{label:this.label,value:t})}async setResizable(e){return a("plugin:window|set_resizable",{label:this.label,value:e})}async setEnabled(e){return a("plugin:window|set_enabled",{label:this.label,value:e})}async isEnabled(){return a("plugin:window|is_enabled",{label:this.label})}async setMaximizable(e){return a("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return a("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return a("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return a("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return a("plugin:window|maximize",{label:this.label})}async unmaximize(){return a("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return a("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return a("plugin:window|minimize",{label:this.label})}async unminimize(){return a("plugin:window|unminimize",{label:this.label})}async show(){return a("plugin:window|show",{label:this.label})}async hide(){return a("plugin:window|hide",{label:this.label})}async close(){return a("plugin:window|close",{label:this.label})}async destroy(){return a("plugin:window|destroy",{label:this.label})}async setDecorations(e){return a("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return a("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return a("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return a("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return a("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return a("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return a("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){return a("plugin:window|set_size",{label:this.label,value:e instanceof o?e:new o(e)})}async setMinSize(e){return a("plugin:window|set_min_size",{label:this.label,value:e instanceof o?e:e?new o(e):null})}async setMaxSize(e){return a("plugin:window|set_max_size",{label:this.label,value:e instanceof o?e:e?new o(e):null})}async setSizeConstraints(e){function t(e){return e?{Logical:e}:null}return a("plugin:window|set_size_constraints",{label:this.label,value:{minWidth:t(null==e?void 0:e.minWidth),minHeight:t(null==e?void 0:e.minHeight),maxWidth:t(null==e?void 0:e.maxWidth),maxHeight:t(null==e?void 0:e.maxHeight)}})}async setPosition(e){return a("plugin:window|set_position",{label:this.label,value:e instanceof h?e:new h(e)})}async setFullscreen(e){return a("plugin:window|set_fullscreen",{label:this.label,value:e})}async setFocus(){return a("plugin:window|set_focus",{label:this.label})}async setIcon(e){return a("plugin:window|set_icon",{label:this.label,value:_(e)})}async setSkipTaskbar(e){return a("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return a("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return a("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return a("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setBackgroundColor(e){return a("plugin:window|set_background_color",{color:e})}async setCursorPosition(e){return a("plugin:window|set_cursor_position",{label:this.label,value:e instanceof h?e:new h(e)})}async setIgnoreCursorEvents(e){return a("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return a("plugin:window|start_dragging",{label:this.label})}async startResizeDragging(e){return a("plugin:window|start_resize_dragging",{label:this.label,value:e})}async setBadgeCount(e){return a("plugin:window|set_badge_count",{label:this.label,value:e})}async setBadgeLabel(e){return a("plugin:window|set_badge_label",{label:this.label,value:e})}async setOverlayIcon(e){return a("plugin:window|set_overlay_icon",{label:this.label,value:e?_(e):void 0})}async setProgressBar(e){return a("plugin:window|set_progress_bar",{label:this.label,value:e})}async setVisibleOnAllWorkspaces(e){return a("plugin:window|set_visible_on_all_workspaces",{label:this.label,value:e})}async setTitleBarStyle(e){return a("plugin:window|set_title_bar_style",{label:this.label,value:e})}async setTheme(e){return a("plugin:window|set_theme",{label:this.label,value:e})}async onResized(e){return this.listen(d.WINDOW_RESIZED,(t=>{t.payload=new r(t.payload),e(t)}))}async onMoved(e){return this.listen(d.WINDOW_MOVED,(t=>{t.payload=new c(t.payload),e(t)}))}async onCloseRequested(e){return this.listen(d.WINDOW_CLOSE_REQUESTED,(async t=>{const i=new v(t);await e(i),i.isPreventDefault()||await this.destroy()}))}async onDragDropEvent(e){const t=await this.listen(d.DRAG_ENTER,(t=>{e({...t,payload:{type:"enter",paths:t.payload.paths,position:new c(t.payload.position)}})})),i=await this.listen(d.DRAG_OVER,(t=>{e({...t,payload:{type:"over",position:new c(t.payload.position)}})})),n=await this.listen(d.DRAG_DROP,(t=>{e({...t,payload:{type:"drop",paths:t.payload.paths,position:new c(t.payload.position)}})})),a=await this.listen(d.DRAG_LEAVE,(t=>{e({...t,payload:{type:"leave"}})}));return()=>{t(),n(),i(),a()}}async onFocusChanged(e){const t=await this.listen(d.WINDOW_FOCUS,(t=>{e({...t,payload:!0})})),i=await this.listen(d.WINDOW_BLUR,(t=>{e({...t,payload:!1})}));return()=>{t(),i()}}async onScaleChanged(e){return this.listen(d.WINDOW_SCALE_FACTOR_CHANGED,e)}async onThemeChanged(e){return this.listen(d.WINDOW_THEME_CHANGED,e)}}var D,S,A,W;async function O(e,t){await a("plugin:window-state|restore_state",{label:e,flags:t})}return function(e){e.Disabled="disabled",e.Throttle="throttle",e.Suspend="suspend"}(D||(D={})),function(e){e.AppearanceBased="appearanceBased",e.Light="light",e.Dark="dark",e.MediumLight="mediumLight",e.UltraDark="ultraDark",e.Titlebar="titlebar",e.Selection="selection",e.Menu="menu",e.Popover="popover",e.Sidebar="sidebar",e.HeaderView="headerView",e.Sheet="sheet",e.WindowBackground="windowBackground",e.HudWindow="hudWindow",e.FullScreenUI="fullScreenUI",e.Tooltip="tooltip",e.ContentBackground="contentBackground",e.UnderWindowBackground="underWindowBackground",e.UnderPageBackground="underPageBackground",e.Mica="mica",e.Blur="blur",e.Acrylic="acrylic",e.Tabbed="tabbed",e.TabbedDark="tabbedDark",e.TabbedLight="tabbedLight"}(S||(S={})),function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"}(A||(A={})),e.StateFlags=void 0,(W=e.StateFlags||(e.StateFlags={}))[W.SIZE=1]="SIZE",W[W.POSITION=2]="POSITION",W[W.MAXIMIZED=4]="MAXIMIZED",W[W.VISIBLE=8]="VISIBLE",W[W.DECORATIONS=16]="DECORATIONS",W[W.FULLSCREEN=32]="FULLSCREEN",W[W.ALL=63]="ALL",e.filename=async function(){return await a("plugin:window-state|filename")},e.restoreState=O,e.restoreStateCurrent=async function(e){await O(f().label,e)},e.saveWindowState=async function(e){await a("plugin:window-state|save_window_state",{flags:e})},e}({});Object.defineProperty(window.__TAURI__,"windowState",{value:__TAURI_PLUGIN_WINDOW_STATE__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_WINDOW_STATE__=function(e){"use strict";var t;"function"==typeof SuppressedError&&SuppressedError;const i="__TAURI_TO_IPC_KEY__";function n(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}async function a(e,t={},i){return window.__TAURI_INTERNALS__.invoke(e,t,i)}class l{get rid(){return function(e,t,i,n){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"===i?n:"a"===i?n.call(e):n?n.value:t.get(e)}(this,t,"f")}constructor(e){t.set(this,void 0),function(e,t,i){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,i)}(this,t,e)}async close(){return a("plugin:resources|close",{rid:this.rid})}}t=new WeakMap;class s{constructor(...e){this.type="Logical",1===e.length?"Logical"in e[0]?(this.width=e[0].Logical.width,this.height=e[0].Logical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toPhysical(e){return new r(this.width*e,this.height*e)}[i](){return{width:this.width,height:this.height}}toJSON(){return this[i]()}}class r{constructor(...e){this.type="Physical",1===e.length?"Physical"in e[0]?(this.width=e[0].Physical.width,this.height=e[0].Physical.height):(this.width=e[0].width,this.height=e[0].height):(this.width=e[0],this.height=e[1])}toLogical(e){return new s(this.width/e,this.height/e)}[i](){return{width:this.width,height:this.height}}toJSON(){return this[i]()}}class o{constructor(e){this.size=e}toLogical(e){return this.size instanceof s?this.size:this.size.toLogical(e)}toPhysical(e){return this.size instanceof r?this.size:this.size.toPhysical(e)}[i](){return{[`${this.size.type}`]:{width:this.size.width,height:this.size.height}}}toJSON(){return this[i]()}}class u{constructor(...e){this.type="Logical",1===e.length?"Logical"in e[0]?(this.x=e[0].Logical.x,this.y=e[0].Logical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toPhysical(e){return new c(this.x*e,this.y*e)}[i](){return{x:this.x,y:this.y}}toJSON(){return this[i]()}}class c{constructor(...e){this.type="Physical",1===e.length?"Physical"in e[0]?(this.x=e[0].Physical.x,this.y=e[0].Physical.y):(this.x=e[0].x,this.y=e[0].y):(this.x=e[0],this.y=e[1])}toLogical(e){return new u(this.x/e,this.y/e)}[i](){return{x:this.x,y:this.y}}toJSON(){return this[i]()}}class h{constructor(e){this.position=e}toLogical(e){return this.position instanceof u?this.position:this.position.toLogical(e)}toPhysical(e){return this.position instanceof c?this.position:this.position.toPhysical(e)}[i](){return{[`${this.position.type}`]:{x:this.position.x,y:this.position.y}}}toJSON(){return this[i]()}}var d,w,b;async function g(e,t){window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener(e,t),await a("plugin:event|unlisten",{event:e,eventId:t})}async function p(e,t,i){var l;const s="string"==typeof(null==i?void 0:i.target)?{kind:"AnyLabel",label:i.target}:null!==(l=null==i?void 0:i.target)&&void 0!==l?l:{kind:"Any"};return a("plugin:event|listen",{event:e,target:s,handler:n(t)}).then((t=>async()=>g(e,t)))}!function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG_ENTER="tauri://drag-enter",e.DRAG_OVER="tauri://drag-over",e.DRAG_DROP="tauri://drag-drop",e.DRAG_LEAVE="tauri://drag-leave"}(d||(d={}));class y extends l{constructor(e){super(e)}static async new(e,t,i){return a("plugin:image|new",{rgba:_(e),width:t,height:i}).then((e=>new y(e)))}static async fromBytes(e){return a("plugin:image|from_bytes",{bytes:_(e)}).then((e=>new y(e)))}static async fromPath(e){return a("plugin:image|from_path",{path:e}).then((e=>new y(e)))}async rgba(){return a("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return a("plugin:image|size",{rid:this.rid})}}function _(e){return null==e?null:"string"==typeof e?e:e instanceof y?e.rid:e}!function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"}(w||(w={}));class v{constructor(e){this._preventDefault=!1,this.event=e.event,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}}function f(){return new I(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}async function m(){return a("plugin:window|get_all_windows").then((e=>e.map((e=>new I(e,{skip:!0})))))}!function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"}(b||(b={}));const E=["tauri://created","tauri://error"];class I{constructor(e,t={}){var i;this.label=e,this.listeners=Object.create(null),(null==t?void 0:t.skip)||a("plugin:window|create",{options:{...t,parent:"string"==typeof t.parent?t.parent:null===(i=t.parent)||void 0===i?void 0:i.label,label:e}}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static async getByLabel(e){var t;return null!==(t=(await m()).find((t=>t.label===e)))&&void 0!==t?t:null}static getCurrent(){return f()}static async getAll(){return m()}static async getFocusedWindow(){for(const e of await m())if(await e.isFocused())return e;return null}async listen(e,t){return this._handleTauriEvent(e,t)?()=>{const i=this.listeners[e];i.splice(i.indexOf(t),1)}:p(e,t,{target:{kind:"Window",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?()=>{const i=this.listeners[e];i.splice(i.indexOf(t),1)}:async function(e,t,i){return p(e,(i=>{g(e,i.id),t(i)}),i)}(e,t,{target:{kind:"Window",label:this.label}})}async emit(e,t){if(!E.includes(e))return async function(e,t){await a("plugin:event|emit",{event:e,payload:t})}(e,t);for(const i of this.listeners[e]||[])i({event:e,id:-1,payload:t})}async emitTo(e,t,i){if(!E.includes(t))return async function(e,t,i){const n="string"==typeof e?{kind:"AnyLabel",label:e}:e;await a("plugin:event|emit_to",{target:n,event:t,payload:i})}(e,t,i);for(const e of this.listeners[t]||[])e({event:t,id:-1,payload:i})}_handleTauriEvent(e,t){return!!E.includes(e)&&(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0)}async scaleFactor(){return a("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return a("plugin:window|inner_position",{label:this.label}).then((e=>new c(e)))}async outerPosition(){return a("plugin:window|outer_position",{label:this.label}).then((e=>new c(e)))}async innerSize(){return a("plugin:window|inner_size",{label:this.label}).then((e=>new r(e)))}async outerSize(){return a("plugin:window|outer_size",{label:this.label}).then((e=>new r(e)))}async isFullscreen(){return a("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return a("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return a("plugin:window|is_maximized",{label:this.label})}async isFocused(){return a("plugin:window|is_focused",{label:this.label})}async isDecorated(){return a("plugin:window|is_decorated",{label:this.label})}async isResizable(){return a("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return a("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return a("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return a("plugin:window|is_closable",{label:this.label})}async isVisible(){return a("plugin:window|is_visible",{label:this.label})}async title(){return a("plugin:window|title",{label:this.label})}async theme(){return a("plugin:window|theme",{label:this.label})}async isAlwaysOnTop(){return a("plugin:window|is_always_on_top",{label:this.label})}async center(){return a("plugin:window|center",{label:this.label})}async requestUserAttention(e){let t=null;return e&&(t=e===w.Critical?{type:"Critical"}:{type:"Informational"}),a("plugin:window|request_user_attention",{label:this.label,value:t})}async setResizable(e){return a("plugin:window|set_resizable",{label:this.label,value:e})}async setEnabled(e){return a("plugin:window|set_enabled",{label:this.label,value:e})}async isEnabled(){return a("plugin:window|is_enabled",{label:this.label})}async setMaximizable(e){return a("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return a("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return a("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return a("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return a("plugin:window|maximize",{label:this.label})}async unmaximize(){return a("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return a("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return a("plugin:window|minimize",{label:this.label})}async unminimize(){return a("plugin:window|unminimize",{label:this.label})}async show(){return a("plugin:window|show",{label:this.label})}async hide(){return a("plugin:window|hide",{label:this.label})}async close(){return a("plugin:window|close",{label:this.label})}async destroy(){return a("plugin:window|destroy",{label:this.label})}async setDecorations(e){return a("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return a("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return a("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return a("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return a("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return a("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return a("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){return a("plugin:window|set_size",{label:this.label,value:e instanceof o?e:new o(e)})}async setMinSize(e){return a("plugin:window|set_min_size",{label:this.label,value:e instanceof o?e:e?new o(e):null})}async setMaxSize(e){return a("plugin:window|set_max_size",{label:this.label,value:e instanceof o?e:e?new o(e):null})}async setSizeConstraints(e){function t(e){return e?{Logical:e}:null}return a("plugin:window|set_size_constraints",{label:this.label,value:{minWidth:t(null==e?void 0:e.minWidth),minHeight:t(null==e?void 0:e.minHeight),maxWidth:t(null==e?void 0:e.maxWidth),maxHeight:t(null==e?void 0:e.maxHeight)}})}async setPosition(e){return a("plugin:window|set_position",{label:this.label,value:e instanceof h?e:new h(e)})}async setFullscreen(e){return a("plugin:window|set_fullscreen",{label:this.label,value:e})}async setSimpleFullscreen(e){return a("plugin:window|set_simple_fullscreen",{label:this.label,value:e})}async setFocus(){return a("plugin:window|set_focus",{label:this.label})}async setFocusable(e){return a("plugin:window|set_focusable",{label:this.label,value:e})}async setIcon(e){return a("plugin:window|set_icon",{label:this.label,value:_(e)})}async setSkipTaskbar(e){return a("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return a("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return a("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return a("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setBackgroundColor(e){return a("plugin:window|set_background_color",{color:e})}async setCursorPosition(e){return a("plugin:window|set_cursor_position",{label:this.label,value:e instanceof h?e:new h(e)})}async setIgnoreCursorEvents(e){return a("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return a("plugin:window|start_dragging",{label:this.label})}async startResizeDragging(e){return a("plugin:window|start_resize_dragging",{label:this.label,value:e})}async setBadgeCount(e){return a("plugin:window|set_badge_count",{label:this.label,value:e})}async setBadgeLabel(e){return a("plugin:window|set_badge_label",{label:this.label,value:e})}async setOverlayIcon(e){return a("plugin:window|set_overlay_icon",{label:this.label,value:e?_(e):void 0})}async setProgressBar(e){return a("plugin:window|set_progress_bar",{label:this.label,value:e})}async setVisibleOnAllWorkspaces(e){return a("plugin:window|set_visible_on_all_workspaces",{label:this.label,value:e})}async setTitleBarStyle(e){return a("plugin:window|set_title_bar_style",{label:this.label,value:e})}async setTheme(e){return a("plugin:window|set_theme",{label:this.label,value:e})}async onResized(e){return this.listen(d.WINDOW_RESIZED,(t=>{t.payload=new r(t.payload),e(t)}))}async onMoved(e){return this.listen(d.WINDOW_MOVED,(t=>{t.payload=new c(t.payload),e(t)}))}async onCloseRequested(e){return this.listen(d.WINDOW_CLOSE_REQUESTED,(async t=>{const i=new v(t);await e(i),i.isPreventDefault()||await this.destroy()}))}async onDragDropEvent(e){const t=await this.listen(d.DRAG_ENTER,(t=>{e({...t,payload:{type:"enter",paths:t.payload.paths,position:new c(t.payload.position)}})})),i=await this.listen(d.DRAG_OVER,(t=>{e({...t,payload:{type:"over",position:new c(t.payload.position)}})})),n=await this.listen(d.DRAG_DROP,(t=>{e({...t,payload:{type:"drop",paths:t.payload.paths,position:new c(t.payload.position)}})})),a=await this.listen(d.DRAG_LEAVE,(t=>{e({...t,payload:{type:"leave"}})}));return()=>{t(),n(),i(),a()}}async onFocusChanged(e){const t=await this.listen(d.WINDOW_FOCUS,(t=>{e({...t,payload:!0})})),i=await this.listen(d.WINDOW_BLUR,(t=>{e({...t,payload:!1})}));return()=>{t(),i()}}async onScaleChanged(e){return this.listen(d.WINDOW_SCALE_FACTOR_CHANGED,e)}async onThemeChanged(e){return this.listen(d.WINDOW_THEME_CHANGED,e)}}var D,S,A,W;async function O(e,t){await a("plugin:window-state|restore_state",{label:e,flags:t})}return function(e){e.Disabled="disabled",e.Throttle="throttle",e.Suspend="suspend"}(D||(D={})),function(e){e.AppearanceBased="appearanceBased",e.Light="light",e.Dark="dark",e.MediumLight="mediumLight",e.UltraDark="ultraDark",e.Titlebar="titlebar",e.Selection="selection",e.Menu="menu",e.Popover="popover",e.Sidebar="sidebar",e.HeaderView="headerView",e.Sheet="sheet",e.WindowBackground="windowBackground",e.HudWindow="hudWindow",e.FullScreenUI="fullScreenUI",e.Tooltip="tooltip",e.ContentBackground="contentBackground",e.UnderWindowBackground="underWindowBackground",e.UnderPageBackground="underPageBackground",e.Mica="mica",e.Blur="blur",e.Acrylic="acrylic",e.Tabbed="tabbed",e.TabbedDark="tabbedDark",e.TabbedLight="tabbedLight"}(S||(S={})),function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"}(A||(A={})),e.StateFlags=void 0,(W=e.StateFlags||(e.StateFlags={}))[W.SIZE=1]="SIZE",W[W.POSITION=2]="POSITION",W[W.MAXIMIZED=4]="MAXIMIZED",W[W.VISIBLE=8]="VISIBLE",W[W.DECORATIONS=16]="DECORATIONS",W[W.FULLSCREEN=32]="FULLSCREEN",W[W.ALL=63]="ALL",e.filename=async function(){return await a("plugin:window-state|filename")},e.restoreState=O,e.restoreStateCurrent=async function(e){await O(f().label,e)},e.saveWindowState=async function(e){await a("plugin:window-state|save_window_state",{flags:e})},e}({});Object.defineProperty(window.__TAURI__,"windowState",{value:__TAURI_PLUGIN_WINDOW_STATE__})} diff --git a/plugins/window-state/package.json b/plugins/window-state/package.json index ac5555cee..14179c30e 100644 --- a/plugins/window-state/package.json +++ b/plugins/window-state/package.json @@ -25,6 +25,6 @@ "LICENSE" ], "dependencies": { - "@tauri-apps/api": "^2.6.0" + "@tauri-apps/api": "^2.8.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d5ccb1675..542879114 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,26 +12,26 @@ importers: .: devDependencies: '@eslint/js': - specifier: 9.32.0 - version: 9.32.0 + specifier: 9.36.0 + version: 9.36.0 '@rollup/plugin-node-resolve': specifier: 16.0.1 - version: 16.0.1(rollup@4.46.2) + version: 16.0.1(rollup@4.52.3) '@rollup/plugin-terser': specifier: 0.4.4 - version: 0.4.4(rollup@4.46.2) + version: 0.4.4(rollup@4.52.3) '@rollup/plugin-typescript': specifier: 12.1.4 - version: 12.1.4(rollup@4.46.2)(tslib@2.8.1)(typescript@5.9.2) + version: 12.1.4(rollup@4.52.3)(tslib@2.8.1)(typescript@5.9.2) covector: specifier: ^0.12.4 version: 0.12.4(mocha@10.8.2) eslint: - specifier: 9.32.0 - version: 9.32.0(jiti@2.4.2) + specifier: 9.36.0 + version: 9.36.0(jiti@2.4.2) eslint-config-prettier: specifier: 10.1.8 - version: 10.1.8(eslint@9.32.0(jiti@2.4.2)) + version: 10.1.8(eslint@9.36.0(jiti@2.4.2)) eslint-plugin-security: specifier: 3.0.1 version: 3.0.1 @@ -39,8 +39,8 @@ importers: specifier: 3.6.2 version: 3.6.2 rollup: - specifier: 4.46.2 - version: 4.46.2 + specifier: 4.52.3 + version: 4.52.3 tslib: specifier: 2.8.1 version: 2.8.1 @@ -48,14 +48,14 @@ importers: specifier: 5.9.2 version: 5.9.2 typescript-eslint: - specifier: 8.39.0 - version: 8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2) + specifier: 8.44.1 + version: 8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2) examples/api: dependencies: '@tauri-apps/api': - specifier: 2.7.0 - version: 2.7.0 + specifier: 2.8.0 + version: 2.8.0 '@tauri-apps/plugin-barcode-scanner': specifier: ^2.4.0 version: link:../../plugins/barcode-scanner @@ -69,10 +69,10 @@ importers: specifier: ^2.3.0 version: link:../../plugins/clipboard-manager '@tauri-apps/plugin-dialog': - specifier: ^2.3.2 + specifier: ^2.4.0 version: link:../../plugins/dialog '@tauri-apps/plugin-fs': - specifier: ^2.4.1 + specifier: ^2.4.2 version: link:../../plugins/fs '@tauri-apps/plugin-geolocation': specifier: ^2.2.0 @@ -84,19 +84,19 @@ importers: specifier: ^2.2.0 version: link:../../plugins/haptics '@tauri-apps/plugin-http': - specifier: ^2.5.1 + specifier: ^2.5.2 version: link:../../plugins/http '@tauri-apps/plugin-nfc': - specifier: ^2.3.0 + specifier: ^2.3.1 version: link:../../plugins/nfc '@tauri-apps/plugin-notification': - specifier: ^2.3.0 + specifier: ^2.3.1 version: link:../../plugins/notification '@tauri-apps/plugin-opener': - specifier: ^2.4.0 + specifier: ^2.5.0 version: link:../../plugins/opener '@tauri-apps/plugin-os': - specifier: ^2.3.0 + specifier: ^2.3.1 version: link:../../plugins/os '@tauri-apps/plugin-process': specifier: ^2.3.0 @@ -105,10 +105,10 @@ importers: specifier: file:../../plugins/secure-storage version: link:../../plugins/secure-storage '@tauri-apps/plugin-shell': - specifier: ^2.3.0 + specifier: ^2.3.1 version: link:../../plugins/shell '@tauri-apps/plugin-store': - specifier: ^2.3.0 + specifier: ^2.4.0 version: link:../../plugins/store '@tauri-apps/plugin-updater': specifier: ^2.9.0 @@ -128,10 +128,10 @@ importers: version: 1.2.2 '@sveltejs/vite-plugin-svelte': specifier: ^6.0.0 - version: 6.0.0(svelte@5.28.2)(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)) + version: 6.0.0(svelte@5.28.2)(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)) '@tauri-apps/cli': - specifier: 2.7.1 - version: 2.7.1 + specifier: 2.8.4 + version: 2.8.4 '@unocss/extractor-svelte': specifier: ^66.3.3 version: 66.3.3 @@ -140,143 +140,147 @@ importers: version: 5.28.2 unocss: specifier: ^66.3.3 - version: 66.3.3(postcss@8.5.6)(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)) + version: 66.3.3(postcss@8.5.6)(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)) vite: - specifier: ^7.0.4 - version: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + specifier: ^7.0.7 + version: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) plugins/autostart: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/barcode-scanner: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/biometric: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/cli: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/clipboard-manager: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/deep-link: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 + devDependencies: + '@tauri-apps/cli': + specifier: 2.8.4 + version: 2.8.4 plugins/deep-link/examples/app: dependencies: '@tauri-apps/api': - specifier: 2.7.0 - version: 2.7.0 + specifier: 2.8.0 + version: 2.8.0 '@tauri-apps/plugin-deep-link': - specifier: 2.4.1 + specifier: 2.4.3 version: link:../.. devDependencies: '@tauri-apps/cli': - specifier: 2.7.1 - version: 2.7.1 + specifier: 2.8.4 + version: 2.8.4 typescript: specifier: ^5.7.3 version: 5.9.2 vite: - specifier: ^7.0.4 - version: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + specifier: ^7.0.7 + version: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) plugins/dialog: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/fs: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/geolocation: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/global-shortcut: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/haptics: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/http: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/log: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/nfc: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/notification: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/opener: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/os: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/positioner: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/process: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/secure-storage: dependencies: @@ -287,62 +291,62 @@ importers: plugins/shell: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/single-instance/examples/vanilla: devDependencies: '@tauri-apps/cli': - specifier: 2.7.1 - version: 2.7.1 + specifier: 2.8.4 + version: 2.8.4 plugins/sql: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/store: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/store/examples/AppSettingsManager: devDependencies: '@tauri-apps/cli': - specifier: 2.7.1 - version: 2.7.1 + specifier: 2.8.4 + version: 2.8.4 typescript: specifier: ^5.7.3 version: 5.9.2 vite: - specifier: ^7.0.4 - version: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + specifier: ^7.0.7 + version: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) plugins/stronghold: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/updater: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/upload: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/websocket: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 plugins/websocket/examples/tauri-app: dependencies: @@ -351,20 +355,20 @@ importers: version: link:../.. devDependencies: '@tauri-apps/cli': - specifier: 2.7.1 - version: 2.7.1 + specifier: 2.8.4 + version: 2.8.4 typescript: specifier: ^5.7.3 version: 5.9.2 vite: - specifier: ^7.0.4 - version: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + specifier: ^7.0.7 + version: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) plugins/window-state: dependencies: '@tauri-apps/api': - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.8.0 + version: 2.8.0 packages: @@ -611,8 +615,8 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + '@eslint-community/eslint-utils@4.8.0': + resolution: {integrity: sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -625,28 +629,28 @@ packages: resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.3.0': - resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.15.1': - resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.32.0': - resolution: {integrity: sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==} + '@eslint/js@9.36.0': + resolution: {integrity: sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.4': - resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==} + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': @@ -761,103 +765,113 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.46.2': - resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} + '@rollup/rollup-android-arm-eabi@4.52.3': + resolution: {integrity: sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.46.2': - resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==} + '@rollup/rollup-android-arm64@4.52.3': + resolution: {integrity: sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.46.2': - resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} + '@rollup/rollup-darwin-arm64@4.52.3': + resolution: {integrity: sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.46.2': - resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==} + '@rollup/rollup-darwin-x64@4.52.3': + resolution: {integrity: sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.46.2': - resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} + '@rollup/rollup-freebsd-arm64@4.52.3': + resolution: {integrity: sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.46.2': - resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==} + '@rollup/rollup-freebsd-x64@4.52.3': + resolution: {integrity: sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.46.2': - resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.3': + resolution: {integrity: sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.46.2': - resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} + '@rollup/rollup-linux-arm-musleabihf@4.52.3': + resolution: {integrity: sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.46.2': - resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} + '@rollup/rollup-linux-arm64-gnu@4.52.3': + resolution: {integrity: sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.46.2': - resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} + '@rollup/rollup-linux-arm64-musl@4.52.3': + resolution: {integrity: sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.46.2': - resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} + '@rollup/rollup-linux-loong64-gnu@4.52.3': + resolution: {integrity: sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.46.2': - resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} + '@rollup/rollup-linux-ppc64-gnu@4.52.3': + resolution: {integrity: sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.46.2': - resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} + '@rollup/rollup-linux-riscv64-gnu@4.52.3': + resolution: {integrity: sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.46.2': - resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} + '@rollup/rollup-linux-riscv64-musl@4.52.3': + resolution: {integrity: sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.46.2': - resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} + '@rollup/rollup-linux-s390x-gnu@4.52.3': + resolution: {integrity: sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.46.2': - resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} + '@rollup/rollup-linux-x64-gnu@4.52.3': + resolution: {integrity: sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.46.2': - resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} + '@rollup/rollup-linux-x64-musl@4.52.3': + resolution: {integrity: sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.46.2': - resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} + '@rollup/rollup-openharmony-arm64@4.52.3': + resolution: {integrity: sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.3': + resolution: {integrity: sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.46.2': - resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==} + '@rollup/rollup-win32-ia32-msvc@4.52.3': + resolution: {integrity: sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.46.2': - resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==} + '@rollup/rollup-win32-x64-gnu@4.52.3': + resolution: {integrity: sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.3': + resolution: {integrity: sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==} cpu: [x64] os: [win32] @@ -881,77 +895,77 @@ packages: svelte: ^5.0.0 vite: ^6.3.0 || ^7.0.0 - '@tauri-apps/api@2.7.0': - resolution: {integrity: sha512-v7fVE8jqBl8xJFOcBafDzXFc8FnicoH3j8o8DNNs0tHuEBmXUDqrCOAzMRX0UkfpwqZLqvrvK0GNQ45DfnoVDg==} + '@tauri-apps/api@2.8.0': + resolution: {integrity: sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==} - '@tauri-apps/cli-darwin-arm64@2.7.1': - resolution: {integrity: sha512-j2NXQN6+08G03xYiyKDKqbCV2Txt+hUKg0a8hYr92AmoCU8fgCjHyva/p16lGFGUG3P2Yu0xiNe1hXL9ZuRMzA==} + '@tauri-apps/cli-darwin-arm64@2.8.4': + resolution: {integrity: sha512-BKu8HRkYV01SMTa7r4fLx+wjgtRK8Vep7lmBdHDioP6b8XH3q2KgsAyPWfEZaZIkZ2LY4SqqGARaE9oilNe0oA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tauri-apps/cli-darwin-x64@2.7.1': - resolution: {integrity: sha512-CdYAefeM35zKsc91qIyKzbaO7FhzTyWKsE8hj7tEJ1INYpoh1NeNNyL/NSEA3Nebi5ilugioJ5tRK8ZXG8y3gw==} + '@tauri-apps/cli-darwin-x64@2.8.4': + resolution: {integrity: sha512-imb9PfSd/7G6VAO7v1bQ2A3ZH4NOCbhGJFLchxzepGcXf9NKkfun157JH9mko29K6sqAwuJ88qtzbKCbWJTH9g==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tauri-apps/cli-linux-arm-gnueabihf@2.7.1': - resolution: {integrity: sha512-dnvyJrTA1UJxJjQ8q1N/gWomjP8Twij1BUQu2fdcT3OPpqlrbOk5R1yT0oD/721xoKNjroB5BXCsmmlykllxNg==} + '@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': + resolution: {integrity: sha512-Ml215UnDdl7/fpOrF1CNovym/KjtUbCuPgrcZ4IhqUCnhZdXuphud/JT3E8X97Y03TZ40Sjz8raXYI2ET0exzw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tauri-apps/cli-linux-arm64-gnu@2.7.1': - resolution: {integrity: sha512-FtBW6LJPNRTws3qyUc294AqCWU91l/H0SsFKq6q4Q45MSS4x6wxLxou8zB53tLDGEPx3JSoPLcDaSfPlSbyujQ==} + '@tauri-apps/cli-linux-arm64-gnu@2.8.4': + resolution: {integrity: sha512-pbcgBpMyI90C83CxE5REZ9ODyIlmmAPkkJXtV398X3SgZEIYy5TACYqlyyv2z5yKgD8F8WH4/2fek7+jH+ZXAw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-arm64-musl@2.7.1': - resolution: {integrity: sha512-/HXY0t4FHkpFzjeYS5c16mlA6z0kzn5uKLWptTLTdFSnYpr8FCnOP4Sdkvm2TDQPF2ERxXtNCd+WR/jQugbGnA==} + '@tauri-apps/cli-linux-arm64-musl@2.8.4': + resolution: {integrity: sha512-zumFeaU1Ws5Ay872FTyIm7z8kfzEHu8NcIn8M6TxbJs0a7GRV21KBdpW1zNj2qy7HynnpQCqjAYXTUUmm9JAOw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-riscv64-gnu@2.7.1': - resolution: {integrity: sha512-GeW5lVI2GhhnaYckiDzstG2j2Jwlud5d2XefRGwlOK+C/bVGLT1le8MNPYK8wgRlpeK8fG1WnJJYD6Ke7YQ8bg==} + '@tauri-apps/cli-linux-riscv64-gnu@2.8.4': + resolution: {integrity: sha512-qiqbB3Zz6IyO201f+1ojxLj65WYj8mixL5cOMo63nlg8CIzsP23cPYUrx1YaDPsCLszKZo7tVs14pc7BWf+/aQ==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - '@tauri-apps/cli-linux-x64-gnu@2.7.1': - resolution: {integrity: sha512-DprxKQkPxIPYwUgg+cscpv2lcIUhn2nxEPlk0UeaiV9vATxCXyytxr1gLcj3xgjGyNPlM0MlJyYaPy1JmRg1cA==} + '@tauri-apps/cli-linux-x64-gnu@2.8.4': + resolution: {integrity: sha512-TaqaDd9Oy6k45Hotx3pOf+pkbsxLaApv4rGd9mLuRM1k6YS/aw81YrsMryYPThrxrScEIUcmNIHaHsLiU4GMkw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-linux-x64-musl@2.7.1': - resolution: {integrity: sha512-KLlq3kOK7OUyDR757c0zQjPULpGZpLhNB0lZmZpHXvoOUcqZoCXJHh4dT/mryWZJp5ilrem5l8o9ngrDo0X1AA==} + '@tauri-apps/cli-linux-x64-musl@2.8.4': + resolution: {integrity: sha512-ot9STAwyezN8w+bBHZ+bqSQIJ0qPZFlz/AyscpGqB/JnJQVDFQcRDmUPFEaAtt2UUHSWzN3GoTJ5ypqLBp2WQA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-win32-arm64-msvc@2.7.1': - resolution: {integrity: sha512-dH7KUjKkSypCeWPiainHyXoES3obS+JIZVoSwSZfKq2gWgs48FY3oT0hQNYrWveE+VR4VoR3b/F3CPGbgFvksA==} + '@tauri-apps/cli-win32-arm64-msvc@2.8.4': + resolution: {integrity: sha512-+2aJ/g90dhLiOLFSD1PbElXX3SoMdpO7HFPAZB+xot3CWlAZD1tReUFy7xe0L5GAR16ZmrxpIDM9v9gn5xRy/w==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tauri-apps/cli-win32-ia32-msvc@2.7.1': - resolution: {integrity: sha512-1oeibfyWQPVcijOrTg709qhbXArjX3x1MPjrmA5anlygwrbByxLBcLXvotcOeULFcnH2FYUMMLLant8kgvwE5A==} + '@tauri-apps/cli-win32-ia32-msvc@2.8.4': + resolution: {integrity: sha512-yj7WDxkL1t9Uzr2gufQ1Hl7hrHuFKTNEOyascbc109EoiAqCp0tgZ2IykQqOZmZOHU884UAWI1pVMqBhS/BfhA==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@tauri-apps/cli-win32-x64-msvc@2.7.1': - resolution: {integrity: sha512-D7Q9kDObutuirCNLxYQ7KAg2Xxg99AjcdYz/KuMw5HvyEPbkC9Q7JL0vOrQOrHEHxIQ2lYzFOZvKKoC2yyqXcg==} + '@tauri-apps/cli-win32-x64-msvc@2.8.4': + resolution: {integrity: sha512-XuvGB4ehBdd7QhMZ9qbj/8icGEatDuBNxyYHbLKsTYh90ggUlPa/AtaqcC1Fo69lGkTmq9BOKrs1aWSi7xDonA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tauri-apps/cli@2.7.1': - resolution: {integrity: sha512-RcGWR4jOUEl92w3uvI0h61Llkfj9lwGD1iwvDRD2isMrDhOzjeeeVn9aGzeW1jubQ/kAbMYfydcA4BA0Cy733Q==} + '@tauri-apps/cli@2.8.4': + resolution: {integrity: sha512-ejUZBzuQRcjFV+v/gdj/DcbyX/6T4unZQjMSBZwLzP/CymEjKcc2+Fc8xTORThebHDUvqoXMdsCZt8r+hyN15g==} engines: {node: '>= 10'} hasBin: true @@ -970,63 +984,63 @@ packages: '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - '@typescript-eslint/eslint-plugin@8.39.0': - resolution: {integrity: sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==} + '@typescript-eslint/eslint-plugin@8.44.1': + resolution: {integrity: sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.39.0 + '@typescript-eslint/parser': ^8.44.1 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.39.0': - resolution: {integrity: sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==} + '@typescript-eslint/parser@8.44.1': + resolution: {integrity: sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.39.0': - resolution: {integrity: sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==} + '@typescript-eslint/project-service@8.44.1': + resolution: {integrity: sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.39.0': - resolution: {integrity: sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==} + '@typescript-eslint/scope-manager@8.44.1': + resolution: {integrity: sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.39.0': - resolution: {integrity: sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==} + '@typescript-eslint/tsconfig-utils@8.44.1': + resolution: {integrity: sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.39.0': - resolution: {integrity: sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==} + '@typescript-eslint/type-utils@8.44.1': + resolution: {integrity: sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.39.0': - resolution: {integrity: sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==} + '@typescript-eslint/types@8.44.1': + resolution: {integrity: sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.39.0': - resolution: {integrity: sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==} + '@typescript-eslint/typescript-estree@8.44.1': + resolution: {integrity: sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.39.0': - resolution: {integrity: sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==} + '@typescript-eslint/utils@8.44.1': + resolution: {integrity: sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.39.0': - resolution: {integrity: sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==} + '@typescript-eslint/visitor-keys@8.44.1': + resolution: {integrity: sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@unocss/astro@66.3.3': @@ -1406,8 +1420,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.32.0: - resolution: {integrity: sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==} + eslint@9.36.0: + resolution: {integrity: sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1482,8 +1496,9 @@ packages: fault@1.0.4: resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} - fdir@6.4.6: - resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -1859,8 +1874,8 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} pino-abstract-transport@1.2.0: @@ -1967,8 +1982,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.46.2: - resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} + rollup@4.52.3: + resolution: {integrity: sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2082,8 +2097,8 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} to-regex-range@5.0.1: @@ -2122,8 +2137,8 @@ packages: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} - typescript-eslint@8.39.0: - resolution: {integrity: sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==} + typescript-eslint@8.44.1: + resolution: {integrity: sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2171,8 +2186,8 @@ packages: vfile@4.2.1: resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} - vite@7.0.4: - resolution: {integrity: sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==} + vite@7.1.7: + resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -2531,9 +2546,9 @@ snapshots: '@esbuild/win32-x64@0.25.6': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.32.0(jiti@2.4.2))': + '@eslint-community/eslint-utils@4.8.0(eslint@9.36.0(jiti@2.4.2))': dependencies: - eslint: 9.32.0(jiti@2.4.2) + eslint: 9.36.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -2546,9 +2561,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.0': {} + '@eslint/config-helpers@0.3.1': {} - '@eslint/core@0.15.1': + '@eslint/core@0.15.2': dependencies: '@types/json-schema': 7.0.15 @@ -2566,13 +2581,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.32.0': {} + '@eslint/js@9.36.0': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.3.4': + '@eslint/plugin-kit@0.3.5': dependencies: - '@eslint/core': 0.15.1 + '@eslint/core': 0.15.2 levn: 0.4.1 '@humanfs/core@0.19.1': {} @@ -2651,175 +2666,181 @@ snapshots: dependencies: quansync: 0.2.10 - '@rollup/plugin-node-resolve@16.0.1(rollup@4.46.2)': + '@rollup/plugin-node-resolve@16.0.1(rollup@4.52.3)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.46.2) + '@rollup/pluginutils': 5.1.4(rollup@4.52.3) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.10 optionalDependencies: - rollup: 4.46.2 + rollup: 4.52.3 - '@rollup/plugin-terser@0.4.4(rollup@4.46.2)': + '@rollup/plugin-terser@0.4.4(rollup@4.52.3)': dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 terser: 5.39.0 optionalDependencies: - rollup: 4.46.2 + rollup: 4.52.3 - '@rollup/plugin-typescript@12.1.4(rollup@4.46.2)(tslib@2.8.1)(typescript@5.9.2)': + '@rollup/plugin-typescript@12.1.4(rollup@4.52.3)(tslib@2.8.1)(typescript@5.9.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.46.2) + '@rollup/pluginutils': 5.1.4(rollup@4.52.3) resolve: 1.22.10 typescript: 5.9.2 optionalDependencies: - rollup: 4.46.2 + rollup: 4.52.3 tslib: 2.8.1 - '@rollup/pluginutils@5.1.4(rollup@4.46.2)': + '@rollup/pluginutils@5.1.4(rollup@4.52.3)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 - picomatch: 4.0.2 + picomatch: 4.0.3 optionalDependencies: - rollup: 4.46.2 + rollup: 4.52.3 - '@rollup/rollup-android-arm-eabi@4.46.2': + '@rollup/rollup-android-arm-eabi@4.52.3': optional: true - '@rollup/rollup-android-arm64@4.46.2': + '@rollup/rollup-android-arm64@4.52.3': optional: true - '@rollup/rollup-darwin-arm64@4.46.2': + '@rollup/rollup-darwin-arm64@4.52.3': optional: true - '@rollup/rollup-darwin-x64@4.46.2': + '@rollup/rollup-darwin-x64@4.52.3': optional: true - '@rollup/rollup-freebsd-arm64@4.46.2': + '@rollup/rollup-freebsd-arm64@4.52.3': optional: true - '@rollup/rollup-freebsd-x64@4.46.2': + '@rollup/rollup-freebsd-x64@4.52.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + '@rollup/rollup-linux-arm-gnueabihf@4.52.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.46.2': + '@rollup/rollup-linux-arm-musleabihf@4.52.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.46.2': + '@rollup/rollup-linux-arm64-gnu@4.52.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.46.2': + '@rollup/rollup-linux-arm64-musl@4.52.3': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + '@rollup/rollup-linux-loong64-gnu@4.52.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.46.2': + '@rollup/rollup-linux-ppc64-gnu@4.52.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.46.2': + '@rollup/rollup-linux-riscv64-gnu@4.52.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.46.2': + '@rollup/rollup-linux-riscv64-musl@4.52.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.46.2': + '@rollup/rollup-linux-s390x-gnu@4.52.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.46.2': + '@rollup/rollup-linux-x64-gnu@4.52.3': optional: true - '@rollup/rollup-linux-x64-musl@4.46.2': + '@rollup/rollup-linux-x64-musl@4.52.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.46.2': + '@rollup/rollup-openharmony-arm64@4.52.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.46.2': + '@rollup/rollup-win32-arm64-msvc@4.52.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.46.2': + '@rollup/rollup-win32-ia32-msvc@4.52.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.52.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.52.3': optional: true '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': dependencies: acorn: 8.15.0 - '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.0.0(svelte@5.28.2)(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)))(svelte@5.28.2)(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.0.0(svelte@5.28.2)(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)))(svelte@5.28.2)(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.0.0(svelte@5.28.2)(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)) + '@sveltejs/vite-plugin-svelte': 6.0.0(svelte@5.28.2)(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)) debug: 4.4.1(supports-color@8.1.1) svelte: 5.28.2 - vite: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + vite: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.0.0(svelte@5.28.2)(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))': + '@sveltejs/vite-plugin-svelte@6.0.0(svelte@5.28.2)(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.0.0(svelte@5.28.2)(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)))(svelte@5.28.2)(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.0.0(svelte@5.28.2)(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)))(svelte@5.28.2)(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)) debug: 4.4.1(supports-color@8.1.1) deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: 5.28.2 - vite: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) - vitefu: 1.1.1(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)) + vite: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + vitefu: 1.1.1(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)) transitivePeerDependencies: - supports-color - '@tauri-apps/api@2.7.0': {} + '@tauri-apps/api@2.8.0': {} - '@tauri-apps/cli-darwin-arm64@2.7.1': + '@tauri-apps/cli-darwin-arm64@2.8.4': optional: true - '@tauri-apps/cli-darwin-x64@2.7.1': + '@tauri-apps/cli-darwin-x64@2.8.4': optional: true - '@tauri-apps/cli-linux-arm-gnueabihf@2.7.1': + '@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': optional: true - '@tauri-apps/cli-linux-arm64-gnu@2.7.1': + '@tauri-apps/cli-linux-arm64-gnu@2.8.4': optional: true - '@tauri-apps/cli-linux-arm64-musl@2.7.1': + '@tauri-apps/cli-linux-arm64-musl@2.8.4': optional: true - '@tauri-apps/cli-linux-riscv64-gnu@2.7.1': + '@tauri-apps/cli-linux-riscv64-gnu@2.8.4': optional: true - '@tauri-apps/cli-linux-x64-gnu@2.7.1': + '@tauri-apps/cli-linux-x64-gnu@2.8.4': optional: true - '@tauri-apps/cli-linux-x64-musl@2.7.1': + '@tauri-apps/cli-linux-x64-musl@2.8.4': optional: true - '@tauri-apps/cli-win32-arm64-msvc@2.7.1': + '@tauri-apps/cli-win32-arm64-msvc@2.8.4': optional: true - '@tauri-apps/cli-win32-ia32-msvc@2.7.1': + '@tauri-apps/cli-win32-ia32-msvc@2.8.4': optional: true - '@tauri-apps/cli-win32-x64-msvc@2.7.1': + '@tauri-apps/cli-win32-x64-msvc@2.8.4': optional: true - '@tauri-apps/cli@2.7.1': + '@tauri-apps/cli@2.8.4': optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 2.7.1 - '@tauri-apps/cli-darwin-x64': 2.7.1 - '@tauri-apps/cli-linux-arm-gnueabihf': 2.7.1 - '@tauri-apps/cli-linux-arm64-gnu': 2.7.1 - '@tauri-apps/cli-linux-arm64-musl': 2.7.1 - '@tauri-apps/cli-linux-riscv64-gnu': 2.7.1 - '@tauri-apps/cli-linux-x64-gnu': 2.7.1 - '@tauri-apps/cli-linux-x64-musl': 2.7.1 - '@tauri-apps/cli-win32-arm64-msvc': 2.7.1 - '@tauri-apps/cli-win32-ia32-msvc': 2.7.1 - '@tauri-apps/cli-win32-x64-msvc': 2.7.1 + '@tauri-apps/cli-darwin-arm64': 2.8.4 + '@tauri-apps/cli-darwin-x64': 2.8.4 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.8.4 + '@tauri-apps/cli-linux-arm64-gnu': 2.8.4 + '@tauri-apps/cli-linux-arm64-musl': 2.8.4 + '@tauri-apps/cli-linux-riscv64-gnu': 2.8.4 + '@tauri-apps/cli-linux-x64-gnu': 2.8.4 + '@tauri-apps/cli-linux-x64-musl': 2.8.4 + '@tauri-apps/cli-win32-arm64-msvc': 2.8.4 + '@tauri-apps/cli-win32-ia32-msvc': 2.8.4 + '@tauri-apps/cli-win32-x64-msvc': 2.8.4 '@types/estree@1.0.8': {} @@ -2833,15 +2854,15 @@ snapshots: '@types/unist@2.0.11': {} - '@typescript-eslint/eslint-plugin@8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.39.0 - '@typescript-eslint/type-utils': 8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2) - '@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.39.0 - eslint: 9.32.0(jiti@2.4.2) + '@typescript-eslint/parser': 8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.44.1 + '@typescript-eslint/type-utils': 8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.44.1 + eslint: 9.36.0(jiti@2.4.2) graphemer: 1.4.0 ignore: 7.0.4 natural-compare: 1.4.0 @@ -2850,56 +2871,56 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)': + '@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2)': dependencies: - '@typescript-eslint/scope-manager': 8.39.0 - '@typescript-eslint/types': 8.39.0 - '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.39.0 + '@typescript-eslint/scope-manager': 8.44.1 + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.44.1 debug: 4.4.1(supports-color@8.1.1) - eslint: 9.32.0(jiti@2.4.2) + eslint: 9.36.0(jiti@2.4.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.39.0(typescript@5.9.2)': + '@typescript-eslint/project-service@8.44.1(typescript@5.9.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.9.2) - '@typescript-eslint/types': 8.39.0 + '@typescript-eslint/tsconfig-utils': 8.44.1(typescript@5.9.2) + '@typescript-eslint/types': 8.44.1 debug: 4.4.1(supports-color@8.1.1) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.39.0': + '@typescript-eslint/scope-manager@8.44.1': dependencies: - '@typescript-eslint/types': 8.39.0 - '@typescript-eslint/visitor-keys': 8.39.0 + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/visitor-keys': 8.44.1 - '@typescript-eslint/tsconfig-utils@8.39.0(typescript@5.9.2)': + '@typescript-eslint/tsconfig-utils@8.44.1(typescript@5.9.2)': dependencies: typescript: 5.9.2 - '@typescript-eslint/type-utils@8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 8.39.0 - '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2) + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2) debug: 4.4.1(supports-color@8.1.1) - eslint: 9.32.0(jiti@2.4.2) + eslint: 9.36.0(jiti@2.4.2) ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.39.0': {} + '@typescript-eslint/types@8.44.1': {} - '@typescript-eslint/typescript-estree@8.39.0(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@8.44.1(typescript@5.9.2)': dependencies: - '@typescript-eslint/project-service': 8.39.0(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.9.2) - '@typescript-eslint/types': 8.39.0 - '@typescript-eslint/visitor-keys': 8.39.0 + '@typescript-eslint/project-service': 8.44.1(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.44.1(typescript@5.9.2) + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/visitor-keys': 8.44.1 debug: 4.4.1(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 @@ -2910,29 +2931,29 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2)': + '@typescript-eslint/utils@8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.4.2)) - '@typescript-eslint/scope-manager': 8.39.0 - '@typescript-eslint/types': 8.39.0 - '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2) - eslint: 9.32.0(jiti@2.4.2) + '@eslint-community/eslint-utils': 4.8.0(eslint@9.36.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.44.1 + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + eslint: 9.36.0(jiti@2.4.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.39.0': + '@typescript-eslint/visitor-keys@8.44.1': dependencies: - '@typescript-eslint/types': 8.39.0 + '@typescript-eslint/types': 8.44.1 eslint-visitor-keys: 4.2.1 - '@unocss/astro@66.3.3(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2))': + '@unocss/astro@66.3.3(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2))': dependencies: '@unocss/core': 66.3.3 '@unocss/reset': 66.3.3 - '@unocss/vite': 66.3.3(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)) + '@unocss/vite': 66.3.3(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)) optionalDependencies: - vite: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + vite: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) transitivePeerDependencies: - vue @@ -2949,7 +2970,7 @@ snapshots: magic-string: 0.30.17 pathe: 2.0.3 perfect-debounce: 1.0.0 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 unplugin-utils: 0.2.4 '@unocss/config@66.3.3': @@ -2983,7 +3004,7 @@ snapshots: '@unocss/rule-utils': 66.3.3 css-tree: 3.1.0 postcss: 8.5.6 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 '@unocss/preset-attributify@66.3.3': dependencies: @@ -3065,7 +3086,7 @@ snapshots: dependencies: '@unocss/core': 66.3.3 - '@unocss/vite@66.3.3(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2))': + '@unocss/vite@66.3.3(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2))': dependencies: '@ampproject/remapping': 2.3.0 '@unocss/config': 66.3.3 @@ -3074,9 +3095,9 @@ snapshots: chokidar: 3.6.0 magic-string: 0.30.17 pathe: 2.0.3 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 unplugin-utils: 0.2.4 - vite: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + vite: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) transitivePeerDependencies: - vue @@ -3385,9 +3406,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@9.32.0(jiti@2.4.2)): + eslint-config-prettier@10.1.8(eslint@9.36.0(jiti@2.4.2)): dependencies: - eslint: 9.32.0(jiti@2.4.2) + eslint: 9.36.0(jiti@2.4.2) eslint-plugin-security@3.0.1: dependencies: @@ -3402,16 +3423,16 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.32.0(jiti@2.4.2): + eslint@9.36.0(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.4.2)) + '@eslint-community/eslint-utils': 4.8.0(eslint@9.36.0(jiti@2.4.2)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.0 - '@eslint/core': 0.15.1 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.32.0 - '@eslint/plugin-kit': 0.3.4 + '@eslint/js': 9.36.0 + '@eslint/plugin-kit': 0.3.5 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.2 @@ -3502,9 +3523,9 @@ snapshots: dependencies: format: 0.2.2 - fdir@6.4.6(picomatch@4.0.2): + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: - picomatch: 4.0.2 + picomatch: 4.0.3 file-entry-cache@8.0.0: dependencies: @@ -3865,7 +3886,7 @@ snapshots: picomatch@2.3.1: {} - picomatch@4.0.2: {} + picomatch@4.0.3: {} pino-abstract-transport@1.2.0: dependencies: @@ -3978,30 +3999,32 @@ snapshots: reusify@1.1.0: {} - rollup@4.46.2: + rollup@4.52.3: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.46.2 - '@rollup/rollup-android-arm64': 4.46.2 - '@rollup/rollup-darwin-arm64': 4.46.2 - '@rollup/rollup-darwin-x64': 4.46.2 - '@rollup/rollup-freebsd-arm64': 4.46.2 - '@rollup/rollup-freebsd-x64': 4.46.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.46.2 - '@rollup/rollup-linux-arm-musleabihf': 4.46.2 - '@rollup/rollup-linux-arm64-gnu': 4.46.2 - '@rollup/rollup-linux-arm64-musl': 4.46.2 - '@rollup/rollup-linux-loongarch64-gnu': 4.46.2 - '@rollup/rollup-linux-ppc64-gnu': 4.46.2 - '@rollup/rollup-linux-riscv64-gnu': 4.46.2 - '@rollup/rollup-linux-riscv64-musl': 4.46.2 - '@rollup/rollup-linux-s390x-gnu': 4.46.2 - '@rollup/rollup-linux-x64-gnu': 4.46.2 - '@rollup/rollup-linux-x64-musl': 4.46.2 - '@rollup/rollup-win32-arm64-msvc': 4.46.2 - '@rollup/rollup-win32-ia32-msvc': 4.46.2 - '@rollup/rollup-win32-x64-msvc': 4.46.2 + '@rollup/rollup-android-arm-eabi': 4.52.3 + '@rollup/rollup-android-arm64': 4.52.3 + '@rollup/rollup-darwin-arm64': 4.52.3 + '@rollup/rollup-darwin-x64': 4.52.3 + '@rollup/rollup-freebsd-arm64': 4.52.3 + '@rollup/rollup-freebsd-x64': 4.52.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.3 + '@rollup/rollup-linux-arm-musleabihf': 4.52.3 + '@rollup/rollup-linux-arm64-gnu': 4.52.3 + '@rollup/rollup-linux-arm64-musl': 4.52.3 + '@rollup/rollup-linux-loong64-gnu': 4.52.3 + '@rollup/rollup-linux-ppc64-gnu': 4.52.3 + '@rollup/rollup-linux-riscv64-gnu': 4.52.3 + '@rollup/rollup-linux-riscv64-musl': 4.52.3 + '@rollup/rollup-linux-s390x-gnu': 4.52.3 + '@rollup/rollup-linux-x64-gnu': 4.52.3 + '@rollup/rollup-linux-x64-musl': 4.52.3 + '@rollup/rollup-openharmony-arm64': 4.52.3 + '@rollup/rollup-win32-arm64-msvc': 4.52.3 + '@rollup/rollup-win32-ia32-msvc': 4.52.3 + '@rollup/rollup-win32-x64-gnu': 4.52.3 + '@rollup/rollup-win32-x64-msvc': 4.52.3 fsevents: 2.3.3 run-parallel@1.2.0: @@ -4117,10 +4140,10 @@ snapshots: tinyexec@1.0.1: {} - tinyglobby@0.2.14: + tinyglobby@0.2.15: dependencies: - fdir: 6.4.6(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 to-regex-range@5.0.1: dependencies: @@ -4152,13 +4175,13 @@ snapshots: type-fest@0.7.1: {} - typescript-eslint@8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2): + typescript-eslint@8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2) - '@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2) - '@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.4.2))(typescript@5.9.2) - eslint: 9.32.0(jiti@2.4.2) + '@typescript-eslint/eslint-plugin': 8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2))(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2) + '@typescript-eslint/parser': 8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.1(eslint@9.36.0(jiti@2.4.2))(typescript@5.9.2) + eslint: 9.36.0(jiti@2.4.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color @@ -4188,9 +4211,9 @@ snapshots: dependencies: '@types/unist': 2.0.11 - unocss@66.3.3(postcss@8.5.6)(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)): + unocss@66.3.3(postcss@8.5.6)(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)): dependencies: - '@unocss/astro': 66.3.3(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)) + '@unocss/astro': 66.3.3(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)) '@unocss/cli': 66.3.3 '@unocss/core': 66.3.3 '@unocss/postcss': 66.3.3(postcss@8.5.6) @@ -4208,9 +4231,9 @@ snapshots: '@unocss/transformer-compile-class': 66.3.3 '@unocss/transformer-directives': 66.3.3 '@unocss/transformer-variant-group': 66.3.3 - '@unocss/vite': 66.3.3(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)) + '@unocss/vite': 66.3.3(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2))(vue@3.5.13(typescript@5.9.2)) optionalDependencies: - vite: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + vite: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) transitivePeerDependencies: - postcss - supports-color @@ -4219,7 +4242,7 @@ snapshots: unplugin-utils@0.2.4: dependencies: pathe: 2.0.3 - picomatch: 4.0.2 + picomatch: 4.0.3 uri-js@4.4.1: dependencies: @@ -4237,23 +4260,23 @@ snapshots: unist-util-stringify-position: 2.0.3 vfile-message: 2.0.4 - vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2): + vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2): dependencies: esbuild: 0.25.6 - fdir: 6.4.6(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.46.2 - tinyglobby: 0.2.14 + rollup: 4.52.3 + tinyglobby: 0.2.15 optionalDependencies: fsevents: 2.3.3 jiti: 2.4.2 terser: 5.39.0 tsx: 4.19.2 - vitefu@1.1.1(vite@7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)): + vitefu@1.1.1(vite@7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2)): optionalDependencies: - vite: 7.0.4(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) + vite: 7.1.7(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.2) vue-flow-layout@0.1.1(vue@3.5.13(typescript@5.9.2)): dependencies: diff --git a/renovate.json b/renovate.json index f0c3d6ea8..4a76a6604 100644 --- a/renovate.json +++ b/renovate.json @@ -16,14 +16,23 @@ "rangeStrategy": "replace", "packageRules": [ { + "matchPackageNames": ["*"], "semanticCommitType": "chore", - "matchPackageNames": ["*"] + "minimumReleaseAge": "3 days" }, { "description": "Disable node/pnpm version updates", "matchPackageNames": ["node", "pnpm"], "matchDepTypes": ["engines", "packageManager"], "enabled": false + }, + { + "description": "Group windows-rs / webview2-com crates", + "groupName": "windows-rs and webview2 crates", + "matchSourceUrls": [ + "https://github.com/microsoft/windows-rs", + "https://github.com/wravery/webview2-rs" + ] } ], "postUpdateOptions": ["pnpmDedupe"]