Merge branch 'dev' into feat/ios-ci-signing

This commit is contained in:
Lucas Nogueira 2024-07-12 10:52:03 -03:00
commit 14727923cf
No known key found for this signature in database
GPG Key ID: 7C32FCA95C8C95D7
203 changed files with 9494 additions and 2623 deletions

View File

@ -0,0 +1,6 @@
---
"tauri-cli": "patch:bug"
"@tauri-apps/cli": "patch:bug"
---
Fix `migrate` command, migrating incorrect permissions for `clipboard`.

View File

@ -0,0 +1,14 @@
---
"tauri-cli": "patch:enhance"
"@tauri-apps/cli": "patch:enhance"
---
Enhance `tauri migrate` to also migrate variables like `appWindow`:
```ts
import { appWindow } from '@tauri-apps/api/window'
```
will become:
```ts
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
const appWindow = getCurrentWebviewWindow()
```

View File

@ -0,0 +1,6 @@
---
"tauri-cli": "patch:bug"
"@tauri-apps/cli": "patch:bug"
---
Fix `tauri migrate` incorrectly migrating `@tauri-apps/api/tauri` module to just `core` and `@tauri-apps/api/window` to just `webviewWindow`.

View File

@ -0,0 +1,6 @@
---
"tauri-cli": "patch:bug"
"@tauri-apps/cli": "patch:bug"
---
Fix parsing of cargo profile when using `--profile=<profile>` syntax.

View File

@ -0,0 +1,6 @@
---
"tauri-cli": "patch:bug"
"@tauri-apps/cli": "patch:bug"
---
Fix cli failing to detect the correct cargo target directory when using cargo `--target-dir` flag with `tauri build` or `tauri dev`

View File

@ -0,0 +1,15 @@
---
"tauri": "patch:breaking"
---
Added `Emitter` and `Listener` traits that defines what an emitter or a listener can do, this however comes with a few breaking changes:
- Removed `Manager::listen_any`, use `Listener::listen_any` instead.
- Removed `Manager::once_any`, use `Listener::once_any` instead.
- Removed `Manager::unlisten`, use `Listener::unlisten` instead.
- Removed `Manager::emit`, use `Emitter::emit` instead.
- Removed `Manager::emit_to`, use `Emitter::emit_to` instead.
- Removed `Manager::emit_filter`, use `Emitter::emit_filter` instead.
- Removed `App/AppHandle::listen`, `WebviewWindow::listen`, `Window::listen` and `Webview::listen`, use `Listener::listen` instead.
- Removed `App/AppHandle::once`, `WebviewWindow::once`, `Window::once` and `Webview::once`, use `Listener::once` instead.
- Removed `App/AppHandle::unlisten`, `WebviewWindow::unlisten`, `Window::unlisten` and `Webview::unlisten`, use `Listener::unlisten` instead.

View File

@ -0,0 +1,5 @@
---
"tauri": "patch:enhance"
---
Mark `AppHandle::restart` and `process::restart` as [diverging functions](https://doc.rust-lang.org/rust-by-example/fn/diverging.html).

View File

@ -0,0 +1,17 @@
---
"@tauri-apps/api": "patch:breaking"
---
Renamed drag and drop events in `TauriEvent` enum to better convey when they are triggered:
- `TauriEvent.DRAG` -> `TauriEvent.DRAG_ENTER`
- `TauriEvent.DROP_OVER` -> `TauriEvent.DRAG_OVER`
- `TauriEvent.DROP` -> `TauriEvent.DRAG_DROP`
- `TauriEvent.DROP_CANCELLED` -> `TauriEvent::DRAG_LEAVE`
Also the `type` field values in `Window/Webview/WebviewWindow.onDropEvent` and `DragDropEvent` have changed:
- `dragged` -> `enter`
- `dragOver` -> `over`
- `dropped` -> `drop`
- `cancelled` -> `leave`

View File

@ -0,0 +1,17 @@
---
"tauri": "patch:breaking"
---
Renamed `DragDropEvent` enum variants to better convey when they are triggered:
- `DragDropEvent::Dragged` -> `DragDropEvent::Enter`
- `DragDropEvent::DragOver` -> `DragDropEvent::Over`
- `DragDropEvent::Dropped` -> `DragDropEvent::Drop`
- `DragDropEvent::Cancelled` -> `DragDropEvent::Leave`
This also comes with a change in the events being emitted to JS and Rust event listeners:
- `tauri://drag` -> `tauri://drag-enter`
- `tauri://drop-over` -> `tauri://drag-over`
- `tauri://drop` -> `tauri://drag-drop`
- `tauri://drag-cancelled` -> `tauri://drag-leave`

View File

@ -0,0 +1,6 @@
---
"tauri-bundler": "patch:feat"
"tauri-cli": "patch:feat"
---
Upgraded the WiX version to 3.14 which fixes vulnerability issues and adds support for Arm targets.

View File

@ -0,0 +1,5 @@
---
"tauri-codegen": patch:bug
---
Fixes Info.plist rewriting always triggering build to rerun.

View File

@ -0,0 +1,5 @@
---
tauri-bundler: patch:bug
---
On macOS, the bundler will now correctly print a warning when the updater is enabled while the `.app` bundle is disabled.

View File

@ -0,0 +1,5 @@
---
'@tauri-apps/api': 'patch:bug'
---
Fix `once` doesn't detached after one callback if event handler throws

View File

@ -0,0 +1,5 @@
---
tauri-bundler: patch:enhance
---
Added a public property to the msi to tell the installer to launch the app after installation. This was added for the updater plugin.

View File

@ -0,0 +1,6 @@
---
"tauri-macros": "patch"
"tauri-codegen": "patch"
---
Add support for `test = true` in `generate_context!` macro to skip some code generation that could affect some tests, for now it only skips empedding a plist on macOS.

View File

@ -0,0 +1,13 @@
---
"@tauri-apps/api": "patch:breaking"
"tauri": "patch:breaking"
---
Renamed the JS `getCurrent` and `getAll` functions to a clearer name to avoid ambiguity:
- `getCurrent` in `window` module has been renamed to `getCurrentWindow`
- `getCurrent` in `webview` module has been renamed to `getCurrentWebview`
- `getCurrent` in `webviewWindow` module has been renamed to `getCurrentWebviewWindow`
- `getAll` in `window` module has been renamed to `getAllWindows`
- `getAll` in `webview` module has been renamed to `getAllWebviews`
- `getAll` in `webviewWindow` module has been renamed to `getAllWebviewWindows`

View File

@ -0,0 +1,5 @@
---
"tauri-build": patch:bug
---
Fix build script rerun-if-changed instruction if Info.plist do not exist next to tauri.conf.json.

View File

@ -0,0 +1,5 @@
---
"tauri": "patch:bug"
---
Fix `InvokeBody::deserialize` method deserialization for `InvokeBody::Raw` variant

View File

@ -0,0 +1,9 @@
---
"@tauri-apps/cli": patch:bug
"tauri-cli": patch:bug
---
Removed alpha channel from default icons in iOS template to comply with Apple's human interface guideline
(https://developer.apple.com/design/human-interface-guidelines/app-icons), because
transparent icons with alpha channel are not allowed, and will be rejected
upon upload to Apple appstore.

View File

@ -0,0 +1,5 @@
---
"tauri": major:breaking
---
Add `TSend` generic to `ipc::Channel` for typesafe `send` calls and type inspection in `tauri-specta`

View File

@ -0,0 +1,5 @@
---
"tauri": "patch:bug"
---
Fix deserialization of raw invoke requests when using `isolation` pattern.

View File

@ -0,0 +1,5 @@
---
"tauri-build": "patch:enhance"
---
Fix delete app data button gone on higher scaling (>= 1.5)

View File

@ -0,0 +1,5 @@
---
"tauri-bundler": "patch:breaking"
---
Changed NSIS installer hooks from `!define` to `!macro`

View File

@ -0,0 +1,5 @@
---
'tauri-bundler': 'patch:bug'
---
Fix NSIS installer failing to launch apps that contain spaces after installation.

View File

@ -0,0 +1,5 @@
---
"tauri-bundler": "patch:breaking"
---
Changed NSIS start menu shortcut to be placed directly inside `%AppData%\Microsoft\Windows\Start Menu\Programs` without an additional folder. You can get the old behavior by setting `bundle > nsis > startMenuFolder` to the same value as your `productName`

View File

@ -0,0 +1,6 @@
---
"tauri-utils": "patch:feat"
"tauri-bundler": "patch:feat"
---
Add `bundle > nsis > startMenuFolder` option to customize start menu folder for NSIS installer

View File

@ -0,0 +1,5 @@
---
'@tauri-apps/api': 'patch:enhance'
---
Use `EventName` on `Window`, `Webview` and `WebviewWindow`'s `once` so you can get auto complete for tauri's built-in events

View File

@ -0,0 +1,5 @@
---
"tauri-utils": patch:enhance
---
Mark ACL `permissions` array with unique items

View File

@ -7,6 +7,8 @@
".changes/acl-scope-refactor.md",
".changes/acl-urlpattern.md",
".changes/allow-recursive-asset-scope-on-file-drop-directory.md",
".changes/android-warning.md",
".changes/api-app-defaultWindowIcon.md",
".changes/api-isTauri-type.md",
".changes/api-isTauri.md",
".changes/api-position-size-args.md",
@ -21,6 +23,7 @@
".changes/app-handle-set-activation-policy.md",
".changes/app-manifest.md",
".changes/app-region-drag.md",
".changes/appimage-duplicate-desktop-file.md",
".changes/assets-setup.md",
".changes/beta.md",
".changes/better-error-for-invalid-plugin-config.md",
@ -28,13 +31,17 @@
".changes/build-resource-target-same-src.md",
".changes/build-schema-generation.md",
".changes/bunderl-installer-hooks.md",
".changes/bundler-compression-option.md",
".changes/bundler-deep-link-reg-path.md",
".changes/bundler-homepage-url.md",
".changes/bundler-license.md",
".changes/bundler-long_description.md",
".changes/bundler-nsis-deep-links.md",
".changes/bundler-nsis-license-file-bom.md",
".changes/bundler-nsis-tauri-utils.md",
".changes/bundler-r-flag.md",
".changes/bundler-resources-unix.md",
".changes/bundler-rpm-filename.md",
".changes/bundler-rpm-license.md",
".changes/bundler-shortcuts-updating.md",
".changes/capabilities-multiwebview.md",
@ -53,13 +60,16 @@
".changes/cli-frontend-dist-expected-path.md",
".changes/cli-icon-non-0-exit.md",
".changes/cli-include-dir-cargo-manifest-dir.md",
".changes/cli-migrate-clipboard-globalShortcut.md",
".changes/cli-migrate-non-utf8.md",
".changes/cli-migrate-unknown-plugins.md",
".changes/cli-mobile-init-partition.md",
".changes/cli-openssl-cargo-mobile2-removal.md",
".changes/cli-perserve-cargo-bin-name.md",
".changes/cli-plugin-android-init.md",
".changes/cli-plugin-template-updates.md",
".changes/cli-plugins-migrate.md",
".changes/cli-profile-parse-syntax.md",
".changes/cli-update-deps-fix-log.md",
".changes/cli-updater-unkown-fields.md",
".changes/cli-windows-build-tools-detect-utf8.md",
@ -99,6 +109,7 @@
".changes/default-generic-for-menu.md",
".changes/default-generic-for-tray.md",
".changes/dev-fn.md",
".changes/dirs-next-dirs.md",
".changes/downgrade-minisign.md",
".changes/drop-nsis-applicationid.md",
".changes/enhance-event-emit.md",
@ -109,12 +120,15 @@
".changes/expose-js-image.md",
".changes/fix-acl-webview-check.md",
".changes/fix-add-child-deadlock.md",
".changes/fix-api-export-mocks-module.md",
".changes/fix-build-script-rerun-macos.md",
".changes/fix-capability-schema-definitions.md",
".changes/fix-capability-totokens.md",
".changes/fix-channel-buffer-processing.md",
".changes/fix-channel-ipc-response.md",
".changes/fix-clear-residual-listeners.md",
".changes/fix-cli-add-more-plugins.md",
".changes/fix-cli-icon-svg-render-text.md",
".changes/fix-cli-migration-http-acl.md",
".changes/fix-codegen-rerun-if-changed.md",
".changes/fix-config-arg.md",
@ -124,11 +138,13 @@
".changes/fix-inner-size.md",
".changes/fix-invoke-devtools-by-hotkey.md",
".changes/fix-ios-dev-logs.md",
".changes/fix-ios-plugin-xcode-deployment-target.md",
".changes/fix-ipc-error-json.md",
".changes/fix-macos-deep-link-cfbundleurlname.md",
".changes/fix-menu-remove-api.md",
".changes/fix-metadata-on-close.md",
".changes/fix-migrate-updater.md",
".changes/fix-missing-depends.md",
".changes/fix-mobile-cmd-case.md",
".changes/fix-mobile-process-spawn.md",
".changes/fix-pnpm-check.md",
@ -149,8 +165,10 @@
".changes/fix-window-center-work-area.md",
".changes/fix-window-destroy-deadlock.md",
".changes/fix-window-inner-size-crash.md",
".changes/fix-wix-autostart-after-update.md",
".changes/global-api-script-path-plugins.md",
".changes/handle-empty-permissions.md",
".changes/hardened-runtime-option.md",
".changes/hide-windows-on-cleanup.md",
".changes/http-v1.md",
".changes/ico-featrue-flags.md",
@ -158,7 +176,11 @@
".changes/image-rgba-uint8array.md",
".changes/image-size-refactor.md",
".changes/improve-errors-for-missing-links-property.md",
".changes/include-image-macro-codegen.md",
".changes/include-image-macro.md",
".changes/inline-plugins.md",
".changes/invoke-body-raw-deserialization.md",
".changes/ios-non-transparent-icons.md",
".changes/ios-signing-optional.md",
".changes/ipc-allow-headers.md",
".changes/ipc-only-main-frame.md",
@ -166,22 +188,33 @@
".changes/ipc-request-param-refactor.md",
".changes/isolation-pattern-key-extractable.md",
".changes/isolation-script-remove-itself.md",
".changes/linux-gtk-app-id.md",
".changes/mobile-use-identifier-as-id.md",
".changes/mobile-watcher.md",
".changes/monitor-from-point-js.md",
".changes/monitor-from-point.md",
".changes/multiwebview-bounds-fixes.md",
".changes/nsis-append-product-name.md",
".changes/nsis-deep-link-uninstall.md",
".changes/nsis-delete-data-button-dpi.md",
".changes/nsis-dpi-aware.md",
".changes/nsis-esitimated-size-unit.md",
".changes/nsis-migrate-shortcut.md",
".changes/nsis-no-compression.md",
".changes/nsis-run-as-user.md",
".changes/nsis-shortcuts-regression.md",
".changes/nsis-start-menu-folder-breaking.md",
".changes/nsis-start-menu-folder.md",
".changes/path-result-error-rexport.md",
".changes/permission-platforms.md",
".changes/permission-schema-unique-item.md",
".changes/permission-table.md",
".changes/plugin-global-api-script.md",
".changes/plugin-init-script-context.md",
".changes/plugin-ios-xcode-project.md",
".changes/preserve-channel-order.md",
".changes/progress-bar-state-refactor.md",
".changes/raw-window-handle-std.md",
".changes/re-export-progress-bar-status.md",
".changes/rect-strcut.md",
".changes/refactor-capabilities-schema.md",
@ -207,6 +240,7 @@
".changes/rwh-06.md",
".changes/schema_str.md",
".changes/script-older-os.md",
".changes/separate-updater-field.md",
".changes/set-auto-resize.md",
".changes/set-zoom.md",
".changes/skip-webview-install-mod.md",
@ -236,8 +270,10 @@
".changes/tauri-window-origin-default-to-null.md",
".changes/tray-icon-event.md",
".changes/tray-icon-rect.md",
".changes/tray-menu-default-permission.md",
".changes/tray-rect.md",
".changes/truncate-before-write-buildtask.md",
".changes/undecorated-resizing.md",
".changes/unstable-child-webview.md",
".changes/update-acl-paths-cli.md",
".changes/update-app-template-capabilities-conf.md",
@ -248,12 +284,16 @@
".changes/url-result.md",
".changes/utils-bundle-target-all.md",
".changes/utils-bundle-type-all.md",
".changes/utils-compression-option.md",
".changes/utils-debug-eprintln.md",
".changes/utils-installer-hooks.md",
".changes/utils-named-capability-file.md",
".changes/utils-remove-asset-trait.md",
".changes/utils-sign-command.md",
".changes/webview-bounds.md",
".changes/webview-builder-on-download.md",
".changes/webview-navigate-result.md",
".changes/windows-rs-0.57.md",
".changes/wry-0.36.md",
".changes/wry-0.37.md",
".changes/wry-0.38.md",

View File

@ -0,0 +1,5 @@
---
"tauri-codegen": patch:bug
---
Fix icon rewriting always triggering build to rerun due to conflicts between file names.

View File

@ -0,0 +1,5 @@
---
"tauri-runtime-wry": "patch:bug"
---
Fix window edge not working after setting resizable false and decorated false dynamically

View File

@ -0,0 +1,8 @@
---
"tauri-utils": patch:breaking
"tauri-bundler": patch:breaking
"tauri-cli": patch:breaking
"@tauri-apps/cli": patch:breaking
---
Move updater target from `bundle > targets` to a separate field `bundle > createUpdaterArtifacts`

View File

@ -0,0 +1,6 @@
---
'tauri': 'patch:feat'
'@tauri-apps/api': 'patch:feat'
---
Add a new method to set title bar style dynamically on macOS.

View File

@ -0,0 +1,5 @@
---
"tauri-utils": "patch:feat"
---
Add `RawIsolationPayload::content_type` method.

View File

@ -0,0 +1,5 @@
---
"tauri": "patch:breaking"
---
Changed `WebviewWindow::navigate` and `Webview::navigate` method signature to return a `Result`

View File

@ -0,0 +1,5 @@
---
"tauri": patch:bug
---
Move `PluginApi::register_ios_plugin` behind the `wry` Cargo feature as `Webview::with_webview` is only available when that feature is enabled.

View File

@ -18,3 +18,5 @@ jobs:
id: covector
with:
command: 'status'
token: ${{ secrets.GITHUB_TOKEN }}
comment: true

View File

@ -87,9 +87,10 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }}
CARGO_AUDIT_OPTIONS: ${{ secrets.CARGO_AUDIT_OPTIONS }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
command: 'version-or-publish'
token: ${{ secrets.GITHUB_TOKEN }}
createRelease: true
recognizeContributors: true
- name: Create Pull Request With Versions Bumped
if: steps.covector.outputs.commandRan == 'version'
@ -116,18 +117,14 @@ jobs:
if: |
steps.covector.outputs.successfulPublish == 'true' &&
contains(steps.covector.outputs.packagesPublished, '@tauri-apps/cli')
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.ORG_TAURI_BOT_PAT }}
event-type: publish-js-cli
client-payload: >-
{"releaseId": "${{ steps.covector.outputs['-tauri-apps-cli-releaseId'] }}" }
run: gh workflow run 31554138 -r dev -f releaseId=${{ steps.covector.outputs['-tauri-apps-cli-releaseId'] }}
env:
GITHUB_TOKEN: ${{ secrets.ORG_TAURI_BOT_PAT }}
- name: Trigger `tauri-cli` publishing workflow
if: |
steps.covector.outputs.successfulPublish == 'true' &&
contains(steps.covector.outputs.packagesPublished, 'tauri-cli')
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.ORG_TAURI_BOT_PAT }}
event-type: publish-clirs
run: gh workflow run 31554139 -r dev
env:
GITHUB_TOKEN: ${{ secrets.ORG_TAURI_BOT_PAT }}

104
Cargo.lock generated
View File

@ -810,6 +810,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "erased-serde"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d"
dependencies = [
"serde",
"typeid",
]
[[package]]
name = "errno"
version = "0.3.9"
@ -3198,6 +3208,17 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-untagged"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2676ba99bd82f75cae5cbd2c8eda6fa0b8760f18978ea840e980dd5567b5c5b6"
dependencies = [
"erased-serde",
"serde",
"typeid",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
@ -3585,9 +3606,9 @@ dependencies = [
[[package]]
name = "tao"
version = "0.28.0"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12a8121bd5721ebbbe0889f8286d5824673beeb04071519b68916fbed04f3093"
checksum = "ea538df05fbc2dcbbd740ba0cfe8607688535f4798d213cbbfa13ce494f3451f"
dependencies = [
"bitflags 2.5.0",
"cocoa",
@ -3616,8 +3637,8 @@ dependencies = [
"tao-macros",
"unicode-segmentation",
"url",
"windows 0.56.0",
"windows-core 0.56.0",
"windows 0.57.0",
"windows-core 0.57.0",
"windows-version",
"x11-dl",
]
@ -3641,7 +3662,7 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
[[package]]
name = "tauri"
version = "2.0.0-beta.22"
version = "2.0.0-beta.23"
dependencies = [
"anyhow",
"bytes",
@ -3710,7 +3731,7 @@ dependencies = [
[[package]]
name = "tauri-build"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
dependencies = [
"anyhow",
"cargo_toml",
@ -3732,7 +3753,7 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
dependencies = [
"base64 0.22.1",
"brotli",
@ -3769,7 +3790,7 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@ -3781,7 +3802,7 @@ dependencies = [
[[package]]
name = "tauri-plugin"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
dependencies = [
"anyhow",
"glob",
@ -3796,7 +3817,7 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.0.0-beta.18"
version = "2.0.0-beta.19"
dependencies = [
"dpi",
"gtk",
@ -3813,7 +3834,7 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.0.0-beta.18"
version = "2.0.0-beta.19"
dependencies = [
"cocoa",
"gtk",
@ -3836,7 +3857,7 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
dependencies = [
"aes-gcm",
"brotli",
@ -3859,6 +3880,7 @@ dependencies = [
"schemars",
"semver",
"serde",
"serde-untagged",
"serde_json",
"serde_with",
"serialize-to-javascript",
@ -4222,6 +4244,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "typeid"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf"
[[package]]
name = "typenum"
version = "1.17.0"
@ -4611,8 +4639,8 @@ dependencies = [
"webview2-com-sys",
"windows 0.57.0",
"windows-core 0.57.0",
"windows-implement 0.57.0",
"windows-interface 0.57.0",
"windows-implement",
"windows-interface",
]
[[package]]
@ -4690,16 +4718,6 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
dependencies = [
"windows-core 0.56.0",
"windows-targets 0.52.5",
]
[[package]]
name = "windows"
version = "0.57.0"
@ -4719,41 +4737,18 @@ dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "windows-core"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6"
dependencies = [
"windows-implement 0.56.0",
"windows-interface 0.56.0",
"windows-result",
"windows-targets 0.52.5",
]
[[package]]
name = "windows-core"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
dependencies = [
"windows-implement 0.57.0",
"windows-interface 0.57.0",
"windows-implement",
"windows-interface",
"windows-result",
"windows-targets 0.52.5",
]
[[package]]
name = "windows-implement"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
]
[[package]]
name = "windows-implement"
version = "0.57.0"
@ -4765,17 +4760,6 @@ dependencies = [
"syn 2.0.66",
]
[[package]]
name = "windows-interface"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
]
[[package]]
name = "windows-interface"
version = "0.57.0"

View File

@ -52,7 +52,8 @@
"type": "array",
"items": {
"$ref": "#/definitions/PermissionEntry"
}
},
"uniqueItems": true
},
"platforms": {
"description": "Limit which target platforms this capability applies to.\n\n By default all platforms are targeted.\n\n ## Example\n\n `[\"macOS\",\"windows\"]`",

View File

@ -21,7 +21,7 @@
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"description": "Human-readable description of what the permission does.\n Tauri internal convention is to use <h4> headings in markdown content\n for Tauri documentation generation purposes.",
"type": [
"string",
"null"

View File

@ -1,5 +1,17 @@
# Changelog
## \[2.0.0-beta.18]
### Enhancements
- [`35110dba2`](https://www.github.com/tauri-apps/tauri/commit/35110dba21d7db0f155c45da58b41c9ca4d5853c) ([#10106](https://www.github.com/tauri-apps/tauri/pull/10106)) Fix delete app data button gone on higher scaling (>= 1.5)
### Dependencies
- Upgraded to `tauri-utils@2.0.0-beta.18`
- Upgraded to `tauri-codegen@2.0.0-beta.18`
- [`f955f7b49`](https://www.github.com/tauri-apps/tauri/commit/f955f7b4903bcea376c0a8b430736f66c8cebf56) ([#9929](https://www.github.com/tauri-apps/tauri/pull/9929)) Switch from `dirs_next` to `dirs` as `dirs_next` is now unmaintained while `dirs` is
## \[2.0.0-beta.17]
### Enhancements

View File

@ -1,6 +1,6 @@
[package]
name = "tauri-build"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
description = "build time code to pair with https://crates.io/crates/tauri"
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@ -28,8 +28,8 @@ rustdoc-args = [ "--cfg", "docsrs" ]
[dependencies]
anyhow = "1"
quote = { version = "1", optional = true }
tauri-codegen = { version = "2.0.0-beta.17", path = "../tauri-codegen", optional = true }
tauri-utils = { version = "2.0.0-beta.17", path = "../tauri-utils", features = [ "build", "resources" ] }
tauri-codegen = { version = "2.0.0-beta.18", path = "../tauri-codegen", optional = true }
tauri-utils = { version = "2.0.0-beta.18", path = "../tauri-utils", features = [ "build", "resources" ] }
cargo_toml = "0.17"
serde = "1"
serde_json = "1"

View File

@ -115,10 +115,12 @@ impl CodegenContext {
}
#[cfg(target_os = "macos")]
println!(
"cargo:rerun-if-changed={}",
config_parent.join("Info.plist").display()
);
{
let info_plist_path = config_parent.join("Info.plist");
if info_plist_path.exists() {
println!("cargo:rerun-if-changed={}", info_plist_path.display());
}
}
let code = context_codegen(ContextData {
dev: crate::is_dev(),
@ -129,6 +131,7 @@ impl CodegenContext {
root: quote::quote!(::tauri),
capabilities: self.capabilities,
assets: None,
test: false,
})?;
// get the full output file path

View File

@ -156,7 +156,7 @@ fn copy_dir(from: &Path, to: &Path) -> Result<()> {
// Copies the framework under `{src_dir}/{framework}.framework` to `{dest_dir}/{framework}.framework`.
fn copy_framework_from(src_dir: &Path, framework: &str, dest_dir: &Path) -> Result<bool> {
let src_name = format!("{}.framework", framework);
let src_name = format!("{framework}.framework");
let src_path = src_dir.join(&src_name);
if src_path.exists() {
copy_dir(&src_path, &dest_dir.join(&src_name))?;
@ -168,12 +168,8 @@ fn copy_framework_from(src_dir: &Path, framework: &str, dest_dir: &Path) -> Resu
// Copies the macOS application bundle frameworks to the target folder
fn copy_frameworks(dest_dir: &Path, frameworks: &[String]) -> Result<()> {
std::fs::create_dir_all(dest_dir).with_context(|| {
format!(
"Failed to create frameworks output directory at {:?}",
dest_dir
)
})?;
std::fs::create_dir_all(dest_dir)
.with_context(|| format!("Failed to create frameworks output directory at {dest_dir:?}"))?;
for framework in frameworks.iter() {
if framework.ends_with(".framework") {
let src_path = PathBuf::from(framework);
@ -469,7 +465,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
let mut android_package_prefix = String::new();
for (i, w) in s.enumerate() {
if i == last {
println!("cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_APP_NAME={}", w);
println!("cargo:rustc-env=TAURI_ANDROID_PACKAGE_NAME_APP_NAME={w}");
} else {
android_package_prefix.push_str(w);
android_package_prefix.push('_');

View File

@ -55,9 +55,9 @@ dependencies {"
app_build_gradle.push_str("\n}");
if let Some(version) = config.version.as_ref() {
app_tauri_properties.push(format!("tauri.android.versionName={}", version));
app_tauri_properties.push(format!("tauri.android.versionName={version}"));
if let Some(version_code) = config.bundle.android.version_code.as_ref() {
app_tauri_properties.push(format!("tauri.android.versionCode={}", version_code));
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
} else if let Ok(version) = Version::parse(version) {
let mut version_code = version.major * 1000000 + version.minor * 1000 + version.patch;
@ -76,7 +76,7 @@ dependencies {"
));
}
app_tauri_properties.push(format!("tauri.android.versionCode={}", version_code));
app_tauri_properties.push(format!("tauri.android.versionCode={version_code}"));
}
}

View File

@ -1,5 +1,19 @@
# Changelog
## \[2.0.0-beta.18]
### New Features
- [`5b769948a`](https://www.github.com/tauri-apps/tauri/commit/5b769948a81cac333f64c870a470ba6525bd5cd3) ([#9959](https://www.github.com/tauri-apps/tauri/pull/9959)) Add `include_image_codegen` function to help embedding instances of `Image` struct at compile-time in rust to be used with window, menu or tray icons.
### Bug Fixes
- [`1f6e478c8`](https://www.github.com/tauri-apps/tauri/commit/1f6e478c842a16219798b9962718e9ddb969c041) ([#9878](https://www.github.com/tauri-apps/tauri/pull/9878)) Fixes Info.plist rewriting always triggering build to rerun.
### Dependencies
- Upgraded to `tauri-utils@2.0.0-beta.18`
## \[2.0.0-beta.17]
### What's Changed

View File

@ -1,6 +1,6 @@
[package]
name = "tauri-codegen"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
description = "code generation meant to be consumed inside of `tauri` through `tauri-build` or `tauri-macros`"
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@ -20,7 +20,7 @@ quote = "1"
syn = "2"
serde = { version = "1", features = [ "derive" ] }
serde_json = "1"
tauri-utils = { version = "2.0.0-beta.17", path = "../tauri-utils", features = [ "build" ] }
tauri-utils = { version = "2.0.0-beta.18", path = "../tauri-utils", features = [ "build" ] }
thiserror = "1"
walkdir = "2"
brotli = { version = "3", optional = true, default-features = false, features = [ "std" ] }

View File

@ -43,6 +43,8 @@ pub struct ContextData {
pub capabilities: Option<Vec<PathBuf>>,
/// The custom assets implementation
pub assets: Option<Expr>,
/// Skip runtime-only types generation for tests (e.g. embed-plist usage).
pub test: bool,
}
fn inject_script_hashes(document: &NodeRef, key: &AssetKey, csp_hashes: &mut CspHashes) {
@ -140,8 +142,12 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
root,
capabilities: additional_capabilities,
assets,
test,
} = data;
#[allow(unused_variables)]
let running_tests = test;
let target = std::env::var("TAURI_ENV_TARGET_TRIPLE")
.as_deref()
.map(Target::from_triple)
@ -183,8 +189,7 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
let assets_path = config_parent.join(path);
if !assets_path.exists() {
panic!(
"The `frontendDist` configuration is set to `{:?}` but this path doesn't exist",
path
"The `frontendDist` configuration is set to `{path:?}` but this path doesn't exist"
)
}
EmbeddedAssets::new(assets_path, &options, map_core_assets(&options))?
@ -216,7 +221,8 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
"icons/icon.ico",
);
if icon_path.exists() {
ico_icon(&root, &out_dir, &icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
ico_icon(&root, &out_dir, &icon_path, "default-window-icon.png")
.map(|i| quote!(::std::option::Option::Some(#i)))?
} else {
let icon_path = find_icon(
&config,
@ -224,7 +230,8 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
|i| i.ends_with(".png"),
"icons/icon.png",
);
png_icon(&root, &out_dir, &icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
png_icon(&root, &out_dir, &icon_path, "default-window-icon.png")
.map(|i| quote!(::std::option::Option::Some(#i)))?
}
} else {
// handle default window icons for Unix targets
@ -234,7 +241,8 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
|i| i.ends_with(".png"),
"icons/icon.png",
);
png_icon(&root, &out_dir, &icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
png_icon(&root, &out_dir, &icon_path, "default-window-icon.png")
.map(|i| quote!(::std::option::Option::Some(#i)))?
}
};
@ -253,7 +261,7 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
"icons/icon.png",
);
}
raw_icon(&out_dir, &icon_path)?
raw_icon(&out_dir, &icon_path, "dev-macos-icon.png")?
} else {
quote!(::std::option::Option::None)
};
@ -282,7 +290,7 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
let with_tray_icon_code = if target.is_desktop() {
if let Some(tray) = &config.app.tray_icon {
let tray_icon_icon_path = config_parent.join(&tray.icon_path);
image_icon(&root, &out_dir, &tray_icon_icon_path)
image_icon(&root, &out_dir, &tray_icon_icon_path, "tray-icon")
.map(|i| quote!(context.set_tray_icon(Some(#i));))?
} else {
quote!()
@ -292,7 +300,7 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
};
#[cfg(target_os = "macos")]
let info_plist = if target == Target::MacOS && dev {
let info_plist = if target == Target::MacOS && dev && !running_tests {
let info_plist_path = config_parent.join("Info.plist");
let mut info_plist = if info_plist_path.exists() {
plist::Value::from_file(&info_plist_path)
@ -307,17 +315,23 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
}
if let Some(version) = &config.version {
plist.insert("CFBundleShortVersionString".into(), version.clone().into());
}
let format =
time::format_description::parse("[year][month][day].[hour][minute][second]").unwrap();
if let Ok(build_number) = time::OffsetDateTime::now_utc().format(&format) {
plist.insert("CFBundleVersion".into(), build_number.into());
plist.insert("CFBundleVersion".into(), version.clone().into());
}
}
let plist_file = out_dir.join("Info.plist");
let mut plist_contents = std::io::BufWriter::new(Vec::new());
info_plist
.to_file_xml(out_dir.join("Info.plist"))
.expect("failed to write Info.plist");
.to_writer_xml(&mut plist_contents)
.expect("failed to serialize plist");
let plist_contents =
String::from_utf8_lossy(&plist_contents.into_inner().unwrap()).into_owned();
if plist_contents != std::fs::read_to_string(&plist_file).unwrap_or_default() {
std::fs::write(&plist_file, &plist_contents).expect("failed to write Info.plist");
}
quote!({
tauri::embed_plist::embed_info_plist!(concat!(std::env!("OUT_DIR"), "/Info.plist"));
})

View File

@ -8,7 +8,10 @@ use quote::{quote, ToTokens};
use std::path::Path;
use syn::{punctuated::Punctuated, Ident, PathArguments, PathSegment, Token};
pub fn include_image_codegen(path: &Path) -> EmbeddedAssetsResult<TokenStream> {
pub fn include_image_codegen(
path: &Path,
out_file_name: &str,
) -> EmbeddedAssetsResult<TokenStream> {
let out_dir = ensure_out_dir()?;
let mut segments = Punctuated::new();
@ -21,19 +24,20 @@ pub fn include_image_codegen(path: &Path) -> EmbeddedAssetsResult<TokenStream> {
segments,
};
image_icon(&root.to_token_stream(), &out_dir, path)
image_icon(&root.to_token_stream(), &out_dir, path, out_file_name)
}
pub(crate) fn image_icon(
root: &TokenStream,
out_dir: &Path,
path: &Path,
out_file_name: &str,
) -> EmbeddedAssetsResult<TokenStream> {
let extension = path.extension().unwrap_or_default();
if extension == "ico" {
ico_icon(root, out_dir, path)
ico_icon(root, out_dir, path, out_file_name)
} else if extension == "png" {
png_icon(root, out_dir, path)
png_icon(root, out_dir, path, out_file_name)
} else {
Err(EmbeddedAssetsError::InvalidImageExtension {
extension: extension.into(),
@ -42,19 +46,22 @@ pub(crate) fn image_icon(
}
}
pub(crate) fn raw_icon(out_dir: &Path, path: &Path) -> EmbeddedAssetsResult<TokenStream> {
pub(crate) fn raw_icon(
out_dir: &Path,
path: &Path,
out_file_name: &str,
) -> EmbeddedAssetsResult<TokenStream> {
let bytes =
std::fs::read(path).unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e));
let out_path = out_dir.join(path.file_name().unwrap());
let out_path = out_dir.join(out_file_name);
write_if_changed(&out_path, &bytes).map_err(|error| EmbeddedAssetsError::AssetWrite {
path: path.to_owned(),
error,
})?;
let icon_path = path.file_name().unwrap().to_str().unwrap().to_string();
let icon = quote!(::std::option::Option::Some(
include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_path)).to_vec()
include_bytes!(concat!(std::env!("OUT_DIR"), "/", #out_file_name)).to_vec()
));
Ok(icon)
}
@ -63,6 +70,7 @@ pub(crate) fn ico_icon(
root: &TokenStream,
out_dir: &Path,
path: &Path,
out_file_name: &str,
) -> EmbeddedAssetsResult<TokenStream> {
let file = std::fs::File::open(path)
.unwrap_or_else(|e| panic!("failed to open icon {}: {}", path.display(), e));
@ -77,15 +85,13 @@ pub(crate) fn ico_icon(
let width = entry.width();
let height = entry.height();
let icon_file_name = path.file_name().unwrap();
let out_path = out_dir.join(icon_file_name);
let out_path = out_dir.join(out_file_name);
write_if_changed(&out_path, &rgba).map_err(|error| EmbeddedAssetsError::AssetWrite {
path: path.to_owned(),
error,
})?;
let icon_file_name = icon_file_name.to_str().unwrap();
let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)), #width, #height));
let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #out_file_name)), #width, #height));
Ok(icon)
}
@ -93,6 +99,7 @@ pub(crate) fn png_icon(
root: &TokenStream,
out_dir: &Path,
path: &Path,
out_file_name: &str,
) -> EmbeddedAssetsResult<TokenStream> {
let file = std::fs::File::open(path)
.unwrap_or_else(|e| panic!("failed to open icon {}: {}", path.display(), e));
@ -114,23 +121,22 @@ pub(crate) fn png_icon(
let width = reader.info().width;
let height = reader.info().height;
let icon_file_name = path.file_name().unwrap();
let out_path = out_dir.join(icon_file_name);
let out_path = out_dir.join(out_file_name);
write_if_changed(&out_path, &buffer).map_err(|error| EmbeddedAssetsError::AssetWrite {
path: path.to_owned(),
error,
})?;
let icon_file_name = icon_file_name.to_str().unwrap();
let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)), #width, #height));
let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #out_file_name)), #width, #height));
Ok(icon)
}
pub(crate) fn write_if_changed(out_path: &Path, data: &[u8]) -> std::io::Result<()> {
fn write_if_changed(out_path: &Path, data: &[u8]) -> std::io::Result<()> {
if let Ok(curr) = std::fs::read(out_path) {
if curr == data {
return Ok(());
}
}
std::fs::write(out_path, data)
}

View File

@ -72,6 +72,7 @@
"android": {
"minSdkVersion": 24
},
"createUpdaterArtifacts": false,
"iOS": {},
"icon": [],
"linux": {
@ -1126,7 +1127,8 @@
"type": "array",
"items": {
"$ref": "#/definitions/PermissionEntry"
}
},
"uniqueItems": true
},
"platforms": {
"description": "Limit which target platforms this capability applies to.\n\n By default all platforms are targeted.\n\n ## Example\n\n `[\"macOS\",\"windows\"]`",
@ -1518,7 +1520,7 @@
"type": "boolean"
},
"targets": {
"description": "The bundle targets, currently supports [\"deb\", \"rpm\", \"appimage\", \"nsis\", \"msi\", \"app\", \"dmg\", \"updater\"] or \"all\".",
"description": "The bundle targets, currently supports [\"deb\", \"rpm\", \"appimage\", \"nsis\", \"msi\", \"app\", \"dmg\"] or \"all\".",
"default": "all",
"allOf": [
{
@ -1526,6 +1528,15 @@
}
]
},
"createUpdaterArtifacts": {
"description": "Produce updaters and their signatures or not",
"default": false,
"allOf": [
{
"$ref": "#/definitions/Updater"
}
]
},
"publisher": {
"description": "The application's publisher. Defaults to the second element in the identifier string.\n Currently maps to the Manufacturer property of the Windows Installer.",
"type": [
@ -1793,12 +1804,34 @@
"enum": [
"dmg"
]
}
]
},
"Updater": {
"description": "Updater type",
"anyOf": [
{
"description": "Generates lagacy zipped v1 compatible updaters",
"allOf": [
{
"$ref": "#/definitions/V1Compatible"
}
]
},
{
"description": "The Tauri updater bundle.",
"description": "Produce updaters and their signatures or not",
"type": "boolean"
}
]
},
"V1Compatible": {
"description": "Generates lagacy zipped v1 compatible updaters",
"oneOf": [
{
"description": "Generates lagacy zipped v1 compatible updaters",
"type": "string",
"enum": [
"updater"
"v1Compatible"
]
}
]
@ -2306,8 +2339,15 @@
}
]
},
"startMenuFolder": {
"description": "Set the folder name for the start menu shortcut.\n\n Use this option if you have multiple apps and wish to group their shortcuts under one folder\n or if you generally prefer to set your shortcut inside a folder.\n\n Examples:\n - `AwesomePublisher`, shortcut will be placed in `%AppData%\\Microsoft\\Windows\\Start Menu\\Programs\\AwesomePublisher\\<your-app>.lnk`\n - If unset, shortcut will be placed in `%AppData%\\Microsoft\\Windows\\Start Menu\\Programs\\<your-app>.lnk`",
"type": [
"string",
"null"
]
},
"installerHooks": {
"description": "A path to a `.nsh` file that contains special NSIS macros to be hooked into the\n main installer.nsi script.\n\n Supported hooks are:\n - `NSIS_HOOK_PREINSTALL`: This hook runs before copying files, setting registry key values and creating shortcuts.\n - `NSIS_HOOK_POSTINSTALL`: This hook runs after the installer has finished copying all files, setting the registry keys and created shortcuts.\n - `NSIS_HOOK_PREUNINSTALL`: This hook runs before removing any files, registry keys and shortcuts.\n - `NSIS_HOOK_POSTUNINSTALL`: This hook runs after files, registry keys and shortcuts have been removed.\n\n\n ### Example\n\n ```nsh\n !define NSIS_HOOK_PREINSTALL \"NSIS_HOOK_PREINSTALL_\"\n !macro NSIS_HOOK_PREINSTALL_\n MessageBox MB_OK \"PreInstall\"\n !macroend\n\n !define NSIS_HOOK_POSTINSTALL \"NSIS_HOOK_POSTINSTALL_\"\n !macro NSIS_HOOK_POSTINSTALL_\n MessageBox MB_OK \"PostInstall\"\n !macroend\n\n !define NSIS_HOOK_PREUNINSTALL \"NSIS_HOOK_PREUNINSTALL_\"\n !macro NSIS_HOOK_PREUNINSTALL_\n MessageBox MB_OK \"PreUnInstall\"\n !macroend\n\n !define NSIS_HOOK_POSTUNINSTALL \"NSIS_HOOK_POSTUNINSTALL_\"\n !macro NSIS_HOOK_POSTUNINSTALL_\n MessageBox MB_OK \"PostUninstall\"\n !macroend\n\n ```",
"description": "A path to a `.nsh` file that contains special NSIS macros to be hooked into the\n main installer.nsi script.\n\n Supported hooks are:\n - `NSIS_HOOK_PREINSTALL`: This hook runs before copying files, setting registry key values and creating shortcuts.\n - `NSIS_HOOK_POSTINSTALL`: This hook runs after the installer has finished copying all files, setting the registry keys and created shortcuts.\n - `NSIS_HOOK_PREUNINSTALL`: This hook runs before removing any files, registry keys and shortcuts.\n - `NSIS_HOOK_POSTUNINSTALL`: This hook runs after files, registry keys and shortcuts have been removed.\n\n\n ### Example\n\n ```nsh\n !macro NSIS_HOOK_PREINSTALL\n MessageBox MB_OK \"PreInstall\"\n !macroend\n\n !macro NSIS_HOOK_POSTINSTALL\n MessageBox MB_OK \"PostInstall\"\n !macroend\n\n !macro NSIS_HOOK_PREUNINSTALL\n MessageBox MB_OK \"PreUnInstall\"\n !macroend\n\n !macro NSIS_HOOK_POSTUNINSTALL\n MessageBox MB_OK \"PostUninstall\"\n !macroend\n\n ```",
"type": [
"string",
"null"

View File

@ -1,5 +1,16 @@
# Changelog
## \[2.0.0-beta.18]
### New Features
- [`5b769948a`](https://www.github.com/tauri-apps/tauri/commit/5b769948a81cac333f64c870a470ba6525bd5cd3) ([#9959](https://www.github.com/tauri-apps/tauri/pull/9959)) Add `include_image` macro to help embedding instances of `Image` struct at compile-time in rust to be used with window, menu or tray icons.
### Dependencies
- Upgraded to `tauri-utils@2.0.0-beta.18`
- Upgraded to `tauri-codegen@2.0.0-beta.18`
## \[2.0.0-beta.17]
### Dependencies

View File

@ -1,6 +1,6 @@
[package]
name = "tauri-macros"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
description = "Macros for the tauri crate."
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@ -20,8 +20,8 @@ proc-macro2 = { version = "1", features = [ "span-locations" ] }
quote = "1"
syn = { version = "2", features = [ "full" ] }
heck = "0.5"
tauri-codegen = { version = "2.0.0-beta.17", default-features = false, path = "../tauri-codegen" }
tauri-utils = { version = "2.0.0-beta.17", path = "../tauri-utils" }
tauri-codegen = { version = "2.0.0-beta.18", default-features = false, path = "../tauri-codegen" }
tauri-utils = { version = "2.0.0-beta.18", path = "../tauri-utils" }
[features]
custom-protocol = [ ]

View File

@ -8,7 +8,7 @@ use std::path::PathBuf;
use syn::{
parse::{Parse, ParseBuffer},
punctuated::Punctuated,
Expr, ExprLit, Lit, LitStr, Meta, PathArguments, PathSegment, Token,
Expr, ExprLit, Lit, LitBool, LitStr, Meta, PathArguments, PathSegment, Token,
};
use tauri_codegen::{context_codegen, get_config, ContextData};
use tauri_utils::{config::parse::does_supported_file_name_exist, platform::Target};
@ -18,6 +18,7 @@ pub(crate) struct ContextItems {
root: syn::Path,
capabilities: Option<Vec<PathBuf>>,
assets: Option<Expr>,
test: bool,
}
impl Parse for ContextItems {
@ -31,6 +32,7 @@ impl Parse for ContextItems {
let mut root = None;
let mut capabilities = None;
let mut assets = None;
let mut test = false;
let config_file = input.parse::<LitStr>().ok().map(|raw| {
let _ = input.parse::<Token![,]>();
let path = PathBuf::from(raw.value());
@ -93,6 +95,17 @@ impl Parse for ContextItems {
"assets" => {
assets.replace(v.value);
}
"test" => {
if let Expr::Lit(ExprLit {
lit: Lit::Bool(LitBool { value, .. }),
..
}) = v.value
{
test = value;
} else {
return Err(syn::Error::new(input.span(), "unexpected value for test"));
}
}
name => {
return Err(syn::Error::new(
input.span(),
@ -105,6 +118,8 @@ impl Parse for ContextItems {
return Err(syn::Error::new(input.span(), "unexpected list input"));
}
}
let _ = input.parse::<Token![,]>();
}
Ok(Self {
@ -128,6 +143,7 @@ impl Parse for ContextItems {
}),
capabilities,
assets,
test,
})
}
}
@ -142,6 +158,7 @@ pub(crate) fn generate_context(context: ContextItems) -> TokenStream {
root: context.root.to_token_stream(),
capabilities: context.capabilities,
assets: context.assets,
test: context.test,
})
.and_then(|data| context_codegen(data).map_err(|e| e.to_string()));

View File

@ -109,7 +109,7 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre
/// ```
/// but you can't have mixed negations and positive kinds.
/// ```ignore
/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check | Submeun);
/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check | Submenu);
/// ```
///
/// #### Example
@ -203,7 +203,12 @@ pub fn include_image(tokens: TokenStream) -> TokenStream {
);
return quote!(compile_error!(#error_string)).into();
}
match tauri_codegen::include_image_codegen(&resolved_path).map_err(|error| error.to_string()) {
match tauri_codegen::include_image_codegen(
&resolved_path,
resolved_path.file_name().unwrap().to_str().unwrap(),
)
.map_err(|error| error.to_string())
{
Ok(output) => output,
Err(error) => quote!(compile_error!(#error)),
}

View File

@ -1,5 +1,11 @@
# Changelog
## \[2.0.0-beta.18]
### Dependencies
- Upgraded to `tauri-utils@2.0.0-beta.18`
## \[2.0.0-beta.17]
### New Features

View File

@ -1,6 +1,6 @@
[package]
name = "tauri-plugin"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
description = "Build script and runtime Tauri plugin definitions"
authors = { workspace = true }
homepage = { workspace = true }
@ -30,7 +30,7 @@ runtime = [ ]
[dependencies]
anyhow = { version = "1", optional = true }
serde = { version = "1", optional = true }
tauri-utils = { version = "2.0.0-beta.17", default-features = false, features = [ "build" ], path = "../tauri-utils" }
tauri-utils = { version = "2.0.0-beta.18", default-features = false, features = [ "build" ], path = "../tauri-utils" }
serde_json = { version = "1", optional = true }
glob = { version = "0.3", optional = true }
toml = { version = "0.8", optional = true }

View File

@ -121,7 +121,11 @@ impl<'a> Builder<'a> {
let _ = std::fs::remove_file(autogenerated.join(acl::build::PERMISSION_DOCS_FILE_NAME));
} else {
acl::build::generate_schema(&permissions, "./permissions")?;
acl::build::generate_docs(&permissions, &autogenerated)?;
acl::build::generate_docs(
&permissions,
&autogenerated,
name.strip_prefix("tauri-plugin-").unwrap_or(&name),
)?;
}
if let Some(global_scope_schema) = self.global_scope_schema {

View File

@ -1,5 +1,25 @@
# Changelog
## \[2.0.0-beta.19]
### Bug Fixes
- [`f29b78811`](https://www.github.com/tauri-apps/tauri/commit/f29b78811080bc8313459f34545152d939c62bf6) ([#9862](https://www.github.com/tauri-apps/tauri/pull/9862)) On Windows, handle resizing undecorated windows natively which improves performance and fixes a couple of annoyances with previous JS implementation:
- No more cursor flickering when moving the cursor across an edge.
- Can resize from top even when `data-tauri-drag-region` element exists there.
- Upon starting rezing, clicks don't go through elements behind it so no longer accidental clicks.
### What's Changed
- [`669b9c6b5`](https://www.github.com/tauri-apps/tauri/commit/669b9c6b5af791129b77ee440dacaa98288c906b) ([#9621](https://www.github.com/tauri-apps/tauri/pull/9621)) Set the gtk application to the identifier defined in `tauri.conf.json` to ensure the app uniqueness.
### Dependencies
- Upgraded to `tauri-utils@2.0.0-beta.18`
- Upgraded to `tauri-runtime@2.0.0-beta.19`
- [`d4c908cfb`](https://www.github.com/tauri-apps/tauri/commit/d4c908cfb8c567abdaf99b85f65f482ea81967e5) ([#10048](https://www.github.com/tauri-apps/tauri/pull/10048)) Update `windows` crate to version `0.57` and `webview2-com` crate to version `0.31`
## \[2.0.0-beta.18]
### Enhancements

View File

@ -1,6 +1,6 @@
[package]
name = "tauri-runtime-wry"
version = "2.0.0-beta.18"
version = "2.0.0-beta.19"
description = "Wry bindings to the Tauri runtime"
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@ -14,9 +14,9 @@ rust-version = { workspace = true }
[dependencies]
wry = { version = "0.41", default-features = false, features = [ "drag-drop", "protocol", "os-webview" ] }
tao = { version = "0.28", default-features = false, features = [ "rwh_06" ] }
tauri-runtime = { version = "2.0.0-beta.18", path = "../tauri-runtime" }
tauri-utils = { version = "2.0.0-beta.17", path = "../tauri-utils" }
tao = { version = "0.28.1", default-features = false, features = [ "rwh_06" ] }
tauri-runtime = { version = "2.0.0-beta.19", path = "../tauri-runtime" }
tauri-utils = { version = "2.0.0-beta.18", path = "../tauri-utils" }
raw-window-handle = "0.6"
http = "1.1"
url = "2"

View File

@ -1155,6 +1155,7 @@ pub enum WindowMessage {
SetCursorPosition(Position),
SetIgnoreCursorEvents(bool),
SetProgressBar(ProgressBarState),
SetTitleBarStyle(tauri_utils::TitleBarStyle),
DragWindow,
ResizeDragWindow(tauri_runtime::ResizeDirection),
RequestRedraw,
@ -1948,6 +1949,13 @@ impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
),
)
}
fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
send_user_message(
&self.context,
Message::Window(self.window_id, WindowMessage::SetTitleBarStyle(style)),
)
}
}
#[derive(Clone)]
@ -2766,7 +2774,15 @@ fn handle_user_message<T: UserEvent>(
WindowMessage::RequestUserAttention(request_type) => {
window.request_user_attention(request_type.map(|r| r.0));
}
WindowMessage::SetResizable(resizable) => window.set_resizable(resizable),
WindowMessage::SetResizable(resizable) => {
window.set_resizable(resizable);
#[cfg(windows)]
if !resizable {
undecorated_resizing::detach_resize_handler(window.hwnd());
} else if !window.is_decorated() {
undecorated_resizing::attach_resize_handler(window.hwnd());
}
}
WindowMessage::SetMaximizable(maximizable) => window.set_maximizable(maximizable),
WindowMessage::SetMinimizable(minimizable) => window.set_minimizable(minimizable),
WindowMessage::SetClosable(closable) => window.set_closable(closable),
@ -2788,7 +2804,7 @@ fn handle_user_message<T: UserEvent>(
#[cfg(windows)]
if decorations {
undecorated_resizing::detach_resize_handler(window.hwnd());
} else {
} else if window.is_resizable() {
undecorated_resizing::attach_resize_handler(window.hwnd());
}
}
@ -2872,6 +2888,23 @@ fn handle_user_message<T: UserEvent>(
WindowMessage::SetProgressBar(progress_state) => {
window.set_progress_bar(ProgressBarStateWrapper::from(progress_state).0);
}
WindowMessage::SetTitleBarStyle(_style) => {
#[cfg(target_os = "macos")]
match _style {
TitleBarStyle::Visible => {
window.set_titlebar_transparent(false);
window.set_fullsize_content_view(true);
}
TitleBarStyle::Transparent => {
window.set_titlebar_transparent(true);
window.set_fullsize_content_view(false);
}
TitleBarStyle::Overlay => {
window.set_titlebar_transparent(true);
window.set_fullsize_content_view(true);
}
};
}
}
}
}
@ -3832,21 +3865,21 @@ fn create_webview<T: UserEvent>(
WryDragDropEvent::Enter {
paths,
position: (x, y),
} => DragDropEvent::Dragged {
} => DragDropEvent::Enter {
paths,
position: PhysicalPosition::new(x as _, y as _),
},
WryDragDropEvent::Over { position: (x, y) } => DragDropEvent::DragOver {
WryDragDropEvent::Over { position: (x, y) } => DragDropEvent::Over {
position: PhysicalPosition::new(x as _, y as _),
},
WryDragDropEvent::Drop {
paths,
position: (x, y),
} => DragDropEvent::Dropped {
} => DragDropEvent::Drop {
paths,
position: PhysicalPosition::new(x as _, y as _),
},
WryDragDropEvent::Leave => DragDropEvent::Cancelled,
WryDragDropEvent::Leave => DragDropEvent::Leave,
_ => unimplemented!(),
};

View File

@ -1,5 +1,12 @@
# Changelog
## \[2.0.0-beta.19]
### Dependencies
- Upgraded to `tauri-utils@2.0.0-beta.18`
- [`d4c908cfb`](https://www.github.com/tauri-apps/tauri/commit/d4c908cfb8c567abdaf99b85f65f482ea81967e5) ([#10048](https://www.github.com/tauri-apps/tauri/pull/10048)) Update `windows` crate to version `0.57` and `webview2-com` crate to version `0.31`
## \[2.0.0-beta.18]
### Enhancements

View File

@ -1,6 +1,6 @@
[package]
name = "tauri-runtime"
version = "2.0.0-beta.18"
version = "2.0.0-beta.19"
description = "Runtime for Tauri applications"
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@ -29,7 +29,7 @@ targets = [
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
thiserror = "1.0"
tauri-utils = { version = "2.0.0-beta.17", path = "../tauri-utils" }
tauri-utils = { version = "2.0.0-beta.18", path = "../tauri-utils" }
http = "1.1"
raw-window-handle = "0.6"
url = { version = "2" }

View File

@ -783,4 +783,11 @@ pub trait WindowDispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 's
/// - **Linux / macOS**: Progress bar is app-wide and not specific to this window. Only supported desktop environments with `libunity` (e.g. GNOME).
/// - **iOS / Android:** Unsupported.
fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()>;
/// Sets the title bar style. Available on macOS only.
///
/// ## Platform-specific
///
/// - **Linux / Windows / iOS / Android:** Unsupported.
fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()>;
}

View File

@ -71,27 +71,27 @@ pub enum WebviewEvent {
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum DragDropEvent {
/// A drag operation started.
Dragged {
/// Paths of the files that are being dragged.
/// A drag operation has entered the webview.
Enter {
/// List of paths that are being dragged onto the webview.
paths: Vec<PathBuf>,
/// The position of the mouse cursor.
position: dpi::PhysicalPosition<f64>,
},
/// The files have been dragged onto the window, but have not been dropped yet.
DragOver {
/// A drag operation is moving over the webview.
Over {
/// The position of the mouse cursor.
position: dpi::PhysicalPosition<f64>,
},
/// The user dropped the operation.
Dropped {
/// Path of the files that were dropped.
/// The file(s) have been dropped onto the webview.
Drop {
/// List of paths that are being dropped onto the window.
paths: Vec<PathBuf>,
/// The position of the mouse cursor.
position: dpi::PhysicalPosition<f64>,
},
/// The drag operation was cancelled.
Cancelled,
/// The drag operation has been cancelled or left the window.
Leave,
}
/// Describes the appearance of the mouse cursor.

View File

@ -1,5 +1,25 @@
# Changelog
## \[2.0.0-beta.18]
### New Features
- [`fafc238f7`](https://www.github.com/tauri-apps/tauri/commit/fafc238f7288548975ca7d3e5207b925c0295c91) ([#9977](https://www.github.com/tauri-apps/tauri/pull/9977)) Add `bundle > homepage` option, if unset, it will fallback to `homepage` defined in `Cargo.toml`.
- [`656a64974`](https://www.github.com/tauri-apps/tauri/commit/656a64974468bc207bf39537e02ae179bdee9b83) ([#9318](https://www.github.com/tauri-apps/tauri/pull/9318)) Added a configuration option to disable hardened runtime on macOS codesign.
- [`5b769948a`](https://www.github.com/tauri-apps/tauri/commit/5b769948a81cac333f64c870a470ba6525bd5cd3) ([#9959](https://www.github.com/tauri-apps/tauri/pull/9959)) Add `include_image` macro to help embedding instances of `Image` struct at compile-time in rust to be used with window, menu or tray icons.
- [`3ab170917`](https://www.github.com/tauri-apps/tauri/commit/3ab170917ed535fc9013f0a9255631fb34493e18) ([#9932](https://www.github.com/tauri-apps/tauri/pull/9932)) Add an option to disable NSIS compression `bundle > nsis > compression: "none"`
- [`f21029b1b`](https://www.github.com/tauri-apps/tauri/commit/f21029b1bc25f5cb987e1a25de94c2d364e3e462) ([#9994](https://www.github.com/tauri-apps/tauri/pull/9994)) Add `bundle > nsis > startMenuFolder` option to customize start menu folder for NSIS installer
### Enhancements
- [`878198777`](https://www.github.com/tauri-apps/tauri/commit/878198777ef693efdbd394cb4be4b234e8a7ed3d) ([#9999](https://www.github.com/tauri-apps/tauri/pull/9999)) Mark ACL `permissions` array with unique items
### Breaking Changes
- [`3ab170917`](https://www.github.com/tauri-apps/tauri/commit/3ab170917ed535fc9013f0a9255631fb34493e18) ([#9932](https://www.github.com/tauri-apps/tauri/pull/9932)) Changed `NsisSettings::compression` field from `Option<NsisCompression>` to just `NsisCompression`
- [`911242f09`](https://www.github.com/tauri-apps/tauri/commit/911242f0928e0a2add3595fa9de27850fb875fa6) ([#9883](https://www.github.com/tauri-apps/tauri/pull/9883)) Move updater target from `bundle > targets` to a separate field `bundle > createUpdaterArtifacts`
- [`3ab170917`](https://www.github.com/tauri-apps/tauri/commit/3ab170917ed535fc9013f0a9255631fb34493e18) ([#9932](https://www.github.com/tauri-apps/tauri/pull/9932)) Changed `NsisConfig::compression` field from `Option<NsisCompression>` to just `NsisCompression`
## \[2.0.0-beta.17]
### New Features

View File

@ -1,6 +1,6 @@
[package]
name = "tauri-utils"
version = "2.0.0-beta.17"
version = "2.0.0-beta.18"
description = "Utilities for Tauri"
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@ -42,6 +42,7 @@ infer = "0.15"
dunce = "1"
log = "0.4.21"
cargo_metadata = { version = "0.18", optional = true }
serde-untagged = "0.1"
[target."cfg(target_os = \"macos\")".dependencies]
swift-rs = { version = "1.0.6", optional = true, features = [ "build" ] }

View File

@ -237,36 +237,66 @@ pub fn generate_schema<P: AsRef<Path>>(
}
/// Generate a markdown documentation page containing the list of permissions of the plugin.
pub fn generate_docs(permissions: &[PermissionFile], out_dir: &Path) -> Result<(), Error> {
let mut docs = "| Permission | Description |\n|------|-----|\n".to_string();
pub fn generate_docs(
permissions: &[PermissionFile],
out_dir: &Path,
plugin_identifier: &str,
) -> Result<(), Error> {
let mut permission_table = "".to_string();
let permission_table_header =
"### Permission Table \n\n<table>\n<tr>\n<th>Identifier</th>\n<th>Description</th>\n</tr>\n"
.to_string();
fn docs_from(id: &str, description: Option<&str>) -> String {
let mut docs = format!("|`{id}`");
let mut default_permission = "## Default Permission\n\n".to_string();
let mut contains_default = false;
fn docs_from(id: &str, description: Option<&str>, plugin_identifier: &str) -> String {
let mut docs = format!("\n<tr>\n<td>\n\n`{plugin_identifier}:{id}`\n\n</td>\n");
if let Some(d) = description {
docs.push_str(&format!("|{d}|"));
docs.push_str(&format!("<td>\n\n{d}\n\n</td>"));
}
docs.push_str("\n</tr>");
docs
}
for permission in permissions {
for set in &permission.set {
docs.push_str(&docs_from(&set.identifier, Some(&set.description)));
docs.push('\n');
permission_table.push_str(&docs_from(
&set.identifier,
Some(&set.description),
plugin_identifier,
));
permission_table.push('\n');
}
if let Some(default) = &permission.default {
docs.push_str(&docs_from("default", default.description.as_deref()));
docs.push('\n');
default_permission.push_str(default.description.as_deref().unwrap_or_default());
default_permission.push('\n');
default_permission.push('\n');
for permission in &default.permissions {
default_permission.push_str(&format!("- `{permission}`"));
default_permission.push('\n');
}
contains_default = true;
}
for permission in &permission.permission {
docs.push_str(&docs_from(
permission_table.push_str(&docs_from(
&permission.identifier,
permission.description.as_deref(),
plugin_identifier,
));
docs.push('\n');
permission_table.push('\n');
}
}
permission_table.push_str("</table>");
if !contains_default {
default_permission = "".to_string();
}
let docs = format!("{default_permission}\n{permission_table_header}\n{permission_table}\n");
let reference_path = out_dir.join(PERMISSION_DOCS_FILE_NAME);
if docs != read_to_string(&reference_path).unwrap_or_default() {
@ -405,8 +435,6 @@ identifier = "deny-{slugified_command}"
description = "Denies the {command} command without any pre-configured scope."
commands.deny = ["{command}"]
"###,
command = command,
slugified_command = slugified_command,
);
let out_path = path.join(format!("{command}.toml"));

View File

@ -7,15 +7,19 @@
use std::{path::Path, str::FromStr};
use crate::{acl::Identifier, platform::Target};
use serde::{Deserialize, Serialize};
use serde::{
de::{Error, IntoDeserializer},
Deserialize, Deserializer, Serialize,
};
use serde_untagged::UntaggedEnumVisitor;
use super::Scopes;
/// An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`]
/// or an object that references a permission and extends its scope.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
#[derive(Debug, Clone, PartialEq, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(untagged)]
pub enum PermissionEntry {
/// Reference a permission or permission set by identifier.
PermissionRef(Identifier),
@ -42,6 +46,34 @@ impl PermissionEntry {
}
}
impl<'de> Deserialize<'de> for PermissionEntry {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct ExtendedPermissionStruct {
identifier: Identifier,
#[serde(default, flatten)]
scope: Scopes,
}
UntaggedEnumVisitor::new()
.string(|string| {
let de = string.into_deserializer();
Identifier::deserialize(de).map(Self::PermissionRef)
})
.map(|map| {
let ext_perm = map.deserialize::<ExtendedPermissionStruct>()?;
Ok(Self::ExtendedPermission {
identifier: ext_perm.identifier,
scope: ext_perm.scope,
})
})
.deserialize(deserializer)
}
}
/// A grouping and boundary mechanism developers can use to isolate access to the IPC layer.
///
/// It controls application windows fine grained access to the Tauri core, application, or plugin commands.
@ -157,6 +189,7 @@ pub struct Capability {
/// "allow": [{ "path": "$HOME/test.txt" }]
/// }
/// ```
#[cfg_attr(feature = "schema", schemars(schema_with = "unique_permission"))]
pub permissions: Vec<PermissionEntry>,
/// Limit which target platforms this capability applies to.
///
@ -169,6 +202,21 @@ pub struct Capability {
pub platforms: Option<Vec<Target>>,
}
#[cfg(feature = "schema")]
fn unique_permission(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
use schemars::schema;
schema::SchemaObject {
instance_type: Some(schema::InstanceType::Array.into()),
array: Some(Box::new(schema::ArrayValidation {
unique_items: Some(true),
items: Some(gen.subschema_for::<PermissionEntry>().into()),
..Default::default()
})),
..Default::default()
}
.into()
}
fn default_capability_local() -> bool {
true
}
@ -188,9 +236,9 @@ pub struct CapabilityRemote {
}
/// Capability formats accepted in a capability file.
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(untagged)]
#[cfg_attr(feature = "schema", schemars(untagged))]
#[cfg_attr(test, derive(Debug, PartialEq))]
pub enum CapabilityFile {
/// A single capability.
Capability(Capability),
@ -218,6 +266,36 @@ impl CapabilityFile {
}
}
impl<'de> Deserialize<'de> for CapabilityFile {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.seq(|seq| seq.deserialize::<Vec<Capability>>().map(Self::List))
.map(|map| {
#[derive(Deserialize)]
struct CapabilityNamedList {
capabilities: Vec<Capability>,
}
let value: serde_json::Map<String, serde_json::Value> = map.deserialize()?;
if value.contains_key("capabilities") {
serde_json::from_value::<CapabilityNamedList>(value.into())
.map(|named| Self::NamedList {
capabilities: named.capabilities,
})
.map_err(|e| serde_untagged::de::Error::custom(e.to_string()))
} else {
serde_json::from_value::<Capability>(value.into())
.map(Self::Capability)
.map_err(|e| serde_untagged::de::Error::custom(e.to_string()))
}
})
.deserialize(deserializer)
}
}
impl FromStr for CapabilityFile {
type Err = super::Error;
@ -293,3 +371,71 @@ mod build {
}
}
}
#[cfg(test)]
mod tests {
use crate::acl::{Identifier, Scopes};
use super::{Capability, CapabilityFile, PermissionEntry};
#[test]
fn permission_entry_de() {
let identifier = Identifier::try_from("plugin:perm".to_string()).unwrap();
let identifier_json = serde_json::to_string(&identifier).unwrap();
assert_eq!(
serde_json::from_str::<PermissionEntry>(&identifier_json).unwrap(),
PermissionEntry::PermissionRef(identifier.clone())
);
assert_eq!(
serde_json::from_value::<PermissionEntry>(serde_json::json!({
"identifier": identifier,
"allow": [],
"deny": null
}))
.unwrap(),
PermissionEntry::ExtendedPermission {
identifier,
scope: Scopes {
allow: Some(vec![]),
deny: None
}
}
);
}
#[test]
fn capability_file_de() {
let capability = Capability {
identifier: "test".into(),
description: "".into(),
remote: None,
local: true,
windows: vec![],
webviews: vec![],
permissions: vec![],
platforms: None,
};
let capability_json = serde_json::to_string(&capability).unwrap();
assert_eq!(
serde_json::from_str::<CapabilityFile>(&capability_json).unwrap(),
CapabilityFile::Capability(capability.clone())
);
assert_eq!(
serde_json::from_str::<CapabilityFile>(&format!("[{capability_json}]")).unwrap(),
CapabilityFile::List(vec![capability.clone()])
);
assert_eq!(
serde_json::from_str::<CapabilityFile>(&format!(
"{{ \"capabilities\": [{capability_json}] }}"
))
.unwrap(),
CapabilityFile::NamedList {
capabilities: vec![capability.clone()]
}
);
}
}

View File

@ -19,6 +19,8 @@ pub struct DefaultPermission {
pub version: Option<NonZeroU64>,
/// Human-readable description of what the permission does.
/// Tauri convention is to use <h4> headings in markdown content
/// for Tauri documentation generation purposes.
pub description: Option<String>,
/// All permissions this set contains.

View File

@ -172,6 +172,8 @@ pub struct Permission {
pub identifier: String,
/// Human-readable description of what the permission does.
/// Tauri internal convention is to use <h4> headings in markdown content
/// for Tauri documentation generation purposes.
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,

View File

@ -18,6 +18,7 @@ use serde::{
Deserialize, Serialize, Serializer,
};
use serde_json::Value as JsonValue;
use serde_untagged::UntaggedEnumVisitor;
use serde_with::skip_serializing_none;
use url::Url;
@ -115,8 +116,6 @@ pub enum BundleType {
App,
/// The Apple Disk Image bundle (.dmg).
Dmg,
/// The Tauri updater bundle.
Updater,
}
impl BundleType {
@ -130,7 +129,6 @@ impl BundleType {
BundleType::Nsis,
BundleType::App,
BundleType::Dmg,
BundleType::Updater,
]
}
}
@ -148,7 +146,6 @@ impl Display for BundleType {
Self::Nsis => "nsis",
Self::App => "app",
Self::Dmg => "dmg",
Self::Updater => "updater",
}
)
}
@ -177,7 +174,6 @@ impl<'de> Deserialize<'de> for BundleType {
"nsis" => Ok(Self::Nsis),
"app" => Ok(Self::App),
"dmg" => Ok(Self::Dmg),
"updater" => Ok(Self::Updater),
_ => Err(DeError::custom(format!("unknown bundle target '{s}'"))),
}
}
@ -270,7 +266,14 @@ impl<'de> Deserialize<'de> for BundleTarget {
match BundleTargetInner::deserialize(deserializer)? {
BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All),
BundleTargetInner::All(t) => Err(DeError::custom(format!("invalid bundle type {t}"))),
BundleTargetInner::All(t) => Err(DeError::custom(format!(
"invalid bundle type {t}, expected one of `all`, {}",
BundleType::all()
.iter()
.map(|b| format!("`{b}`"))
.collect::<Vec<_>>()
.join(", ")
))),
BundleTargetInner::List(l) => Ok(Self::List(l)),
BundleTargetInner::One(t) => Ok(Self::One(t)),
}
@ -776,6 +779,16 @@ pub struct NsisConfig {
/// See <https://nsis.sourceforge.io/Reference/SetCompressor>
#[serde(default)]
pub compression: NsisCompression,
/// Set the folder name for the start menu shortcut.
///
/// Use this option if you have multiple apps and wish to group their shortcuts under one folder
/// or if you generally prefer to set your shortcut inside a folder.
///
/// Examples:
/// - `AwesomePublisher`, shortcut will be placed in `%AppData%\Microsoft\Windows\Start Menu\Programs\AwesomePublisher\<your-app>.lnk`
/// - If unset, shortcut will be placed in `%AppData%\Microsoft\Windows\Start Menu\Programs\<your-app>.lnk`
#[serde(alias = "start-menu-folder")]
pub start_menu_folder: Option<String>,
/// A path to a `.nsh` file that contains special NSIS macros to be hooked into the
/// main installer.nsi script.
///
@ -789,23 +802,19 @@ pub struct NsisConfig {
/// ### Example
///
/// ```nsh
/// !define NSIS_HOOK_PREINSTALL "NSIS_HOOK_PREINSTALL_"
/// !macro NSIS_HOOK_PREINSTALL_
/// !macro NSIS_HOOK_PREINSTALL
/// MessageBox MB_OK "PreInstall"
/// !macroend
///
/// !define NSIS_HOOK_POSTINSTALL "NSIS_HOOK_POSTINSTALL_"
/// !macro NSIS_HOOK_POSTINSTALL_
/// !macro NSIS_HOOK_POSTINSTALL
/// MessageBox MB_OK "PostInstall"
/// !macroend
///
/// !define NSIS_HOOK_PREUNINSTALL "NSIS_HOOK_PREUNINSTALL_"
/// !macro NSIS_HOOK_PREUNINSTALL_
/// !macro NSIS_HOOK_PREUNINSTALL
/// MessageBox MB_OK "PreUnInstall"
/// !macroend
///
/// !define NSIS_HOOK_POSTUNINSTALL "NSIS_HOOK_POSTUNINSTALL_"
/// !macro NSIS_HOOK_POSTUNINSTALL_
/// !macro NSIS_HOOK_POSTUNINSTALL
/// MessageBox MB_OK "PostUninstall"
/// !macroend
///
@ -1052,6 +1061,33 @@ impl BundleResources {
}
}
/// Updater type
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)]
pub enum Updater {
/// Generates lagacy zipped v1 compatible updaters
String(V1Compatible),
/// Produce updaters and their signatures or not
// Can't use untagged on enum field here: https://github.com/GREsau/schemars/issues/222
Bool(bool),
}
impl Default for Updater {
fn default() -> Self {
Self::Bool(false)
}
}
/// Generates lagacy zipped v1 compatible updaters
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub enum V1Compatible {
/// Generates lagacy zipped v1 compatible updaters
V1Compatible,
}
/// Configuration for tauri-bundler.
///
/// See more: <https://tauri.app/v1/api/config#bundleconfig>
@ -1063,9 +1099,12 @@ pub struct BundleConfig {
/// Whether Tauri should bundle your application or just output the executable.
#[serde(default)]
pub active: bool,
/// The bundle targets, currently supports ["deb", "rpm", "appimage", "nsis", "msi", "app", "dmg", "updater"] or "all".
/// The bundle targets, currently supports ["deb", "rpm", "appimage", "nsis", "msi", "app", "dmg"] or "all".
#[serde(default)]
pub targets: BundleTarget,
#[serde(default)]
/// Produce updaters and their signatures or not
pub create_updater_artifacts: Updater,
/// The application's publisher. Defaults to the second element in the identifier string.
/// Currently maps to the Manufacturer property of the Windows Installer.
pub publisher: Option<String>,
@ -1698,9 +1737,9 @@ pub struct SecurityConfig {
}
/// A capability entry which can be either an inlined capability or a reference to a capability defined on its own file.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[derive(Debug, Clone, PartialEq, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", untagged)]
#[serde(untagged)]
pub enum CapabilityEntry {
/// An inlined capability.
Inlined(Capability),
@ -1708,6 +1747,18 @@ pub enum CapabilityEntry {
Reference(String),
}
impl<'de> Deserialize<'de> for CapabilityEntry {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.string(|string| Ok(Self::Reference(string.to_owned())))
.map(|map| map.deserialize::<Capability>().map(Self::Inlined))
.deserialize(deserializer)
}
}
/// The application pattern.
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
@ -2444,6 +2495,7 @@ mod build {
let icon = vec_lit(&self.icon, str_lit);
let active = self.active;
let targets = quote!(Default::default());
let create_updater_artifacts = quote!(Default::default());
let resources = quote!(None);
let copyright = quote!(None);
let category = quote!(None);
@ -2467,6 +2519,7 @@ mod build {
homepage,
icon,
targets,
create_updater_artifacts,
resources,
copyright,
category,
@ -2781,6 +2834,7 @@ mod test {
let bundle = BundleConfig {
active: false,
targets: Default::default(),
create_updater_artifacts: Default::default(),
publisher: None,
homepage: None,
icon: Vec::new(),

View File

@ -37,20 +37,27 @@
* @param {object} data
* @return {Promise<{nonce: number[], payload: number[]}>}
*/
async function encrypt(data) {
async function encrypt(payload) {
const algorithm = Object.create(null)
algorithm.name = 'AES-GCM'
algorithm.iv = window.crypto.getRandomValues(new Uint8Array(12))
const encoder = new TextEncoder()
const encoded = encoder.encode(__RAW_process_ipc_message_fn__(data).data)
const { contentType, data } = __RAW_process_ipc_message_fn__(payload)
const message =
typeof data === 'string'
? new TextEncoder().encode(data)
: ArrayBuffer.isView(data) || data instanceof ArrayBuffer
? data
: new Uint8Array(data)
return window.crypto.subtle
.encrypt(algorithm, aesGcmKey, encoded)
.encrypt(algorithm, aesGcmKey, message)
.then((payload) => {
const result = Object.create(null)
result.nonce = Array.from(new Uint8Array(algorithm.iv))
result.payload = Array.from(new Uint8Array(payload))
result.contentType = contentType
return result
})
}
@ -66,7 +73,9 @@
const keys = data.payload ? Object.keys(data.payload) : []
return (
keys.length > 0 &&
keys.every((key) => key === 'nonce' || key === 'payload')
keys.every(
(key) => key === 'nonce' || key === 'payload' || key === 'contentType'
)
)
}
return false

View File

@ -73,6 +73,14 @@ impl AesGcmPair {
pub fn key(&self) -> &Aes256Gcm {
&self.key
}
#[doc(hidden)]
pub fn encrypt(&self, nonce: &[u8; 12], payload: &[u8]) -> Result<Vec<u8>, Error> {
self
.key
.encrypt(nonce.into(), payload)
.map_err(|_| self::Error::Aes)
}
}
/// All cryptographic keys required for Isolation encryption
@ -97,7 +105,7 @@ impl Keys {
/// Decrypts a message using the generated keys.
pub fn decrypt(&self, raw: RawIsolationPayload<'_>) -> Result<Vec<u8>, Error> {
let RawIsolationPayload { nonce, payload } = raw;
let RawIsolationPayload { nonce, payload, .. } = raw;
let nonce: [u8; 12] = nonce.as_ref().try_into()?;
self
.aes_gcm
@ -109,9 +117,18 @@ impl Keys {
/// Raw representation of
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RawIsolationPayload<'a> {
nonce: Cow<'a, [u8]>,
payload: Cow<'a, [u8]>,
content_type: Cow<'a, str>,
}
impl<'a> RawIsolationPayload<'a> {
/// Content type of this payload.
pub fn content_type(&self) -> &Cow<'a, str> {
&self.content_type
}
}
impl<'a> TryFrom<&'a Vec<u8>> for RawIsolationPayload<'a> {

View File

@ -1,5 +1,45 @@
# Changelog
## \[2.0.0-beta.23]
### New Features
- [`148f04887`](https://www.github.com/tauri-apps/tauri/commit/148f048871caee21498b236c058b8890f2b66cc7) ([#9979](https://www.github.com/tauri-apps/tauri/pull/9979)) Add `defaultWindowIcon` to the JS `app` module to retrieve the default window icon in JS.
- [`5b769948a`](https://www.github.com/tauri-apps/tauri/commit/5b769948a81cac333f64c870a470ba6525bd5cd3) ([#9959](https://www.github.com/tauri-apps/tauri/pull/9959)) Add `include_image` macro to help embedding instances of `Image` struct at compile-time in rust to be used with window, menu or tray icons.
- [`ddaabda36`](https://www.github.com/tauri-apps/tauri/commit/ddaabda365ed5dc0780925049473989cbd1d7ea3) ([#9922](https://www.github.com/tauri-apps/tauri/pull/9922)) Add `WebviewWindowBuilder::on_download`.
### Enhancements
- [`cee0bfcd6`](https://www.github.com/tauri-apps/tauri/commit/cee0bfcd6c03c2a6794abca8f4fde700f3f818ba) ([#10092](https://www.github.com/tauri-apps/tauri/pull/10092)) Make `tray:default` and `menu:default` include all tray and menu permissions
### Bug Fixes
- [`e93ca1df3`](https://www.github.com/tauri-apps/tauri/commit/e93ca1df3b3948647f501f9f958e894ade6a27fb) ([#10138](https://www.github.com/tauri-apps/tauri/pull/10138)) Fix `InvokeBody::deserialize` method deserialization for `InvokeBody::Raw` variant
- [`e6e17ad1c`](https://www.github.com/tauri-apps/tauri/commit/e6e17ad1c8a6b53463946c407a354c250bd7e701) ([#9954](https://www.github.com/tauri-apps/tauri/pull/9954)) Add `std` feature to `raw-window-handle` crate so that using `default-features = false` on `tauri` crate can work
- [`f29b78811`](https://www.github.com/tauri-apps/tauri/commit/f29b78811080bc8313459f34545152d939c62bf6) ([#9862](https://www.github.com/tauri-apps/tauri/pull/9862)) On Windows, handle resizing undecorated windows natively which improves performance and fixes a couple of annoyances with previous JS implementation:
- No more cursor flickering when moving the cursor across an edge.
- Can resize from top even when `data-tauri-drag-region` element exists there.
- Upon starting rezing, clicks don't go through elements behind it so no longer accidental clicks.
### What's Changed
- [`669b9c6b5`](https://www.github.com/tauri-apps/tauri/commit/669b9c6b5af791129b77ee440dacaa98288c906b) ([#9621](https://www.github.com/tauri-apps/tauri/pull/9621)) Set the gtk application to the identifier defined in `tauri.conf.json` to ensure the app uniqueness.
### Dependencies
- Upgraded to `tauri-utils@2.0.0-beta.18`
- Upgraded to `tauri-build@2.0.0-beta.18`
- Upgraded to `tauri-macros@2.0.0-beta.18`
- Upgraded to `tauri-runtime-wry@2.0.0-beta.19`
- Upgraded to `tauri-runtime@2.0.0-beta.19`
- [`f955f7b49`](https://www.github.com/tauri-apps/tauri/commit/f955f7b4903bcea376c0a8b430736f66c8cebf56) ([#9929](https://www.github.com/tauri-apps/tauri/pull/9929)) Switch from `dirs_next` to `dirs` as `dirs_next` is now unmaintained while `dirs` is
- [`d4c908cfb`](https://www.github.com/tauri-apps/tauri/commit/d4c908cfb8c567abdaf99b85f65f482ea81967e5) ([#10048](https://www.github.com/tauri-apps/tauri/pull/10048)) Update `windows` crate to version `0.57` and `webview2-com` crate to version `0.31`
### Breaking Changes
- [`3afe82894`](https://www.github.com/tauri-apps/tauri/commit/3afe8289407b53791e761764964a42207a7f7881) ([#10134](https://www.github.com/tauri-apps/tauri/pull/10134)) Changed `WebviewWindow::navigate` and `Webview::navigate` method signature to return a `Result`
## \[2.0.0-beta.22]
### Bug Fixes

View File

@ -1,6 +1,6 @@
[package]
name = "tauri"
version = "2.0.0-beta.22"
version = "2.0.0-beta.23"
description = "Make tiny, secure apps for all desktop platforms with Tauri"
exclude = [ "/test", "/.scripts", "CHANGELOG.md", "/target" ]
readme = "README.md"
@ -51,10 +51,10 @@ uuid = { version = "1", features = [ "v4" ], optional = true }
url = "2"
anyhow = "1.0"
thiserror = "1.0"
tauri-runtime = { version = "2.0.0-beta.18", path = "../tauri-runtime" }
tauri-macros = { version = "2.0.0-beta.17", path = "../tauri-macros" }
tauri-utils = { version = "2.0.0-beta.17", features = [ "resources" ], path = "../tauri-utils" }
tauri-runtime-wry = { version = "2.0.0-beta.18", path = "../tauri-runtime-wry", optional = true }
tauri-runtime = { version = "2.0.0-beta.19", path = "../tauri-runtime" }
tauri-macros = { version = "2.0.0-beta.18", path = "../tauri-macros" }
tauri-utils = { version = "2.0.0-beta.18", features = [ "resources" ], path = "../tauri-utils" }
tauri-runtime-wry = { version = "2.0.0-beta.19", path = "../tauri-runtime-wry", optional = true }
getrandom = "0.2"
serde_repr = "0.1"
state = "0.6"
@ -110,8 +110,8 @@ swift-rs = "1.0.6"
[build-dependencies]
heck = "0.5"
tauri-build = { path = "../tauri-build/", default-features = false, version = "2.0.0-beta.17" }
tauri-utils = { path = "../tauri-utils/", version = "2.0.0-beta.17", features = [ "build" ] }
tauri-build = { path = "../tauri-build/", default-features = false, version = "2.0.0-beta.18" }
tauri-utils = { path = "../tauri-utils/", version = "2.0.0-beta.18", features = [ "build" ] }
[dev-dependencies]
proptest = "1.4.0"

View File

@ -106,6 +106,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
("start_resize_dragging", false),
("set_progress_bar", false),
("set_icon", false),
("set_title_bar_style", false),
("toggle_maximize", false),
// internal
("internal_toggle_maximize", true),
@ -228,7 +229,7 @@ fn main() {
alias("custom_protocol", custom_protocol);
alias("dev", dev);
println!("cargo:dev={}", dev);
println!("cargo:dev={dev}");
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let mobile = target_os == "ios" || target_os == "android";
@ -256,10 +257,7 @@ fn main() {
if let Ok(kotlin_out_dir) = std::env::var("WRY_ANDROID_KOTLIN_FILES_OUT_DIR") {
fn env_var(var: &str) -> String {
std::env::var(var).unwrap_or_else(|_| {
panic!(
"`{}` is not set, which is needed to generate the kotlin files for android.",
var
)
panic!("`{var}` is not set, which is needed to generate the kotlin files for android.")
})
}
@ -345,7 +343,7 @@ fn define_permissions(out_dir: &Path) {
.filter(|(_cmd, default)| *default)
.map(|(cmd, _)| {
let slugified_command = cmd.replace('_', "-");
format!("\"allow-{}\"", slugified_command)
format!("\"allow-{slugified_command}\"")
})
.collect::<Vec<_>>()
.join(", ");
@ -378,8 +376,12 @@ permissions = [{default_permissions}]
let docs_out_dir = Path::new("permissions").join(plugin).join("autogenerated");
create_dir_all(&docs_out_dir).expect("failed to create plugin documentation directory");
tauri_utils::acl::build::generate_docs(&permissions, &docs_out_dir)
.expect("failed to generate plugin documentation page");
tauri_utils::acl::build::generate_docs(
&permissions,
&docs_out_dir,
plugin.strip_prefix("tauri-plugin-").unwrap_or(plugin),
)
.expect("failed to generate plugin documentation page");
}
}

View File

@ -1,15 +1,173 @@
| Permission | Description |
|------|-----|
|`allow-app-hide`|Enables the app_hide command without any pre-configured scope.|
|`deny-app-hide`|Denies the app_hide command without any pre-configured scope.|
|`allow-app-show`|Enables the app_show command without any pre-configured scope.|
|`deny-app-show`|Denies the app_show command without any pre-configured scope.|
|`allow-default-window-icon`|Enables the default_window_icon command without any pre-configured scope.|
|`deny-default-window-icon`|Denies the default_window_icon command without any pre-configured scope.|
|`allow-name`|Enables the name command without any pre-configured scope.|
|`deny-name`|Denies the name command without any pre-configured scope.|
|`allow-tauri-version`|Enables the tauri_version command without any pre-configured scope.|
|`deny-tauri-version`|Denies the tauri_version command without any pre-configured scope.|
|`allow-version`|Enables the version command without any pre-configured scope.|
|`deny-version`|Denies the version command without any pre-configured scope.|
|`default`|Default permissions for the plugin.|
## Default Permission
Default permissions for the plugin.
- `allow-version`
- `allow-name`
- `allow-tauri-version`
### Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`app:allow-app-hide`
</td>
<td>
Enables the app_hide command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:deny-app-hide`
</td>
<td>
Denies the app_hide command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:allow-app-show`
</td>
<td>
Enables the app_show command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:deny-app-show`
</td>
<td>
Denies the app_show command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:allow-default-window-icon`
</td>
<td>
Enables the default_window_icon command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:deny-default-window-icon`
</td>
<td>
Denies the default_window_icon command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:allow-name`
</td>
<td>
Enables the name command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:deny-name`
</td>
<td>
Denies the name command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:allow-tauri-version`
</td>
<td>
Enables the tauri_version command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:deny-tauri-version`
</td>
<td>
Denies the tauri_version command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:allow-version`
</td>
<td>
Enables the version command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`app:deny-version`
</td>
<td>
Denies the version command without any pre-configured scope.
</td>
</tr>
</table>

View File

@ -1,11 +1,122 @@
| Permission | Description |
|------|-----|
|`allow-emit`|Enables the emit command without any pre-configured scope.|
|`deny-emit`|Denies the emit command without any pre-configured scope.|
|`allow-emit-to`|Enables the emit_to command without any pre-configured scope.|
|`deny-emit-to`|Denies the emit_to command without any pre-configured scope.|
|`allow-listen`|Enables the listen command without any pre-configured scope.|
|`deny-listen`|Denies the listen command without any pre-configured scope.|
|`allow-unlisten`|Enables the unlisten command without any pre-configured scope.|
|`deny-unlisten`|Denies the unlisten command without any pre-configured scope.|
|`default`|Default permissions for the plugin.|
## Default Permission
Default permissions for the plugin.
- `allow-listen`
- `allow-unlisten`
- `allow-emit`
- `allow-emit-to`
### Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`event:allow-emit`
</td>
<td>
Enables the emit command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`event:deny-emit`
</td>
<td>
Denies the emit command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`event:allow-emit-to`
</td>
<td>
Enables the emit_to command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`event:deny-emit-to`
</td>
<td>
Denies the emit_to command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`event:allow-listen`
</td>
<td>
Enables the listen command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`event:deny-listen`
</td>
<td>
Denies the listen command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`event:allow-unlisten`
</td>
<td>
Enables the unlisten command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`event:deny-unlisten`
</td>
<td>
Denies the unlisten command without any pre-configured scope.
</td>
</tr>
</table>

View File

@ -1,13 +1,149 @@
| Permission | Description |
|------|-----|
|`allow-from-bytes`|Enables the from_bytes command without any pre-configured scope.|
|`deny-from-bytes`|Denies the from_bytes command without any pre-configured scope.|
|`allow-from-path`|Enables the from_path command without any pre-configured scope.|
|`deny-from-path`|Denies the from_path command without any pre-configured scope.|
|`allow-new`|Enables the new command without any pre-configured scope.|
|`deny-new`|Denies the new command without any pre-configured scope.|
|`allow-rgba`|Enables the rgba command without any pre-configured scope.|
|`deny-rgba`|Denies the rgba command without any pre-configured scope.|
|`allow-size`|Enables the size command without any pre-configured scope.|
|`deny-size`|Denies the size command without any pre-configured scope.|
|`default`|Default permissions for the plugin.|
## Default Permission
Default permissions for the plugin.
- `allow-new`
- `allow-from-bytes`
- `allow-from-path`
- `allow-rgba`
- `allow-size`
### Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`image:allow-from-bytes`
</td>
<td>
Enables the from_bytes command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`image:deny-from-bytes`
</td>
<td>
Denies the from_bytes command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`image:allow-from-path`
</td>
<td>
Enables the from_path command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`image:deny-from-path`
</td>
<td>
Denies the from_path command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`image:allow-new`
</td>
<td>
Enables the new command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`image:deny-new`
</td>
<td>
Denies the new command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`image:allow-rgba`
</td>
<td>
Enables the rgba command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`image:deny-rgba`
</td>
<td>
Denies the rgba command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`image:allow-size`
</td>
<td>
Enables the size command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`image:deny-size`
</td>
<td>
Denies the size command without any pre-configured scope.
</td>
</tr>
</table>

View File

@ -1,47 +1,608 @@
| Permission | Description |
|------|-----|
|`allow-append`|Enables the append command without any pre-configured scope.|
|`deny-append`|Denies the append command without any pre-configured scope.|
|`allow-create-default`|Enables the create_default command without any pre-configured scope.|
|`deny-create-default`|Denies the create_default command without any pre-configured scope.|
|`allow-get`|Enables the get command without any pre-configured scope.|
|`deny-get`|Denies the get command without any pre-configured scope.|
|`allow-insert`|Enables the insert command without any pre-configured scope.|
|`deny-insert`|Denies the insert command without any pre-configured scope.|
|`allow-is-checked`|Enables the is_checked command without any pre-configured scope.|
|`deny-is-checked`|Denies the is_checked command without any pre-configured scope.|
|`allow-is-enabled`|Enables the is_enabled command without any pre-configured scope.|
|`deny-is-enabled`|Denies the is_enabled command without any pre-configured scope.|
|`allow-items`|Enables the items command without any pre-configured scope.|
|`deny-items`|Denies the items command without any pre-configured scope.|
|`allow-new`|Enables the new command without any pre-configured scope.|
|`deny-new`|Denies the new command without any pre-configured scope.|
|`allow-popup`|Enables the popup command without any pre-configured scope.|
|`deny-popup`|Denies the popup command without any pre-configured scope.|
|`allow-prepend`|Enables the prepend command without any pre-configured scope.|
|`deny-prepend`|Denies the prepend command without any pre-configured scope.|
|`allow-remove`|Enables the remove command without any pre-configured scope.|
|`deny-remove`|Denies the remove command without any pre-configured scope.|
|`allow-remove-at`|Enables the remove_at command without any pre-configured scope.|
|`deny-remove-at`|Denies the remove_at command without any pre-configured scope.|
|`allow-set-accelerator`|Enables the set_accelerator command without any pre-configured scope.|
|`deny-set-accelerator`|Denies the set_accelerator command without any pre-configured scope.|
|`allow-set-as-app-menu`|Enables the set_as_app_menu command without any pre-configured scope.|
|`deny-set-as-app-menu`|Denies the set_as_app_menu command without any pre-configured scope.|
|`allow-set-as-help-menu-for-nsapp`|Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.|
|`deny-set-as-help-menu-for-nsapp`|Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.|
|`allow-set-as-window-menu`|Enables the set_as_window_menu command without any pre-configured scope.|
|`deny-set-as-window-menu`|Denies the set_as_window_menu command without any pre-configured scope.|
|`allow-set-as-windows-menu-for-nsapp`|Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.|
|`deny-set-as-windows-menu-for-nsapp`|Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.|
|`allow-set-checked`|Enables the set_checked command without any pre-configured scope.|
|`deny-set-checked`|Denies the set_checked command without any pre-configured scope.|
|`allow-set-enabled`|Enables the set_enabled command without any pre-configured scope.|
|`deny-set-enabled`|Denies the set_enabled command without any pre-configured scope.|
|`allow-set-icon`|Enables the set_icon command without any pre-configured scope.|
|`deny-set-icon`|Denies the set_icon command without any pre-configured scope.|
|`allow-set-text`|Enables the set_text command without any pre-configured scope.|
|`deny-set-text`|Denies the set_text command without any pre-configured scope.|
|`allow-text`|Enables the text command without any pre-configured scope.|
|`deny-text`|Denies the text command without any pre-configured scope.|
|`default`|Default permissions for the plugin.|
## Default Permission
Default permissions for the plugin.
- `allow-new`
- `allow-append`
- `allow-prepend`
- `allow-insert`
- `allow-remove`
- `allow-remove-at`
- `allow-items`
- `allow-get`
- `allow-popup`
- `allow-create-default`
- `allow-set-as-app-menu`
- `allow-set-as-window-menu`
- `allow-text`
- `allow-set-text`
- `allow-is-enabled`
- `allow-set-enabled`
- `allow-set-accelerator`
- `allow-set-as-windows-menu-for-nsapp`
- `allow-set-as-help-menu-for-nsapp`
- `allow-is-checked`
- `allow-set-checked`
- `allow-set-icon`
### Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`menu:allow-append`
</td>
<td>
Enables the append command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-append`
</td>
<td>
Denies the append command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-create-default`
</td>
<td>
Enables the create_default command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-create-default`
</td>
<td>
Denies the create_default command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-get`
</td>
<td>
Enables the get command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-get`
</td>
<td>
Denies the get command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-insert`
</td>
<td>
Enables the insert command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-insert`
</td>
<td>
Denies the insert command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-is-checked`
</td>
<td>
Enables the is_checked command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-is-checked`
</td>
<td>
Denies the is_checked command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-is-enabled`
</td>
<td>
Enables the is_enabled command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-is-enabled`
</td>
<td>
Denies the is_enabled command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-items`
</td>
<td>
Enables the items command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-items`
</td>
<td>
Denies the items command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-new`
</td>
<td>
Enables the new command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-new`
</td>
<td>
Denies the new command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-popup`
</td>
<td>
Enables the popup command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-popup`
</td>
<td>
Denies the popup command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-prepend`
</td>
<td>
Enables the prepend command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-prepend`
</td>
<td>
Denies the prepend command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-remove`
</td>
<td>
Enables the remove command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-remove`
</td>
<td>
Denies the remove command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-remove-at`
</td>
<td>
Enables the remove_at command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-remove-at`
</td>
<td>
Denies the remove_at command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-set-accelerator`
</td>
<td>
Enables the set_accelerator command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-set-accelerator`
</td>
<td>
Denies the set_accelerator command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-set-as-app-menu`
</td>
<td>
Enables the set_as_app_menu command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-set-as-app-menu`
</td>
<td>
Denies the set_as_app_menu command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-set-as-help-menu-for-nsapp`
</td>
<td>
Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-set-as-help-menu-for-nsapp`
</td>
<td>
Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-set-as-window-menu`
</td>
<td>
Enables the set_as_window_menu command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-set-as-window-menu`
</td>
<td>
Denies the set_as_window_menu command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-set-as-windows-menu-for-nsapp`
</td>
<td>
Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-set-as-windows-menu-for-nsapp`
</td>
<td>
Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-set-checked`
</td>
<td>
Enables the set_checked command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-set-checked`
</td>
<td>
Denies the set_checked command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-set-enabled`
</td>
<td>
Enables the set_enabled command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-set-enabled`
</td>
<td>
Denies the set_enabled command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-set-icon`
</td>
<td>
Enables the set_icon command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-set-icon`
</td>
<td>
Denies the set_icon command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-set-text`
</td>
<td>
Enables the set_text command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-set-text`
</td>
<td>
Denies the set_text command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:allow-text`
</td>
<td>
Enables the text command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`menu:deny-text`
</td>
<td>
Denies the text command without any pre-configured scope.
</td>
</tr>
</table>

View File

@ -1,19 +1,230 @@
| Permission | Description |
|------|-----|
|`allow-basename`|Enables the basename command without any pre-configured scope.|
|`deny-basename`|Denies the basename command without any pre-configured scope.|
|`allow-dirname`|Enables the dirname command without any pre-configured scope.|
|`deny-dirname`|Denies the dirname command without any pre-configured scope.|
|`allow-extname`|Enables the extname command without any pre-configured scope.|
|`deny-extname`|Denies the extname command without any pre-configured scope.|
|`allow-is-absolute`|Enables the is_absolute command without any pre-configured scope.|
|`deny-is-absolute`|Denies the is_absolute command without any pre-configured scope.|
|`allow-join`|Enables the join command without any pre-configured scope.|
|`deny-join`|Denies the join command without any pre-configured scope.|
|`allow-normalize`|Enables the normalize command without any pre-configured scope.|
|`deny-normalize`|Denies the normalize command without any pre-configured scope.|
|`allow-resolve`|Enables the resolve command without any pre-configured scope.|
|`deny-resolve`|Denies the resolve command without any pre-configured scope.|
|`allow-resolve-directory`|Enables the resolve_directory command without any pre-configured scope.|
|`deny-resolve-directory`|Denies the resolve_directory command without any pre-configured scope.|
|`default`|Default permissions for the plugin.|
## Default Permission
Default permissions for the plugin.
- `allow-resolve-directory`
- `allow-resolve`
- `allow-normalize`
- `allow-join`
- `allow-dirname`
- `allow-extname`
- `allow-basename`
- `allow-is-absolute`
### Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`path:allow-basename`
</td>
<td>
Enables the basename command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:deny-basename`
</td>
<td>
Denies the basename command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:allow-dirname`
</td>
<td>
Enables the dirname command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:deny-dirname`
</td>
<td>
Denies the dirname command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:allow-extname`
</td>
<td>
Enables the extname command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:deny-extname`
</td>
<td>
Denies the extname command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:allow-is-absolute`
</td>
<td>
Enables the is_absolute command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:deny-is-absolute`
</td>
<td>
Denies the is_absolute command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:allow-join`
</td>
<td>
Enables the join command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:deny-join`
</td>
<td>
Denies the join command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:allow-normalize`
</td>
<td>
Enables the normalize command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:deny-normalize`
</td>
<td>
Denies the normalize command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:allow-resolve`
</td>
<td>
Enables the resolve command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:deny-resolve`
</td>
<td>
Denies the resolve command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:allow-resolve-directory`
</td>
<td>
Enables the resolve_directory command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`path:deny-resolve-directory`
</td>
<td>
Denies the resolve_directory command without any pre-configured scope.
</td>
</tr>
</table>

View File

@ -1,5 +1,41 @@
| Permission | Description |
|------|-----|
|`allow-close`|Enables the close command without any pre-configured scope.|
|`deny-close`|Denies the close command without any pre-configured scope.|
|`default`|Default permissions for the plugin.|
## Default Permission
Default permissions for the plugin.
- `allow-close`
### Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`resources:allow-close`
</td>
<td>
Enables the close command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`resources:deny-close`
</td>
<td>
Denies the close command without any pre-configured scope.
</td>
</tr>
</table>

View File

@ -1,25 +1,311 @@
| Permission | Description |
|------|-----|
|`allow-get-by-id`|Enables the get_by_id command without any pre-configured scope.|
|`deny-get-by-id`|Denies the get_by_id command without any pre-configured scope.|
|`allow-new`|Enables the new command without any pre-configured scope.|
|`deny-new`|Denies the new command without any pre-configured scope.|
|`allow-remove-by-id`|Enables the remove_by_id command without any pre-configured scope.|
|`deny-remove-by-id`|Denies the remove_by_id command without any pre-configured scope.|
|`allow-set-icon`|Enables the set_icon command without any pre-configured scope.|
|`deny-set-icon`|Denies the set_icon command without any pre-configured scope.|
|`allow-set-icon-as-template`|Enables the set_icon_as_template command without any pre-configured scope.|
|`deny-set-icon-as-template`|Denies the set_icon_as_template command without any pre-configured scope.|
|`allow-set-menu`|Enables the set_menu command without any pre-configured scope.|
|`deny-set-menu`|Denies the set_menu command without any pre-configured scope.|
|`allow-set-show-menu-on-left-click`|Enables the set_show_menu_on_left_click command without any pre-configured scope.|
|`deny-set-show-menu-on-left-click`|Denies the set_show_menu_on_left_click command without any pre-configured scope.|
|`allow-set-temp-dir-path`|Enables the set_temp_dir_path command without any pre-configured scope.|
|`deny-set-temp-dir-path`|Denies the set_temp_dir_path command without any pre-configured scope.|
|`allow-set-title`|Enables the set_title command without any pre-configured scope.|
|`deny-set-title`|Denies the set_title command without any pre-configured scope.|
|`allow-set-tooltip`|Enables the set_tooltip command without any pre-configured scope.|
|`deny-set-tooltip`|Denies the set_tooltip command without any pre-configured scope.|
|`allow-set-visible`|Enables the set_visible command without any pre-configured scope.|
|`deny-set-visible`|Denies the set_visible command without any pre-configured scope.|
|`default`|Default permissions for the plugin.|
## Default Permission
Default permissions for the plugin.
- `allow-new`
- `allow-get-by-id`
- `allow-remove-by-id`
- `allow-set-icon`
- `allow-set-menu`
- `allow-set-tooltip`
- `allow-set-title`
- `allow-set-visible`
- `allow-set-temp-dir-path`
- `allow-set-icon-as-template`
- `allow-set-show-menu-on-left-click`
### Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`tray:allow-get-by-id`
</td>
<td>
Enables the get_by_id command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-get-by-id`
</td>
<td>
Denies the get_by_id command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-new`
</td>
<td>
Enables the new command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-new`
</td>
<td>
Denies the new command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-remove-by-id`
</td>
<td>
Enables the remove_by_id command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-remove-by-id`
</td>
<td>
Denies the remove_by_id command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-set-icon`
</td>
<td>
Enables the set_icon command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-set-icon`
</td>
<td>
Denies the set_icon command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-set-icon-as-template`
</td>
<td>
Enables the set_icon_as_template command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-set-icon-as-template`
</td>
<td>
Denies the set_icon_as_template command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-set-menu`
</td>
<td>
Enables the set_menu command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-set-menu`
</td>
<td>
Denies the set_menu command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-set-show-menu-on-left-click`
</td>
<td>
Enables the set_show_menu_on_left_click command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-set-show-menu-on-left-click`
</td>
<td>
Denies the set_show_menu_on_left_click command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-set-temp-dir-path`
</td>
<td>
Enables the set_temp_dir_path command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-set-temp-dir-path`
</td>
<td>
Denies the set_temp_dir_path command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-set-title`
</td>
<td>
Enables the set_title command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-set-title`
</td>
<td>
Denies the set_title command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-set-tooltip`
</td>
<td>
Enables the set_tooltip command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-set-tooltip`
</td>
<td>
Denies the set_tooltip command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:allow-set-visible`
</td>
<td>
Enables the set_visible command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`tray:deny-set-visible`
</td>
<td>
Denies the set_visible command without any pre-configured scope.
</td>
</tr>
</table>

View File

@ -1,27 +1,329 @@
| Permission | Description |
|------|-----|
|`allow-create-webview`|Enables the create_webview command without any pre-configured scope.|
|`deny-create-webview`|Denies the create_webview command without any pre-configured scope.|
|`allow-create-webview-window`|Enables the create_webview_window command without any pre-configured scope.|
|`deny-create-webview-window`|Denies the create_webview_window command without any pre-configured scope.|
|`allow-internal-toggle-devtools`|Enables the internal_toggle_devtools command without any pre-configured scope.|
|`deny-internal-toggle-devtools`|Denies the internal_toggle_devtools command without any pre-configured scope.|
|`allow-print`|Enables the print command without any pre-configured scope.|
|`deny-print`|Denies the print command without any pre-configured scope.|
|`allow-reparent`|Enables the reparent command without any pre-configured scope.|
|`deny-reparent`|Denies the reparent command without any pre-configured scope.|
|`allow-set-webview-focus`|Enables the set_webview_focus command without any pre-configured scope.|
|`deny-set-webview-focus`|Denies the set_webview_focus command without any pre-configured scope.|
|`allow-set-webview-position`|Enables the set_webview_position command without any pre-configured scope.|
|`deny-set-webview-position`|Denies the set_webview_position command without any pre-configured scope.|
|`allow-set-webview-size`|Enables the set_webview_size command without any pre-configured scope.|
|`deny-set-webview-size`|Denies the set_webview_size command without any pre-configured scope.|
|`allow-set-webview-zoom`|Enables the set_webview_zoom command without any pre-configured scope.|
|`deny-set-webview-zoom`|Denies the set_webview_zoom command without any pre-configured scope.|
|`allow-webview-close`|Enables the webview_close command without any pre-configured scope.|
|`deny-webview-close`|Denies the webview_close command without any pre-configured scope.|
|`allow-webview-position`|Enables the webview_position command without any pre-configured scope.|
|`deny-webview-position`|Denies the webview_position command without any pre-configured scope.|
|`allow-webview-size`|Enables the webview_size command without any pre-configured scope.|
|`deny-webview-size`|Denies the webview_size command without any pre-configured scope.|
|`default`|Default permissions for the plugin.|
## Default Permission
Default permissions for the plugin.
- `allow-webview-position`
- `allow-webview-size`
- `allow-internal-toggle-devtools`
### Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`webview:allow-create-webview`
</td>
<td>
Enables the create_webview command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-create-webview`
</td>
<td>
Denies the create_webview command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-create-webview-window`
</td>
<td>
Enables the create_webview_window command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-create-webview-window`
</td>
<td>
Denies the create_webview_window command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-internal-toggle-devtools`
</td>
<td>
Enables the internal_toggle_devtools command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-internal-toggle-devtools`
</td>
<td>
Denies the internal_toggle_devtools command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-print`
</td>
<td>
Enables the print command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-print`
</td>
<td>
Denies the print command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-reparent`
</td>
<td>
Enables the reparent command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-reparent`
</td>
<td>
Denies the reparent command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-set-webview-focus`
</td>
<td>
Enables the set_webview_focus command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-set-webview-focus`
</td>
<td>
Denies the set_webview_focus command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-set-webview-position`
</td>
<td>
Enables the set_webview_position command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-set-webview-position`
</td>
<td>
Denies the set_webview_position command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-set-webview-size`
</td>
<td>
Enables the set_webview_size command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-set-webview-size`
</td>
<td>
Denies the set_webview_size command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-set-webview-zoom`
</td>
<td>
Enables the set_webview_zoom command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-set-webview-zoom`
</td>
<td>
Denies the set_webview_zoom command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-webview-close`
</td>
<td>
Enables the webview_close command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-webview-close`
</td>
<td>
Denies the webview_close command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-webview-position`
</td>
<td>
Enables the webview_position command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-webview-position`
</td>
<td>
Denies the webview_position command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:allow-webview-size`
</td>
<td>
Enables the webview_size command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`webview:deny-webview-size`
</td>
<td>
Denies the webview_size command without any pre-configured scope.
</td>
</tr>
</table>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -39,7 +39,9 @@
const keys = Object.keys(event.data.payload || {})
return (
keys.length > 0 &&
keys.every((key) => key === 'nonce' || key === 'payload')
keys.every(
(key) => key === 'contentType' || key === 'nonce' || key === 'payload'
)
)
}
return false

View File

@ -22,8 +22,8 @@ use crate::{
utils::config::Config,
utils::Env,
webview::PageLoadPayload,
Context, DeviceEventFilter, EventLoopMessage, Manager, Monitor, Runtime, Scopes, StateManager,
Theme, Webview, WebviewWindowBuilder, Window,
Context, DeviceEventFilter, Emitter, EventLoopMessage, Listener, Manager, Monitor, Result,
Runtime, Scopes, StateManager, Theme, Webview, WebviewWindowBuilder, Window,
};
#[cfg(desktop)]
@ -42,6 +42,7 @@ use tauri_runtime::{
};
use tauri_utils::PackageInfo;
use serde::Serialize;
use std::{
borrow::Cow,
collections::HashMap,
@ -66,7 +67,7 @@ pub(crate) type GlobalWebviewEventListener<R> =
Box<dyn Fn(&Webview<R>, &WebviewEvent) + Send + Sync>;
/// A closure that is run when the Tauri application is setting up.
pub type SetupHook<R> =
Box<dyn FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send>;
Box<dyn FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send>;
/// A closure that is run every time a page starts or finishes loading.
pub type OnPageLoad<R> = dyn Fn(&Webview<R>, &PageLoadPayload<'_>) + Send + Sync + 'static;
@ -328,7 +329,7 @@ impl<R: Runtime> Clone for AppHandle<R> {
impl<'de, R: Runtime> CommandArg<'de, R> for AppHandle<R> {
/// Grabs the [`Window`] from the [`CommandItem`] and returns the associated [`AppHandle`]. This will never fail.
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
fn from_command(command: CommandItem<'de, R>) -> std::result::Result<Self, InvokeError> {
Ok(command.message.webview().window().app_handle)
}
}
@ -417,7 +418,7 @@ impl<R: Runtime> AppHandle<R> {
}
/// Restarts the app by triggering [`RunEvent::ExitRequested`] with code [`RESTART_EXIT_CODE`] and [`RunEvent::Exit`]..
pub fn restart(&self) {
pub fn restart(&self) -> ! {
if self.runtime_handle.request_exit(RESTART_EXIT_CODE).is_err() {
self.cleanup_before_exit();
}
@ -820,14 +821,13 @@ macro_rules! shared_app_impl {
}
}
/// Event system APIs.
impl<R: Runtime> $app {
impl<R: Runtime> Listener<R> for $app {
/// Listen to an event on this app.
///
/// # Examples
///
/// ```
/// use tauri::Manager;
/// use tauri::Listener;
///
/// tauri::Builder::default()
/// .setup(|app| {
@ -838,19 +838,29 @@ macro_rules! shared_app_impl {
/// Ok(())
/// });
/// ```
pub fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
self.manager.listen(event.into(), EventTarget::App, handler)
}
/// Listen to an event on this app only once.
///
/// See [`Self::listen`] for more information.
fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
self.manager.once(event.into(), EventTarget::App, handler)
}
/// Unlisten to an event on this app.
///
/// # Examples
///
/// ```
/// use tauri::Manager;
/// use tauri::Listener;
///
/// tauri::Builder::default()
/// .setup(|app| {
@ -864,18 +874,82 @@ macro_rules! shared_app_impl {
/// Ok(())
/// });
/// ```
pub fn unlisten(&self, id: EventId) {
fn unlisten(&self, id: EventId) {
self.manager.unlisten(id)
}
}
/// Listen to an event on this app only once.
impl<R: Runtime> Emitter<R> for $app {
/// Emits an event to all [targets](EventTarget).
///
/// See [`Self::listen`] for more information.
pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
/// # Examples
/// ```
/// use tauri::Emitter;
///
/// #[tauri::command]
/// fn synchronize(app: tauri::AppHandle) {
/// // emits the synchronized event to all webviews
/// app.emit("synchronized", ());
/// }
/// ```
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
self.manager.emit(event, payload)
}
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to all listeners
/// app.emit_to(EventTarget::any(), "download-progress", i);
/// // emit an event to listeners that used App::listen or AppHandle::listen
/// app.emit_to(EventTarget::app(), "download-progress", i);
/// // emit an event to any webview/window/webviewWindow matching the given label
/// app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
/// app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
/// // emit an event to listeners that used WebviewWindow::listen
/// app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
/// }
/// }
/// ```
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
where
F: FnOnce(Event) + Send + 'static,
I: Into<EventTarget>,
S: Serialize + Clone,
{
self.manager.once(event.into(), EventTarget::App, handler)
self.manager.emit_to(target, event, payload)
}
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// app.emit_filter("download-progress", i, |t| match t {
/// EventTarget::WebviewWindow { label } => label == "main",
/// _ => false,
/// });
/// }
/// }
/// ```
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool,
{
self.manager.emit_filter(event, payload, filter)
}
}
};
@ -1239,7 +1313,7 @@ tauri::Builder::default()
#[must_use]
pub fn setup<F>(mut self, setup: F) -> Self
where
F: FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send + 'static,
F: FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send + 'static,
{
self.setup = Box::new(setup);
self

View File

@ -175,22 +175,20 @@ pub fn listen_js_script(
) -> String {
format!(
"(function () {{
if (window['{listeners}'] === void 0) {{
Object.defineProperty(window, '{listeners}', {{ value: Object.create(null) }});
if (window['{listeners_object_name}'] === void 0) {{
Object.defineProperty(window, '{listeners_object_name}', {{ value: Object.create(null) }});
}}
if (window['{listeners}']['{event}'] === void 0) {{
Object.defineProperty(window['{listeners}'], '{event}', {{ value: Object.create(null) }});
if (window['{listeners_object_name}']['{event}'] === void 0) {{
Object.defineProperty(window['{listeners_object_name}'], '{event}', {{ value: Object.create(null) }});
}}
const eventListeners = window['{listeners}']['{event}']
const eventListeners = window['{listeners_object_name}']['{event}']
const listener = {{
target: {target},
target: {serialized_target},
handler: {handler}
}};
Object.defineProperty(eventListeners, '{event_id}', {{ value: listener, configurable: true }});
}})()
",
listeners = listeners_object_name,
target = serialized_target,
)
}

View File

@ -9,8 +9,8 @@ use serde_json::Value as JsonValue;
use tauri_runtime::window::is_label_valid;
use crate::plugin::{Builder, TauriPlugin};
use crate::{command, ipc::CallbackFn, EventId, Manager, Result, Runtime};
use crate::{AppHandle, Webview};
use crate::{command, ipc::CallbackFn, EventId, Result, Runtime};
use crate::{AppHandle, Emitter, Webview};
use super::{is_event_name_valid, EventTarget};

View File

@ -132,7 +132,7 @@ impl TryFrom<Image<'_>> for tray_icon::Icon {
}
/// An image type that accepts file paths, raw bytes, previously loaded images and image objects.
/// This type is meant to be used along the [transformImage](https://beta.tauri.app/references/v2/js/image/namespaceimage/#transformimage) API.
/// This type is meant to be used along the [transformImage](https://v2.tauri.app/reference/javascript/api/namespaceimage/#transformimage) API.
///
/// # Stability
///

View File

@ -37,12 +37,20 @@ pub struct ChannelDataIpcQueue(pub(crate) Arc<Mutex<HashMap<u32, InvokeBody>>>);
/// An IPC channel.
#[derive(Clone)]
pub struct Channel {
pub struct Channel<TSend = InvokeBody> {
id: u32,
on_message: Arc<dyn Fn(InvokeBody) -> crate::Result<()> + Send + Sync>,
phantom: std::marker::PhantomData<TSend>,
}
impl Serialize for Channel {
#[cfg(feature = "specta")]
const _: () = {
#[derive(specta::Type)]
#[specta(remote = Channel, rename = "TAURI_CHANNEL")]
struct Channel<TSend>(std::marker::PhantomData<TSend>);
};
impl<TSend> Serialize for Channel<TSend> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
@ -88,7 +96,7 @@ impl FromStr for JavaScriptChannelId {
impl JavaScriptChannelId {
/// Gets a [`Channel`] for this channel ID on the given [`Webview`].
pub fn channel_on<R: Runtime>(&self, webview: Webview<R>) -> Channel {
pub fn channel_on<R: Runtime, TSend>(&self, webview: Webview<R>) -> Channel<TSend> {
let callback_id = self.0;
let counter = AtomicUsize::new(0);
@ -128,7 +136,7 @@ impl<'de> Deserialize<'de> for JavaScriptChannelId {
}
}
impl Channel {
impl<TSend> Channel<TSend> {
/// Creates a new channel with the given message handler.
pub fn new<F: Fn(InvokeBody) -> crate::Result<()> + Send + Sync + 'static>(
on_message: F,
@ -144,10 +152,15 @@ impl Channel {
let channel = Self {
id,
on_message: Arc::new(on_message),
phantom: Default::default(),
};
#[cfg(mobile)]
crate::plugin::mobile::register_channel(channel.clone());
crate::plugin::mobile::register_channel(Channel {
id,
on_message: channel.on_message.clone(),
phantom: Default::default(),
});
channel
}
@ -178,13 +191,16 @@ impl Channel {
}
/// Sends the given data through the channel.
pub fn send<T: IpcResponse>(&self, data: T) -> crate::Result<()> {
pub fn send(&self, data: TSend) -> crate::Result<()>
where
TSend: IpcResponse,
{
let body = data.body()?;
(self.on_message)(body)
}
}
impl<'de, R: Runtime> CommandArg<'de, R> for Channel {
impl<'de, R: Runtime, TSend: Clone> CommandArg<'de, R> for Channel<TSend> {
/// Grabs the [`Webview`] from the [`CommandItem`] and returns the associated [`Channel`].
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
let name = command.name;
@ -196,8 +212,8 @@ impl<'de, R: Runtime> CommandArg<'de, R> for Channel {
.map(|id| id.channel_on(webview))
.map_err(|_| {
InvokeError::from_anyhow(anyhow::anyhow!(
"invalid channel value `{value}`, expected a string in the `{IPC_PAYLOAD_PREFIX}ID` format"
))
"invalid channel value `{value}`, expected a string in the `{IPC_PAYLOAD_PREFIX}ID` format"
))
})
}
}

View File

@ -10,7 +10,10 @@ use std::sync::{Arc, Mutex};
use futures_util::Future;
use http::HeaderMap;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde::{
de::{DeserializeOwned, IntoDeserializer},
Deserialize, Serialize,
};
use serde_json::Value as JsonValue;
pub use serialize_to_javascript::Options as SerializeOptions;
use tauri_macros::default_runtime;
@ -43,6 +46,7 @@ pub type OwnedInvokeResponder<R> =
/// Possible values of an IPC payload.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub enum InvokeBody {
/// Json payload.
Json(JsonValue),
@ -89,7 +93,7 @@ impl InvokeBody {
pub fn deserialize<T: DeserializeOwned>(self) -> serde_json::Result<T> {
match self {
InvokeBody::Json(v) => serde_json::from_value(v),
InvokeBody::Raw(v) => serde_json::from_slice(&v),
InvokeBody::Raw(v) => T::deserialize(v.into_deserializer()),
}
}
}
@ -518,3 +522,28 @@ impl<R: Runtime> InvokeMessage<R> {
/// The `Callback` type is the return value of the `transformCallback` JavaScript function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct CallbackFn(pub u32);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize_invoke_body() {
let json = InvokeBody::Json(serde_json::Value::Array(vec![
serde_json::Value::Number(1.into()),
serde_json::Value::Number(123.into()),
serde_json::Value::Number(1231.into()),
]));
assert_eq!(json.deserialize::<Vec<u16>>().unwrap(), vec![1, 123, 1231]);
let json = InvokeBody::Json(serde_json::Value::String("string value".into()));
assert_eq!(json.deserialize::<String>().unwrap(), "string value");
let json = InvokeBody::Json(serde_json::Value::String("string value".into()));
assert!(json.deserialize::<Vec<u16>>().is_err());
let values = vec![1, 2, 3, 4, 5, 6, 1];
let raw = InvokeBody::Raw(values.clone());
assert_eq!(raw.deserialize::<Vec<u8>>().unwrap(), values);
}
}

View File

@ -388,6 +388,15 @@ fn parse_invoke_request<R: Runtime>(
// so we must ignore it because some commands use the IPC for faster response
let has_payload = !body.is_empty();
#[allow(unused_mut)]
let mut content_type = parts
.headers
.get(http::header::CONTENT_TYPE)
.and_then(|h| h.to_str().ok())
.map(|mime| mime.parse())
.unwrap_or(Ok(mime::APPLICATION_OCTET_STREAM))
.map_err(|_| "unknown content type")?;
#[cfg(feature = "isolation")]
if let crate::Pattern::Isolation { crypto_keys, .. } = &*manager.pattern {
// if the platform does not support request body, we ignore it
@ -395,8 +404,18 @@ fn parse_invoke_request<R: Runtime>(
#[cfg(feature = "tracing")]
let _span = tracing::trace_span!("ipc::request::decrypt_isolation_payload").entered();
body = crate::utils::pattern::isolation::RawIsolationPayload::try_from(&body)
.and_then(|raw| crypto_keys.decrypt(raw))
(body, content_type) = crate::utils::pattern::isolation::RawIsolationPayload::try_from(&body)
.and_then(|raw| {
let content_type = raw.content_type().clone();
crypto_keys.decrypt(raw).map(|decrypted| {
(
decrypted,
content_type
.parse()
.unwrap_or(mime::APPLICATION_OCTET_STREAM),
)
})
})
.map_err(|e| e.to_string())?;
}
}
@ -440,14 +459,6 @@ fn parse_invoke_request<R: Runtime>(
.map_err(|_| "Tauri error header value must be a numeric string")?,
);
let content_type = parts
.headers
.get(http::header::CONTENT_TYPE)
.and_then(|h| h.to_str().ok())
.map(|mime| mime.parse())
.unwrap_or(Ok(mime::APPLICATION_OCTET_STREAM))
.map_err(|_| "unknown content type")?;
#[cfg(feature = "tracing")]
let span = tracing::trace_span!("ipc::request::deserialize").entered();
@ -481,3 +492,194 @@ fn parse_invoke_request<R: Runtime>(
Ok(payload)
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
use crate::{manager::AppManager, plugin::PluginStore, StateManager, Wry};
use http::header::*;
use serde_json::json;
use tauri_macros::generate_context;
#[test]
fn parse_invoke_request() {
let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate, test = true);
let manager: AppManager<Wry> = AppManager::with_handlers(
context,
PluginStore::default(),
Box::new(|_| false),
None,
Default::default(),
StateManager::new(),
Default::default(),
Default::default(),
Default::default(),
(None, "".into()),
crate::generate_invoke_key().unwrap(),
);
let cmd = "write_something";
let url = "tauri://localhost";
let invoke_key = "1234ahdsjkl123";
let callback = 12378123;
let error = 6243;
let headers = HeaderMap::from_iter(vec![
(
CONTENT_TYPE,
HeaderValue::from_str(mime::APPLICATION_OCTET_STREAM.as_ref()).unwrap(),
),
(
HeaderName::from_str(TAURI_INVOKE_KEY_HEADER_NAME).unwrap(),
HeaderValue::from_str(invoke_key).unwrap(),
),
(
HeaderName::from_str(TAURI_CALLBACK_HEADER_NAME).unwrap(),
HeaderValue::from_str(&callback.to_string()).unwrap(),
),
(
HeaderName::from_str(TAURI_ERROR_HEADER_NAME).unwrap(),
HeaderValue::from_str(&error.to_string()).unwrap(),
),
(ORIGIN, HeaderValue::from_str("tauri://localhost").unwrap()),
]);
let mut request = Request::builder().uri(format!("ipc://localhost/{cmd}"));
*request.headers_mut().unwrap() = headers.clone();
let body = vec![123, 31, 45];
let request = request.body(body.clone()).unwrap();
let invoke_request = super::parse_invoke_request(&manager, request).unwrap();
assert_eq!(invoke_request.cmd, cmd);
assert_eq!(invoke_request.callback.0, callback);
assert_eq!(invoke_request.error.0, error);
assert_eq!(invoke_request.invoke_key, invoke_key);
assert_eq!(invoke_request.url, url.parse().unwrap());
assert_eq!(invoke_request.headers, headers);
assert_eq!(invoke_request.body, InvokeBody::Raw(body));
let body = json!({
"key": 1,
"anotherKey": "asda",
});
let mut headers = headers.clone();
headers.insert(
CONTENT_TYPE,
HeaderValue::from_str(mime::APPLICATION_JSON.as_ref()).unwrap(),
);
let mut request = Request::builder().uri(format!("ipc://localhost/{cmd}"));
*request.headers_mut().unwrap() = headers.clone();
let request = request.body(serde_json::to_vec(&body).unwrap()).unwrap();
let invoke_request = super::parse_invoke_request(&manager, request).unwrap();
assert_eq!(invoke_request.headers, headers);
assert_eq!(invoke_request.body, InvokeBody::Json(body));
}
#[test]
#[cfg(feature = "isolation")]
fn parse_invoke_request_isolation() {
let context = generate_context!(
"test/fixture/isolation/src-tauri/tauri.conf.json",
crate,
test = false
);
let crate::pattern::Pattern::Isolation { crypto_keys, .. } = &context.pattern else {
unreachable!()
};
let mut nonce = [0u8; 12];
getrandom::getrandom(&mut nonce).unwrap();
let body_raw = vec![1, 41, 65, 12, 78];
let body_bytes = crypto_keys.aes_gcm().encrypt(&nonce, &body_raw).unwrap();
let isolation_payload_raw = json!({
"nonce": nonce,
"payload": body_bytes,
"contentType": mime::APPLICATION_OCTET_STREAM.to_string(),
});
let body_json = json!({
"key": 1,
"anotherKey": "string"
});
let body_bytes = crypto_keys
.aes_gcm()
.encrypt(&nonce, &serde_json::to_vec(&body_json).unwrap())
.unwrap();
let isolation_payload_json = json!({
"nonce": nonce,
"payload": body_bytes,
"contentType": mime::APPLICATION_JSON.to_string(),
});
let manager: AppManager<Wry> = AppManager::with_handlers(
context,
PluginStore::default(),
Box::new(|_| false),
None,
Default::default(),
StateManager::new(),
Default::default(),
Default::default(),
Default::default(),
(None, "".into()),
crate::generate_invoke_key().unwrap(),
);
let cmd = "write_something";
let url = "tauri://localhost";
let invoke_key = "1234ahdsjkl123";
let callback = 12378123;
let error = 6243;
let headers = HeaderMap::from_iter(vec![
(
CONTENT_TYPE,
HeaderValue::from_str(mime::APPLICATION_JSON.as_ref()).unwrap(),
),
(
HeaderName::from_str(TAURI_INVOKE_KEY_HEADER_NAME).unwrap(),
HeaderValue::from_str(invoke_key).unwrap(),
),
(
HeaderName::from_str(TAURI_CALLBACK_HEADER_NAME).unwrap(),
HeaderValue::from_str(&callback.to_string()).unwrap(),
),
(
HeaderName::from_str(TAURI_ERROR_HEADER_NAME).unwrap(),
HeaderValue::from_str(&error.to_string()).unwrap(),
),
(ORIGIN, HeaderValue::from_str("tauri://localhost").unwrap()),
]);
let mut request = Request::builder().uri(format!("ipc://localhost/{cmd}"));
*request.headers_mut().unwrap() = headers.clone();
let body = serde_json::to_vec(&isolation_payload_raw).unwrap();
let request = request.body(body).unwrap();
let invoke_request = super::parse_invoke_request(&manager, request).unwrap();
assert_eq!(invoke_request.cmd, cmd);
assert_eq!(invoke_request.callback.0, callback);
assert_eq!(invoke_request.error.0, error);
assert_eq!(invoke_request.invoke_key, invoke_key);
assert_eq!(invoke_request.url, url.parse().unwrap());
assert_eq!(invoke_request.headers, headers);
assert_eq!(invoke_request.body, InvokeBody::Raw(body_raw));
let mut request = Request::builder().uri(format!("ipc://localhost/{cmd}"));
*request.headers_mut().unwrap() = headers.clone();
let body = serde_json::to_vec(&isolation_payload_json).unwrap();
let request = request.body(body).unwrap();
let invoke_request = super::parse_invoke_request(&manager, request).unwrap();
assert_eq!(invoke_request.headers, headers);
assert_eq!(invoke_request.body, InvokeBody::Json(body_json));
}
}

View File

@ -539,178 +539,6 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
self.manager().package_info()
}
/// Listen to an emitted event to any [target](EventTarget).
///
/// # Examples
/// ```
/// use tauri::Manager;
///
/// #[tauri::command]
/// fn synchronize(window: tauri::Window) {
/// // emits the synchronized event to all windows
/// window.emit("synchronized", ());
/// }
///
/// tauri::Builder::default()
/// .setup(|app| {
/// app.listen_any("synchronized", |event| {
/// println!("app is in sync");
/// });
/// Ok(())
/// })
/// .invoke_handler(tauri::generate_handler![synchronize]);
/// ```
fn listen_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
self
.manager()
.listen(event.into(), EventTarget::Any, handler)
}
/// Remove an event listener.
///
/// # Examples
/// ```
/// use tauri::Manager;
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle().clone();
/// let handler = app.listen_any("ready", move |event| {
/// println!("app is ready");
///
/// // we no longer need to listen to the event
/// // we also could have used `app.once_global` instead
/// handle.unlisten(event.id());
/// });
///
/// // stop listening to the event when you do not need it anymore
/// app.unlisten(handler);
///
///
/// Ok(())
/// });
/// ```
fn unlisten(&self, id: EventId) {
self.manager().unlisten(id)
}
/// Listens once to an emitted event to any [target](EventTarget) .
///
/// See [`Self::listen_any`] for more information.
fn once_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
self.manager().once(event.into(), EventTarget::Any, handler)
}
/// Emits an event to all [targets](EventTarget).
///
/// # Examples
/// ```
/// use tauri::Manager;
///
/// #[tauri::command]
/// fn synchronize(app: tauri::AppHandle) {
/// // emits the synchronized event to all webviews
/// app.emit("synchronized", ());
/// }
/// ```
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit", skip(self, payload))
)]
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
self.manager().emit(event, payload)
}
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
/// ```
/// use tauri::{Manager, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to all listeners
/// app.emit_to(EventTarget::any(), "download-progress", i);
/// // emit an event to listeners that used App::listen or AppHandle::listen
/// app.emit_to(EventTarget::app(), "download-progress", i);
/// // emit an event to any webview/window/webviewWindow matching the given label
/// app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
/// app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
/// // emit an event to listeners that used WebviewWindow::listen
/// app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
/// }
/// }
/// ```
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit::to", skip(self, target, payload), fields(target))
)]
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone,
{
let target = target.into();
#[cfg(feature = "tracing")]
tracing::Span::current().record("target", format!("{target:?}"));
match target {
// if targeting all, emit to all using emit without filter
EventTarget::Any => self.manager().emit(event, payload),
// if targeting any label, emit using emit_filter and filter labels
EventTarget::AnyLabel {
label: target_label,
} => self.manager().emit_filter(event, payload, |t| match t {
EventTarget::Window { label }
| EventTarget::Webview { label }
| EventTarget::WebviewWindow { label } => label == &target_label,
_ => false,
}),
// otherwise match same target
_ => self.manager().emit_filter(event, payload, |t| t == &target),
}
}
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
/// ```
/// use tauri::{Manager, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// app.emit_filter("download-progress", i, |t| match t {
/// EventTarget::WebviewWindow { label } => label == "main",
/// _ => false,
/// });
/// }
/// }
/// ```
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit::filter", skip(self, payload, filter))
)]
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool,
{
self.manager().emit_filter(event, payload, filter)
}
/// Fetch a single window from the manager.
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
@ -933,6 +761,174 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
}
}
/// Listen to events.
pub trait Listener<R: Runtime>: sealed::ManagerBase<R> {
/// Listen to an emitted event on this manager.
///
/// # Examples
/// ```
/// use tauri::{Manager, Listener, Emitter};
///
/// #[tauri::command]
/// fn synchronize(window: tauri::Window) {
/// // emits the synchronized event to all windows
/// window.emit("synchronized", ());
/// }
///
/// tauri::Builder::default()
/// .setup(|app| {
/// app.listen("synchronized", |event| {
/// println!("app is in sync");
/// });
/// Ok(())
/// })
/// .invoke_handler(tauri::generate_handler![synchronize]);
/// ```
fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static;
/// Listen to an event on this manager only once.
///
/// See [`Self::listen`] for more information.
fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static;
/// Remove an event listener.
///
/// # Examples
/// ```
/// use tauri::{Manager, Listener};
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle().clone();
/// let handler = app.listen_any("ready", move |event| {
/// println!("app is ready");
///
/// // we no longer need to listen to the event
/// // we also could have used `app.once_global` instead
/// handle.unlisten(event.id());
/// });
///
/// // stop listening to the event when you do not need it anymore
/// app.unlisten(handler);
///
///
/// Ok(())
/// });
/// ```
fn unlisten(&self, id: EventId);
/// Listen to an emitted event to any [target](EventTarget).
///
/// # Examples
/// ```
/// use tauri::{Manager, Emitter, Listener};
///
/// #[tauri::command]
/// fn synchronize(window: tauri::Window) {
/// // emits the synchronized event to all windows
/// window.emit("synchronized", ());
/// }
///
/// tauri::Builder::default()
/// .setup(|app| {
/// app.listen_any("synchronized", |event| {
/// println!("app is in sync");
/// });
/// Ok(())
/// })
/// .invoke_handler(tauri::generate_handler![synchronize]);
/// ```
fn listen_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
self
.manager()
.listen(event.into(), EventTarget::Any, handler)
}
/// Listens once to an emitted event to any [target](EventTarget) .
///
/// See [`Self::listen_any`] for more information.
fn once_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
self.manager().once(event.into(), EventTarget::Any, handler)
}
}
/// Emit events.
pub trait Emitter<R: Runtime>: sealed::ManagerBase<R> {
/// Emits an event to all [targets](EventTarget).
///
/// # Examples
/// ```
/// use tauri::Emitter;
///
/// #[tauri::command]
/// fn synchronize(app: tauri::AppHandle) {
/// // emits the synchronized event to all webviews
/// app.emit("synchronized", ());
/// }
/// ```
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()>;
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to all listeners
/// app.emit_to(EventTarget::any(), "download-progress", i);
/// // emit an event to listeners that used App::listen or AppHandle::listen
/// app.emit_to(EventTarget::app(), "download-progress", i);
/// // emit an event to any webview/window/webviewWindow matching the given label
/// app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
/// app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
/// // emit an event to listeners that used WebviewWindow::listen
/// app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
/// }
/// }
/// ```
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone;
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// app.emit_filter("download-progress", i, |t| match t {
/// EventTarget::WebviewWindow { label } => label == "main",
/// _ => false,
/// });
/// }
/// }
/// ```
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool;
}
/// Prevent implementation details from leaking out of the [`Manager`] trait.
pub(crate) mod sealed {
use super::Runtime;

View File

@ -472,10 +472,6 @@ impl<R: Runtime> AppManager<R> {
self.listeners().listen(event, target, handler)
}
pub fn unlisten(&self, id: EventId) {
self.listeners().unlisten(id)
}
pub fn once<F: FnOnce(Event) + Send + 'static>(
&self,
event: String,
@ -486,6 +482,33 @@ impl<R: Runtime> AppManager<R> {
self.listeners().once(event, target, handler)
}
pub fn unlisten(&self, id: EventId) {
self.listeners().unlisten(id)
}
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit", skip(self, payload))
)]
pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
assert_event_name_is_valid(event);
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("emit::run").entered();
let emit_args = EmitArgs::new(event, payload)?;
let listeners = self.listeners();
listeners.emit_js(self.webview.webviews_lock().values(), event, &emit_args)?;
listeners.emit(emit_args)?;
Ok(())
}
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit::filter", skip(self, payload, filter))
)]
pub fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
S: Serialize + Clone,
@ -511,19 +534,36 @@ impl<R: Runtime> AppManager<R> {
Ok(())
}
pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
assert_event_name_is_valid(event);
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit::to", skip(self, target, payload), fields(target))
)]
pub fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> crate::Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone,
{
let target = target.into();
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("emit::run").entered();
let emit_args = EmitArgs::new(event, payload)?;
tracing::Span::current().record("target", format!("{target:?}"));
let listeners = self.listeners();
match target {
// if targeting all, emit to all using emit without filter
EventTarget::Any => self.emit(event, payload),
listeners.emit_js(self.webview.webviews_lock().values(), event, &emit_args)?;
listeners.emit(emit_args)?;
// if targeting any label, emit using emit_filter and filter labels
EventTarget::AnyLabel {
label: target_label,
} => self.emit_filter(event, payload, |t| match t {
EventTarget::Window { label }
| EventTarget::Webview { label }
| EventTarget::WebviewWindow { label } => label == &target_label,
_ => false,
}),
Ok(())
// otherwise match same target
_ => self.emit_filter(event, payload, |t| t == &target),
}
}
pub fn get_window(&self, label: &str) -> Option<Window<R>> {
@ -634,7 +674,8 @@ mod test {
test::{mock_app, MockRuntime},
webview::WebviewBuilder,
window::WindowBuilder,
App, Manager, StateManager, Webview, WebviewWindow, WebviewWindowBuilder, Window, Wry,
App, Emitter, Listener, Manager, StateManager, Webview, WebviewWindow, WebviewWindowBuilder,
Window, Wry,
};
use super::AppManager;
@ -651,7 +692,7 @@ mod test {
#[test]
fn check_get_url() {
let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate);
let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate, test = true);
let manager: AppManager<Wry> = AppManager::with_handlers(
context,
PluginStore::default(),
@ -759,9 +800,7 @@ mod test {
assert_eq!(
received.len(),
expected.len(),
"received {:?} `{kind}` events but expected {:?}",
received,
expected
"received {received:?} `{kind}` events but expected {expected:?}"
);
}
@ -782,7 +821,11 @@ mod test {
run_emit_test("emit (webview_window)", webview_window, &rx);
}
fn run_emit_test<M: Manager<MockRuntime>>(kind: &str, m: M, rx: &Receiver<(&str, String)>) {
fn run_emit_test<M: Manager<MockRuntime> + Emitter<MockRuntime>>(
kind: &str,
m: M,
rx: &Receiver<(&str, String)>,
) {
let mut received = Vec::new();
let payload = "global-payload";
m.emit(TEST_EVENT_NAME, payload).unwrap();
@ -855,7 +898,7 @@ mod test {
);
}
fn run_emit_to_test<M: Manager<MockRuntime>>(
fn run_emit_to_test<M: Manager<MockRuntime> + Emitter<MockRuntime>>(
kind: &str,
m: &M,
window: &Window<MockRuntime>,

View File

@ -25,14 +25,11 @@ use crate::{
pattern::PatternJavascript,
sealed::ManagerBase,
webview::PageLoadPayload,
AppHandle, EventLoopMessage, EventTarget, Manager, Runtime, Scopes, Webview, Window,
AppHandle, Emitter, EventLoopMessage, EventTarget, Manager, Runtime, Scopes, Webview, Window,
};
use super::{
window::{
DragDropPayload, DragOverPayload, DRAG_EVENT, DROP_CANCELLED_EVENT, DROP_EVENT,
DROP_HOVER_EVENT,
},
window::{DragDropPayload, DRAG_DROP_EVENT, DRAG_ENTER_EVENT, DRAG_LEAVE_EVENT, DRAG_OVER_EVENT},
AppManager,
};
@ -631,7 +628,7 @@ impl<R: Runtime> WebviewManager<R> {
.webview_created(webview_);
});
#[cfg(target_os = "ios")]
#[cfg(all(target_os = "ios", feature = "wry"))]
{
webview
.with_webview(|w| {
@ -689,15 +686,21 @@ impl<R: Runtime> Webview<R> {
fn on_webview_event<R: Runtime>(webview: &Webview<R>, event: &WebviewEvent) -> crate::Result<()> {
match event {
WebviewEvent::DragDrop(event) => match event {
DragDropEvent::Dragged { paths, position } => {
let payload = DragDropPayload { paths, position };
webview.emit_to_webview(DRAG_EVENT, payload)?
DragDropEvent::Enter { paths, position } => {
let payload = DragDropPayload {
paths: Some(paths),
position,
};
webview.emit_to_webview(DRAG_ENTER_EVENT, payload)?
}
DragDropEvent::DragOver { position } => {
let payload = DragOverPayload { position };
webview.emit_to_webview(DROP_HOVER_EVENT, payload)?
DragDropEvent::Over { position } => {
let payload = DragDropPayload {
position,
paths: None,
};
webview.emit_to_webview(DRAG_OVER_EVENT, payload)?
}
DragDropEvent::Dropped { paths, position } => {
DragDropEvent::Drop { paths, position } => {
let scopes = webview.state::<Scopes>();
for path in paths {
if path.is_file() {
@ -706,10 +709,13 @@ fn on_webview_event<R: Runtime>(webview: &Webview<R>, event: &WebviewEvent) -> c
let _ = scopes.allow_directory(path, false);
}
}
let payload = DragDropPayload { paths, position };
webview.emit_to_webview(DROP_EVENT, payload)?
let payload = DragDropPayload {
paths: Some(paths),
position,
};
webview.emit_to_webview(DRAG_DROP_EVENT, payload)?
}
DragDropEvent::Cancelled => webview.emit_to_webview(DROP_CANCELLED_EVENT, ())?,
DragDropEvent::Leave => webview.emit_to_webview(DRAG_LEAVE_EVENT, ())?,
_ => unimplemented!(),
},
}

View File

@ -17,8 +17,8 @@ use tauri_runtime::{
};
use crate::{
app::GlobalWindowEventListener, image::Image, sealed::ManagerBase, AppHandle, EventLoopMessage,
EventTarget, Manager, Runtime, Scopes, Window, WindowEvent,
app::GlobalWindowEventListener, image::Image, sealed::ManagerBase, AppHandle, Emitter,
EventLoopMessage, EventTarget, Manager, Runtime, Scopes, Window, WindowEvent,
};
const WINDOW_RESIZED_EVENT: &str = "tauri://resize";
@ -29,10 +29,10 @@ const WINDOW_FOCUS_EVENT: &str = "tauri://focus";
const WINDOW_BLUR_EVENT: &str = "tauri://blur";
const WINDOW_SCALE_FACTOR_CHANGED_EVENT: &str = "tauri://scale-change";
const WINDOW_THEME_CHANGED: &str = "tauri://theme-changed";
pub const DRAG_EVENT: &str = "tauri://drag";
pub const DROP_EVENT: &str = "tauri://drop";
pub const DROP_HOVER_EVENT: &str = "tauri://drop-over";
pub const DROP_CANCELLED_EVENT: &str = "tauri://drag-cancelled";
pub(crate) const DRAG_ENTER_EVENT: &str = "tauri://drag-enter";
pub(crate) const DRAG_OVER_EVENT: &str = "tauri://drag-over";
pub(crate) const DRAG_DROP_EVENT: &str = "tauri://drag-drop";
pub(crate) const DRAG_LEAVE_EVENT: &str = "tauri://drag-leave";
pub struct WindowManager<R: Runtime> {
pub windows: Mutex<HashMap<String, Window<R>>>,
@ -144,13 +144,9 @@ impl<R: Runtime> Window<R> {
}
#[derive(Serialize, Clone)]
pub struct DragDropPayload<'a> {
pub paths: &'a Vec<PathBuf>,
pub position: &'a PhysicalPosition<f64>,
}
#[derive(Serialize, Clone)]
pub struct DragOverPayload<'a> {
pub(crate) struct DragDropPayload<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub paths: Option<&'a Vec<PathBuf>>,
pub position: &'a PhysicalPosition<f64>,
}
@ -194,28 +190,38 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
},
)?,
WindowEvent::DragDrop(event) => match event {
DragDropEvent::Dragged { paths, position } => {
let payload = DragDropPayload { paths, position };
DragDropEvent::Enter { paths, position } => {
let payload = DragDropPayload {
paths: Some(paths),
position,
};
if window.is_webview_window() {
window.emit_to(EventTarget::labeled(window.label()), DRAG_EVENT, payload)?
} else {
window.emit_to_window(DRAG_EVENT, payload)?
}
}
DragDropEvent::DragOver { position } => {
let payload = DragOverPayload { position };
if window.is_webview_window() {
window.emit_to(
EventTarget::labeled(window.label()),
DROP_HOVER_EVENT,
DRAG_ENTER_EVENT,
payload,
)?
} else {
window.emit_to_window(DROP_HOVER_EVENT, payload)?
window.emit_to_window(DRAG_ENTER_EVENT, payload)?
}
}
DragDropEvent::Dropped { paths, position } => {
DragDropEvent::Over { position } => {
let payload = DragDropPayload {
position,
paths: None,
};
if window.is_webview_window() {
window.emit_to(
EventTarget::labeled(window.label()),
DRAG_OVER_EVENT,
payload,
)?
} else {
window.emit_to_window(DRAG_OVER_EVENT, payload)?
}
}
DragDropEvent::Drop { paths, position } => {
let scopes = window.state::<Scopes>();
for path in paths {
if path.is_file() {
@ -224,23 +230,26 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
let _ = scopes.allow_directory(path, true);
}
}
let payload = DragDropPayload { paths, position };
let payload = DragDropPayload {
paths: Some(paths),
position,
};
if window.is_webview_window() {
window.emit_to(EventTarget::labeled(window.label()), DROP_EVENT, payload)?
} else {
window.emit_to_window(DROP_EVENT, payload)?
}
}
DragDropEvent::Cancelled => {
if window.is_webview_window() {
window.emit_to(
EventTarget::labeled(window.label()),
DROP_CANCELLED_EVENT,
(),
DRAG_DROP_EVENT,
payload,
)?
} else {
window.emit_to_window(DROP_CANCELLED_EVENT, ())?
window.emit_to_window(DRAG_DROP_EVENT, payload)?
}
}
DragDropEvent::Leave => {
if window.is_webview_window() {
window.emit_to(EventTarget::labeled(window.label()), DRAG_LEAVE_EVENT, ())?
} else {
window.emit_to_window(DRAG_LEAVE_EVENT, ())?
}
}
_ => unimplemented!(),

View File

@ -350,7 +350,7 @@ fn new<R: Runtime>(
kind: ItemKind,
options: Option<NewOptions>,
channels: State<'_, MenuChannels>,
handler: Channel,
handler: Channel<MenuId>,
) -> crate::Result<(ResourceId, MenuId)> {
let options = options.unwrap_or_default();
let mut resources_table = app.resources_table();
@ -866,7 +866,7 @@ fn set_icon<R: Runtime>(
}
}
struct MenuChannels(Mutex<HashMap<MenuId, Channel>>);
struct MenuChannels(Mutex<HashMap<MenuId, Channel<MenuId>>>);
pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("menu")
@ -877,7 +877,7 @@ pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
.on_event(|app, e| {
if let RunEvent::MenuEvent(e) = e {
if let Some(channel) = app.state::<MenuChannels>().0.lock().unwrap().get(&e.id) {
let _ = channel.send(&e.id);
let _ = channel.send(e.id.clone());
}
}
})

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