Compare commits

...
Author SHA1 Message Date
renovate[bot]andGitHub e4f5e4b967 chore(deps): update rust crate base64 to 0.23 2026-07-26 17:30:17 +00:00
TonyandGitHub 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
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
TonyandGitHub 7e45774610 refactor(fs): cfg gate some ios code (#3510) 2026-07-21 14:42:45 +08:00
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]GitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Tony
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
TonyandGitHub 2ed6d6c1de fix(store): StoreOptions.defaults should not be required (#3499) 2026-07-18 19:46:31 +08:00
TonyandGitHub 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
TonyandGitHub 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
TonyandGitHub 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
TonyandGitHub edc52ea056 chore(deps): bump create-pull-request to v8 (#3489) 2026-07-15 23:49:37 +08:00
TonyandGitHub 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
TonyandGitHub 13c63af965 chore: fix clippy (#3488) 2026-07-15 16:01:07 +08:00
github-actions[bot]GitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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
BajocaandGitHub 254f222e0e chore(log): use kv stable feature rather than unstable (#3477) 2026-07-13 13:23:56 +02:00
TonyandGitHub b1439be667 chore: ignore cargo audit errors (#3481)
* chore: ignore cargo audit errors

* Bump anyhow
2026-07-13 16:51:50 +08:00
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
TonyandGitHub 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
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]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Tony
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
nandanpugaliaandGitHub aecec42d5d docs(http): fix connectTimeout option in README (#3451) 2026-06-15 10:08:31 +02:00
renovate[bot]andGitHub 0767dcbd5c chore(deps): update dependency eslint-plugin-security to v4.0.1 (#3452) 2026-06-15 09:59:15 +02:00
Slava MinamotoandGitHub 4be7690085 fix(positioner): return error instead of panicking when window has no monitor (#3449) 2026-06-11 21:05:20 +08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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
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]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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]andGitHub 1953f00188 chore(deps): update dependency prettier to v3.8.3 (#3387) 2026-05-28 16:14:25 +02:00
github-actions[bot]andGitHub 9883f396dc Publish New Versions (v2) (#3425) 2026-05-28 12:06:17 +02:00
zankowitch alexisandGitHub 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
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
JsuandGitHub e571e0be9d docs(fs): typo wether -> whether in commands.rs (#3429) 2026-05-23 21:28:55 +02:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Tony
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 SwardtandGitHub 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]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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
99 changed files with 1939 additions and 1510 deletions
+10
View File
@@ -1,7 +1,17 @@
[advisories] [advisories]
ignore = [ ignore = [
# rsa Marvin Attack
"RUSTSEC-2023-0071",
# time crate can't be updated in the repo because of MSRV, users are unaffected # time crate can't be updated in the repo because of MSRV, users are unaffected
"RUSTSEC-2026-0009", "RUSTSEC-2026-0009",
# libflate crates can't be updated in the repo because of MSRV, users are unaffected # libflate crates can't be updated in the repo because of MSRV, users are unaffected
"RUSTSEC-2026-0105", "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: audit-js:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: Cache pnpm modules - name: Cache pnpm modules
uses: actions/cache@v4 uses: actions/cache@v6
with: with:
path: ~/.pnpm-store path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: | restore-keys: |
${{ runner.os }}- ${{ runner.os }}-
- uses: actions/setup-node@v4 - uses: actions/setup-node@v7
with: with:
node-version: 'lts/*' node-version: 'lts/*'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v6
with: with:
version: 10.x.x
run_install: true run_install: true
- name: audit - name: audit
run: pnpm audit run: pnpm audit
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
audit-rust: audit-rust:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- uses: rustsec/audit-check@v2 - uses: rustsec/audit-check@v2
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
check: check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: check change files end with .md - name: check change files end with .md
run: | run: |
+5 -6
View File
@@ -21,7 +21,7 @@ jobs:
outputs: outputs:
packages: ${{ steps.filter.outputs.changes }} packages: ${{ steps.filter.outputs.changes }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- uses: dorny/paths-filter@v2 - uses: dorny/paths-filter@v2
id: filter id: filter
with: with:
@@ -153,21 +153,20 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: Cache pnpm modules - name: Cache pnpm modules
uses: actions/cache@v4 uses: actions/cache@v6
with: with:
path: ~/.pnpm-store path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: | restore-keys: |
${{ runner.os }}- ${{ runner.os }}-
- uses: actions/setup-node@v4 - uses: actions/setup-node@v7
with: with:
node-version: 'lts/*' node-version: 'lts/*'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v6
with: with:
version: 10.x.x
run_install: true run_install: true
- name: build api - name: build api
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
check: check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- uses: dorny/paths-filter@v2 - uses: dorny/paths-filter@v2
id: filter id: filter
with: with:
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
with: with:
fetch-depth: 0 # required for use of git history fetch-depth: 0 # required for use of git history
- name: covector status - name: covector status
@@ -28,18 +28,17 @@ jobs:
successfulPublish: ${{ steps.covector.outputs.successfulPublish }} successfulPublish: ${{ steps.covector.outputs.successfulPublish }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
with: with:
fetch-depth: 0 # required for use of git history fetch-depth: 0 # required for use of git history
- uses: actions/setup-node@v4 - uses: actions/setup-node@v7
with: with:
node-version: 'lts/*' node-version: 'lts/*'
registry-url: 'https://registry.npmjs.org' registry-url: 'https://registry.npmjs.org'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v6
with: with:
version: 10.x.x
run_install: true run_install: true
- name: cargo login - name: cargo login
@@ -74,7 +73,7 @@ jobs:
- name: Create Pull Request With Versions Bumped - name: Create Pull Request With Versions Bumped
id: cpr 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' if: steps.covector.outputs.commandRan == 'version'
with: with:
title: 'Publish New Versions (${{ github.ref_name }})' title: 'Publish New Versions (${{ github.ref_name }})'
+6 -7
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: install Rust stable and rustfmt - name: install Rust stable and rustfmt
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
@@ -25,20 +25,19 @@ jobs:
prettier: prettier:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: Cache pnpm modules - name: Cache pnpm modules
uses: actions/cache@v4 uses: actions/cache@v6
with: with:
path: ~/.pnpm-store path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: | restore-keys: |
${{ runner.os }}- ${{ runner.os }}-
- uses: actions/setup-node@v4 - uses: actions/setup-node@v7
with: with:
node-version: 'lts/*' node-version: 'lts/*'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v6
with: with:
version: 10.x.x
run_install: true run_install: true
- run: pnpm format:check - run: pnpm format:check
@@ -46,7 +45,7 @@ jobs:
name: taplo (.toml files) name: taplo (.toml files)
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: install Rust stable - name: install Rust stable
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
platform: [ubuntu-22.04, macos-latest, windows-latest] platform: [ubuntu-22.04, macos-latest, windows-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
with: with:
fetch-depth: 0 fetch-depth: 0
+4 -5
View File
@@ -36,20 +36,19 @@ jobs:
eslint: eslint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: Cache pnpm modules - name: Cache pnpm modules
uses: actions/cache@v4 uses: actions/cache@v6
with: with:
path: ~/.pnpm-store path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: | restore-keys: |
${{ runner.os }}- ${{ runner.os }}-
- uses: actions/setup-node@v4 - uses: actions/setup-node@v7
with: with:
node-version: 'lts/*' node-version: 'lts/*'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v6
with: with:
version: 10.x.x
run_install: true run_install: true
- name: eslint - name: eslint
run: pnpm lint run: pnpm lint
+2 -2
View File
@@ -36,7 +36,7 @@ jobs:
outputs: outputs:
packages: ${{ steps.filter.outputs.changes }} packages: ${{ steps.filter.outputs.changes }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- uses: dorny/paths-filter@v2 - uses: dorny/paths-filter@v2
id: filter id: filter
with: with:
@@ -136,7 +136,7 @@ jobs:
package: ${{ fromJSON(needs.changes.outputs.packages) }} package: ${{ fromJSON(needs.changes.outputs.packages) }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: install webkit2gtk - name: install webkit2gtk
run: | run: |
+4 -5
View File
@@ -19,26 +19,25 @@ jobs:
sync-to-mirrors: sync-to-mirrors:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: Fetch git tags - name: Fetch git tags
run: git fetch origin 'refs/tags/*:refs/tags/*' run: git fetch origin 'refs/tags/*:refs/tags/*'
- name: Cache pnpm modules - name: Cache pnpm modules
uses: actions/cache@v4 uses: actions/cache@v6
with: with:
path: ~/.pnpm-store path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: | restore-keys: |
${{ runner.os }}- ${{ runner.os }}-
- uses: actions/setup-node@v4 - uses: actions/setup-node@v7
with: with:
node-version: 'lts/*' node-version: 'lts/*'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v6
with: with:
version: 10.x.x
run_install: true run_install: true
- name: Build packages - name: Build packages
+2 -2
View File
@@ -38,7 +38,7 @@ jobs:
outputs: outputs:
packages: ${{ steps.filter.outputs.changes }} packages: ${{ steps.filter.outputs.changes }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- uses: dorny/paths-filter@v2 - uses: dorny/paths-filter@v2
id: filter id: filter
with: with:
@@ -225,7 +225,7 @@ jobs:
runs-on: ${{ matrix.platform.os }} runs-on: ${{ matrix.platform.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- name: install webkit2gtk - name: install webkit2gtk
if: contains(matrix.platform.target, 'unknown-linux') if: contains(matrix.platform.target, 'unknown-linux')
Generated
+78 -141
View File
@@ -201,13 +201,13 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.98" version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
[[package]] [[package]]
name = "api" name = "api"
version = "2.0.43" version = "2.0.46"
dependencies = [ dependencies = [
"log", "log",
"serde", "serde",
@@ -300,7 +300,7 @@ dependencies = [
"objc2-foundation 0.3.0", "objc2-foundation 0.3.0",
"parking_lot", "parking_lot",
"percent-encoding", "percent-encoding",
"windows-sys 0.52.0", "windows-sys 0.59.0",
"wl-clipboard-rs", "wl-clipboard-rs",
"x11rb", "x11rb",
] ]
@@ -332,7 +332,7 @@ dependencies = [
"enumflags2", "enumflags2",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"rand 0.9.3", "rand 0.9.4",
"raw-window-handle", "raw-window-handle",
"serde", "serde",
"serde_repr", "serde_repr",
@@ -596,6 +596,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b25655df2c3cdd83c5e5b293b88acd880332b2ddadd7c30ac43144fdc0033da9"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
version = "1.6.0" version = "1.6.0"
@@ -772,17 +778,6 @@ version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 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]] [[package]]
name = "bytecheck" name = "bytecheck"
version = "0.6.12" version = "0.6.12"
@@ -1052,7 +1047,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"windows-sys 0.48.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -1308,7 +1303,7 @@ dependencies = [
"cssparser-macros", "cssparser-macros",
"dtoa-short", "dtoa-short",
"itoa", "itoa",
"phf 0.13.1", "phf",
"smallvec", "smallvec",
] ]
@@ -1873,7 +1868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -2470,9 +2465,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]] [[package]]
name = "global-hotkey" name = "global-hotkey"
version = "0.7.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7" checksum = "8c386b0a4a70cb2d39fffd74480f985b6f0bfbcb934b6a6b6b7e630e448f242e"
dependencies = [ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"keyboard-types", "keyboard-types",
@@ -3093,7 +3088,7 @@ dependencies = [
"iterator-sorted", "iterator-sorted",
"k256", "k256",
"pbkdf2", "pbkdf2",
"rand 0.8.5", "rand 0.8.6",
"scrypt", "scrypt",
"serde", "serde",
"sha2", "sha2",
@@ -3474,9 +3469,6 @@ name = "log"
version = "0.4.27" version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
dependencies = [
"value-bag",
]
[[package]] [[package]]
name = "lru-slab" name = "lru-slab"
@@ -3607,7 +3599,7 @@ dependencies = [
"hyper", "hyper",
"hyper-util", "hyper-util",
"log", "log",
"rand 0.9.3", "rand 0.9.4",
"regex", "regex",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
@@ -3784,7 +3776,7 @@ dependencies = [
"num-integer", "num-integer",
"num-iter", "num-iter",
"num-traits", "num-traits",
"rand 0.8.5", "rand 0.8.6",
"smallvec", "smallvec",
"zeroize", "zeroize",
] ]
@@ -4351,24 +4343,14 @@ dependencies = [
"indexmap 2.11.4", "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]] [[package]]
name = "phf" name = "phf"
version = "0.13.1" version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
dependencies = [ dependencies = [
"phf_macros 0.13.1", "phf_macros",
"phf_shared 0.13.1", "phf_shared",
"serde", "serde",
] ]
@@ -4378,18 +4360,8 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
dependencies = [ dependencies = [
"phf_generator 0.13.1", "phf_generator",
"phf_shared 0.13.1", "phf_shared",
]
[[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",
] ]
[[package]] [[package]]
@@ -4399,20 +4371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
dependencies = [ dependencies = [
"fastrand", "fastrand",
"phf_shared 0.13.1", "phf_shared",
]
[[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",
] ]
[[package]] [[package]]
@@ -4421,22 +4380,13 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
dependencies = [ dependencies = [
"phf_generator 0.13.1", "phf_generator",
"phf_shared 0.13.1", "phf_shared",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.100", "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]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.13.1" version = "0.13.1"
@@ -4750,7 +4700,7 @@ dependencies = [
"bytes", "bytes",
"getrandom 0.3.2", "getrandom 0.3.2",
"lru-slab", "lru-slab",
"rand 0.9.3", "rand 0.9.4",
"ring", "ring",
"rustc-hash", "rustc-hash",
"rustls", "rustls",
@@ -4773,7 +4723,7 @@ dependencies = [
"once_cell", "once_cell",
"socket2", "socket2",
"tracing", "tracing",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -4799,9 +4749,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.5" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [ dependencies = [
"libc", "libc",
"rand_chacha 0.3.1", "rand_chacha 0.3.1",
@@ -4810,9 +4760,9 @@ dependencies = [
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.9.3" version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
dependencies = [ dependencies = [
"rand_chacha 0.9.0", "rand_chacha 0.9.0",
"rand_core 0.9.3", "rand_core 0.9.3",
@@ -5186,7 +5136,7 @@ dependencies = [
"borsh", "borsh",
"bytes", "bytes",
"num-traits", "num-traits",
"rand 0.8.5", "rand 0.8.6",
"rkyv", "rkyv",
"serde", "serde",
"serde_json", "serde_json",
@@ -5223,7 +5173,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.4.15", "linux-raw-sys 0.4.15",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -5481,7 +5431,7 @@ dependencies = [
"derive_more", "derive_more",
"log", "log",
"new_debug_unreachable", "new_debug_unreachable",
"phf 0.13.1", "phf",
"phf_codegen", "phf_codegen",
"precomputed-hash", "precomputed-hash",
"rustc-hash", "rustc-hash",
@@ -5995,7 +5945,7 @@ dependencies = [
"memchr", "memchr",
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"rand 0.8.5", "rand 0.8.6",
"rsa", "rsa",
"rust_decimal", "rust_decimal",
"serde", "serde",
@@ -6036,7 +5986,7 @@ dependencies = [
"md-5", "md-5",
"memchr", "memchr",
"once_cell", "once_cell",
"rand 0.8.5", "rand 0.8.6",
"rust_decimal", "rust_decimal",
"serde", "serde",
"serde_json", "serde_json",
@@ -6097,7 +6047,7 @@ checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901"
dependencies = [ dependencies = [
"new_debug_unreachable", "new_debug_unreachable",
"parking_lot", "parking_lot",
"phf_shared 0.13.1", "phf_shared",
"precomputed-hash", "precomputed-hash",
] ]
@@ -6107,8 +6057,8 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69"
dependencies = [ dependencies = [
"phf_generator 0.13.1", "phf_generator",
"phf_shared 0.13.1", "phf_shared",
"proc-macro2", "proc-macro2",
"quote", "quote",
] ]
@@ -6147,7 +6097,7 @@ dependencies = [
"libsodium-sys-stable", "libsodium-sys-stable",
"log", "log",
"nix 0.24.3", "nix 0.24.3",
"rand 0.8.5", "rand 0.8.6",
"serde", "serde",
"thiserror 1.0.69", "thiserror 1.0.69",
"windows 0.36.1", "windows 0.36.1",
@@ -6160,7 +6110,7 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8300214898af5e153e7f66e49dbd1c6a21585f2d592d9f24f58b969792475ed6" checksum = "8300214898af5e153e7f66e49dbd1c6a21585f2d592d9f24f58b969792475ed6"
dependencies = [ dependencies = [
"rand 0.8.5", "rand 0.8.6",
"stronghold-derive", "stronghold-derive",
] ]
@@ -6348,9 +6298,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]] [[package]]
name = "tar" name = "tar"
version = "0.4.45" version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [ dependencies = [
"filetime", "filetime",
"libc", "libc",
@@ -6365,9 +6315,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]] [[package]]
name = "tauri" name = "tauri"
version = "2.11.0" version = "2.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d059f2527558d9dba6f186dec4772610e1aecfd3f94002397613e7e648752b66" checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@@ -6420,9 +6370,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-build" name = "tauri-build"
version = "2.6.0" version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be9aa8c59a894f76c29a002501c589de5eb4987a5913d62a6e0a47f320901988" checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cargo_toml", "cargo_toml",
@@ -6443,9 +6393,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-codegen" name = "tauri-codegen"
version = "2.6.0" version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3e4e8230d565106aa19dfbaa01a7ed01abf78047fe0577a83377224bd1bf20e" checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"ico", "ico",
@@ -6469,9 +6419,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-macros" name = "tauri-macros"
version = "2.6.0" version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc8de2cddbbc33dbdf4c84f170121886595efdbcc9cb4b3d76342b79d082cedc" checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@@ -6511,7 +6461,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-barcode-scanner" name = "tauri-plugin-barcode-scanner"
version = "2.4.4" version = "2.4.5"
dependencies = [ dependencies = [
"log", "log",
"serde", "serde",
@@ -6581,7 +6531,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-dialog" name = "tauri-plugin-dialog"
version = "2.7.1" version = "2.7.2"
dependencies = [ dependencies = [
"log", "log",
"raw-window-handle", "raw-window-handle",
@@ -6634,7 +6584,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-global-shortcut" name = "tauri-plugin-global-shortcut"
version = "2.3.1" version = "2.3.2"
dependencies = [ dependencies = [
"global-hotkey", "global-hotkey",
"log", "log",
@@ -6696,10 +6646,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-log" name = "tauri-plugin-log"
version = "2.8.0" version = "2.9.0"
dependencies = [ dependencies = [
"android_logger", "android_logger",
"byte-unit",
"fern", "fern",
"log", "log",
"objc2 0.6.4", "objc2 0.6.4",
@@ -6734,7 +6683,7 @@ version = "2.3.3"
dependencies = [ dependencies = [
"log", "log",
"notify-rust", "notify-rust",
"rand 0.9.3", "rand 0.9.4",
"serde", "serde",
"serde_json", "serde_json",
"serde_repr", "serde_repr",
@@ -6799,7 +6748,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-positioner" name = "tauri-plugin-positioner"
version = "2.3.1" version = "2.3.3"
dependencies = [ dependencies = [
"log", "log",
"serde", "serde",
@@ -6839,7 +6788,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-single-instance" name = "tauri-plugin-single-instance"
version = "2.4.2" version = "2.4.3"
dependencies = [ dependencies = [
"semver", "semver",
"serde", "serde",
@@ -6847,6 +6796,7 @@ dependencies = [
"tauri", "tauri",
"tauri-plugin-deep-link", "tauri-plugin-deep-link",
"thiserror 2.0.12", "thiserror 2.0.12",
"tokio",
"tracing", "tracing",
"windows-sys 0.60.2", "windows-sys 0.60.2",
"zbus", "zbus",
@@ -6873,7 +6823,7 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-store" name = "tauri-plugin-store"
version = "2.4.3" version = "2.4.4"
dependencies = [ dependencies = [
"dunce", "dunce",
"serde", "serde",
@@ -6893,7 +6843,7 @@ dependencies = [
"iota-crypto", "iota-crypto",
"iota_stronghold", "iota_stronghold",
"log", "log",
"rand 0.9.3", "rand 0.9.4",
"rand_chacha 0.9.0", "rand_chacha 0.9.0",
"rand_core 0.9.3", "rand_core 0.9.3",
"rust-argon2 2.1.0", "rust-argon2 2.1.0",
@@ -6910,7 +6860,7 @@ dependencies = [
name = "tauri-plugin-updater" name = "tauri-plugin-updater"
version = "2.10.1" version = "2.10.1"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.23.0",
"dirs 6.0.0", "dirs 6.0.0",
"flate2", "flate2",
"futures-util", "futures-util",
@@ -6962,7 +6912,7 @@ dependencies = [
"futures-util", "futures-util",
"http", "http",
"log", "log",
"rand 0.9.3", "rand 0.9.4",
"rustls", "rustls",
"serde", "serde",
"serde_json", "serde_json",
@@ -6988,9 +6938,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime" name = "tauri-runtime"
version = "2.11.0" version = "2.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e42bbcb76237351fbaa02f08d808c537dc12eb5a6eabbf3e517b50056334d95" checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8"
dependencies = [ dependencies = [
"cookie", "cookie",
"dpi", "dpi",
@@ -7013,9 +6963,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime-wry" name = "tauri-runtime-wry"
version = "2.11.0" version = "2.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cadb13dad0c681e1e0a2c49ae488f0e2906ded3d57e7a0017f4aaf46e387117" checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f"
dependencies = [ dependencies = [
"gtk", "gtk",
"http", "http",
@@ -7039,9 +6989,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-utils" name = "tauri-utils"
version = "2.9.0" version = "2.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55f61d2bf7188fbcf2b0ed095b67a6bc498f713c939314bb19eb700118a573b7" checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887"
dependencies = [ dependencies = [
"aes-gcm", "aes-gcm",
"anyhow", "anyhow",
@@ -7056,7 +7006,7 @@ dependencies = [
"json-patch", "json-patch",
"log", "log",
"memchr", "memchr",
"phf 0.11.3", "phf",
"plist", "plist",
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -7089,11 +7039,10 @@ dependencies = [
[[package]] [[package]]
name = "tauri-winrt-notification" name = "tauri-winrt-notification"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" checksum = "9ed071c670382e85fc2f48ae706492d8c338f4f89bf72520d32f8abfe880aade"
dependencies = [ dependencies = [
"quick-xml 0.37.4",
"thiserror 2.0.12", "thiserror 2.0.12",
"windows 0.61.1", "windows 0.61.1",
"windows-version", "windows-version",
@@ -7524,9 +7473,9 @@ dependencies = [
[[package]] [[package]]
name = "tray-icon" name = "tray-icon"
version = "0.23.1" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" checksum = "65ba1e5f6b9ef9fd87e21b9c6f351554dbd717960089168fcfdef854686961dc"
dependencies = [ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"dirs 6.0.0", "dirs 6.0.0",
@@ -7581,7 +7530,7 @@ dependencies = [
"httparse", "httparse",
"log", "log",
"native-tls", "native-tls",
"rand 0.9.3", "rand 0.9.4",
"rustls", "rustls",
"rustls-pki-types", "rustls-pki-types",
"sha1", "sha1",
@@ -7774,12 +7723,6 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
[[package]]
name = "utf8-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
[[package]] [[package]]
name = "utf8_iter" name = "utf8_iter"
version = "1.0.4" version = "1.0.4"
@@ -7804,12 +7747,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "value-bag"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
@@ -8080,7 +8017,7 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538"
dependencies = [ dependencies = [
"phf 0.13.1", "phf",
"phf_codegen", "phf_codegen",
"string_cache", "string_cache",
"string_cache_codegen", "string_cache_codegen",
@@ -8255,7 +8192,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
+1 -1
View File
@@ -11,7 +11,7 @@ resolver = "2"
[workspace.dependencies] [workspace.dependencies]
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
tracing = "0.1" tracing = "0.1"
log = "0.4" log = "0.4.21"
tauri = { version = "2.10", default-features = false } tauri = { version = "2.10", default-features = false }
tauri-build = "2.5" tauri-build = "2.5"
tauri-plugin = "2.5" tauri-plugin = "2.5"
+20
View File
@@ -1,5 +1,25 @@
# Changelog # 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] ## \[2.0.39]
### Dependencies ### Dependencies
+3 -2
View File
@@ -1,12 +1,13 @@
<!doctype html> <!doctype html>
<html lang="en" theme="dark"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta <meta
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=0" 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> </head>
<body> <body>
+21 -21
View File
@@ -1,7 +1,7 @@
{ {
"name": "api", "name": "api",
"private": true, "private": true,
"version": "2.0.39", "version": "2.0.42",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --clearScreen false", "dev": "vite --clearScreen false",
@@ -11,32 +11,32 @@
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.11.0", "@tauri-apps/api": "^2.11.0",
"@tauri-apps/plugin-barcode-scanner": "^2.4.4", "@tauri-apps/plugin-barcode-scanner": "workspace:*",
"@tauri-apps/plugin-biometric": "^2.3.2", "@tauri-apps/plugin-biometric": "workspace:*",
"@tauri-apps/plugin-cli": "^2.4.1", "@tauri-apps/plugin-cli": "workspace:*",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2", "@tauri-apps/plugin-clipboard-manager": "workspace:*",
"@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-dialog": "2",
"@tauri-apps/plugin-fs": "^2.5.1", "@tauri-apps/plugin-fs": "workspace:*",
"@tauri-apps/plugin-geolocation": "^2.3.2", "@tauri-apps/plugin-geolocation": "workspace:*",
"@tauri-apps/plugin-global-shortcut": "^2.3.1", "@tauri-apps/plugin-global-shortcut": "workspace:*",
"@tauri-apps/plugin-haptics": "^2.3.2", "@tauri-apps/plugin-haptics": "workspace:*",
"@tauri-apps/plugin-http": "^2.5.9", "@tauri-apps/plugin-http": "workspace:*",
"@tauri-apps/plugin-nfc": "^2.3.5", "@tauri-apps/plugin-nfc": "workspace:*",
"@tauri-apps/plugin-notification": "^2.3.3", "@tauri-apps/plugin-notification": "workspace:*",
"@tauri-apps/plugin-opener": "^2.5.4", "@tauri-apps/plugin-opener": "workspace:*",
"@tauri-apps/plugin-os": "^2.3.2", "@tauri-apps/plugin-os": "workspace:*",
"@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-process": "workspace:*",
"@tauri-apps/plugin-shell": "^2.3.5", "@tauri-apps/plugin-shell": "workspace:*",
"@tauri-apps/plugin-store": "^2.4.3", "@tauri-apps/plugin-store": "2",
"@tauri-apps/plugin-updater": "^2.10.1", "@tauri-apps/plugin-updater": "workspace:*",
"@tauri-apps/plugin-upload": "^2.4.0", "@tauri-apps/plugin-upload": "workspace:*",
"@zerodevx/svelte-json-view": "1.0.11" "@zerodevx/svelte-json-view": "1.0.11"
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/codicon": "^1.2.49", "@iconify-json/codicon": "^1.2.49",
"@iconify-json/ph": "^1.2.2", "@iconify-json/ph": "^1.2.2",
"@sveltejs/vite-plugin-svelte": "^7.0.0", "@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", "@unocss/extractor-svelte": "^66.6.7",
"svelte": "^5.54.0", "svelte": "^5.54.0",
"unocss": "^66.6.7", "unocss": "^66.6.7",
+20
View File
@@ -1,5 +1,25 @@
# Changelog # 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] ## \[2.0.43]
### Dependencies ### Dependencies
+6 -6
View File
@@ -1,7 +1,7 @@
[package] [package]
name = "api" name = "api"
publish = false publish = false
version = "2.0.43" version = "2.0.46"
description = "An example Tauri Application showcasing the api" description = "An example Tauri Application showcasing the api"
edition = "2021" edition = "2021"
rust-version = { workspace = true } rust-version = { workspace = true }
@@ -20,12 +20,12 @@ serde = { workspace = true }
tiny_http = "0.12" tiny_http = "0.12"
time = "0.3" time = "0.3"
log = { workspace = true } 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 = [ tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.5.1", features = [
"watch", "watch",
] } ] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.2" } 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 = [ tauri-plugin-http = { path = "../../../plugins/http", features = [
"multipart", "multipart",
"cookies", "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-process = { path = "../../../plugins/process", version = "2.3.1" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.5.4" } tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.5.4" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.5" } 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" } tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" }
[dependencies.tauri] [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] [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-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-updater = { path = "../../../plugins/updater", version = "2.10.1" }
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" } tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" }
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies] [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-nfc = { path = "../../../plugins/nfc", version = "2.3.5" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.2" } tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.2" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.2" } tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.2" }
@@ -15,10 +15,10 @@
] ]
}, },
"core:default", "core:default",
"core:app:allow-set-app-theme",
"fs:default", "fs:default",
"core:window:allow-minimize", "core:window:allow-minimize",
"core:window:allow-maximize", "core:window:allow-toggle-maximize",
"core:window:allow-unmaximize",
"core:window:allow-close", "core:window:allow-close",
"core:window:allow-start-dragging", "core:window:allow-start-dragging",
"notification:default", "notification:default",
@@ -1,3 +1,11 @@
package com.tauri.api 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"> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- 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. --> <!-- Customize your theme here. -->
</style> </style>
</resources> </resources>
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- 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. --> <!-- Customize your theme here. -->
</style> </style>
</resources> </resources>
@@ -21,7 +21,23 @@ open class BuildTask : DefaultTask() {
runTauriCli(executable) runTauriCli(executable)
} catch (e: Exception) { } catch (e: Exception) {
if (Os.isFamily(Os.FAMILY_WINDOWS)) { 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 { } else {
throw e; throw e;
} }
@@ -1,6 +1,6 @@
#Tue May 10 19:22:52 CST 2022 #Tue May 10 19:22:52 CST 2022
distributionBase=GRADLE_USER_HOME 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 distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
+5
View File
@@ -77,7 +77,9 @@
}, },
"updater": { "updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDE5QzMxNjYwNTM5OEUwNTgKUldSWTRKaFRZQmJER1h4d1ZMYVA3dnluSjdpN2RmMldJR09hUFFlZDY0SlFqckkvRUJhZDJVZXAK", "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDE5QzMxNjYwNTM5OEUwNTgKUldSWTRKaFRZQmJER1h4d1ZMYVA3dnluSjdpN2RmMldJR09hUFFlZDY0SlFqckkvRUJhZDJVZXAK",
"dangerousInsecureTransportProtocol": true,
"endpoints": [ "endpoints": [
"http://localhost:5173/updater-test/updater.json",
"https://tauri-update-server.vercel.app/update/{{target}}/{{current_version}}" "https://tauri-update-server.vercel.app/update/{{target}}/{{current_version}}"
] ]
} }
@@ -99,6 +101,9 @@
"localePath": "locales/pt-BR.wxl" "localePath": "locales/pt-BR.wxl"
} }
} }
},
"nsis": {
"compression": "none"
} }
}, },
"iOS": { "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 { 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 { getCurrentWebview } from '@tauri-apps/api/webview'
import { setTheme } from '@tauri-apps/api/app'
import * as os from '@tauri-apps/plugin-os' import * as os from '@tauri-apps/plugin-os'
import Welcome from './views/Welcome.svelte' import Welcome from './views/Welcome.svelte'
@@ -23,11 +32,10 @@
import Biometric from './views/Biometric.svelte' import Biometric from './views/Biometric.svelte'
import Geolocation from './views/Geolocation.svelte' import Geolocation from './views/Geolocation.svelte'
import Haptics from './views/Haptics.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 Nfc from './views/Nfc.svelte'
import TitleBar from './lib/TitleBar.svelte'
const appWindow = getCurrentWindow() const appWindow = getCurrentWindow()
if (appWindow.label !== 'main') { if (appWindow.label !== 'main') {
@@ -148,114 +156,98 @@
component: Haptics, component: Haptics,
icon: 'i-ph-vibrate' icon: 'i-ph-vibrate'
} }
] ].filter(Boolean) as {
label: string
component: typeof Haptics
icon: string
}[]
let selected = $state.raw(views[0])
let selected = views[0] // dark/light themes
function select(view) { const preferDark = new MediaQuery('prefers-color-scheme: dark')
selected = view let theme = $state<Theme | 'auto'>(
} (localStorage.getItem('theme') as Theme | null) || 'auto'
)
// Window controls async function switchTheme() {
let isWindowMaximized switch (theme) {
onMount(async () => { case 'dark':
isWindowMaximized = await appWindow.isMaximized() theme = 'light'
appWindow.onResized(async () => { break
isWindowMaximized = await appWindow.isMaximized() case 'light':
}) theme = 'auto'
}) break
case 'auto':
function minimize() { theme = 'dark'
appWindow.minimize() break
}
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()
}
} }
applyTheme()
} }
// dark/light function applyTheme() {
let isDark const isDark = theme === 'auto' ? preferDark.current : theme === 'dark'
onMount(() => { if (isDark) {
isDark = localStorage && localStorage.getItem('theme') == 'dark' document.documentElement.classList.add('dark')
applyTheme(isDark) 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 // Console
let messages = writable([]) const messages = writable<string[]>([])
let consoleTextEl let consoleTextEl: HTMLDivElement
async function onMessage(value) {
// 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) => [ messages.update((r) => [
...r, ...r,
{ `<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ${html}</pre>`
html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
(typeof value === 'string' ? value : JSON.stringify(value, null, 1)) +
'</pre>'
}
]) ])
await tick() await tick()
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight consoleTextEl.scrollTop = consoleTextEl.scrollHeight
} }
// this function renders HTML without sanitizing it so it's insecure async function onMessage(value: unknown) {
// we only use it with our own input data const valueStr =
async function insecureRenderHtml(html) { typeof value === 'string'
messages.update((r) => [ ? value
...r, : JSON.stringify(
{ value instanceof ArrayBuffer
html: ? Array.from(new Uint8Array(value))
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` + : value,
html + null,
'</pre>' 1
} )
]) insecureRenderHtml(valueStr)
await tick()
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight
} }
function clear() { function clear() {
messages.update(() => []) messages.update(() => [])
} }
let consoleEl, consoleH, cStartY let consoleEl: HTMLDivElement
let minConsoleHeight = 50 let consoleH = 0
function startResizingConsole(e) { let cStartY = 0
const minConsoleHeight = 50
function startResizingConsole(e: MouseEvent) {
cStartY = e.clientY cStartY = e.clientY
const styles = window.getComputedStyle(consoleEl) const styles = window.getComputedStyle(consoleEl)
consoleH = parseInt(styles.height, 10) consoleH = parseInt(styles.height, 10)
const moveHandler = (e) => { const moveHandler = (e: MouseEvent) => {
const dy = e.clientY - cStartY const dy = e.clientY - cStartY
const newH = consoleH - dy const newH = consoleH - dy
consoleEl.style.height = `${ consoleEl.style.height = `${newH < minConsoleHeight ? minConsoleHeight : newH}px`
newH < minConsoleHeight ? minConsoleHeight : newH
}px`
} }
const upHandler = () => { const upHandler = () => {
document.removeEventListener('mouseup', upHandler) document.removeEventListener('mouseup', upHandler)
@@ -265,21 +257,19 @@
document.addEventListener('mousemove', moveHandler) document.addEventListener('mousemove', moveHandler)
} }
let isWindows let isWindows = os.platform() === 'windows'
onMount(async () => {
isWindows = (await os.platform()) === 'windows'
})
// mobile // mobile
let isSideBarOpen = false let isSideBarOpen = $state(false)
let sidebar let sidebar: HTMLElement
let sidebarToggle let sidebarToggle: HTMLElement
let isDraggingSideBar = false let isDraggingSideBar = false
let draggingStartPosX = 0 let draggingStartPosX = 0
let draggingEndPosX = 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( sidebar.style.setProperty(
'--translate-x', '--translate-x',
`${isSideBarOpen ? '0' : '-18.75'}rem` `${isSideBarOpen ? '0' : '-18.75'}rem`
@@ -287,10 +277,9 @@
} }
onMount(() => { onMount(() => {
sidebar = document.querySelector('#sidebar')
sidebarToggle = document.querySelector('#sidebarToggle')
document.addEventListener('click', (e) => { document.addEventListener('click', (e) => {
if (!(e.target instanceof Node)) return
if (sidebarToggle.contains(e.target)) { if (sidebarToggle.contains(e.target)) {
isSideBarOpen = !isSideBarOpen isSideBarOpen = !isSideBarOpen
} else if (isSideBarOpen && !sidebar.contains(e.target)) { } else if (isSideBarOpen && !sidebar.contains(e.target)) {
@@ -299,6 +288,7 @@
}) })
document.addEventListener('touchstart', (e) => { document.addEventListener('touchstart', (e) => {
if (!(e.target instanceof Node)) return
if (sidebarToggle.contains(e.target)) return if (sidebarToggle.contains(e.target)) return
const x = e.touches[0].clientX const x = e.touches[0].clientX
@@ -330,76 +320,22 @@
}) })
}) })
$: { $effect(() => {
const sidebar = document.querySelector('#sidebar') toggleSidebar()
if (sidebar) { })
toggleSidebar(sidebar, isSideBarOpen)
}
}
</script> </script>
<!-- custom titlebar for Windows --> <!-- custom titlebar for Windows -->
{#if isWindows} {#if isWindows}
<div <TitleBar {appWindow} {theme} {switchTheme} />
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>
{/if} {/if}
<!-- Sidebar toggle, only visible on small screens --> <!-- Sidebar toggle, only visible on small screens -->
<div <div
id="sidebarToggle" id="sidebarToggle"
class="z-2000 sidebar-toggle hidden lt-sm:flex justify-center absolute items-center w-8 h-8 rd-8 bind:this={sidebarToggle}
bg-accent dark:bg-darkAccent active:bg-accentDark dark:active:bg-darkAccentDark" 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} {#if isSideBarOpen}
<span class="i-codicon-close animate-duration-300ms animate-fade-in"></span> <span class="i-codicon-close animate-duration-300ms animate-fade-in"></span>
@@ -409,10 +345,11 @@
</div> </div>
<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 <aside
id="sidebar" 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 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" 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" /> <img class="p-7" src="tauri_logo.png" alt="Tauri logo" />
</a> </a>
{#if !isWindows} {#if !isWindows}
<a href="##" class="nv justify-between h-8" on:click={toggleDark}> <a href="##" class="nv justify-between" onclick={switchTheme}>
{#if isDark} {#if theme === 'auto'}
Switch to Light mode
<div class="i-ph-sun"></div>
{:else}
Switch to Dark mode 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> <div class="i-ph-moon"></div>
{:else if theme === 'light'}
Switch to Auto mode
<div class="i-ph-sun"></div>
{/if} {/if}
</a> </a>
<br /> <br />
@@ -435,7 +375,7 @@
{/if} {/if}
<a <a
class="nv justify-between h-8" class="nv justify-between"
target="_blank" target="_blank"
href="https://tauri.app/v1/guides/" href="https://tauri.app/v1/guides/"
> >
@@ -443,7 +383,7 @@
<span class="i-codicon-link-external"></span> <span class="i-codicon-link-external"></span>
</a> </a>
<a <a
class="nv justify-between h-8" class="nv justify-between"
target="_blank" target="_blank"
href="https://github.com/tauri-apps/tauri" href="https://github.com/tauri-apps/tauri"
> >
@@ -451,7 +391,7 @@
<span class="i-codicon-link-external"></span> <span class="i-codicon-link-external"></span>
</a> </a>
<a <a
class="nv justify-between h-8" class="nv justify-between"
target="_blank" target="_blank"
href="https://github.com/tauri-apps/tauri/tree/dev/examples/api" href="https://github.com/tauri-apps/tauri/tree/dev/examples/api"
> >
@@ -461,23 +401,19 @@
<br /> <br />
<div class="bg-white/5 h-2px"></div> <div class="bg-white/5 h-2px"></div>
<br /> <br />
<div <div class="flex flex-col overflow-y-auto children-flex-none gap-1">
class="flex flex-col overflow-y-auto children-h-10 children-flex-none gap-1"
>
{#each views as view} {#each views as view}
{#if view} <a
<a href="##"
href="##" class="nv {selected === view ? 'nv_selected' : ''}"
class="nv {selected === view ? 'nv_selected' : ''}" onclick={() => {
on:click={() => { selected = view
select(view) isSideBarOpen = false
isSideBarOpen = false }}
}} >
> <div class="{view.icon} mr-2"></div>
<div class="{view.icon} mr-2"></div> <p>{view.label}</p></a
<p>{view.label}</p></a >
>
{/if}
{/each} {/each}
</div> </div>
</aside> </aside>
@@ -489,11 +425,7 @@
<h1>{selected.label}</h1> <h1>{selected.label}</h1>
<div class="overflow-y-auto"> <div class="overflow-y-auto">
<div class="mr-2"> <div class="mr-2">
<svelte:component <selected.component {onMessage} />
this={selected.component}
{onMessage}
{insecureRenderHtml}
/>
</div> </div>
</div> </div>
</div> </div>
@@ -503,20 +435,21 @@
id="console" id="console"
class="select-none h-15rem grid grid-rows-[2px_2rem_1fr] gap-1 overflow-hidden" class="select-none h-15rem grid grid-rows-[2px_2rem_1fr] gap-1 overflow-hidden"
> >
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div <div
on:mousedown={startResizingConsole} role="button"
tabindex="0"
onmousedown={startResizingConsole}
class="bg-black/20 h-2px cursor-ns-resize" class="bg-black/20 h-2px cursor-ns-resize"
></div> ></div>
<div class="flex justify-between items-center px-2"> <div class="flex justify-between items-center px-2">
<p class="font-semibold">Console</p> <p class="font-semibold">Console</p>
<button <button
aria-label="Clear Console" 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 hover:bg-hoverOverlay dark:hover:bg-darkHoverOverlay
active:bg-hoverOverlay/25 dark:active:bg-darkHoverOverlay/25 active:bg-hoverOverlay/25 dark:active:bg-darkHoverOverlay/25
" "
on:click={clear} onclick={clear}
> >
<div class="i-codicon-clear-all"></div> <div class="i-codicon-clear-all"></div>
</button> </button>
@@ -525,8 +458,8 @@
bind:this={consoleTextEl} bind:this={consoleTextEl}
class="px-2 overflow-y-auto all:font-mono code-block all:text-xs select-text mr-2" class="px-2 overflow-y-auto all:font-mono code-block all:text-xs select-text mr-2"
> >
{#each $messages as r} {#each $messages as messageHtml}
{@html r.html} {@html messageHtml}
{/each} {/each}
</div> </div>
</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) { *:not(h1, h2, h3, h4, h5, h6) {
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -8,6 +19,11 @@
font-family: 'Rubik', sans-serif; font-family: 'Rubik', sans-serif;
} }
button {
color: inherit;
background-color: inherit;
}
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 0.25rem; width: 0.25rem;
height: 3px; height: 3px;
@@ -33,27 +49,34 @@ code.code-block {
width: 18.75rem; width: 18.75rem;
} }
@media screen and (max-width: 640px) { @media screen and (width < 640px) {
#sidebar { #sidebar {
--translate-x: -18.75rem; --translate-x: -18.75rem;
transform: translateX(var(--translate-x)); transform: translateX(var(--translate-x));
} }
} }
.sidebar-toggle { #sidebar,
margin-top: 0.5rem; main {
margin-left: 0.5rem; padding-top: calc(env(safe-area-inset-top) + 2rem);
} }
body { #sidebar {
overflow: hidden; padding-left: calc(env(safe-area-inset-left) + 0.5rem);
padding: env(safe-area-inset-top) env(safe-area-inset-right) }
env(safe-area-inset-bottom) env(safe-area-inset-left);
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, #sidebar,
#console { #console {
padding-bottom: calc(env(safe-area-inset-bottom) + 24px); padding-bottom: calc(env(safe-area-inset-bottom) + 1.5rem);
} }
.transparent { .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' import { readFile } from '@tauri-apps/plugin-fs'
export let onMessage export let onMessage
export let insecureRenderHtml
let text = 'clipboard message' let text = 'clipboard message'
function writeText() { function writeText() {
@@ -41,7 +40,7 @@
const image = await clipboard.readImage() const image = await clipboard.readImage()
arrayBufferToBase64(await image.rgba(), function (base64) { arrayBufferToBase64(await image.rgba(), function (base64) {
const src = 'data:image/png;base64,' + base64 const src = 'data:image/png;base64,' + base64
insecureRenderHtml('<img src="' + src + '"></img>') onMessage('<img src="' + src + '"></img>')
}) })
return return
} catch (_) {} } catch (_) {}
+87 -77
View File
@@ -1,129 +1,138 @@
<script> <script lang="ts">
import { open, save, confirm, message } from "@tauri-apps/plugin-dialog"; import {
import { readFile } from "@tauri-apps/plugin-fs"; 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; let { onMessage }: ViewProps = $props()
export let insecureRenderHtml;
let defaultPath = null;
let filter = null;
let multiple = false;
let directory = false;
let pickerMode = "document";
let fileAccessMode = "scoped";
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], { var blob = new Blob([buffer], {
type: "application/octet-binary", type: 'application/octet-binary'
}); })
var reader = new FileReader(); var reader = new FileReader()
reader.onload = function (evt) { reader.onload = function (evt) {
var dataurl = evt.target.result; var dataurl = evt.target!.result as string
callback(dataurl.substr(dataurl.indexOf(",") + 1)); callback(dataurl.split(',')[1])
}; }
reader.readAsDataURL(blob); reader.readAsDataURL(blob)
} }
async function prompt() { async function prompt() {
confirm("Do you want to do something?") confirm('Do you want to do something?')
.then((res) => onMessage(res ? "Yes" : "No")) .then((res) => onMessage(res ? 'Yes' : 'No'))
.catch(onMessage); .catch(onMessage)
} }
async function promptCustom() { async function promptCustom() {
confirm("Is Tauri awesome?", { confirm('Is Tauri awesome?', {
okLabel: "Absolutely", okLabel: 'Absolutely',
cancelLabel: "Totally", cancelLabel: 'Totally'
}) })
.then((res) => .then((res) =>
onMessage( 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() { 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) { async function msgCustom() {
const buttons = { yes: "awesome", no: "amazing", cancel: "stunning" }; const buttons = { yes: 'awesome', no: 'amazing', cancel: 'stunning' }
await message(`Tauri is: `, { buttons }) await message(`Tauri is: `, { buttons })
.then((res) => onMessage(`Tauri is ${res}`)) .then((res) => onMessage(`Tauri is ${res}`))
.catch(onMessage); .catch(onMessage)
} }
async function openDialog() { async function openDialog() {
try { try {
var result = await open({ const result = await open({
title: "My wonderful open dialog", title: 'My wonderful open dialog',
defaultPath, defaultPath,
filters: filter filters: filter
? [ ? [
{ {
name: "Tauri Example", name: 'Tauri Example',
extensions: filter.split(",").map((f) => f.trim()), extensions: filter.split(',').map((f) => f.trim())
}, }
] ]
: [], : [],
multiple, multiple,
directory, directory,
pickerMode, pickerMode,
fileAccessMode, fileAccessMode
}) })
if (Array.isArray(result)) { if (Array.isArray(result)) {
onMessage(result); onMessage(result)
} else if (result === null) {
onMessage('user cancelled the selection')
} else { } else {
var pathToRead = result; const pathToRead = result
var isFile = pathToRead.match(/\S+\.\S+$/g); const isFile = pathToRead.match(/\S+\.\S+$/g)
await readFile(pathToRead) await readFile(pathToRead).then(function (res) {
.then(function (res) { if (isFile) {
if (isFile) { if (
if ( pathToRead.includes('.png')
pathToRead.includes(".png") || || pathToRead.includes('.jpg')
pathToRead.includes(".jpg") || || pathToRead.includes('.jpeg')
pathToRead.includes(".jpeg") ) {
) { arrayBufferToBase64(res.buffer, function (base64) {
arrayBufferToBase64( const src = 'data:image/png;base64,' + base64
new Uint8Array(res), onMessage('<img src="' + src + '"></img>')
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);
}
} else { } 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) onMessage(exception)
} }
} }
function saveDialog() { function saveDialog() {
save({ save({
title: "My wonderful save dialog", title: 'My wonderful save dialog',
defaultPath, defaultPath,
filters: filter filters: filter
? [ ? [
{ {
name: "Tauri Example", name: 'Tauri Example',
extensions: filter.split(",").map((f) => f.trim()), extensions: filter.split(',').map((f) => f.trim())
}, }
] ]
: [], : []
}) })
.then(onMessage) .then(onMessage)
.catch(onMessage); .catch(onMessage)
} }
</script> </script>
@@ -171,15 +180,16 @@
<br /> <br />
<div class="flex flex-wrap flex-col md:flex-row gap-2 children:flex-shrink-0"> <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="open-dialog" onclick={openDialog}>Open dialog</button>
<button class="btn" id="save-dialog" on:click={saveDialog} <button class="btn" id="save-dialog" onclick={saveDialog}
>Open save dialog</button >Open save dialog</button
> >
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button> <button class="btn" id="prompt-dialog" onclick={prompt}>Prompt</button>
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom} <button class="btn" id="custom-prompt-dialog" onclick={promptCustom}
>Prompt (custom)</button >Prompt (custom)</button
> >
<button class="btn" id="message-dialog" on:click={msg}>Message</button> <button class="btn" id="message-dialog" onclick={msg}>Message</button>
<button class="btn" id="message-dialog" on:click={msgCustom}>Message (custom)</button> <button class="btn" id="message-dialog" onclick={msgCustom}
>Message (custom)</button
>
</div> </div>
+3 -3
View File
@@ -5,7 +5,7 @@
import { arrayBufferToBase64 } from '../lib/utils' import { arrayBufferToBase64 } from '../lib/utils'
import { onDestroy, onMount } from 'svelte' import { onDestroy, onMount } from 'svelte'
const { onMessage, insecureRenderHtml } = $props() const { onMessage } = $props()
let path = $state('') let path = $state('')
let img let img
@@ -118,12 +118,12 @@
new Uint8Array(response), new Uint8Array(response),
function (base64) { function (base64) {
const src = 'data:image/png;base64,' + base64 const src = 'data:image/png;base64,' + base64
insecureRenderHtml('<img src="' + src + '"></img>') onMessage('<img src="' + src + '"></img>')
} }
) )
} else { } else {
const value = String.fromCharCode.apply(null, response) const value = String.fromCharCode.apply(null, response)
insecureRenderHtml( onMessage(
'<textarea id="file-response"></textarea><button id="file-save">Save</button>' '<textarea id="file-response"></textarea><button id="file-save">Save</button>'
) )
setTimeout(() => { setTimeout(() => {
+19 -11
View File
@@ -1,12 +1,18 @@
<script> <script lang="ts">
import { check } from '@tauri-apps/plugin-updater' import { check, Update } from '@tauri-apps/plugin-updater'
import { relaunch } from '@tauri-apps/plugin-process' import { relaunch } from '@tauri-apps/plugin-process'
import { onDestroy } from 'svelte'
export let onMessage let { onMessage } = $props()
let isChecking, isInstalling, newUpdate let isChecking = $state(false)
let totalSize = 0, let isInstalling = $state(false)
downloadedSize = 0 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() { async function checkUpdate() {
isChecking = true isChecking = true
@@ -31,10 +37,10 @@
isInstalling = true isInstalling = true
downloadedSize = 0 downloadedSize = 0
try { try {
await newUpdate.downloadAndInstall((downloadProgress) => { await newUpdate!.downloadAndInstall((downloadProgress) => {
switch (downloadProgress.event) { switch (downloadProgress.event) {
case 'Started': case 'Started':
totalSize = downloadProgress.data.contentLength totalSize = downloadProgress.data.contentLength!
break break
case 'Progress': case 'Progress':
downloadedSize += downloadProgress.data.chunkLength downloadedSize += downloadProgress.data.chunkLength
@@ -54,14 +60,16 @@
} }
} }
$: progress = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0 onDestroy(() => {
newUpdate?.close()
})
</script> </script>
<div class="flex children:grow children:h10"> <div class="flex children:grow children:h10">
{#if !isChecking && !newUpdate} {#if !isChecking && !newUpdate}
<button class="btn" on:click={checkUpdate}>Check update</button> <button class="btn" onclick={checkUpdate}>Check update</button>
{:else if !isInstalling && newUpdate} {:else if !isInstalling && newUpdate}
<button class="btn" on:click={install}>Install update</button> <button class="btn" onclick={install}>Install update</button>
{:else} {:else}
<div class="progress"> <div class="progress">
<span>{progress}%</span> <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, port: 5173,
strictPort: true, strictPort: true,
fs: { fs: {
allow: ['.', '../../tooling/api/dist'] allow: ['.']
} }
} }
} }
+5 -15
View File
@@ -15,25 +15,15 @@
"@rollup/plugin-node-resolve": "16.0.3", "@rollup/plugin-node-resolve": "16.0.3",
"@rollup/plugin-terser": "1.0.0", "@rollup/plugin-terser": "1.0.0",
"@rollup/plugin-typescript": "12.3.0", "@rollup/plugin-typescript": "12.3.0",
"eslint": "10.2.0", "eslint": "10.4.0",
"eslint-config-prettier": "10.1.8", "eslint-config-prettier": "10.1.8",
"eslint-plugin-security": "4.0.0", "eslint-plugin-security": "4.0.1",
"prettier": "3.8.1", "prettier": "3.8.3",
"rollup": "4.59.0", "rollup": "4.60.3",
"tslib": "2.8.1", "tslib": "2.8.1",
"typescript": "6.0.3", "typescript": "6.0.3",
"typescript-eslint": "8.58.2" "typescript-eslint": "8.58.2"
}, },
"minimumReleaseAge": 4320, "minimumReleaseAge": 4320,
"pnpm": { "packageManager": "pnpm@11.13.0"
"overrides": {
"esbuild@<0.25.0": ">=0.25.0"
},
"onlyBuiltDependencies": [
"esbuild"
]
},
"engines": {
"pnpm": "^10.16.0"
}
} }
+4
View File
@@ -1,5 +1,9 @@
# Changelog # 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] ## \[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. - [`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] [package]
name = "tauri-plugin-barcode-scanner" 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" description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -337,10 +337,11 @@ class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
} }
@objc private func cancel(_ invoke: Invoke) { @objc private func cancel(_ invoke: Invoke) {
self.invoke?.reject("cancelled") DispatchQueue.main.async { [self] in
self.invoke?.reject("cancelled")
destroy() self.destroy()
invoke.resolve() invoke.resolve()
}
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-barcode-scanner", "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", "description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
+2 -2
View File
@@ -11,10 +11,10 @@
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.11.0", "@tauri-apps/api": "^2.11.0",
"@tauri-apps/plugin-deep-link": "2.4.9" "@tauri-apps/plugin-deep-link": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.11.0", "@tauri-apps/cli": "2.11.4",
"typescript": "^6.0.0", "typescript": "^6.0.0",
"vite": "^8.0.1" "vite": "^8.0.1"
} }
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- 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. --> <!-- Customize your theme here. -->
</style> </style>
</resources> </resources>
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- 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. --> <!-- Customize your theme here. -->
</style> </style>
</resources> </resources>
+17 -7
View File
@@ -14,9 +14,11 @@ import { type UnlistenFn, listen } from '@tauri-apps/api/event'
* const urls = await getCurrent(); * 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. * #### Platform-specific
* 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.. * - **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 * @since 2.0.0
*/ */
@@ -35,7 +37,9 @@ export async function getCurrent(): Promise<string[] | null> {
* await register("my-scheme"); * await register("my-scheme");
* ``` * ```
* *
* #### - **macOS / Android / iOS**: Unsupported. * #### Platform-specific
*
* - **macOS / Android / iOS:** Unsupported.
* *
* @since 2.0.0 * @since 2.0.0
*/ */
@@ -54,7 +58,9 @@ export async function register(protocol: string): Promise<null> {
* await unregister("my-scheme"); * await unregister("my-scheme");
* ``` * ```
* *
* #### - **macOS / Linux / Android / iOS**: Unsupported. * #### Platform-specific
*
* - **macOS / Linux / Android / iOS:** Unsupported.
* *
* @since 2.0.0 * @since 2.0.0
*/ */
@@ -73,7 +79,9 @@ export async function unregister(protocol: string): Promise<null> {
* await isRegistered("my-scheme"); * await isRegistered("my-scheme");
* ``` * ```
* *
* #### - **macOS / Android / iOS**: Unsupported. * #### Platform-specific
*
* - **macOS / Android / iOS:** Unsupported.
* *
* @since 2.0.0 * @since 2.0.0
*/ */
@@ -92,7 +100,9 @@ export async function isRegistered(protocol: string): Promise<boolean> {
* await onOpenUrl((urls) => { console.log(urls) }); * 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 * @since 2.0.0
*/ */
+1 -1
View File
@@ -28,6 +28,6 @@
"@tauri-apps/api": "^2.11.0" "@tauri-apps/api": "^2.11.0"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.11.0" "@tauri-apps/cli": "2.11.4"
} }
} }
+4
View File
@@ -1,5 +1,9 @@
# Changelog # 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] ## \[2.7.1]
### Dependencies ### Dependencies
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-dialog" 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." description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
+2 -2
View File
@@ -35,8 +35,8 @@ android {
dependencies { dependencies {
implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.0") implementation("androidx.appcompat:appcompat:1.7.1")
implementation("com.google.android.material:material:1.7.0") implementation("com.google.android.material:material:1.13.0")
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
@@ -5,7 +5,6 @@
package app.tauri.dialog package app.tauri.dialog
import android.app.Activity import android.app.Activity
import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Handler import android.os.Handler
@@ -21,6 +20,7 @@ import app.tauri.plugin.Invoke
import app.tauri.plugin.JSArray import app.tauri.plugin.JSArray
import app.tauri.plugin.JSObject import app.tauri.plugin.JSObject
import app.tauri.plugin.Plugin import app.tauri.plugin.Plugin
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@InvokeArg @InvokeArg
class Filter { class Filter {
@@ -158,7 +158,7 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
Handler(Looper.getMainLooper()) Handler(Looper.getMainLooper())
.post { .post {
val builder = AlertDialog.Builder(activity) val builder = MaterialAlertDialogBuilder(activity)
if (args.title != null) { if (args.title != null) {
builder.setTitle(args.title) builder.setTitle(args.title)
@@ -191,8 +191,7 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
} }
} }
val dialog = builder.create() builder.show()
dialog.show()
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-dialog", "name": "@tauri-apps/plugin-dialog",
"version": "2.7.1", "version": "2.7.2",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "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. `$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.""" Allowing access can lead to sensitive information disclosure and should be well considered."""
[[scope.deny]] [[permission.scope.deny]]
path = "$APPLOCALDATA/**" path = "$APPLOCALDATA/**"
[[permission]] [[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. `$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.""" Allowing access can lead to sensitive information disclosure and should be well considered."""
[[scope.deny]] [[permission.scope.deny]]
path = "$APPLOCALDATA/EBWebView/**" 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. /// Represents either a plain PathBuf or a PathHandle that manages security-scoped resources.
pub enum PathKind<R: Runtime> { pub enum PathKind<R: Runtime> {
/// A plain path that doesn't manage security-scoped resources. /// 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), Path(PathBuf),
/// A path handle that manages security-scoped resources and will clean them up on drop. /// A path handle that manages security-scoped resources and will clean them up on drop.
Handle(PathHandle<R>), Handle(PathHandle<R>),
@@ -84,6 +84,7 @@ impl<R: Runtime> PathKind<R> {
/// Get a reference to the underlying path. /// Get a reference to the underlying path.
pub fn as_path(&self) -> &Path { pub fn as_path(&self) -> &Path {
match self { match self {
#[cfg(mobile)]
PathKind::Path(p) => p.as_ref(), PathKind::Path(p) => p.as_ref(),
PathKind::Handle(h) => h.as_ref(), PathKind::Handle(h) => h.as_ref(),
} }
@@ -92,6 +93,7 @@ impl<R: Runtime> PathKind<R> {
/// Get a reference to the underlying PathBuf. /// Get a reference to the underlying PathBuf.
pub fn as_path_buf(&self) -> &PathBuf { pub fn as_path_buf(&self) -> &PathBuf {
match self { match self {
#[cfg(mobile)]
PathKind::Path(p) => p, PathKind::Path(p) => p,
PathKind::Handle(h) => h, PathKind::Handle(h) => h,
} }
@@ -114,27 +116,13 @@ impl<R: Runtime> AsRef<PathBuf> for PathKind<R> {
pub struct FileHandle<R: Runtime> { pub struct FileHandle<R: Runtime> {
file: File, file: File,
path: PathKind<R>, path: PathKind<R>,
#[allow(dead_code)] // Used in Drop implementation #[cfg(target_os = "ios")]
path_: SafeFilePath, path_: SafeFilePath,
#[allow(dead_code)] // Used in Drop implementation #[cfg(target_os = "ios")]
app_handle: tauri::AppHandle<R>, app_handle: tauri::AppHandle<R>,
} }
impl<R: Runtime> FileHandle<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. /// Get the resolved path.
pub fn path(&self) -> &Path { pub fn path(&self) -> &Path {
self.path.as_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> { impl<R: Runtime> Drop for FileHandle<R> {
fn drop(&mut self) { 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
// Only clean up if we have a plain PathBuf, not a PathHandle if let PathKind::Path(_) = &self.path {
// PathHandle will handle its own cleanup when it's dropped use crate::{FilePath, FsExt};
if let PathKind::Path(_) = &self.path { // Convert SafeFilePath to FilePath
use crate::{FilePath, FsExt}; let file_path: FilePath = match &self.path_ {
// Convert SafeFilePath to FilePath SafeFilePath::Url(url) => FilePath::Url(url.clone()),
let file_path: FilePath = match &self.path_ { SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
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 // Only clean up if we're tracking this resource
// If start_accessing_security_scoped_resource was used, it won't be in our tracking // If start_accessing_security_scoped_resource was used, it won't be in our tracking
// and we shouldn't interfere // and we shouldn't interfere
if let FilePath::Url(url) = file_path { if let FilePath::Url(url) = file_path {
if url.scheme() == "file" { if url.scheme() == "file" {
let security_scoped_resources = let security_scoped_resources =
self.app_handle.state::<crate::SecurityScopedResources>(); self.app_handle.state::<crate::SecurityScopedResources>();
// Only clean up if it's not tracked manually // Only clean up if it's not tracked manually
if !security_scoped_resources.is_tracked_manually(url.as_str()) { if !security_scoped_resources.is_tracked_manually(url.as_str()) {
log::debug!("Stopping accessing security-scoped resource for URL: {url} on drop"); log::debug!(
let _ = self "Stopping accessing security-scoped resource for URL: {url} on drop"
.app_handle );
.fs() let _ = self
.stop_accessing_security_scoped_resource(FilePath::Url( .app_handle
url.clone(), .fs()
)); .stop_accessing_security_scoped_resource(FilePath::Url(url.clone()));
security_scoped_resources.remove(url.as_str()); security_scoped_resources.remove(url.as_str());
} else { } else {
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)"); 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> { impl<R: Runtime> Drop for PathHandle<R> {
fn drop(&mut self) { fn drop(&mut self) {
#[cfg(target_os = "ios")] use crate::{FilePath, FsExt};
{ // Convert SafeFilePath to FilePath
use crate::{FilePath, FsExt}; let file_path: FilePath = match &self.path_ {
// Convert SafeFilePath to FilePath SafeFilePath::Url(url) => FilePath::Url(url.clone()),
let file_path: FilePath = match &self.path_ { SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
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) // 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 // If start_accessing_security_scoped_resource was used, it won't be in our tracking
// and we shouldn't interfere // and we shouldn't interfere
if let FilePath::Url(url) = file_path { if let FilePath::Url(url) = file_path {
if url.scheme() == "file" { if url.scheme() == "file" {
let security_scoped_resources = let security_scoped_resources =
self.app_handle.state::<crate::SecurityScopedResources>(); self.app_handle.state::<crate::SecurityScopedResources>();
// Only clean up if it's not tracked manually // Only clean up if it's not tracked manually
if !security_scoped_resources.is_tracked_manually(url.as_str()) { if !security_scoped_resources.is_tracked_manually(url.as_str()) {
log::debug!( log::debug!(
"Stopping accessing security-scoped resource for URL: {url} on drop" "Stopping accessing security-scoped resource for URL: {url} on drop"
); );
let _ = self let _ = self
.app_handle .app_handle
.fs() .fs()
.stop_accessing_security_scoped_resource(FilePath::Url(url.clone())); .stop_accessing_security_scoped_resource(FilePath::Url(url.clone()));
security_scoped_resources.remove(url.as_str()); security_scoped_resources.remove(url.as_str());
} else { } else {
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)"); 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, path: SafeFilePath,
options: Option<BaseOptions>, options: Option<BaseOptions>,
) -> CommandResult<ResourceId> { ) -> CommandResult<ResourceId> {
#[cfg(target_os = "ios")]
let path_ = path.clone(); let path_ = path.clone();
let resolved_path_handle = resolve_path( let resolved_path_handle = resolve_path(
"create", "create",
@@ -303,13 +288,16 @@ pub fn create<R: Runtime>(
resolved_path_handle.display() resolved_path_handle.display()
) )
})?; })?;
#[cfg(target_os = "ios")]
let app_handle = webview.app_handle().clone(); let app_handle = webview.app_handle().clone();
let file_handle = FileHandle::new( let file_handle = FileHandle {
file, file,
PathKind::Handle(resolved_path_handle), path: PathKind::Handle(resolved_path_handle),
#[cfg(target_os = "ios")]
path_, path_,
#[cfg(target_os = "ios")]
app_handle, app_handle,
); };
let rid = webview let rid = webview
.resources_table() .resources_table()
.add(StdFileResource::new(file_handle)); .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 lines = resource_table.get::<StdLinesResource>(rid)?;
let ret = StdLinesResource::with_lock(&lines, |lines| -> CommandResult<Vec<u8>> { 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` // at the end of returned vector so we can use `tauri::ipc::Response`
// and avoid serialization overhead of separate values. // and avoid serialization overhead of separate values.
match lines.next() { match lines.next() {
@@ -1417,6 +1405,7 @@ fn resolve_file_in_fs<R: Runtime>(
path: SafeFilePath, path: SafeFilePath,
open_options: OpenOptions, open_options: OpenOptions,
) -> CommandResult<FileHandle<R>> { ) -> CommandResult<FileHandle<R>> {
#[cfg(target_os = "ios")]
let path_ = path.clone(); let path_ = path.clone();
let resolved_path_handle = resolve_path( let resolved_path_handle = resolve_path(
permission, permission,
@@ -1436,13 +1425,16 @@ fn resolve_file_in_fs<R: Runtime>(
) )
})?; })?;
#[cfg(target_os = "ios")]
let app_handle = webview.app_handle().clone(); let app_handle = webview.app_handle().clone();
Ok(FileHandle::new( Ok(FileHandle {
file, file,
PathKind::Handle(resolved_path_handle), path: PathKind::Handle(resolved_path_handle),
#[cfg(target_os = "ios")]
path_, path_,
#[cfg(target_os = "ios")]
app_handle, app_handle,
)) })
} }
#[cfg(mobile)] #[cfg(mobile)]
@@ -1456,6 +1448,7 @@ pub fn resolve_file<R: Runtime>(
) -> CommandResult<FileHandle<R>> { ) -> CommandResult<FileHandle<R>> {
use crate::FsExt; use crate::FsExt;
#[cfg(target_os = "ios")]
let path_ = path.clone(); let path_ = path.clone();
match path { match path {
SafeFilePath::Url(url) => { SafeFilePath::Url(url) => {
@@ -1463,13 +1456,16 @@ pub fn resolve_file<R: Runtime>(
let file = webview let file = webview
.fs() .fs()
.open(SafeFilePath::Url(url.clone()), open_options.options)?; .open(SafeFilePath::Url(url.clone()), open_options.options)?;
#[cfg(target_os = "ios")]
let app_handle = webview.app_handle().clone(); let app_handle = webview.app_handle().clone();
Ok(FileHandle::new( Ok(FileHandle {
file, file,
PathKind::Path(resolved_path), path: PathKind::Path(resolved_path),
#[cfg(target_os = "ios")]
path_, path_,
#[cfg(target_os = "ios")]
app_handle, app_handle,
)) })
} }
SafeFilePath::Path(path) => resolve_file_in_fs( SafeFilePath::Path(path) => resolve_file_in_fs(
permission, 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 // 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 // Only if it hasn't been started already via start_accessing_security_scoped_resource
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
{ if let SafeFilePath::Url(url) = &path {
if let SafeFilePath::Url(url) = &path { if url.scheme() == "file" {
if url.scheme() == "file" { use objc2_foundation::{NSString, NSURL};
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) // Check if already active (started via start_accessing_security_scoped_resource)
if !security_scoped_resources.is_tracked_manually(url.as_str()) { if !security_scoped_resources.is_tracked_manually(url.as_str()) {
let url_nsstring = NSString::from_str(url.as_str()); let url_nsstring = NSString::from_str(url.as_str());
let ns_url = unsafe { NSURL::URLWithString(&url_nsstring) }; let ns_url = unsafe { NSURL::URLWithString(&url_nsstring) };
if let Some(ns_url) = ns_url { if let Some(ns_url) = ns_url {
// Start accessing the security-scoped resource // Start accessing the security-scoped resource
// This is required for files outside the app's sandbox (e.g., from file picker) // This is required for files outside the app's sandbox (e.g., from file picker)
unsafe { unsafe {
let success = ns_url.startAccessingSecurityScopedResource(); let success = ns_url.startAccessingSecurityScopedResource();
if success { if success {
log::debug!("Started accessing security-scoped resource for URL: {} (via resolve_path)", url.as_str()); log::debug!("Started accessing security-scoped resource for URL: {} (via resolve_path)", url.as_str());
// Track it so we know to clean it up // Track it so we know to clean it up
security_scoped_resources.track_manually(url.as_str().to_string()); security_scoped_resources.track_manually(url.as_str().to_string());
} else { } else {
log::warn!("Failed to start accessing security-scoped resource for URL: {}", url.as_str()); 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 { } 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 # 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] ## \[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. - [`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] [package]
name = "tauri-plugin-global-shortcut" name = "tauri-plugin-global-shortcut"
version = "2.3.1" version = "2.3.2"
description = "Register global hotkeys listeners on your Tauri application." description = "Register global hotkeys listeners on your Tauri application."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -27,4 +27,4 @@ log = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies] [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", "name": "@tauri-apps/plugin-global-shortcut",
"version": "2.3.1", "version": "2.3.2",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "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' import { fetch } from '@tauri-apps/plugin-http'
const response = await fetch('http://localhost:3003/users/2', { const response = await fetch('http://localhost:3003/users/2', {
method: 'GET', method: 'GET',
timeout: 30 connectTimeout: 30
}) })
``` ```
+6
View File
@@ -1,5 +1,11 @@
# Changelog # 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] ## \[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`. - [`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] [package]
name = "tauri-plugin-log" name = "tauri-plugin-log"
version = "2.8.0" version = "2.9.0"
description = "Configurable logging for your Tauri app." description = "Configurable logging for your Tauri app."
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
@@ -25,8 +25,7 @@ serde_json = { workspace = true }
tauri = { workspace = true } tauri = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
serde_repr = "0.1" serde_repr = "0.1"
byte-unit = "5" log = { workspace = true, features = ["kv"] }
log = { workspace = true, features = ["kv_unstable"] }
time = { version = "0.3", features = [ time = { version = "0.3", features = [
"formatting", "formatting",
"local-offset", "local-offset",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-log", "name": "@tauri-apps/plugin-log",
"version": "2.8.0", "version": "2.9.0",
"description": "Configurable logging for your Tauri app.", "description": "Configurable logging for your Tauri app.",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "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_ROTATION_STRATEGY: RotationStrategy = RotationStrategy::KeepOne;
const DEFAULT_TIMEZONE_STRATEGY: TimezoneStrategy = TimezoneStrategy::UseUtc; const DEFAULT_TIMEZONE_STRATEGY: TimezoneStrategy = TimezoneStrategy::UseUtc;
const DEFAULT_FILE_OPEN_STRATEGY: FileOpenStrategy = FileOpenStrategy::Append;
const DEFAULT_LOG_TARGETS: [Target; 2] = [ const DEFAULT_LOG_TARGETS: [Target; 2] = [
Target::new(TargetKind::Stdout), Target::new(TargetKind::Stdout),
Target::new(TargetKind::LogDir { file_name: None }), 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. /// A custom log writer that rotates the log file when it exceeds specified size.
struct RotatingFile { struct RotatingFile {
dir: PathBuf, dir: PathBuf,
file_name: String, file_name: String,
path: PathBuf, path: PathBuf,
/// Maximum file size before rotating in bytes
max_size: u64, max_size: u64,
/// Current file size in bytes
current_size: u64, current_size: u64,
rotation_strategy: RotationStrategy, rotation_strategy: RotationStrategy,
timezone_strategy: TimezoneStrategy, timezone_strategy: TimezoneStrategy,
file_open_strategy: FileOpenStrategy,
inner: Option<File>, inner: Option<File>,
buffer: Vec<u8>, buffer: Vec<u8>,
} }
@@ -166,6 +178,7 @@ impl RotatingFile {
max_size: u64, max_size: u64,
rotation_strategy: RotationStrategy, rotation_strategy: RotationStrategy,
timezone_strategy: TimezoneStrategy, timezone_strategy: TimezoneStrategy,
file_open_strategy: FileOpenStrategy,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let dir = dir.as_ref().to_path_buf(); let dir = dir.as_ref().to_path_buf();
let path = dir.join(&file_name).with_extension("log"); let path = dir.join(&file_name).with_extension("log");
@@ -178,12 +191,15 @@ impl RotatingFile {
current_size: 0, current_size: 0,
rotation_strategy, rotation_strategy,
timezone_strategy, timezone_strategy,
file_open_strategy,
inner: None, inner: None,
buffer: Vec::new(), buffer: Vec::new(),
}; };
rotator.open_file()?; 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()?; rotator.rotate()?;
} }
if let RotationStrategy::KeepSome(keep_count) = rotator.rotation_strategy { if let RotationStrategy::KeepSome(keep_count) = rotator.rotation_strategy {
@@ -396,6 +412,7 @@ pub struct Builder {
dispatch: fern::Dispatch, dispatch: fern::Dispatch,
rotation_strategy: RotationStrategy, rotation_strategy: RotationStrategy,
timezone_strategy: TimezoneStrategy, timezone_strategy: TimezoneStrategy,
file_open_strategy: FileOpenStrategy,
max_file_size: u128, max_file_size: u128,
targets: Vec<Target>, targets: Vec<Target>,
is_skip_logger: bool, is_skip_logger: bool,
@@ -423,6 +440,7 @@ impl Default for Builder {
dispatch, dispatch,
rotation_strategy: DEFAULT_ROTATION_STRATEGY, rotation_strategy: DEFAULT_ROTATION_STRATEGY,
timezone_strategy: DEFAULT_TIMEZONE_STRATEGY, timezone_strategy: DEFAULT_TIMEZONE_STRATEGY,
file_open_strategy: DEFAULT_FILE_OPEN_STRATEGY,
max_file_size: DEFAULT_MAX_FILE_SIZE as u128, max_file_size: DEFAULT_MAX_FILE_SIZE as u128,
targets: DEFAULT_LOG_TARGETS.into(), targets: DEFAULT_LOG_TARGETS.into(),
is_skip_logger: false, is_skip_logger: false,
@@ -435,11 +453,18 @@ impl Builder {
Default::default() Default::default()
} }
/// Sets the [`RotationStrategy`].
///
/// Default is [`RotationStrategy::KeepOne`]
pub fn rotation_strategy(mut self, rotation_strategy: RotationStrategy) -> Self { pub fn rotation_strategy(mut self, rotation_strategy: RotationStrategy) -> Self {
self.rotation_strategy = rotation_strategy; self.rotation_strategy = rotation_strategy;
self 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 { pub fn timezone_strategy(mut self, timezone_strategy: TimezoneStrategy) -> Self {
self.timezone_strategy = timezone_strategy.clone(); self.timezone_strategy = timezone_strategy.clone();
@@ -456,15 +481,28 @@ impl Builder {
self 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`. /// 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 { 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.max_file_size = max_file_size.min(u64::MAX as u128);
self 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 { pub fn clear_format(mut self) -> Self {
self.dispatch = self.dispatch.format(|out, message, _record| { self.dispatch = self.dispatch.format(|out, message, _record| {
out.finish(format_args!("{message}")); out.finish(format_args!("{message}"));
@@ -472,6 +510,37 @@ impl Builder {
self 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 pub fn format<F>(mut self, formatter: F) -> Self
where where
F: Fn(FormatCallback, &Arguments, &Record) + Sync + Send + 'static, F: Fn(FormatCallback, &Arguments, &Record) + Sync + Send + 'static,
@@ -480,16 +549,64 @@ impl Builder {
self 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 { pub fn level(mut self, level_filter: impl Into<LevelFilter>) -> Self {
self.dispatch = self.dispatch.level(level_filter.into()); self.dispatch = self.dispatch.level(level_filter.into());
self 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 { pub fn level_for(mut self, module: impl Into<Cow<'static, str>>, level: LevelFilter) -> Self {
self.dispatch = self.dispatch.level_for(module, level); self.dispatch = self.dispatch.level_for(module, level);
self 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 pub fn filter<F>(mut self, filter: F) -> Self
where where
F: Fn(&log::Metadata) -> bool + Send + Sync + 'static, F: Fn(&log::Metadata) -> bool + Send + Sync + 'static,
@@ -511,6 +628,19 @@ impl Builder {
/// tauri_plugin_log::Builder::new() /// tauri_plugin_log::Builder::new()
/// .target(Target::new(TargetKind::Webview)); /// .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 { pub fn target(mut self, target: Target) -> Self {
self.targets.push(target); self.targets.push(target);
self self
@@ -543,6 +673,19 @@ impl Builder {
/// Target::new(TargetKind::LogDir { file_name: Some("rust".into()) }).filter(|metadata| !metadata.target().starts_with(WEBVIEW_TARGET)), /// 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 { pub fn targets(mut self, targets: impl IntoIterator<Item = Target>) -> Self {
self.targets = Vec::from_iter(targets); self.targets = Vec::from_iter(targets);
self self
@@ -569,6 +712,7 @@ impl Builder {
mut dispatch: fern::Dispatch, mut dispatch: fern::Dispatch,
rotation_strategy: RotationStrategy, rotation_strategy: RotationStrategy,
timezone_strategy: TimezoneStrategy, timezone_strategy: TimezoneStrategy,
file_open_strategy: FileOpenStrategy,
max_file_size: u64, max_file_size: u64,
targets: Vec<Target>, targets: Vec<Target>,
) -> Result<(log::LevelFilter, Box<dyn log::Log>), Error> { ) -> Result<(log::LevelFilter, Box<dyn log::Log>), Error> {
@@ -621,6 +765,7 @@ impl Builder {
max_file_size, max_file_size,
rotation_strategy.clone(), rotation_strategy.clone(),
timezone_strategy.clone(), timezone_strategy.clone(),
file_open_strategy.clone(),
)?; )?;
fern::Output::writer(Box::new(rotator), "\n") fern::Output::writer(Box::new(rotator), "\n")
} }
@@ -636,6 +781,7 @@ impl Builder {
max_file_size, max_file_size,
rotation_strategy.clone(), rotation_strategy.clone(),
timezone_strategy.clone(), timezone_strategy.clone(),
file_open_strategy.clone(),
)?; )?;
fern::Output::writer(Box::new(rotator), "\n") fern::Output::writer(Box::new(rotator), "\n")
} }
@@ -681,6 +827,7 @@ impl Builder {
self.dispatch, self.dispatch,
self.rotation_strategy, self.rotation_strategy,
self.timezone_strategy, self.timezone_strategy,
self.file_open_strategy,
self.max_file_size as u64, self.max_file_size as u64,
self.targets, self.targets,
)?; )?;
@@ -697,6 +844,7 @@ impl Builder {
self.dispatch, self.dispatch,
self.rotation_strategy, self.rotation_strategy,
self.timezone_strategy, self.timezone_strategy,
self.file_open_strategy,
self.max_file_size as u64, self.max_file_size as u64,
self.targets, self.targets,
)?; )?;
+1
View File
@@ -47,6 +47,7 @@ impl<R: Runtime> Nfc<R> {
pub fn scan(&self, payload: ScanRequest) -> crate::Result<ScanResponse> { pub fn scan(&self, payload: ScanRequest) -> crate::Result<ScanResponse> {
self.0 self.0
.run_mobile_plugin("scan", payload) .run_mobile_plugin("scan", payload)
.map(|v| ScanResponse { tag: v })
.map_err(Into::into) .map_err(Into::into)
} }
+1 -2
View File
@@ -232,10 +232,9 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
let app_dir_ = app_dir.clone(); let app_dir_ = app_dir.clone();
if let Some(fs_scope) = &fs_scope { if let Some(fs_scope) = &fs_scope {
let app_ = app.clone();
fs_scope.listen(move |event| { fs_scope.listen(move |event| {
if let tauri::fs::Event::PathAllowed(_) = 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 # 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] ## \[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. - [`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] [package]
name = "tauri-plugin-positioner" name = "tauri-plugin-positioner"
version = "2.3.1" version = "2.3.3"
description = "Position your windows at well-known locations." description = "Position your windows at well-known locations."
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-positioner", "name": "@tauri-apps/plugin-positioner",
"version": "2.3.1", "version": "2.3.3",
"description": "Position your windows at well-known locations.", "description": "Position your windows at well-known locations.",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
+21 -7
View File
@@ -152,7 +152,9 @@ fn calculate_position<R: Runtime>(
) -> Result<PhysicalPosition<i32>> { ) -> Result<PhysicalPosition<i32>> {
use Position::*; 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, // Only use the screen_position for the Tray independent positioning,
// because a tray event may not be called on the currently active monitor. // because a tray event may not be called on the currently active monitor.
let screen_position = screen.position(); let screen_position = screen.position();
@@ -225,7 +227,9 @@ fn calculate_position<R: Runtime>(
PhysicalPosition { x: tray_x, y } PhysicalPosition { x: tray_x, y }
} else { } else {
panic!("Tray position not set"); return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
} }
} }
#[cfg(feature = "tray-icon")] #[cfg(feature = "tray-icon")]
@@ -236,7 +240,9 @@ fn calculate_position<R: Runtime>(
y: tray_y, y: tray_y,
} }
} else { } else {
panic!("Tray position not set"); return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
} }
} }
#[cfg(feature = "tray-icon")] #[cfg(feature = "tray-icon")]
@@ -257,7 +263,9 @@ fn calculate_position<R: Runtime>(
y, y,
} }
} else { } else {
panic!("Tray position not set"); return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
} }
} }
#[cfg(feature = "tray-icon")] #[cfg(feature = "tray-icon")]
@@ -268,7 +276,9 @@ fn calculate_position<R: Runtime>(
y: tray_y, y: tray_y,
} }
} else { } else {
panic!("Tray position not set"); return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
} }
} }
#[cfg(feature = "tray-icon")] #[cfg(feature = "tray-icon")]
@@ -287,7 +297,9 @@ fn calculate_position<R: Runtime>(
PhysicalPosition { x, y } PhysicalPosition { x, y }
} else { } else {
panic!("Tray position not set"); return Err(tauri::Error::Io(std::io::Error::other(
"Tray position not set",
)));
} }
} }
#[cfg(feature = "tray-icon")] #[cfg(feature = "tray-icon")]
@@ -298,7 +310,9 @@ fn calculate_position<R: Runtime>(
y: tray_y, y: tray_y,
} }
} else { } 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 }); let _ = block_on_task(async move { tx_.send(wrapper(buf)).await });
} }
Err(e) => { Err(e) => {
let tx_ = tx.clone(); let _ =
let _ = block_on_task( block_on_task(async move { tx.send(CommandEvent::Error(e.to_string())).await });
async move { tx_.send(CommandEvent::Error(e.to_string())).await },
);
break; break;
} }
} }
+4
View File
@@ -1,5 +1,9 @@
# Changelog # 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] ## \[2.4.2]
### Dependencies ### Dependencies
+2 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-single-instance" name = "tauri-plugin-single-instance"
version = "2.4.2" version = "2.4.3"
description = "Ensure a single instance of your tauri app is running." description = "Ensure a single instance of your tauri app is running."
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
@@ -24,6 +24,7 @@ tracing = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tauri-plugin-deep-link = { path = "../deep-link", version = "2.4.9", optional = true } tauri-plugin-deep-link = { path = "../deep-link", version = "2.4.9", optional = true }
semver = { version = "1", optional = true } semver = { version = "1", optional = true }
tokio = { version = "1", features = ["net"] }
[target."cfg(target_os = \"windows\")".dependencies.windows-sys] [target."cfg(target_os = \"windows\")".dependencies.windows-sys]
version = "0.60" version = "0.60"
@@ -9,6 +9,6 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.11.0" "@tauri-apps/cli": "2.11.4"
} }
} }
@@ -3,8 +3,8 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::{ use std::{
io::{BufWriter, Error, ErrorKind, Read, Write}, io::{BufWriter, Error, ErrorKind, Write},
os::unix::net::{UnixListener, UnixStream}, os::unix::net::UnixStream,
path::PathBuf, path::PathBuf,
}; };
@@ -15,6 +15,7 @@ use tauri::{
plugin::{self, TauriPlugin}, plugin::{self, TauriPlugin},
AppHandle, Config, Manager, RunEvent, Runtime, AppHandle, Config, Manager, RunEvent, Runtime,
}; };
use tokio::io::AsyncReadExt;
pub fn init<R: Runtime>(cb: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> { pub fn init<R: Runtime>(cb: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
plugin::Builder::new("single-instance") plugin::Builder::new("single-instance")
@@ -31,7 +32,7 @@ pub fn init<R: Runtime>(cb: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
ErrorKind::NotFound | ErrorKind::ConnectionRefused => { ErrorKind::NotFound | ErrorKind::ConnectionRefused => {
// This process claims itself as singleton as likely none exists // This process claims itself as singleton as likely none exists
socket_cleanup(&socket); socket_cleanup(&socket);
listen_for_other_instances(&socket, app.clone(), cb); listen_for_other_instances(socket, app.clone(), cb);
} }
_ => { _ => {
tracing::debug!( tracing::debug!(
@@ -92,42 +93,40 @@ fn notify_singleton(socket: &PathBuf) -> Result<(), Error> {
} }
fn listen_for_other_instances<A: Runtime>( fn listen_for_other_instances<A: Runtime>(
socket: &PathBuf, socket: PathBuf,
app: AppHandle<A>, app: AppHandle<A>,
mut cb: Box<SingleInstanceCallback<A>>, mut cb: Box<SingleInstanceCallback<A>>,
) { ) {
match UnixListener::bind(socket) { tauri::async_runtime::spawn(async move {
Ok(listener) => { match tokio::net::UnixListener::bind(socket) {
tauri::async_runtime::spawn(async move { Ok(listener) => loop {
for stream in listener.incoming() { match listener.accept().await {
match stream { Ok((mut stream, _addr)) => {
Ok(mut stream) => { let mut s = String::new();
let mut s = String::new(); match stream.read_to_string(&mut s).await {
match stream.read_to_string(&mut s) { Ok(_) => {
Ok(_) => { let (cwd, args) = s.split_once("\0\0").unwrap_or_default();
let (cwd, args) = s.split_once("\0\0").unwrap_or_default(); let args: Vec<String> =
let args: Vec<String> = args.split('\0').map(String::from).collect();
args.split('\0').map(String::from).collect(); cb(app.app_handle(), args, cwd.to_string());
cb(app.app_handle(), args, cwd.to_string()); }
} Err(e) => {
Err(e) => { tracing::debug!("single_instance failed to be notified: {e}")
tracing::debug!("single_instance failed to be notified: {e}")
}
} }
} }
Err(err) => { }
tracing::debug!("single_instance failed to be notified: {}", err); Err(err) => {
continue; 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 # 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] ## \[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`) - [`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] [package]
name = "tauri-plugin-store" name = "tauri-plugin-store"
version = "2.4.3" version = "2.4.4"
description = "Simple, persistent key-value store." description = "Simple, persistent key-value store."
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
@@ -8,7 +8,7 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.11.0", "@tauri-apps/cli": "2.11.4",
"typescript": "^6.0.0", "typescript": "^6.0.0",
"vite": "^8.0.1" "vite": "^8.0.1"
} }
+1 -1
View File
@@ -21,7 +21,7 @@ export type StoreOptions = {
/** /**
* Default value of the store * 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 * 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", "name": "@tauri-apps/plugin-store",
"version": "2.4.3", "version": "2.4.4",
"description": "Simple, persistent key-value store.", "description": "Simple, persistent key-value store.",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
+1 -1
View File
@@ -41,7 +41,7 @@ url = { workspace = true }
http = "1" http = "1"
minisign-verify = "0.2" minisign-verify = "0.2"
time = { version = "0.3", features = ["parsing", "formatting"] } time = { version = "0.3", features = ["parsing", "formatting"] }
base64 = "0.22" base64 = "0.23"
semver = { version = "1", features = ["serde"] } semver = { version = "1", features = ["serde"] }
futures-util = "0.3" futures-util = "0.3"
tempfile = "3.20" tempfile = "3.20"
+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 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 { interface UpdateMetadata {
rid: number rid: number
currentVersion: string currentVersion: string
@@ -95,14 +103,15 @@ class Update extends Resource {
} }
/** Install downloaded updater package */ /** Install downloaded updater package */
async install(): Promise<void> { async install(options?: InstallOptions): Promise<void> {
if (!this.downloadedBytes) { if (!this.downloadedBytes) {
throw new Error('Update.install called before Update.download') throw new Error('Update.install called before Update.download')
} }
await invoke('plugin:updater|install', { await invoke('plugin:updater|install', {
updateRid: this.rid, 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 // 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 */ /** Downloads the updater package and installs it */
async downloadAndInstall( async downloadAndInstall(
onEvent?: (progress: DownloadEvent) => void, onEvent?: (progress: DownloadEvent) => void,
options?: DownloadOptions options?: DownloadOptions & InstallOptions
): Promise<void> { ): Promise<void> {
convertToRustHeaders(options) convertToRustHeaders(options)
const channel = new Channel<DownloadEvent>() const channel = new Channel<DownloadEvent>()
+16 -1
View File
@@ -142,12 +142,22 @@ pub(crate) async fn install<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
update_rid: ResourceId, update_rid: ResourceId,
bytes_rid: ResourceId, bytes_rid: ResourceId,
restart_after_install: Option<bool>,
) -> Result<()> { ) -> Result<()> {
let update = webview.resources_table().get::<Update>(update_rid)?; let update = webview.resources_table().get::<Update>(update_rid)?;
let bytes = webview let bytes = webview
.resources_table() .resources_table()
.get::<DownloadedBytes>(bytes_rid)?; .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); let _ = webview.resources_table().close(bytes_rid);
Ok(()) Ok(())
} }
@@ -159,6 +169,7 @@ pub(crate) async fn download_and_install<R: Runtime>(
on_event: Channel<DownloadEvent>, on_event: Channel<DownloadEvent>,
headers: Option<Vec<(String, String)>>, headers: Option<Vec<(String, String)>>,
timeout: Option<u64>, timeout: Option<u64>,
restart_after_install: Option<bool>,
) -> Result<()> { ) -> Result<()> {
let update = webview.resources_table().get::<Update>(rid)?; 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)); 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; let mut first_chunk = true;
update 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. /// Returns the associated nsis arguments.
pub fn nsis_args(&self) -> &'static [&'static str] { pub fn nsis_args(&self) -> &'static [&'static str] {
// `/P`: Passive // `/P`: Passive
// `/S`: Silent // `/S`: Silent
// `/R`: Restart // `/R`: Restart
match self { match self {
Self::Passive => &["/P", "/R"], Self::Passive => &["/P"],
Self::Quiet => &["/S", "/R"], 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 { impl Display for WindowsUpdateInstallMode {
@@ -63,6 +76,8 @@ impl Display for WindowsUpdateInstallMode {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct WindowsConfig { pub struct WindowsConfig {
/// Additional arguments given to the NSIS or WiX installer. /// Additional arguments given to the NSIS or WiX installer.
///
/// Note: this applies to both WiX and NSIS installers
#[serde( #[serde(
default, default,
alias = "installer-args", 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); builder = builder.target(target);
} }
let args = self.env().args_os; #[cfg(windows)]
if !args.is_empty() { {
builder = builder.current_exe_args(args); builder = builder.current_exe_args(self.env().args_os);
} }
builder.version_comparator = version_comparator.clone(); 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 OnBeforeExit = Arc<dyn Fn() + Send + Sync + 'static>;
pub type OnBeforeRequest = Arc<dyn Fn(ClientBuilder) -> ClientBuilder + 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>; pub type VersionComparator = Arc<dyn Fn(Version, RemoteRelease) -> bool + Send + Sync>;
#[cfg(target_os = "macos")]
type MainThreadClosure = Box<dyn FnOnce() + Send + Sync + 'static>; type MainThreadClosure = Box<dyn FnOnce() + Send + Sync + 'static>;
type RunOnMainThread = #[cfg(target_os = "macos")]
Box<dyn Fn(MainThreadClosure) -> std::result::Result<(), tauri::Error> + Send + Sync + 'static>; 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 { pub struct UpdaterBuilder {
#[allow(dead_code)]
run_on_main_thread: RunOnMainThread,
app_name: String,
current_version: Version, current_version: Version,
config: Config,
pub(crate) version_comparator: Option<VersionComparator>, pub(crate) version_comparator: Option<VersionComparator>,
executable_path: Option<PathBuf>, executable_path: Option<PathBuf>,
target: Option<String>, target: Option<String>,
@@ -147,27 +165,38 @@ pub struct UpdaterBuilder {
timeout: Option<Duration>, timeout: Option<Duration>,
proxy: Option<Url>, proxy: Option<Url>,
no_proxy: bool, no_proxy: bool,
installer_args: Vec<OsString>, context: UpdaterContext,
current_exe_args: Vec<OsString>,
on_before_exit: Option<OnBeforeExit>,
configure_client: Option<OnBeforeRequest>,
} }
impl UpdaterBuilder { impl UpdaterBuilder {
pub(crate) fn new<R: Runtime>(app: &AppHandle<R>, config: crate::Config) -> Self { pub(crate) fn new<R: Runtime>(app: &AppHandle<R>, config: crate::Config) -> Self {
let app_ = app.clone(); #[cfg(target_os = "macos")]
let run_on_main_thread = move |f| app_.run_on_main_thread(f); let run_on_main_thread = {
let app_ = app.clone();
Arc::new(move |f| app_.run_on_main_thread(f))
};
Self { Self {
run_on_main_thread: Box::new(run_on_main_thread), context: UpdaterContext {
installer_args: config #[cfg(windows)]
.windows installer_args: config
.as_ref() .windows
.map(|w| w.installer_args.clone()) .as_ref()
.unwrap_or_default(), .map(|w| w.installer_args.clone())
current_exe_args: Vec::new(), .unwrap_or_default(),
app_name: app.package_info().name.clone(), 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(), current_version: app.package_info().version.clone(),
config,
version_comparator: None, version_comparator: None,
executable_path: None, executable_path: None,
target: None, target: None,
@@ -176,8 +205,6 @@ impl UpdaterBuilder {
timeout: None, timeout: None,
proxy: None, proxy: None,
no_proxy: false, 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> { pub fn endpoints(mut self, endpoints: Vec<Url>) -> Result<Self> {
crate::config::validate_endpoints( crate::config::validate_endpoints(
&endpoints, &endpoints,
self.config.dangerous_insecure_transport_protocol, self.context.config.dangerous_insecure_transport_protocol,
)?; )?;
self.endpoints.replace(endpoints); self.endpoints.replace(endpoints);
@@ -251,26 +278,40 @@ impl UpdaterBuilder {
} }
pub fn pubkey<S: Into<String>>(mut self, pubkey: S) -> Self { pub fn pubkey<S: Into<String>>(mut self, pubkey: S) -> Self {
self.config.pubkey = pubkey.into(); self.context.config.pubkey = pubkey.into();
self self
} }
/// Adds an argument to pass to the Windows installer. /// 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 pub fn installer_arg<S>(mut self, arg: S) -> Self
where where
S: Into<OsString>, S: Into<OsString>,
{ {
self.installer_args.push(arg.into()); #[cfg(windows)]
{
self.context.installer_args.push(arg.into());
}
self self
} }
/// Adds multiple arguments to pass to the Windows installer. /// 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 pub fn installer_args<I, S>(mut self, args: I) -> Self
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
S: Into<OsString>, 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 self
} }
@@ -280,14 +321,32 @@ impl UpdaterBuilder {
/// [`Self::installer_arg`], [`crate::Builder::installer_arg`] /// [`Self::installer_arg`], [`crate::Builder::installer_arg`]
/// and the `plugins > updater > windows > installerArgs` config, /// and the `plugins > updater > windows > installerArgs` config,
/// not the ones managed by us (e.g. `/UPDATER` flag passed to the NSIS installer) /// 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 { pub fn clear_installer_args(mut self) -> Self {
self.installer_args.clear(); #[cfg(windows)]
{
self.context.installer_args.clear();
}
self self
} }
/// Function to run before we run the installer and exit the app through `std::process::exit(0)` on Windows /// 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 { 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 self
} }
@@ -299,14 +358,14 @@ impl UpdaterBuilder {
mut self, mut self,
f: F, f: F,
) -> Self { ) -> Self {
self.configure_client.replace(Arc::new(f)); self.context.configure_client.replace(Arc::new(f));
self self
} }
pub fn build(self) -> Result<Updater> { pub fn build(self) -> Result<Updater> {
let endpoints = self let endpoints = self
.endpoints .endpoints
.unwrap_or_else(|| self.config.endpoints.clone()); .unwrap_or_else(|| self.context.config.endpoints.clone());
if endpoints.is_empty() { if endpoints.is_empty() {
return Err(Error::EmptyEndpoints); return Err(Error::EmptyEndpoints);
@@ -324,44 +383,36 @@ impl UpdaterBuilder {
}; };
Ok(Updater { 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, current_version: self.current_version,
version_comparator: self.version_comparator, version_comparator: self.version_comparator,
timeout: self.timeout, timeout: self.timeout,
proxy: self.proxy, proxy: self.proxy,
no_proxy: self.no_proxy, no_proxy: self.no_proxy,
endpoints, endpoints,
installer_args: self.installer_args,
current_exe_args: self.current_exe_args,
arch, arch,
target: self.target, target: self.target,
headers: self.headers, headers: self.headers,
extract_path, extract_path,
on_before_exit: self.on_before_exit, context: self.context.clone(),
configure_client: self.configure_client,
}) })
} }
} }
#[cfg(windows)]
impl UpdaterBuilder { impl UpdaterBuilder {
pub(crate) fn current_exe_args<I, S>(mut self, args: I) -> Self pub(crate) fn current_exe_args<I, S>(mut self, args: I) -> Self
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
S: Into<OsString>, S: Into<OsString>,
{ {
self.current_exe_args self.context
.current_exe_args
.extend(args.into_iter().map(Into::into)); .extend(args.into_iter().map(Into::into));
self self
} }
} }
pub struct Updater { pub struct Updater {
#[allow(dead_code)]
run_on_main_thread: Arc<RunOnMainThread>,
config: Config,
app_name: String,
current_version: Version, current_version: Version,
version_comparator: Option<VersionComparator>, version_comparator: Option<VersionComparator>,
timeout: Option<Duration>, timeout: Option<Duration>,
@@ -374,12 +425,7 @@ pub struct Updater {
target: Option<String>, target: Option<String>,
headers: HeaderMap, headers: HeaderMap,
extract_path: PathBuf, extract_path: PathBuf,
on_before_exit: Option<OnBeforeExit>, context: UpdaterContext,
configure_client: Option<OnBeforeRequest>,
#[allow(unused)]
installer_args: Vec<OsString>,
#[allow(unused)]
current_exe_args: Vec<OsString>,
} }
impl Updater { impl Updater {
@@ -449,10 +495,10 @@ impl Updater {
} }
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT); 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); 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); request = request.danger_accept_invalid_hostnames(true);
} }
if let Some(timeout) = self.timeout { if let Some(timeout) = self.timeout {
@@ -467,7 +513,7 @@ impl Updater {
request = request.proxy(proxy); 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); request = configure_client(request);
} }
@@ -537,10 +583,6 @@ impl Updater {
let update = if should_update { let update = if should_update {
Some(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(), current_version: self.current_version.to_string(),
target: target.to_owned(), target: target.to_owned(),
extract_path: self.extract_path.clone(), extract_path: self.extract_path.clone(),
@@ -554,9 +596,7 @@ impl Updater {
proxy: self.proxy.clone(), proxy: self.proxy.clone(),
no_proxy: self.no_proxy, no_proxy: self.no_proxy,
headers: self.headers.clone(), headers: self.headers.clone(),
installer_args: self.installer_args.clone(), context: self.context.clone(),
current_exe_args: self.current_exe_args.clone(),
configure_client: self.configure_client.clone(),
}) })
} else { } else {
None None
@@ -600,11 +640,6 @@ impl Updater {
#[derive(Clone)] #[derive(Clone)]
pub struct Update { pub struct Update {
#[allow(dead_code)]
run_on_main_thread: Arc<RunOnMainThread>,
config: Config,
#[allow(unused)]
on_before_exit: Option<OnBeforeExit>,
/// Update description /// Update description
pub body: Option<String>, pub body: Option<String>,
/// Version used to check for update /// Version used to check for update
@@ -633,14 +668,7 @@ pub struct Update {
/// Extract path /// Extract path
#[allow(unused)] #[allow(unused)]
extract_path: PathBuf, extract_path: PathBuf,
/// App name, used for creating named tempfiles on Windows context: UpdaterContext,
#[allow(unused)]
app_name: String,
#[allow(unused)]
installer_args: Vec<OsString>,
#[allow(unused)]
current_exe_args: Vec<OsString>,
configure_client: Option<OnBeforeRequest>,
} }
impl Resource for Update {} impl Resource for Update {}
@@ -661,10 +689,10 @@ impl Update {
} }
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT); 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); 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); request = request.danger_accept_invalid_hostnames(true);
} }
if let Some(timeout) = self.timeout { if let Some(timeout) = self.timeout {
@@ -676,7 +704,7 @@ impl Update {
let proxy = reqwest::Proxy::all(proxy.as_str())?; let proxy = reqwest::Proxy::all(proxy.as_str())?;
request = request.proxy(proxy); 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); request = configure_client(request);
} }
let response = request let response = request
@@ -709,7 +737,7 @@ impl Update {
} }
on_download_finish(); on_download_finish();
verify_signature(&buffer, &self.signature, &self.config.pubkey)?; verify_signature(&buffer, &self.signature, &self.context.config.pubkey)?;
Ok(buffer) Ok(buffer)
} }
@@ -733,6 +761,16 @@ impl Update {
fn install_inner(&self, _bytes: &[u8]) -> Result<()> { fn install_inner(&self, _bytes: &[u8]) -> Result<()> {
Ok(()) 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)] #[cfg(windows)]
@@ -785,7 +823,6 @@ impl Update {
/// │ └──[AppName]_[version]_x64-setup.exe # NSIS installer /// │ └──[AppName]_[version]_x64-setup.exe # NSIS installer
/// └── ... /// └── ...
fn install_inner(&self, bytes: &[u8]) -> Result<()> { fn install_inner(&self, bytes: &[u8]) -> Result<()> {
use std::iter::once;
use windows_sys::{ use windows_sys::{
w, w,
Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW}, Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOW},
@@ -793,48 +830,7 @@ impl Update {
let updater_type = self.extract(bytes)?; let updater_type = self.extract(bytes)?;
let install_mode = self.config.install_mode(); if let Some(on_before_exit) = self.context.on_before_exit.as_ref() {
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() {
log::debug!("running on_before_exit hook"); log::debug!("running on_before_exit hook");
on_before_exit(); on_before_exit();
} }
@@ -846,9 +842,11 @@ impl Update {
|p| OsString::from(format!("{p}\\System32\\msiexec.exe")), |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); let parameters = encode_wide(parameters);
unsafe { unsafe {
@@ -865,18 +863,72 @@ impl Update {
std::process::exit(0); std::process::exit(0);
} }
fn installer_args(&self) -> Vec<&OsStr> { fn updater_parameters(&self, updater_type: &WindowsUpdaterType) -> OsString {
self.installer_args let install_mode = self.context.config.install_mode();
.iter() let current_args = &self.context.current_exe_args[1..];
.map(OsStr::new)
.collect::<Vec<_>>() 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> { fn installer_args(
self.current_exe_args &self,
.iter() ) -> std::iter::Map<std::slice::Iter<'_, OsString>, fn(&OsString) -> &OsStr> {
.map(OsStr::new) self.context.installer_args.iter().map(OsStr::new)
.collect::<Vec<_>>()
} }
fn extract(&self, bytes: &[u8]) -> Result<WindowsUpdaterType> { fn extract(&self, bytes: &[u8]) -> Result<WindowsUpdaterType> {
@@ -890,7 +942,10 @@ impl Update {
fn make_temp_dir(&self) -> Result<PathBuf> { fn make_temp_dir(&self) -> Result<PathBuf> {
Ok(tempfile::Builder::new() Ok(tempfile::Builder::new()
.prefix(&format!("{}-{}-updater-", self.app_name, self.version)) .prefix(&format!(
"{}-{}-updater-",
self.context.app_name, self.version
))
.tempdir()? .tempdir()?
.keep()) .keep())
} }
@@ -938,7 +993,10 @@ impl Update {
let temp_dir = self.make_temp_dir()?; let temp_dir = self.make_temp_dir()?;
let mut temp_file = tempfile::Builder::new() 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) .suffix(ext)
.rand_bytes(0) .rand_bytes(0)
.tempfile_in(temp_dir)?; .tempfile_in(temp_dir)?;
@@ -1277,7 +1335,7 @@ impl Update {
); );
let (tx, rx) = std::sync::mpsc::channel(); 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 = let mut script =
osakit::Script::new_from_source(osakit::Language::AppleScript, &apple_script); osakit::Script::new_from_source(osakit::Language::AppleScript, &apple_script);
script.compile().expect("invalid AppleScript"); script.compile().expect("invalid AppleScript");
@@ -1450,7 +1508,7 @@ where
} }
// Validate signature // 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 // we need to convert the pub key
let pub_key_decoded = base64_to_string(pub_key)?; let pub_key_decoded = base64_to_string(pub_key)?;
let public_key = PublicKey::decode(&pub_key_decoded)?; 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 // Validate signature or bail out
public_key.verify(data, &signature, true)?; public_key.verify(data, &signature, true)?;
Ok(true) Ok(())
} }
fn base64_to_string(base64_string: &str) -> Result<String> { 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 // adapted from https://github.com/rust-lang/rust/blob/1c047506f94cd2d05228eb992b0a6bbed1942349/library/std/src/sys/args/windows.rs#L174
#[cfg(windows)] #[cfg(windows)]
fn escape_nsis_current_exe_arg(arg: &&OsStr) -> String { fn escape_nsis_current_exe_arg(arg: impl AsRef<OsStr>) -> OsString {
let arg = arg.to_string_lossy(); use std::os::windows::ffi::{OsStrExt, OsStringExt};
let mut cmd: Vec<char> = Vec::new();
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. // 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; let escape = true;
if quote { if quote {
cmd.push('"'); cmd.push('"' as u16);
} }
let mut backslashes: usize = 0; let mut backslashes: usize = 0;
for x in arg.chars() { for x in arg.encode_wide() {
if escape { if escape {
if x == '\\' { if x == '\\' as u16 {
backslashes += 1; backslashes += 1;
} else { } else {
if x == '"' { if x == '"' as u16 {
// Add n+1 backslashes to total 2n+1 before internal '"'. // Add n+1 backslashes to total 2n+1 before internal '"'.
cmd.extend((0..=backslashes).map(|_| '\\')); cmd.extend((0..=backslashes).map(|_| '\\' as u16));
} }
backslashes = 0; backslashes = 0;
} }
@@ -1525,10 +1589,10 @@ fn escape_nsis_current_exe_arg(arg: &&OsStr) -> String {
} }
if quote { if quote {
// Add n backslashes to total 2n before ending '"'. // Add n backslashes to total 2n before ending '"'.
cmd.extend((0..backslashes).map(|_| '\\')); cmd.extend((0..backslashes).map(|_| '\\' as u16));
cmd.push('"'); cmd.push('"' as u16);
} }
cmd.into_iter().collect() OsString::from_wide(&cmd)
} }
#[cfg(windows)] #[cfg(windows)]
@@ -6,8 +6,7 @@
"endpoints": ["http://localhost:3007"], "endpoints": ["http://localhost:3007"],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUwNDRGMjkwRjg2MDhCRDAKUldUUWkyRDRrUEpFNEQ4SmdwcU5PaXl6R2ZRUUNvUnhIaVkwVUltV0NMaEx6VTkrWVhpT0ZqeEEK", "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUwNDRGMjkwRjg2MDhCRDAKUldUUWkyRDRrUEpFNEQ4SmdwcU5PaXl6R2ZRUUNvUnhIaVkwVUltV0NMaEx6VTkrWVhpT0ZqeEEK",
"windows": { "windows": {
"installMode": "quiet", "installMode": "quiet"
"installerArgs": ["/NS"]
} }
} }
}, },
@@ -46,7 +46,7 @@ struct Update {
platforms: HashMap<String, PlatformUpdate>, 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"); let mut command = Command::new("cargo");
command command
.args(["tauri", "build", "--verbose"]) .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", "") .env("TAURI_SIGNING_PRIVATE_KEY_PASSWORD", "")
.current_dir(cwd); .current_dir(cwd);
#[cfg(target_os = "linux")] if let Some(target) = target {
command.args(["--bundles", target.name()]); command.arg("--bundles").arg(target.name());
#[cfg(target_os = "macos")]
command.args(["--bundles", target.name()]);
if bundle_updater {
#[cfg(windows)]
command.args(["--bundles", "msi", "nsis"]);
} else { } else {
#[cfg(windows)] command.arg("--no-bundle");
command.args(["--bundles", target.name()]);
} }
let status = command let status = command
@@ -318,7 +311,7 @@ fn update_app() {
{ {
// bundle app update // bundle app update
config.version = "1.0.0"; 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 { let bundle_updater_ext = if v1_compatible {
out_bundle_path out_bundle_path
@@ -401,7 +394,7 @@ fn update_app() {
config.version = "0.1.0"; config.version = "0.1.0";
// bundle initial app version // 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 { for expected_exit_code in status_checks {
let mut binary_cmd = if cfg!(windows) { let mut binary_cmd = if cfg!(windows) {
+9 -10
View File
@@ -1822,15 +1822,14 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.78" version = "0.10.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"cfg-if", "cfg-if",
"foreign-types", "foreign-types",
"libc", "libc",
"once_cell",
"openssl-macros", "openssl-macros",
"openssl-sys", "openssl-sys",
] ]
@@ -1854,9 +1853,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.114" version = "0.9.116"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -2951,9 +2950,9 @@ dependencies = [
[[package]] [[package]]
name = "tar" name = "tar"
version = "0.4.45" version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [ dependencies = [
"filetime", "filetime",
"libc", "libc",
@@ -3795,7 +3794,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@@ -4138,9 +4137,9 @@ dependencies = [
[[package]] [[package]]
name = "wry" name = "wry"
version = "0.24.10" version = "0.24.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00711278ed357350d44c749c286786ecac644e044e4da410d466212152383b45" checksum = "4a2a144c3ab5e83e04724bc8e67cea552ffae413185fda459fafdae173fd985d"
dependencies = [ dependencies = [
"base64 0.13.1", "base64 0.13.1",
"block", "block",
+6 -3
View File
@@ -66,7 +66,8 @@ import { upload, HttpMethod } from '@tauri-apps/plugin-upload'
upload( upload(
'https://example.com/file-upload', 'https://example.com/file-upload',
'./path/to/my/file.txt', './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 { 'Content-Type': 'text/plain' } // optional headers to send with the request
) )
@@ -74,7 +75,8 @@ upload(
upload( upload(
'https://example.com/file-upload', 'https://example.com/file-upload',
'./path/to/my/file.txt', './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' }, { 'Content-Type': 'text/plain' },
HttpMethod.Put // Use HttpMethod enum - supports POST, PUT, PATCH HttpMethod.Put // Use HttpMethod enum - supports POST, PUT, PATCH
) )
@@ -86,7 +88,8 @@ import { download } from '@tauri-apps/plugin-upload'
download( download(
'https://example.com/file-download-link', 'https://example.com/file-download-link',
'./path/to/save/my/file.txt', './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 { 'Content-Type': 'text/plain' } // optional headers to send with the request
) )
``` ```
@@ -9,7 +9,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.11.0", "@tauri-apps/cli": "2.11.4",
"typescript": "^6.0.0", "typescript": "^6.0.0",
"vite": "^8.0.1" "vite": "^8.0.1"
}, },
+36 -36
View File
@@ -452,47 +452,47 @@ impl Builder {
} }
} }
WindowEvent::Moved(position) if state_flags.contains(StateFlags::POSITION) => { WindowEvent::Moved(position)
if window_clone if state_flags.contains(StateFlags::POSITION)
.state::<RestoringWindowState>() && window_clone
.0 .state::<RestoringWindowState>()
.try_lock() .0
.is_ok() .try_lock()
&& !window_clone.is_minimized().unwrap_or_default() .is_ok()
{ && !window_clone.is_minimized().unwrap_or_default() =>
let mut c = cache.lock().unwrap(); {
if let Some(state) = c.get_mut(&label) { let mut c = cache.lock().unwrap();
state.prev_x = state.x; if let Some(state) = c.get_mut(&label) {
state.prev_y = state.y; state.prev_x = state.x;
state.prev_y = state.y;
state.x = position.x; state.x = position.x;
state.y = position.y; state.y = position.y;
}
} }
} }
WindowEvent::Resized(size) if state_flags.contains(StateFlags::SIZE) => { WindowEvent::Resized(size)
if window_clone if state_flags.contains(StateFlags::SIZE)
.state::<RestoringWindowState>() && window_clone
.0 .state::<RestoringWindowState>()
.try_lock() .0
.is_ok() .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. false
let is_maximized = if cfg!(target_os = "macos") } else {
&& (!window_clone.is_decorated().unwrap_or_default() window_clone.is_maximized().unwrap_or_default()
|| !window_clone.is_resizable().unwrap_or_default()) };
{
false
} else {
window_clone.is_maximized().unwrap_or_default()
};
if !window_clone.is_minimized().unwrap_or_default() && !is_maximized { if !window_clone.is_minimized().unwrap_or_default() && !is_maximized {
let mut c = cache.lock().unwrap(); let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) { if let Some(state) = c.get_mut(&label) {
state.width = size.width; state.width = size.width;
state.height = size.height; state.height = size.height;
}
} }
} }
} }
+551 -549
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -2,3 +2,6 @@ packages:
- plugins/* - plugins/*
- plugins/*/examples/* - plugins/*/examples/*
- examples/* - examples/*
allowBuilds:
esbuild: true