Compare commits

...

98 Commits

Author SHA1 Message Date
github-actions[bot] 4bbcdbd556 publish new versions (#2542) 2025-03-16 21:51:48 -03:00
Lucas Fernandes Nogueira 12c4537b8e feat(updater): add support to riscv64 architecture (#2541)
Tauri now supports it, so the updater should check the riscv64 arch and replace the {{arch}} and {{target}} variables properly.

ref https://github.com/tauri-apps/tauri/pull/12602/
2025-03-17 02:42:37 +02:00
Amr Bashir a15eedf378 fix(http): avoid closing stream multiple times (#2535)
* fix(http): avoid closing stream multiple times

closes #2533

* use different signal than empty vec
2025-03-16 16:45:45 -03:00
github-actions[bot] 5347de8db9 publish new versions (#2534)
Co-authored-by: amrbashir <48618675+amrbashir@users.noreply.github.com>
2025-03-15 02:08:18 +02:00
Adriel Jansen Siahaya d3183aa99d fix(http): return response early in JS before waiting for chunks on the rust side (#2522) 2025-03-15 02:06:41 +02:00
github-actions[bot] 1e9e496b06 publish new versions (#2526)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
2025-03-12 22:29:34 +01:00
Knobibrot 1bb1ced538 feat(log): enable TargetKind::LogDir on mobile (#2524)
* enable log dir on iOS/Android

* Create change-pr-2524.md

---------

Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-03-12 22:27:40 +01:00
renovate[bot] 68eb74353b chore(deps): update dependency @rollup/plugin-node-resolve to v16.0.1 (#2520)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-12 16:34:42 +08:00
renovate[bot] 95e8f909b1 chore(deps): update dependency typescript-eslint to v8.26.1 (#2517)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 20:55:32 +01:00
renovate[bot] 8882dea3b9 chore(deps): update rust crate android_logger to 0.15 (#2502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 16:00:28 +01:00
github-actions[bot] b40a02c525 publish new versions (#2477)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
2025-03-10 14:17:57 +01:00
Fabian-Lars a1b3fa27f1 fix: Re-export api structs (#2515)
* fix: Re-export api structs

* whoops
2025-03-10 13:57:17 +01:00
Tony e54cfcb261 fix(updater): should be log::debug not println (#2514) 2025-03-10 02:59:19 +02:00
renovate[bot] 22ba197b80 chore(deps): update eslint monorepo to v9.22.0 (#2508)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-09 10:42:49 +08:00
renovate[bot] 77520a3587 chore(deps): update dependency rollup to v4.35.0 (#2511)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-09 10:33:37 +08:00
renovate[bot] dbc5fe120a chore(deps): update dependency eslint-config-prettier to v10.1.1 (#2503)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-09 10:33:15 +08:00
Guilherme Oenning faefcc9fd8 feat(updater): add configure_client to UpdaterBuilder (#2430) 2025-03-09 03:53:16 +02:00
Lucas Fernandes Nogueira ac60d589ec feat(updater): improve tracing and error logging (#2513) 2025-03-09 03:24:45 +02:00
Adriel Jansen Siahaya cb38f54f4a HTTP add stream support (#2479)
* feat: add stream support

* feat: add stream support

* Revert "feat: add stream support"

This reverts commit 5edea81680.

* feat: add stream support

* Discard changes to pnpm-lock.yaml

* Discard changes to plugins/http/package.json

* fix(stream): change IPC packet

* fix: update stream message guest-js

* fix: return early when aborted

* fix: use InvokeResponseBody as packet

* fix: remove serde_bytes

* fix: remove reqwest response

* fix: content conversion bug

* fix: remove ReqwestResponses along with its implementations

* formatting and update changelog

* build api-iife.js

---------

Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-03-08 15:45:05 -03:00
Clément Fouré d37bbdef8d fix(clipboard-manager): Wayland support (#2507)
* fix(clipboard-manager): Wayland support

* style(deps): merge dependencies declaration
2025-03-07 19:53:14 +01:00
Khoshbin Ali Ahmed 3a750c7300 docs(clipboard): Fixed typo from rbga to rgba (#2499) 2025-03-06 21:28:28 +01:00
renovate[bot] 5c9cf52e75 chore(deps): update dependency typescript-eslint to v8.26.0 (#2488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-04 10:26:23 +01:00
Tony 02df0421d6 chore(deps): update openssl in lock file (#2484) 2025-03-03 09:08:29 +01:00
Tony f7f5bbf259 Ignore vscode settings.json and tasks.json (#2482) 2025-03-03 04:39:43 +02:00
renovate[bot] 561cf39f64 chore(deps): update dependency prettier to v3.5.3 (#2480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 10:06:03 +08:00
Jim 024815018f feat(log): Add skip_logger setting (#2377) 2025-03-01 16:20:18 +01:00
FabianLars e90cd9fab1 ci: Use upstream create-pull-request version 2025-03-01 14:55:23 +01:00
FabianLars 1b86af8e26 chore(deps): Small lockfile update for deduplication purposes. 2025-03-01 14:27:53 +01:00
renovate[bot] 1dba334f00 chore(deps): update dependency rollup to v4.34.9 (#2474)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 12:31:09 +01:00
renovate[bot] 54e7864507 chore(deps): update dependency typescript to v5.8.2 (#2472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 19:03:25 +08:00
renovate[bot] 45c7848714 chore(deps): update dependency @tauri-apps/cli to v2.3.1 (#2470)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 11:56:59 +01:00
github-actions[bot] 393e956728 publish new versions (#2468)
Co-authored-by: FabianLars <FabianLars@users.noreply.github.com>
2025-02-27 23:57:29 +01:00
Fabian-Lars 1a9846599b chore(deps): Update objc2 to 0.6 (#2469) 2025-02-27 15:24:18 +01:00
renovate[bot] 71f95c9f05 chore(deps): update rust crate windows to 0.60 (v2) (#2445)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-02-27 11:45:55 +01:00
Tony 8b3cd9e827 chore(deps): update tauri to 2.3 (#2466) 2025-02-27 10:18:15 +01:00
Tony 39c022eea2 chore(deps): update js dependencies (#2467)
* chore(deps): update js dependencies

* Bump pnpm version in github actions
2025-02-27 05:24:53 +02:00
renovate[bot] 7527a3465a chore(deps): update dependency eslint-config-prettier to v10.0.2 (#2463)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 09:32:33 +08:00
renovate[bot] abca2ebc0d chore(deps): update tauri monorepo to v2.3.0 (v2) (#2465)
* chore(deps): update tauri monorepo to v2.3.0

* Regenerate window-state api-iife

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-02-27 09:19:05 +08:00
renovate[bot] ddcfad46f9 chore(deps): update dependency typescript-eslint to v8.25.0 (#2459)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 11:31:58 +08:00
github-actions[bot] fbd4d28920 publish new versions (#2404)
Co-authored-by: amrbashir <amrbashir@users.noreply.github.com>
2025-02-24 16:31:28 +01:00
Amr Bashir 0af367f7d4 refactor(single-instance): improve userdata ptr handling (#2453)
* fix(single-instance): fix null pointer dereference panic on Windows

* fmt
2025-02-24 17:10:30 +02:00
Fabian-Lars 1ab5f15763 fix(single-instance): check if pointer is null (#2452) 2025-02-23 19:25:00 +02:00
renovate[bot] c10d5bfb54 chore(deps): update rust crate infer to 0.19 (v2) (#2385)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-02-23 13:22:51 +01:00
renovate[bot] f6838d507f chore(deps): update dependency @types/eslint__js to v9 (v2) (#2447)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-02-22 23:12:38 +01:00
renovate[bot] 5b3a1aaf73 chore(deps): update rust crate color-backtrace to 0.7 (#2409)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 22:35:05 +01:00
renovate[bot] 3276d65801 chore(deps): update rust crate windows-registry to 0.5 (#2446)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 18:31:57 +01:00
renovate[bot] e0900f0451 chore(deps): update eslint monorepo to v9.21.0 (#2402)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 17:40:04 +01:00
renovate[bot] 4d38066ef9 chore(deps): update unocss monorepo to v66 (#2436)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 17:37:09 +01:00
renovate[bot] 3b37ce1508 chore(deps): update dependency prettier to v3.5.2 (#2444)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 23:37:35 +08:00
Fabian-Lars 6f881293fc fix(updater): Propagate request error (#2439) 2025-02-20 11:47:37 +01:00
renovate[bot] 9229f991fe chore(deps): update dependency typescript-eslint to v8.24.1 (#2435)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 09:20:26 +08:00
renovate[bot] b3bf74212e chore(deps): update dependency rollup to v4.34.8 (#2433)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 19:12:19 +08:00
Tony c5da9d2579 chore: update prettier to 3.5.1 and enable experimentalOperatorPosition (#2427) 2025-02-16 10:36:53 +08:00
Tony 7a5495963b Check for generated files on pnpm lock changed (#2429) 2025-02-15 22:47:28 +08:00
Tony 7d3869ced8 Regenerate api-iife (#2428) 2025-02-15 15:10:23 +02:00
renovate[bot] fa8c544479 chore(deps): update dependency typescript-eslint to v8.24.0 (#2412)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 10:35:37 +08:00
renovate[bot] 14660b6230 chore(deps): update dependency rollup to v4.34.7 (#2420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 10:20:15 +08:00
Seishiin 6b4c391738 fix(log): timezone_strategy overwriting LogLevels (#2401) 2025-02-08 09:41:34 +08:00
renovate[bot] d5d9f7c970 chore(deps): update dependency rollup to v4.34.6 (v2) (#2399)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-07 18:43:37 +01:00
renovate[bot] 63f9449c9a chore(deps): update dependency rollup to v4.34.4 (v2) (#2396)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-06 11:33:40 +01:00
renovate[bot] e0cff9bcf4 chore(deps): update dependency rollup to v4.34.2 (v2) (#2394)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 19:58:00 +01:00
Dominic Moser 5411db68a8 docs(haptics): Include permissions in readme (#2395)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-02-04 19:50:50 +01:00
github-actions[bot] 80804adc68 publish new versions (#2380)
Co-authored-by: Legend-Master <Legend-Master@users.noreply.github.com>
2025-02-04 07:27:53 -03:00
renovate[bot] 93edbd1434 chore(deps): update dependency typescript-eslint to v8.23.0 (#2390)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 08:45:25 +08:00
dependabot[bot] 3ebec74983 chore(deps): bump openssl from 0.10.66 to 0.10.70 in /plugins/updater/tests/updater-migration/v1-app (#2392)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-03 21:31:13 +01:00
renovate[bot] 51f5aff58a chore(deps): update dependency rollup to v4.34.1 (#2388)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 21:35:03 +08:00
jLynx 5369898db7 fix(macOS): Tauri V2 Update Permission Denied Error (#2067)
* WIP

* Fixed linting

* WIP

* Fixed linting

* use osakit to show actual app name on dialog

* sync versions

* lint

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-02-02 17:54:09 -03:00
renovate[bot] a7497b0aeb chore(deps): update rust crate gethostname to v1 (v2) (#2382)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-01 11:47:53 +01:00
renovate[bot] aaa88fa45f chore(deps): update dependency rollup to v4.34.0 (v2) (#2381)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-01 11:47:09 +01:00
Fabian-Lars ca7395a5ce fix(upload): Enable reqwest's rustls-tls feature by default (#2378) 2025-02-01 11:17:22 +01:00
renovate[bot] 6ae853c2e6 chore(deps): update dependency rollup to v4.32.1 (v2) (#2373)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-28 13:42:36 +01:00
renovate[bot] d5fb5337a0 chore(deps): update rust crate dirs to v6 (v2) (#2311)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-01-27 22:16:22 +01:00
renovate[bot] 512a188c7a chore(deps): update dependency typescript-eslint to v8.22.0 (v2) (#2363)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 20:09:10 +01:00
github-actions[bot] ad17266273 Publish New Versions (v2) (#2348)
Co-authored-by: FabianLars <FabianLars@users.noreply.github.com>
2025-01-27 19:34:40 +01:00
Twilight 78acfa456f feat(websocket): add ability to remove listener (#2027) 2025-01-27 15:18:24 +01:00
renovate[bot] 0b3b3a22d2 chore(deps): update dependency @tauri-apps/cli to v2.2.7 (v2) (#2359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 22:14:17 +01:00
renovate[bot] 384b598fcf chore(deps): update dependency eslint to v9.19.0 (v2) (#2356)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-24 22:25:43 +01:00
renovate[bot] b2fe305a84 chore(deps): update dependency @eslint/js to v9.19.0 (v2) (#2352)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-24 19:59:26 +01:00
Joban 68d8f3be38 docs(process): fix typo in Default Permission section (#2351) 2025-01-24 19:57:07 +01:00
renovate[bot] 9ac2aa88e6 chore(deps): update dependency rollup to v4.32.0 (v2) (#2349)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-24 16:49:22 +01:00
Fabian-Lars 18700f1e82 docs(log): Update mobile support matrix
fixes https://github.com/tauri-apps/tauri-docs/issues/2840
2025-01-24 16:47:10 +01:00
Fabian-Lars 0afc9b6be0 feat(updater): Collect unknown response fields (#2325) 2025-01-23 02:54:02 +02:00
renovate[bot] bda803fbdd chore(deps): update dependency vite to v6.0.9 [security] (#2346)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 09:59:59 +08:00
DiamondDrake 105136494c feat(http): add dangerous settings / disable ssl verification - issue #518 (#2204) 2025-01-22 02:23:37 +02:00
Fabian-Lars ce11079f19 fix(clipboard-manager): Drop on exit (#2280)
* fix(clipboard-manager): Drop on exit

ref https://docs.rs/arboard/latest/arboard/struct.Clipboard.html
ref https://github.com/tauri-apps/plugins-workspace/issues/2267

* more specific
2025-01-22 02:15:01 +02:00
Mads Marquart 784a54a390 [log] Use objc2 (#2344) 2025-01-21 22:57:35 +02:00
Mads Marquart bdb7febf9c [opener] Prepare for objc2 frameworks v0.3 (#2343)
These will have a bunch of default features enabled, so let's
preemptively disable them.
2025-01-21 22:26:26 +02:00
renovate[bot] e76272b619 chore(deps): update dependency typescript-eslint to v8.21.0 (v2) (#2337)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 22:28:43 +01:00
Anthony Oleinik cf0dff049b [log] add mention of cability to log plugin README.md (#2339) 2025-01-20 21:56:13 +01:00
github-actions[bot] 28f5c33d65 Publish New Versions (v2) (#2324)
Co-authored-by: FabianLars <FabianLars@users.noreply.github.com>
2025-01-19 19:28:26 +01:00
Didrik Nordström 5b82118158 fix(opener): invoke the mobile plugin in the commands too (#2234) (#2332)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-19 18:47:14 +01:00
renovate[bot] 125ec1dbcf chore(deps): update dependency rollup to v4.31.0 (v2) (#2333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-19 15:51:03 +01:00
renovate[bot] c545fcf896 chore(deps): update dependency @tauri-apps/cli to v2.2.5 (#2331)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 20:38:04 +08:00
thewh1teagle 0ec895c378 feat(window-state): add filter callback for excluding windows from tracking (#2330)
* feat(window-state): Add glob pattern support to denylist for flexible window management

* changefile

fix(window-state): Remove unnecessary reference in denylist pattern processing

chore(changes): Update window-state and window-state-js versioning for glob pattern support

docs(window-state): Update glob pattern denylist documentation for clarity

* refactor(window-state): Simplify denylist handling and error management in with_denylist method

* feat: add with_denylist_glob

* feat(window-state): Add filter callback for excluding windows from tracking

* refactor(window-state): Remove unused Glob error variant from Error enum

* refactor(window-state): Change filter_callback from Arc<Mutex<FilterCallbackFn>> to Box<FilterCallbackFn>

* refactor(window-state): Remove glob dependency from Cargo.toml and Cargo.lock

* feat(window-state): Introduce filter callback for dynamic window exclusion

* Update .changes/window-state-filter-callback.md
2025-01-18 09:31:20 +08:00
Fabian-Lars 406e6f484c fix(geo/haptics): Fix serde import error with specta feature disabled (#2323) 2025-01-15 13:28:40 +01:00
Fabian-Lars b63d724e85 chore(opener): Fix Cargo.toml property name for tauri-docs generator 2025-01-14 11:57:14 +01:00
renovate[bot] c73b773724 chore(deps): update dependency typescript-eslint to v8.20.0 (#2320)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 12:46:26 +08:00
renovate[bot] d467313d0c chore(deps): update dependency eslint-config-prettier to v10 (v2) (#2318)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 17:01:20 +01:00
120 changed files with 2370 additions and 1927 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
with:
version: 9.x.x
version: 10.x.x
run_install: true
- name: audit
run: pnpm audit
+25 -1
View File
@@ -8,6 +8,7 @@ on:
pull_request:
paths:
- '.github/workflows/check-generated-files.yml'
- pnpm-lock.yaml
- '**/guest-js/**'
concurrency:
@@ -27,94 +28,117 @@ jobs:
filters: |
autostart:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/autostart/guest-js/**
- plugins/autostart/src/api-iife.js
cli:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/cli/guest-js/**
- plugins/cli/src/api-iife.js
clipboard-manager:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/clipboard-manager/guest-js/**
- plugins/clipboard-manager/src/api-iife.js
dialog:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/dialog/guest-js/**
- plugins/dialog/src/api-iife.js
fs:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/fs/guest-js/**
- plugins/fs/src/api-iife.js
geolocation:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/geolocation/guest-js/**
- plugins/geolocation/src/api-iife.js
global-shortcut:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/global-shortcut/guest-js/**
- plugins/global-shortcut/src/api-iife.js
opener:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/opener/guest-js/**
- plugins/opener/src/api-iife.js
haptics:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/haptics/guest-js/**
- plugins/haptics/src/api-iife.js
http:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/http/guest-js/**
- plugins/http/src/api-iife.js
log:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/log/guest-js/**
- plugins/log/src/api-iife.js
notification:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/notification/guest-js/**
- plugins/notification/src/api-iife.js
os:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/os/guest-js/**
- plugins/os/src/api-iife.js
positioner:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/positioner/guest-js/**
- plugins/positioner/src/api-iife.js
process:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/process/guest-js/**
- plugins/process/src/api-iife.js
shell:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/shell/guest-js/**
- plugins/shell/src/api-iife.js
sql:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/sql/guest-js/**
- plugins/sql/src/api-iife.js
store:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/store/guest-js/**
- plugins/store/src/api-iife.js
stronghold:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/stronghold/guest-js/**
- plugins/stronghold/src/api-iife.js
updater:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/updater/guest-js/**
- plugins/updater/src/api-iife.js
upload:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/upload/guest-js/**
- plugins/upload/src/api-iife.js
websocket:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/websocket/guest-js/**
- plugins/websocket/src/api-iife.js
window-state:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/window-state/guest-js/**
- plugins/window-state/src/api-iife.js
@@ -143,7 +167,7 @@ jobs:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
with:
version: 9.x.x
version: 10.x.x
run_install: true
- name: build api
@@ -39,7 +39,7 @@ jobs:
- uses: pnpm/action-setup@v4
with:
version: 9.x.x
version: 10.x.x
run_install: true
- name: cargo login
@@ -75,7 +75,7 @@ jobs:
- name: Create Pull Request With Versions Bumped
id: cpr
uses: tauri-apps/create-pull-request@v3
uses: peter-evans/create-pull-request@dd2324fc52d5d43c699a5636bcf19fceaa70c284 # 7.0.7
if: steps.covector.outputs.commandRan == 'version'
with:
title: 'Publish New Versions (${{ github.ref_name }})'
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
with:
version: 9.x.x
version: 10.x.x
run_install: true
- run: pnpm format:check
+1 -1
View File
@@ -49,7 +49,7 @@ jobs:
node-version: 'lts/*'
- uses: pnpm/action-setup@v4
with:
version: 9.x.x
version: 10.x.x
run_install: true
- name: eslint
run: pnpm lint
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
- uses: pnpm/action-setup@v4
with:
version: 9.x.x
version: 10.x.x
run_install: true
- name: Build packages
+2
View File
@@ -14,6 +14,8 @@ target/
# .vscode workspace settings file
.vscode/settings.json
.vscode/launch.json
.vscode/tasks.json
# npm, yarn and bun lock files
package-lock.json
+2 -1
View File
@@ -1,5 +1,6 @@
{
"singleQuote": true,
"semi": false,
"trailingComma": "none"
"trailingComma": "none",
"experimentalOperatorPosition": "start"
}
+6 -6
View File
@@ -32,8 +32,8 @@ const ignore = [
async function checkFile(file) {
if (
extensions.some((e) => file.endsWith(e)) &&
!ignore.some((i) => file.includes(`${path.sep}${i}`))
extensions.some((e) => file.endsWith(e))
&& !ignore.some((i) => file.includes(`${path.sep}${i}`))
) {
const fileStream = fs.createReadStream(file)
const rl = readline.createInterface({
@@ -46,10 +46,10 @@ async function checkFile(file) {
for await (let line of rl) {
// ignore empty lines, allow shebang, swift-tools-version and bundler license
if (
line.length === 0 ||
line.startsWith('#!') ||
line.startsWith('// swift-tools-version:') ||
ignoredLicenses.includes(line)
line.length === 0
|| line.startsWith('#!')
|| line.startsWith('// swift-tools-version:')
|| ignoredLicenses.includes(line)
) {
continue
}
+2 -1
View File
@@ -1 +1,2 @@
plugins/*/permissions/autogenerated/
plugins/*/permissions/autogenerated/
plugins/*/android/.tauri/tauri-api/build/
Generated
+513 -392
View File
File diff suppressed because it is too large Load Diff
+65
View File
@@ -1,5 +1,70 @@
# Changelog
## \[2.0.19]
### Dependencies
- Upgraded to `http-js@2.4.2`
- Upgraded to `updater-js@2.6.1`
## \[2.0.18]
### Dependencies
- Upgraded to `http-js@2.4.1`
## \[2.0.17]
### Dependencies
- Upgraded to `log-js@2.3.1`
## \[2.0.16]
### Dependencies
- Upgraded to `clipboard-manager-js@2.2.2`
- Upgraded to `notification-js@2.2.2`
- Upgraded to `os-js@2.2.1`
- Upgraded to `http-js@2.4.0`
- Upgraded to `log-js@2.3.0`
- Upgraded to `updater-js@2.6.0`
## \[2.0.15]
### Dependencies
- Upgraded to `log-js@2.2.3`
- Upgraded to `opener-js@2.2.6`
## \[2.0.14]
### Dependencies
- Upgraded to `log-js@2.2.2`
- Upgraded to `updater-js@2.5.1`
## \[2.0.13]
### Dependencies
- Upgraded to `updater-js@2.5.0`
## \[2.0.12]
### Dependencies
- Upgraded to `clipboard-manager-js@2.2.1`
- Upgraded to `http-js@2.3.0`
- Upgraded to `log-js@2.2.1`
- Upgraded to `updater-js@2.4.0`
## \[2.0.11]
### Dependencies
- Upgraded to `opener-js@2.2.5`
## \[2.0.10]
### Dependencies
+16 -16
View File
@@ -1,7 +1,7 @@
{
"name": "api",
"private": true,
"version": "2.0.10",
"version": "2.0.19",
"type": "module",
"scripts": {
"dev": "vite --clearScreen false",
@@ -10,35 +10,35 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.2.0",
"@tauri-apps/api": "2.3.0",
"@tauri-apps/plugin-barcode-scanner": "^2.2.0",
"@tauri-apps/plugin-biometric": "^2.2.0",
"@tauri-apps/plugin-cli": "^2.2.0",
"@tauri-apps/plugin-clipboard-manager": "^2.2.0",
"@tauri-apps/plugin-clipboard-manager": "^2.2.2",
"@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-geolocation": "^2.2.0",
"@tauri-apps/plugin-global-shortcut": "^2.2.0",
"@tauri-apps/plugin-opener": "^2.2.4",
"@tauri-apps/plugin-haptics": "^2.2.0",
"@tauri-apps/plugin-http": "^2.2.0",
"@tauri-apps/plugin-http": "^2.4.2",
"@tauri-apps/plugin-nfc": "^2.2.0",
"@tauri-apps/plugin-notification": "^2.2.1",
"@tauri-apps/plugin-os": "^2.2.0",
"@tauri-apps/plugin-notification": "^2.2.2",
"@tauri-apps/plugin-opener": "^2.2.6",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-process": "^2.2.0",
"@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-store": "^2.2.0",
"@tauri-apps/plugin-updater": "^2.2.0",
"@tauri-apps/plugin-updater": "^2.6.1",
"@zerodevx/svelte-json-view": "1.0.11"
},
"devDependencies": {
"@iconify-json/codicon": "^1.2.6",
"@iconify-json/ph": "^1.2.1",
"@sveltejs/vite-plugin-svelte": "^5.0.1",
"@tauri-apps/cli": "2.2.4",
"@unocss/extractor-svelte": "^65.0.0",
"svelte": "^5.10.0",
"unocss": "^65.0.0",
"vite": "^6.0.3"
"@iconify-json/codicon": "^1.2.12",
"@iconify-json/ph": "^1.2.2",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tauri-apps/cli": "2.3.1",
"@unocss/extractor-svelte": "^66.0.0",
"svelte": "^5.20.4",
"unocss": "^66.0.0",
"vite": "^6.2.0"
}
}
+69
View File
@@ -1,5 +1,74 @@
# Changelog
## \[2.0.23]
### Dependencies
- Upgraded to `http@2.4.2`
- Upgraded to `updater@2.6.1`
## \[2.0.22]
### Dependencies
- Upgraded to `http@2.4.1`
## \[2.0.21]
### Dependencies
- Upgraded to `log@2.3.1`
## \[2.0.20]
### Dependencies
- Upgraded to `clipboard-manager@2.2.2`
- Upgraded to `geolocation@2.2.4`
- Upgraded to `haptics@2.2.4`
- Upgraded to `notification@2.2.2`
- Upgraded to `os@2.2.1`
- Upgraded to `http@2.4.0`
- Upgraded to `log@2.3.0`
- Upgraded to `updater@2.6.0`
## \[2.0.19]
### Dependencies
- Upgraded to `log@2.2.3`
- Upgraded to `opener@2.2.6`
## \[2.0.18]
### Dependencies
- Upgraded to `log@2.2.2`
- Upgraded to `updater@2.5.1`
## \[2.0.17]
### Dependencies
- Upgraded to `updater@2.5.0`
## \[2.0.16]
### Dependencies
- Upgraded to `clipboard-manager@2.2.1`
- Upgraded to `http@2.3.0`
- Upgraded to `log@2.2.1`
- Upgraded to `updater@2.4.0`
## \[2.0.15]
### Dependencies
- Upgraded to `haptics@2.2.3`
- Upgraded to `geolocation@2.2.3`
- Upgraded to `opener@2.2.5`
## \[2.0.14]
### Dependencies
+10 -10
View File
@@ -1,7 +1,7 @@
[package]
name = "api"
publish = false
version = "2.0.14"
version = "2.0.23"
description = "An example Tauri Application showcasing the api"
edition = "2021"
rust-version = { workspace = true }
@@ -19,21 +19,21 @@ serde_json = { workspace = true }
serde = { workspace = true }
tiny_http = "0.12"
log = { workspace = true }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.2.0" }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.3.1" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.2.0", features = [
"watch",
] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.2.0" }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.2.2" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.2.0" }
tauri-plugin-http = { path = "../../../plugins/http", features = [
"multipart",
], version = "2.2.0" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.2.1", features = [
], version = "2.4.2" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.2.2", features = [
"windows7-compat",
] }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.2.0" }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.2.1" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.2.0" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.2.4" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.2.6" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.2.0" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.2.0" }
@@ -53,15 +53,15 @@ features = [
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.2.0" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.2.0" }
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.3.1" }
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.6.1" }
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" }
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.2.0" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.2.0" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.2.0" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.2.2" }
tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.2.2" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.2.4" }
tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.2.4" }
[features]
prod = ["tauri/custom-protocol"]
+18 -15
View File
@@ -11,26 +11,29 @@
"example:api:dev": "pnpm run --filter \"api\" tauri dev"
},
"devDependencies": {
"@eslint/js": "9.18.0",
"@rollup/plugin-node-resolve": "16.0.0",
"@eslint/js": "9.22.0",
"@rollup/plugin-node-resolve": "16.0.1",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "11.1.6",
"@types/eslint__js": "8.42.3",
"covector": "^0.12.3",
"eslint": "9.18.0",
"eslint-config-prettier": "9.1.0",
"@rollup/plugin-typescript": "12.1.2",
"covector": "^0.12.4",
"eslint": "9.22.0",
"eslint-config-prettier": "10.1.1",
"eslint-plugin-security": "3.0.1",
"prettier": "3.4.2",
"rollup": "4.30.1",
"prettier": "3.5.3",
"rollup": "4.35.0",
"tslib": "2.8.1",
"typescript": "5.7.3",
"typescript-eslint": "8.19.1"
"typescript": "5.8.2",
"typescript-eslint": "8.26.1"
},
"resolutions": {
"semver": ">=7.5.2",
"optionator": ">=0.9.3"
"pnpm": {
"overrides": {
"esbuild@<0.25.0": ">=0.25.0"
},
"onlyBuiltDependencies": [
"esbuild"
]
},
"engines": {
"pnpm": "^9.0.0"
"pnpm": "^10.0.0"
}
}
+10
View File
@@ -1,5 +1,15 @@
# Changelog
## \[2.2.2]
### bug
- [`d37bbdef`](https://github.com/tauri-apps/plugins-workspace/commit/d37bbdef8dc70e61e59f9fe0bb8b2a48999d0aa1) ([#2507](https://github.com/tauri-apps/plugins-workspace/pull/2507) by [@SquitchYT](https://github.com/tauri-apps/plugins-workspace/../../SquitchYT)) Fix clipboard-manager Wayland support.
## \[2.2.1]
- [`ce11079f`](https://github.com/tauri-apps/plugins-workspace/commit/ce11079f19852fbefdecf0e4c7d947af3624fee0) ([#2280](https://github.com/tauri-apps/plugins-workspace/pull/2280) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Explicitly drop `arboard::Clipboard` on exit. Add recommendation to not use read methods on the mainthread.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-clipboard-manager"
version = "2.2.0"
version = "2.2.2"
description = "Read and write to the system clipboard."
edition = { workspace = true }
authors = { workspace = true }
@@ -36,4 +36,4 @@ thiserror = { workspace = true }
tauri = { workspace = true, features = ["wry"] }
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
arboard = "3"
arboard = { version = "3", features = ["wayland-data-control"] }
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var t;async function r(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;class n{get rid(){return function(e,t,r,n){if("a"===r&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}(this,t,"f")}constructor(e){t.set(this,void 0),function(e,t,r){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");t.set(e,r)}(this,t,e)}async close(){return r("plugin:resources|close",{rid:this.rid})}}t=new WeakMap;class a extends n{constructor(e){super(e)}static async new(e,t,n){return r("plugin:image|new",{rgba:i(e),width:t,height:n}).then((e=>new a(e)))}static async fromBytes(e){return r("plugin:image|from_bytes",{bytes:i(e)}).then((e=>new a(e)))}static async fromPath(e){return r("plugin:image|from_path",{path:e}).then((e=>new a(e)))}async rgba(){return r("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return r("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await r("plugin:clipboard-manager|clear")},e.readImage=async function(){return await r("plugin:clipboard-manager|read_image").then((e=>new a(e)))},e.readText=async function(){return await r("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,t){await r("plugin:clipboard-manager|write_html",{html:e,altText:t})},e.writeImage=async function(e){await r("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,t){await r("plugin:clipboard-manager|write_text",{label:t?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var n;async function t(e,n={},t){return window.__TAURI_INTERNALS__.invoke(e,n,t)}"function"==typeof SuppressedError&&SuppressedError;class r{get rid(){return function(e,n,t,r){if("function"==typeof n||!n.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?r:"a"===t?r.call(e):r?r.value:n.get(e)}(this,n,"f")}constructor(e){n.set(this,void 0),function(e,n,t){if("function"==typeof n||!n.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");n.set(e,t)}(this,n,e)}async close(){return t("plugin:resources|close",{rid:this.rid})}}n=new WeakMap;class a extends r{constructor(e){super(e)}static async new(e,n,r){return t("plugin:image|new",{rgba:i(e),width:n,height:r}).then((e=>new a(e)))}static async fromBytes(e){return t("plugin:image|from_bytes",{bytes:i(e)}).then((e=>new a(e)))}static async fromPath(e){return t("plugin:image|from_path",{path:e}).then((e=>new a(e)))}async rgba(){return t("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return t("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await t("plugin:clipboard-manager|clear")},e.readImage=async function(){return await t("plugin:clipboard-manager|read_image").then((e=>new a(e)))},e.readText=async function(){return await t("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,n){await t("plugin:clipboard-manager|write_html",{html:e,altText:n})},e.writeImage=async function(e){await t("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,n){await t("plugin:clipboard-manager|write_text",{label:n?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}
+1 -1
View File
@@ -91,7 +91,7 @@ async function writeImage(
* import { readImage } from '@tauri-apps/plugin-clipboard-manager';
*
* const clipboardImage = await readImage();
* const blob = new Blob([await clipboardImage.rbga()], { type: 'image' })
* const blob = new Blob([await clipboardImage.rgba()], { type: 'image' })
* const url = URL.createObjectURL(blob)
* ```
* @since 2.0.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-clipboard-manager",
"version": "2.2.0",
"version": "2.2.2",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+32 -6
View File
@@ -14,7 +14,7 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
) -> crate::Result<Clipboard<R>> {
Ok(Clipboard {
app: app.clone(),
clipboard: arboard::Clipboard::new().map(Mutex::new),
clipboard: arboard::Clipboard::new().map(|c| Mutex::new(Some(c))),
})
}
@@ -22,13 +22,21 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
pub struct Clipboard<R: Runtime> {
#[allow(dead_code)]
app: AppHandle<R>,
clipboard: Result<Mutex<arboard::Clipboard>, arboard::Error>,
// According to arboard docs the clipboard must be dropped before exit.
// Since tauri doesn't call drop on exit we'll use an Option to take() on RunEvent::Exit.
clipboard: Result<Mutex<Option<arboard::Clipboard>>, arboard::Error>,
}
impl<R: Runtime> Clipboard<R> {
pub fn write_text<'a, T: Into<Cow<'a, str>>>(&self, text: T) -> crate::Result<()> {
match &self.clipboard {
Ok(clipboard) => clipboard.lock().unwrap().set_text(text).map_err(Into::into),
Ok(clipboard) => clipboard
.lock()
.unwrap()
.as_mut()
.unwrap()
.set_text(text)
.map_err(Into::into),
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}
@@ -38,6 +46,8 @@ impl<R: Runtime> Clipboard<R> {
Ok(clipboard) => clipboard
.lock()
.unwrap()
.as_mut()
.unwrap()
.set_image(ImageData {
bytes: Cow::Borrowed(image.rgba()),
width: image.width() as usize,
@@ -48,10 +58,11 @@ impl<R: Runtime> Clipboard<R> {
}
}
/// Warning: This method should not be used on the main thread! Otherwise the underlying libraries may deadlock on Linux, freezing the whole app, when trying to copy data copied from this app, for example if the user copies text from the WebView.
pub fn read_text(&self) -> crate::Result<String> {
match &self.clipboard {
Ok(clipboard) => {
let text = clipboard.lock().unwrap().get_text()?;
let text = clipboard.lock().unwrap().as_mut().unwrap().get_text()?;
Ok(text)
}
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
@@ -67,6 +78,8 @@ impl<R: Runtime> Clipboard<R> {
Ok(clipboard) => clipboard
.lock()
.unwrap()
.as_mut()
.unwrap()
.set_html(html, alt_text)
.map_err(Into::into),
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
@@ -75,15 +88,22 @@ impl<R: Runtime> Clipboard<R> {
pub fn clear(&self) -> crate::Result<()> {
match &self.clipboard {
Ok(clipboard) => clipboard.lock().unwrap().clear().map_err(Into::into),
Ok(clipboard) => clipboard
.lock()
.unwrap()
.as_mut()
.unwrap()
.clear()
.map_err(Into::into),
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}
/// Warning: This method should not be used on the main thread! Otherwise the underlying libraries may deadlock on Linux, freezing the whole app, when trying to copy data copied from this app, for example if the user copies text from the WebView.
pub fn read_image(&self) -> crate::Result<Image<'_>> {
match &self.clipboard {
Ok(clipboard) => {
let image = clipboard.lock().unwrap().get_image()?;
let image = clipboard.lock().unwrap().as_mut().unwrap().get_image()?;
let image = Image::new_owned(
image.bytes.to_vec(),
image.width as u32,
@@ -94,4 +114,10 @@ impl<R: Runtime> Clipboard<R> {
Err(e) => Err(crate::Error::Clipboard(e.to_string())),
}
}
pub(crate) fn cleanup(&self) {
if let Ok(clipboard) = &self.clipboard {
clipboard.lock().unwrap().take();
}
}
}
+7 -1
View File
@@ -11,7 +11,7 @@
use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
Manager, RunEvent, Runtime,
};
#[cfg(desktop)]
@@ -59,5 +59,11 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
app.manage(clipboard);
Ok(())
})
.on_event(|_app, _event| {
#[cfg(desktop)]
if let RunEvent::Exit = _event {
_app.clipboard().cleanup();
}
})
.build()
}
+1 -1
View File
@@ -38,7 +38,7 @@ url = { workspace = true }
[target."cfg(windows)".dependencies]
dunce = "1"
windows-registry = "0.4"
windows-registry = "0.5"
windows-result = "0.3"
[target."cfg(target_os = \"linux\")".dependencies]
+4 -4
View File
@@ -10,12 +10,12 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.2.0",
"@tauri-apps/api": "2.3.0",
"@tauri-apps/plugin-deep-link": "2.2.0"
},
"devDependencies": {
"@tauri-apps/cli": "2.2.4",
"typescript": "^5.2.2",
"vite": "^6.0.0"
"@tauri-apps/cli": "2.3.1",
"typescript": "^5.7.3",
"vite": "^6.2.0"
}
}
+3 -6
View File
@@ -265,18 +265,15 @@ mod imp {
.to_string();
let key_reg = CURRENT_USER.create(&key_base)?;
key_reg.set_string(
"",
&format!("URL:{} protocol", self.app.config().identifier),
)?;
key_reg.set_string("", format!("URL:{} protocol", self.app.config().identifier))?;
key_reg.set_string("URL Protocol", "")?;
let icon_reg = CURRENT_USER.create(format!("{key_base}\\DefaultIcon"))?;
icon_reg.set_string("", &format!("{exe},0"))?;
icon_reg.set_string("", format!("{exe},0"))?;
let cmd_reg = CURRENT_USER.create(format!("{key_base}\\shell\\open\\command"))?;
cmd_reg.set_string("", &format!("\"{exe}\" \"%1\""))?;
cmd_reg.set_string("", format!("\"{exe}\" \"%1\""))?;
Ok(())
}
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -604,8 +604,8 @@ async function copyFile(
options?: CopyFileOptions
): Promise<void> {
if (
(fromPath instanceof URL && fromPath.protocol !== 'file:') ||
(toPath instanceof URL && toPath.protocol !== 'file:')
(fromPath instanceof URL && fromPath.protocol !== 'file:')
|| (toPath instanceof URL && toPath.protocol !== 'file:')
) {
throw new TypeError('Must be a file URL.')
}
@@ -919,8 +919,8 @@ async function rename(
options?: RenameOptions
): Promise<void> {
if (
(oldPath instanceof URL && oldPath.protocol !== 'file:') ||
(newPath instanceof URL && newPath.protocol !== 'file:')
(oldPath instanceof URL && oldPath.protocol !== 'file:')
|| (newPath instanceof URL && newPath.protocol !== 'file:')
) {
throw new TypeError('Must be a file URL.')
}
+2 -8
View File
@@ -53,10 +53,7 @@ impl FilePath {
#[inline]
pub fn into_path(self) -> Result<PathBuf> {
match self {
Self::Url(url) => url
.to_file_path()
.map(PathBuf::from)
.map_err(|_| Error::InvalidPathUrl),
Self::Url(url) => url.to_file_path().map_err(|_| Error::InvalidPathUrl),
Self::Path(p) => Ok(p),
}
}
@@ -91,10 +88,7 @@ impl SafeFilePath {
#[inline]
pub fn into_path(self) -> Result<PathBuf> {
match self {
Self::Url(url) => url
.to_file_path()
.map(PathBuf::from)
.map_err(|_| Error::InvalidPathUrl),
Self::Url(url) => url.to_file_path().map_err(|_| Error::InvalidPathUrl),
Self::Path(p) => Ok(p.as_ref().to_owned()),
}
}
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.2.4]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
## \[2.2.3]
- [`406e6f48`](https://github.com/tauri-apps/plugins-workspace/commit/406e6f484cdc13d35c50fb949f7489ca9eeccc44) ([#2323](https://github.com/tauri-apps/plugins-workspace/pull/2323) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused build failures when the `haptics` or `geolocation` plugin was used without their `specta` feature flag enabled.
## \[2.2.2]
- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) **Breaking change:** `specta` integration is now behind a `specta` feature flag like in Tauri.
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "tauri-plugin-geolocation"
description = "Get and track the device's current position"
version = "2.2.2"
version = "2.2.4"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
+2 -2
View File
@@ -118,8 +118,8 @@ import {
let permissions = await checkPermissions()
if (
permissions.location === 'prompt' ||
permissions.location === 'prompt-with-rationale'
permissions.location === 'prompt'
|| permissions.location === 'prompt-with-rationale'
) {
permissions = await requestPermissions(['location'])
}
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_GEOLOCATION__=function(t){"use strict";function n(t,n,e,i){if("a"===e&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof n?t!==n||!i:!n.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===e?i:"a"===e?i.call(t):i?i.value:n.get(t)}function e(t,n,e,i,s){if("function"==typeof n||!n.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return n.set(t,e),e}var i,s,o;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),s.set(this,0),o.set(this,[]),this.id=function(t,n=!1){return window.__TAURI_INTERNALS__.transformCallback(t,n)}((({message:t,id:r})=>{if(r==n(this,s,"f"))for(n(this,i,"f").call(this,t),e(this,s,n(this,s,"f")+1);n(this,s,"f")in n(this,o,"f");){const t=n(this,o,"f")[n(this,s,"f")];n(this,i,"f").call(this,t),delete n(this,o,"f")[n(this,s,"f")],e(this,s,n(this,s,"f")+1)}else n(this,o,"f")[r]=t}))}set onmessage(t){e(this,i,t)}get onmessage(){return n(this,i,"f")}[(i=new WeakMap,s=new WeakMap,o=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}async function c(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}return t.checkPermissions=async function(){return await async function(t){return c(`plugin:${t}|check_permissions`)}("geolocation")},t.clearWatch=async function(t){await c("plugin:geolocation|clear_watch",{channelId:t})},t.getCurrentPosition=async function(t){return await c("plugin:geolocation|get_current_position",{options:t})},t.requestPermissions=async function(t){return await c("plugin:geolocation|request_permissions",{permissions:t})},t.watchPosition=async function(t,n){const e=new a;return e.onmessage=t=>{"string"==typeof t?n(null,t):n(t)},await c("plugin:geolocation|watch_position",{options:t,channel:e}),e.id},t}({});Object.defineProperty(window.__TAURI__,"geolocation",{value:__TAURI_PLUGIN_GEOLOCATION__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_GEOLOCATION__=function(t){"use strict";function n(t,n,e,i){if("function"==typeof n||!n.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===e?i:"a"===e?i.call(t):i?i.value:n.get(t)}function e(t,n,e,i,s){if("function"==typeof n||!n.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return n.set(t,e),e}var i,s,o;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),s.set(this,0),o.set(this,[]),this.id=function(t,n=!1){return window.__TAURI_INTERNALS__.transformCallback(t,n)}((({message:t,id:r})=>{if(r==n(this,s,"f"))for(n(this,i,"f").call(this,t),e(this,s,n(this,s,"f")+1);n(this,s,"f")in n(this,o,"f");){const t=n(this,o,"f")[n(this,s,"f")];n(this,i,"f").call(this,t),delete n(this,o,"f")[n(this,s,"f")],e(this,s,n(this,s,"f")+1)}else n(this,o,"f")[r]=t}))}set onmessage(t){e(this,i,t)}get onmessage(){return n(this,i,"f")}[(i=new WeakMap,s=new WeakMap,o=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}async function c(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}return t.checkPermissions=async function(){return await async function(t){return c(`plugin:${t}|check_permissions`)}("geolocation")},t.clearWatch=async function(t){await c("plugin:geolocation|clear_watch",{channelId:t})},t.getCurrentPosition=async function(t){return await c("plugin:geolocation|get_current_position",{options:t})},t.requestPermissions=async function(t){return await c("plugin:geolocation|request_permissions",{permissions:t})},t.watchPosition=async function(t,n){const e=new a;return e.onmessage=t=>{"string"==typeof t?n(null,t):n(t)},await c("plugin:geolocation|watch_position",{options:t,channel:e}),e.id},t}({});Object.defineProperty(window.__TAURI__,"geolocation",{value:__TAURI_PLUGIN_GEOLOCATION__})}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-geolocation",
"version": "2.2.2",
"version": "2.2.4",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+1 -1
View File
@@ -14,7 +14,7 @@ pub enum Error {
#[cfg(mobile)]
#[error(transparent)]
PluginInvoke(
#[serde(skip)]
#[cfg_attr(feature = "specta", serde(skip))]
#[from]
tauri::plugin::mobile::PluginInvokeError,
),
+2 -2
View File
@@ -21,9 +21,9 @@ mod models;
pub use error::{Error, Result};
#[cfg(desktop)]
use desktop::Geolocation;
pub use desktop::Geolocation;
#[cfg(mobile)]
use mobile::Geolocation;
pub use mobile::Geolocation;
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the geolocation APIs.
pub trait GeolocationExt<R: Runtime> {
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBAL_SHORTCUT__=function(t){"use strict";function e(t,e,r,s){if("a"===r&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function r(t,e,r,s,i){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,r),r}var s,i,n;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,s.set(this,(()=>{})),i.set(this,0),n.set(this,[]),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:o})=>{if(o==e(this,i,"f"))for(e(this,s,"f").call(this,t),r(this,i,e(this,i,"f")+1);e(this,i,"f")in e(this,n,"f");){const t=e(this,n,"f")[e(this,i,"f")];e(this,s,"f").call(this,t),delete e(this,n,"f")[e(this,i,"f")],r(this,i,e(this,i,"f")+1)}else e(this,n,"f")[o]=t}))}set onmessage(t){r(this,s,t)}get onmessage(){return e(this,s,"f")}[(s=new WeakMap,i=new WeakMap,n=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function _(t,e={},r){return window.__TAURI_INTERNALS__.invoke(t,e,r)}return t.isRegistered=async function(t){return await _("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const r=new a;return r.onmessage=e,await _("plugin:global-shortcut|register",{shortcuts:Array.isArray(t)?t:[t],handler:r})},t.unregister=async function(t){return await _("plugin:global-shortcut|unregister",{shortcuts:Array.isArray(t)?t:[t]})},t.unregisterAll=async function(){return await _("plugin:global-shortcut|unregister_all",{})},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBAL_SHORTCUT__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBAL_SHORTCUT__=function(t){"use strict";function e(t,e,r,s){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function r(t,e,r,s,i){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,r),r}var s,i,n;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,s.set(this,(()=>{})),i.set(this,0),n.set(this,[]),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:o})=>{if(o==e(this,i,"f"))for(e(this,s,"f").call(this,t),r(this,i,e(this,i,"f")+1);e(this,i,"f")in e(this,n,"f");){const t=e(this,n,"f")[e(this,i,"f")];e(this,s,"f").call(this,t),delete e(this,n,"f")[e(this,i,"f")],r(this,i,e(this,i,"f")+1)}else e(this,n,"f")[o]=t}))}set onmessage(t){r(this,s,t)}get onmessage(){return e(this,s,"f")}[(s=new WeakMap,i=new WeakMap,n=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function _(t,e={},r){return window.__TAURI_INTERNALS__.invoke(t,e,r)}return t.isRegistered=async function(t){return await _("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const r=new a;return r.onmessage=e,await _("plugin:global-shortcut|register",{shortcuts:Array.isArray(t)?t:[t],handler:r})},t.unregister=async function(t){return await _("plugin:global-shortcut|unregister",{shortcuts:Array.isArray(t)?t:[t]})},t.unregisterAll=async function(){return await _("plugin:global-shortcut|unregister_all",{})},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBAL_SHORTCUT__})}
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## \[2.2.4]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
## \[2.2.3]
- [`406e6f48`](https://github.com/tauri-apps/plugins-workspace/commit/406e6f484cdc13d35c50fb949f7489ca9eeccc44) ([#2323](https://github.com/tauri-apps/plugins-workspace/pull/2323) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused build failures when the `haptics` or `geolocation` plugin was used without their `specta` feature flag enabled.
## \[2.2.2]
- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) **Breaking change:** `specta` integration is now behind a `specta` feature flag like in Tauri.
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "tauri-plugin-haptics"
description = "Haptic feedback and vibrations on Android and iOS"
version = "2.2.2"
version = "2.2.4"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
+13
View File
@@ -69,6 +69,19 @@ fn main() {
}
```
Second, add the required permissions in the project:
`src-tauri/capabilities/default.json`
```json
"permissions": [
"haptics:allow-impact-feedback",
"haptics:allow-notification-feedback",
"haptics:allow-selection-feedback",
"haptics:allow-vibrate"
]
```
Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
```javascript
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-haptics",
"version": "2.2.2",
"version": "2.2.4",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+1 -1
View File
@@ -14,7 +14,7 @@ pub enum Error {
#[cfg(mobile)]
#[error(transparent)]
PluginInvoke(
#[serde(skip)]
#[cfg_attr(feature = "specta", serde(skip))]
#[from]
tauri::plugin::mobile::PluginInvokeError,
),
+2 -2
View File
@@ -21,9 +21,9 @@ mod models;
pub use error::{Error, Result};
#[cfg(desktop)]
use desktop::Haptics;
pub use desktop::Haptics;
#[cfg(mobile)]
use mobile::Haptics;
pub use mobile::Haptics;
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the haptics APIs.
pub trait HapticsExt<R: Runtime> {
+16
View File
@@ -1,5 +1,21 @@
# Changelog
## \[2.4.2]
- [`a15eedf3`](https://github.com/tauri-apps/plugins-workspace/commit/a15eedf37854344f7ffbcb0d373d848563817011) ([#2535](https://github.com/tauri-apps/plugins-workspace/pull/2535) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Fix `fetch` occasionally throwing an error due to trying to close the underline stream twice.
## \[2.4.1]
- [`d3183aa9`](https://github.com/tauri-apps/plugins-workspace/commit/d3183aa99da7ca67e627394132ddeb3b85ccef06) ([#2522](https://github.com/tauri-apps/plugins-workspace/pull/2522) by [@adrieljss](https://github.com/tauri-apps/plugins-workspace/../../adrieljss)) Fix `fetch` blocking until the whole response is read even if it was a streaming response.
## \[2.4.0]
- [`cb38f54f`](https://github.com/tauri-apps/plugins-workspace/commit/cb38f54f4a4ef30995283cd82166c62da17bac44) ([#2479](https://github.com/tauri-apps/plugins-workspace/pull/2479) by [@adrieljss](https://github.com/tauri-apps/plugins-workspace/../../adrieljss)) Add stream support for HTTP stream responses.
## \[2.3.0]
- [`10513649`](https://github.com/tauri-apps/plugins-workspace/commit/105136494c5a5bf4b1f1cc06cc71815412d17ec8) ([#2204](https://github.com/tauri-apps/plugins-workspace/pull/2204) by [@RickeyWard](https://github.com/tauri-apps/plugins-workspace/../../RickeyWard)) Add `dangerous-settings` feature flag and new JS `danger` option to disable tls hostname/certificate validation.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-http"
version = "2.2.0"
version = "2.4.2"
description = "Access an HTTP client written in Rust."
edition = { workspace = true }
authors = { workspace = true }
@@ -73,3 +73,4 @@ charset = ["reqwest/charset"]
macos-system-configuration = ["reqwest/macos-system-configuration"]
unsafe-headers = []
tracing = ["dep:tracing"]
dangerous-settings = []
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";async function t(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;const r="Request canceled";return e.fetch=async function(e,n){const a=n?.signal;if(a?.aborted)throw new Error(r);const o=n?.maxRedirections,s=n?.connectTimeout,i=n?.proxy;n&&(delete n.maxRedirections,delete n.connectTimeout,delete n.proxy);const d=n?.headers?n.headers instanceof Headers?n.headers:new Headers(n.headers):new Headers,c=new Request(e,n),u=await c.arrayBuffer(),f=0!==u.byteLength?Array.from(new Uint8Array(u)):null;for(const[e,t]of c.headers)d.get(e)||d.set(e,t);const _=(d instanceof Headers?Array.from(d.entries()):Array.isArray(d)?d:Object.entries(d)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(a?.aborted)throw new Error(r);const h=await t("plugin:http|fetch",{clientConfig:{method:c.method,url:c.url,headers:_,data:f,maxRedirections:o,connectTimeout:s,proxy:i}}),l=()=>t("plugin:http|fetch_cancel",{rid:h});if(a?.aborted)throw l(),new Error(r);a?.addEventListener("abort",(()=>{l()}));const{status:p,statusText:w,url:y,headers:T,rid:A}=await t("plugin:http|fetch_send",{rid:h}),g=await t("plugin:http|fetch_read_body",{rid:A}),R=new Response(g instanceof ArrayBuffer&&0!==g.byteLength?g:g instanceof Array&&g.length>0?new Uint8Array(g):null,{status:p,statusText:w});return Object.defineProperty(R,"url",{value:y}),Object.defineProperty(R,"headers",{value:new Headers(T)}),R},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";function t(e,t,r,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}function r(e,t,r,n,s){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,r),r}var n,s,a;"function"==typeof SuppressedError&&SuppressedError;const i="__TAURI_TO_IPC_KEY__";class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),s.set(this,0),a.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:i})=>{if(i==t(this,s,"f"))for(t(this,n,"f").call(this,e),r(this,s,t(this,s,"f")+1);t(this,s,"f")in t(this,a,"f");){const e=t(this,a,"f")[t(this,s,"f")];t(this,n,"f").call(this,e),delete t(this,a,"f")[t(this,s,"f")],r(this,s,t(this,s,"f")+1)}else t(this,a,"f")[i]=e}))}set onmessage(e){r(this,n,e)}get onmessage(){return t(this,n,"f")}[(n=new WeakMap,s=new WeakMap,a=new WeakMap,i)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[i]()}}async function c(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}const d="Request cancelled";return e.fetch=async function(e,t){const r=t?.signal;if(r?.aborted)throw new Error(d);const n=t?.maxRedirections,s=t?.connectTimeout,a=t?.proxy,i=t?.danger;t&&(delete t.maxRedirections,delete t.connectTimeout,delete t.proxy,delete t.danger);const h=t?.headers?t.headers instanceof Headers?t.headers:new Headers(t.headers):new Headers,f=new Request(e,t),_=await f.arrayBuffer(),u=0!==_.byteLength?Array.from(new Uint8Array(_)):null;for(const[e,t]of f.headers)h.get(e)||h.set(e,t);const l=(h instanceof Headers?Array.from(h.entries()):Array.isArray(h)?h:Object.entries(h)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(r?.aborted)throw new Error(d);const w=await c("plugin:http|fetch",{clientConfig:{method:f.method,url:f.url,headers:l,data:u,maxRedirections:n,connectTimeout:s,proxy:a,danger:i}}),p=()=>c("plugin:http|fetch_cancel",{rid:w});if(r?.aborted)throw p(),new Error(d);r?.addEventListener("abort",(()=>{p()}));const{status:y,statusText:m,url:T,headers:g,rid:b}=await c("plugin:http|fetch_send",{rid:w}),A=new ReadableStream({start:e=>{const t=new o;t.onmessage=t=>{if(r?.aborted)return void e.error(d);const n=new Uint8Array(t),s=n[n.byteLength-1],a=n.slice(0,n.byteLength-1);1!=s?e.enqueue(a):e.close()},c("plugin:http|fetch_read_body",{rid:b,streamChannel:t}).catch((t=>{e.error(t)}))}}),R=new Response(A,{status:y,statusText:m});return Object.defineProperty(R,"url",{value:T}),Object.defineProperty(R,"headers",{value:new Headers(g)}),R},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})}
+66 -25
View File
@@ -26,7 +26,7 @@
* @module
*/
import { invoke } from '@tauri-apps/api/core'
import { Channel, invoke } from '@tauri-apps/api/core'
/**
* Configuration of a proxy that a Client should pass requests to.
@@ -84,9 +84,29 @@ export interface ClientOptions {
* Configuration of a proxy that a Client should pass requests to.
*/
proxy?: Proxy
/**
* Configuration for dangerous settings on the client such as disabling SSL verification.
*/
danger?: DangerousSettings
}
const ERROR_REQUEST_CANCELLED = 'Request canceled'
/**
* Configuration for dangerous settings on the client such as disabling SSL verification.
*
* @since 2.3.0
*/
export interface DangerousSettings {
/**
* Disables SSL verification.
*/
acceptInvalidCerts?: boolean
/**
* Disables hostname verification.
*/
acceptInvalidHostnames?: boolean
}
const ERROR_REQUEST_CANCELLED = 'Request cancelled'
/**
* Fetch a resource from the network. It returns a `Promise` that resolves to the
@@ -115,12 +135,14 @@ export async function fetch(
const maxRedirections = init?.maxRedirections
const connectTimeout = init?.connectTimeout
const proxy = init?.proxy
const danger = init?.danger
// Remove these fields before creating the request
if (init) {
delete init.maxRedirections
delete init.connectTimeout
delete init.proxy
delete init.danger
}
const headers = init?.headers
@@ -172,7 +194,8 @@ export async function fetch(
data,
maxRedirections,
connectTimeout,
proxy
proxy,
danger
}
})
@@ -206,31 +229,49 @@ export async function fetch(
rid
})
const body = await invoke<ArrayBuffer | number[]>(
'plugin:http|fetch_read_body',
{
rid: responseRid
}
)
const readableStreamBody = new ReadableStream({
start: (controller) => {
const streamChannel = new Channel<ArrayBuffer | number[]>()
streamChannel.onmessage = (res: ArrayBuffer | number[]) => {
// close early if aborted
if (signal?.aborted) {
controller.error(ERROR_REQUEST_CANCELLED)
return
}
const res = new Response(
body instanceof ArrayBuffer && body.byteLength !== 0
? body
: body instanceof Array && body.length > 0
? new Uint8Array(body)
: null,
{
status,
statusText
}
)
const resUint8 = new Uint8Array(res)
const lastByte = resUint8[resUint8.byteLength - 1]
const actualRes = resUint8.slice(0, resUint8.byteLength - 1)
// url and headers are read only properties
// but seems like we can set them like this
// close when the signal to close (last byte is 1) is sent from the IPC.
if (lastByte == 1) {
controller.close()
return
}
controller.enqueue(actualRes)
}
// run a non-blocking body stream fetch
invoke('plugin:http|fetch_read_body', {
rid: responseRid,
streamChannel
}).catch((e) => {
controller.error(e)
})
}
})
const res = new Response(readableStreamBody, {
status,
statusText
})
// Set `Response` properties that are ignored by the
// constructor, like url and some headers
//
// we define theme like this, because using `Response`
// constructor, it removes url and some headers
// like `set-cookie` headers
// Since url and headers are read only properties
// this is the only way to set them.
Object.defineProperty(res, 'url', { value: url })
Object.defineProperty(res, 'headers', {
value: new Headers(responseHeaders)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-http",
"version": "2.2.0",
"version": "2.4.2",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+50 -7
View File
@@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use tauri::{
async_runtime::Mutex,
command,
ipc::{CommandScope, GlobalScope},
ipc::{Channel, CommandScope, GlobalScope},
Manager, ResourceId, ResourceTable, Runtime, State, Webview,
};
use tokio::sync::oneshot::{channel, Receiver, Sender};
@@ -75,6 +75,14 @@ pub struct FetchResponse {
rid: ResourceId,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)] //feature flags shoudln't affect api
pub struct DangerousSettings {
accept_invalid_certs: bool,
accept_invalid_hostnames: bool,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientConfig {
@@ -85,6 +93,7 @@ pub struct ClientConfig {
connect_timeout: Option<u64>,
max_redirections: Option<usize>,
proxy: Option<Proxy>,
danger: Option<DangerousSettings>,
}
#[derive(Debug, Deserialize)]
@@ -181,6 +190,7 @@ pub async fn fetch<R: Runtime>(
connect_timeout,
max_redirections,
proxy,
danger,
} = client_config;
let scheme = url.scheme();
@@ -220,6 +230,24 @@ pub async fn fetch<R: Runtime>(
{
let mut builder = reqwest::ClientBuilder::new();
if let Some(danger_config) = danger {
#[cfg(not(feature = "dangerous-settings"))]
{
#[cfg(debug_assertions)]
{
eprintln!("[\x1b[33mWARNING\x1b[0m] using dangerous settings requires `dangerous-settings` feature flag in your Cargo.toml");
}
let _ = danger_config;
return Err(Error::DangerousSettings);
}
#[cfg(feature = "dangerous-settings")]
{
builder = builder
.danger_accept_invalid_certs(danger_config.accept_invalid_certs)
.danger_accept_invalid_hostnames(danger_config.accept_invalid_hostnames)
}
}
if let Some(timeout) = connect_timeout {
builder = builder.connect_timeout(Duration::from_millis(timeout));
}
@@ -287,6 +315,7 @@ pub async fn fetch<R: Runtime>(
tracing::trace!("{:?}", request);
let fut = async move { request.send().await.map_err(Into::into) };
let mut resources_table = webview.resources_table();
let rid = resources_table.add_request(Box::pin(fut));
@@ -330,7 +359,7 @@ pub fn fetch_cancel<R: Runtime>(webview: Webview<R>, rid: ResourceId) -> crate::
Ok(())
}
#[tauri::command]
#[command]
pub async fn fetch_send<R: Runtime>(
webview: Webview<R>,
rid: ResourceId,
@@ -382,17 +411,31 @@ pub async fn fetch_send<R: Runtime>(
})
}
#[tauri::command]
pub(crate) async fn fetch_read_body<R: Runtime>(
#[command]
pub async fn fetch_read_body<R: Runtime>(
webview: Webview<R>,
rid: ResourceId,
) -> crate::Result<tauri::ipc::Response> {
stream_channel: Channel<tauri::ipc::InvokeResponseBody>,
) -> crate::Result<()> {
let res = {
let mut resources_table = webview.resources_table();
resources_table.take::<ReqwestResponse>(rid)?
};
let res = Arc::into_inner(res).unwrap().0;
Ok(tauri::ipc::Response::new(res.bytes().await?.to_vec()))
let mut res = Arc::into_inner(res).unwrap().0;
// send response through IPC channel
while let Some(chunk) = res.chunk().await? {
let mut chunk = chunk.to_vec();
// append 0 to indicate we are not done yet
chunk.push(0);
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(chunk))?;
}
// send 1 to indicate we are done
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(vec![1]))?;
Ok(())
}
// forbidden headers per fetch spec https://fetch.spec.whatwg.org/#terminology-headers
+2
View File
@@ -41,6 +41,8 @@ pub enum Error {
Tauri(#[from] tauri::Error),
#[error(transparent)]
Utf8(#[from] std::string::FromUtf8Error),
#[error("dangerous settings used but are not enabled")]
DangerousSettings,
}
impl Serialize for Error {
+1 -1
View File
@@ -37,7 +37,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
commands::fetch,
commands::fetch_cancel,
commands::fetch_send,
commands::fetch_read_body,
commands::fetch_read_body
])
.build()
}
+22
View File
@@ -1,5 +1,27 @@
# Changelog
## \[2.3.1]
- [`1bb1ced5`](https://github.com/tauri-apps/plugins-workspace/commit/1bb1ced53820127204aa7adf57510c1cbce55e12) ([#2524](https://github.com/tauri-apps/plugins-workspace/pull/2524) by [@elwerene](https://github.com/tauri-apps/plugins-workspace/../../elwerene)) enable TargetKind::LogDir on mobile
## \[2.3.0]
### feat
- [`02481501`](https://github.com/tauri-apps/plugins-workspace/commit/024815018fbc63a37afc716796a454925aa7d25e) ([#2377](https://github.com/tauri-apps/plugins-workspace/pull/2377) by [@3lpsy](https://github.com/tauri-apps/plugins-workspace/../../3lpsy)) Add a `is_skip_logger` flag to the Log Plugin `Builder` struct, a `skip_logger()` method to the Builder, and logic to avoid acquiring (creating) a logger and attaching it to the global logger. Since acquire_logger is pub, a `LoggerNotInitialized` is added and returned if it's called when the `is_skip_looger` flag is set. Overall, this feature permits a user to avoid calling `attach_logger` which can only be called once in a program's lifetime and allows the user to control the logger returned from `logger()`. Additionally, it also will allow users to generate multiple Tauri Mock apps in test suites that run and parallel and have the `log` plugin attached (assuming they use `skip_logger()`).
## \[2.2.3]
- [`1a984659`](https://github.com/tauri-apps/plugins-workspace/commit/1a9846599b6a71faf330845847a30f6bf9735898) ([#2469](https://github.com/tauri-apps/plugins-workspace/pull/2469) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Update `objc2` crate to 0.6. No user facing changes.
## \[2.2.2]
- [`6b4c3917`](https://github.com/tauri-apps/plugins-workspace/commit/6b4c3917389f4bc489d03b48a837557ac0584175) ([#2401](https://github.com/tauri-apps/plugins-workspace/pull/2401) by [@Seishiin](https://github.com/tauri-apps/plugins-workspace/../../Seishiin)) Fix timezone_strategy overwriting previously set LogLevels.
## \[2.2.1]
- [`784a54a3`](https://github.com/tauri-apps/plugins-workspace/commit/784a54a39094dfbaaa8dd123eb083c04dc6c3bb2) ([#2344](https://github.com/tauri-apps/plugins-workspace/pull/2344) by [@madsmtm](https://github.com/tauri-apps/plugins-workspace/../../madsmtm)) Use `objc2` instead of `objc`.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+9 -6
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-log"
version = "2.2.0"
version = "2.3.1"
description = "Configurable logging for your Tauri app."
authors = { workspace = true }
license = { workspace = true }
@@ -17,8 +17,8 @@ rustdoc-args = ["--cfg", "docsrs"]
windows = { level = "full", notes = "" }
linux = { level = "full", notes = "" }
macos = { level = "full", notes = "" }
android = { level = "unknown", notes = "" }
ios = { level = "unknown", notes = "" }
android = { level = "full", notes = "" }
ios = { level = "full", notes = "" }
[build-dependencies]
tauri-plugin = { workspace = true, features = ["build"] }
@@ -35,12 +35,15 @@ time = { version = "0.3", features = ["formatting", "local-offset"] }
fern = "0.7"
[target."cfg(target_os = \"android\")".dependencies]
android_logger = "0.14"
android_logger = "0.15"
[target."cfg(target_os = \"ios\")".dependencies]
swift-rs = "1"
objc = "0.2"
cocoa = "0.26"
objc2 = "0.6"
objc2-foundation = { version = "0.3", default-features = false, features = [
"std",
"NSString",
] }
[features]
colored = ["fern/colored"]
+17 -1
View File
@@ -54,7 +54,23 @@ yarn add https://github.com/tauri-apps/tauri-plugin-log#v2
## Usage
First you need to register the core plugin with Tauri:
First, you should enable the `log:default` capability:
```json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"opener:default",
"log:default" # add this!
]
}
```
Then, you need to register the core plugin with Tauri:
`src-tauri/src/lib.rs`
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-log",
"version": "2.2.0",
"version": "2.3.1",
"description": "Configurable logging for your Tauri app.",
"license": "MIT OR Apache-2.0",
"authors": [
+47 -46
View File
@@ -35,31 +35,6 @@ pub const WEBVIEW_TARGET: &str = "webview";
#[cfg(target_os = "ios")]
mod ios {
use cocoa::base::id;
use objc::*;
const UTF8_ENCODING: usize = 4;
pub struct NSString(pub id);
impl NSString {
pub fn new(s: &str) -> Self {
// Safety: objc runtime calls are unsafe
NSString(unsafe {
let ns_string: id = msg_send![class!(NSString), alloc];
let ns_string: id = msg_send![ns_string,
initWithBytes:s.as_ptr()
length:s.len()
encoding:UTF8_ENCODING];
// The thing is allocated in rust, the thing must be set to autorelease in rust to relinquish control
// or it can not be released correctly in OC runtime
let _: () = msg_send![ns_string, autorelease];
ns_string
})
}
}
swift_rs::swift!(pub fn tauri_log(
level: u8, message: *const std::ffi::c_void
));
@@ -83,6 +58,8 @@ pub enum Error {
TimeFormat(#[from] time::error::Format),
#[error(transparent)]
InvalidFormatDescription(#[from] time::error::InvalidFormatDescription),
#[error("Internal logger disabled and cannot be acquired or attached")]
LoggerNotInitialized,
}
/// An enum representing the available verbosity levels of the logger.
@@ -182,11 +159,12 @@ pub enum TargetKind {
///
/// ### Platform-specific
///
/// |Platform | Value | Example |
/// | ------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
/// | Linux | `$XDG_DATA_HOME/{bundleIdentifier}/logs` or `$HOME/.local/share/{bundleIdentifier}/logs` | `/home/alice/.local/share/com.tauri.dev/logs` |
/// | macOS | `{homeDir}/Library/Logs/{bundleIdentifier}` | `/Users/Alice/Library/Logs/com.tauri.dev` |
/// | Windows | `{FOLDERID_LocalAppData}/{bundleIdentifier}/logs` | `C:\Users\Alice\AppData\Local\com.tauri.dev\logs` |
/// |Platform | Value | Example |
/// | --------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
/// | Linux | `$XDG_DATA_HOME/{bundleIdentifier}/logs` or `$HOME/.local/share/{bundleIdentifier}/logs` | `/home/alice/.local/share/com.tauri.dev/logs` |
/// | macOS/iOS | `{homeDir}/Library/Logs/{bundleIdentifier}` | `/Users/Alice/Library/Logs/com.tauri.dev` |
/// | Windows | `{FOLDERID_LocalAppData}/{bundleIdentifier}/logs` | `C:\Users\Alice\AppData\Local\com.tauri.dev\logs` |
/// | Android | `{ConfigDir}/logs` | `/data/data/com.tauri.dev/files/logs` |
LogDir { file_name: Option<String> },
/// Forward logs to the webview (via the `log://log` event).
///
@@ -255,6 +233,7 @@ pub struct Builder {
timezone_strategy: TimezoneStrategy,
max_file_size: u128,
targets: Vec<Target>,
is_skip_logger: bool,
}
impl Default for Builder {
@@ -283,6 +262,7 @@ impl Default for Builder {
timezone_strategy: DEFAULT_TIMEZONE_STRATEGY,
max_file_size: DEFAULT_MAX_FILE_SIZE,
targets: DEFAULT_LOG_TARGETS.into(),
is_skip_logger: false,
}
}
}
@@ -303,7 +283,7 @@ impl Builder {
let format =
time::format_description::parse("[[[year]-[month]-[day]][[[hour]:[minute]:[second]]")
.unwrap();
self.dispatch = fern::Dispatch::new().format(move |out, message, record| {
self.dispatch = self.dispatch.format(move |out, message, record| {
out.finish(format_args!(
"{}[{}][{}] {}",
timezone_strategy.get_now().format(&format).unwrap(),
@@ -364,6 +344,22 @@ impl Builder {
self
}
/// Skip the creation and global registration of a logger
///
/// If you wish to use your own global logger, you must call `skip_logger` so that the plugin does not attempt to set a second global logger. In this configuration, no logger will be created and the plugin's `log` command will rely on the result of `log::logger()`. You will be responsible for configuring the logger yourself and any included targets will be ignored. This can also be used with `tracing-log` or if running tests in parallel that require the plugin to be registered.
/// ```rust
/// static LOGGER: SimpleLogger = SimpleLogger;
///
/// log::set_logger(&SimpleLogger)?;
/// log::set_max_level(LevelFilter::Info);
/// tauri_plugin_log::Builder::new()
/// .skip_logger();
/// ```
pub fn skip_logger(mut self) -> Self {
self.is_skip_logger = true;
self
}
/// Adds a collection of targets to the logger.
///
/// ```rust
@@ -429,7 +425,12 @@ impl Builder {
log::Level::Info => 2,
log::Level::Warn | log::Level::Error => 3,
},
ios::NSString::new(message.as_str()).0 as _,
// The string is allocated in rust, so we must
// autorelease it rust to give it to the Swift
// runtime.
objc2::rc::Retained::autorelease_ptr(
objc2_foundation::NSString::from_str(message.as_str()),
) as _,
);
}
}),
@@ -451,9 +452,6 @@ impl Builder {
)?)?
.into()
}
#[cfg(mobile)]
TargetKind::LogDir { .. } => continue,
#[cfg(desktop)]
TargetKind::LogDir { file_name } => {
let path = app_handle.path().app_log_dir()?;
if !path.exists() {
@@ -501,6 +499,9 @@ impl Builder {
self,
app_handle: &AppHandle<R>,
) -> Result<(TauriPlugin<R>, log::LevelFilter, Box<dyn log::Log>), Error> {
if self.is_skip_logger {
return Err(Error::LoggerNotInitialized);
}
let plugin = Self::plugin_builder();
let (max_level, log) = Self::acquire_logger(
app_handle,
@@ -517,17 +518,17 @@ impl Builder {
pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
Self::plugin_builder()
.setup(move |app_handle, _api| {
let (max_level, log) = Self::acquire_logger(
app_handle,
self.dispatch,
self.rotation_strategy,
self.timezone_strategy,
self.max_file_size,
self.targets,
)?;
attach_logger(max_level, log)?;
if !self.is_skip_logger {
let (max_level, log) = Self::acquire_logger(
app_handle,
self.dispatch,
self.rotation_strategy,
self.timezone_strategy,
self.max_file_size,
self.targets,
)?;
attach_logger(max_level, log)?;
}
Ok(())
})
.build()
+2 -2
View File
@@ -181,8 +181,8 @@ function encodeURI(uri: string): number[] {
protocols.slice(1).forEach(function (protocol) {
if (
(prefix.length === 0 || prefix === 'urn:') &&
uri.indexOf(protocol) === 0
(prefix.length === 0 || prefix === 'urn:')
&& uri.indexOf(protocol) === 0
) {
prefix = protocol
}
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.2.2]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
## \[2.2.1]
- [`da5c59e2`](https://github.com/tauri-apps/plugins-workspace/commit/da5c59e2fe879d177e3cfd52fcacce85440423cb) ([#2271](https://github.com/tauri-apps/plugins-workspace/pull/2271) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `zbus` dependency to version 5. No API changes.
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-notification"
version = "2.2.1"
version = "2.2.2"
description = "Send desktop and mobile notifications on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
@@ -46,7 +46,7 @@ windows-version = { version = "0.1", optional = true }
notify-rust = "4.11"
[dev-dependencies]
color-backtrace = "0.6"
color-backtrace = "0.7"
ctor = "0.2"
maplit = "1"
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_NOTIFICATION__=function(i){"use strict";function t(i,t,n,e){if("a"===n&&!e)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?i!==t||!e:!t.has(i))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?e:"a"===n?e.call(i):e?e.value:t.get(i)}function n(i,t,n,e,a){if("function"==typeof t||!t.has(i))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(i,n),n}var e,a,o;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class s{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,e.set(this,(()=>{})),a.set(this,0),o.set(this,[]),this.id=function(i,t=!1){return window.__TAURI_INTERNALS__.transformCallback(i,t)}((({message:i,id:r})=>{if(r==t(this,a,"f"))for(t(this,e,"f").call(this,i),n(this,a,t(this,a,"f")+1);t(this,a,"f")in t(this,o,"f");){const i=t(this,o,"f")[t(this,a,"f")];t(this,e,"f").call(this,i),delete t(this,o,"f")[t(this,a,"f")],n(this,a,t(this,a,"f")+1)}else t(this,o,"f")[r]=i}))}set onmessage(i){n(this,e,i)}get onmessage(){return t(this,e,"f")}[(e=new WeakMap,a=new WeakMap,o=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}class c{constructor(i,t,n){this.plugin=i,this.event=t,this.channelId=n}async unregister(){return u(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function l(i,t,n){const e=new s;return e.onmessage=n,u(`plugin:${i}|registerListener`,{event:t,handler:e}).then((()=>new c(i,t,e.id)))}async function u(i,t={},n){return window.__TAURI_INTERNALS__.invoke(i,t,n)}var f,h,d;i.ScheduleEvery=void 0,(f=i.ScheduleEvery||(i.ScheduleEvery={})).Year="year",f.Month="month",f.TwoWeeks="twoWeeks",f.Week="week",f.Day="day",f.Hour="hour",f.Minute="minute",f.Second="second";return i.Importance=void 0,(h=i.Importance||(i.Importance={}))[h.None=0]="None",h[h.Min=1]="Min",h[h.Low=2]="Low",h[h.Default=3]="Default",h[h.High=4]="High",i.Visibility=void 0,(d=i.Visibility||(i.Visibility={}))[d.Secret=-1]="Secret",d[d.Private=0]="Private",d[d.Public=1]="Public",i.Schedule=class{static at(i,t=!1,n=!1){return{at:{date:i,repeating:t,allowWhileIdle:n},interval:void 0,every:void 0}}static interval(i,t=!1){return{at:void 0,interval:{interval:i,allowWhileIdle:t},every:void 0}}static every(i,t,n=!1){return{at:void 0,interval:void 0,every:{interval:i,count:t,allowWhileIdle:n}}}},i.active=async function(){return await u("plugin:notification|get_active")},i.cancel=async function(i){await u("plugin:notification|cancel",{notifications:i})},i.cancelAll=async function(){await u("plugin:notification|cancel")},i.channels=async function(){return await u("plugin:notification|listChannels")},i.createChannel=async function(i){await u("plugin:notification|create_channel",{...i})},i.isPermissionGranted=async function(){return"default"!==window.Notification.permission?await Promise.resolve("granted"===window.Notification.permission):await u("plugin:notification|is_permission_granted")},i.onAction=async function(i){return await l("notification","actionPerformed",i)},i.onNotificationReceived=async function(i){return await l("notification","notification",i)},i.pending=async function(){return await u("plugin:notification|get_pending")},i.registerActionTypes=async function(i){await u("plugin:notification|register_action_types",{types:i})},i.removeActive=async function(i){await u("plugin:notification|remove_active",{notifications:i})},i.removeAllActive=async function(){await u("plugin:notification|remove_active")},i.removeChannel=async function(i){await u("plugin:notification|delete_channel",{id:i})},i.requestPermission=async function(){return await window.Notification.requestPermission()},i.sendNotification=function(i){"string"==typeof i?new window.Notification(i):new window.Notification(i.title,i)},i}({});Object.defineProperty(window.__TAURI__,"notification",{value:__TAURI_PLUGIN_NOTIFICATION__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_NOTIFICATION__=function(i){"use strict";function n(i,n,t,e){if("function"==typeof n||!n.has(i))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?e:"a"===t?e.call(i):e?e.value:n.get(i)}function t(i,n,t,e,o){if("function"==typeof n||!n.has(i))throw new TypeError("Cannot write private member to an object whose class did not declare it");return n.set(i,t),t}var e,o,a;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class c{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,e.set(this,(()=>{})),o.set(this,0),a.set(this,[]),this.id=function(i,n=!1){return window.__TAURI_INTERNALS__.transformCallback(i,n)}((({message:i,id:r})=>{if(r==n(this,o,"f"))for(n(this,e,"f").call(this,i),t(this,o,n(this,o,"f")+1);n(this,o,"f")in n(this,a,"f");){const i=n(this,a,"f")[n(this,o,"f")];n(this,e,"f").call(this,i),delete n(this,a,"f")[n(this,o,"f")],t(this,o,n(this,o,"f")+1)}else n(this,a,"f")[r]=i}))}set onmessage(i){t(this,e,i)}get onmessage(){return n(this,e,"f")}[(e=new WeakMap,o=new WeakMap,a=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}class s{constructor(i,n,t){this.plugin=i,this.event=n,this.channelId=t}async unregister(){return u(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function l(i,n,t){const e=new c;return e.onmessage=t,u(`plugin:${i}|registerListener`,{event:n,handler:e}).then((()=>new s(i,n,e.id)))}async function u(i,n={},t){return window.__TAURI_INTERNALS__.invoke(i,n,t)}var f,h,d;i.ScheduleEvery=void 0,(f=i.ScheduleEvery||(i.ScheduleEvery={})).Year="year",f.Month="month",f.TwoWeeks="twoWeeks",f.Week="week",f.Day="day",f.Hour="hour",f.Minute="minute",f.Second="second";return i.Importance=void 0,(h=i.Importance||(i.Importance={}))[h.None=0]="None",h[h.Min=1]="Min",h[h.Low=2]="Low",h[h.Default=3]="Default",h[h.High=4]="High",i.Visibility=void 0,(d=i.Visibility||(i.Visibility={}))[d.Secret=-1]="Secret",d[d.Private=0]="Private",d[d.Public=1]="Public",i.Schedule=class{static at(i,n=!1,t=!1){return{at:{date:i,repeating:n,allowWhileIdle:t},interval:void 0,every:void 0}}static interval(i,n=!1){return{at:void 0,interval:{interval:i,allowWhileIdle:n},every:void 0}}static every(i,n,t=!1){return{at:void 0,interval:void 0,every:{interval:i,count:n,allowWhileIdle:t}}}},i.active=async function(){return await u("plugin:notification|get_active")},i.cancel=async function(i){await u("plugin:notification|cancel",{notifications:i})},i.cancelAll=async function(){await u("plugin:notification|cancel")},i.channels=async function(){return await u("plugin:notification|listChannels")},i.createChannel=async function(i){await u("plugin:notification|create_channel",{...i})},i.isPermissionGranted=async function(){return"default"!==window.Notification.permission?await Promise.resolve("granted"===window.Notification.permission):await u("plugin:notification|is_permission_granted")},i.onAction=async function(i){return await l("notification","actionPerformed",i)},i.onNotificationReceived=async function(i){return await l("notification","notification",i)},i.pending=async function(){return await u("plugin:notification|get_pending")},i.registerActionTypes=async function(i){await u("plugin:notification|register_action_types",{types:i})},i.removeActive=async function(i){await u("plugin:notification|remove_active",{notifications:i})},i.removeAllActive=async function(){await u("plugin:notification|remove_active")},i.removeChannel=async function(i){await u("plugin:notification|delete_channel",{id:i})},i.requestPermission=async function(){return await window.Notification.requestPermission()},i.sendNotification=function(i){"string"==typeof i?new window.Notification(i):new window.Notification(i.title,i)},i}({});Object.defineProperty(window.__TAURI__,"notification",{value:__TAURI_PLUGIN_NOTIFICATION__})}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-notification",
"version": "2.2.1",
"version": "2.2.2",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+2
View File
@@ -18,6 +18,8 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
}
/// Access to the notification APIs.
///
/// You can get an instance of this type via [`NotificationExt`](crate::NotificationExt)
pub struct Notification<R: Runtime>(AppHandle<R>);
impl<R: Runtime> crate::NotificationBuilder<R> {
+3 -3
View File
@@ -34,9 +34,9 @@ mod models;
pub use error::{Error, Result};
#[cfg(desktop)]
use desktop::Notification;
pub use desktop::Notification;
#[cfg(mobile)]
use mobile::Notification;
pub use mobile::Notification;
/// The notification builder.
#[derive(Debug)]
@@ -120,7 +120,7 @@ impl<R: Runtime> NotificationBuilder<R> {
/// Identifier used to group multiple notifications.
///
/// https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent/1649872-threadidentifier
/// <https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent/1649872-threadidentifier>
pub fn group(mut self, group: impl Into<String>) -> Self {
self.data.group.replace(group.into());
self
+2
View File
@@ -40,6 +40,8 @@ impl<R: Runtime> crate::NotificationBuilder<R> {
}
/// Access to the notification APIs.
///
/// You can get an instance of this type via [`NotificationExt`](crate::NotificationExt)
pub struct Notification<R: Runtime>(PluginHandle<R>);
impl<R: Runtime> Notification<R> {
+9
View File
@@ -1,5 +1,14 @@
# Changelog
## \[2.2.6]
- [`1a984659`](https://github.com/tauri-apps/plugins-workspace/commit/1a9846599b6a71faf330845847a30f6bf9735898) ([#2469](https://github.com/tauri-apps/plugins-workspace/pull/2469) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Update `objc2` crate to 0.6. No user facing changes.
- [`71f95c9f`](https://github.com/tauri-apps/plugins-workspace/commit/71f95c9f05b29cf1be586849614c0b007757c15d) ([#2445](https://github.com/tauri-apps/plugins-workspace/pull/2445) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `windows` crate to 0.60 to match Tauri 2.3.0. No user facing changes.
## \[2.2.5]
- [`5b821181`](https://github.com/tauri-apps/plugins-workspace/commit/5b8211815825ddae2dcc0c00520e0cfdff002763) ([#2332](https://github.com/tauri-apps/plugins-workspace/pull/2332) by [@betamos](https://github.com/tauri-apps/plugins-workspace/../../betamos)) Fix broken JS commands `opener.openPath` and `opener.openUrl` on mobile.
## \[2.2.4]
- [`da5c59e2`](https://github.com/tauri-apps/plugins-workspace/commit/da5c59e2fe879d177e3cfd52fcacce85440423cb) ([#2271](https://github.com/tauri-apps/plugins-workspace/pull/2271) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `zbus` dependency to version 5. No API changes.
+9 -7
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-opener"
version = "2.2.4"
version = "2.2.6"
description = "Open files and URLs using their default application."
edition = { workspace = true }
authors = { workspace = true }
@@ -15,7 +15,7 @@ rustdoc-args = ["--cfg", "docsrs"]
# Platforms supported by the plugin
# Support levels are "full", "partial", "none", "unknown"
# Details of the support level are left to plugin maintainer
[package.metadata.platforms]
[package.metadata.platforms.support]
windows = { level = "full", notes = "" }
linux = { level = "full", notes = "" }
macos = { level = "full", notes = "" }
@@ -40,7 +40,7 @@ glob = { workspace = true }
dunce = { workspace = true }
[target."cfg(windows)".dependencies.windows]
version = "0.58"
version = "0.60"
features = [
"Win32_Foundation",
"Win32_UI_Shell_Common",
@@ -54,12 +54,14 @@ zbus = { workspace = true }
url = { workspace = true }
[target."cfg(target_os = \"macos\")".dependencies.objc2-app-kit]
version = "0.2"
features = ["NSWorkspace"]
version = "0.3"
default-features = false
features = ["std", "NSWorkspace"]
[target."cfg(target_os = \"macos\")".dependencies.objc2-foundation]
version = "0.2"
features = ["NSURL", "NSArray", "NSString"]
version = "0.3"
default-features = false
features = ["std", "NSURL", "NSArray", "NSString"]
[target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] }
+12 -12
View File
@@ -9,13 +9,13 @@ window.addEventListener('click', function (evt) {
// return early if
if (
// event was prevented
evt.defaultPrevented ||
evt.defaultPrevented
// or not a left click
evt.button !== 0 ||
|| evt.button !== 0
// or meta key pressed
evt.metaKey ||
|| evt.metaKey
// or al key pressed
evt.altKey
|| evt.altKey
)
return
@@ -28,16 +28,16 @@ window.addEventListener('click', function (evt) {
// return early if
if (
// not tirggered from <a> element
!a ||
!a
// or doesn't have a href
!a.href ||
|| !a.href
// or not supposed to be open in a new tab
!(
a.target === '_blank' ||
|| !(
a.target === '_blank'
// or ctrl key pressed
evt.ctrlKey ||
|| evt.ctrlKey
// or shift key pressed
evt.shiftKey
|| evt.shiftKey
)
)
return
@@ -47,9 +47,9 @@ window.addEventListener('click', function (evt) {
// return early if
if (
// same origin (internal navigation)
url.origin === window.location.origin ||
url.origin === window.location.origin
// not default protocols
['http:', 'https:', 'mailto:', 'tel:'].every((p) => url.protocol !== p)
|| ['http:', 'https:', 'mailto:', 'tel:'].every((p) => url.protocol !== p)
)
return
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-opener",
"version": "2.2.4",
"version": "2.2.6",
"description": "Open files and URLs using their default application.",
"license": "MIT OR Apache-2.0",
"authors": [
+3 -3
View File
@@ -9,7 +9,7 @@ use tauri::{
AppHandle, Runtime,
};
use crate::{scope::Scope, Error};
use crate::{scope::Scope, Error, OpenerExt};
#[tauri::command]
pub async fn open_url<R: Runtime>(
@@ -34,7 +34,7 @@ pub async fn open_url<R: Runtime>(
);
if scope.is_url_allowed(&url, with.as_deref()) {
crate::open_url(url, with)
app.opener().open_url(url, with)
} else {
Err(Error::ForbiddenUrl { url, with })
}
@@ -63,7 +63,7 @@ pub async fn open_path<R: Runtime>(
);
if scope.is_path_allowed(Path::new(&path), with.as_deref())? {
crate::open_path(path, with)
app.opener().open_path(path, with)
} else {
Err(Error::ForbiddenPath { path, with })
}
+2 -2
View File
@@ -56,7 +56,7 @@ impl<R: Runtime> Opener<R> {
/// - **Android / iOS**: Always opens using default program.
#[cfg(desktop)]
pub fn open_url(&self, url: impl Into<String>, with: Option<impl Into<String>>) -> Result<()> {
crate::open::open(url.into(), with.map(Into::into)).map_err(Into::into)
crate::open::open(url.into(), with.map(Into::into))
}
/// Open a url with a default or specific program.
@@ -108,7 +108,7 @@ impl<R: Runtime> Opener<R> {
path: impl Into<String>,
with: Option<impl Into<String>>,
) -> Result<()> {
crate::open::open(path.into(), with.map(Into::into)).map_err(Into::into)
crate::open::open(path.into(), with.map(Into::into))
}
/// Open a path with a default or specific program.
+1 -1
View File
@@ -185,7 +185,7 @@ mod imp {
let path = path.to_string_lossy();
let path = NSString::from_str(&path);
let urls = vec![NSURL::fileURLWithPath(&path)];
let urls = NSArray::from_vec(urls);
let urls = NSArray::from_retained_slice(&urls);
let workspace = NSWorkspace::new();
workspace.activateFileViewerSelectingURLs(&urls);
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.2.1]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-os"
version = "2.2.0"
version = "2.2.1"
description = "Read information about the operating system."
edition = { workspace = true }
authors = { workspace = true }
@@ -31,5 +31,5 @@ log = { workspace = true }
thiserror = { workspace = true }
os_info = "3"
sys-locale = "0.3"
gethostname = "0.5"
gethostname = "1.0"
serialize-to-javascript = "=0.1.1"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-os",
"version": "2.2.0",
"version": "2.2.1",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+1 -1
View File
@@ -88,7 +88,7 @@ pub fn exe_extension() -> &'static str {
std::env::consts::EXE_EXTENSION
}
/// Returns the current operating system locale with the `BCP-47` language tag. If the locale couldnt be obtained, `None` is returned instead.
/// Returns the current operating system locale with the `BCP-47` language tag. If the locale couldn't be obtained, `None` is returned instead.
pub fn locale() -> Option<String> {
sys_locale::get_locale()
}
@@ -1,7 +1,7 @@
## Default Permission
This permission set configures which
process feeatures are by default exposed.
process features are by default exposed.
#### Granted Permissions
+1 -1
View File
@@ -3,7 +3,7 @@
[default]
description = """
This permission set configures which
process feeatures are by default exposed.
process features are by default exposed.
#### Granted Permissions
@@ -315,7 +315,7 @@
"const": "deny-restart"
},
{
"description": "This permission set configures which\nprocess feeatures are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n",
"description": "This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n",
"type": "string",
"const": "default"
}
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_SHELL__=function(e){"use strict";function t(e,t,s,i){if("a"===s&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(e):i?i.value:t.get(e)}function s(e,t,s,i,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,s),s}var i,n,r;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),n.set(this,0),r.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:o})=>{if(o==t(this,n,"f"))for(t(this,i,"f").call(this,e),s(this,n,t(this,n,"f")+1);t(this,n,"f")in t(this,r,"f");){const e=t(this,r,"f")[t(this,n,"f")];t(this,i,"f").call(this,e),delete t(this,r,"f")[t(this,n,"f")],s(this,n,t(this,n,"f")+1)}else t(this,r,"f")[o]=e}))}set onmessage(e){s(this,i,e)}get onmessage(){return t(this,i,"f")}[(i=new WeakMap,n=new WeakMap,r=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function h(e,t={},s){return window.__TAURI_INTERNALS__.invoke(e,t,s)}class c{constructor(){this.eventListeners=Object.create(null)}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}on(e,t){return e in this.eventListeners?this.eventListeners[e].push(t):this.eventListeners[e]=[t],this}once(e,t){const s=i=>{this.removeListener(e,s),t(i)};return this.addListener(e,s)}off(e,t){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter((e=>e!==t))),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,t){if(e in this.eventListeners){const s=this.eventListeners[e];for(const e of s)e(t);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,t){return e in this.eventListeners?this.eventListeners[e].unshift(t):this.eventListeners[e]=[t],this}prependOnceListener(e,t){const s=i=>{this.removeListener(e,s),t(i)};return this.prependListener(e,s)}}class l{constructor(e){this.pid=e}async write(e){await h("plugin:shell|stdin_write",{pid:this.pid,buffer:e})}async kill(){await h("plugin:shell|kill",{cmd:"killChild",pid:this.pid})}}class u extends c{constructor(e,t=[],s){super(),this.stdout=new c,this.stderr=new c,this.program=e,this.args="string"==typeof t?[t]:t,this.options=s??{}}static create(e,t=[],s){return new u(e,t,s)}static sidecar(e,t=[],s){const i=new u(e,t,s);return i.options.sidecar=!0,i}async spawn(){const e=this.program,t=this.args,s=this.options;"object"==typeof t&&Object.freeze(t);const i=new a;return i.onmessage=e=>{switch(e.event){case"Error":this.emit("error",e.payload);break;case"Terminated":this.emit("close",e.payload);break;case"Stdout":this.stdout.emit("data",e.payload);break;case"Stderr":this.stderr.emit("data",e.payload)}},await h("plugin:shell|spawn",{program:e,args:t,options:s,onEvent:i}).then((e=>new l(e)))}async execute(){const e=this.program,t=this.args,s=this.options;return"object"==typeof t&&Object.freeze(t),await h("plugin:shell|execute",{program:e,args:t,options:s})}}return e.Child=l,e.Command=u,e.EventEmitter=c,e.open=async function(e,t){await h("plugin:shell|open",{path:e,with:t})},e}({});Object.defineProperty(window.__TAURI__,"shell",{value:__TAURI_PLUGIN_SHELL__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_SHELL__=function(e){"use strict";function t(e,t,s,i){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(e):i?i.value:t.get(e)}function s(e,t,s,i,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,s),s}var i,n,r;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),n.set(this,0),r.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:o})=>{if(o==t(this,n,"f"))for(t(this,i,"f").call(this,e),s(this,n,t(this,n,"f")+1);t(this,n,"f")in t(this,r,"f");){const e=t(this,r,"f")[t(this,n,"f")];t(this,i,"f").call(this,e),delete t(this,r,"f")[t(this,n,"f")],s(this,n,t(this,n,"f")+1)}else t(this,r,"f")[o]=e}))}set onmessage(e){s(this,i,e)}get onmessage(){return t(this,i,"f")}[(i=new WeakMap,n=new WeakMap,r=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function h(e,t={},s){return window.__TAURI_INTERNALS__.invoke(e,t,s)}class c{constructor(){this.eventListeners=Object.create(null)}addListener(e,t){return this.on(e,t)}removeListener(e,t){return this.off(e,t)}on(e,t){return e in this.eventListeners?this.eventListeners[e].push(t):this.eventListeners[e]=[t],this}once(e,t){const s=i=>{this.removeListener(e,s),t(i)};return this.addListener(e,s)}off(e,t){return e in this.eventListeners&&(this.eventListeners[e]=this.eventListeners[e].filter((e=>e!==t))),this}removeAllListeners(e){return e?delete this.eventListeners[e]:this.eventListeners=Object.create(null),this}emit(e,t){if(e in this.eventListeners){const s=this.eventListeners[e];for(const e of s)e(t);return!0}return!1}listenerCount(e){return e in this.eventListeners?this.eventListeners[e].length:0}prependListener(e,t){return e in this.eventListeners?this.eventListeners[e].unshift(t):this.eventListeners[e]=[t],this}prependOnceListener(e,t){const s=i=>{this.removeListener(e,s),t(i)};return this.prependListener(e,s)}}class l{constructor(e){this.pid=e}async write(e){await h("plugin:shell|stdin_write",{pid:this.pid,buffer:e})}async kill(){await h("plugin:shell|kill",{cmd:"killChild",pid:this.pid})}}class u extends c{constructor(e,t=[],s){super(),this.stdout=new c,this.stderr=new c,this.program=e,this.args="string"==typeof t?[t]:t,this.options=s??{}}static create(e,t=[],s){return new u(e,t,s)}static sidecar(e,t=[],s){const i=new u(e,t,s);return i.options.sidecar=!0,i}async spawn(){const e=this.program,t=this.args,s=this.options;"object"==typeof t&&Object.freeze(t);const i=new a;return i.onmessage=e=>{switch(e.event){case"Error":this.emit("error",e.payload);break;case"Terminated":this.emit("close",e.payload);break;case"Stdout":this.stdout.emit("data",e.payload);break;case"Stderr":this.stderr.emit("data",e.payload)}},await h("plugin:shell|spawn",{program:e,args:t,options:s,onEvent:i}).then((e=>new l(e)))}async execute(){const e=this.program,t=this.args,s=this.options;return"object"==typeof t&&Object.freeze(t),await h("plugin:shell|execute",{program:e,args:t,options:s})}}return e.Child=l,e.Command=u,e.EventEmitter=c,e.open=async function(e,t){await h("plugin:shell|open",{path:e,with:t})},e}({});Object.defineProperty(window.__TAURI__,"shell",{value:__TAURI_PLUGIN_SHELL__})}
+6 -6
View File
@@ -12,11 +12,11 @@ function openLinks(): void {
if (target.matches('a')) {
const t = target as HTMLAnchorElement
if (
t.href !== '' &&
['http://', 'https://', 'mailto:', 'tel:'].some((v) =>
t.href !== ''
&& ['http://', 'https://', 'mailto:', 'tel:'].some((v) =>
t.href.startsWith(v)
) &&
t.target === '_blank'
)
&& t.target === '_blank'
) {
void invoke('plugin:shell|open', {
path: t.href
@@ -31,8 +31,8 @@ function openLinks(): void {
}
if (
document.readyState === 'complete' ||
document.readyState === 'interactive'
document.readyState === 'complete'
|| document.readyState === 'interactive'
) {
openLinks()
} else {
+1 -1
View File
@@ -75,7 +75,7 @@ impl<R: Runtime> Shell<R> {
#[deprecated(since = "2.1.0", note = "Use tauri-plugin-opener instead.")]
#[allow(deprecated)]
pub fn open(&self, path: impl Into<String>, with: Option<open::Program>) -> Result<()> {
open::open(&self.open_scope, path.into(), with).map_err(Into::into)
open::open(&self.open_scope, path.into(), with)
}
/// Open a (url) path with a default or specific browser opening program.
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.2.2]
- [`1ab5f157`](https://github.com/tauri-apps/plugins-workspace/commit/1ab5f1576333174095bc7dad4bef7a8576bb29ab) ([#2452](https://github.com/tauri-apps/plugins-workspace/pull/2452) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed `null pointer dereference` panic on rust nightly on Windows.
## \[2.2.1]
- [`da5c59e2`](https://github.com/tauri-apps/plugins-workspace/commit/da5c59e2fe879d177e3cfd52fcacce85440423cb) ([#2271](https://github.com/tauri-apps/plugins-workspace/pull/2271) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `zbus` dependency to version 5. No API changes.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-single-instance"
version = "2.2.1"
version = "2.2.2"
description = "Ensure a single instance of your tauri app is running."
authors = { workspace = true }
license = { workspace = true }
@@ -9,6 +9,6 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@tauri-apps/cli": "2.2.4"
"@tauri-apps/cli": "2.3.1"
}
}
@@ -3,5 +3,5 @@
// SPDX-License-Identifier: MIT
fn main() {
tauri_build::build()
tauri_build::build()
}
@@ -1,14 +0,0 @@
max_width = 100
hard_tabs = false
tab_spaces = 2
newline_style = "Auto"
use_small_heuristics = "Default"
reorder_imports = true
reorder_modules = true
remove_nested_parens = true
edition = "2021"
merge_derives = true
use_try_shorthand = false
use_field_init_shorthand = false
force_explicit_abi = true
imports_granularity = "Crate"
@@ -3,16 +3,15 @@
// SPDX-License-Identifier: MIT
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_cli::init())
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
println!("{}, {argv:?}, {cwd}", app.package_info().name);
}))
.run(tauri::generate_context!())
.expect("error while running tauri application");
tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
println!("{}, {argv:?}, {cwd}", app.package_info().name);
}))
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
@@ -20,18 +20,38 @@ use windows_sys::Win32::{
},
UI::WindowsAndMessaging::{
self as w32wm, CreateWindowExW, DefWindowProcW, DestroyWindow, FindWindowW,
RegisterClassExW, SendMessageW, GWL_STYLE, GWL_USERDATA, WINDOW_LONG_PTR_INDEX,
WM_COPYDATA, WM_DESTROY, WNDCLASSEXW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,
WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
RegisterClassExW, SendMessageW, CREATESTRUCTW, GWLP_USERDATA, GWL_STYLE,
WINDOW_LONG_PTR_INDEX, WM_COPYDATA, WM_CREATE, WM_DESTROY, WNDCLASSEXW, WS_EX_LAYERED,
WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
},
};
struct MutexHandle(isize);
struct TargetWindowHandle(isize);
const WMCOPYDATA_SINGLE_INSTANCE_DATA: usize = 1542;
pub fn init<R: Runtime>(f: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
struct MutexHandle(isize);
struct TargetWindowHandle(isize);
struct UserData<R: Runtime> {
app: AppHandle<R>,
callback: Box<SingleInstanceCallback<R>>,
}
impl<R: Runtime> UserData<R> {
unsafe fn from_hwnd_raw(hwnd: HWND) -> *mut Self {
GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut Self
}
unsafe fn from_hwnd<'a>(hwnd: HWND) -> &'a mut Self {
&mut *Self::from_hwnd_raw(hwnd)
}
fn run_callback(&mut self, args: Vec<String>, cwd: String) {
(self.callback)(&self.app, args, cwd)
}
}
pub fn init<R: Runtime>(callback: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
plugin::Builder::new("single-instance")
.setup(|app, _api| {
#[allow(unused_mut)]
@@ -54,21 +74,22 @@ pub fn init<R: Runtime>(f: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
let hwnd = FindWindowW(class_name.as_ptr(), window_name.as_ptr());
if !hwnd.is_null() {
let data = format!(
"{}|{}\0",
std::env::current_dir()
.unwrap_or_default()
.to_str()
.unwrap_or_default(),
std::env::args().collect::<Vec<String>>().join("|")
);
let cwd = std::env::current_dir().unwrap_or_default();
let cwd = cwd.to_str().unwrap_or_default();
let args = std::env::args().collect::<Vec<String>>().join("|");
let data = format!("{cwd}|{args}\0",);
let bytes = data.as_bytes();
let cds = COPYDATASTRUCT {
dwData: WMCOPYDATA_SINGLE_INSTANCE_DATA,
cbData: bytes.len() as _,
lpData: bytes.as_ptr() as _,
};
SendMessageW(hwnd, WM_COPYDATA, 0, &cds as *const _ as _);
app.cleanup_before_exit();
std::process::exit(0);
}
@@ -76,15 +97,12 @@ pub fn init<R: Runtime>(f: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
} else {
app.manage(MutexHandle(hmutex as _));
let hwnd = create_event_target_window::<R>(&class_name, &window_name);
unsafe {
SetWindowLongPtrW(
hwnd,
GWL_USERDATA,
Box::into_raw(Box::new((app.clone(), f))) as _,
)
let userdata = UserData {
app: app.clone(),
callback,
};
let userdata = Box::into_raw(Box::new(userdata));
let hwnd = create_event_target_window::<R>(&class_name, &window_name, userdata);
app.manage(TargetWindowHandle(hwnd as _));
}
@@ -116,32 +134,43 @@ unsafe extern "system" fn single_instance_window_proc<R: Runtime>(
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
let data_ptr = GetWindowLongPtrW(hwnd, GWL_USERDATA)
as *mut (AppHandle<R>, Box<SingleInstanceCallback<R>>);
let (app_handle, callback) = &mut *data_ptr;
match msg {
WM_CREATE => {
let create_struct = &*(lparam as *const CREATESTRUCTW);
let userdata = create_struct.lpCreateParams as *const UserData<R>;
SetWindowLongPtrW(hwnd, GWLP_USERDATA, userdata as _);
0
}
WM_COPYDATA => {
let cds_ptr = lparam as *const COPYDATASTRUCT;
if (*cds_ptr).dwData == WMCOPYDATA_SINGLE_INSTANCE_DATA {
let userdata = UserData::<R>::from_hwnd(hwnd);
let data = CStr::from_ptr((*cds_ptr).lpData as _).to_string_lossy();
let mut s = data.split('|');
let cwd = s.next().unwrap();
let args = s.map(|s| s.to_string()).collect();
callback(app_handle, args, cwd.to_string());
userdata.run_callback(args, cwd.to_string());
}
1
}
WM_DESTROY => {
let _ = Box::from_raw(data_ptr);
let userdata = UserData::<R>::from_hwnd_raw(hwnd);
drop(Box::from_raw(userdata));
0
}
_ => DefWindowProcW(hwnd, msg, wparam, lparam),
}
}
fn create_event_target_window<R: Runtime>(class_name: &[u16], window_name: &[u16]) -> HWND {
fn create_event_target_window<R: Runtime>(
class_name: &[u16],
window_name: &[u16],
userdata: *const UserData<R>,
) -> HWND {
unsafe {
let class = WNDCLASSEXW {
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
@@ -182,7 +211,7 @@ fn create_event_target_window<R: Runtime>(class_name: &[u16], window_name: &[u16
std::ptr::null_mut(),
std::ptr::null_mut(),
GetModuleHandleW(std::ptr::null()),
std::ptr::null(),
userdata as _,
);
SetWindowLongPtrW(
hwnd,
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_STORE__=function(t){"use strict";var e,a;function r(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}async function s(t,e={},a){return window.__TAURI_INTERNALS__.invoke(t,e,a)}"function"==typeof SuppressedError&&SuppressedError;class i{get rid(){return function(t,e,a,r){if("a"===a&&!r)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!r:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===a?r:"a"===a?r.call(t):r?r.value:e.get(t)}(this,e,"f")}constructor(t){e.set(this,void 0),function(t,e,a){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");e.set(t,a)}(this,e,t)}async close(){return s("plugin:resources|close",{rid:this.rid})}}async function n(t,e,a){const i={kind:"Any"};return s("plugin:event|listen",{event:t,target:i,handler:r(e)}).then((e=>async()=>async function(t,e){await s("plugin:event|unlisten",{event:t,eventId:e})}(t,e)))}async function o(t,e){return await u.load(t,e)}e=new WeakMap,function(t){t.WINDOW_RESIZED="tauri://resize",t.WINDOW_MOVED="tauri://move",t.WINDOW_CLOSE_REQUESTED="tauri://close-requested",t.WINDOW_DESTROYED="tauri://destroyed",t.WINDOW_FOCUS="tauri://focus",t.WINDOW_BLUR="tauri://blur",t.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",t.WINDOW_THEME_CHANGED="tauri://theme-changed",t.WINDOW_CREATED="tauri://window-created",t.WEBVIEW_CREATED="tauri://webview-created",t.DRAG_ENTER="tauri://drag-enter",t.DRAG_OVER="tauri://drag-over",t.DRAG_DROP="tauri://drag-drop",t.DRAG_LEAVE="tauri://drag-leave"}(a||(a={}));class u extends i{constructor(t){super(t)}static async load(t,e){const a=await s("plugin:store|load",{path:t,...e});return new u(a)}static async get(t){return await s("plugin:store|get_store",{path:t}).then((t=>t?new u(t):null))}async set(t,e){await s("plugin:store|set",{rid:this.rid,key:t,value:e})}async get(t){const[e,a]=await s("plugin:store|get",{rid:this.rid,key:t});return a?e:void 0}async has(t){return await s("plugin:store|has",{rid:this.rid,key:t})}async delete(t){return await s("plugin:store|delete",{rid:this.rid,key:t})}async clear(){await s("plugin:store|clear",{rid:this.rid})}async reset(){await s("plugin:store|reset",{rid:this.rid})}async keys(){return await s("plugin:store|keys",{rid:this.rid})}async values(){return await s("plugin:store|values",{rid:this.rid})}async entries(){return await s("plugin:store|entries",{rid:this.rid})}async length(){return await s("plugin:store|length",{rid:this.rid})}async reload(){await s("plugin:store|reload",{rid:this.rid})}async save(){await s("plugin:store|save",{rid:this.rid})}async onKeyChange(t,e){return await n("store://change",(a=>{a.payload.resourceId===this.rid&&a.payload.key===t&&e(a.payload.exists?a.payload.value:void 0)}))}async onChange(t){return await n("store://change",(e=>{e.payload.resourceId===this.rid&&t(e.payload.key,e.payload.exists?e.payload.value:void 0)}))}}return t.LazyStore=class{get store(){return this._store||(this._store=o(this.path,this.options)),this._store}constructor(t,e){this.path=t,this.options=e}async init(){await this.store}async set(t,e){return(await this.store).set(t,e)}async get(t){return(await this.store).get(t)}async has(t){return(await this.store).has(t)}async delete(t){return(await this.store).delete(t)}async clear(){await(await this.store).clear()}async reset(){await(await this.store).reset()}async keys(){return(await this.store).keys()}async values(){return(await this.store).values()}async entries(){return(await this.store).entries()}async length(){return(await this.store).length()}async reload(){await(await this.store).reload()}async save(){await(await this.store).save()}async onKeyChange(t,e){return(await this.store).onKeyChange(t,e)}async onChange(t){return(await this.store).onChange(t)}async close(){this._store&&await(await this._store).close()}},t.Store=u,t.getStore=async function(t){return await u.get(t)},t.load=o,t}({});Object.defineProperty(window.__TAURI__,"store",{value:__TAURI_PLUGIN_STORE__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_STORE__=function(t){"use strict";var e,a;function r(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}async function s(t,e={},a){return window.__TAURI_INTERNALS__.invoke(t,e,a)}"function"==typeof SuppressedError&&SuppressedError;class i{get rid(){return function(t,e,a,r){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===a?r:"a"===a?r.call(t):r?r.value:e.get(t)}(this,e,"f")}constructor(t){e.set(this,void 0),function(t,e,a){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");e.set(t,a)}(this,e,t)}async close(){return s("plugin:resources|close",{rid:this.rid})}}async function n(t,e,a){const i={kind:"Any"};return s("plugin:event|listen",{event:t,target:i,handler:r(e)}).then((e=>async()=>async function(t,e){await s("plugin:event|unlisten",{event:t,eventId:e})}(t,e)))}async function o(t,e){return await u.load(t,e)}e=new WeakMap,function(t){t.WINDOW_RESIZED="tauri://resize",t.WINDOW_MOVED="tauri://move",t.WINDOW_CLOSE_REQUESTED="tauri://close-requested",t.WINDOW_DESTROYED="tauri://destroyed",t.WINDOW_FOCUS="tauri://focus",t.WINDOW_BLUR="tauri://blur",t.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",t.WINDOW_THEME_CHANGED="tauri://theme-changed",t.WINDOW_CREATED="tauri://window-created",t.WEBVIEW_CREATED="tauri://webview-created",t.DRAG_ENTER="tauri://drag-enter",t.DRAG_OVER="tauri://drag-over",t.DRAG_DROP="tauri://drag-drop",t.DRAG_LEAVE="tauri://drag-leave"}(a||(a={}));class u extends i{constructor(t){super(t)}static async load(t,e){const a=await s("plugin:store|load",{path:t,...e});return new u(a)}static async get(t){return await s("plugin:store|get_store",{path:t}).then((t=>t?new u(t):null))}async set(t,e){await s("plugin:store|set",{rid:this.rid,key:t,value:e})}async get(t){const[e,a]=await s("plugin:store|get",{rid:this.rid,key:t});return a?e:void 0}async has(t){return await s("plugin:store|has",{rid:this.rid,key:t})}async delete(t){return await s("plugin:store|delete",{rid:this.rid,key:t})}async clear(){await s("plugin:store|clear",{rid:this.rid})}async reset(){await s("plugin:store|reset",{rid:this.rid})}async keys(){return await s("plugin:store|keys",{rid:this.rid})}async values(){return await s("plugin:store|values",{rid:this.rid})}async entries(){return await s("plugin:store|entries",{rid:this.rid})}async length(){return await s("plugin:store|length",{rid:this.rid})}async reload(){await s("plugin:store|reload",{rid:this.rid})}async save(){await s("plugin:store|save",{rid:this.rid})}async onKeyChange(t,e){return await n("store://change",(a=>{a.payload.resourceId===this.rid&&a.payload.key===t&&e(a.payload.exists?a.payload.value:void 0)}))}async onChange(t){return await n("store://change",(e=>{e.payload.resourceId===this.rid&&t(e.payload.key,e.payload.exists?e.payload.value:void 0)}))}}return t.LazyStore=class{get store(){return this._store||(this._store=o(this.path,this.options)),this._store}constructor(t,e){this.path=t,this.options=e}async init(){await this.store}async set(t,e){return(await this.store).set(t,e)}async get(t){return(await this.store).get(t)}async has(t){return(await this.store).has(t)}async delete(t){return(await this.store).delete(t)}async clear(){await(await this.store).clear()}async reset(){await(await this.store).reset()}async keys(){return(await this.store).keys()}async values(){return(await this.store).values()}async entries(){return(await this.store).entries()}async length(){return(await this.store).length()}async reload(){await(await this.store).reload()}async save(){await(await this.store).save()}async onKeyChange(t,e){return(await this.store).onKeyChange(t,e)}async onChange(t){return(await this.store).onChange(t)}async close(){this._store&&await(await this._store).close()}},t.Store=u,t.getStore=async function(t){return await u.get(t)},t.load=o,t}({});Object.defineProperty(window.__TAURI__,"store",{value:__TAURI_PLUGIN_STORE__})}
@@ -8,8 +8,8 @@
"tauri": "tauri"
},
"devDependencies": {
"@tauri-apps/cli": "2.2.4",
"vite": "^6.0.0",
"typescript": "^5.4.7"
"@tauri-apps/cli": "2.3.1",
"typescript": "^5.7.3",
"vite": "^6.2.0"
}
}
+22
View File
@@ -1,5 +1,27 @@
# Changelog
## \[2.6.1]
- [`12c4537b`](https://github.com/tauri-apps/plugins-workspace/commit/12c4537b8e4fed29b415ff817434b664c0596dac) ([#2541](https://github.com/tauri-apps/plugins-workspace/pull/2541) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Add support to the `riscv64` architecture.
## \[2.6.0]
- [`faefcc9f`](https://github.com/tauri-apps/plugins-workspace/commit/faefcc9fd8c61f709d491649e255a7fcac82c09a) ([#2430](https://github.com/tauri-apps/plugins-workspace/pull/2430) by [@goenning](https://github.com/tauri-apps/plugins-workspace/../../goenning)) Add `UpdaterBuilder::configure_client` method on Rust side, to configure the `reqwest` client used to check and download the update.
- [`ac60d589`](https://github.com/tauri-apps/plugins-workspace/commit/ac60d589eca2bbc4aed040feb18da148e66ec171) ([#2513](https://github.com/tauri-apps/plugins-workspace/pull/2513) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Enhance error logging.
## \[2.5.1]
- [`6f881293`](https://github.com/tauri-apps/plugins-workspace/commit/6f881293fcd67838f6f3f8063f536292431dd1f7) ([#2439](https://github.com/tauri-apps/plugins-workspace/pull/2439) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused the plugin to emit a `ReleaseNotFound` error instead of a `Reqwest` error when the http request in `check()` failed.
## \[2.5.0]
- [`5369898d`](https://github.com/tauri-apps/plugins-workspace/commit/5369898db7a6098e3e2f43436100ea556d405628) ([#2067](https://github.com/tauri-apps/plugins-workspace/pull/2067) by [@jLynx](https://github.com/tauri-apps/plugins-workspace/../../jLynx)) Fix update installation on macOS when using an user without admin privileges.
- [`5369898d`](https://github.com/tauri-apps/plugins-workspace/commit/5369898db7a6098e3e2f43436100ea556d405628) ([#2067](https://github.com/tauri-apps/plugins-workspace/pull/2067) by [@jLynx](https://github.com/tauri-apps/plugins-workspace/../../jLynx)) Remove the `UpdaterBuilder::new` function, use `UpdaterExt::updater_builder` instead.
## \[2.4.0]
- [`0afc9b6b`](https://github.com/tauri-apps/plugins-workspace/commit/0afc9b6be07bee1077f05a86285d977e57810ed9) ([#2325](https://github.com/tauri-apps/plugins-workspace/pull/2325) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) The `Update` struct/object will now contain a `raw_json`/`rawJson` property to be able to read parts of server's json response that are not handled by the plugin.
## \[2.3.1]
- [`57efb47c`](https://github.com/tauri-apps/plugins-workspace/commit/57efb47c116f880477f72f02a8e4239e88007d44) ([#2235](https://github.com/tauri-apps/plugins-workspace/pull/2235) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Add `Builder::header` and `Builder::headers` method to configure default headers for updater.
+5 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-updater"
version = "2.3.1"
version = "2.6.1"
description = "In-app updates for Tauri applications."
edition = { workspace = true }
authors = { workspace = true }
@@ -30,6 +30,7 @@ tauri = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
log = { workspace = true }
tokio = "1"
reqwest = { version = "0.12", default-features = false, features = [
"json",
@@ -43,7 +44,7 @@ base64 = "0.22"
semver = { version = "1", features = ["serde"] }
futures-util = "0.3"
tempfile = "3"
infer = "0.16"
infer = "0.19"
percent-encoding = "2.3"
[target."cfg(target_os = \"windows\")".dependencies]
@@ -55,13 +56,14 @@ windows-sys = { version = "0.59.0", features = [
] }
[target."cfg(target_os = \"linux\")".dependencies]
dirs = "5"
dirs = "6"
tar = { version = "0.4", optional = true }
flate2 = { version = "1", optional = true }
[target."cfg(target_os = \"macos\")".dependencies]
tar = "0.4"
flate2 = "1"
osakit = { version = "0.3", features = ["full"] }
[features]
default = ["rustls-tls", "zip"]
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_UPDATER__=function(e){"use strict";function t(e,t,s,i){if("a"===s&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?i:"a"===s?i.call(e):i?i.value:t.get(e)}function s(e,t,s,i,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,s),s}var i,n,r,a;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class d{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),n.set(this,0),r.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:a})=>{if(a==t(this,n,"f"))for(t(this,i,"f").call(this,e),s(this,n,t(this,n,"f")+1);t(this,n,"f")in t(this,r,"f");){const e=t(this,r,"f")[t(this,n,"f")];t(this,i,"f").call(this,e),delete t(this,r,"f")[t(this,n,"f")],s(this,n,t(this,n,"f")+1)}else t(this,r,"f")[a]=e}))}set onmessage(e){s(this,i,e)}get onmessage(){return t(this,i,"f")}[(i=new WeakMap,n=new WeakMap,r=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function l(e,t={},s){return window.__TAURI_INTERNALS__.invoke(e,t,s)}class c{get rid(){return t(this,a,"f")}constructor(e){a.set(this,void 0),s(this,a,e)}async close(){return l("plugin:resources|close",{rid:this.rid})}}a=new WeakMap;class h extends c{constructor(e){super(e.rid),this.available=e.available,this.currentVersion=e.currentVersion,this.version=e.version,this.date=e.date,this.body=e.body}async download(e,t){const s=new d;e&&(s.onmessage=e);const i=await l("plugin:updater|download",{onEvent:s,rid:this.rid,...t});this.downloadedBytes=new c(i)}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(e,t){const s=new d;e&&(s.onmessage=e),await l("plugin:updater|download_and_install",{onEvent:s,rid:this.rid,...t})}async close(){await(this.downloadedBytes?.close()),await super.close()}}return e.Update=h,e.check=async function(e){return e?.headers&&(e.headers=Array.from(new Headers(e.headers).entries())),await l("plugin:updater|check",{...e}).then((e=>e.available?new h(e):null))},e}({});Object.defineProperty(window.__TAURI__,"updater",{value:__TAURI_PLUGIN_UPDATER__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_UPDATER__=function(e){"use strict";function t(e,t,s,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===s?n:"a"===s?n.call(e):n?n.value:t.get(e)}function s(e,t,s,n,i){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,s),s}var n,i,a,r;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class d{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),i.set(this,0),a.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:r})=>{if(r==t(this,i,"f"))for(t(this,n,"f").call(this,e),s(this,i,t(this,i,"f")+1);t(this,i,"f")in t(this,a,"f");){const e=t(this,a,"f")[t(this,i,"f")];t(this,n,"f").call(this,e),delete t(this,a,"f")[t(this,i,"f")],s(this,i,t(this,i,"f")+1)}else t(this,a,"f")[r]=e}))}set onmessage(e){s(this,n,e)}get onmessage(){return t(this,n,"f")}[(n=new WeakMap,i=new WeakMap,a=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function l(e,t={},s){return window.__TAURI_INTERNALS__.invoke(e,t,s)}class h{get rid(){return t(this,r,"f")}constructor(e){r.set(this,void 0),s(this,r,e)}async close(){return l("plugin:resources|close",{rid:this.rid})}}r=new WeakMap;class c extends h{constructor(e){super(e.rid),this.available=e.available,this.currentVersion=e.currentVersion,this.version=e.version,this.date=e.date,this.body=e.body,this.rawJson=e.rawJson}async download(e,t){const s=new d;e&&(s.onmessage=e);const n=await l("plugin:updater|download",{onEvent:s,rid:this.rid,...t});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(e,t){const s=new d;e&&(s.onmessage=e),await l("plugin:updater|download_and_install",{onEvent:s,rid:this.rid,...t})}async close(){await(this.downloadedBytes?.close()),await super.close()}}return e.Update=c,e.check=async function(e){return e?.headers&&(e.headers=Array.from(new Headers(e.headers).entries())),await l("plugin:updater|check",{...e}).then((e=>e.available?new c(e):null))},e}({});Object.defineProperty(window.__TAURI__,"updater",{value:__TAURI_PLUGIN_UPDATER__})}
+3
View File
@@ -43,6 +43,7 @@ interface UpdateMetadata {
version: string
date?: string
body?: string
rawJson: Record<string, unknown>
}
/** Updater download event */
@@ -57,6 +58,7 @@ class Update extends Resource {
version: string
date?: string
body?: string
rawJson: Record<string, unknown>
private downloadedBytes?: Resource
constructor(metadata: UpdateMetadata) {
@@ -66,6 +68,7 @@ class Update extends Resource {
this.version = metadata.version
this.date = metadata.date
this.body = metadata.body
this.rawJson = metadata.rawJson
}
/** Download the updater package */
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-updater",
"version": "2.3.1",
"version": "2.6.1",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+2
View File
@@ -34,6 +34,7 @@ pub(crate) struct Metadata {
version: String,
date: Option<String>,
body: Option<String>,
raw_json: serde_json::Value,
}
struct DownloadedBytes(pub Vec<u8>);
@@ -73,6 +74,7 @@ pub(crate) async fn check<R: Runtime>(
metadata.version.clone_from(&update.version);
metadata.date = update.date.map(|d| d.to_string());
metadata.body.clone_from(&update.body);
metadata.raw_json.clone_from(&update.raw_json);
metadata.rid = Some(webview.resources_table().add(update));
}
+1 -7
View File
@@ -70,7 +70,6 @@ pub trait UpdaterExt<R: Runtime> {
impl<R: Runtime, T: Manager<R>> UpdaterExt<R> for T {
fn updater_builder(&self) -> UpdaterBuilder {
let app = self.app_handle();
let package_info = app.package_info();
let UpdaterState {
config,
target,
@@ -78,12 +77,7 @@ impl<R: Runtime, T: Manager<R>> UpdaterExt<R> for T {
headers,
} = self.state::<UpdaterState>().inner();
let mut builder = UpdaterBuilder::new(
package_info.name.clone(),
package_info.version.clone(),
config.clone(),
)
.headers(headers.clone());
let mut builder = UpdaterBuilder::new(app, config.clone()).headers(headers.clone());
if let Some(target) = target {
builder = builder.target(target);
+167 -45
View File
@@ -4,7 +4,7 @@
use std::{
collections::HashMap,
ffi::{OsStr, OsString},
ffi::OsString,
io::Cursor,
path::{Path, PathBuf},
str::FromStr,
@@ -12,6 +12,9 @@ use std::{
time::Duration,
};
#[cfg(not(target_os = "macos"))]
use std::ffi::OsStr;
use base64::Engine;
use futures_util::StreamExt;
use http::HeaderName;
@@ -23,7 +26,7 @@ use reqwest::{
};
use semver::Version;
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize};
use tauri::{utils::platform::current_exe, Resource};
use tauri::{utils::platform::current_exe, AppHandle, Resource, Runtime};
use time::OffsetDateTime;
use url::Url;
@@ -93,9 +96,15 @@ impl RemoteRelease {
}
pub type OnBeforeExit = Arc<dyn Fn() + Send + Sync + 'static>;
pub type OnBeforeRequest = Arc<dyn Fn(ClientBuilder) -> ClientBuilder + Send + Sync + 'static>;
pub type VersionComparator = Arc<dyn Fn(Version, RemoteRelease) -> bool + Send + Sync>;
type MainThreadClosure = Box<dyn FnOnce() + Send + Sync + 'static>;
type RunOnMainThread =
Box<dyn Fn(MainThreadClosure) -> std::result::Result<(), tauri::Error> + Send + Sync + 'static>;
pub struct UpdaterBuilder {
#[allow(dead_code)]
run_on_main_thread: RunOnMainThread,
app_name: String,
current_version: Version,
config: Config,
@@ -109,21 +118,24 @@ pub struct UpdaterBuilder {
installer_args: Vec<OsString>,
current_exe_args: Vec<OsString>,
on_before_exit: Option<OnBeforeExit>,
configure_client: Option<OnBeforeRequest>,
}
impl UpdaterBuilder {
/// It's prefered to use [`crate::UpdaterExt::updater_builder`] instead of
/// constructing a [`UpdaterBuilder`] with this function yourself
pub fn new(app_name: String, current_version: Version, config: crate::Config) -> Self {
pub(crate) fn new<R: Runtime>(app: &AppHandle<R>, config: crate::Config) -> Self {
let app_ = app.clone();
let run_on_main_thread =
move |f: Box<dyn FnOnce() + Send + Sync + 'static>| app_.run_on_main_thread(f);
Self {
run_on_main_thread: Box::new(run_on_main_thread),
installer_args: config
.windows
.as_ref()
.map(|w| w.installer_args.clone())
.unwrap_or_default(),
current_exe_args: Vec::new(),
app_name,
current_version,
app_name: app.package_info().name.clone(),
current_version: app.package_info().version.clone(),
config,
version_comparator: None,
executable_path: None,
@@ -133,6 +145,7 @@ impl UpdaterBuilder {
timeout: None,
proxy: None,
on_before_exit: None,
configure_client: None,
}
}
@@ -232,6 +245,19 @@ impl UpdaterBuilder {
self
}
/// Allows you to modify the `reqwest` client builder before the HTTP request is sent.
///
/// Note that `reqwest` crate may be updated in minor releases of tauri-plugin-updater.
/// Therefore it's recommended to pin the plugin to at least a minor version when you're using `configure_client`.
///
pub fn configure_client<F: Fn(ClientBuilder) -> ClientBuilder + Send + Sync + 'static>(
mut self,
f: F,
) -> Self {
self.configure_client.replace(Arc::new(f));
self
}
pub fn build(self) -> Result<Updater> {
let endpoints = self
.endpoints
@@ -259,6 +285,7 @@ impl UpdaterBuilder {
};
Ok(Updater {
run_on_main_thread: Arc::new(self.run_on_main_thread),
config: self.config,
app_name: self.app_name,
current_version: self.current_version,
@@ -274,6 +301,7 @@ impl UpdaterBuilder {
headers: self.headers,
extract_path,
on_before_exit: self.on_before_exit,
configure_client: self.configure_client,
})
}
}
@@ -291,6 +319,8 @@ impl UpdaterBuilder {
}
pub struct Updater {
#[allow(dead_code)]
run_on_main_thread: Arc<RunOnMainThread>,
config: Config,
app_name: String,
current_version: Version,
@@ -306,6 +336,7 @@ pub struct Updater {
headers: HeaderMap,
extract_path: PathBuf,
on_before_exit: Option<OnBeforeExit>,
configure_client: Option<OnBeforeRequest>,
#[allow(unused)]
installer_args: Vec<OsString>,
#[allow(unused)]
@@ -330,6 +361,7 @@ impl Updater {
}
let mut remote_release: Option<RemoteRelease> = None;
let mut raw_json: Option<serde_json::Value> = None;
let mut last_error: Option<Error> = None;
for url in &self.endpoints {
// replace {{current_version}}, {{target}} and {{arch}} in the provided URL
@@ -357,14 +389,22 @@ impl Updater {
.replace("{{arch}}", self.arch)
.parse()?;
log::debug!("checking for updates {url}");
let mut request = ClientBuilder::new().user_agent(UPDATER_USER_AGENT);
if let Some(timeout) = self.timeout {
request = request.timeout(timeout);
}
if let Some(ref proxy) = self.proxy {
log::debug!("using proxy {proxy}");
let proxy = reqwest::Proxy::all(proxy.as_str())?;
request = request.proxy(proxy);
}
if let Some(ref configure_client) = self.configure_client {
request = configure_client(request);
}
let response = request
.build()?
.get(url)
@@ -372,25 +412,43 @@ impl Updater {
.send()
.await;
if let Ok(res) = response {
if res.status().is_success() {
// no updates found!
if StatusCode::NO_CONTENT == res.status() {
return Ok(None);
};
match response {
Ok(res) => {
if res.status().is_success() {
// no updates found!
if StatusCode::NO_CONTENT == res.status() {
log::debug!("update endpoint returned 204 No Content");
return Ok(None);
};
match serde_json::from_value::<RemoteRelease>(res.json().await?)
.map_err(Into::into)
{
Ok(release) => {
last_error = None;
remote_release = Some(release);
// we found a relase, break the loop
break;
let update_response: serde_json::Value = res.json().await?;
log::debug!("update response: {update_response:?}");
raw_json = Some(update_response.clone());
match serde_json::from_value::<RemoteRelease>(update_response)
.map_err(Into::into)
{
Ok(release) => {
log::debug!("parsed release response {release:?}");
last_error = None;
remote_release = Some(release);
// we found a release, break the loop
break;
}
Err(err) => {
log::error!("failed to deserialize update response: {err}");
last_error = Some(err)
}
}
Err(err) => last_error = Some(err),
} else {
log::error!(
"update endpoint did not respond with a successful status code"
);
}
}
Err(err) => {
log::error!("failed to check for updates: {err}");
last_error = Some(err.into())
}
}
}
@@ -410,6 +468,7 @@ impl Updater {
let update = if should_update {
Some(Update {
run_on_main_thread: self.run_on_main_thread.clone(),
config: self.config.clone(),
on_before_exit: self.on_before_exit.clone(),
app_name: self.app_name.clone(),
@@ -421,11 +480,13 @@ impl Updater {
download_url: release.download_url(&self.json_target)?.to_owned(),
body: release.notes.clone(),
signature: release.signature(&self.json_target)?.to_owned(),
raw_json: raw_json.unwrap(),
timeout: self.timeout,
proxy: self.proxy.clone(),
headers: self.headers.clone(),
installer_args: self.installer_args.clone(),
current_exe_args: self.current_exe_args.clone(),
configure_client: self.configure_client.clone(),
})
} else {
None
@@ -437,6 +498,8 @@ impl Updater {
#[derive(Clone)]
pub struct Update {
#[allow(dead_code)]
run_on_main_thread: Arc<RunOnMainThread>,
config: Config,
#[allow(unused)]
on_before_exit: Option<OnBeforeExit>,
@@ -454,6 +517,8 @@ pub struct Update {
pub download_url: Url,
/// Signature announced
pub signature: String,
/// The raw version of server's JSON response. Useful if the response contains additional fields that the updater doesn't handle.
pub raw_json: serde_json::Value,
/// Request timeout
pub timeout: Option<Duration>,
/// Request proxy
@@ -470,6 +535,7 @@ pub struct Update {
installer_args: Vec<OsString>,
#[allow(unused)]
current_exe_args: Vec<OsString>,
configure_client: Option<OnBeforeRequest>,
}
impl Resource for Update {}
@@ -498,6 +564,9 @@ impl Update {
let proxy = reqwest::Proxy::all(proxy.as_str())?;
request = request.proxy(proxy);
}
if let Some(ref configure_client) = self.configure_client {
request = configure_client(request);
}
let response = request
.build()?
.get(self.download_url.clone())
@@ -646,6 +715,7 @@ impl Update {
};
if let Some(on_before_exit) = self.on_before_exit.as_ref() {
log::debug!("running on_before_exit hook");
on_before_exit();
}
@@ -814,6 +884,7 @@ impl Update {
#[cfg(feature = "zip")]
if infer::archive::is_gz(bytes) {
log::debug!("extracting AppImage");
// extract the buffer to the tmp_dir
// we extract our signed archive into our final directory without any temp file
let archive = Cursor::new(bytes);
@@ -837,6 +908,7 @@ impl Update {
return Err(Error::BinaryNotFoundInArchive);
}
log::debug!("rewriting AppImage");
return match std::fs::write(&self.extract_path, bytes)
.and_then(|_| std::fs::set_permissions(&self.extract_path, permissions))
{
@@ -890,6 +962,7 @@ impl Update {
fn install_deb(&self, bytes: &[u8]) -> Result<()> {
// First verify the bytes are actually a .deb package
if !infer::archive::is_deb(bytes) {
log::warn!("update is not a valid deb package");
return Err(Error::InvalidUpdaterFormat);
}
@@ -932,6 +1005,7 @@ impl Update {
.status()
{
if status.success() {
log::debug!("installed deb with pkexec");
return Ok(());
}
}
@@ -939,6 +1013,7 @@ impl Update {
// 2. Try zenity or kdialog for a graphical sudo experience
if let Ok(password) = self.get_password_graphically() {
if self.install_with_sudo(deb_path, &password)? {
log::debug!("installed deb with GUI sudo");
return Ok(());
}
}
@@ -951,6 +1026,7 @@ impl Update {
.status()?;
if status.success() {
log::debug!("installed deb with sudo");
Ok(())
} else {
Err(Error::DebInstallFailed)
@@ -1026,42 +1102,86 @@ impl Update {
let cursor = Cursor::new(bytes);
let mut extracted_files: Vec<PathBuf> = Vec::new();
// the first file in the tar.gz will always be
// <app_name>/Contents
let tmp_dir = tempfile::Builder::new()
// Create temp directories for backup and extraction
let tmp_backup_dir = tempfile::Builder::new()
.prefix("tauri_current_app")
.tempdir()?;
// create backup of our current app
std::fs::rename(&self.extract_path, tmp_dir.path())?;
let tmp_extract_dir = tempfile::Builder::new()
.prefix("tauri_updated_app")
.tempdir()?;
let decoder = GzDecoder::new(cursor);
let mut archive = tar::Archive::new(decoder);
std::fs::create_dir(&self.extract_path)?;
// Extract files to temporary directory
for entry in archive.entries()? {
let mut entry = entry?;
// skip the first folder (should be the app name)
let collected_path: PathBuf = entry.path()?.iter().skip(1).collect();
let extraction_path = &self.extract_path.join(collected_path);
let extraction_path = tmp_extract_dir.path().join(&collected_path);
// if something went wrong during the extraction, we should restore previous app
if let Err(err) = entry.unpack(extraction_path) {
for file in extracted_files.iter().rev() {
// delete all the files we extracted
if file.is_dir() {
std::fs::remove_dir(file)?;
} else {
std::fs::remove_file(file)?;
}
}
std::fs::rename(tmp_dir.path(), &self.extract_path)?;
return Err(err.into());
// Ensure parent directories exist
if let Some(parent) = extraction_path.parent() {
std::fs::create_dir_all(parent)?;
}
extracted_files.push(extraction_path.to_path_buf());
if let Err(err) = entry.unpack(&extraction_path) {
// Cleanup on error
std::fs::remove_dir_all(tmp_extract_dir.path()).ok();
return Err(err.into());
}
extracted_files.push(extraction_path);
}
// Try to move the current app to backup
let move_result = std::fs::rename(
&self.extract_path,
tmp_backup_dir.path().join("current_app"),
);
let need_authorization = if let Err(err) = move_result {
if err.kind() == std::io::ErrorKind::PermissionDenied {
true
} else {
std::fs::remove_dir_all(tmp_extract_dir.path()).ok();
return Err(err.into());
}
} else {
false
};
if need_authorization {
log::debug!("app installation needs admin privileges");
// Use AppleScript to perform moves with admin privileges
let apple_script = format!(
"do shell script \"rm -rf '{src}' && mv -f '{new}' '{src}'\" with administrator privileges",
src = self.extract_path.display(),
new = tmp_extract_dir.path().display()
);
let (tx, rx) = std::sync::mpsc::channel();
let res = (self.run_on_main_thread)(Box::new(move || {
let mut script =
osakit::Script::new_from_source(osakit::Language::AppleScript, &apple_script);
script.compile().expect("invalid AppleScript");
let r = script.execute();
tx.send(r).unwrap();
}));
let result = rx.recv().unwrap();
if res.is_err() || result.is_err() {
std::fs::remove_dir_all(tmp_extract_dir.path()).ok();
return Err(Error::Io(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"Failed to move the new app into place",
)));
}
} else {
// Remove existing directory if it exists
if self.extract_path.exists() {
std::fs::remove_dir_all(&self.extract_path)?;
}
// Move the new app to the target path
std::fs::rename(tmp_extract_dir.path(), &self.extract_path)?;
}
let _ = std::process::Command::new("touch")
@@ -1103,6 +1223,8 @@ pub(crate) fn get_updater_arch() -> Option<&'static str> {
Some("armv7")
} else if cfg!(target_arch = "aarch64") {
Some("aarch64")
} else if cfg!(target_arch = "riscv64") {
Some("riscv64")
} else {
None
}
@@ -63,8 +63,6 @@ fn build_app(cwd: &Path, config: &Config, bundle_updater: bool, target: BundleTa
if bundle_updater {
#[cfg(windows)]
command.args(["--bundles", "msi", "nsis"]);
command.args(["--bundles", "updater"]);
} else {
#[cfg(windows)]
command.args(["--bundles", target.name()]);
@@ -67,8 +67,6 @@ mod v1 {
if bundle_updater {
#[cfg(windows)]
command.args(["--bundles", "msi", "nsis"]);
command.args(["--bundles", "updater"]);
} else {
#[cfg(windows)]
command.args(["--bundles", target.name()]);
@@ -173,8 +171,6 @@ mod v2 {
if bundle_updater {
#[cfg(windows)]
command.args(["--bundles", "msi", "nsis"]);
command.args(["--bundles", "updater"]);
} else {
#[cfg(windows)]
command.args(["--bundles", target.name()]);

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