Compare commits

..

158 Commits

Author SHA1 Message Date
github-actions[bot] 6e2e7e48c3 publish new versions (#3070)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-08 15:34:17 +01:00
FabianLars 9a2c98f450 temp: remove updater changefiles 2026-01-08 15:17:30 +01:00
FabianLars 4a2ecb6287 chore(deps): update rkyv, closes #3196 2026-01-06 11:05:04 +01:00
Tunglies 31415effdf docs(shell): update example to include Encoding usage in Command::spawn (#3183)
* docs(shell): update example to include Encoding usage in `Command::spawn`

* docs(shell): update patch level to minor for Encoding usage in `Command::spawn` documentation

* Update .changes/doc-to-Encoding-usage-in-shell.md

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

* docs(shell): update examples to demonstrate manual Encoding usage in command output

* Update plugins/shell/src/process/mod.rs

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

* docs(shell): update example to use 'windows-1252' encoding in command output

* Update plugins/shell/src/process/mod.rs

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

* docs(shell): update example command in documentation to use 'some-program' with 'some-arg'

* Bump shell-js in change file

* Fix indent
2025-12-29 10:58:55 +08:00
renovate[bot] 04b33ea0b0 chore(deps): update dependency typescript-eslint to v8.50.1 (#3181)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-27 22:30:26 +08:00
renovate[bot] 54e21f142b chore(deps): update dependency rollup to v4.54.0 (#3179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 22:50:36 +08:00
renovate[bot] d528c88b4f chore(deps): update dependency rollup to v4.53.5 (#3172)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 19:16:32 +08:00
renovate[bot] 69146fa852 chore(deps): update dependency rollup to v4.53.4 (#3167)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 11:20:50 +08:00
renovate[bot] 9f68f2d827 chore(deps): update dependency typescript-eslint to v8.50.0 (#3170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 10:58:03 +08:00
Fabian-Lars 3d0d2e041b fix(opener): ignore inAppBrowser on desktop (#3163) 2025-12-16 07:07:15 -03:00
renovate[bot] 1e3d7ef16e chore(deps): update eslint monorepo to v9.39.2 (#3164)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-16 12:36:13 +08:00
renovate[bot] ce6835d50f chore(deps): update dependency @tauri-apps/cli to v2.9.6 (#3162)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-12 22:13:33 +08:00
renovate[bot] 1d9f47c882 chore(deps): update dependency typescript-eslint to v8.49.0 (#3160)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-12 10:39:11 +08:00
Fabian-Lars e221a04ef4 docs(stronghold): add scrypt opt-level recommendation, closes #2048 2025-12-09 11:50:09 +01:00
Tony eebfd2ed3e fix(store): error instead of panic on invalid rid (#3157) 2025-12-09 18:02:23 +08:00
Elias* 521cd8b372 fix(fs): off by one error in readTextFileLines (#3155)
* fix: off by one error in readTextFileLines (#3154)

Signed-off-by: EliasStar <31409841+EliasStar@users.noreply.github.com>

* Format and regenerate `api-iife.js`

---------

Signed-off-by: EliasStar <31409841+EliasStar@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-12-09 11:48:18 +08:00
renovate[bot] e4a40f4423 chore(deps): update tauri monorepo (v2) (#3142)
* chore(deps): update tauri monorepo

* Build

* Bump rust tauri

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-12-07 00:03:50 +08:00
renovate[bot] fa8b11f19d chore(deps): update dependency prettier to v3.7.4 (#3150)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-06 13:10:40 +08:00
renovate[bot] baefd761e0 chore(deps): update dependency typescript-eslint to v8.48.1 (#3146)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-05 18:35:15 +08:00
Tunglies 2804803949 fix: deep-link clippy warnings (#3143)
* Update build.rs

* fix: address clippy warnings in deep-link module

* fix: add deep-link-js patch to address clippy warnings
2025-12-04 20:04:56 +08:00
renovate[bot] d9d51eb8ea chore(deps): update dependency prettier to v3.7.3 (#3140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 09:58:49 +08:00
renovate[bot] 1483c63101 chore(deps): update dependency prettier to v3.7.2 (#3137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-02 10:31:13 +08:00
renovate[bot] 26ed78989a chore(deps): update dependency prettier to v3.7.1 (#3134)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 20:01:16 +08:00
renovate[bot] f1564e58b6 chore(deps): update dependency prettier to v3.7.0 (#3132)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 19:53:50 +08:00
Fabian-Lars f2d4abb9e2 fix(dialog): rfd feature flags (#3129) 2025-11-28 15:25:58 +01:00
renovate[bot] 3dcf7522b1 chore(deps): update dependency typescript-eslint to v8.48.0 (#3126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-28 14:45:59 +08:00
renovate[bot] 02068550e8 chore(deps): update rust crate rfd to 0.16 (#3128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-28 13:47:22 +08:00
Fabian-Lars 66a75ece27 fix(websocket): install crypto provider if needed (#3124) 2025-11-25 20:02:47 +01:00
Christian Leingang fa601e8754 feat(updater): allow accepting invalid TLS certs/hostnames via config (#3057)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-11-24 14:56:59 +01:00
Niccolò Gnani d8bfe61f20 fix(barcode-scanner): remove unnecessary permission checks on Android (fix #2312) (#3121) 2025-11-24 14:27:46 +01:00
Amr Bashir e8915f17e4 fix(http): fix aborting a streaming response (#2562) 2025-11-24 13:55:11 +01:00
Lucas Fernandes Nogueira 6de61f854b chore(log): remove iOS simulator workarounds (#3113) 2025-11-24 13:35:19 +01:00
renovate[bot] ad7a8d3e42 chore(deps): update dependency rollup to v4.53.3 (#3119)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 16:25:46 +08:00
FabianLars d5509a9ac8 chore: add docs issue template and enable blank issues 2025-11-23 13:55:52 +01:00
FabianLars 06727b19c1 chore: fix issue templates 2025-11-23 13:51:01 +01:00
Fabian-Lars 2f9fa79747 chore: add issue templates (#3122) 2025-11-23 13:42:24 +01:00
renovate[bot] 1db4b0719d chore(deps): update dependency typescript-eslint to v8.47.0 (#3117)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-21 10:47:11 +08:00
Andrew de Waal dff6fa986a feat(dialog) - Support picker mode for open dialog (#3030) (#3034)
Co-authored-by: Andrew de Waal <andrewldewaal@gmail.com>
2025-11-20 22:28:13 +01:00
Nazar Antoniuk a4aa53ab90 chore(shell): clarify that the code field in ExitStatus is intentionally left private (#3116)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-11-20 15:33:19 +01:00
liuzhch1 ae278ddf60 fix(log): custom writer to rotate log file when exceeds max_file_size (fix #707) (#3110) 2025-11-20 10:29:17 +01:00
Jacob Bolda e644f38673 remove token for oidc publishing (#3077) 2025-11-19 21:52:57 -06:00
Lucas Fernandes Nogueira 8bfa445023 chore(localhost): disable caching (#3112)
* chore(localhost): disable caching

mostly seeing this cache being a problem on CEF - the response is cached so app updates are not reflected properly

* fmt
2025-11-19 17:03:32 -03:00
Lucas Nogueira 14fb36e347 chore: remove test logs 2025-11-19 17:00:14 -03:00
Fabian-Lars 5767b848fa chore: add PR ref in updater changefile
Added reference to pull request for clarity on changes.
2025-11-18 12:11:12 +01:00
renovate[bot] 277a45f56c chore(deps): update dependency rollup to v4.53.2 (#3103)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-18 00:07:40 +08:00
renovate[bot] 8dbe7e3233 chore(deps): update dependency typescript-eslint to v8.46.4 (v2) (#3105)
* chore(deps): update dependency typescript-eslint to v8.46.4

* Fix audit (bump js-yaml)

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-11-17 23:44:02 +08:00
Blaine c23fa03f07 feat(dialog): add xdg-portal as an optional feature for rfd (#3098) 2025-11-17 14:56:02 +01:00
Kingsword 631d0e256a fix(barcode-scanner): fix the cameraView is not removed after scanning in iOS (#2440)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-11-16 22:31:30 +01:00
Ben Clarke b4348cee92 fix(nfc): Resolve boolean in isAvailable to agree with TypeScript API (#3101)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-11-13 20:38:15 +01:00
renovate[bot] 3019063ae1 chore(deps): update dependency rollup to v4.53.1 (#3093)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-12 22:06:11 +08:00
renovate[bot] 944614f46a chore(deps): update dependency @tauri-apps/cli to v2.9.4 (#3100)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-12 21:15:09 +08:00
Tony a368cef912 docs(updater): installer args and on_before_exit (#3099)
* docs(updater): installer args and `on_before_exit`

* Update plugins/updater/src/updater.rs

Co-authored-by: Fabian-Lars <github@fabianlars.de>

* Explicitly state there are internal args

* s
2025-11-12 20:49:41 +08:00
Fabian-Lars 27790aa67c chore: fix upload test on macos ci (#3097) 2025-11-11 22:56:09 +01:00
Matthew Richardson ad910b1135 feat(upload): Add HTTP method selection for upload (#2991)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-11-10 21:31:57 +01:00
Fabian-Lars 6b854421a1 docs(localhost): improve readme example, closes #3095 2025-11-10 21:27:39 +01:00
Fabian-Lars 5438a5cd22 chore: apply clippy fixes (#3092) 2025-11-09 18:43:17 +01:00
Fabian-Lars 5cd7778723 docs(dialog): add/fix doc item links, closes #3089 (#3091) 2025-11-09 17:07:35 +01:00
renovate[bot] 1a03e9761f chore(deps): update dependency @tauri-apps/cli to v2.9.3 (#3088)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-08 13:34:05 +08:00
renovate[bot] 1d4bffadda chore(deps): update eslint monorepo to v9.39.1 (#3086)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-08 12:36:37 +08:00
renovate[bot] b8794272ae chore(deps): update dependency typescript-eslint to v8.46.3 (#3083)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-08 12:02:18 +08:00
Michelle Tilley 2a625adff3 feat(log): Allow a log formatter per target (#3065)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-11-04 15:52:46 +01:00
FabianLars 371cd8227c chore(deps): update pino to 9.14 to remove fast-redact 2025-11-04 10:00:51 +01:00
renovate[bot] 70ef6f8d3e chore(deps): update eslint monorepo to v9.39.0 (#3079)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 09:54:50 +01:00
renovate[bot] 5f0ac1436f chore(deps): update dependency @tauri-apps/cli to v2.9.2 (#3078)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-02 09:50:14 +08:00
Fabian-Lars ea172bfa3c docs(global-shortcut): import Emitter instead of Manager in example 2025-10-29 11:46:16 +01:00
FabianLars 6aead24047 Revert "chore: temp delete updater changefile"
This reverts commit b5550a3b0d.
2025-10-27 11:43:23 +01:00
github-actions[bot] e7a68fa637 publish new versions (#3068)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-27 11:31:06 +01:00
FabianLars b5550a3b0d chore: temp delete updater changefile 2025-10-27 11:13:33 +01:00
FabianLars 93426f8512 fix: fix docsrs builds 2025-10-27 11:12:56 +01:00
FabianLars 4ee61e055e Revert "chore: temp delete updater changefile"
This reverts commit 6314b004ab.
2025-10-26 22:13:19 +01:00
github-actions[bot] 06124af8d6 publish new versions (#2972)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-10-26 22:02:33 +01:00
renovate[bot] 060219e597 chore(deps): update dependency @rollup/plugin-typescript to v12.3.0 (#3067)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 20:25:22 +01:00
renovate[bot] c7e9766ff5 chore(deps): update tauri monorepo (v2) (#3058)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-10-26 17:22:06 +01:00
renovate[bot] d4a8ce962b chore(deps): update rust crate tokio-tungstenite to 0.28 (#3016)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 14:07:12 +01:00
renovate[bot] cdc7eec415 chore(deps): update dependency @rollup/plugin-typescript to v12.2.0 (#3066)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 12:36:06 +01:00
Fabian-Lars 6314b004ab chore: temp delete updater changefile 2025-10-26 12:34:59 +01:00
renovate[bot] fb4c8ae54c chore(deps): update dependency typescript-eslint to v8.46.2 (#3060)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-24 16:46:50 +08:00
renovate[bot] fccc1cfb7e chore(deps): update eslint monorepo to v9.38.0 (#3044)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 17:17:32 +08:00
renovate[bot] 3702308e6f chore(deps): update dependency rollup to v4.52.5 (#3043)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 16:32:14 +08:00
renovate[bot] c9c8b39b56 chore(deps): update dependency typescript-eslint to v8.46.1 (#3025)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 16:31:31 +08:00
renovate[bot] 3c396feedf chore(deps): update dependency typescript to v5.9.3 (#3028)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 15:42:04 +08:00
renovate[bot] b514d482fa chore(deps): update dependency @rollup/plugin-node-resolve to v16.0.3 (#3036)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 15:41:37 +08:00
renovate[bot] 60e5714ceb chore(deps): update dependency vite to v7.1.11 [security] (#3040)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 15:28:07 +08:00
Daniel Mader 654bf4891a feat(barcode-scanner): update androidx.camera from 1.1.0 to 1.5.1 to support 16 KB memory page sizes (#3038) 2025-10-20 17:11:17 +02:00
Fabian-Lars 6c9b61fb65 chore: Update compileSdk to 36 (#3039) 2025-10-20 16:58:29 +02:00
Amr Bashir 6b5b1053ba enhance(fs): improve error message when resolving path in debug builds (#1939)
* enhance(fs): improve error message when resolving path in debug builds

closes #11338

* fix compilation

* deduplicate read_file code

* fix compile

* fix compile again

* fmt

* comma

* add change file

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-10-09 08:58:08 -03:00
Tony 67a7bf80f8 fix(opener): doesn't open same origin links (#3018) 2025-10-04 21:16:08 +08:00
Kevin Schoonover e25a9339f4 fix(deep-link): update the Exec= in handler if the executable path changes (#3019)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-10-01 12:01:09 +02:00
Tony 92f98a93b4 docs(fs): fs:scope usage (#2969)
* docs(fs): `fs:scope` usage

* Use APPDATA for the example instead

* `/**/*`
2025-10-01 17:24:04 +08:00
renovate[bot] c0f82e5a5e chore(deps): update dependency rollup to v4.52.3 (#3021)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-01 11:54:47 +08:00
renovate[bot] 5204145c6a chore(deps): update dependency typescript-eslint to v8.44.1 (#3011)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-27 22:45:12 +08:00
renovate[bot] b4ee33dd01 chore(deps): update dependency rollup to v4.52.2 (#3013)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-27 21:55:53 +08:00
Fabian-Lars e933acb004 fix(geolocation): use timeout for min,max,interval on android (#3010) 2025-09-25 15:41:03 +02:00
FabianLars e4a9154f4a ci(renovate): Group windows crates into one PR 2025-09-25 13:31:39 +02:00
renovate[bot] d44ac9afed chore(deps): update eslint monorepo to v9.36.0 (#3004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-25 12:59:04 +02:00
renovate[bot] 1b197c2d4d chore(deps): update dependency rollup to v4.52.0 (#3003)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-25 12:57:57 +02:00
renovate[bot] 650ff5ff23 chore(deps): update dependency typescript-eslint to v8.44.0 (#2988)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-25 12:21:23 +02:00
Fabian-Lars 2930aa39a4 chore: set minimumReleaseAge to 3 days in renovate and pnpm (#2999) 2025-09-25 11:39:52 +02:00
frieddeeu 3e92ce260b fix(geolocation): remove logging of priority (#3002) 2025-09-25 11:38:49 +02:00
renovate[bot] 12da195fce chore(deps): update dependency rollup to v4.50.1 (#2986)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 17:47:39 +08:00
renovate[bot] 30dd109d9f chore(deps): update eslint monorepo to v9.35.0 (#2975)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 09:03:34 +08:00
renovate[bot] 5e5bc30ad0 chore(deps): update dependency typescript-eslint to v8.42.0 (v2) (#2973)
* chore(deps): update dependency typescript-eslint to v8.42.0

* Switch to `defineConfig`
2025-09-03 10:36:36 +08:00
renovate[bot] f6dca71343 chore(deps): update dependency @tauri-apps/cli to v2.8.4 (#2971)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 10:16:08 +08:00
FabianLars 021e573248 Revert "ci: delete .changes/updater-new-bundle-support.md"
This reverts commit 51b430be98.
2025-09-02 16:49:58 +02:00
Fabian-Lars 51b430be98 ci: delete .changes/updater-new-bundle-support.md 2025-09-02 15:43:40 +02:00
github-actions[bot] fd439b143e Publish New Versions (v2) (#2964)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-09-02 14:04:12 +02:00
Sean Wang 2522b71f6b fix(deep-link): revert the breaking change introduced by #2928 (#2970) 2025-09-02 13:50:10 +02:00
renovate[bot] 9021a73247 chore(deps): update dependency rollup to v4.50.0 (#2966)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-31 20:53:18 +08:00
Lucas Fernandes Nogueira 625bb1c096 feat(log): re-export the log crate (#2965)
* feat(log): re-export the log crate

* code review

* Move emit_trace

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2025-08-30 23:44:03 -03:00
renovate[bot] 6215afe023 chore(deps): update dependency rollup to v4.49.0 (#2962)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 10:40:39 +08:00
Fabian-Lars 8cf8eeab02 feat(updater): inject bundle_type into endpoint url (#2960)
* feat(updater): inject bundle_type into endpoint url

* Revert schemas

* replace with unknown if none
2025-08-27 15:52:17 -03:00
Amr Bashir 509eba8d44 feat: support message dialogs with 3 buttons (#2641)
* feat: support message dialogs with 3 buttons

* change file

* From<String>

* untagged & YesNoCancel

* revert package.json

* Update plugins/dialog/src/desktop.rs

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

* no optional

* Update desktop.rs

* Update plugins/dialog/src/models.rs

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>

* change to an enum

* convert back into union

* regen

* update @since

* map buttons for linux

* enhance type

* Add examples

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-08-27 09:40:47 -03:00
kandrelczyk 9ac5fe84e7 feat(updater): support bundle-specific targets (#2624)
* fallback targets

* linux test

* linux ready

* RPM installation

* small error fix

* fix windows build

* windows tests

* add aider files to .gitignore

* get bundle type out of patched variable

* windows tests

* patch windows binary

* format

* fix bundler

* remove local tauri dependency

* remove print

* rever Cargo.lock

* move __TAURI_BUNDLE_TYPE to tauri::utils

* get_current_bundle_type

* update tauri

* fix macos integration test

* fix fallback logic

Signed-off-by: Krzysztof Andrelczyk <cristof@curiana.net>

* amend! fallback targets

fallback targets

* reformat

* fix tests

* reformat

* bump tari versio

* fix fallback logic

* restore Cargo.lock

* Bump tauri and add notes

* Rename some staffs

* move target logic

* Refactor the target fallback to a function

* Format and clippy

* Keep target in `Update` since it's public

* Keep updater/tests/app-updater/src/main.rs lf

* Revert changes in tests/app-updater/src/main.rs

* Clean up

* changefile

* Bump updater-js as well

* update pub fn target docs

* update pub fn target docs

* Update plugins/updater/src/error.rs

Co-authored-by: Fabian-Lars <github@fabianlars.de>

* Update plugins/updater/src/updater.rs

Co-authored-by: Fabian-Lars <github@fabianlars.de>

* Update plugins/updater/src/updater.rs

Co-authored-by: Fabian-Lars <github@fabianlars.de>

* suggestios

* add comment

* restore error

* Revert "Bump tauri and add notes"

This reverts commit 0a495ccc6a.

* Revert "bump tari versio"

This reverts commit 5b4c1c164b.

---------

Signed-off-by: Krzysztof Andrelczyk <cristof@curiana.net>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Co-authored-by: Tony <legendmastertony@gmail.com>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-08-26 09:33:58 -03:00
renovate[bot] c247410319 chore(deps): update dependency typescript-eslint to v8.41.0 (#2956)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-26 09:37:44 +08:00
Fabian-Lars de45034082 docs(store): tauri-docs compatibility 2025-08-25 20:13:16 +02:00
renovate[bot] 50c6b7c644 chore(deps): update dependency rollup to v4.48.1 (#2952)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 18:55:07 +02:00
renovate[bot] b79d02d896 chore(deps): update dependency @tauri-apps/cli to v2.8.3 (#2955)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 18:54:23 +02:00
github-actions[bot] b75f9f5cd3 publish new versions (#2954)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-25 18:15:22 +02:00
Lucas Fernandes Nogueira d865ed4768 fix(shell): run sidecar with dots in filename, closes #2310 (#2950)
* fix(shell): run sidecar with dots in filename, closes #2310

* fix import

* remove dead code

* code review suggestions

* clippy

* clippy
2025-08-25 10:44:47 -03:00
renovate[bot] 1107c46425 chore(deps): update dependency @tauri-apps/cli to v2.8.2 (#2932)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 09:25:07 +08:00
renovate[bot] 23a3705857 chore(deps): update dependency rollup to v4.48.0 (#2948)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-23 17:43:32 +08:00
renovate[bot] 6f65e68340 chore(deps): update eslint monorepo to v9.34.0 (#2946)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-23 09:05:42 +08:00
github-actions[bot] 1a0b791650 publish new versions (#2942)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-21 10:07:03 -03:00
Keerthi 2d03e2eac2 Add sound support for desktop notifications in Tauri v2 (#2678)
* Add sound support for desktop notifications in Tauri v2

* ci

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-21 10:05:27 -03:00
Fabian-Lars 21d721a0c2 fix(deep-link): Add command name to linux errors (#2928) 2025-08-21 08:56:09 -03:00
Christian Pavilonis 8abb31ee59 fix(notifications): crash on ios when scheduling a notification (#2905)
* Converts NotificationSchedule enum to a dictionary to fix crash while
scheduleing notifications.

* change file

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-21 08:39:22 -03:00
renovate[bot] 0354046817 chore(deps): update dependency rollup to v4.47.1 (#2940)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 18:18:33 +08:00
Fabian-Lars 04a0954857 chore(example): Improve dialog/fs mobile examples (#2410) 2025-08-21 12:04:46 +02:00
renovate[bot] 9e4e859bea chore(deps): update dependency rollup to v4.47.0 (#2938)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 14:47:35 +08:00
github-actions[bot] 2371804172 publish new versions (#2888)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-20 11:42:19 +02:00
FabianLars 90f9b9310a chore: fix cli version in deep-link package.json 2025-08-20 11:36:09 +02:00
renovate[bot] 1c58f3372c chore(deps): update dependency rollup to v4.46.4 (v2) (#2935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-08-20 11:33:19 +02:00
SoSweetHam 75617a6a92 fix(mobile): deeplinks (#2870)
* feat: android deeplinks

* feat: explicit app link declarations

* feat: add ios code

* fix: add ios deeplink adaptation

* feat: ios working

(some swift plugin api improvements needed)

* fix: revert ios to prior logic

* fix(cleanup): regen android files with old names

* fix: web link criteria

* fix: conditional auto verify intent filter for android app links

* fix: default to true

* fix: typo

* fix: pnpm version

* cleanup

* fix: web link regression

* trim androidmanifest update

* fix deep link validation broken due to appLink=true default

* implement update_info_plist

from https://github.com/tauri-apps/tauri/pull/13888

* fix: remove old patch crates

* fix: use latest patch tauri

* lint

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-08-19 13:09:56 -03:00
renovate[bot] 5a963a0496 chore(deps): update dependency @tauri-apps/cli to v2.8.1 (#2930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 21:38:06 +08:00
renovate[bot] 76f4e7bb84 chore(deps): update eslint monorepo to v9.33.0 (#2903)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 21:37:24 +08:00
renovate[bot] 670ac1d7c1 chore(deps): update dependency typescript-eslint to v8.40.0 (#2923)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 21:20:48 +08:00
renovate[bot] ed0deef2cf chore(deps): update dependency @tauri-apps/api to v2.8.0 (#2929)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 21:20:29 +08:00
Lucas Fernandes Nogueira 50cebdb6d5 chore(deps): update to tauri 2.8.0 (#2925)
* chore(deps): update to tauri 2.8.0

* fmt

* uipdate build

* tauri 2.8.1 and bump toml in plugin-fs

* tauri 2.8.1

* 2.8.2

* clippy [skip ci]

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2025-08-19 10:09:44 -03:00
renovate[bot] dd2ea9cfa5 chore(deps): update dependency rollup to v4.46.3 (#2920)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 19:02:01 +08:00
Sam Hinshaw 515182a179 fix: Specify Generic for readFile ArrayBuffer (fix #2914) (#2915) 2025-08-17 17:19:44 +08:00
Fabian-Lars d3d290ab8a fix(os): unlock serialize-to-javascript dependency version (#2912) 2025-08-12 19:13:46 +08:00
renovate[bot] b51c827b2c chore(deps): update dependency typescript-eslint to v8.39.1 (#2910)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 09:55:16 +08:00
Tony a4b71a1992 refactor(opener): rename to FailedToConvertPathToItemIdList (#2906) 2025-08-10 18:52:54 +08:00
Tony 4eb36b0ff5 fix(fs): writeFile with ReadableStream throws (#2907) 2025-08-10 18:44:33 +08:00
Petr b8056f484c feat(opener): reveal multiple items in dir (#2897)
* feature: reveal multiple items in dir

* feature: reveal multiple items in dir

* feature: reveal multiple items in dir

* feature: reveal multiple items in dir

* feature: reveal multiple items in dir

* feature: reveal multiple items in dir

* feature: reveal multiple items in dir

* Support multiple roots on Windows

* feature: reveal multiple items in dir

* feature: reveal multiple items in dir

* feature: reveal multiple items in dir

* feature: reveal multiple items in dir

---------

Co-authored-by: Tony <legendmastertony@gmail.com>
2025-08-09 09:06:56 +08:00
Tony 5ac8fbb1fa feat(store): load override defaults (#2857)
* feat(store): load override defaults

* Update docs

* Update example

* Allow setting defaults from js

* Tweak resolve

* Merge remote-tracking branch 'upstream/v2' into store-load-override-defaults

* Merge branch 'v2' of https://github.com/tauri-apps/plugins-workspace into store-load-override-defaults

* Merge branch 'v2' into store-load-override-defaults

* Rename to ignore defaults

* Merge remote-tracking branch 'upstream/v2' into store-load-override-defaults
2025-08-05 18:45:09 +08:00
renovate[bot] e0323ec752 chore(deps): update dependency typescript-eslint to v8.39.0 (#2894)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-05 10:08:29 +08:00
Fabian-Lars cc98e6a892 docs: Remove mirror install instructions (#2893) 2025-08-04 12:30:18 +02:00
renovate[bot] af22ae0a97 chore(deps): update rust crate notify-debouncer-full to 0.6 (v2) (#2889)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-08-03 22:10:31 +02:00
Fabian-Lars 2f24c7a70c chore(deps): Update rand to 0.9 (#2367) 2025-08-03 21:22:23 +02:00
Ray fe23a5e013 fix(nfc): Ensure that Session is dropped when an error causes it to become invalid (#2885)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-08-01 20:49:49 +02:00
renovate[bot] ff6d23ede1 chore(deps): update dependency typescript to v5.9.2 (#2886)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-01 11:04:56 +08:00
renovate[bot] 449dd117a4 chore(deps): update dependency rollup to v4.46.2 (#2882)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 13:52:44 +08:00
github-actions[bot] 9b43f48856 publish new versions (#2880)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-28 17:14:41 +02:00
Fabian-Lars af08c66faa fix(dialog): remove use of ACTION_PICK (#2871) 2025-07-28 16:43:35 +02:00
renovate[bot] 7974acae22 chore(deps): update dependency rollup to v4.46.1 (#2878)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 15:19:17 +08:00
renovate[bot] a985359e69 chore(deps): update dependency rollup to v4.46.0 (#2876)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-27 20:39:56 +08:00
renovate[bot] 97bebcf6e8 chore(deps): update eslint monorepo to v9.32.0 (#2873)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-26 19:50:08 +08:00
258 changed files with 4766 additions and 2550 deletions
+63
View File
@@ -0,0 +1,63 @@
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
name: 🐞 Bug Report
description: Report a bug
labels: ['type: bug']
body:
- type: markdown
attributes:
value: |
## First of all
1. Please search for [existing issues](https://github.com/tauri-apps/plugins-workspace/issues?q=is%3Aissue) about this problem first.
2. Make sure `rustc` and all relevant Tauri packages are up to date.
3. Make sure it's an issue with a tauri plugin and not something else you are using.
4. Remember to follow our community guidelines and be friendly.
- type: textarea
id: description
attributes:
label: Describe the bug
description: A clear description of what the bug is. Include screenshots if applicable.
placeholder: Bug description
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction
description: A link to a reproduction repo or steps to reproduce the behaviour.
placeholder: |
Please provide a minimal reproduction or steps to reproduce, see this guide https://stackoverflow.com/help/minimal-reproducible-example
Why reproduction is required? see this article https://antfu.me/posts/why-reproductions-are-required
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: A clear description of what you expected to happen.
- type: textarea
id: info
attributes:
label: Full `tauri info` output
description: 'Output of `npm run tauri info` or `cargo tauri info`. Issues without this will be closed!'
render: text
validations:
required: true
- type: textarea
id: logs
attributes:
label: Stack trace
description: A stack trace or ANY error messages you may see.
render: text
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context about the problem here. For example a link to a Discord discussion.
+12
View File
@@ -0,0 +1,12 @@
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
#blank_issues_enabled: false
contact_links:
- name: 💡 Request a new plugin
url: https://github.com/orgs/tauri-apps/discussions/new?category=plugin-requests
about: Propose a new Plugin to the community or the Tauri org.
- name: 💬 Discord Chat
url: https://discord.com/invite/tauri
about: Ask questions and talk to other Tauri users and the maintainers
+5
View File
@@ -0,0 +1,5 @@
---
name: 📚 Docs Report
about: Create a report to help us improve the docs
labels: 'type: documentation'
---
@@ -0,0 +1,46 @@
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
name: 💡 Feature Request
description: Request a feature or enhancement for an existing plugin
labels: ['type: feature request']
body:
- type: markdown
attributes:
value: |
## First of all
- Please search for [existing issues](https://github.com/tauri-apps/plugins-workspace/issues?q=is%3Aissue) for this request first.
- Only requests for plugins that exist in this repo are allowed.
- You can request new plugins [here](https://github.com/orgs/tauri-apps/discussions/new?category=plugin-requests)
- type: textarea
id: problem
attributes:
label: Describe the problem
description: A clear description of the problem this feature would solve
placeholder: "I'm always frustrated when..."
validations:
required: true
- type: textarea
id: solution
attributes:
label: "Describe the solution you'd like"
description: A clear description of what change you would like
placeholder: 'I would like to...'
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: "Any alternative solutions you've considered"
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context about the problem here.
@@ -11,7 +11,7 @@ on:
- v2
permissions:
# required for npm provenance
# required for oidc token
id-token: write
# required to create the GitHub Release
contents: write
@@ -62,7 +62,6 @@ jobs:
id: covector
env:
CARGO_TARGET_DIR: /mnt/target
NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
command: 'version-or-publish'
+1
View File
@@ -154,4 +154,5 @@ jobs:
run: cargo clippy --package ${{ matrix.package }} --all-targets -- -D warnings
- name: clippy ${{ matrix.package }} --all-features
if: matrix.package != 'tauri-plugin-dialog'
run: cargo clippy --package ${{ matrix.package }} --all-targets --all-features -- -D warnings
+2 -2
View File
@@ -246,9 +246,9 @@ jobs:
run: cargo +stable install cross --git https://github.com/cross-rs/cross
- name: test ${{ matrix.package }}
if: matrix.package != 'tauri-plugin-http'
if: ${{ matrix.package != 'tauri-plugin-http' && matrix.package != 'tauri-plugin-dialog' }}
run: ${{ matrix.platform.runner }} ${{ matrix.platform.command }} --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets --all-features
- name: test ${{ matrix.package }}
if: matrix.package == 'tauri-plugin-http'
if: ${{ matrix.package == 'tauri-plugin-http' || matrix.package == 'tauri-plugin-dialog' }}
run: ${{ matrix.platform.runner }} ${{ matrix.platform.command }} --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets
+1 -1
View File
@@ -58,4 +58,4 @@ pids
.idea
debug.log
TODO.md
.aider*
.aider.*
Generated
+256 -154
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -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.9.3", default-features = false }
tauri-build = "2.4"
tauri-plugin = "2.4"
tauri-utils = "2.7"
serde_json = "1"
thiserror = "2"
url = "2"
+2 -1
View File
@@ -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',
+87
View File
@@ -1,5 +1,92 @@
# Changelog
## \[2.0.35]
### Dependencies
- Upgraded to `dialog-js@2.5.0`
- Upgraded to `log-js@2.8.0`
- Upgraded to `shell-js@2.3.4`
- Upgraded to `barcode-scanner-js@2.4.3`
- Upgraded to `fs-js@2.4.5`
- Upgraded to `http-js@2.5.5`
- Upgraded to `nfc-js@2.3.4`
- Upgraded to `opener-js@2.5.3`
- Upgraded to `store-js@2.4.2`
## \[2.0.34]
### Dependencies
- Upgraded to `barcode-scanner-js@2.4.2`
- Upgraded to `biometric-js@2.3.2`
- Upgraded to `cli-js@2.4.1`
- Upgraded to `clipboard-manager-js@2.3.2`
- Upgraded to `dialog-js@2.4.2`
- Upgraded to `fs-js@2.4.4`
- Upgraded to `global-shortcut-js@2.3.1`
- Upgraded to `http-js@2.5.4`
- Upgraded to `log-js@2.7.1`
- Upgraded to `nfc-js@2.3.3`
- Upgraded to `notification-js@2.3.3`
- Upgraded to `opener-js@2.5.2`
- Upgraded to `os-js@2.3.2`
- Upgraded to `process-js@2.3.1`
- Upgraded to `shell-js@2.3.3`
- Upgraded to `store-js@2.4.1`
## \[2.0.33]
### Dependencies
- Upgraded to `barcode-scanner-js@2.4.1`
- Upgraded to `biometric-js@2.3.1`
- Upgraded to `clipboard-manager-js@2.3.1`
- Upgraded to `dialog-js@2.4.1`
- Upgraded to `fs-js@2.4.3`
- Upgraded to `nfc-js@2.3.2`
- Upgraded to `notification-js@2.3.2`
- Upgraded to `opener-js@2.5.1`
- Upgraded to `shell-js@2.3.2`
- Upgraded to `http-js@2.5.3`
## \[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
- Upgraded to `dialog-js@2.3.2`
## \[2.0.27]
### Dependencies
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=0"
/>
<title>Svelte + Vite App</title>
</head>
+19 -19
View File
@@ -1,7 +1,7 @@
{
"name": "api",
"private": true,
"version": "2.0.27",
"version": "2.0.35",
"type": "module",
"scripts": {
"dev": "vite --clearScreen false",
@@ -10,24 +10,24 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.7.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.1",
"@tauri-apps/plugin-fs": "^2.4.1",
"@tauri-apps/api": "2.9.1",
"@tauri-apps/plugin-barcode-scanner": "^2.4.3",
"@tauri-apps/plugin-biometric": "^2.3.2",
"@tauri-apps/plugin-cli": "^2.4.1",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
"@tauri-apps/plugin-dialog": "^2.5.0",
"@tauri-apps/plugin-fs": "^2.4.5",
"@tauri-apps/plugin-geolocation": "^2.2.0",
"@tauri-apps/plugin-global-shortcut": "^2.3.0",
"@tauri-apps/plugin-global-shortcut": "^2.3.1",
"@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-process": "^2.3.0",
"@tauri-apps/plugin-shell": "^2.3.0",
"@tauri-apps/plugin-store": "^2.3.0",
"@tauri-apps/plugin-http": "^2.5.5",
"@tauri-apps/plugin-nfc": "^2.3.4",
"@tauri-apps/plugin-notification": "^2.3.3",
"@tauri-apps/plugin-opener": "^2.5.3",
"@tauri-apps/plugin-os": "^2.3.2",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-shell": "^2.3.4",
"@tauri-apps/plugin-store": "^2.4.2",
"@tauri-apps/plugin-updater": "^2.9.0",
"@tauri-apps/plugin-upload": "^2.3.0",
"@zerodevx/svelte-json-view": "1.0.11"
@@ -36,10 +36,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.9.6",
"@unocss/extractor-svelte": "^66.3.3",
"svelte": "^5.20.4",
"unocss": "^66.3.3",
"vite": "^7.0.4"
"vite": "^7.0.7"
}
}
+91
View File
@@ -1,5 +1,96 @@
# Changelog
## \[2.0.39]
### Dependencies
- Upgraded to `dialog@2.5.0`
- Upgraded to `log@2.8.0`
- Upgraded to `shell@2.3.4`
- Upgraded to `barcode-scanner@2.4.3`
- Upgraded to `fs@2.4.5`
- Upgraded to `http@2.5.5`
- Upgraded to `nfc@2.3.4`
- Upgraded to `opener@2.5.3`
- Upgraded to `store@2.4.2`
## \[2.0.38]
### Dependencies
- Upgraded to `barcode-scanner@2.4.2`
- Upgraded to `biometric@2.3.2`
- Upgraded to `cli@2.4.1`
- Upgraded to `clipboard-manager@2.3.2`
- Upgraded to `dialog@2.4.2`
- Upgraded to `fs@2.4.4`
- Upgraded to `geolocation@2.3.2`
- Upgraded to `global-shortcut@2.3.1`
- Upgraded to `haptics@2.3.2`
- Upgraded to `http@2.5.4`
- Upgraded to `log@2.7.1`
- Upgraded to `nfc@2.3.3`
- Upgraded to `notification@2.3.3`
- Upgraded to `opener@2.5.2`
- Upgraded to `os@2.3.2`
- Upgraded to `process@2.3.1`
- Upgraded to `shell@2.3.3`
- Upgraded to `store@2.4.1`
## \[2.0.37]
### Dependencies
- Upgraded to `barcode-scanner@2.4.1`
- Upgraded to `biometric@2.3.1`
- Upgraded to `clipboard-manager@2.3.1`
- Upgraded to `dialog@2.4.1`
- Upgraded to `fs@2.4.3`
- Upgraded to `geolocation@2.3.1`
- Upgraded to `haptics@2.3.1`
- Upgraded to `nfc@2.3.2`
- Upgraded to `notification@2.3.2`
- Upgraded to `opener@2.5.1`
- Upgraded to `shell@2.3.2`
- Upgraded to `http@2.5.3`
## \[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
- Upgraded to `dialog@2.3.2`
## \[2.0.31]
### Dependencies
+19 -19
View File
@@ -1,7 +1,7 @@
[package]
name = "api"
publish = false
version = "2.0.31"
version = "2.0.39"
description = "An example Tauri Application showcasing the api"
edition = "2021"
rust-version = { workspace = true }
@@ -20,24 +20,24 @@ 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.8.0" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.4.5", features = [
"watch",
] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.0" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.3.1" }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.2" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.5.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.5" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.3", features = [
"windows7-compat",
] }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.0" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.3.0" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.4.0" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.0" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.3.0" }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.2" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.3.1" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.5.3" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.4" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.4.2" }
tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" }
[dependencies.tauri]
@@ -55,17 +55,17 @@ features = [
]
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.4.0" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.3.0" }
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.4.1" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.3.1" }
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.9.0" }
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" }
[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-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" }
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.4.3" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.4" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.2" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.2" }
tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.3.2" }
[features]
prod = ["tauri/custom-protocol"]
@@ -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/**"
}
@@ -14,13 +14,13 @@ val tauriProperties = Properties().apply {
}
android {
compileSdk = 34
compileSdk = 36
namespace = "com.tauri.api"
defaultConfig {
manifestPlaceholders["usesCleartextTraffic"] = "false"
applicationId = "com.tauri.api"
minSdk = 24
targetSdk = 34
targetSdk = 36
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
}
@@ -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")
}
}
@@ -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")
}
+35 -11
View File
@@ -8,6 +8,7 @@
let filter = null;
let multiple = false;
let directory = false;
let pickerMode = "";
function arrayBufferToBase64(buffer, callback) {
var blob = new Blob([buffer], {
@@ -44,6 +45,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",
@@ -58,6 +66,7 @@
: [],
multiple,
directory,
pickerMode: pickerMode === "" ? undefined : pickerMode,
})
.then(function (res) {
if (Array.isArray(res)) {
@@ -87,7 +96,7 @@
onMessage(res);
}
})
.catch(onMessage(res));
.catch(onMessage);
}
})
.catch(onMessage);
@@ -105,7 +114,7 @@
},
]
: [],
})
})
.then(onMessage)
.catch(onMessage);
}
@@ -135,13 +144,28 @@
<input type="checkbox" id="dialog-directory" bind:checked={directory} />
<label for="dialog-directory">Directory</label>
</div>
<div>
<label for="dialog-picker-mode">Picker Mode:</label>
<select id="dialog-picker-mode" bind:value={pickerMode}>
<option value="">None</option>
<option value="media">Media</option>
<option value="image">Image</option>
<option value="video">Video</option>
<option value="document">Document</option>
</select>
</div>
<br />
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
<button class="btn" id="save-dialog" on:click={saveDialog}
>Open save dialog</button
>
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom}
>Prompt (custom)</button
>
<button class="btn" id="message-dialog" on:click={msg}>Message</button>
<div class="flex flex-wrap flex-col md:flex-row gap-2 children:flex-shrink-0">
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
<button class="btn" id="save-dialog" on:click={saveDialog}
>Open save dialog</button
>
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom}
>Prompt (custom)</button
>
<button class="btn" id="message-dialog" on:click={msg}>Message</button>
<button class="btn" id="message-dialog" on:click={msgCustom}>Message (custom)</button>
</div>
+114 -93
View File
@@ -1,194 +1,214 @@
<script>
import * as fs from "@tauri-apps/plugin-fs";
import { convertFileSrc } from "@tauri-apps/api/core";
import { arrayBufferToBase64 } from "../lib/utils";
import { onDestroy } from "svelte";
import * as fs from '@tauri-apps/plugin-fs'
import * as os from '@tauri-apps/plugin-os'
import { convertFileSrc } from '@tauri-apps/api/core'
import { arrayBufferToBase64 } from '../lib/utils'
import { onDestroy, onMount } from 'svelte'
export let onMessage;
export let insecureRenderHtml;
const { onMessage, insecureRenderHtml } = $props()
let path = "";
let img;
let path = $state('')
let img
/** @type {fs.FileHandle} */
let file;
let renameTo;
let watchPath = "";
let watchDebounceDelay = "0";
let watchRecursive = false;
let unwatchFn;
let unwatchPath = "";
let file = $state()
let renameTo = $state()
let watchPath = $state('')
let watchDebounceDelay = $state(0)
let watchRecursive = $state(false)
/** @type {fs.BaseDirectory | undefined} */
let baseDir = $state()
let unwatchFn
let unwatchPath = ''
let isMobile = $state(false)
function getDir() {
const dirSelect = document.getElementById("dir");
return dirSelect.value ? parseInt(dir.value) : null;
}
onMount(() => {
let platform = os.platform()
isMobile = platform === 'android' || platform === 'ios'
})
const DirOptions = Object.keys(fs.BaseDirectory)
.filter((key) => isNaN(parseInt(key)))
.map((dir) => [dir, fs.BaseDirectory[dir]]);
const dirOptions = Object.keys(fs.BaseDirectory).filter((key) =>
isNaN(parseInt(key))
)
function open() {
fs.open(path, {
baseDir: getDir(),
baseDir,
read: true,
write: true,
create: true,
create: true
})
.then((f) => {
file = f;
onMessage(`Opened ${path}`);
file = f
onMessage(`Opened ${path}`)
})
.catch(onMessage);
.catch(onMessage)
}
function mkdir() {
fs.mkdir(path, { baseDir: getDir() })
fs.mkdir(path, { baseDir, recursive: true })
.then(() => {
onMessage(`Created dir ${path}`);
onMessage(`Created dir ${path}`)
})
.catch(onMessage);
.catch(onMessage)
}
function remove() {
fs.remove(path, { baseDir: getDir() })
fs.remove(path, { baseDir })
.then(() => {
onMessage(`Removed ${path}`);
onMessage(`Removed ${path}`)
})
.catch(onMessage);
.catch(onMessage)
}
function rename() {
fs.rename(path, renameTo, {
oldPathBaseDir: getDir(),
newPathBaseDir: getDir(),
oldPathBaseDir,
newPathBaseDir
})
.then(() => {
onMessage(`Renamed ${path} to ${renameTo}`);
onMessage(`Renamed ${path} to ${renameTo}`)
})
.catch(onMessage);
.catch(onMessage)
}
function truncate() {
file
.truncate(0)
.then(() => {
onMessage(`Truncated file`);
onMessage(`Truncated file`)
})
.catch(onMessage);
.catch(onMessage)
}
function write() {
const encoder = new TextEncoder()
file
.write(encoder.encode('Hello from Tauri :)'))
.then(() => {
onMessage(`wrote to file`)
})
.catch(onMessage)
}
function stat() {
file
.stat()
.then((stat) => {
onMessage(`File stat ${JSON.stringify(stat)}`);
onMessage(`File stat ${JSON.stringify(stat)}`)
})
.catch(onMessage);
.catch(onMessage)
}
function read() {
const opts = {
baseDir: getDir(),
};
baseDir
}
fs.stat(path, opts)
.then((stat) => {
const isFile = stat.isFile;
const isFile = stat.isFile
const promise = isFile
? fs.readFile(path, opts)
: fs.readDir(path, opts);
: fs.readDir(path, opts)
promise
.then(function (response) {
if (isFile) {
if (path.includes(".png") || path.includes(".jpg")) {
if (path.includes('.png') || path.includes('.jpg')) {
arrayBufferToBase64(
new Uint8Array(response),
function (base64) {
const src = "data:image/png;base64," + base64;
insecureRenderHtml('<img src="' + src + '"></img>');
const src = 'data:image/png;base64,' + base64
insecureRenderHtml('<img src="' + src + '"></img>')
}
);
)
} else {
const value = String.fromCharCode.apply(null, response);
const value = String.fromCharCode.apply(null, response)
insecureRenderHtml(
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
);
)
setTimeout(() => {
const fileInput = document.getElementById("file-response");
fileInput.value = value;
const fileInput = document.getElementById('file-response')
fileInput.value = value
document
.getElementById("file-save")
.addEventListener("click", function () {
.getElementById('file-save')
.addEventListener('click', function () {
fs.writeTextFile(path, fileInput.value, {
baseDir: getDir(),
}).catch(onMessage);
});
});
baseDir
}).catch(onMessage)
})
})
}
} else {
onMessage(response);
onMessage(response)
}
})
.catch(onMessage);
.catch(onMessage)
})
.catch(onMessage);
.catch(onMessage)
}
function setSrc() {
img.src = convertFileSrc(path);
img.src = convertFileSrc(path)
}
function watch() {
unwatch();
unwatch()
if (watchPath) {
onMessage(`Watching ${watchPath} for changes`);
onMessage(`Watching ${watchPath} for changes`)
let options = {
recursive: watchRecursive,
delayMs: parseInt(watchDebounceDelay),
};
delayMs: watchDebounceDelay
}
if (options.delayMs === 0) {
fs.watchImmediate(watchPath, onMessage, options)
.then((fn) => {
unwatchFn = fn;
unwatchPath = watchPath;
unwatchFn = fn
unwatchPath = watchPath
})
.catch(onMessage);
.catch(onMessage)
} else {
fs.watch(watchPath, onMessage, options)
.then((fn) => {
unwatchFn = fn;
unwatchPath = watchPath;
unwatchFn = fn
unwatchPath = watchPath
})
.catch(onMessage);
.catch(onMessage)
}
}
}
function unwatch() {
if (unwatchFn) {
onMessage(`Stopped watching ${unwatchPath} for changes`);
unwatchFn();
onMessage(`Stopped watching ${unwatchPath} for changes`)
unwatchFn()
}
unwatchFn = undefined;
unwatchPath = undefined;
unwatchFn = undefined
unwatchPath = undefined
}
onDestroy(() => {
if (file) {
file.close();
file.close()
}
if (unwatchFn) {
unwatchFn();
unwatchFn()
}
})
</script>
<div class="flex flex-col">
{#if isMobile}
<div>
On mobile, paths outside of App* paths require the use of dialogs
regardless of Tauri's scope mechanism.
</div>
<br />
{/if}
<div class="flex gap-1">
<select class="input" id="dir">
<option value="">None</option>
{#each DirOptions as dir}
<option value={dir[1]}>{dir[0]}</option>
<select class="input" bind:value={baseDir}>
<option value={undefined} selected>None</option>
{#each dirOptions as dir}
<option value={fs.BaseDirectory[dir]}>{dir}</option>
{/each}
</select>
<input
@@ -199,20 +219,21 @@
</div>
<br />
<div>
<button class="btn" on:click={open}>Open</button>
<button class="btn" on:click={read}>Read</button>
<button class="btn" on:click={mkdir}>Mkdir</button>
<button class="btn" on:click={remove}>Remove</button>
<button class="btn" onclick={open}>Open</button>
<button class="btn" onclick={read}>Read</button>
<button class="btn" onclick={mkdir}>Mkdir</button>
<button class="btn" onclick={remove}>Remove</button>
<div class="flex flex-row">
<button class="btn" on:click={rename}>Rename</button>
<button class="btn" onclick={rename}>Rename</button>
<input class="input" bind:value={renameTo} placeholder="To" />
</div>
<button class="btn" type="button" on:click={setSrc}>Use as img src</button>
<button class="btn" type="button" onclick={setSrc}>Use as img src</button>
</div>
{#if file}
<div>
<button class="btn" on:click={truncate}>Truncate</button>
<button class="btn" on:click={stat}>Stat</button>
<button class="btn" onclick={write}>Write</button>
<button class="btn" onclick={truncate}>Truncate</button>
<button class="btn" onclick={stat}>Stat</button>
</div>
{/if}
@@ -241,8 +262,8 @@
</div>
<br />
<div>
<button class="btn" on:click={watch}>Watch</button>
<button class="btn" on:click={unwatch}>Unwatch</button>
<button class="btn" onclick={watch}>Watch</button>
<button class="btn" onclick={unwatch}>Unwatch</button>
</div>
</div>
+14 -4
View File
@@ -1,16 +1,21 @@
<script>
import { sendNotification } from '@tauri-apps/plugin-notification'
export let onMessage
let sound = ''
// send the notification directly
// the backend is responsible for checking the permission
function _sendNotification() {
new Notification('Notification title', {
body: 'This is the notification body'
sendNotification({
title: 'Notification title',
body: 'This is the notification body',
sound: sound || null
})
}
// alternatively, check the permission ourselves
function sendNotification() {
function triggerNotification() {
if (Notification.permission === 'default') {
Notification.requestPermission()
.then(function (response) {
@@ -29,6 +34,11 @@
}
</script>
<button class="btn" id="notification" on:click={sendNotification}>
<input
class="input grow"
placeholder="Notification sound..."
bind:value={sound}
/>
<button class="btn" id="notification" on:click={triggerNotification}>
Send test notification
</button>
+49 -32
View File
@@ -1,71 +1,85 @@
<script>
import { LazyStore } from "@tauri-apps/plugin-store";
import { onMount } from "svelte";
import { appDataDir, resolve } from '@tauri-apps/api/path'
import { LazyStore } from '@tauri-apps/plugin-store'
import { onMount } from 'svelte'
export let onMessage;
let { onMessage } = $props()
let key;
let value;
let key = $state()
let value = $state()
let store = new LazyStore("cache.json");
let cache = {};
const storeName = 'cache.json'
let store = new LazyStore(storeName)
let path = $state('')
let cache = $state({})
async function refreshEntries() {
try {
const values = await store.entries();
cache = {};
const values = await store.entries()
cache = {}
for (const [key, value] of values) {
cache[key] = value;
cache[key] = value
}
} catch (error) {
onMessage(error);
onMessage(error)
}
}
onMount(async () => {
await refreshEntries();
});
path = await resolve(await appDataDir(), storeName)
await refreshEntries()
})
async function write(key, value) {
try {
if (value) {
await store.set(key, value);
await store.set(key, value)
} else {
await store.delete(key);
await store.delete(key)
}
const v = await store.get(key);
const v = await store.get(key)
if (v === undefined) {
delete cache[key];
cache = cache;
delete cache[key]
cache = cache
} else {
cache[key] = v;
cache[key] = v
}
} catch (error) {
onMessage(error);
onMessage(error)
}
}
async function reset() {
try {
await store.reset();
await store.reset()
} catch (error) {
onMessage(error);
onMessage(error)
}
await refreshEntries();
await refreshEntries()
}
async function reload() {
try {
await store.reload({ overrideDefaults: true })
} catch (error) {
onMessage(error)
}
await refreshEntries()
}
async function close() {
try {
await store.close();
onMessage("Store is now closed, any new operations will error out");
await store.close()
onMessage('Store is now closed, any new operations will error out')
} catch (error) {
onMessage(error);
onMessage(error)
}
}
function reopen() {
store = new LazyStore("cache.json");
onMessage("We made a new `LazyStore` instance, operations will now work");
store = new LazyStore(storeName)
onMessage('We made a new `LazyStore` instance, operations will now work')
}
</script>
@@ -82,14 +96,17 @@
</div>
<div>
<button class="btn" on:click={() => write(key, value)}>Write</button>
<button class="btn" on:click={() => reset()}>Reset</button>
<button class="btn" on:click={() => close()}>Close</button>
<button class="btn" on:click={() => reopen()}>Re-open</button>
<button class="btn" onclick={() => write(key, value)}>Write</button>
<button class="btn" onclick={() => reset()}>Reset</button>
<button class="btn" onclick={() => reload()}>Reload</button>
<button class="btn" onclick={() => close()}>Close</button>
<button class="btn" onclick={() => reopen()}>Re-open</button>
</div>
<div>Store at <code>{path}</code> on disk</div>
</div>
<div>
<h2>Store Values</h2>
{#each Object.entries(cache) as [k, v]}
<div>{k} = {v}</div>
{/each}
+44 -3
View File
@@ -1,5 +1,5 @@
<script>
import { download, upload } from '@tauri-apps/plugin-upload'
import { download, upload, HttpMethod } from '@tauri-apps/plugin-upload'
import { open } from '@tauri-apps/plugin-dialog'
import { JsonView } from '@zerodevx/svelte-json-view'
import { appDataDir } from '@tauri-apps/api/path'
@@ -16,6 +16,22 @@
let uploadUrl = 'https://httpbin.org/post'
let uploadFilePath = ''
let uploadMethod = HttpMethod.Post
// Update URL when method changes
$: {
switch (uploadMethod) {
case HttpMethod.Post:
uploadUrl = 'https://httpbin.org/post'
break
case HttpMethod.Put:
uploadUrl = 'https://httpbin.org/put'
break
case HttpMethod.Patch:
uploadUrl = 'https://httpbin.org/patch'
break
}
}
let uploadProgress = null
let uploadResult = null
let isUploading = false
@@ -197,7 +213,8 @@
},
new Map([
['User-Agent', 'Tauri Upload Plugin Demo']
])
]),
uploadMethod
)
uploadResult = {
@@ -340,12 +357,36 @@
</div>
</div>
<div>
<label for="upload-method" class="block text-sm font-medium text-gray-700 mb-1">HTTP Method:</label>
<select
id="upload-method"
bind:value={uploadMethod}
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
disabled={isUploading}
>
<option value={HttpMethod.Post}>POST</option>
<option value={HttpMethod.Put}>PUT</option>
<option value={HttpMethod.Patch}>PATCH</option>
</select>
<p class="text-xs text-gray-500 mt-1">Choose the HTTP method for the upload request</p>
</div>
<div class="bg-blue-50 border border-blue-200 p-3 rounded-md">
<div class="text-sm text-blue-800">
<strong>Upload Configuration:</strong>
<div class="font-mono text-xs mt-1">
Method: {uploadMethod} | URL: {uploadUrl || 'Not set'}
</div>
</div>
</div>
<button
on:click={startUpload}
class="w-full px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 disabled:opacity-50 disabled:cursor-not-allowed"
disabled={isUploading || !uploadUrl || !uploadFilePath}
>
{isUploading ? 'Uploading...' : 'Upload File'}
{isUploading ? `Uploading (${uploadMethod})...` : `Upload File (${uploadMethod})`}
</button>
{#if uploadProgress}
+10 -9
View File
@@ -11,20 +11,21 @@
"example:api:dev": "pnpm run --filter \"api\" tauri dev"
},
"devDependencies": {
"@eslint/js": "9.31.0",
"@rollup/plugin-node-resolve": "16.0.1",
"@eslint/js": "9.39.2",
"@rollup/plugin-node-resolve": "16.0.3",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "12.1.4",
"@rollup/plugin-typescript": "12.3.0",
"covector": "^0.12.4",
"eslint": "9.31.0",
"eslint": "9.39.2",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-security": "3.0.1",
"prettier": "3.6.2",
"rollup": "4.45.1",
"prettier": "3.7.4",
"rollup": "4.54.0",
"tslib": "2.8.1",
"typescript": "5.8.3",
"typescript-eslint": "8.38.0"
"typescript": "5.9.3",
"typescript-eslint": "8.50.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"
}
}
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.5.1]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.5.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
+1 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-autostart"
version = "2.5.0"
version = "2.5.1"
description = "Automatically launch your application at startup."
authors = { workspace = true }
license = { workspace = true }
@@ -9,10 +9,6 @@ rust-version = { workspace = true }
repository = { workspace = true }
links = "tauri-plugin-autostart"
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.platforms.support]
windows = { level = "full", notes = "" }
linux = { level = "full", notes = "" }
-9
View File
@@ -33,21 +33,12 @@ tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspac
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh
pnpm add @tauri-apps/plugin-autostart
# or
npm add @tauri-apps/plugin-autostart
# or
yarn add @tauri-apps/plugin-autostart
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-autostart#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-autostart#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-autostart#v2
```
## Usage
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-autostart",
"version": "2.5.0",
"version": "2.5.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"
}
}
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## \[2.4.3]
- [`d8bfe61f`](https://github.com/tauri-apps/plugins-workspace/commit/d8bfe61f20f235314bad93a9c50d8b7f3eade734) ([#3121](https://github.com/tauri-apps/plugins-workspace/pull/3121) by [@NKIPSC](https://github.com/tauri-apps/plugins-workspace/../../NKIPSC)) Remove unnecessary checks on Android when requesting camera permission.
- [`631d0e25`](https://github.com/tauri-apps/plugins-workspace/commit/631d0e256a37946b6a9102ca35511abfbebb92c5) ([#2440](https://github.com/tauri-apps/plugins-workspace/pull/2440) by [@kingsword09](https://github.com/tauri-apps/plugins-workspace/../../kingsword09)) Fix the `cameraView` is not removed after scanning in iOS.
## \[2.4.2]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.4.1]
- [`654bf489`](https://github.com/tauri-apps/plugins-workspace/commit/654bf4891a35769f7e82971641d3ad99974b2dfe) ([#3038](https://github.com/tauri-apps/plugins-workspace/pull/3038) by [@daniel-mader](https://github.com/tauri-apps/plugins-workspace/../../daniel-mader)) Update `androidx.camera` from `1.1.0` to `1.5.1` to support 16 KB memory page sizes.
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
## \[2.4.0]
- [`aa9140e1`](https://github.com/tauri-apps/plugins-workspace/commit/aa9140e1ac239ab9f015f92b2ed52bbf0eda7c12) ([#2437](https://github.com/tauri-apps/plugins-workspace/pull/2437) by [@enkhjile](https://github.com/tauri-apps/plugins-workspace/../../enkhjile)) Added support for GS1 DataBar on iOS 15.4+
+1 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-barcode-scanner"
version = "2.4.0"
version = "2.4.3"
description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
edition = { workspace = true }
authors = { workspace = true }
@@ -10,8 +10,6 @@ repository = { workspace = true }
links = "tauri-plugin-barcode-scanner"
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-linux-android"]
[package.metadata.platforms.support]
-9
View File
@@ -33,21 +33,12 @@ tauri-plugin-barcode-scanner = { git = "https://github.com/tauri-apps/plugins-wo
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh
pnpm add @tauri-apps/plugin-barcode-scanner
# or
npm add @tauri-apps/plugin-barcode-scanner
# or
yarn add @tauri-apps/plugin-barcode-scanner
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
```
## Usage
@@ -5,10 +5,10 @@ plugins {
android {
namespace = "app.tauri.barcodescanner"
compileSdk = 34
compileSdk = 36
defaultConfig {
minSdk = 24
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
@@ -33,16 +33,14 @@ android {
}
dependencies {
val camerax_version = "1.5.1"
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0")
implementation("androidx.camera:camera-core:1.1.0")
implementation("androidx.camera:camera-view:1.1.0")
implementation("androidx.camera:camera-lifecycle:1.1.0")
implementation("androidx.camera:camera-camera2:1.1.0")
implementation("androidx.camera:camera-lifecycle:1.1.0")
implementation("androidx.camera:camera-view:1.1.0")
implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:${camerax_version}")
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
implementation("androidx.camera:camera-view:${camerax_version}")
implementation("com.google.android.gms:play-services-mlkit-barcode-scanning:18.1.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
@@ -54,7 +54,6 @@ import java.util.concurrent.ExecutionException
private const val PERMISSION_ALIAS_CAMERA = "camera"
private const val PERMISSION_NAME = Manifest.permission.CAMERA
private const val PREFS_PERMISSION_FIRST_TIME_ASKING = "PREFS_PERMISSION_FIRST_TIME_ASKING"
@InvokeArg
class ScanOptions {
@@ -354,17 +353,6 @@ class BarcodeScannerPlugin(private val activity: Activity) : Plugin(activity),
}
}
private fun markFirstPermissionRequest() {
val sharedPreference: SharedPreferences =
activity.getSharedPreferences(PREFS_PERMISSION_FIRST_TIME_ASKING, MODE_PRIVATE)
sharedPreference.edit().putBoolean(PERMISSION_NAME, false).apply()
}
private fun firstPermissionRequest(): Boolean {
return activity.getSharedPreferences(PREFS_PERMISSION_FIRST_TIME_ASKING, MODE_PRIVATE)
.getBoolean(PERMISSION_NAME, true)
}
@SuppressLint("ObsoleteSdkInt")
@PermissionCallback
fun cameraPermissionCallback(invoke: Invoke) {
@@ -380,9 +368,7 @@ class BarcodeScannerPlugin(private val activity: Activity) : Plugin(activity),
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!activity.shouldShowRequestPermissionRationale(PERMISSION_NAME)) {
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.DENIED)
}
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.DENIED)
} else {
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
}
@@ -401,20 +387,12 @@ class BarcodeScannerPlugin(private val activity: Activity) : Plugin(activity),
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (firstPermissionRequest() || activity.shouldShowRequestPermissionRationale(
PERMISSION_NAME
)
) {
markFirstPermissionRequest()
requestPermissionForAlias(
PERMISSION_ALIAS_CAMERA,
invoke,
"cameraPermissionCallback"
)
return
} else {
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.DENIED)
}
requestPermissionForAlias(
PERMISSION_ALIAS_CAMERA,
invoke,
"cameraPermissionCallback"
)
return
} else {
requestPermissionResponse.put(PERMISSION_ALIAS_CAMERA, PermissionState.GRANTED)
}
@@ -191,6 +191,7 @@ class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
if self.captureSession != nil {
self.captureSession!.stopRunning()
self.cameraView.removePreviewLayer()
self.cameraView.removeFromSuperview()
self.captureVideoPreviewLayer = nil
self.metaOutput = nil
self.captureSession = nil
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-barcode-scanner",
"version": "2.4.0",
"version": "2.4.3",
"description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
"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"
}
}
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.2]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.3.1]
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
## \[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
+1 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-biometric"
version = "2.3.0"
version = "2.3.2"
description = "Prompt the user for biometric authentication on Android and iOS."
edition = { workspace = true }
authors = { workspace = true }
@@ -9,8 +9,6 @@ repository = { workspace = true }
links = "tauri-plugin-biometric"
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-linux-android"]
[package.metadata.platforms.support]
-9
View File
@@ -33,8 +33,6 @@ tauri-plugin-biometric = { git = "https://github.com/tauri-apps/plugins-workspac
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
<!-- Add the branch for installations using git! -->
```sh
@@ -43,13 +41,6 @@ pnpm add @tauri-apps/plugin-biometric
npm add @tauri-apps/plugin-biometric
# or
yarn add @tauri-apps/plugin-biometric
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-biometric#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-biometric#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-biometric#v2
```
## Usage
+2 -2
View File
@@ -5,10 +5,10 @@ plugins {
android {
namespace = "app.tauri.biometric"
compileSdk = 34
compileSdk = 36
defaultConfig {
minSdk = 24
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-biometric",
"version": "2.3.0",
"version": "2.3.2",
"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"
}
}
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.4.1]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[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
+1 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-cli"
version = "2.4.0"
version = "2.4.1"
description = "Parse arguments from your Tauri application's command line interface."
edition = { workspace = true }
authors = { workspace = true }
@@ -9,10 +9,6 @@ rust-version = { workspace = true }
repository = { workspace = true }
links = "tauri-plugin-cli"
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.platforms.support]
windows = { level = "full", notes = "" }
linux = { level = "full", notes = "" }
-9
View File
@@ -34,21 +34,12 @@ tauri-plugin-cli = { git = "https://github.com/tauri-apps/plugins-workspace", br
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh
pnpm add @tauri-apps/plugin-cli
# or
npm add @tauri-apps/plugin-cli
# or
yarn add @tauri-apps/plugin-cli
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-cli#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-cli#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-cli#v2
```
## Usage
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-cli",
"version": "2.4.0",
"version": "2.4.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"
}
}
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.2]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.3.1]
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
## \[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
+2 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-clipboard-manager"
version = "2.3.0"
version = "2.3.2"
description = "Read and write to the system clipboard."
edition = { workspace = true }
authors = { workspace = true }
@@ -10,8 +10,7 @@ repository = { workspace = true }
links = "tauri-plugin-clipboard-manager"
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-unknown-linux-gnu", "x86_64-linux-android"]
[package.metadata.platforms.support]
-9
View File
@@ -33,21 +33,12 @@ tauri-plugin-clipboard-manager = { git = "https://github.com/tauri-apps/plugins-
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh
pnpm add @tauri-apps/plugin-clipboard-manager
# or
npm add @tauri-apps/plugin-clipboard-manager
# or
yarn add @tauri-apps/plugin-clipboard-manager
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-clipboard-manager#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-clipboard-manager#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-clipboard-manager#v2
```
## Usage
@@ -5,10 +5,10 @@ plugins {
android {
namespace = "app.tauri.clipboard"
compileSdk = 34
compileSdk = 36
defaultConfig {
minSdk = 24
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-clipboard-manager",
"version": "2.3.0",
"version": "2.3.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"
}
}
+21
View File
@@ -1,5 +1,26 @@
# Changelog
## \[2.4.6]
- [`28048039`](https://github.com/tauri-apps/plugins-workspace/commit/28048039496e84b46847c008416d341f1349e30e) ([#3143](https://github.com/tauri-apps/plugins-workspace/pull/3143) by [@Tunglies](https://github.com/tauri-apps/plugins-workspace/../../Tunglies)) Fix clippy warnings. No user facing changes.
## \[2.4.5]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.4.4]
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
- [`e25a9339`](https://github.com/tauri-apps/plugins-workspace/commit/e25a9339f46268f70dc9afd0c5ab3decbf79b330) ([#3019](https://github.com/tauri-apps/plugins-workspace/pull/3019) by [@kevinschoonover](https://github.com/tauri-apps/plugins-workspace/../../kevinschoonover)) Fix Exec= field in desktop handler if executable path changes
## \[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
+4 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-deep-link"
version = "2.4.1"
version = "2.4.6"
description = "Set your Tauri application as the default handler for an URL"
authors = { workspace = true }
license = { workspace = true }
@@ -10,8 +10,6 @@ repository = { workspace = true }
links = "tauri-plugin-deep-link"
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-linux-android"]
[package.metadata.platforms.support]
@@ -27,6 +25,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 }
-9
View File
@@ -33,21 +33,12 @@ tauri-plugin-deep-link = { git = "https://github.com/tauri-apps/plugins-workspac
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh
pnpm add @tauri-apps/plugin-deep-link
# or
npm add @tauri-apps/plugin-deep-link
# or
yarn add @tauri-apps/plugin-deep-link
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-deep-link#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-deep-link#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-deep-link#v2
```
## Setting up
+2 -2
View File
@@ -5,10 +5,10 @@ plugins {
android {
namespace = "app.tauri.deep_link"
compileSdk = 34
compileSdk = 36
defaultConfig {
minSdk = 24
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
+113 -26
View File
@@ -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#"<data android:host="{h}" />"#))
.unwrap_or_default();
let auto_verify = if domain.is_app_link() {
r#"android:autoVerify="true" "#.to_string()
} else {
String::new()
};
format!(
r#"<intent-filter android:autoVerify="true">
r#"<intent-filter {auto_verify}>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
{}
<data android:host="{}" />
{}
{}
{}
{}
{schemes}
{host}
{domains}
{path_patterns}
{path_prefixes}
{path_suffixes}
</intent-filter>"#,
domain
schemes = domain
.scheme
.iter()
.map(|scheme| format!(r#"<data android:scheme="{scheme}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain.host,
domain
host = host,
domains = domain
.path
.iter()
.map(|path| format!(r#"<data android:path="{path}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain
path_patterns = domain
.path_pattern
.iter()
.map(|pattern| format!(r#"<data android:pathPattern="{pattern}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain
path_prefixes = domain
.path_prefix
.iter()
.map(|prefix| format!(r#"<data android:pathPrefix="{prefix}" />"#))
.collect::<Vec<_>>()
.join("\n "),
domain
path_suffixes = domain
.path_suffix
.iter()
.map(|suffix| format!(r#"<data android:pathSuffix="{suffix}" />"#))
.collect::<Vec<_>>()
.join("\n "),
)
.trim()
.to_string()
}
fn main() {
@@ -68,6 +82,16 @@ fn main() {
}
if let Some(config) = tauri_plugin::plugin_config::<Config>("deep-link") {
let errors: Vec<String> = 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,83 @@ 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::<Vec<_>>()
.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::<Vec<_>>()
.into(),
);
})
.expect("failed to update entitlements");
}
let deep_link_domains = config
.mobile
.iter()
.filter(|domain| domain.is_app_link())
.collect::<Vec<_>>();
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::<Vec<_>>();
let mut dict = plist::Dictionary::new();
dict.insert(
"CFBundleURLSchemes".into(),
schemes
.iter()
.map(|s| s.to_string().into())
.collect::<Vec<_>>()
.into(),
);
dict.insert(
"CFBundleURLName".into(),
domain.scheme[0].clone().into(),
);
plist::Value::Dictionary(dict)
})
.collect::<Vec<_>>()
.into(),
);
})
.expect("failed to update Info.plist");
}
}
}
}
@@ -1,5 +1,35 @@
# Changelog
## \[2.2.9]
### Dependencies
- Upgraded to `deep-link-js@2.4.6`
## \[2.2.8]
### Dependencies
- Upgraded to `deep-link-js@2.4.5`
## \[2.2.7]
### Dependencies
- Upgraded to `deep-link-js@2.4.4`
## \[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
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "deep-link-example",
"private": true,
"version": "2.2.4",
"version": "2.2.9",
"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.9.1",
"@tauri-apps/plugin-deep-link": "2.4.6"
},
"devDependencies": {
"@tauri-apps/cli": "2.7.1",
"@tauri-apps/cli": "2.9.6",
"typescript": "^5.7.3",
"vite": "^7.0.4"
"vite": "^7.0.7"
}
}
@@ -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")
@@ -23,23 +23,40 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
<intent-filter android:autoVerify="true">
<intent-filter android:autoVerify="true" >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="http" />
<data android:host="fabianlars.de" />
<data android:pathPrefix="/intent" />
</intent-filter>
<intent-filter android:autoVerify="true">
<intent-filter android:autoVerify="true" >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="http" />
<data android:host="tauri.app" />
</intent-filter>
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="taurideeplink" />
</intent-filter>
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
</activity>
@@ -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)
}
}
@@ -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")
}
}
@@ -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")
}
@@ -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
@@ -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"
}
}
}
@@ -1,6 +1,6 @@
{
"info": {
"version": 1,
"author": "xcode"
"info" : {
"version" : 1,
"author" : "xcode"
}
}
}
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17150" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17150" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17122"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
@@ -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;
};
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.0.0</string>
<string>0.1.0</string>
<key>CFBundleVersion</key>
<string>0.1.0</string>
<key>LSRequiresIPhoneOS</key>
@@ -40,5 +40,16 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>taurideeplink</string>
</array>
<key>CFBundleURLName</key>
<string>taurideeplink</string>
</dict>
</array>
</dict>
</plist>
@@ -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
@@ -35,6 +35,9 @@
},
{
"host": "tauri.app"
},
{
"scheme": ["taurideeplink"]
}
],
"desktop": {
+5 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-deep-link",
"version": "2.4.1",
"version": "2.4.6",
"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.9.6"
}
}
+46 -12
View File
@@ -11,10 +11,8 @@ use tauri_utils::config::DeepLinkProtocol;
pub struct AssociatedDomain {
#[serde(default = "default_schemes")]
pub scheme: Vec<String>,
#[serde(deserialize_with = "deserialize_associated_host")]
pub host: String,
#[serde(default, deserialize_with = "deserialize_associated_host")]
pub host: Option<String>, // Optional custom uri schemes dont NEED a host (may have one still), but required for https/http schemes
#[serde(default)]
pub path: Vec<String>,
#[serde(default, alias = "path-pattern", rename = "pathPattern")]
@@ -23,6 +21,41 @@ pub struct AssociatedDomain {
pub path_prefix: Vec<String>,
#[serde(default, alias = "path-suffix", rename = "pathSuffix")]
pub path_suffix: Vec<String>,
#[serde(default, alias = "app-link", rename = "appLink")]
pub app_link: Option<bool>,
}
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<String> {
vec!["https".to_string(), "http".to_string()]
}
fn deserialize_associated_host<'de, D>(deserializer: D) -> Result<String, D::Error>
fn deserialize_associated_host<'de, D>(deserializer: D) -> Result<Option<String>, 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::<String>::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)]
+10
View File
@@ -28,6 +28,16 @@ pub enum Error {
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
}
// TODO(v3): change this into an error in v3,
// see <https://github.com/tauri-apps/plugins-workspace/pull/2970#issuecomment-3244660138>.
#[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<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
+30 -6
View File
@@ -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<S: AsRef<str>>(&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<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<bool> {
#[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))
}
+2 -2
View File
@@ -1,7 +1,7 @@
[Desktop Entry]
Type=Application
Name={name}
Exec={exec} %u
Exec={qualified_exec}
Terminal=false
MimeType={mime_type}
NoDisplay=true
NoDisplay=true
+42
View File
@@ -1,5 +1,47 @@
# Changelog
## \[2.5.0]
- [`dff6fa98`](https://github.com/tauri-apps/plugins-workspace/commit/dff6fa986a9a05ba98b6ca660fea78ae97251fc2) ([#3034](https://github.com/tauri-apps/plugins-workspace/pull/3034) by [@onehumandev](https://github.com/tauri-apps/plugins-workspace/../../onehumandev)) Add `pickerMode` option to file picker (currently only used on iOS)
### Dependencies
- Upgraded to `fs-js@2.4.5`
### feat
- [`c23fa03f`](https://github.com/tauri-apps/plugins-workspace/commit/c23fa03f07d5c1c220bcf0bca482364513e3f754) ([#3098](https://github.com/tauri-apps/plugins-workspace/pull/3098) by [@Lepidopteran](https://github.com/tauri-apps/plugins-workspace/../../Lepidopteran)) Add `xdg-portal` as an optional feature for `rfd`
## \[2.4.2]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
### Dependencies
- Upgraded to `fs-js@2.4.4`
## \[2.4.1]
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
### Dependencies
- Upgraded to `fs-js@2.4.3`
## \[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.
## \[2.3.1]
### Dependencies
+9 -7
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-dialog"
version = "2.3.1"
version = "2.5.0"
description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
@@ -9,9 +9,12 @@ rust-version = { workspace = true }
repository = { workspace = true }
links = "tauri-plugin-dialog"
[features]
default = ["gtk3"]
xdg-portal = ["rfd/xdg-portal", "rfd/tokio", "rfd/wayland"]
gtk3 = ["rfd/gtk3"]
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-unknown-linux-gnu", "x86_64-linux-android"]
[package.metadata.platforms.support]
@@ -34,15 +37,14 @@ 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.5" }
[target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] }
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
rfd = { version = "0.15", default-features = false, features = [
"tokio",
"gtk3",
rfd = { version = "0.16", default-features = false, features = [
"common-controls-v6",
] }
raw-window-handle = "0.6"
+18 -9
View File
@@ -31,9 +31,25 @@ tauri-plugin-dialog = "2.0.0"
tauri-plugin-dialog = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
```
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
### Linux XDG Desktop Portal Support
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
By default, this plugin uses gtk to show dialogs, however since `v2.5.0` you can switch to using [XDG Desktop Portal](https://flatpak.github.io/xdg-desktop-portal/) by adding the following to your `Cargo.toml` file:
```toml
[dependencies]
tauri-plugin-dialog = { version = "2.5.0", default-features = false, features = ["xdg-portal"] }
# alternatively with Git:
tauri-plugin-dialog = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2", default-features = false, features = ["xdg-portal"] }
```
Do note if you use the `xdg-portal` feature, you need to ensure that [`zenity`](https://gitlab.gnome.org/GNOME/zenity) and an [XDG Desktop Portal backend](https://flatpak.github.io/xdg-desktop-portal#using-portals) is installed with your program.
For more information, see [XDG Desktop Portal documentation](https://flatpak.github.io/xdg-desktop-portal/) and [`rfd` documentation](https://docs.rs/rfd/latest/rfd#xdg-desktop-portal-backend).
### JavaScript
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
```sh
pnpm add @tauri-apps/plugin-dialog
@@ -41,13 +57,6 @@ pnpm add @tauri-apps/plugin-dialog
npm add @tauri-apps/plugin-dialog
# or
yarn add @tauri-apps/plugin-dialog
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-dialog#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-dialog#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-dialog#v2
```
## Usage
+2 -2
View File
@@ -5,10 +5,10 @@ plugins {
android {
namespace = "app.tauri.dialog"
compileSdk = 34
compileSdk = 36
defaultConfig {
minSdk = 24
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
@@ -31,6 +31,7 @@ class Filter {
class FilePickerOptions {
lateinit var filters: Array<Filter>
var multiple: Boolean? = null
var pickerMode: String? = null
}
@InvokeArg
@@ -38,6 +39,7 @@ class MessageOptions {
var title: String? = null
lateinit var message: String
var okButtonLabel: String? = null
var noButtonLabel: String? = null
var cancelButtonLabel: String? = null
}
@@ -56,20 +58,27 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
try {
val args = invoke.parseArgs(FilePickerOptions::class.java)
val parsedTypes = parseFiltersOption(args.filters)
val intent = if (parsedTypes.isNotEmpty()) {
val intent = Intent(Intent.ACTION_PICK)
setIntentMimeTypes(intent, parsedTypes)
intent
} else {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
// TODO: ACTION_OPEN_DOCUMENT ??
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
if (args.pickerMode == "image") {
intent.type = "image/*"
} else if (args.pickerMode == "video") {
intent.type = "video/*"
} else if (args.pickerMode == "media") {
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("video/*", "image/*"))
} else if (parsedTypes.isNotEmpty()) {
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, parsedTypes)
} else {
intent.type = "*/*"
intent
}
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, args.multiple ?: false)
startActivityForResult(invoke, intent, "filePickerResult")
} catch (ex: Exception) {
val message = ex.message ?: "Failed to pick file"
@@ -115,7 +124,7 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
callResult.put("files", JSArray.from(uris.toTypedArray()))
return callResult
}
private fun parseFiltersOption(filters: Array<Filter>): Array<String> {
val mimeTypes = mutableListOf<String>()
for (filter in filters) {
@@ -132,46 +141,17 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
return mimeTypes.toTypedArray()
}
private fun setIntentMimeTypes(intent: Intent, mimeTypes: Array<String>) {
if (mimeTypes.isNotEmpty()) {
var uniqueMimeKind = true
var mimeKind: String? = null
for (mime in mimeTypes) {
val kind = mime.split("/")[0]
if (mimeKind == null) {
mimeKind = kind
} else if (mimeKind != kind) {
uniqueMimeKind = false
}
}
if (uniqueMimeKind) {
if (mimeTypes.size > 1) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
intent.type = Intent.normalizeMimeType("$mimeKind/*")
} else {
intent.type = mimeTypes[0]
}
} else {
intent.type = "*/*"
}
} else {
intent.type = "*/*"
}
}
@Command
fun showMessageDialog(invoke: Invoke) {
val args = invoke.parseArgs(MessageOptions::class.java)
if (activity.isFinishing) {
invoke.reject("App is finishing")
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)
}
@@ -179,28 +159,38 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
Handler(Looper.getMainLooper())
.post {
val builder = AlertDialog.Builder(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()
}
@@ -213,10 +203,14 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
val parsedTypes = parseFiltersOption(args.filters)
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
setIntentMimeTypes(intent, parsedTypes)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.putExtra(Intent.EXTRA_TITLE, args.fileName ?: "")
intent.type = "*/*"
if (parsedTypes.isNotEmpty()) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, parsedTypes)
}
startActivityForResult(invoke, intent, "saveFileDialogResult")
} catch (ex: Exception) {
val message = ex.message ?: "Failed to pick save file"
+1 -1
View File
@@ -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__})}
+175 -5
View File
@@ -14,6 +14,16 @@ interface DialogFilter {
name: string
/**
* Extensions to filter, without a `.` prefix.
*
* **Note:** Mobile platforms have different APIs for filtering that may not support extensions.
* iOS: Extensions are supported in the document picker, but not in the media picker.
* Android: Extensions are not supported.
*
* For these platforms, MIME types are the primary way to filter files, as opposed to extensions.
* This means the string values here labeled as `extensions` may also be a MIME type.
* This property name of `extensions` is being kept for backwards compatibility, but this may be revisited to
* specify the difference between extension or MIME type filtering.
*
* @example
* ```typescript
* extensions: ['svg', 'png']
@@ -30,7 +40,14 @@ interface DialogFilter {
interface OpenDialogOptions {
/** The title of the dialog window (desktop only). */
title?: string
/** The filters of the dialog. */
/**
* The filters of the dialog.
* On mobile platforms, if either:
* A) the {@linkcode pickerMode} is set to `media`, `image`, or `video`
* -- or --
* B) the filters include **only** either image or video mime types, the media picker will be displayed.
* Otherwise, the document picker will be displayed.
*/
filters?: DialogFilter[]
/**
* Initial directory or file path.
@@ -52,6 +69,13 @@ interface OpenDialogOptions {
recursive?: boolean
/** Whether to allow creating directories in the dialog. Enabled by default. **macOS Only** */
canCreateDirectories?: boolean
/**
* The preferred mode of the dialog.
* This is meant for mobile platforms (iOS and Android) which have distinct file and media pickers.
* If not provided, the dialog will automatically choose the best mode based on the MIME types or extensions of the {@linkcode filters}.
* On desktop, this option is ignored.
*/
pickerMode?: PickerMode
}
/**
@@ -77,6 +101,90 @@ interface SaveDialogOptions {
canCreateDirectories?: boolean
}
/**
* The preferred mode of the dialog.
* This is meant for mobile platforms (iOS and Android) which have distinct file and media pickers.
* On desktop, this option is ignored.
* If not provided, the dialog will automatically choose the best mode based on the MIME types or extensions of the {@linkcode filters}.
*
* **Note:** This option is only supported on iOS 14 and above. This parameter is ignored on iOS 13 and below.
*/
export type PickerMode = 'document' | 'media' | 'image' | 'video'
/**
* 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<Allowed extends ButtonKey> = Partial<
Record<Exclude<ButtonKey, Allowed>, 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 +193,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 +360,16 @@ async function save(options: SaveDialogOptions = {}): Promise<string | null> {
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 +390,15 @@ async function save(options: SaveDialogOptions = {}): Promise<string | null> {
async function message(
message: string,
options?: string | MessageDialogOptions
): Promise<void> {
): Promise<MessageDialogResult> {
const opts = typeof options === 'string' ? { title: options } : options
await invoke('plugin:dialog|message', {
return invoke<MessageDialogResult>('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)
})
}
+129 -70
View File
@@ -8,6 +8,7 @@ import PhotosUI
import SwiftRs
import Tauri
import UIKit
import UniformTypeIdentifiers
import WebKit
enum FilePickerEvent {
@@ -20,6 +21,7 @@ struct MessageDialogOptions: Decodable {
var title: String?
let message: String
var okButtonLabel: String?
var noButtonLabel: String?
var cancelButtonLabel: String?
}
@@ -31,6 +33,7 @@ struct FilePickerOptions: Decodable {
var multiple: Bool?
var filters: [Filter]?
var defaultPath: String?
var pickerMode: PickerMode?
}
struct SaveFileDialogOptions: Decodable {
@@ -38,6 +41,13 @@ struct SaveFileDialogOptions: Decodable {
var defaultPath: String?
}
enum PickerMode: String, Decodable {
case document
case media
case image
case video
}
class DialogPlugin: Plugin {
var filePickerController: FilePickerController!
@@ -51,26 +61,6 @@ class DialogPlugin: Plugin {
@objc public func showFilePicker(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(FilePickerOptions.self)
let parsedTypes = parseFiltersOption(args.filters ?? [])
var isMedia = !parsedTypes.isEmpty
var uniqueMimeType: Bool? = nil
var mimeKind: String? = nil
if !parsedTypes.isEmpty {
uniqueMimeType = true
for mime in parsedTypes {
let kind = mime.components(separatedBy: "/")[0]
if kind != "image" && kind != "video" {
isMedia = false
}
if mimeKind == nil {
mimeKind = kind
} else if mimeKind != kind {
uniqueMimeType = false
}
}
}
onFilePickerResult = { (event: FilePickerEvent) -> Void in
switch event {
case .selected(let urls):
@@ -80,51 +70,57 @@ class DialogPlugin: Plugin {
case .error(let error):
invoke.reject(error)
}
}
}
if uniqueMimeType == true || isMedia {
DispatchQueue.main.async {
if #available(iOS 14, *) {
if #available(iOS 14, *) {
let parsedTypes = parseFiltersOption(args.filters ?? [])
let mimeKinds = Set(parsedTypes.compactMap { $0.preferredMIMEType?.components(separatedBy: "/")[0] })
let filtersIncludeImage = mimeKinds.contains("image")
let filtersIncludeVideo = mimeKinds.contains("video")
let filtersIncludeNonMedia = mimeKinds.contains(where: { $0 != "image" && $0 != "video" })
// If the picker mode is media, images, or videos, we always want to show the media picker regardless of what's in the filters.
// Otherwise, if the filters A) do not include non-media types and B) include either image or video, we want to show the media picker.
if args.pickerMode == .media
|| args.pickerMode == .image
|| args.pickerMode == .video
|| (!filtersIncludeNonMedia && (filtersIncludeImage || filtersIncludeVideo)) {
DispatchQueue.main.async {
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.selectionLimit = (args.multiple ?? false) ? 0 : 1
if uniqueMimeType == true {
if mimeKind == "image" {
configuration.filter = .images
} else if mimeKind == "video" {
configuration.filter = .videos
}
// If the filters include image or video, use the appropriate filter.
// If both are true, don't define a filter, which means we will display all media.
if args.pickerMode == .image || (filtersIncludeImage && !filtersIncludeVideo) {
configuration.filter = .images
} else if args.pickerMode == .video || (filtersIncludeVideo && !filtersIncludeImage) {
configuration.filter = .videos
}
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self.filePickerController
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
} else {
let picker = UIImagePickerController()
picker.delegate = self.filePickerController
if uniqueMimeType == true && mimeKind == "image" {
picker.sourceType = .photoLibrary
}
} else {
DispatchQueue.main.async {
// The UTType.item is the catch-all, allowing for any file type to be selected.
let contentTypes = parsedTypes.isEmpty ? [UTType.item] : parsedTypes
let picker: UIDocumentPickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: contentTypes, asCopy: true)
if let defaultPath = args.defaultPath {
picker.directoryURL = URL(string: defaultPath)
}
picker.sourceType = .photoLibrary
picker.delegate = self.filePickerController
picker.allowsMultipleSelection = args.multiple ?? false
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
}
}
} else {
let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
DispatchQueue.main.async {
let picker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .import)
if let defaultPath = args.defaultPath {
picker.directoryURL = URL(string: defaultPath)
}
picker.delegate = self.filePickerController
picker.allowsMultipleSelection = args.multiple ?? false
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
}
showFilePickerLegacy(args: args)
}
}
@@ -172,19 +168,80 @@ class DialogPlugin: Plugin {
self.manager.viewController?.present(viewControllerToPresent, animated: true, completion: nil)
}
private func parseFiltersOption(_ filters: [Filter]) -> [String] {
@available(iOS 14, *)
private func parseFiltersOption(_ filters: [Filter]) -> [UTType] {
var parsedTypes: [UTType] = []
for filter in filters {
for ext in filter.extensions ?? [] {
// We need to support extensions as well as MIME types.
if let utType = UTType(mimeType: ext) {
parsedTypes.append(utType)
} else if let utType = UTType(filenameExtension: ext) {
parsedTypes.append(utType)
}
}
}
return parsedTypes
}
/// This function is only used for iOS < 14, and should be removed if/when the deployment target is raised to 14.
private func showFilePickerLegacy(args: FilePickerOptions) {
let parsedTypes = parseFiltersOptionLegacy(args.filters ?? [])
var filtersIncludeImage: Bool = false
var filtersIncludeVideo: Bool = false
var filtersIncludeNonMedia: Bool = false
if !parsedTypes.isEmpty {
let mimeKinds = Set(parsedTypes.map { $0.components(separatedBy: "/")[0] })
filtersIncludeImage = mimeKinds.contains("image")
filtersIncludeVideo = mimeKinds.contains("video")
filtersIncludeNonMedia = mimeKinds.contains(where: { $0 != "image" && $0 != "video" })
}
if !filtersIncludeNonMedia && (filtersIncludeImage || filtersIncludeVideo) {
DispatchQueue.main.async {
let picker = UIImagePickerController()
picker.delegate = self.filePickerController
if filtersIncludeImage && !filtersIncludeVideo {
picker.sourceType = .photoLibrary
}
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
}
} else {
let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
DispatchQueue.main.async {
let picker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .import)
if let defaultPath = args.defaultPath {
picker.directoryURL = URL(string: defaultPath)
}
picker.delegate = self.filePickerController
picker.allowsMultipleSelection = args.multiple ?? false
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
}
}
}
/// This function is only used for iOS < 14, and should be removed if/when the deployment target is raised to 14.
private func parseFiltersOptionLegacy(_ filters: [Filter]) -> [String] {
var parsedTypes: [String] = []
for filter in filters {
for ext in filter.extensions ?? [] {
guard
let utType: String = UTTypeCreatePreferredIdentifierForTag(
kUTTagClassMIMEType, ext as CFString, nil)?.takeRetainedValue() as String?
let utType: String = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, ext as CFString, nil)?.takeRetainedValue() as String?
else {
continue
}
parsedTypes.append(utType)
}
}
return parsedTypes
}
@@ -200,36 +257,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)
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-dialog",
"version": "2.3.1",
"version": "2.5.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"
}
}
+31 -21
View File
@@ -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, PickerMode, Result, CANCEL, NO, OK, YES,
};
#[derive(Serialize)]
@@ -56,6 +56,13 @@ pub struct OpenDialogOptions {
recursive: bool,
/// Whether to allow creating directories in the dialog **macOS Only**
can_create_directories: Option<bool>,
/// The preferred mode of the dialog.
/// This is meant for mobile platforms (iOS and Android) which have distinct file and media pickers.
/// On desktop, this option is ignored.
/// If not provided, the dialog will automatically choose the best mode based on the MIME types of the filters.
#[serde(default)]
#[cfg_attr(mobile, allow(dead_code))]
picker_mode: Option<PickerMode>,
}
/// The options for the save dialog API.
@@ -127,6 +134,9 @@ pub(crate) async fn open<R: Runtime>(
if let Some(can) = options.can_create_directories {
dialog_builder = dialog_builder.set_can_create_directories(can);
}
if let Some(picker_mode) = options.picker_mode {
dialog_builder = dialog_builder.set_picker_mode(picker_mode);
}
for filter in options.filters {
let extensions: Vec<&str> = filter.extensions.iter().map(|s| &**s).collect();
dialog_builder = dialog_builder.add_filter(filter.name, &extensions);
@@ -248,7 +258,7 @@ fn message_dialog<R: Runtime>(
message: String,
kind: Option<MessageDialogKind>,
buttons: MessageDialogButtons,
) -> bool {
) -> MessageDialogBuilder<R> {
let mut builder = dialog.message(message);
builder = builder.buttons(buttons);
@@ -266,7 +276,7 @@ fn message_dialog<R: Runtime>(
builder = builder.kind(kind);
}
builder.blocking_show()
builder
}
#[command]
@@ -277,19 +287,15 @@ pub(crate) async fn message<R: Runtime>(
message: String,
kind: Option<MessageDialogKind>,
ok_button_label: Option<String>,
) -> Result<bool> {
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<MessageDialogButtons>,
) -> Result<MessageDialogResult> {
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 +308,7 @@ pub(crate) async fn ask<R: Runtime>(
yes_button_label: Option<String>,
no_button_label: Option<String>,
) -> Result<bool> {
Ok(message_dialog(
let dialog = message_dialog(
window,
dialog,
title,
@@ -318,7 +324,9 @@ pub(crate) async fn ask<R: Runtime>(
} else {
MessageDialogButtons::YesNo
},
))
);
Ok(dialog.blocking_show())
}
#[command]
@@ -331,7 +339,7 @@ pub(crate) async fn confirm<R: Runtime>(
ok_button_label: Option<String>,
cancel_button_label: Option<String>,
) -> Result<bool> {
Ok(message_dialog(
let dialog = message_dialog(
window,
dialog,
title,
@@ -347,5 +355,7 @@ pub(crate) async fn confirm<R: Runtime>(
} else {
MessageDialogButtons::OkCancel
},
))
);
Ok(dialog.blocking_show())
}
+40 -18
View File
@@ -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<R: Runtime, C: DeserializeOwned>(
app: &AppHandle<R>,
@@ -115,6 +115,10 @@ impl From<MessageDialogButtons> 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<R: Runtime, F: FnOnce(Option<FilePath>) + Send + 'static>(
}
/// Shows a message dialog
pub fn show_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>(
pub fn show_message_dialog<R: Runtime, F: FnOnce(MessageDialogResult) + Send + 'static>(
dialog: MessageDialogBuilder<R>,
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);
});
});
}
+107 -13
View File
@@ -9,7 +9,7 @@
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
)]
use serde::Serialize;
use serde::{Deserialize, Serialize};
use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
@@ -44,6 +44,15 @@ pub use desktop::Dialog;
#[cfg(mobile)]
pub use mobile::Dialog;
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "lowercase")]
pub enum PickerMode {
Document,
Media,
Image,
Video,
}
pub(crate) const OK: &str = "Ok";
pub(crate) const CANCEL: &str = "Cancel";
pub(crate) const YES: &str = "Yes";
@@ -216,6 +225,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 +248,17 @@ impl<R: Runtime> MessageDialogBuilder<R> {
#[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 +266,7 @@ impl<R: Runtime> MessageDialogBuilder<R> {
message: &self.message,
kind: &self.kind,
ok_button_label,
no_button_label,
cancel_button_label,
}
}
@@ -295,16 +310,55 @@ impl<R: Runtime> MessageDialogBuilder<R> {
}
/// Shows a message dialog
///
/// Returns `true` if the user pressed the OK/Yes button,
pub fn show<F: FnOnce(bool) + Send + 'static>(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<F: FnOnce(MessageDialogResult) + Send + 'static>(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 {
@@ -324,6 +378,7 @@ pub struct FileDialogBuilder<R: Runtime> {
pub(crate) file_name: Option<String>,
pub(crate) title: Option<String>,
pub(crate) can_create_directories: Option<bool>,
pub(crate) picker_mode: Option<PickerMode>,
#[cfg(desktop)]
pub(crate) parent: Option<crate::desktop::WindowHandle>,
}
@@ -335,6 +390,7 @@ pub(crate) struct FileDialogPayload<'a> {
file_name: &'a Option<String>,
filters: &'a Vec<Filter>,
multiple: bool,
picker_mode: &'a Option<PickerMode>,
}
// raw window handle :(
@@ -350,6 +406,7 @@ impl<R: Runtime> FileDialogBuilder<R> {
file_name: None,
title: None,
can_create_directories: None,
picker_mode: None,
#[cfg(desktop)]
parent: None,
}
@@ -361,6 +418,7 @@ impl<R: Runtime> FileDialogBuilder<R> {
file_name: &self.file_name,
filters: &self.filters,
multiple,
picker_mode: &self.picker_mode,
}
}
@@ -421,11 +479,21 @@ impl<R: Runtime> FileDialogBuilder<R> {
self
}
/// Set the picker mode of the dialog.
/// This is meant for mobile platforms (iOS and Android) which have distinct file and media pickers.
/// On desktop, this option is ignored.
/// If not provided, the dialog will automatically choose the best mode based on the MIME types of the filters.
pub fn set_picker_mode(mut self, mode: PickerMode) -> Self {
self.picker_mode.replace(mode);
self
}
/// Shows the dialog to select a single file.
///
/// This is not a blocking operation,
/// and should be used when running on the main thread to avoid deadlocks with the event loop.
///
/// For usage in other contexts such as commands, prefer [`Self::pick_file`].
/// See [`Self::blocking_pick_file`] for a blocking version for use in other contexts.
///
/// # Examples
///
@@ -445,9 +513,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
}
/// Shows the dialog to select multiple files.
///
/// This is not a blocking operation,
/// and should be used when running on the main thread to avoid deadlocks with the event loop.
///
/// See [`Self::blocking_pick_files`] for a blocking version for use in other contexts.
///
/// # Reading the files
///
/// The file paths cannot be read directly on Android as they are behind a content URI.
@@ -490,9 +561,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
}
/// Shows the dialog to select a single folder.
///
/// This is not a blocking operation,
/// and should be used when running on the main thread to avoid deadlocks with the event loop.
///
/// See [`Self::blocking_pick_folder`] for a blocking version for use in other contexts.
///
/// # Examples
///
/// ```
@@ -512,9 +586,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
}
/// Shows the dialog to select multiple folders.
///
/// This is not a blocking operation,
/// and should be used when running on the main thread to avoid deadlocks with the event loop.
///
/// See [`Self::blocking_pick_folders`] for a blocking version for use in other contexts.
///
/// # Examples
///
/// ```
@@ -538,6 +615,8 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// This is not a blocking operation,
/// and should be used when running on the main thread to avoid deadlocks with the event loop.
///
/// See [`Self::blocking_save_file`] for a blocking version for use in other contexts.
///
/// # Examples
///
/// ```
@@ -559,8 +638,11 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// Blocking APIs.
impl<R: Runtime> FileDialogBuilder<R> {
/// Shows the dialog to select a single file.
///
/// This is a blocking operation,
/// and should *NOT* be used when running on the main thread context.
/// and should *NOT* be used when running on the main thread.
///
/// See [`Self::pick_file`] for a non-blocking version for use in main-thread contexts.
///
/// # Examples
///
@@ -578,8 +660,11 @@ impl<R: Runtime> FileDialogBuilder<R> {
}
/// Shows the dialog to select multiple files.
///
/// This is a blocking operation,
/// and should *NOT* be used when running on the main thread context.
/// and should *NOT* be used when running on the main thread.
///
/// See [`Self::pick_files`] for a non-blocking version for use in main-thread contexts.
///
/// # Examples
///
@@ -597,8 +682,11 @@ impl<R: Runtime> FileDialogBuilder<R> {
}
/// Shows the dialog to select a single folder.
///
/// This is a blocking operation,
/// and should *NOT* be used when running on the main thread context.
/// and should *NOT* be used when running on the main thread.
///
/// See [`Self::pick_folder`] for a non-blocking version for use in main-thread contexts.
///
/// # Examples
///
@@ -617,8 +705,11 @@ impl<R: Runtime> FileDialogBuilder<R> {
}
/// Shows the dialog to select multiple folders.
///
/// This is a blocking operation,
/// and should *NOT* be used when running on the main thread context.
/// and should *NOT* be used when running on the main thread.
///
/// See [`Self::pick_folders`] for a non-blocking version for use in main-thread contexts.
///
/// # Examples
///
@@ -637,8 +728,11 @@ impl<R: Runtime> FileDialogBuilder<R> {
}
/// Shows the dialog to save a file.
///
/// This is a blocking operation,
/// and should *NOT* be used when running on the main thread context.
/// and should *NOT* be used when running on the main thread.
///
/// See [`Self::save_file`] for a non-blocking version for use in main-thread contexts.
///
/// # Examples
///
+6 -6
View File
@@ -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<R: Runtime, F: FnOnce(Option<FilePath>) + 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<R: Runtime, F: FnOnce(bool) + Send + 'static>(
pub fn show_message_dialog<R: Runtime, F: FnOnce(MessageDialogResult) + Send + 'static>(
dialog: MessageDialogBuilder<R>,
f: F,
) {
@@ -122,6 +120,8 @@ pub fn show_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>(
.dialog
.0
.run_mobile_plugin::<ShowMessageDialogResponse>("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())
});
}
+44 -8
View File
@@ -6,9 +6,10 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// Types of message, ask and confirm dialogs.
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
pub enum MessageDialogKind {
/// Information dialog.
#[default]
Info,
/// Warning dialog.
Warning,
@@ -16,12 +17,6 @@ pub enum MessageDialogKind {
Error,
}
impl Default for MessageDialogKind {
fn default() -> Self {
Self::Info
}
}
impl<'de> Deserialize<'de> for MessageDialogKind {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@@ -52,7 +47,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 +56,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<rfd::MessageDialogResult> 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<String> 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),
}
}
}
+20
View File
@@ -1,5 +1,25 @@
# Changelog
## \[2.4.5]
- [`521cd8b3`](https://github.com/tauri-apps/plugins-workspace/commit/521cd8b372c862d96b1637775710e4d7cf2443e2) ([#3155](https://github.com/tauri-apps/plugins-workspace/pull/3155) by [@EliasStar](https://github.com/tauri-apps/plugins-workspace/../../EliasStar)) Fix off by one error in the implementation of readTextFileLines causing all lines to end with an (additional) null byte.
Issue: [#3154](https://github.com/tauri-apps/plugins-workspace/issues/3154)
PR: [#3155](https://github.com/tauri-apps/plugins-workspace/pull/3155)
## \[2.4.4]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.4.3]
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
- [`6b5b1053`](https://github.com/tauri-apps/plugins-workspace/commit/6b5b1053ba8aeb789dd5cb5fb05b7e98f3b8de0b) ([#1939](https://github.com/tauri-apps/plugins-workspace/pull/1939) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Enhance error messages.
## \[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<Uint8Array<ArrayBuffer>>` instead of the default `Promise<Uint8Array<ArrayBufferLike>`
## \[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`
+3 -7
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-fs"
version = "2.4.1"
version = "2.4.5"
description = "Access the file system."
authors = { workspace = true }
license = { workspace = true }
@@ -9,10 +9,6 @@ rust-version = { workspace = true }
repository = { workspace = true }
links = "tauri-plugin-fs"
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.platforms.support]
windows = { level = "full", notes = "Apps installed via MSI or NSIS in `perMachine` and `both` mode require admin permissions for write access in `$RESOURCES` folder" }
linux = { level = "full", notes = "No write access to `$RESOURCES` folder" }
@@ -24,7 +20,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]
@@ -41,7 +37,7 @@ notify = { version = "8", optional = true, features = [
"serde",
"serialization-compat-6",
] }
notify-debouncer-full = { version = "0.5", optional = true }
notify-debouncer-full = { version = "0.6", optional = true }
dunce = { workspace = true }
percent-encoding = "2"
-9
View File
@@ -33,21 +33,12 @@ tauri-plugin-fs = { git = "https://github.com/tauri-apps/plugins-workspace", bra
You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh
pnpm add @tauri-apps/plugin-fs
# or
npm add @tauri-apps/plugin-fs
# or
yarn add @tauri-apps/plugin-fs
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-fs#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-fs#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-fs#v2
```
## Usage
+1 -2
View File
@@ -5,11 +5,10 @@ plugins {
android {
namespace = "com.plugin.fs"
compileSdk = 34
compileSdk = 36
defaultConfig {
minSdk = 21
targetSdk = 34
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
File diff suppressed because one or more lines are too long
+10 -3
View File
@@ -739,7 +739,7 @@ interface ReadFileOptions {
async function readFile(
path: string | URL,
options?: ReadFileOptions
): Promise<Uint8Array> {
): Promise<Uint8Array<ArrayBuffer>> {
if (path instanceof URL && path.protocol !== 'file:') {
throw new TypeError('Must be a file URL.')
}
@@ -838,7 +838,9 @@ async function readTextFileLines(
return { value: null, done }
}
const line = new TextDecoder().decode(bytes.slice(0, bytes.byteLength))
const line = new TextDecoder().decode(
bytes.slice(0, bytes.byteLength - 1)
)
return {
value: line,
@@ -1074,7 +1076,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 {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-fs",
"version": "2.4.1",
"version": "2.4.5",
"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"
}
}
@@ -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"
]
}
]
}
```
</td>
</tr>
+2 -2
View File
@@ -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.",

Some files were not shown because too many files have changed in this diff Show More