Compare commits

..

43 Commits

Author SHA1 Message Date
renovate[bot] 6b7d59ca27 chore(deps): update windows-rs and webview2 crates 2026-07-21 10:29:10 +00:00
Tony ab7489c964 feat(updater): option to not restart after install (#3299)
* feat(updater): option to not restart after install

* More platform cfg

* Add the same function for `UpdaterBuilder`

* Fix missing `#[cfg(windows)]`

* Mark `current_exe_args` cfg(windows)

* Mark `on_before_exit` cfg(windows)

* Mark `installer_args` cfg(windows)

* Note about `installer_arg` apply to both

* Remove current args and restart together

* Only build needed bundle on windows as well

* Remove `/NS` since it breaks the msi updater

* Add launch updater debug log

* Add a folder to test updates

* Remove unused `#[allow(unused)]`

* Format

* messed up git stage

* Add change file

* Clean up

* Disable NSIS compression for API example

* Bump wry for v1 test to pull in https://github.com/tauri-apps/wry/pull/1703

* format

* Make typescript happy

* Close new update on destroy
2026-07-21 18:25:16 +08:00
Vitor Ayres 3fb27bf13a fix(docs): deep link platform specific and fs scope (#3504)
* deep link fix Platform Specific heading

* fix fs permission toml

* .changes

* Update change files

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

* Update .changes/fs-deny-scope.md

* fix eol

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2026-07-21 14:56:52 +08:00
Tony 7e45774610 refactor(fs): cfg gate some ios code (#3510) 2026-07-21 14:42:45 +08:00
Pascal Auf der Maur 526726162f fix(nfc): adapt backend invoke to frontend (#3419)
* fix(nfc): adapt backend invoke to frontend

* Revert "fix(nfc): adapt backend invoke to frontend"

This reverts commit e34306e083.

* fix:  enforce nfc function return type

* Update .changes/nfc.md

Co-authored-by: Fabian-Lars <30730186+FabianLars@users.noreply.github.com>

---------

Co-authored-by: Fabian-Lars <30730186+FabianLars@users.noreply.github.com>
2026-07-20 13:11:20 +02:00
github-actions[bot] 03afae6d72 publish new versions (#3500)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-19 00:40:36 +08:00
renovate[bot] 57ac986453 chore(deps): update dependency @tauri-apps/cli to v2.11.4 (#3463)
* chore(deps): update dependency @tauri-apps/cli to v2.11.4

* Use workspace dependencies in example

* Bump tauri

* Leftover

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2026-07-18 22:28:03 +08:00
Tony 2ed6d6c1de fix(store): StoreOptions.defaults should not be required (#3499) 2026-07-18 19:46:31 +08:00
Tony cdfd462955 chore(example): set color-cheme (#3493)
This allows the unstyled user agent elements (like the checkbox) match the selected theme colors
2026-07-16 21:31:40 +08:00
Tony d6e0b6bbb1 chore(example): clean up and migrate to ts partially (#3492)
* chore(example): clean up and migrate to ts

* Add missing permissions

* Mirror line-height: 1.5 and remove h-*
2026-07-16 19:47:52 +08:00
Tony 40ae0a7fa0 enhance(dialog): use MaterialAlertDialogBuilder (#3491)
* Fix example insets

* Add app-region: drag

* Use `Theme.Material3.DayNight.NoActionBar`

* Re-generate some kotlin files

* Use MaterialAlertDialogBuilder

* Add change file

* Revert back to margin-top: 0.5rem

* Re-generate outdated gradle wrapper from #3039

* Move title bar to its own file

* Fix cancel message
2026-07-16 17:27:19 +08:00
Tony edc52ea056 chore(deps): bump create-pull-request to v8 (#3489) 2026-07-15 23:49:37 +08:00
Tony a0d949d93e chore(deps): update pnpm to v11 (#3487)
* chore(deps): update pnpm to v11

* Remove all `version: 11`

* Bump tsx
2026-07-15 20:24:01 +08:00
Tony 13c63af965 chore: fix clippy (#3488) 2026-07-15 16:01:07 +08:00
github-actions[bot] cad301fcc1 publish new versions (#3447)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-14 01:10:29 +02:00
Bajoca 254f222e0e chore(log): use kv stable feature rather than unstable (#3477) 2026-07-13 13:23:56 +02:00
Tony b1439be667 chore: ignore cargo audit errors (#3481)
* chore: ignore cargo audit errors

* Bump anyhow
2026-07-13 16:51:50 +08:00
Bajoca d157387722 fix(single-instance): Yield unix listener in macos (#3466)
* Use tokio UnixListener so the task can yield and release the thread

* use standard unix listener, convert to non blocking and cast into tokio yielding one

* Create fix-yield-single-instance-macos.md

* Update .changes/fix-yield-single-instance-macos.md

Co-authored-by: Fabian-Lars <30730186+FabianLars@users.noreply.github.com>

* use net feature in Tokio dependency

* Use tokio UnixListener::bind, inside the tokio task

---------

Co-authored-by: Fabian-Lars <30730186+FabianLars@users.noreply.github.com>
2026-06-29 11:00:59 +02:00
Tony 41f6274270 docs(log): some builder methods and defaults (#3460)
* docs(log): some builder methods and defaults

* Too much copy pastes

* Merge branch 'v2' into log-docs
2026-06-17 18:38:28 +08:00
Bajoca f08980f123 feat(log): rotate log file on each session (#3445)
* Update lib.rs

* docstring for file_open_strategy

* Create log-file-open-strategy.md

* Update plugins/log/src/lib.rs

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

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2026-06-17 16:25:24 +08:00
renovate[bot] 78df86f810 chore(deps): update dependency vite to v8.0.16 [security] (#3458)
* chore(deps): update dependency vite to v8.0.16 [security]

* Bump esbuild

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2026-06-16 11:18:07 +08:00
nandanpugalia aecec42d5d docs(http): fix connectTimeout option in README (#3451) 2026-06-15 10:08:31 +02:00
renovate[bot] 0767dcbd5c chore(deps): update dependency eslint-plugin-security to v4.0.1 (#3452) 2026-06-15 09:59:15 +02:00
Slava Minamoto 4be7690085 fix(positioner): return error instead of panicking when window has no monitor (#3449) 2026-06-11 21:05:20 +08:00
dependabot[bot] c2b3981248 chore(deps): bump tar in /plugins/updater/tests/updater-migration/v1-app (#3448)
Bumps [tar](https://github.com/composefs/tar-rs) from 0.4.45 to 0.4.46.
- [Release notes](https://github.com/composefs/tar-rs/releases)
- [Commits](https://github.com/composefs/tar-rs/compare/0.4.45...0.4.46)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 0.4.46
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-11 14:44:45 +08:00
beef 0c23b8ecfe remove unused dependency byte-unit from log plugin (#3446)
* remove unused dependency `byte-unit` from log plugin

* Update .changes/rmdep.md

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

* Update .changes/rmdep.md

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

---------

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2026-06-11 14:24:37 +08:00
renovate[bot] 4350ca652d chore(deps): update rust crate tar to v0.4.46 [security] (#3435)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-30 22:03:15 +08:00
renovate[bot] 1953f00188 chore(deps): update dependency prettier to v3.8.3 (#3387) 2026-05-28 16:14:25 +02:00
github-actions[bot] 9883f396dc Publish New Versions (v2) (#3425) 2026-05-28 12:06:17 +02:00
zankowitch alexis d8645ab3e5 fix(barcode-scanner): dispatch iOS cancel() cleanup to the main thread (#3393)
Signed-off-by: Alexis Zankowitch <a.zankowitch@reply.de>
2026-05-28 11:46:25 +02:00
renovate[bot] cb863d57c6 chore(deps): update rust crate global-hotkey to 0.8 (#3408)
Co-authored-by: Fabian-Lars <30730186+FabianLars@users.noreply.github.com>
2026-05-27 11:50:02 +02:00
FabianLars d67e4f8cdc chore(deps): update rand 2026-05-27 10:30:18 +02:00
Jsu e571e0be9d docs(fs): typo wether -> whether in commands.rs (#3429) 2026-05-23 21:28:55 +02:00
renovate[bot] 83ac944abf chore(deps): update dependency @tauri-apps/cli to v2.11.2 (#3427)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-20 10:07:23 +08:00
dependabot[bot] 0139c54d0e chore(deps): bump openssl (#3428)
Bumps [openssl](https://github.com/rust-openssl/rust-openssl) from 0.10.79 to 0.10.80.
- [Release notes](https://github.com/rust-openssl/rust-openssl/releases)
- [Commits](https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.79...openssl-v0.10.80)

---
updated-dependencies:
- dependency-name: openssl
  dependency-version: 0.10.80
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 10:06:59 +08:00
renovate[bot] cad34b73fa chore(deps): update dependency eslint to v10.4.0 (#3400)
* chore(deps): update dependency eslint to v10.4.0

* Fix audit

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2026-05-20 09:58:26 +08:00
Urs de Swardt c0d64bf7d9 fix: replace panics with error returns (#3420)
* refactor: replace panics with error returns

* fix: lints

* chore: add changes md
2026-05-18 21:56:33 +08:00
周诚 e919bab3eb fix(upload): wrong progress callback parameter names in README (#3406)
* Update progress callback parameter names in README

Signed-off-by: zhoucheng <zhoucheng133@outlook.com>

* formatting

Signed-off-by: zhoucheng <zhoucheng133@outlook.com>

* Fixed the misssing braces

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
Signed-off-by: zhoucheng <zhoucheng133@outlook.com>

* docs(readme): fix remaining destructuring syntax in examples

Signed-off-by: zhoucheng <zhoucheng133@outlook.com>

---------

Signed-off-by: zhoucheng <zhoucheng133@outlook.com>
Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
2026-05-15 18:14:17 +08:00
renovate[bot] 95c9291cee chore(deps): update dependency svelte to v5.55.7 [security] (#3422)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-15 10:03:55 +08:00
renovate[bot] 114e902537 chore(deps): update dependency rollup to v4.60.3 (#3358)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 15:16:49 +02:00
renovate[bot] 6972acdca6 chore(deps): update dependency @tauri-apps/cli to v2.11.1 (#3417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 14:47:31 +02:00
renovate[bot] 8bbc7a0d1d chore(deps): update rust crate tauri to v2.11.1 [security] (#3413)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-06 21:23:19 +02:00
dependabot[bot] 2d013294ef chore(deps): bump openssl from 0.10.78 to 0.10.79 in /plugins/updater/tests/updater-migration/v1-app (#3412)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-06 21:20:11 +02:00
101 changed files with 2079 additions and 1548 deletions
+10
View File
@@ -1,7 +1,17 @@
[advisories]
ignore = [
# rsa Marvin Attack
"RUSTSEC-2023-0071",
# time crate can't be updated in the repo because of MSRV, users are unaffected
"RUSTSEC-2026-0009",
# libflate crates can't be updated in the repo because of MSRV, users are unaffected
"RUSTSEC-2026-0105",
# quick-xml, need an update in plist, can't be updated in the repo because of MSRV, users are unaffected
# Also needs on update in wayland-scanner > arboard > tauri-plugin-clipboard-manager
"RUSTSEC-2026-0194",
# quick-xml, need an update in plist, can't be updated in the repo because of MSRV, users are unaffected
# Also needs on update in wayland-scanner > arboard > tauri-plugin-clipboard-manager
"RUSTSEC-2026-0195",
# quinn-proto, can't be updated in the repo because of MSRV, users are unaffected
"RUSTSEC-2026-0185",
]
+6
View File
@@ -0,0 +1,6 @@
---
"deep-link": patch
"deep-link-js": patch
---
adds proper Platform-specific sections to the docs for `register`, `unregister`, `isRegistered`, and `onOpenUrl`
+6
View File
@@ -0,0 +1,6 @@
---
"fs": patch
"fs-js": patch
---
Fixed `deny-webview-data` has no effect
+6
View File
@@ -0,0 +1,6 @@
---
nfc: patch
nfc-js: patch
---
Expect a `NFCTag` as input and return a `ScanResponse` in the `scan` function.
+6
View File
@@ -0,0 +1,6 @@
---
"updater": minor
"updater-js": minor
---
On Windows, add a new option `restartAfterInstall`/`restart_after_install` to install an update without the installer re-launching the app
+4 -5
View File
@@ -33,20 +33,19 @@ jobs:
audit-js:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Cache pnpm modules
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v6
with:
version: 10.x.x
run_install: true
- name: audit
run: pnpm audit
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
audit-rust:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: rustsec/audit-check@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: check change files end with .md
run: |
+5 -6
View File
@@ -21,7 +21,7 @@ jobs:
outputs:
packages: ${{ steps.filter.outputs.changes }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v2
id: filter
with:
@@ -153,21 +153,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Cache pnpm modules
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v6
with:
version: 10.x.x
run_install: true
- name: build api
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v2
id: filter
with:
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0 # required for use of git history
- name: covector status
@@ -28,18 +28,17 @@ jobs:
successfulPublish: ${{ steps.covector.outputs.successfulPublish }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0 # required for use of git history
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 'lts/*'
registry-url: 'https://registry.npmjs.org'
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v6
with:
version: 10.x.x
run_install: true
- name: cargo login
@@ -74,7 +73,7 @@ jobs:
- name: Create Pull Request With Versions Bumped
id: cpr
uses: peter-evans/create-pull-request@dd2324fc52d5d43c699a5636bcf19fceaa70c284 # 7.0.7
uses: peter-evans/create-pull-request@v8.1.1
if: steps.covector.outputs.commandRan == 'version'
with:
title: 'Publish New Versions (${{ github.ref_name }})'
+6 -7
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: install Rust stable and rustfmt
uses: dtolnay/rust-toolchain@stable
@@ -25,20 +25,19 @@ jobs:
prettier:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Cache pnpm modules
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v6
with:
version: 10.x.x
run_install: true
- run: pnpm format:check
@@ -46,7 +45,7 @@ jobs:
name: taplo (.toml files)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
platform: [ubuntu-22.04, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
+4 -5
View File
@@ -36,20 +36,19 @@ jobs:
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Cache pnpm modules
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v6
with:
version: 10.x.x
run_install: true
- name: eslint
run: pnpm lint
+2 -2
View File
@@ -36,7 +36,7 @@ jobs:
outputs:
packages: ${{ steps.filter.outputs.changes }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v2
id: filter
with:
@@ -136,7 +136,7 @@ jobs:
package: ${{ fromJSON(needs.changes.outputs.packages) }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: install webkit2gtk
run: |
+4 -5
View File
@@ -19,26 +19,25 @@ jobs:
sync-to-mirrors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Fetch git tags
run: git fetch origin 'refs/tags/*:refs/tags/*'
- name: Cache pnpm modules
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-
- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v6
with:
version: 10.x.x
run_install: true
- name: Build packages
+2 -2
View File
@@ -38,7 +38,7 @@ jobs:
outputs:
packages: ${{ steps.filter.outputs.changes }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v2
id: filter
with:
@@ -225,7 +225,7 @@ jobs:
runs-on: ${{ matrix.platform.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: install webkit2gtk
if: contains(matrix.platform.target, 'unknown-linux')
Generated
+214 -175
View File
@@ -201,13 +201,13 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.98"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
[[package]]
name = "api"
version = "2.0.43"
version = "2.0.46"
dependencies = [
"log",
"serde",
@@ -300,7 +300,7 @@ dependencies = [
"objc2-foundation 0.3.0",
"parking_lot",
"percent-encoding",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
"wl-clipboard-rs",
"x11rb",
]
@@ -332,7 +332,7 @@ dependencies = [
"enumflags2",
"futures-channel",
"futures-util",
"rand 0.9.3",
"rand 0.9.4",
"raw-window-handle",
"serde",
"serde_repr",
@@ -772,17 +772,6 @@ version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "byte-unit"
version = "5.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174"
dependencies = [
"rust_decimal",
"serde",
"utf8-width",
]
[[package]]
name = "bytecheck"
version = "0.6.12"
@@ -983,7 +972,7 @@ dependencies = [
"iana-time-zone",
"num-traits",
"serde",
"windows-link",
"windows-link 0.1.1",
]
[[package]]
@@ -1052,7 +1041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [
"lazy_static",
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -1308,7 +1297,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
"phf 0.13.1",
"phf",
"smallvec",
]
@@ -1873,7 +1862,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -2470,9 +2459,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "global-hotkey"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7"
checksum = "8c386b0a4a70cb2d39fffd74480f985b6f0bfbcb934b6a6b6b7e630e448f242e"
dependencies = [
"crossbeam-channel",
"keyboard-types",
@@ -2812,7 +2801,7 @@ dependencies = [
"tokio",
"tower-service",
"tracing",
"windows-registry",
"windows-registry 0.5.1",
]
[[package]]
@@ -2827,7 +2816,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
"windows-core 0.61.0",
]
[[package]]
@@ -3093,7 +3082,7 @@ dependencies = [
"iterator-sorted",
"k256",
"pbkdf2",
"rand 0.8.5",
"rand 0.8.6",
"scrypt",
"serde",
"sha2",
@@ -3474,9 +3463,6 @@ name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
dependencies = [
"value-bag",
]
[[package]]
name = "lru-slab"
@@ -3607,7 +3593,7 @@ dependencies = [
"hyper",
"hyper-util",
"log",
"rand 0.9.3",
"rand 0.9.4",
"regex",
"serde_json",
"serde_urlencoded",
@@ -3784,7 +3770,7 @@ dependencies = [
"num-integer",
"num-iter",
"num-traits",
"rand 0.8.5",
"rand 0.8.6",
"smallvec",
"zeroize",
]
@@ -4351,24 +4337,14 @@ dependencies = [
"indexmap 2.11.4",
]
[[package]]
name = "phf"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_macros 0.11.3",
"phf_shared 0.11.3",
]
[[package]]
name = "phf"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
dependencies = [
"phf_macros 0.13.1",
"phf_shared 0.13.1",
"phf_macros",
"phf_shared",
"serde",
]
@@ -4378,18 +4354,8 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
dependencies = [
"phf_generator 0.13.1",
"phf_shared 0.13.1",
]
[[package]]
name = "phf_generator"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared 0.11.3",
"rand 0.8.5",
"phf_generator",
"phf_shared",
]
[[package]]
@@ -4399,20 +4365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
dependencies = [
"fastrand",
"phf_shared 0.13.1",
]
[[package]]
name = "phf_macros"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
dependencies = [
"phf_generator 0.11.3",
"phf_shared 0.11.3",
"proc-macro2",
"quote",
"syn 2.0.100",
"phf_shared",
]
[[package]]
@@ -4421,22 +4374,13 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
dependencies = [
"phf_generator 0.13.1",
"phf_shared 0.13.1",
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher",
]
[[package]]
name = "phf_shared"
version = "0.13.1"
@@ -4750,7 +4694,7 @@ dependencies = [
"bytes",
"getrandom 0.3.2",
"lru-slab",
"rand 0.9.3",
"rand 0.9.4",
"ring",
"rustc-hash",
"rustls",
@@ -4773,7 +4717,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -4799,9 +4743,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [
"libc",
"rand_chacha 0.3.1",
@@ -4810,9 +4754,9 @@ dependencies = [
[[package]]
name = "rand"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
@@ -5186,7 +5130,7 @@ dependencies = [
"borsh",
"bytes",
"num-traits",
"rand 0.8.5",
"rand 0.8.6",
"rkyv",
"serde",
"serde_json",
@@ -5223,7 +5167,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -5481,7 +5425,7 @@ dependencies = [
"derive_more",
"log",
"new_debug_unreachable",
"phf 0.13.1",
"phf",
"phf_codegen",
"precomputed-hash",
"rustc-hash",
@@ -5995,7 +5939,7 @@ dependencies = [
"memchr",
"once_cell",
"percent-encoding",
"rand 0.8.5",
"rand 0.8.6",
"rsa",
"rust_decimal",
"serde",
@@ -6036,7 +5980,7 @@ dependencies = [
"md-5",
"memchr",
"once_cell",
"rand 0.8.5",
"rand 0.8.6",
"rust_decimal",
"serde",
"serde_json",
@@ -6097,7 +6041,7 @@ checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901"
dependencies = [
"new_debug_unreachable",
"parking_lot",
"phf_shared 0.13.1",
"phf_shared",
"precomputed-hash",
]
@@ -6107,8 +6051,8 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69"
dependencies = [
"phf_generator 0.13.1",
"phf_shared 0.13.1",
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
]
@@ -6147,7 +6091,7 @@ dependencies = [
"libsodium-sys-stable",
"log",
"nix 0.24.3",
"rand 0.8.5",
"rand 0.8.6",
"serde",
"thiserror 1.0.69",
"windows 0.36.1",
@@ -6160,7 +6104,7 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8300214898af5e153e7f66e49dbd1c6a21585f2d592d9f24f58b969792475ed6"
dependencies = [
"rand 0.8.5",
"rand 0.8.6",
"stronghold-derive",
]
@@ -6324,7 +6268,7 @@ dependencies = [
"unicode-segmentation",
"url",
"windows 0.61.1",
"windows-core",
"windows-core 0.61.0",
"windows-version",
"x11-dl",
]
@@ -6348,9 +6292,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tar"
version = "0.4.45"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973"
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [
"filetime",
"libc",
@@ -6365,9 +6309,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tauri"
version = "2.11.0"
version = "2.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d059f2527558d9dba6f186dec4772610e1aecfd3f94002397613e7e648752b66"
checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5"
dependencies = [
"anyhow",
"bytes",
@@ -6420,9 +6364,9 @@ dependencies = [
[[package]]
name = "tauri-build"
version = "2.6.0"
version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be9aa8c59a894f76c29a002501c589de5eb4987a5913d62a6e0a47f320901988"
checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632"
dependencies = [
"anyhow",
"cargo_toml",
@@ -6443,9 +6387,9 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.6.0"
version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e4e8230d565106aa19dfbaa01a7ed01abf78047fe0577a83377224bd1bf20e"
checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5"
dependencies = [
"base64 0.22.1",
"ico",
@@ -6469,9 +6413,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.6.0"
version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc8de2cddbbc33dbdf4c84f170121886595efdbcc9cb4b3d76342b79d082cedc"
checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -6511,7 +6455,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-barcode-scanner"
version = "2.4.4"
version = "2.4.5"
dependencies = [
"log",
"serde",
@@ -6575,13 +6519,13 @@ dependencies = [
"thiserror 2.0.12",
"tracing",
"url",
"windows-registry",
"windows-result",
"windows-registry 0.6.1",
"windows-result 0.4.1",
]
[[package]]
name = "tauri-plugin-dialog"
version = "2.7.1"
version = "2.7.2"
dependencies = [
"log",
"raw-window-handle",
@@ -6634,7 +6578,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-global-shortcut"
version = "2.3.1"
version = "2.3.2"
dependencies = [
"global-hotkey",
"log",
@@ -6696,10 +6640,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-log"
version = "2.8.0"
version = "2.9.0"
dependencies = [
"android_logger",
"byte-unit",
"fern",
"log",
"objc2 0.6.4",
@@ -6734,7 +6677,7 @@ version = "2.3.3"
dependencies = [
"log",
"notify-rust",
"rand 0.9.3",
"rand 0.9.4",
"serde",
"serde_json",
"serde_repr",
@@ -6763,7 +6706,7 @@ dependencies = [
"tauri-plugin",
"thiserror 2.0.12",
"url",
"windows 0.61.1",
"windows 0.62.2",
"zbus",
]
@@ -6799,7 +6742,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-positioner"
version = "2.3.1"
version = "2.3.3"
dependencies = [
"log",
"serde",
@@ -6839,7 +6782,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-single-instance"
version = "2.4.2"
version = "2.4.3"
dependencies = [
"semver",
"serde",
@@ -6847,8 +6790,9 @@ dependencies = [
"tauri",
"tauri-plugin-deep-link",
"thiserror 2.0.12",
"tokio",
"tracing",
"windows-sys 0.60.2",
"windows-sys 0.61.2",
"zbus",
]
@@ -6873,7 +6817,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-store"
version = "2.4.3"
version = "2.4.4"
dependencies = [
"dunce",
"serde",
@@ -6893,7 +6837,7 @@ dependencies = [
"iota-crypto",
"iota_stronghold",
"log",
"rand 0.9.3",
"rand 0.9.4",
"rand_chacha 0.9.0",
"rand_core 0.9.3",
"rust-argon2 2.1.0",
@@ -6933,7 +6877,7 @@ dependencies = [
"time",
"tokio",
"url",
"windows-sys 0.60.2",
"windows-sys 0.61.2",
"zip 4.0.0",
]
@@ -6962,7 +6906,7 @@ dependencies = [
"futures-util",
"http",
"log",
"rand 0.9.3",
"rand 0.9.4",
"rustls",
"serde",
"serde_json",
@@ -6988,9 +6932,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.11.0"
version = "2.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e42bbcb76237351fbaa02f08d808c537dc12eb5a6eabbf3e517b50056334d95"
checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8"
dependencies = [
"cookie",
"dpi",
@@ -7013,9 +6957,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.11.0"
version = "2.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cadb13dad0c681e1e0a2c49ae488f0e2906ded3d57e7a0017f4aaf46e387117"
checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f"
dependencies = [
"gtk",
"http",
@@ -7039,9 +6983,9 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "2.9.0"
version = "2.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55f61d2bf7188fbcf2b0ed095b67a6bc498f713c939314bb19eb700118a573b7"
checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887"
dependencies = [
"aes-gcm",
"anyhow",
@@ -7056,7 +7000,7 @@ dependencies = [
"json-patch",
"log",
"memchr",
"phf 0.11.3",
"phf",
"plist",
"proc-macro2",
"quote",
@@ -7089,11 +7033,10 @@ dependencies = [
[[package]]
name = "tauri-winrt-notification"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9"
checksum = "9ed071c670382e85fc2f48ae706492d8c338f4f89bf72520d32f8abfe880aade"
dependencies = [
"quick-xml 0.37.4",
"thiserror 2.0.12",
"windows 0.61.1",
"windows-version",
@@ -7524,9 +7467,9 @@ dependencies = [
[[package]]
name = "tray-icon"
version = "0.23.1"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773"
checksum = "65ba1e5f6b9ef9fd87e21b9c6f351554dbd717960089168fcfdef854686961dc"
dependencies = [
"crossbeam-channel",
"dirs 6.0.0",
@@ -7581,7 +7524,7 @@ dependencies = [
"httparse",
"log",
"native-tls",
"rand 0.9.3",
"rand 0.9.4",
"rustls",
"rustls-pki-types",
"sha1",
@@ -7774,12 +7717,6 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
[[package]]
name = "utf8-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
[[package]]
name = "utf8_iter"
version = "1.0.4"
@@ -7804,12 +7741,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "value-bag"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5"
[[package]]
name = "vcpkg"
version = "0.2.15"
@@ -8080,7 +8011,7 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538"
dependencies = [
"phf 0.13.1",
"phf",
"phf_codegen",
"string_cache",
"string_cache_codegen",
@@ -8180,7 +8111,7 @@ dependencies = [
"webview2-com-macros",
"webview2-com-sys",
"windows 0.61.1",
"windows-core",
"windows-core 0.61.0",
"windows-implement",
"windows-interface",
]
@@ -8204,7 +8135,7 @@ checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c"
dependencies = [
"thiserror 2.0.12",
"windows 0.61.1",
"windows-core",
"windows-core 0.61.0",
]
[[package]]
@@ -8255,7 +8186,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -8298,11 +8229,23 @@ version = "0.61.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
dependencies = [
"windows-collections",
"windows-core",
"windows-future",
"windows-link",
"windows-numerics",
"windows-collections 0.2.0",
"windows-core 0.61.0",
"windows-future 0.2.0",
"windows-link 0.1.1",
"windows-numerics 0.2.0",
]
[[package]]
name = "windows"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
dependencies = [
"windows-collections 0.3.2",
"windows-core 0.62.2",
"windows-future 0.3.2",
"windows-numerics 0.3.1",
]
[[package]]
@@ -8311,7 +8254,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core",
"windows-core 0.61.0",
]
[[package]]
name = "windows-collections"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
dependencies = [
"windows-core 0.62.2",
]
[[package]]
@@ -8322,9 +8274,22 @@ checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
"windows-link 0.1.1",
"windows-result 0.3.2",
"windows-strings 0.4.0",
]
[[package]]
name = "windows-core"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link 0.2.1",
"windows-result 0.4.1",
"windows-strings 0.5.1",
]
[[package]]
@@ -8333,15 +8298,26 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
dependencies = [
"windows-core",
"windows-link",
"windows-core 0.61.0",
"windows-link 0.1.1",
]
[[package]]
name = "windows-future"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
dependencies = [
"windows-core 0.62.2",
"windows-link 0.2.1",
"windows-threading",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
@@ -8350,9 +8326,9 @@ dependencies = [
[[package]]
name = "windows-interface"
version = "0.59.1"
version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
@@ -8365,14 +8341,30 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-numerics"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core",
"windows-link",
"windows-core 0.61.0",
"windows-link 0.1.1",
]
[[package]]
name = "windows-numerics"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
dependencies = [
"windows-core 0.62.2",
"windows-link 0.2.1",
]
[[package]]
@@ -8381,9 +8373,20 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1da3e436dc7653dfdf3da67332e22bff09bb0e28b0239e1624499c7830842e"
dependencies = [
"windows-link",
"windows-result",
"windows-strings",
"windows-link 0.1.1",
"windows-result 0.3.2",
"windows-strings 0.4.0",
]
[[package]]
name = "windows-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
dependencies = [
"windows-link 0.2.1",
"windows-result 0.4.1",
"windows-strings 0.5.1",
]
[[package]]
@@ -8392,7 +8395,16 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
dependencies = [
"windows-link",
"windows-link 0.1.1",
]
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link 0.2.1",
]
[[package]]
@@ -8401,7 +8413,16 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
"windows-link 0.1.1",
]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link 0.2.1",
]
[[package]]
@@ -8449,6 +8470,15 @@ dependencies = [
"windows-targets 0.53.2",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
@@ -8511,13 +8541,22 @@ dependencies = [
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows-threading"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows-version"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c"
dependencies = [
"windows-link",
"windows-link 0.1.1",
]
[[package]]
@@ -8852,7 +8891,7 @@ dependencies = [
"webkit2gtk-sys",
"webview2-com",
"windows 0.61.1",
"windows-core",
"windows-core 0.61.0",
"windows-version",
"x11-dl",
]
+1 -1
View File
@@ -11,7 +11,7 @@ resolver = "2"
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
tracing = "0.1"
log = "0.4"
log = "0.4.21"
tauri = { version = "2.10", default-features = false }
tauri-build = "2.5"
tauri-plugin = "2.5"
+20
View File
@@ -1,5 +1,25 @@
# Changelog
## \[2.0.42]
### Dependencies
- Upgraded to `dialog-js@2.7.2`
- Upgraded to `store-js@2.4.4`
## \[2.0.41]
### Dependencies
- Upgraded to `log-js@2.9.0`
## \[2.0.40]
### Dependencies
- Upgraded to `barcode-scanner-js@2.4.5`
- Upgraded to `global-shortcut-js@2.3.2`
## \[2.0.39]
### Dependencies
+3 -2
View File
@@ -1,12 +1,13 @@
<!doctype html>
<html lang="en" theme="dark">
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=0"
/>
<title>Svelte + Vite App</title>
<meta name="color-scheme" content="dark light" />
<title>API Example App</title>
</head>
<body>
+21 -21
View File
@@ -1,7 +1,7 @@
{
"name": "api",
"private": true,
"version": "2.0.39",
"version": "2.0.42",
"type": "module",
"scripts": {
"dev": "vite --clearScreen false",
@@ -11,32 +11,32 @@
},
"dependencies": {
"@tauri-apps/api": "^2.11.0",
"@tauri-apps/plugin-barcode-scanner": "^2.4.4",
"@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.7.1",
"@tauri-apps/plugin-fs": "^2.5.1",
"@tauri-apps/plugin-geolocation": "^2.3.2",
"@tauri-apps/plugin-global-shortcut": "^2.3.1",
"@tauri-apps/plugin-haptics": "^2.3.2",
"@tauri-apps/plugin-http": "^2.5.9",
"@tauri-apps/plugin-nfc": "^2.3.5",
"@tauri-apps/plugin-notification": "^2.3.3",
"@tauri-apps/plugin-opener": "^2.5.4",
"@tauri-apps/plugin-os": "^2.3.2",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-shell": "^2.3.5",
"@tauri-apps/plugin-store": "^2.4.3",
"@tauri-apps/plugin-updater": "^2.10.1",
"@tauri-apps/plugin-upload": "^2.4.0",
"@tauri-apps/plugin-barcode-scanner": "workspace:*",
"@tauri-apps/plugin-biometric": "workspace:*",
"@tauri-apps/plugin-cli": "workspace:*",
"@tauri-apps/plugin-clipboard-manager": "workspace:*",
"@tauri-apps/plugin-dialog": "2",
"@tauri-apps/plugin-fs": "workspace:*",
"@tauri-apps/plugin-geolocation": "workspace:*",
"@tauri-apps/plugin-global-shortcut": "workspace:*",
"@tauri-apps/plugin-haptics": "workspace:*",
"@tauri-apps/plugin-http": "workspace:*",
"@tauri-apps/plugin-nfc": "workspace:*",
"@tauri-apps/plugin-notification": "workspace:*",
"@tauri-apps/plugin-opener": "workspace:*",
"@tauri-apps/plugin-os": "workspace:*",
"@tauri-apps/plugin-process": "workspace:*",
"@tauri-apps/plugin-shell": "workspace:*",
"@tauri-apps/plugin-store": "2",
"@tauri-apps/plugin-updater": "workspace:*",
"@tauri-apps/plugin-upload": "workspace:*",
"@zerodevx/svelte-json-view": "1.0.11"
},
"devDependencies": {
"@iconify-json/codicon": "^1.2.49",
"@iconify-json/ph": "^1.2.2",
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tauri-apps/cli": "2.11.0",
"@tauri-apps/cli": "2.11.4",
"@unocss/extractor-svelte": "^66.6.7",
"svelte": "^5.54.0",
"unocss": "^66.6.7",
+20
View File
@@ -1,5 +1,25 @@
# Changelog
## \[2.0.46]
### Dependencies
- Upgraded to `dialog@2.7.2`
- Upgraded to `store@2.4.4`
## \[2.0.45]
### Dependencies
- Upgraded to `log@2.9.0`
## \[2.0.44]
### Dependencies
- Upgraded to `barcode-scanner@2.4.5`
- Upgraded to `global-shortcut@2.3.2`
## \[2.0.43]
### Dependencies
+6 -6
View File
@@ -1,7 +1,7 @@
[package]
name = "api"
publish = false
version = "2.0.43"
version = "2.0.46"
description = "An example Tauri Application showcasing the api"
edition = "2021"
rust-version = { workspace = true }
@@ -20,12 +20,12 @@ serde = { workspace = true }
tiny_http = "0.12"
time = "0.3"
log = { workspace = true }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.8.0" }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.9.0" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.5.1", features = [
"watch",
] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.2" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.7.1" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.7.2" }
tauri-plugin-http = { path = "../../../plugins/http", features = [
"multipart",
"cookies",
@@ -37,7 +37,7 @@ 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.4" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.5" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.4.3" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.4.4" }
tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" }
[dependencies.tauri]
@@ -56,12 +56,12 @@ 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.1" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.3.1" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.3.2" }
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.10.1" }
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.4" }
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.4.5" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.5" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.2" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.2" }
@@ -15,10 +15,10 @@
]
},
"core:default",
"core:app:allow-set-app-theme",
"fs:default",
"core:window:allow-minimize",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-toggle-maximize",
"core:window:allow-close",
"core:window:allow-start-dragging",
"notification:default",
@@ -1,3 +1,11 @@
package com.tauri.api
class MainActivity : TauriActivity()
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
class MainActivity : TauriActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
}
}
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.api" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<style name="Theme.api" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.api" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<style name="Theme.api" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
@@ -21,7 +21,23 @@ open class BuildTask : DefaultTask() {
runTauriCli(executable)
} catch (e: Exception) {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
runTauriCli("$executable.cmd")
// Try different Windows-specific extensions
val fallbacks = listOf(
"$executable.exe",
"$executable.cmd",
"$executable.bat",
)
var lastException: Exception = e
for (fallback in fallbacks) {
try {
runTauriCli(fallback)
return
} catch (fallbackException: Exception) {
lastException = fallbackException
}
}
throw lastException
} else {
throw e;
}
@@ -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
+5
View File
@@ -77,7 +77,9 @@
},
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDE5QzMxNjYwNTM5OEUwNTgKUldSWTRKaFRZQmJER1h4d1ZMYVA3dnluSjdpN2RmMldJR09hUFFlZDY0SlFqckkvRUJhZDJVZXAK",
"dangerousInsecureTransportProtocol": true,
"endpoints": [
"http://localhost:5173/updater-test/updater.json",
"https://tauri-update-server.vercel.app/update/{{target}}/{{current_version}}"
]
}
@@ -99,6 +101,9 @@
"localePath": "locales/pt-BR.wxl"
}
}
},
"nsis": {
"compression": "none"
}
},
"iOS": {
+128 -195
View File
@@ -1,7 +1,16 @@
<script>
<script module lang="ts">
export type ViewProps = {
onMessage: (value: unknown) => void
}
</script>
<script lang="ts">
import { onMount, tick } from 'svelte'
import { writable } from 'svelte/store'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { MediaQuery } from 'svelte/reactivity'
import { getCurrentWindow, type Theme } from '@tauri-apps/api/window'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { setTheme } from '@tauri-apps/api/app'
import * as os from '@tauri-apps/plugin-os'
import Welcome from './views/Welcome.svelte'
@@ -23,11 +32,10 @@
import Biometric from './views/Biometric.svelte'
import Geolocation from './views/Geolocation.svelte'
import Haptics from './views/Haptics.svelte'
import { onMount, tick } from 'svelte'
import { ask } from '@tauri-apps/plugin-dialog'
import Nfc from './views/Nfc.svelte'
import TitleBar from './lib/TitleBar.svelte'
const appWindow = getCurrentWindow()
if (appWindow.label !== 'main') {
@@ -148,114 +156,98 @@
component: Haptics,
icon: 'i-ph-vibrate'
}
]
].filter(Boolean) as {
label: string
component: typeof Haptics
icon: string
}[]
let selected = $state.raw(views[0])
let selected = views[0]
function select(view) {
selected = view
}
// dark/light themes
const preferDark = new MediaQuery('prefers-color-scheme: dark')
let theme = $state<Theme | 'auto'>(
(localStorage.getItem('theme') as Theme | null) || 'auto'
)
// Window controls
let isWindowMaximized
onMount(async () => {
isWindowMaximized = await appWindow.isMaximized()
appWindow.onResized(async () => {
isWindowMaximized = await appWindow.isMaximized()
})
})
function minimize() {
appWindow.minimize()
}
async function toggleMaximize() {
;(await appWindow.isMaximized())
? appWindow.unmaximize()
: appWindow.maximize()
}
let confirmed_close = false
async function close() {
if (!confirmed_close) {
confirmed_close = await ask(
'Are you sure that you want to close this window?',
{
title: 'Tauri API'
}
)
if (confirmed_close) {
appWindow.close()
}
async function switchTheme() {
switch (theme) {
case 'dark':
theme = 'light'
break
case 'light':
theme = 'auto'
break
case 'auto':
theme = 'dark'
break
}
applyTheme()
}
// dark/light
let isDark
onMount(() => {
isDark = localStorage && localStorage.getItem('theme') == 'dark'
applyTheme(isDark)
function applyTheme() {
const isDark = theme === 'auto' ? preferDark.current : theme === 'dark'
if (isDark) {
document.documentElement.classList.add('dark')
document.documentElement.classList.remove('light')
} else {
document.documentElement.classList.remove('dark')
document.documentElement.classList.add('light')
}
setTheme(theme === 'auto' ? null : theme)
localStorage.setItem('theme', theme)
}
$effect(() => {
applyTheme()
})
function applyTheme(isDark) {
const html = document.querySelector('html')
isDark ? html.classList.add('dark') : html.classList.remove('dark')
localStorage && localStorage.setItem('theme', isDark ? 'dark' : '')
}
function toggleDark() {
isDark = !isDark
applyTheme(isDark)
}
// Console
let messages = writable([])
let consoleTextEl
async function onMessage(value) {
const messages = writable<string[]>([])
let consoleTextEl: HTMLDivElement
// this function is renders HTML without sanitizing it so it's insecure
// we only use it with our own input data
async function insecureRenderHtml(html: string) {
messages.update((r) => [
...r,
{
html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
(typeof value === 'string' ? value : JSON.stringify(value, null, 1)) +
'</pre>'
}
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ${html}</pre>`
])
await tick()
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight
consoleTextEl.scrollTop = consoleTextEl.scrollHeight
}
// this function renders HTML without sanitizing it so it's insecure
// we only use it with our own input data
async function insecureRenderHtml(html) {
messages.update((r) => [
...r,
{
html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
html +
'</pre>'
}
])
await tick()
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight
async function onMessage(value: unknown) {
const valueStr =
typeof value === 'string'
? value
: JSON.stringify(
value instanceof ArrayBuffer
? Array.from(new Uint8Array(value))
: value,
null,
1
)
insecureRenderHtml(valueStr)
}
function clear() {
messages.update(() => [])
}
let consoleEl, consoleH, cStartY
let minConsoleHeight = 50
function startResizingConsole(e) {
let consoleEl: HTMLDivElement
let consoleH = 0
let cStartY = 0
const minConsoleHeight = 50
function startResizingConsole(e: MouseEvent) {
cStartY = e.clientY
const styles = window.getComputedStyle(consoleEl)
consoleH = parseInt(styles.height, 10)
const moveHandler = (e) => {
const moveHandler = (e: MouseEvent) => {
const dy = e.clientY - cStartY
const newH = consoleH - dy
consoleEl.style.height = `${
newH < minConsoleHeight ? minConsoleHeight : newH
}px`
consoleEl.style.height = `${newH < minConsoleHeight ? minConsoleHeight : newH}px`
}
const upHandler = () => {
document.removeEventListener('mouseup', upHandler)
@@ -265,21 +257,19 @@
document.addEventListener('mousemove', moveHandler)
}
let isWindows
onMount(async () => {
isWindows = (await os.platform()) === 'windows'
})
let isWindows = os.platform() === 'windows'
// mobile
let isSideBarOpen = false
let sidebar
let sidebarToggle
let isSideBarOpen = $state(false)
let sidebar: HTMLElement
let sidebarToggle: HTMLElement
let isDraggingSideBar = false
let draggingStartPosX = 0
let draggingEndPosX = 0
const clamp = (min, num, max) => Math.min(Math.max(num, min), max)
const clamp = (min: number, num: number, max: number) =>
Math.min(Math.max(num, min), max)
function toggleSidebar(sidebar, isSideBarOpen) {
function toggleSidebar() {
sidebar.style.setProperty(
'--translate-x',
`${isSideBarOpen ? '0' : '-18.75'}rem`
@@ -287,10 +277,9 @@
}
onMount(() => {
sidebar = document.querySelector('#sidebar')
sidebarToggle = document.querySelector('#sidebarToggle')
document.addEventListener('click', (e) => {
if (!(e.target instanceof Node)) return
if (sidebarToggle.contains(e.target)) {
isSideBarOpen = !isSideBarOpen
} else if (isSideBarOpen && !sidebar.contains(e.target)) {
@@ -299,6 +288,7 @@
})
document.addEventListener('touchstart', (e) => {
if (!(e.target instanceof Node)) return
if (sidebarToggle.contains(e.target)) return
const x = e.touches[0].clientX
@@ -330,76 +320,22 @@
})
})
$: {
const sidebar = document.querySelector('#sidebar')
if (sidebar) {
toggleSidebar(sidebar, isSideBarOpen)
}
}
$effect(() => {
toggleSidebar()
})
</script>
<!-- custom titlebar for Windows -->
{#if isWindows}
<div
class="w-screen select-none h-8 pl-2 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"
data-tauri-drag-region
>
<span class="lt-sm:pl-10 text-darkPrimaryText">Tauri API Validation</span>
<span
class="
h-100%
children:h-100% children:w-12 children:inline-flex
children:items-center children:justify-center"
>
<button
aria-label="Toggle dark mode"
title={isDark ? 'Switch to Light mode' : 'Switch to Dark mode'}
class="bg-inherit border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
on:click={toggleDark}
>
{#if isDark}
<div class="i-ph-sun"></div>
{:else}
<div class="i-ph-moon"></div>
{/if}
</button>
<button
aria-label="Minimize window"
title="Minimize"
class="bg-inherit border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
on:click={minimize}
>
<div class="i-codicon-chrome-minimize"></div>
</button>
<button
aria-label="Maximize window"
title={isWindowMaximized ? 'Restore' : 'Maximize'}
class="bg-inherit border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
on:click={toggleMaximize}
>
{#if isWindowMaximized}
<div class="i-codicon-chrome-restore"></div>
{:else}
<div class="i-codicon-chrome-maximize"></div>
{/if}
</button>
<button
aria-label="Close window"
title="Close"
class="bg-inherit border-none hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText"
on:click={close}
>
<div class="i-codicon-chrome-close"></div>
</button>
</span>
</div>
<TitleBar {appWindow} {theme} {switchTheme} />
{/if}
<!-- Sidebar toggle, only visible on small screens -->
<div
id="sidebarToggle"
class="z-2000 sidebar-toggle hidden lt-sm:flex justify-center absolute items-center w-8 h-8 rd-8
bg-accent dark:bg-darkAccent active:bg-accentDark dark:active:bg-darkAccentDark"
bind:this={sidebarToggle}
class="z-2000 hidden lt-sm:flex justify-center absolute items-center w-8 h-8 rd-8
bg-accent dark:bg-darkAccent active:bg-accentDark dark:active:bg-darkAccentDark text-accentText dark:text-darkAccentText"
>
{#if isSideBarOpen}
<span class="i-codicon-close animate-duration-300ms animate-fade-in"></span>
@@ -409,10 +345,11 @@
</div>
<div
class="flex h-screen w-screen overflow-hidden children-pt8 children-pb-2 text-primaryText dark:text-darkPrimaryText"
class="flex h-screen w-screen overflow-hidden text-primaryText dark:text-darkPrimaryText"
>
<aside
id="sidebar"
bind:this={sidebar}
class="lt-sm:h-screen lt-sm:shadow-lg lt-sm:shadow lt-sm:transition-transform lt-sm:absolute lt-sm:z-1999
bg-darkPrimaryLighter transition-colors-250 overflow-hidden grid select-none px-2"
>
@@ -420,13 +357,16 @@
<img class="p-7" src="tauri_logo.png" alt="Tauri logo" />
</a>
{#if !isWindows}
<a href="##" class="nv justify-between h-8" on:click={toggleDark}>
{#if isDark}
Switch to Light mode
<div class="i-ph-sun"></div>
{:else}
<a href="##" class="nv justify-between" onclick={switchTheme}>
{#if theme === 'auto'}
Switch to Dark mode
<div class="i-ph-circle-half-fill"></div>
{:else if theme === 'dark'}
Switch to Light mode
<div class="i-ph-moon"></div>
{:else if theme === 'light'}
Switch to Auto mode
<div class="i-ph-sun"></div>
{/if}
</a>
<br />
@@ -435,7 +375,7 @@
{/if}
<a
class="nv justify-between h-8"
class="nv justify-between"
target="_blank"
href="https://tauri.app/v1/guides/"
>
@@ -443,7 +383,7 @@
<span class="i-codicon-link-external"></span>
</a>
<a
class="nv justify-between h-8"
class="nv justify-between"
target="_blank"
href="https://github.com/tauri-apps/tauri"
>
@@ -451,7 +391,7 @@
<span class="i-codicon-link-external"></span>
</a>
<a
class="nv justify-between h-8"
class="nv justify-between"
target="_blank"
href="https://github.com/tauri-apps/tauri/tree/dev/examples/api"
>
@@ -461,23 +401,19 @@
<br />
<div class="bg-white/5 h-2px"></div>
<br />
<div
class="flex flex-col overflow-y-auto children-h-10 children-flex-none gap-1"
>
<div class="flex flex-col overflow-y-auto children-flex-none gap-1">
{#each views as view}
{#if view}
<a
href="##"
class="nv {selected === view ? 'nv_selected' : ''}"
on:click={() => {
select(view)
isSideBarOpen = false
}}
>
<div class="{view.icon} mr-2"></div>
<p>{view.label}</p></a
>
{/if}
<a
href="##"
class="nv {selected === view ? 'nv_selected' : ''}"
onclick={() => {
selected = view
isSideBarOpen = false
}}
>
<div class="{view.icon} mr-2"></div>
<p>{view.label}</p></a
>
{/each}
</div>
</aside>
@@ -489,11 +425,7 @@
<h1>{selected.label}</h1>
<div class="overflow-y-auto">
<div class="mr-2">
<svelte:component
this={selected.component}
{onMessage}
{insecureRenderHtml}
/>
<selected.component {onMessage} />
</div>
</div>
</div>
@@ -503,20 +435,21 @@
id="console"
class="select-none h-15rem grid grid-rows-[2px_2rem_1fr] gap-1 overflow-hidden"
>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
on:mousedown={startResizingConsole}
role="button"
tabindex="0"
onmousedown={startResizingConsole}
class="bg-black/20 h-2px cursor-ns-resize"
></div>
<div class="flex justify-between items-center px-2">
<p class="font-semibold">Console</p>
<button
aria-label="Clear Console"
class="cursor-pointer h-85% rd-1 p-1 flex justify-center items-center border-none bg-inherit
class="cursor-pointer h-85% rd-1 p-1 flex justify-center items-center border-none
hover:bg-hoverOverlay dark:hover:bg-darkHoverOverlay
active:bg-hoverOverlay/25 dark:active:bg-darkHoverOverlay/25
"
on:click={clear}
onclick={clear}
>
<div class="i-codicon-clear-all"></div>
</button>
@@ -525,8 +458,8 @@
bind:this={consoleTextEl}
class="px-2 overflow-y-auto all:font-mono code-block all:text-xs select-text mr-2"
>
{#each $messages as r}
{@html r.html}
{#each $messages as messageHtml}
{@html messageHtml}
{/each}
</div>
</div>
+32 -9
View File
@@ -1,3 +1,14 @@
:root {
line-height: 1.5;
}
:root.dark {
color-scheme: dark;
}
:root.light {
color-scheme: light;
}
*:not(h1, h2, h3, h4, h5, h6) {
margin: 0;
padding: 0;
@@ -8,6 +19,11 @@
font-family: 'Rubik', sans-serif;
}
button {
color: inherit;
background-color: inherit;
}
::-webkit-scrollbar {
width: 0.25rem;
height: 3px;
@@ -33,27 +49,34 @@ code.code-block {
width: 18.75rem;
}
@media screen and (max-width: 640px) {
@media screen and (width < 640px) {
#sidebar {
--translate-x: -18.75rem;
transform: translateX(var(--translate-x));
}
}
.sidebar-toggle {
margin-top: 0.5rem;
margin-left: 0.5rem;
#sidebar,
main {
padding-top: calc(env(safe-area-inset-top) + 2rem);
}
body {
overflow: hidden;
padding: env(safe-area-inset-top) env(safe-area-inset-right)
env(safe-area-inset-bottom) env(safe-area-inset-left);
#sidebar {
padding-left: calc(env(safe-area-inset-left) + 0.5rem);
}
main {
padding-right: env(safe-area-inset-right);
}
#sidebarToggle {
margin-top: calc(env(safe-area-inset-top) + 0.5rem);
margin-left: calc(env(safe-area-inset-left) + 0.5rem);
}
#sidebar,
#console {
padding-bottom: calc(env(safe-area-inset-bottom) + 24px);
padding-bottom: calc(env(safe-area-inset-bottom) + 1.5rem);
}
.transparent {
+103
View File
@@ -0,0 +1,103 @@
<script lang="ts">
import type { Theme, Window } from '@tauri-apps/api/window'
import { ask } from '@tauri-apps/plugin-dialog'
import { onMount } from 'svelte'
let {
appWindow,
switchTheme,
theme
}: { appWindow: Window; switchTheme: () => void; theme: Theme | 'auto' } =
$props()
// Window controls
let isWindowMaximized = $state(false)
onMount(async () => {
isWindowMaximized = await appWindow.isMaximized()
appWindow.onResized(async () => {
isWindowMaximized = await appWindow.isMaximized()
})
})
async function minimize() {
await appWindow.minimize()
}
async function toggleMaximize() {
await appWindow.toggleMaximize()
}
let confirmedClose = false
async function close() {
if (!confirmedClose) {
confirmedClose = await ask(
'Are you sure that you want to close this window?',
{
title: 'Tauri API'
}
)
if (confirmedClose) {
appWindow.close()
}
}
}
</script>
<div
class="w-screen select-none h-8 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"
data-tauri-drag-region
>
<span
class="h-100% pl-2 flex-1 flex items-center lt-sm:pl-10 lt-lg:text-darkPrimaryText [app-region:drag]"
>Tauri API Validation</span
>
<span
class="
h-100%
children:h-100% children:w-12 children:inline-flex
children:items-center children:justify-center"
>
<button
aria-label="Toggle dark mode"
title={theme ? 'Switch to Light mode' : 'Switch to Dark mode'}
class="border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
onclick={switchTheme}
>
{#if theme === 'auto'}
<div title="Switch to Dark mode" class="i-ph-circle-half-fill"></div>
{:else if theme === 'dark'}
<div title="Switch to Light mode" class="i-ph-moon"></div>
{:else if theme === 'light'}
<div title="Switch to Auto mode" class="i-ph-sun"></div>
{/if}
</button>
<button
aria-label="Minimize window"
title="Minimize"
class="border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
onclick={minimize}
>
<div class="i-codicon-chrome-minimize"></div>
</button>
<button
aria-label="Maximize window"
title={isWindowMaximized ? 'Restore' : 'Maximize'}
class="border-none hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
onclick={toggleMaximize}
>
{#if isWindowMaximized}
<div class="i-codicon-chrome-restore"></div>
{:else}
<div class="i-codicon-chrome-maximize"></div>
{/if}
</button>
<button
aria-label="Close window"
title="Close"
class="border-none hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText"
onclick={close}
>
<div class="i-codicon-chrome-close"></div>
</button>
</span>
</div>
+1 -2
View File
@@ -5,7 +5,6 @@
import { readFile } from '@tauri-apps/plugin-fs'
export let onMessage
export let insecureRenderHtml
let text = 'clipboard message'
function writeText() {
@@ -41,7 +40,7 @@
const image = await clipboard.readImage()
arrayBufferToBase64(await image.rgba(), function (base64) {
const src = 'data:image/png;base64,' + base64
insecureRenderHtml('<img src="' + src + '"></img>')
onMessage('<img src="' + src + '"></img>')
})
return
} catch (_) {}
+87 -77
View File
@@ -1,129 +1,138 @@
<script>
import { open, save, confirm, message } from "@tauri-apps/plugin-dialog";
import { readFile } from "@tauri-apps/plugin-fs";
<script lang="ts">
import {
open,
save,
confirm,
message,
type PickerMode,
type FileAccessMode
} from '@tauri-apps/plugin-dialog'
import { readFile } from '@tauri-apps/plugin-fs'
import type { ViewProps } from '../App.svelte'
export let onMessage;
export let insecureRenderHtml;
let defaultPath = null;
let filter = null;
let multiple = false;
let directory = false;
let pickerMode = "document";
let fileAccessMode = "scoped";
let { onMessage }: ViewProps = $props()
function arrayBufferToBase64(buffer, callback) {
let defaultPath: string | undefined = $state()
let filter = $state('')
let multiple = $state(false)
let directory = $state(false)
let pickerMode: PickerMode = $state('document')
let fileAccessMode: FileAccessMode = $state('scoped')
function arrayBufferToBase64(
buffer: ArrayBuffer,
callback: (base64: string) => void
) {
var blob = new Blob([buffer], {
type: "application/octet-binary",
});
var reader = new FileReader();
type: 'application/octet-binary'
})
var reader = new FileReader()
reader.onload = function (evt) {
var dataurl = evt.target.result;
callback(dataurl.substr(dataurl.indexOf(",") + 1));
};
reader.readAsDataURL(blob);
var dataurl = evt.target!.result as string
callback(dataurl.split(',')[1])
}
reader.readAsDataURL(blob)
}
async function prompt() {
confirm("Do you want to do something?")
.then((res) => onMessage(res ? "Yes" : "No"))
.catch(onMessage);
confirm('Do you want to do something?')
.then((res) => onMessage(res ? 'Yes' : 'No'))
.catch(onMessage)
}
async function promptCustom() {
confirm("Is Tauri awesome?", {
okLabel: "Absolutely",
cancelLabel: "Totally",
confirm('Is Tauri awesome?', {
okLabel: 'Absolutely',
cancelLabel: 'Totally'
})
.then((res) =>
onMessage(
res ? "Tauri is absolutely awesome" : "Tauri is totally awesome"
res ? 'Tauri is absolutely awesome' : 'Tauri is totally awesome'
)
)
.catch(onMessage);
.catch(onMessage)
}
async function msg() {
await message("Tauri is awesome!").then((res) => onMessage(res));
await message('Tauri is awesome!').then((res) => onMessage(res))
}
async function msgCustom(result) {
const buttons = { yes: "awesome", no: "amazing", cancel: "stunning" };
async function msgCustom() {
const buttons = { yes: 'awesome', no: 'amazing', cancel: 'stunning' }
await message(`Tauri is: `, { buttons })
.then((res) => onMessage(`Tauri is ${res}`))
.catch(onMessage);
.catch(onMessage)
}
async function openDialog() {
try {
var result = await open({
title: "My wonderful open dialog",
const result = await open({
title: 'My wonderful open dialog',
defaultPath,
filters: filter
? [
{
name: "Tauri Example",
extensions: filter.split(",").map((f) => f.trim()),
},
name: 'Tauri Example',
extensions: filter.split(',').map((f) => f.trim())
}
]
: [],
multiple,
directory,
pickerMode,
fileAccessMode,
fileAccessMode
})
if (Array.isArray(result)) {
onMessage(result);
onMessage(result)
} else if (result === null) {
onMessage('user cancelled the selection')
} else {
var pathToRead = result;
var isFile = pathToRead.match(/\S+\.\S+$/g);
const pathToRead = result
const isFile = pathToRead.match(/\S+\.\S+$/g)
await readFile(pathToRead)
.then(function (res) {
if (isFile) {
if (
pathToRead.includes(".png") ||
pathToRead.includes(".jpg") ||
pathToRead.includes(".jpeg")
) {
arrayBufferToBase64(
new Uint8Array(res),
function (base64) {
var src = "data:image/png;base64," + base64;
insecureRenderHtml('<img src="' + src + '"></img>');
}
);
} else {
// Convert byte array to UTF-8 string
const decoder = new TextDecoder('utf-8');
const text = decoder.decode(new Uint8Array(res));
onMessage(text);
}
await readFile(pathToRead).then(function (res) {
if (isFile) {
if (
pathToRead.includes('.png')
|| pathToRead.includes('.jpg')
|| pathToRead.includes('.jpeg')
) {
arrayBufferToBase64(res.buffer, function (base64) {
const src = 'data:image/png;base64,' + base64
onMessage('<img src="' + src + '"></img>')
})
} else {
onMessage(res);
// Convert byte array to UTF-8 string
const decoder = new TextDecoder('utf-8')
const text = decoder.decode(res)
onMessage(text)
}
} else {
onMessage(res)
}
})
}
} catch(exception) {
} catch (exception) {
onMessage(exception)
}
}
function saveDialog() {
save({
title: "My wonderful save dialog",
title: 'My wonderful save dialog',
defaultPath,
filters: filter
? [
{
name: "Tauri Example",
extensions: filter.split(",").map((f) => f.trim()),
},
name: 'Tauri Example',
extensions: filter.split(',').map((f) => f.trim())
}
]
: [],
})
: []
})
.then(onMessage)
.catch(onMessage);
.catch(onMessage)
}
</script>
@@ -171,15 +180,16 @@
<br />
<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}
<button class="btn" id="open-dialog" onclick={openDialog}>Open dialog</button>
<button class="btn" id="save-dialog" onclick={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}
<button class="btn" id="prompt-dialog" onclick={prompt}>Prompt</button>
<button class="btn" id="custom-prompt-dialog" onclick={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>
<button class="btn" id="message-dialog" onclick={msg}>Message</button>
<button class="btn" id="message-dialog" onclick={msgCustom}
>Message (custom)</button
>
</div>
+3 -3
View File
@@ -5,7 +5,7 @@
import { arrayBufferToBase64 } from '../lib/utils'
import { onDestroy, onMount } from 'svelte'
const { onMessage, insecureRenderHtml } = $props()
const { onMessage } = $props()
let path = $state('')
let img
@@ -118,12 +118,12 @@
new Uint8Array(response),
function (base64) {
const src = 'data:image/png;base64,' + base64
insecureRenderHtml('<img src="' + src + '"></img>')
onMessage('<img src="' + src + '"></img>')
}
)
} else {
const value = String.fromCharCode.apply(null, response)
insecureRenderHtml(
onMessage(
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
)
setTimeout(() => {
+19 -11
View File
@@ -1,12 +1,18 @@
<script>
import { check } from '@tauri-apps/plugin-updater'
<script lang="ts">
import { check, Update } from '@tauri-apps/plugin-updater'
import { relaunch } from '@tauri-apps/plugin-process'
import { onDestroy } from 'svelte'
export let onMessage
let { onMessage } = $props()
let isChecking, isInstalling, newUpdate
let totalSize = 0,
downloadedSize = 0
let isChecking = $state(false)
let isInstalling = $state(false)
let newUpdate = $state<Update | undefined>()
let totalSize = $state(0)
let downloadedSize = $state(0)
let progress = $derived(
totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0
)
async function checkUpdate() {
isChecking = true
@@ -31,10 +37,10 @@
isInstalling = true
downloadedSize = 0
try {
await newUpdate.downloadAndInstall((downloadProgress) => {
await newUpdate!.downloadAndInstall((downloadProgress) => {
switch (downloadProgress.event) {
case 'Started':
totalSize = downloadProgress.data.contentLength
totalSize = downloadProgress.data.contentLength!
break
case 'Progress':
downloadedSize += downloadProgress.data.chunkLength
@@ -54,14 +60,16 @@
}
}
$: progress = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0
onDestroy(() => {
newUpdate?.close()
})
</script>
<div class="flex children:grow children:h10">
{#if !isChecking && !newUpdate}
<button class="btn" on:click={checkUpdate}>Check update</button>
<button class="btn" onclick={checkUpdate}>Check update</button>
{:else if !isInstalling && newUpdate}
<button class="btn" on:click={install}>Install update</button>
<button class="btn" onclick={install}>Install update</button>
{:else}
<div class="progress">
<span>{progress}%</span>
+5
View File
@@ -0,0 +1,5 @@
*
!.gitignore
!README.md
!updater.json
+10
View File
@@ -0,0 +1,10 @@
To test the updater:
1. Comment out `verify_signature` inside `plugins/updater/src/updater.rs`
2. Run `pnpm tauri build --debug` to build the bundles
3. Copy the bundle you want to test to this folder (`examples/api/updater-test/`)
4. Run `pnpm tauri dev` and open the Updater tab to check
If the bundle you're testing doesn't exist in the `updater.json` yet, add it manually and welcome to open an PR
> The `updater.json` and debug bundles will be served by `vite`
+11
View File
@@ -0,0 +1,11 @@
{
"version": "2.1.0",
"notes": "Test update!",
"pub_date": "2026-03-01T14:04:20+00:00",
"platforms": {
"windows-x86_64": {
"signature": "",
"url": "http://localhost:5173/updater-test/Tauri API_2.0.0_x64_en-US.msi"
}
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ export default defineConfig(async () => {
port: 5173,
strictPort: true,
fs: {
allow: ['.', '../../tooling/api/dist']
allow: ['.']
}
}
}
+5 -15
View File
@@ -15,25 +15,15 @@
"@rollup/plugin-node-resolve": "16.0.3",
"@rollup/plugin-terser": "1.0.0",
"@rollup/plugin-typescript": "12.3.0",
"eslint": "10.2.0",
"eslint": "10.4.0",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-security": "4.0.0",
"prettier": "3.8.1",
"rollup": "4.59.0",
"eslint-plugin-security": "4.0.1",
"prettier": "3.8.3",
"rollup": "4.60.3",
"tslib": "2.8.1",
"typescript": "6.0.3",
"typescript-eslint": "8.58.2"
},
"minimumReleaseAge": 4320,
"pnpm": {
"overrides": {
"esbuild@<0.25.0": ">=0.25.0"
},
"onlyBuiltDependencies": [
"esbuild"
]
},
"engines": {
"pnpm": "^10.16.0"
}
"packageManager": "pnpm@11.13.0"
}
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.4.5]
- [`d8645ab3`](https://github.com/tauri-apps/plugins-workspace/commit/d8645ab3e5b508456681eb53275c0837db25aeee) ([#3393](https://github.com/tauri-apps/plugins-workspace/pull/3393) by [@AlexisZankowitch](https://github.com/tauri-apps/plugins-workspace/../../AlexisZankowitch)) Fixed a crash on iOS when `cancel()` is invoked by running the cancel handler on the main thread.
## \[2.4.4]
- [`82fbb0c7`](https://github.com/tauri-apps/plugins-workspace/commit/82fbb0c790288eca72af9ade13828ded7700ff90) ([#3221](https://github.com/tauri-apps/plugins-workspace/pull/3221)) On iOS, fixed an application crash happening when the scanner was started when user denied permission before.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-barcode-scanner"
version = "2.4.4"
version = "2.4.5"
description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
edition = { workspace = true }
authors = { workspace = true }
@@ -337,10 +337,11 @@ class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
}
@objc private func cancel(_ invoke: Invoke) {
self.invoke?.reject("cancelled")
destroy()
invoke.resolve()
DispatchQueue.main.async { [self] in
self.invoke?.reject("cancelled")
self.destroy()
invoke.resolve()
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-barcode-scanner",
"version": "2.4.4",
"version": "2.4.5",
"description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
"license": "MIT OR Apache-2.0",
"authors": [
+2 -2
View File
@@ -39,8 +39,8 @@ url = { workspace = true }
[target."cfg(windows)".dependencies]
dunce = "1"
windows-registry = "0.5"
windows-result = "0.3"
windows-registry = "0.6"
windows-result = "0.4"
[target."cfg(target_os = \"linux\")".dependencies]
rust-ini = "0.21"
+2 -2
View File
@@ -11,10 +11,10 @@
},
"dependencies": {
"@tauri-apps/api": "^2.11.0",
"@tauri-apps/plugin-deep-link": "2.4.9"
"@tauri-apps/plugin-deep-link": "workspace:*"
},
"devDependencies": {
"@tauri-apps/cli": "2.11.0",
"@tauri-apps/cli": "2.11.4",
"typescript": "^6.0.0",
"vite": "^8.0.1"
}
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.deep_link_example" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<style name="Theme.deep_link_example" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.deep_link_example" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<style name="Theme.deep_link_example" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
+17 -7
View File
@@ -14,9 +14,11 @@ import { type UnlistenFn, listen } from '@tauri-apps/api/event'
* const urls = await getCurrent();
* ```
*
* #### - **Windows / Linux**: This function reads the command line arguments and checks if there's only one value, which must be an URL with scheme matching one of the configured values.
* Note that you must manually check the arguments when registering deep link schemes dynamically with [`Self::register`].
* Additionally, the deep link might have been provided as a CLI argument so you should check if its format matches what you expect..
* #### Platform-specific
*
* - **Windows / Linux:** This function reads the command line arguments and checks if there's only one value, which must be an URL with scheme matching one of the configured values.
* Note that you must manually check the arguments when registering deep link schemes dynamically with [`Self::register`].
* Additionally, the deep link might have been provided as a CLI argument so you should check if its format matches what you expect.
*
* @since 2.0.0
*/
@@ -35,7 +37,9 @@ export async function getCurrent(): Promise<string[] | null> {
* await register("my-scheme");
* ```
*
* #### - **macOS / Android / iOS**: Unsupported.
* #### Platform-specific
*
* - **macOS / Android / iOS:** Unsupported.
*
* @since 2.0.0
*/
@@ -54,7 +58,9 @@ export async function register(protocol: string): Promise<null> {
* await unregister("my-scheme");
* ```
*
* #### - **macOS / Linux / Android / iOS**: Unsupported.
* #### Platform-specific
*
* - **macOS / Linux / Android / iOS:** Unsupported.
*
* @since 2.0.0
*/
@@ -73,7 +79,9 @@ export async function unregister(protocol: string): Promise<null> {
* await isRegistered("my-scheme");
* ```
*
* #### - **macOS / Android / iOS**: Unsupported.
* #### Platform-specific
*
* - **macOS / Android / iOS:** Unsupported.
*
* @since 2.0.0
*/
@@ -92,7 +100,9 @@ export async function isRegistered(protocol: string): Promise<boolean> {
* await onOpenUrl((urls) => { console.log(urls) });
* ```
*
* #### - **Windows / Linux**: Unsupported without the single-instance plugin. The OS will spawn a new app instance passing the URL as a CLI argument.
* #### Platform-specific
*
* - **Windows / Linux:** Unsupported without the single-instance plugin. The OS will spawn a new app instance passing the URL as a CLI argument.
*
* @since 2.0.0
*/
+1 -1
View File
@@ -28,6 +28,6 @@
"@tauri-apps/api": "^2.11.0"
},
"devDependencies": {
"@tauri-apps/cli": "2.11.0"
"@tauri-apps/cli": "2.11.4"
}
}
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.7.2]
- [`40ae0a7f`](https://github.com/tauri-apps/plugins-workspace/commit/40ae0a7fa0ecc9b0cdbd952297f7a17f37308229) ([#3491](https://github.com/tauri-apps/plugins-workspace/pull/3491) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Use `com.google.android.material.dialog.MaterialAlertDialogBuilder` instead of `AlertDialog` so the dialog follows the app's theme
## \[2.7.1]
### Dependencies
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-dialog"
version = "2.7.1"
version = "2.7.2"
description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
+2 -2
View File
@@ -35,8 +35,8 @@ android {
dependencies {
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.appcompat:appcompat:1.7.1")
implementation("com.google.android.material:material:1.13.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
@@ -5,7 +5,6 @@
package app.tauri.dialog
import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.net.Uri
import android.os.Handler
@@ -21,6 +20,7 @@ import app.tauri.plugin.Invoke
import app.tauri.plugin.JSArray
import app.tauri.plugin.JSObject
import app.tauri.plugin.Plugin
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@InvokeArg
class Filter {
@@ -158,7 +158,7 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
Handler(Looper.getMainLooper())
.post {
val builder = AlertDialog.Builder(activity)
val builder = MaterialAlertDialogBuilder(activity)
if (args.title != null) {
builder.setTitle(args.title)
@@ -191,8 +191,7 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
}
}
val dialog = builder.create()
dialog.show()
builder.show()
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-dialog",
"version": "2.7.1",
"version": "2.7.2",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
@@ -6,7 +6,7 @@ description = """This denies read access to the
`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.
Allowing access can lead to sensitive information disclosure and should be well considered."""
[[scope.deny]]
[[permission.scope.deny]]
path = "$APPLOCALDATA/**"
[[permission]]
@@ -15,5 +15,5 @@ description = """This denies read access to the
`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.
Allowing access can lead to sensitive information disclosure and should be well considered."""
[[scope.deny]]
[[permission.scope.deny]]
path = "$APPLOCALDATA/EBWebView/**"
+107 -110
View File
@@ -74,7 +74,7 @@ pub type CommandResult<T> = std::result::Result<T, CommandError>;
/// Represents either a plain PathBuf or a PathHandle that manages security-scoped resources.
pub enum PathKind<R: Runtime> {
/// A plain path that doesn't manage security-scoped resources.
#[allow(dead_code)] // only used on mobile
#[cfg(mobile)] // only used on mobile
Path(PathBuf),
/// A path handle that manages security-scoped resources and will clean them up on drop.
Handle(PathHandle<R>),
@@ -84,6 +84,7 @@ impl<R: Runtime> PathKind<R> {
/// Get a reference to the underlying path.
pub fn as_path(&self) -> &Path {
match self {
#[cfg(mobile)]
PathKind::Path(p) => p.as_ref(),
PathKind::Handle(h) => h.as_ref(),
}
@@ -92,6 +93,7 @@ impl<R: Runtime> PathKind<R> {
/// Get a reference to the underlying PathBuf.
pub fn as_path_buf(&self) -> &PathBuf {
match self {
#[cfg(mobile)]
PathKind::Path(p) => p,
PathKind::Handle(h) => h,
}
@@ -114,27 +116,13 @@ impl<R: Runtime> AsRef<PathBuf> for PathKind<R> {
pub struct FileHandle<R: Runtime> {
file: File,
path: PathKind<R>,
#[allow(dead_code)] // Used in Drop implementation
#[cfg(target_os = "ios")]
path_: SafeFilePath,
#[allow(dead_code)] // Used in Drop implementation
#[cfg(target_os = "ios")]
app_handle: tauri::AppHandle<R>,
}
impl<R: Runtime> FileHandle<R> {
fn new(
file: File,
path: PathKind<R>,
path_: SafeFilePath,
app_handle: tauri::AppHandle<R>,
) -> Self {
Self {
file,
path,
path_,
app_handle,
}
}
/// Get the resolved path.
pub fn path(&self) -> &Path {
self.path.as_path()
@@ -155,41 +143,39 @@ impl<R: Runtime> DerefMut for FileHandle<R> {
}
}
#[cfg(target_os = "ios")]
impl<R: Runtime> Drop for FileHandle<R> {
fn drop(&mut self) {
#[cfg(target_os = "ios")]
{
// Only clean up if we have a plain PathBuf, not a PathHandle
// PathHandle will handle its own cleanup when it's dropped
if let PathKind::Path(_) = &self.path {
use crate::{FilePath, FsExt};
// Convert SafeFilePath to FilePath
let file_path: FilePath = match &self.path_ {
SafeFilePath::Url(url) => FilePath::Url(url.clone()),
SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
};
// Only clean up if we have a plain PathBuf, not a PathHandle
// PathHandle will handle its own cleanup when it's dropped
if let PathKind::Path(_) = &self.path {
use crate::{FilePath, FsExt};
// Convert SafeFilePath to FilePath
let file_path: FilePath = match &self.path_ {
SafeFilePath::Url(url) => FilePath::Url(url.clone()),
SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
};
// Only clean up if we're tracking this resource
// If start_accessing_security_scoped_resource was used, it won't be in our tracking
// and we shouldn't interfere
if let FilePath::Url(url) = file_path {
if url.scheme() == "file" {
let security_scoped_resources =
self.app_handle.state::<crate::SecurityScopedResources>();
// Only clean up if we're tracking this resource
// If start_accessing_security_scoped_resource was used, it won't be in our tracking
// and we shouldn't interfere
if let FilePath::Url(url) = file_path {
if url.scheme() == "file" {
let security_scoped_resources =
self.app_handle.state::<crate::SecurityScopedResources>();
// Only clean up if it's not tracked manually
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
log::debug!("Stopping accessing security-scoped resource for URL: {url} on drop");
let _ = self
.app_handle
.fs()
.stop_accessing_security_scoped_resource(FilePath::Url(
url.clone(),
));
security_scoped_resources.remove(url.as_str());
} else {
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)");
}
// Only clean up if it's not tracked manually
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
log::debug!(
"Stopping accessing security-scoped resource for URL: {url} on drop"
);
let _ = self
.app_handle
.fs()
.stop_accessing_security_scoped_resource(FilePath::Url(url.clone()));
security_scoped_resources.remove(url.as_str());
} else {
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)");
}
}
}
@@ -236,38 +222,36 @@ impl<R: Runtime> AsRef<PathBuf> for PathHandle<R> {
}
}
#[cfg(target_os = "ios")]
impl<R: Runtime> Drop for PathHandle<R> {
fn drop(&mut self) {
#[cfg(target_os = "ios")]
{
use crate::{FilePath, FsExt};
// Convert SafeFilePath to FilePath
let file_path: FilePath = match &self.path_ {
SafeFilePath::Url(url) => FilePath::Url(url.clone()),
SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
};
use crate::{FilePath, FsExt};
// Convert SafeFilePath to FilePath
let file_path: FilePath = match &self.path_ {
SafeFilePath::Url(url) => FilePath::Url(url.clone()),
SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
};
// Only clean up if we're tracking this resource (i.e., resolve_path started it)
// If start_accessing_security_scoped_resource was used, it won't be in our tracking
// and we shouldn't interfere
if let FilePath::Url(url) = file_path {
if url.scheme() == "file" {
let security_scoped_resources =
self.app_handle.state::<crate::SecurityScopedResources>();
// Only clean up if we're tracking this resource (i.e., resolve_path started it)
// If start_accessing_security_scoped_resource was used, it won't be in our tracking
// and we shouldn't interfere
if let FilePath::Url(url) = file_path {
if url.scheme() == "file" {
let security_scoped_resources =
self.app_handle.state::<crate::SecurityScopedResources>();
// Only clean up if it's not tracked manually
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
log::debug!(
"Stopping accessing security-scoped resource for URL: {url} on drop"
);
let _ = self
.app_handle
.fs()
.stop_accessing_security_scoped_resource(FilePath::Url(url.clone()));
security_scoped_resources.remove(url.as_str());
} else {
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)");
}
// Only clean up if it's not tracked manually
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
log::debug!(
"Stopping accessing security-scoped resource for URL: {url} on drop"
);
let _ = self
.app_handle
.fs()
.stop_accessing_security_scoped_resource(FilePath::Url(url.clone()));
security_scoped_resources.remove(url.as_str());
} else {
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)");
}
}
}
@@ -288,6 +272,7 @@ pub fn create<R: Runtime>(
path: SafeFilePath,
options: Option<BaseOptions>,
) -> CommandResult<ResourceId> {
#[cfg(target_os = "ios")]
let path_ = path.clone();
let resolved_path_handle = resolve_path(
"create",
@@ -303,13 +288,16 @@ pub fn create<R: Runtime>(
resolved_path_handle.display()
)
})?;
#[cfg(target_os = "ios")]
let app_handle = webview.app_handle().clone();
let file_handle = FileHandle::new(
let file_handle = FileHandle {
file,
PathKind::Handle(resolved_path_handle),
path: PathKind::Handle(resolved_path_handle),
#[cfg(target_os = "ios")]
path_,
#[cfg(target_os = "ios")]
app_handle,
);
};
let rid = webview
.resources_table()
.add(StdFileResource::new(file_handle));
@@ -707,7 +695,7 @@ pub async fn read_text_file_lines_next<R: Runtime>(
let lines = resource_table.get::<StdLinesResource>(rid)?;
let ret = StdLinesResource::with_lock(&lines, |lines| -> CommandResult<Vec<u8>> {
// This is an optimization to include wether we finished iteration or not (1 or 0)
// This is an optimization to include whether we finished iteration or not (1 or 0)
// at the end of returned vector so we can use `tauri::ipc::Response`
// and avoid serialization overhead of separate values.
match lines.next() {
@@ -1417,6 +1405,7 @@ fn resolve_file_in_fs<R: Runtime>(
path: SafeFilePath,
open_options: OpenOptions,
) -> CommandResult<FileHandle<R>> {
#[cfg(target_os = "ios")]
let path_ = path.clone();
let resolved_path_handle = resolve_path(
permission,
@@ -1436,13 +1425,16 @@ fn resolve_file_in_fs<R: Runtime>(
)
})?;
#[cfg(target_os = "ios")]
let app_handle = webview.app_handle().clone();
Ok(FileHandle::new(
Ok(FileHandle {
file,
PathKind::Handle(resolved_path_handle),
path: PathKind::Handle(resolved_path_handle),
#[cfg(target_os = "ios")]
path_,
#[cfg(target_os = "ios")]
app_handle,
))
})
}
#[cfg(mobile)]
@@ -1456,6 +1448,7 @@ pub fn resolve_file<R: Runtime>(
) -> CommandResult<FileHandle<R>> {
use crate::FsExt;
#[cfg(target_os = "ios")]
let path_ = path.clone();
match path {
SafeFilePath::Url(url) => {
@@ -1463,13 +1456,16 @@ pub fn resolve_file<R: Runtime>(
let file = webview
.fs()
.open(SafeFilePath::Url(url.clone()), open_options.options)?;
#[cfg(target_os = "ios")]
let app_handle = webview.app_handle().clone();
Ok(FileHandle::new(
Ok(FileHandle {
file,
PathKind::Path(resolved_path),
path: PathKind::Path(resolved_path),
#[cfg(target_os = "ios")]
path_,
#[cfg(target_os = "ios")]
app_handle,
))
})
}
SafeFilePath::Path(path) => resolve_file_in_fs(
permission,
@@ -1494,36 +1490,37 @@ pub fn resolve_path<R: Runtime>(
// On iOS, start accessing security-scoped resource if the path is a file URL
// Only if it hasn't been started already via start_accessing_security_scoped_resource
#[cfg(target_os = "ios")]
{
if let SafeFilePath::Url(url) = &path {
if url.scheme() == "file" {
use objc2_foundation::{NSString, NSURL};
if let SafeFilePath::Url(url) = &path {
if url.scheme() == "file" {
use objc2_foundation::{NSString, NSURL};
let security_scoped_resources = webview.state::<crate::SecurityScopedResources>();
let security_scoped_resources = webview.state::<crate::SecurityScopedResources>();
// Check if already active (started via start_accessing_security_scoped_resource)
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
let url_nsstring = NSString::from_str(url.as_str());
let ns_url = unsafe { NSURL::URLWithString(&url_nsstring) };
if let Some(ns_url) = ns_url {
// Start accessing the security-scoped resource
// This is required for files outside the app's sandbox (e.g., from file picker)
unsafe {
let success = ns_url.startAccessingSecurityScopedResource();
if success {
log::debug!("Started accessing security-scoped resource for URL: {} (via resolve_path)", url.as_str());
// Track it so we know to clean it up
security_scoped_resources.track_manually(url.as_str().to_string());
} else {
log::warn!("Failed to start accessing security-scoped resource for URL: {}", url.as_str());
}
// Check if already active (started via start_accessing_security_scoped_resource)
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
let url_nsstring = NSString::from_str(url.as_str());
let ns_url = unsafe { NSURL::URLWithString(&url_nsstring) };
if let Some(ns_url) = ns_url {
// Start accessing the security-scoped resource
// This is required for files outside the app's sandbox (e.g., from file picker)
unsafe {
let success = ns_url.startAccessingSecurityScopedResource();
if success {
log::debug!("Started accessing security-scoped resource for URL: {} (via resolve_path)", url.as_str());
// Track it so we know to clean it up
security_scoped_resources.track_manually(url.as_str().to_string());
} else {
log::warn!(
"Failed to start accessing security-scoped resource for URL: {}",
url.as_str()
);
}
} else {
log::debug!("Failed to create NSURL from URL: {}, ignoring security-scoped resource access request", url.as_str());
}
} else {
log::debug!("Security-scoped resource already active for URL: {} (started via start_accessing_security_scoped_resource), skipping", url.as_str());
log::debug!("Failed to create NSURL from URL: {}, ignoring security-scoped resource access request", url.as_str());
}
} else {
log::debug!("Security-scoped resource already active for URL: {} (started via start_accessing_security_scoped_resource), skipping", url.as_str());
}
}
}
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.3.2]
- [`cb863d57`](https://github.com/tauri-apps/plugins-workspace/commit/cb863d57c60790efc41b2bd77d065bd91b4ef2b1) ([#3408](https://github.com/tauri-apps/plugins-workspace/pull/3408) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Update crate `global-hotkey` to 0.8 to fix cpu usage while pressing the keys on windows and to add F13-F24 mappings on Linux/X11.
## \[2.3.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 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-global-shortcut"
version = "2.3.1"
version = "2.3.2"
description = "Register global hotkeys listeners on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
@@ -27,4 +27,4 @@ log = { workspace = true }
thiserror = { workspace = true }
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
global-hotkey = { version = "0.7", features = ["serde"] }
global-hotkey = { version = "0.8", features = ["serde"] }
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-global-shortcut",
"version": "2.3.1",
"version": "2.3.2",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+1 -1
View File
@@ -62,7 +62,7 @@ Afterwards all the plugin's APIs are available through the JavaScript guest bind
import { fetch } from '@tauri-apps/plugin-http'
const response = await fetch('http://localhost:3003/users/2', {
method: 'GET',
timeout: 30
connectTimeout: 30
})
```
+6
View File
@@ -1,5 +1,11 @@
# Changelog
## \[2.9.0]
- [`f08980f1`](https://github.com/tauri-apps/plugins-workspace/commit/f08980f123f191b9505bd290acd8fff0fdefeed9) ([#3445](https://github.com/tauri-apps/plugins-workspace/pull/3445) by [@bajoca05](https://github.com/tauri-apps/plugins-workspace/../../bajoca05)) Added the `FileOpenStrategy` for log rotation. It defaults to append into existing file if any (previous behaviour), and brings a new feature to create a new file per session: `FileOpenStrategy::Rotate`.
- [`254f222e`](https://github.com/tauri-apps/plugins-workspace/commit/254f222e0e2bc79370f977855b6b39d956d3b568) ([#3477](https://github.com/tauri-apps/plugins-workspace/pull/3477) by [@bajoca05](https://github.com/tauri-apps/plugins-workspace/../../bajoca05)) Migrated from the `log` crate's `kv_unstable` feature flag to the `kv` feature flag stabilized in `0.4.21`.
- [`0c23b8ec`](https://github.com/tauri-apps/plugins-workspace/commit/0c23b8ecfe7c2aca582a81ab7339b11e350b3cac) ([#3446](https://github.com/tauri-apps/plugins-workspace/pull/3446) by [@fee1-dead](https://github.com/tauri-apps/plugins-workspace/../../fee1-dead)) Removed an unused dependency `byte-unit`.
## \[2.8.0]
- [`2a625adf`](https://github.com/tauri-apps/plugins-workspace/commit/2a625adff30238904035b86b6e2db7595597e857) ([#3065](https://github.com/tauri-apps/plugins-workspace/pull/3065) by [@BinaryMuse](https://github.com/tauri-apps/plugins-workspace/../../BinaryMuse)) Allow specifying a log formatter per target using the `format` method on `Target`.
+2 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-log"
version = "2.8.0"
version = "2.9.0"
description = "Configurable logging for your Tauri app."
authors = { workspace = true }
license = { workspace = true }
@@ -25,8 +25,7 @@ serde_json = { workspace = true }
tauri = { workspace = true }
thiserror = { workspace = true }
serde_repr = "0.1"
byte-unit = "5"
log = { workspace = true, features = ["kv_unstable"] }
log = { workspace = true, features = ["kv"] }
time = { version = "0.3", features = [
"formatting",
"local-offset",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-log",
"version": "2.8.0",
"version": "2.9.0",
"description": "Configurable logging for your Tauri app.",
"license": "MIT OR Apache-2.0",
"authors": [
+152 -4
View File
@@ -43,9 +43,10 @@ mod ios {
));
}
const DEFAULT_MAX_FILE_SIZE: u64 = 40000;
const DEFAULT_MAX_FILE_SIZE: u64 = 40_000;
const DEFAULT_ROTATION_STRATEGY: RotationStrategy = RotationStrategy::KeepOne;
const DEFAULT_TIMEZONE_STRATEGY: TimezoneStrategy = TimezoneStrategy::UseUtc;
const DEFAULT_FILE_OPEN_STRATEGY: FileOpenStrategy = FileOpenStrategy::Append;
const DEFAULT_LOG_TARGETS: [Target; 2] = [
Target::new(TargetKind::Stdout),
Target::new(TargetKind::LogDir { file_name: None }),
@@ -146,15 +147,26 @@ impl TimezoneStrategy {
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FileOpenStrategy {
/// Open existing file from last session and append, if any.
Append,
/// Create a new file on each session start, rotating the last session if any.
Rotate,
}
/// A custom log writer that rotates the log file when it exceeds specified size.
struct RotatingFile {
dir: PathBuf,
file_name: String,
path: PathBuf,
/// Maximum file size before rotating in bytes
max_size: u64,
/// Current file size in bytes
current_size: u64,
rotation_strategy: RotationStrategy,
timezone_strategy: TimezoneStrategy,
file_open_strategy: FileOpenStrategy,
inner: Option<File>,
buffer: Vec<u8>,
}
@@ -166,6 +178,7 @@ impl RotatingFile {
max_size: u64,
rotation_strategy: RotationStrategy,
timezone_strategy: TimezoneStrategy,
file_open_strategy: FileOpenStrategy,
) -> Result<Self, Error> {
let dir = dir.as_ref().to_path_buf();
let path = dir.join(&file_name).with_extension("log");
@@ -178,12 +191,15 @@ impl RotatingFile {
current_size: 0,
rotation_strategy,
timezone_strategy,
file_open_strategy,
inner: None,
buffer: Vec::new(),
};
rotator.open_file()?;
if rotator.current_size >= rotator.max_size {
if rotator.current_size >= rotator.max_size
|| (rotator.current_size > 0 && rotator.file_open_strategy == FileOpenStrategy::Rotate)
{
rotator.rotate()?;
}
if let RotationStrategy::KeepSome(keep_count) = rotator.rotation_strategy {
@@ -396,6 +412,7 @@ pub struct Builder {
dispatch: fern::Dispatch,
rotation_strategy: RotationStrategy,
timezone_strategy: TimezoneStrategy,
file_open_strategy: FileOpenStrategy,
max_file_size: u128,
targets: Vec<Target>,
is_skip_logger: bool,
@@ -423,6 +440,7 @@ impl Default for Builder {
dispatch,
rotation_strategy: DEFAULT_ROTATION_STRATEGY,
timezone_strategy: DEFAULT_TIMEZONE_STRATEGY,
file_open_strategy: DEFAULT_FILE_OPEN_STRATEGY,
max_file_size: DEFAULT_MAX_FILE_SIZE as u128,
targets: DEFAULT_LOG_TARGETS.into(),
is_skip_logger: false,
@@ -435,11 +453,18 @@ impl Builder {
Default::default()
}
/// Sets the [`RotationStrategy`].
///
/// Default is [`RotationStrategy::KeepOne`]
pub fn rotation_strategy(mut self, rotation_strategy: RotationStrategy) -> Self {
self.rotation_strategy = rotation_strategy;
self
}
/// Sets the [`TimezoneStrategy`].
/// Calling this method overrides the format set in [`Self::format`].
///
/// Default is [`TimezoneStrategy::UseUtc`]
pub fn timezone_strategy(mut self, timezone_strategy: TimezoneStrategy) -> Self {
self.timezone_strategy = timezone_strategy.clone();
@@ -456,15 +481,28 @@ impl Builder {
self
}
/// Sets the maximum file size for log rotation.
/// Sets the strategy to open the log file.
///
/// Values larger than `u64::MAX` will be clamped to `u64::MAX`.
/// The default is [`FileOpenStrategy::Append`].
pub fn file_open_strategy(mut self, file_open_strategy: FileOpenStrategy) -> Self {
self.file_open_strategy = file_open_strategy;
self
}
/// Sets the maximum file size in bytes for log rotation.
///
/// Values larger than [`u64::MAX`] will be clamped to [`u64::MAX`].
/// In v3, this parameter will be changed to `u64`.
///
/// Default is `40_000`
pub fn max_file_size(mut self, max_file_size: u128) -> Self {
self.max_file_size = max_file_size.min(u64::MAX as u128);
self
}
/// Clears the format so that only the message is logged.
///
/// e.g. `log::info!("message")` will log out `message`
pub fn clear_format(mut self) -> Self {
self.dispatch = self.dispatch.format(|out, message, _record| {
out.finish(format_args!("{message}"));
@@ -472,6 +510,37 @@ impl Builder {
self
}
/// Sets the formatter of this dispatch. The closure should accept a
/// callback, a message and a log record, and write the resulting
/// format to the writer.
///
/// The log record is passed for completeness, but the `args()` method of
/// the record should be ignored, and the [`std::fmt::Arguments`] given
/// should be used instead. `record.args()` may be used to retrieve the
/// _original_ log message, but in order to allow for true log
/// chaining, formatters should use the given message instead whenever
/// including the message in the output.
///
/// To avoid all allocation of intermediate results, the formatter is
/// "completed" by calling a callback, which then calls the rest of the
/// logging chain with the new formatted message. The callback object keeps
/// track of if it was called or not via a stack boolean as well, so if
/// you don't use `out.finish` the log message will continue down
/// the logger chain unformatted.
///
/// Example usage:
///
/// ```
/// tauri_plugin_log::Builder::new()
/// .format(|out, message, record| {
/// out.finish(format_args!(
/// "[{} {}] {}",
/// record.level(),
/// record.target(),
/// message
/// ))
/// });
/// ```
pub fn format<F>(mut self, formatter: F) -> Self
where
F: Fn(FormatCallback, &Arguments, &Record) + Sync + Send + 'static,
@@ -480,16 +549,64 @@ impl Builder {
self
}
/// Sets the overarching level filter for this logger.
/// All messages not already filtered by something set by [`Self::level_for`] will be affected.
///
/// All messages filtered will be discarded if less severe than the given level.
///
/// Default level is [`log::LevelFilter::Trace`].
pub fn level(mut self, level_filter: impl Into<LevelFilter>) -> Self {
self.dispatch = self.dispatch.level(level_filter.into());
self
}
/// Sets a per-target log level filter. Default target for log messages is
/// `crate_name::module_name` or
/// `crate_name` for logs in the crate root. Targets can also be set with
/// `info!(target: "target-name", ...)`.
///
/// For each log record fern will first try to match the most specific
/// level_for, and then progressively more general ones until either a
/// matching level is found, or the default level is used.
///
/// For example, a log for the target `hyper::http::h1` will first test a
/// level_for for `hyper::http::h1`, then for `hyper::http`, then for
/// `hyper`, then use the default level.
///
/// Examples:
///
/// A program wants to include a lot of debugging output, but the library
/// "hyper" is known to work well, so debug output from it should be
/// excluded:
///
/// ```
/// # fn main() {
/// tauri_plugin_log::Builder::new()
/// .level(log::LevelFilter::Trace)
/// .level_for("hyper", log::LevelFilter::Info)
/// # ;
/// # }
/// ```
pub fn level_for(mut self, module: impl Into<Cow<'static, str>>, level: LevelFilter) -> Self {
self.dispatch = self.dispatch.level_for(module, level);
self
}
/// Adds a custom filter which can reject messages passing through this logger.
///
/// [`Self::level`] and [`Self::level_for`] are preferred if applicable.
///
/// Example usage:
///
/// ```
/// # fn main() {
/// tauri_plugin_log::Builder::new()
/// .level(log::LevelFilter::Info)
/// .filter(|metadata| {
/// // Reject messages with the `Error` log level.
/// metadata.level() != log::LevelFilter::Error
/// })
/// # }
pub fn filter<F>(mut self, filter: F) -> Self
where
F: Fn(&log::Metadata) -> bool + Send + Sync + 'static,
@@ -511,6 +628,19 @@ impl Builder {
/// tauri_plugin_log::Builder::new()
/// .target(Target::new(TargetKind::Webview));
/// ```
///
/// The default targets are
///
/// ```rust
/// # use tauri_plugin_log::{Target, TargetKind, Builder};
/// # Builder::new()
/// # .targets(
/// [
/// Target::new(TargetKind::Stdout),
/// Target::new(TargetKind::LogDir { file_name: None }),
/// ]
/// # );
/// ```
pub fn target(mut self, target: Target) -> Self {
self.targets.push(target);
self
@@ -543,6 +673,19 @@ impl Builder {
/// Target::new(TargetKind::LogDir { file_name: Some("rust".into()) }).filter(|metadata| !metadata.target().starts_with(WEBVIEW_TARGET)),
/// ]);
/// ```
///
/// The default targets are
///
/// ```rust
/// # use tauri_plugin_log::{Target, TargetKind, Builder};
/// # Builder::new()
/// # .targets(
/// [
/// Target::new(TargetKind::Stdout),
/// Target::new(TargetKind::LogDir { file_name: None }),
/// ]
/// # );
/// ```
pub fn targets(mut self, targets: impl IntoIterator<Item = Target>) -> Self {
self.targets = Vec::from_iter(targets);
self
@@ -569,6 +712,7 @@ impl Builder {
mut dispatch: fern::Dispatch,
rotation_strategy: RotationStrategy,
timezone_strategy: TimezoneStrategy,
file_open_strategy: FileOpenStrategy,
max_file_size: u64,
targets: Vec<Target>,
) -> Result<(log::LevelFilter, Box<dyn log::Log>), Error> {
@@ -621,6 +765,7 @@ impl Builder {
max_file_size,
rotation_strategy.clone(),
timezone_strategy.clone(),
file_open_strategy.clone(),
)?;
fern::Output::writer(Box::new(rotator), "\n")
}
@@ -636,6 +781,7 @@ impl Builder {
max_file_size,
rotation_strategy.clone(),
timezone_strategy.clone(),
file_open_strategy.clone(),
)?;
fern::Output::writer(Box::new(rotator), "\n")
}
@@ -681,6 +827,7 @@ impl Builder {
self.dispatch,
self.rotation_strategy,
self.timezone_strategy,
self.file_open_strategy,
self.max_file_size as u64,
self.targets,
)?;
@@ -697,6 +844,7 @@ impl Builder {
self.dispatch,
self.rotation_strategy,
self.timezone_strategy,
self.file_open_strategy,
self.max_file_size as u64,
self.targets,
)?;
+1
View File
@@ -47,6 +47,7 @@ impl<R: Runtime> Nfc<R> {
pub fn scan(&self, payload: ScanRequest) -> crate::Result<ScanResponse> {
self.0
.run_mobile_plugin("scan", payload)
.map(|v| ScanResponse { tag: v })
.map_err(Into::into)
}
+1 -1
View File
@@ -34,7 +34,7 @@ glob = { workspace = true }
dunce = { workspace = true }
[target."cfg(windows)".dependencies.windows]
version = "0.61"
version = "0.62"
features = [
"Win32_Foundation",
"Win32_UI_Shell_Common",
+1 -2
View File
@@ -232,10 +232,9 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
let app_dir_ = app_dir.clone();
if let Some(fs_scope) = &fs_scope {
let app_ = app.clone();
fs_scope.listen(move |event| {
if let tauri::fs::Event::PathAllowed(_) = event {
save_scopes(&app_.fs_scope(), &app_dir, &fs_scope_state_path);
save_scopes(&app.fs_scope(), &app_dir, &fs_scope_state_path);
}
});
}
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.3.3]
- [`4be76900`](https://github.com/tauri-apps/plugins-workspace/commit/4be76900854cae3dc91363dea71b54505896e928) ([#3449](https://github.com/tauri-apps/plugins-workspace/pull/3449) by [@skkap](https://github.com/tauri-apps/plugins-workspace/../../skkap)) Replaced a panic in `calculate_position` with an error return when the window has no associated monitor (e.g. during display sleep or monitor reconfiguration).
## \[2.3.2]
- [`c0d64bf7`](https://github.com/tauri-apps/plugins-workspace/commit/c0d64bf7d9c0f2c8ed1d2614745e15bbb3cde6a7) ([#3420](https://github.com/tauri-apps/plugins-workspace/pull/3420) by [@UrsDeSwardt](https://github.com/tauri-apps/plugins-workspace/../../UrsDeSwardt)) Removed panics and replaced them with error handling.
## \[2.3.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.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-positioner"
version = "2.3.1"
version = "2.3.3"
description = "Position your windows at well-known locations."
authors = { workspace = true }
license = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-positioner",
"version": "2.3.1",
"version": "2.3.3",
"description": "Position your windows at well-known locations.",
"license": "MIT OR Apache-2.0",
"authors": [
+21 -7
View File
@@ -152,7 +152,9 @@ fn calculate_position<R: Runtime>(
) -> Result<PhysicalPosition<i32>> {
use Position::*;
let screen = window.current_monitor()?.unwrap();
let screen = window.current_monitor()?.ok_or_else(|| {
tauri::Error::Io(std::io::Error::other("No monitor found for the window"))
})?;
// Only use the screen_position for the Tray independent positioning,
// because a tray event may not be called on the currently active monitor.
let screen_position = screen.position();
@@ -225,7 +227,9 @@ fn calculate_position<R: Runtime>(
PhysicalPosition { x: tray_x, y }
} else {
panic!("Tray position not set");
return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
}
}
#[cfg(feature = "tray-icon")]
@@ -236,7 +240,9 @@ fn calculate_position<R: Runtime>(
y: tray_y,
}
} else {
panic!("Tray position not set");
return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
}
}
#[cfg(feature = "tray-icon")]
@@ -257,7 +263,9 @@ fn calculate_position<R: Runtime>(
y,
}
} else {
panic!("Tray position not set");
return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
}
}
#[cfg(feature = "tray-icon")]
@@ -268,7 +276,9 @@ fn calculate_position<R: Runtime>(
y: tray_y,
}
} else {
panic!("Tray position not set");
return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
}
}
#[cfg(feature = "tray-icon")]
@@ -287,7 +297,9 @@ fn calculate_position<R: Runtime>(
PhysicalPosition { x, y }
} else {
panic!("Tray position not set");
return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
}
}
#[cfg(feature = "tray-icon")]
@@ -298,7 +310,9 @@ fn calculate_position<R: Runtime>(
y: tray_y,
}
} else {
panic!("Tray position not set");
return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
}
}
};
+2 -4
View File
@@ -479,10 +479,8 @@ fn read_line<F: Fn(Vec<u8>) -> CommandEvent + Send + Copy + 'static>(
let _ = block_on_task(async move { tx_.send(wrapper(buf)).await });
}
Err(e) => {
let tx_ = tx.clone();
let _ = block_on_task(
async move { tx_.send(CommandEvent::Error(e.to_string())).await },
);
let _ =
block_on_task(async move { tx.send(CommandEvent::Error(e.to_string())).await });
break;
}
}
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.4.3]
- [`d1573877`](https://github.com/tauri-apps/plugins-workspace/commit/d1573877226e609461761aa538cd0ca4f24d22be) ([#3466](https://github.com/tauri-apps/plugins-workspace/pull/3466) by [@bajoca05](https://github.com/tauri-apps/plugins-workspace/../../bajoca05)) Fix blocked thread on the single-instance plugin for MacOS: replace standard `UnixListener` with `tokio::net::UnixListener`, so the task can yield.
## \[2.4.2]
### Dependencies
+3 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-single-instance"
version = "2.4.2"
version = "2.4.3"
description = "Ensure a single instance of your tauri app is running."
authors = { workspace = true }
license = { workspace = true }
@@ -24,9 +24,10 @@ tracing = { workspace = true }
thiserror = { workspace = true }
tauri-plugin-deep-link = { path = "../deep-link", version = "2.4.9", optional = true }
semver = { version = "1", optional = true }
tokio = { version = "1", features = ["net"] }
[target."cfg(target_os = \"windows\")".dependencies.windows-sys]
version = "0.60"
version = "0.61"
features = [
"Win32_System_Threading",
"Win32_System_DataExchange",
@@ -9,6 +9,6 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@tauri-apps/cli": "2.11.0"
"@tauri-apps/cli": "2.11.4"
}
}
@@ -3,8 +3,8 @@
// SPDX-License-Identifier: MIT
use std::{
io::{BufWriter, Error, ErrorKind, Read, Write},
os::unix::net::{UnixListener, UnixStream},
io::{BufWriter, Error, ErrorKind, Write},
os::unix::net::UnixStream,
path::PathBuf,
};
@@ -15,6 +15,7 @@ use tauri::{
plugin::{self, TauriPlugin},
AppHandle, Config, Manager, RunEvent, Runtime,
};
use tokio::io::AsyncReadExt;
pub fn init<R: Runtime>(cb: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
plugin::Builder::new("single-instance")
@@ -31,7 +32,7 @@ pub fn init<R: Runtime>(cb: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
ErrorKind::NotFound | ErrorKind::ConnectionRefused => {
// This process claims itself as singleton as likely none exists
socket_cleanup(&socket);
listen_for_other_instances(&socket, app.clone(), cb);
listen_for_other_instances(socket, app.clone(), cb);
}
_ => {
tracing::debug!(
@@ -92,42 +93,40 @@ fn notify_singleton(socket: &PathBuf) -> Result<(), Error> {
}
fn listen_for_other_instances<A: Runtime>(
socket: &PathBuf,
socket: PathBuf,
app: AppHandle<A>,
mut cb: Box<SingleInstanceCallback<A>>,
) {
match UnixListener::bind(socket) {
Ok(listener) => {
tauri::async_runtime::spawn(async move {
for stream in listener.incoming() {
match stream {
Ok(mut stream) => {
let mut s = String::new();
match stream.read_to_string(&mut s) {
Ok(_) => {
let (cwd, args) = s.split_once("\0\0").unwrap_or_default();
let args: Vec<String> =
args.split('\0').map(String::from).collect();
cb(app.app_handle(), args, cwd.to_string());
}
Err(e) => {
tracing::debug!("single_instance failed to be notified: {e}")
}
tauri::async_runtime::spawn(async move {
match tokio::net::UnixListener::bind(socket) {
Ok(listener) => loop {
match listener.accept().await {
Ok((mut stream, _addr)) => {
let mut s = String::new();
match stream.read_to_string(&mut s).await {
Ok(_) => {
let (cwd, args) = s.split_once("\0\0").unwrap_or_default();
let args: Vec<String> =
args.split('\0').map(String::from).collect();
cb(app.app_handle(), args, cwd.to_string());
}
Err(e) => {
tracing::debug!("single_instance failed to be notified: {e}")
}
}
Err(err) => {
tracing::debug!("single_instance failed to be notified: {}", err);
continue;
}
}
Err(err) => {
tracing::debug!("single_instance failed to be notified: {}", err);
continue;
}
}
});
},
Err(err) => {
tracing::error!(
"single_instance failed to listen to other processes - launching normally: {}",
err
);
}
}
Err(err) => {
tracing::error!(
"single_instance failed to listen to other processes - launching normally: {}",
err
);
}
}
});
}
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.4.4]
- [`2ed6d6c1`](https://github.com/tauri-apps/plugins-workspace/commit/2ed6d6c1de4f5051b87cb6a2c5ac45320c444989) ([#3499](https://github.com/tauri-apps/plugins-workspace/pull/3499) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Fix `StoreOptions` requires `defaults` field
## \[2.4.3]
- [`964e13f1`](https://github.com/tauri-apps/plugins-workspace/commit/964e13f124ad1feeb93c10168b265dc4936f738c) ([#3395](https://github.com/tauri-apps/plugins-workspace/pull/3395) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Fix a deadlock when calling `Store::set` while exiting (on `RunEvent::Exit`)
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-store"
version = "2.4.3"
version = "2.4.4"
description = "Simple, persistent key-value store."
authors = { workspace = true }
license = { workspace = true }
@@ -8,7 +8,7 @@
"tauri": "tauri"
},
"devDependencies": {
"@tauri-apps/cli": "2.11.0",
"@tauri-apps/cli": "2.11.4",
"typescript": "^6.0.0",
"vite": "^8.0.1"
}
+1 -1
View File
@@ -21,7 +21,7 @@ export type StoreOptions = {
/**
* Default value of the store
*/
defaults: { [key: string]: unknown }
defaults?: { [key: string]: unknown }
/**
* Auto save on modification with debounce duration in milliseconds, it's 100ms by default, pass in `false` to disable it
*/
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-store",
"version": "2.4.3",
"version": "2.4.4",
"description": "Simple, persistent key-value store.",
"license": "MIT OR Apache-2.0",
"authors": [
+1 -1
View File
@@ -50,7 +50,7 @@ percent-encoding = "2.3"
[target."cfg(target_os = \"windows\")".dependencies]
zip = { version = "4", default-features = false, optional = true }
windows-sys = { version = "0.60.0", features = [
windows-sys = { version = "0.61.0", features = [
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging",
"Win32_UI_Shell",
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_UPDATER__=function(t){"use strict";function e(t,e,s,n){if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?n:"a"===s?n.call(t):n?n.value:e.get(t)}function s(t,e,s,n,i){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,s),s}var n,i,a,r,o;"function"==typeof SuppressedError&&SuppressedError;const d="__TAURI_TO_IPC_KEY__";class c{constructor(t){n.set(this,void 0),i.set(this,0),a.set(this,[]),r.set(this,void 0),s(this,n,t||(()=>{})),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}(t=>{const o=t.index;if("end"in t)return void(o==e(this,i,"f")?this.cleanupCallback():s(this,r,o));const d=t.message;if(o==e(this,i,"f")){for(e(this,n,"f").call(this,d),s(this,i,e(this,i,"f")+1);e(this,i,"f")in e(this,a,"f");){const t=e(this,a,"f")[e(this,i,"f")];e(this,n,"f").call(this,t),delete e(this,a,"f")[e(this,i,"f")],s(this,i,e(this,i,"f")+1)}e(this,i,"f")===e(this,r,"f")&&this.cleanupCallback()}else e(this,a,"f")[o]=d})}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(t){s(this,n,t)}get onmessage(){return e(this,n,"f")}[(n=new WeakMap,i=new WeakMap,a=new WeakMap,r=new WeakMap,d)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[d]()}}async function l(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}class h{get rid(){return e(this,o,"f")}constructor(t){o.set(this,void 0),s(this,o,t)}async close(){return l("plugin:resources|close",{rid:this.rid})}}o=new WeakMap;class u extends h{constructor(t){super(t.rid),this.available=!0,this.currentVersion=t.currentVersion,this.version=t.version,this.date=t.date,this.body=t.body,this.rawJson=t.rawJson}async download(t,e){_(e);const s=new c;t&&(s.onmessage=t);const n=await l("plugin:updater|download",{onEvent:s,rid:this.rid,...e});this.downloadedBytes=new h(n)}async install(){if(!this.downloadedBytes)throw new Error("Update.install called before Update.download");await l("plugin:updater|install",{updateRid:this.rid,bytesRid:this.downloadedBytes.rid}),this.downloadedBytes=void 0}async downloadAndInstall(t,e){_(e);const s=new c;t&&(s.onmessage=t),await l("plugin:updater|download_and_install",{onEvent:s,rid:this.rid,...e})}async close(){await(this.downloadedBytes?.close()),await super.close()}}function _(t){t?.headers&&(t.headers=Array.from(new Headers(t.headers).entries()))}return t.Update=u,t.check=async function(t){_(t);const e=await l("plugin:updater|check",{...t});return e?new u(e):null},t}({});Object.defineProperty(window.__TAURI__,"updater",{value:__TAURI_PLUGIN_UPDATER__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_UPDATER__=function(t){"use strict";function e(t,e,s,n){if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?n:"a"===s?n.call(t):n?n.value:e.get(t)}function s(t,e,s,n,i){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,s),s}var n,i,a,r,o;"function"==typeof SuppressedError&&SuppressedError;const d="__TAURI_TO_IPC_KEY__";class c{constructor(t){n.set(this,void 0),i.set(this,0),a.set(this,[]),r.set(this,void 0),s(this,n,t||(()=>{})),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}(t=>{const o=t.index;if("end"in t)return void(o==e(this,i,"f")?this.cleanupCallback():s(this,r,o));const d=t.message;if(o==e(this,i,"f")){for(e(this,n,"f").call(this,d),s(this,i,e(this,i,"f")+1);e(this,i,"f")in e(this,a,"f");){const t=e(this,a,"f")[e(this,i,"f")];e(this,n,"f").call(this,t),delete e(this,a,"f")[e(this,i,"f")],s(this,i,e(this,i,"f")+1)}e(this,i,"f")===e(this,r,"f")&&this.cleanupCallback()}else e(this,a,"f")[o]=d})}cleanupCallback(){window.__TAURI_INTERNALS__.unregisterCallback(this.id)}set onmessage(t){s(this,n,t)}get onmessage(){return e(this,n,"f")}[(n=new WeakMap,i=new WeakMap,a=new WeakMap,r=new WeakMap,d)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[d]()}}async function l(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}class h{get rid(){return e(this,o,"f")}constructor(t){o.set(this,void 0),s(this,o,t)}async close(){return l("plugin:resources|close",{rid:this.rid})}}o=new WeakMap;class u extends h{constructor(t){super(t.rid),this.available=!0,this.currentVersion=t.currentVersion,this.version=t.version,this.date=t.date,this.body=t.body,this.rawJson=t.rawJson}async download(t,e){_(e);const s=new c;t&&(s.onmessage=t);const n=await l("plugin:updater|download",{onEvent:s,rid:this.rid,...e});this.downloadedBytes=new h(n)}async install(t){if(!this.downloadedBytes)throw new Error("Update.install called before Update.download");await l("plugin:updater|install",{updateRid:this.rid,bytesRid:this.downloadedBytes.rid,...t}),this.downloadedBytes=void 0}async downloadAndInstall(t,e){_(e);const s=new c;t&&(s.onmessage=t),await l("plugin:updater|download_and_install",{onEvent:s,rid:this.rid,...e})}async close(){await(this.downloadedBytes?.close()),await super.close()}}function _(t){t?.headers&&(t.headers=Array.from(new Headers(t.headers).entries()))}return t.Update=u,t.check=async function(t){_(t);const e=await l("plugin:updater|check",{...t});return e?new u(e):null},t}({});Object.defineProperty(window.__TAURI__,"updater",{value:__TAURI_PLUGIN_UPDATER__})}
+12 -3
View File
@@ -40,6 +40,14 @@ interface DownloadOptions {
timeout?: number
}
/** Options used when installing an update */
interface InstallOptions {
/**
* If the Windows installer should restart the app after installed, default is `true`
*/
restartAfterInstall?: boolean
}
interface UpdateMetadata {
rid: number
currentVersion: string
@@ -95,14 +103,15 @@ class Update extends Resource {
}
/** Install downloaded updater package */
async install(): Promise<void> {
async install(options?: InstallOptions): Promise<void> {
if (!this.downloadedBytes) {
throw new Error('Update.install called before Update.download')
}
await invoke('plugin:updater|install', {
updateRid: this.rid,
bytesRid: this.downloadedBytes.rid
bytesRid: this.downloadedBytes.rid,
...options
})
// Don't need to call close, we did it in rust side already
@@ -112,7 +121,7 @@ class Update extends Resource {
/** Downloads the updater package and installs it */
async downloadAndInstall(
onEvent?: (progress: DownloadEvent) => void,
options?: DownloadOptions
options?: DownloadOptions & InstallOptions
): Promise<void> {
convertToRustHeaders(options)
const channel = new Channel<DownloadEvent>()
+16 -1
View File
@@ -142,12 +142,22 @@ pub(crate) async fn install<R: Runtime>(
webview: Webview<R>,
update_rid: ResourceId,
bytes_rid: ResourceId,
restart_after_install: Option<bool>,
) -> Result<()> {
let update = webview.resources_table().get::<Update>(update_rid)?;
let bytes = webview
.resources_table()
.get::<DownloadedBytes>(bytes_rid)?;
update.install(&bytes.0)?;
if let Some(restart_after_install) = restart_after_install {
let update = (*update).clone();
update
.restart_after_install(restart_after_install)
.install(&bytes.0)?;
} else {
update.install(&bytes.0)?;
}
let _ = webview.resources_table().close(bytes_rid);
Ok(())
}
@@ -159,6 +169,7 @@ pub(crate) async fn download_and_install<R: Runtime>(
on_event: Channel<DownloadEvent>,
headers: Option<Vec<(String, String)>>,
timeout: Option<u64>,
restart_after_install: Option<bool>,
) -> Result<()> {
let update = webview.resources_table().get::<Update>(rid)?;
@@ -176,6 +187,10 @@ pub(crate) async fn download_and_install<R: Runtime>(
update.timeout = Some(Duration::from_millis(timeout));
}
if let Some(restart_after_install) = restart_after_install {
update = update.restart_after_install(restart_after_install);
}
let mut first_chunk = true;
update
+17 -2
View File
@@ -32,17 +32,30 @@ impl WindowsUpdateInstallMode {
}
}
#[cfg(windows)]
pub(crate) fn msi_restart_after_install_args(&self) -> &'static [&'static str] {
&["AUTOLAUNCHAPP=True"]
}
/// Returns the associated nsis arguments.
pub fn nsis_args(&self) -> &'static [&'static str] {
// `/P`: Passive
// `/S`: Silent
// `/R`: Restart
match self {
Self::Passive => &["/P", "/R"],
Self::Quiet => &["/S", "/R"],
Self::Passive => &["/P"],
Self::Quiet => &["/S"],
_ => &[],
}
}
#[cfg(windows)]
pub(crate) fn nsis_restart_after_install_args(&self) -> &'static [&'static str] {
match self {
Self::BasicUi => &[],
_ => &["/R"],
}
}
}
impl Display for WindowsUpdateInstallMode {
@@ -63,6 +76,8 @@ impl Display for WindowsUpdateInstallMode {
#[serde(rename_all = "camelCase")]
pub struct WindowsConfig {
/// Additional arguments given to the NSIS or WiX installer.
///
/// Note: this applies to both WiX and NSIS installers
#[serde(
default,
alias = "installer-args",
+3 -3
View File
@@ -83,9 +83,9 @@ impl<R: Runtime, T: Manager<R>> UpdaterExt<R> for T {
builder = builder.target(target);
}
let args = self.env().args_os;
if !args.is_empty() {
builder = builder.current_exe_args(args);
#[cfg(windows)]
{
builder = builder.current_exe_args(self.env().args_os);
}
builder.version_comparator = version_comparator.clone();
+212 -148
View File
@@ -129,16 +129,34 @@ impl RemoteRelease {
pub type OnBeforeExit = Arc<dyn Fn() + Send + Sync + 'static>;
pub type OnBeforeRequest = Arc<dyn Fn(ClientBuilder) -> ClientBuilder + Send + Sync + 'static>;
pub type VersionComparator = Arc<dyn Fn(Version, RemoteRelease) -> bool + Send + Sync>;
#[cfg(target_os = "macos")]
type MainThreadClosure = Box<dyn FnOnce() + Send + Sync + 'static>;
type RunOnMainThread =
Box<dyn Fn(MainThreadClosure) -> std::result::Result<(), tauri::Error> + Send + Sync + 'static>;
#[cfg(target_os = "macos")]
type RunOnMainThread = Arc<dyn Fn(MainThreadClosure) -> tauri::Result<()> + Send + Sync + 'static>;
// TODO: Move more fields to this in v3 if we can mark those fields non `pub`
/// Updater context shared between [`UpdaterBuilder`], [`Updater`] and [`Update`]
#[derive(Clone)]
struct UpdaterContext {
config: Config,
configure_client: Option<OnBeforeRequest>,
#[cfg(target_os = "macos")]
run_on_main_thread: RunOnMainThread,
/// App name, used for creating named tempfiles
#[cfg(windows)]
app_name: String,
#[cfg(windows)]
installer_args: Vec<OsString>,
#[cfg(windows)]
current_exe_args: Vec<OsString>,
#[cfg(windows)]
on_before_exit: Option<OnBeforeExit>,
#[cfg(windows)]
restart_after_install: bool,
}
pub struct UpdaterBuilder {
#[allow(dead_code)]
run_on_main_thread: RunOnMainThread,
app_name: String,
current_version: Version,
config: Config,
pub(crate) version_comparator: Option<VersionComparator>,
executable_path: Option<PathBuf>,
target: Option<String>,
@@ -147,27 +165,38 @@ pub struct UpdaterBuilder {
timeout: Option<Duration>,
proxy: Option<Url>,
no_proxy: bool,
installer_args: Vec<OsString>,
current_exe_args: Vec<OsString>,
on_before_exit: Option<OnBeforeExit>,
configure_client: Option<OnBeforeRequest>,
context: UpdaterContext,
}
impl UpdaterBuilder {
pub(crate) fn new<R: Runtime>(app: &AppHandle<R>, config: crate::Config) -> Self {
let app_ = app.clone();
let run_on_main_thread = move |f| app_.run_on_main_thread(f);
#[cfg(target_os = "macos")]
let run_on_main_thread = {
let app_ = app.clone();
Arc::new(move |f| app_.run_on_main_thread(f))
};
Self {
run_on_main_thread: Box::new(run_on_main_thread),
installer_args: config
.windows
.as_ref()
.map(|w| w.installer_args.clone())
.unwrap_or_default(),
current_exe_args: Vec::new(),
app_name: app.package_info().name.clone(),
context: UpdaterContext {
#[cfg(windows)]
installer_args: config
.windows
.as_ref()
.map(|w| w.installer_args.clone())
.unwrap_or_default(),
config,
configure_client: None,
#[cfg(target_os = "macos")]
run_on_main_thread,
#[cfg(windows)]
app_name: app.package_info().name.clone(),
#[cfg(windows)]
current_exe_args: Vec::new(),
#[cfg(windows)]
on_before_exit: None,
#[cfg(windows)]
restart_after_install: true,
},
current_version: app.package_info().version.clone(),
config,
version_comparator: None,
executable_path: None,
target: None,
@@ -176,8 +205,6 @@ impl UpdaterBuilder {
timeout: None,
proxy: None,
no_proxy: false,
on_before_exit: None,
configure_client: None,
}
}
@@ -197,7 +224,7 @@ impl UpdaterBuilder {
pub fn endpoints(mut self, endpoints: Vec<Url>) -> Result<Self> {
crate::config::validate_endpoints(
&endpoints,
self.config.dangerous_insecure_transport_protocol,
self.context.config.dangerous_insecure_transport_protocol,
)?;
self.endpoints.replace(endpoints);
@@ -251,26 +278,40 @@ impl UpdaterBuilder {
}
pub fn pubkey<S: Into<String>>(mut self, pubkey: S) -> Self {
self.config.pubkey = pubkey.into();
self.context.config.pubkey = pubkey.into();
self
}
/// Adds an argument to pass to the Windows installer.
///
/// Note: this applies to both WiX and NSIS installers
#[cfg_attr(not(windows), allow(unused))]
pub fn installer_arg<S>(mut self, arg: S) -> Self
where
S: Into<OsString>,
{
self.installer_args.push(arg.into());
#[cfg(windows)]
{
self.context.installer_args.push(arg.into());
}
self
}
/// Adds multiple arguments to pass to the Windows installer.
///
/// Note: this applies to both WiX and NSIS installers
#[cfg_attr(not(windows), allow(unused))]
pub fn installer_args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<OsString>,
{
self.installer_args.extend(args.into_iter().map(Into::into));
#[cfg(windows)]
{
self.context
.installer_args
.extend(args.into_iter().map(Into::into));
}
self
}
@@ -280,14 +321,32 @@ impl UpdaterBuilder {
/// [`Self::installer_arg`], [`crate::Builder::installer_arg`]
/// and the `plugins > updater > windows > installerArgs` config,
/// not the ones managed by us (e.g. `/UPDATER` flag passed to the NSIS installer)
#[cfg_attr(not(windows), allow(unused))]
pub fn clear_installer_args(mut self) -> Self {
self.installer_args.clear();
#[cfg(windows)]
{
self.context.installer_args.clear();
}
self
}
/// Function to run before we run the installer and exit the app through `std::process::exit(0)` on Windows
#[cfg_attr(not(windows), allow(unused))]
pub fn on_before_exit<F: Fn() + Send + Sync + 'static>(mut self, f: F) -> Self {
self.on_before_exit.replace(Arc::new(f));
#[cfg(windows)]
{
self.context.on_before_exit.replace(Arc::new(f));
}
self
}
/// If the Windows installer should restart the app after installed, default is `true`
#[cfg_attr(not(windows), allow(unused))]
pub fn restart_after_install(mut self, restart_after_install: bool) -> Self {
#[cfg(windows)]
{
self.context.restart_after_install = restart_after_install;
}
self
}
@@ -299,14 +358,14 @@ impl UpdaterBuilder {
mut self,
f: F,
) -> Self {
self.configure_client.replace(Arc::new(f));
self.context.configure_client.replace(Arc::new(f));
self
}
pub fn build(self) -> Result<Updater> {
let endpoints = self
.endpoints
.unwrap_or_else(|| self.config.endpoints.clone());
.unwrap_or_else(|| self.context.config.endpoints.clone());
if endpoints.is_empty() {
return Err(Error::EmptyEndpoints);
@@ -324,44 +383,36 @@ impl UpdaterBuilder {
};
Ok(Updater {
run_on_main_thread: Arc::new(self.run_on_main_thread),
config: self.config,
app_name: self.app_name,
current_version: self.current_version,
version_comparator: self.version_comparator,
timeout: self.timeout,
proxy: self.proxy,
no_proxy: self.no_proxy,
endpoints,
installer_args: self.installer_args,
current_exe_args: self.current_exe_args,
arch,
target: self.target,
headers: self.headers,
extract_path,
on_before_exit: self.on_before_exit,
configure_client: self.configure_client,
context: self.context.clone(),
})
}
}
#[cfg(windows)]
impl UpdaterBuilder {
pub(crate) fn current_exe_args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<OsString>,
{
self.current_exe_args
self.context
.current_exe_args
.extend(args.into_iter().map(Into::into));
self
}
}
pub struct Updater {
#[allow(dead_code)]
run_on_main_thread: Arc<RunOnMainThread>,
config: Config,
app_name: String,
current_version: Version,
version_comparator: Option<VersionComparator>,
timeout: Option<Duration>,
@@ -374,12 +425,7 @@ pub struct Updater {
target: Option<String>,
headers: HeaderMap,
extract_path: PathBuf,
on_before_exit: Option<OnBeforeExit>,
configure_client: Option<OnBeforeRequest>,
#[allow(unused)]
installer_args: Vec<OsString>,
#[allow(unused)]
current_exe_args: Vec<OsString>,
context: UpdaterContext,
}
impl Updater {
@@ -449,10 +495,10 @@ impl Updater {
}
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT);
if self.config.dangerous_accept_invalid_certs {
if self.context.config.dangerous_accept_invalid_certs {
request = request.danger_accept_invalid_certs(true);
}
if self.config.dangerous_accept_invalid_hostnames {
if self.context.config.dangerous_accept_invalid_hostnames {
request = request.danger_accept_invalid_hostnames(true);
}
if let Some(timeout) = self.timeout {
@@ -467,7 +513,7 @@ impl Updater {
request = request.proxy(proxy);
}
if let Some(ref configure_client) = self.configure_client {
if let Some(ref configure_client) = self.context.configure_client {
request = configure_client(request);
}
@@ -537,10 +583,6 @@ impl Updater {
let update = if should_update {
Some(Update {
run_on_main_thread: self.run_on_main_thread.clone(),
config: self.config.clone(),
on_before_exit: self.on_before_exit.clone(),
app_name: self.app_name.clone(),
current_version: self.current_version.to_string(),
target: target.to_owned(),
extract_path: self.extract_path.clone(),
@@ -554,9 +596,7 @@ impl Updater {
proxy: self.proxy.clone(),
no_proxy: self.no_proxy,
headers: self.headers.clone(),
installer_args: self.installer_args.clone(),
current_exe_args: self.current_exe_args.clone(),
configure_client: self.configure_client.clone(),
context: self.context.clone(),
})
} else {
None
@@ -600,11 +640,6 @@ impl Updater {
#[derive(Clone)]
pub struct Update {
#[allow(dead_code)]
run_on_main_thread: Arc<RunOnMainThread>,
config: Config,
#[allow(unused)]
on_before_exit: Option<OnBeforeExit>,
/// Update description
pub body: Option<String>,
/// Version used to check for update
@@ -633,14 +668,7 @@ pub struct Update {
/// Extract path
#[allow(unused)]
extract_path: PathBuf,
/// App name, used for creating named tempfiles on Windows
#[allow(unused)]
app_name: String,
#[allow(unused)]
installer_args: Vec<OsString>,
#[allow(unused)]
current_exe_args: Vec<OsString>,
configure_client: Option<OnBeforeRequest>,
context: UpdaterContext,
}
impl Resource for Update {}
@@ -661,10 +689,10 @@ impl Update {
}
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT);
if self.config.dangerous_accept_invalid_certs {
if self.context.config.dangerous_accept_invalid_certs {
request = request.danger_accept_invalid_certs(true);
}
if self.config.dangerous_accept_invalid_hostnames {
if self.context.config.dangerous_accept_invalid_hostnames {
request = request.danger_accept_invalid_hostnames(true);
}
if let Some(timeout) = self.timeout {
@@ -676,7 +704,7 @@ impl Update {
let proxy = reqwest::Proxy::all(proxy.as_str())?;
request = request.proxy(proxy);
}
if let Some(ref configure_client) = self.configure_client {
if let Some(ref configure_client) = self.context.configure_client {
request = configure_client(request);
}
let response = request
@@ -709,7 +737,7 @@ impl Update {
}
on_download_finish();
verify_signature(&buffer, &self.signature, &self.config.pubkey)?;
verify_signature(&buffer, &self.signature, &self.context.config.pubkey)?;
Ok(buffer)
}
@@ -733,6 +761,16 @@ impl Update {
fn install_inner(&self, _bytes: &[u8]) -> Result<()> {
Ok(())
}
/// Whether the Windows installer should restart the app after installed, default is `true`
#[cfg_attr(not(windows), allow(unused))]
pub fn restart_after_install(mut self, restart_after_install: bool) -> Self {
#[cfg(windows)]
{
self.context.restart_after_install = restart_after_install;
}
self
}
}
#[cfg(windows)]
@@ -785,7 +823,6 @@ impl Update {
/// │ └──[AppName]_[version]_x64-setup.exe # NSIS installer
/// └── ...
fn install_inner(&self, bytes: &[u8]) -> Result<()> {
use std::iter::once;
use windows_sys::{
w,
Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW},
@@ -793,48 +830,7 @@ impl Update {
let updater_type = self.extract(bytes)?;
let install_mode = self.config.install_mode();
let current_args = &self.current_exe_args()[1..];
let msi_args;
let nsis_args;
let installer_args: Vec<&OsStr> = match &updater_type {
WindowsUpdaterType::Nsis { .. } => {
nsis_args = current_args
.iter()
.map(escape_nsis_current_exe_arg)
.collect::<Vec<_>>();
install_mode
.nsis_args()
.iter()
.map(OsStr::new)
.chain(once(OsStr::new("/UPDATE")))
.chain(once(OsStr::new("/ARGS")))
.chain(nsis_args.iter().map(OsStr::new))
.chain(self.installer_args())
.collect()
}
WindowsUpdaterType::Msi { path, .. } => {
let escaped_args = current_args
.iter()
.map(escape_msi_property_arg)
.collect::<Vec<_>>()
.join(" ");
msi_args = OsString::from(format!("LAUNCHAPPARGS=\"{escaped_args}\""));
[OsStr::new("/i"), path.as_os_str()]
.into_iter()
.chain(install_mode.msiexec_args().iter().map(OsStr::new))
.chain(once(OsStr::new("/promptrestart")))
.chain(self.installer_args())
.chain(once(OsStr::new("AUTOLAUNCHAPP=True")))
.chain(once(msi_args.as_os_str()))
.collect()
}
};
if let Some(on_before_exit) = self.on_before_exit.as_ref() {
if let Some(on_before_exit) = self.context.on_before_exit.as_ref() {
log::debug!("running on_before_exit hook");
on_before_exit();
}
@@ -846,9 +842,11 @@ impl Update {
|p| OsString::from(format!("{p}\\System32\\msiexec.exe")),
),
};
let file = encode_wide(file);
let parameters = self.updater_parameters(&updater_type);
let parameters = installer_args.join(OsStr::new(" "));
log::debug!("Executing updater {file:?} with parameters: {parameters:?}");
let file = encode_wide(file);
let parameters = encode_wide(parameters);
unsafe {
@@ -865,18 +863,72 @@ impl Update {
std::process::exit(0);
}
fn installer_args(&self) -> Vec<&OsStr> {
self.installer_args
.iter()
.map(OsStr::new)
.collect::<Vec<_>>()
fn updater_parameters(&self, updater_type: &WindowsUpdaterType) -> OsString {
let install_mode = self.context.config.install_mode();
let current_args = &self.context.current_exe_args[1..];
match updater_type {
WindowsUpdaterType::Nsis { .. } => {
let mut installer_args: Vec<&OsStr> = Vec::new();
installer_args.extend(install_mode.nsis_args().iter().map(OsStr::new));
installer_args.push(OsStr::new("/UPDATE"));
let nsis_current_exe_arg;
if self.context.restart_after_install {
nsis_current_exe_arg = current_args
.iter()
.map(escape_nsis_current_exe_arg)
.collect::<Vec<_>>();
installer_args.extend(
install_mode
.nsis_restart_after_install_args()
.iter()
.map(OsStr::new),
);
installer_args.push(OsStr::new("/ARGS"));
installer_args.extend(nsis_current_exe_arg.iter().map(OsStr::new));
}
installer_args.extend(self.installer_args());
installer_args.join(OsStr::new(" "))
}
WindowsUpdaterType::Msi { path, .. } => {
let mut installer_args: Vec<&OsStr> = vec![OsStr::new("/i"), path.as_os_str()];
installer_args.extend(install_mode.msiexec_args().iter().map(OsStr::new));
installer_args.push(OsStr::new("/promptrestart"));
installer_args.extend(self.installer_args());
let msi_current_exe_arg;
if self.context.restart_after_install {
msi_current_exe_arg = format!(
"LAUNCHAPPARGS=\"{}\"",
current_args
.iter()
.map(escape_msi_property_arg)
.collect::<Vec<_>>()
.join(" ")
);
installer_args.extend(
install_mode
.msi_restart_after_install_args()
.iter()
.map(OsStr::new),
);
installer_args.push(OsStr::new(&msi_current_exe_arg));
}
installer_args.join(OsStr::new(" "))
}
}
}
fn current_exe_args(&self) -> Vec<&OsStr> {
self.current_exe_args
.iter()
.map(OsStr::new)
.collect::<Vec<_>>()
fn installer_args(
&self,
) -> std::iter::Map<std::slice::Iter<'_, OsString>, fn(&OsString) -> &OsStr> {
self.context.installer_args.iter().map(OsStr::new)
}
fn extract(&self, bytes: &[u8]) -> Result<WindowsUpdaterType> {
@@ -890,7 +942,10 @@ impl Update {
fn make_temp_dir(&self) -> Result<PathBuf> {
Ok(tempfile::Builder::new()
.prefix(&format!("{}-{}-updater-", self.app_name, self.version))
.prefix(&format!(
"{}-{}-updater-",
self.context.app_name, self.version
))
.tempdir()?
.keep())
}
@@ -938,7 +993,10 @@ impl Update {
let temp_dir = self.make_temp_dir()?;
let mut temp_file = tempfile::Builder::new()
.prefix(&format!("{}-{}-installer", self.app_name, self.version))
.prefix(&format!(
"{}-{}-installer",
self.context.app_name, self.version
))
.suffix(ext)
.rand_bytes(0)
.tempfile_in(temp_dir)?;
@@ -1277,7 +1335,7 @@ impl Update {
);
let (tx, rx) = std::sync::mpsc::channel();
let res = (self.run_on_main_thread)(Box::new(move || {
let res = (self.context.run_on_main_thread)(Box::new(move || {
let mut script =
osakit::Script::new_from_source(osakit::Language::AppleScript, &apple_script);
script.compile().expect("invalid AppleScript");
@@ -1450,7 +1508,7 @@ where
}
// Validate signature
fn verify_signature(data: &[u8], release_signature: &str, pub_key: &str) -> Result<bool> {
fn verify_signature(data: &[u8], release_signature: &str, pub_key: &str) -> Result<()> {
// we need to convert the pub key
let pub_key_decoded = base64_to_string(pub_key)?;
let public_key = PublicKey::decode(&pub_key_decoded)?;
@@ -1459,7 +1517,7 @@ fn verify_signature(data: &[u8], release_signature: &str, pub_key: &str) -> Resu
// Validate signature or bail out
public_key.verify(data, &signature, true)?;
Ok(true)
Ok(())
}
fn base64_to_string(base64_string: &str) -> Result<String> {
@@ -1498,25 +1556,31 @@ impl PathExt for PathBuf {
// adapted from https://github.com/rust-lang/rust/blob/1c047506f94cd2d05228eb992b0a6bbed1942349/library/std/src/sys/args/windows.rs#L174
#[cfg(windows)]
fn escape_nsis_current_exe_arg(arg: &&OsStr) -> String {
let arg = arg.to_string_lossy();
let mut cmd: Vec<char> = Vec::new();
fn escape_nsis_current_exe_arg(arg: impl AsRef<OsStr>) -> OsString {
use std::os::windows::ffi::{OsStrExt, OsStringExt};
let arg = arg.as_ref();
let mut cmd: Vec<u16> = Vec::new();
// compared to std we additionally escape `/` so that nsis won't interpret them as a beginning of an nsis argument.
let quote = arg.chars().any(|c| c == ' ' || c == '\t' || c == '/') || arg.is_empty();
let quote = arg
.as_encoded_bytes()
.iter()
.any(|c| *c == b' ' || *c == b'\t' || *c == b'/')
|| arg.is_empty();
let escape = true;
if quote {
cmd.push('"');
cmd.push('"' as u16);
}
let mut backslashes: usize = 0;
for x in arg.chars() {
for x in arg.encode_wide() {
if escape {
if x == '\\' {
if x == '\\' as u16 {
backslashes += 1;
} else {
if x == '"' {
if x == '"' as u16 {
// Add n+1 backslashes to total 2n+1 before internal '"'.
cmd.extend((0..=backslashes).map(|_| '\\'));
cmd.extend((0..=backslashes).map(|_| '\\' as u16));
}
backslashes = 0;
}
@@ -1525,10 +1589,10 @@ fn escape_nsis_current_exe_arg(arg: &&OsStr) -> String {
}
if quote {
// Add n backslashes to total 2n before ending '"'.
cmd.extend((0..backslashes).map(|_| '\\'));
cmd.push('"');
cmd.extend((0..backslashes).map(|_| '\\' as u16));
cmd.push('"' as u16);
}
cmd.into_iter().collect()
OsString::from_wide(&cmd)
}
#[cfg(windows)]
@@ -6,8 +6,7 @@
"endpoints": ["http://localhost:3007"],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUwNDRGMjkwRjg2MDhCRDAKUldUUWkyRDRrUEpFNEQ4SmdwcU5PaXl6R2ZRUUNvUnhIaVkwVUltV0NMaEx6VTkrWVhpT0ZqeEEK",
"windows": {
"installMode": "quiet",
"installerArgs": ["/NS"]
"installMode": "quiet"
}
}
},
@@ -46,7 +46,7 @@ struct Update {
platforms: HashMap<String, PlatformUpdate>,
}
fn build_app(cwd: &Path, config: &Config, bundle_updater: bool, target: BundleTarget) {
fn build_app(cwd: &Path, config: &Config, target: Option<BundleTarget>) {
let mut command = Command::new("cargo");
command
.args(["tauri", "build", "--verbose"])
@@ -56,17 +56,10 @@ fn build_app(cwd: &Path, config: &Config, bundle_updater: bool, target: BundleTa
.env("TAURI_SIGNING_PRIVATE_KEY_PASSWORD", "")
.current_dir(cwd);
#[cfg(target_os = "linux")]
command.args(["--bundles", target.name()]);
#[cfg(target_os = "macos")]
command.args(["--bundles", target.name()]);
if bundle_updater {
#[cfg(windows)]
command.args(["--bundles", "msi", "nsis"]);
if let Some(target) = target {
command.arg("--bundles").arg(target.name());
} else {
#[cfg(windows)]
command.args(["--bundles", target.name()]);
command.arg("--no-bundle");
}
let status = command
@@ -318,7 +311,7 @@ fn update_app() {
{
// bundle app update
config.version = "1.0.0";
build_app(&manifest_dir, &config, true, BundleTarget::default());
build_app(&manifest_dir, &config, Some(bundle_target));
let bundle_updater_ext = if v1_compatible {
out_bundle_path
@@ -401,7 +394,7 @@ fn update_app() {
config.version = "0.1.0";
// bundle initial app version
build_app(&manifest_dir, &config, false, bundle_target);
build_app(&manifest_dir, &config, None);
for expected_exit_code in status_checks {
let mut binary_cmd = if cfg!(windows) {
+9 -10
View File
@@ -1822,15 +1822,14 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "openssl"
version = "0.10.78"
version = "0.10.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222"
checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
@@ -1854,9 +1853,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.114"
version = "0.9.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6"
checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4"
dependencies = [
"cc",
"libc",
@@ -2951,9 +2950,9 @@ dependencies = [
[[package]]
name = "tar"
version = "0.4.45"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973"
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [
"filetime",
"libc",
@@ -3795,7 +3794,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]
@@ -4138,9 +4137,9 @@ dependencies = [
[[package]]
name = "wry"
version = "0.24.10"
version = "0.24.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00711278ed357350d44c749c286786ecac644e044e4da410d466212152383b45"
checksum = "4a2a144c3ab5e83e04724bc8e67cea552ffae413185fda459fafdae173fd985d"
dependencies = [
"base64 0.13.1",
"block",
+6 -3
View File
@@ -66,7 +66,8 @@ import { upload, HttpMethod } from '@tauri-apps/plugin-upload'
upload(
'https://example.com/file-upload',
'./path/to/my/file.txt',
(progress, total) => console.log(`Uploaded ${progress} of ${total} bytes`), // a callback that will be called with the upload progress
({ progressTotal, total }) =>
console.log(`Uploaded ${progressTotal} of ${total} bytes`), // a callback that will be called with the upload progress
{ 'Content-Type': 'text/plain' } // optional headers to send with the request
)
@@ -74,7 +75,8 @@ upload(
upload(
'https://example.com/file-upload',
'./path/to/my/file.txt',
(progress, total) => console.log(`Uploaded ${progress} of ${total} bytes`),
({ progressTotal, total }) =>
console.log(`Uploaded ${progressTotal} of ${total} bytes`),
{ 'Content-Type': 'text/plain' },
HttpMethod.Put // Use HttpMethod enum - supports POST, PUT, PATCH
)
@@ -86,7 +88,8 @@ import { download } from '@tauri-apps/plugin-upload'
download(
'https://example.com/file-download-link',
'./path/to/save/my/file.txt',
(progress, total) => console.log(`Downloaded ${progress} of ${total} bytes`), // a callback that will be called with the download progress
({ progressTotal, total }) =>
console.log(`Downloaded ${progressTotal} of ${total} bytes`), // a callback that will be called with the download progress
{ 'Content-Type': 'text/plain' } // optional headers to send with the request
)
```
@@ -9,7 +9,7 @@
"preview": "vite preview"
},
"devDependencies": {
"@tauri-apps/cli": "2.11.0",
"@tauri-apps/cli": "2.11.4",
"typescript": "^6.0.0",
"vite": "^8.0.1"
},
+36 -36
View File
@@ -452,47 +452,47 @@ impl Builder {
}
}
WindowEvent::Moved(position) if state_flags.contains(StateFlags::POSITION) => {
if window_clone
.state::<RestoringWindowState>()
.0
.try_lock()
.is_ok()
&& !window_clone.is_minimized().unwrap_or_default()
{
let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) {
state.prev_x = state.x;
state.prev_y = state.y;
WindowEvent::Moved(position)
if state_flags.contains(StateFlags::POSITION)
&& window_clone
.state::<RestoringWindowState>()
.0
.try_lock()
.is_ok()
&& !window_clone.is_minimized().unwrap_or_default() =>
{
let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) {
state.prev_x = state.x;
state.prev_y = state.y;
state.x = position.x;
state.y = position.y;
}
state.x = position.x;
state.y = position.y;
}
}
WindowEvent::Resized(size) if state_flags.contains(StateFlags::SIZE) => {
if window_clone
.state::<RestoringWindowState>()
.0
.try_lock()
.is_ok()
WindowEvent::Resized(size)
if state_flags.contains(StateFlags::SIZE)
&& window_clone
.state::<RestoringWindowState>()
.0
.try_lock()
.is_ok() =>
{
// TODO: Remove once https://github.com/tauri-apps/tauri/issues/5812 is resolved.
let is_maximized = if cfg!(target_os = "macos")
&& (!window_clone.is_decorated().unwrap_or_default()
|| !window_clone.is_resizable().unwrap_or_default())
{
// TODO: Remove once https://github.com/tauri-apps/tauri/issues/5812 is resolved.
let is_maximized = if cfg!(target_os = "macos")
&& (!window_clone.is_decorated().unwrap_or_default()
|| !window_clone.is_resizable().unwrap_or_default())
{
false
} else {
window_clone.is_maximized().unwrap_or_default()
};
false
} else {
window_clone.is_maximized().unwrap_or_default()
};
if !window_clone.is_minimized().unwrap_or_default() && !is_maximized {
let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) {
state.width = size.width;
state.height = size.height;
}
if !window_clone.is_minimized().unwrap_or_default() && !is_maximized {
let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) {
state.width = size.width;
state.height = size.height;
}
}
}
+551 -549
View File
File diff suppressed because it is too large Load Diff

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