diff --git a/.changes/api-moduleresolution-node.md b/.changes/api-moduleresolution-node.md
new file mode 100644
index 000000000000..26e96bf91dff
--- /dev/null
+++ b/.changes/api-moduleresolution-node.md
@@ -0,0 +1,5 @@
+---
+"@tauri-apps/api": "patch:bug"
+---
+
+Fix a regression where typescript could not find types when using `"moduleResolution": "node"`
diff --git a/.changes/api-primitives-core.md b/.changes/api-primitives-core.md
new file mode 100644
index 000000000000..0615fd486a55
--- /dev/null
+++ b/.changes/api-primitives-core.md
@@ -0,0 +1,5 @@
+---
+'@tauri-apps/api': 'patch:breaking'
+---
+
+Changed former `tauri` module from `primitives` to `core`.
diff --git a/.changes/api-top-level-main-module.md b/.changes/api-top-level-main-module.md
new file mode 100644
index 000000000000..fba6cdb2c37b
--- /dev/null
+++ b/.changes/api-top-level-main-module.md
@@ -0,0 +1,5 @@
+---
+'@tauri-apps/api': 'patch:bug'
+---
+
+Add top-level `main`, `module` and `types` fields in `package.json` to be compliant with typescripts's `"moduleResolution": "node"`
diff --git a/.changes/api-tray-menu.md b/.changes/api-tray-menu.md
new file mode 100644
index 000000000000..2071bcca177b
--- /dev/null
+++ b/.changes/api-tray-menu.md
@@ -0,0 +1,5 @@
+---
+'@tauri-apps/api': 'minor:feat'
+---
+
+Add `tray` and `menu` modules to create and manage tray icons and menus from Javascript.
diff --git a/.changes/cli-signer-env-vars.md b/.changes/cli-signer-env-vars.md
new file mode 100644
index 000000000000..41c04062ea2f
--- /dev/null
+++ b/.changes/cli-signer-env-vars.md
@@ -0,0 +1,9 @@
+---
+'tauri-cli': 'patch:enhance'
+---
+
+Read the following env vars when using the `tauri signer sign` command to make it easier to use in CI.
+
+- `TAURI_PRIVATE_KEY`
+- `TAURI_PRIVATE_KEY_PASSWORD`
+- `TAURI_PRIVATE_KEY_PATH`
diff --git a/.changes/fix-file-drop-event-payload.md b/.changes/fix-file-drop-event-payload.md
new file mode 100644
index 000000000000..44c1ff4bfe19
--- /dev/null
+++ b/.changes/fix-file-drop-event-payload.md
@@ -0,0 +1,5 @@
+---
+"@tauri-apps/api": 'patch:enhance'
+---
+
+Added `position` field to the `FileDropEvent` payload.
diff --git a/.changes/fix-ide-build-run.md b/.changes/fix-ide-build-run.md
new file mode 100644
index 000000000000..f89fab21bf12
--- /dev/null
+++ b/.changes/fix-ide-build-run.md
@@ -0,0 +1,6 @@
+---
+"tauri-cli": patch:bug
+"@tauri-apps/cli": patch:bug
+---
+
+Fixes `android build --open` and `ios build --open` IDE failing to read CLI options.
diff --git a/.changes/fix-ios-channel.md b/.changes/fix-ios-channel.md
new file mode 100644
index 000000000000..a1bf8bf7ab41
--- /dev/null
+++ b/.changes/fix-ios-channel.md
@@ -0,0 +1,5 @@
+---
+"tauri": 'patch:enhance'
+---
+
+Fixed the deserialisation of a `Channel` in iOS.
\ No newline at end of file
diff --git a/.changes/get-ipc-response-test.md b/.changes/get-ipc-response-test.md
new file mode 100644
index 000000000000..9cd3ce1d26c2
--- /dev/null
+++ b/.changes/get-ipc-response-test.md
@@ -0,0 +1,5 @@
+---
+"tauri": patch:enhance
+---
+
+Added `test::get_ipc_response`.
diff --git a/.changes/merge-ios-plist.md b/.changes/merge-ios-plist.md
new file mode 100644
index 000000000000..87cf86f3a789
--- /dev/null
+++ b/.changes/merge-ios-plist.md
@@ -0,0 +1,6 @@
+---
+"tauri-cli": patch:feat
+"@tauri-apps/cli": patch:feat
+---
+
+Merge `src-tauri/Info.plist` and `src-tauri/Info.ios.plist` with the iOS project plist file.
diff --git a/.changes/pre.json b/.changes/pre.json
index af746e0d3138..29c82cdd2e1b 100644
--- a/.changes/pre.json
+++ b/.changes/pre.json
@@ -21,6 +21,7 @@
".changes/api-min-node-18.md",
".changes/api-primitives.md",
".changes/api-tabbed.md",
+ ".changes/api-tray-menu.md",
".changes/api-window.md",
".changes/app-builder-send.md",
".changes/app-plugin-core.md",
@@ -53,6 +54,7 @@
".changes/cli-nodejs-detection.md",
".changes/cli-npx-mobile.md",
".changes/cli-plugin-init.md",
+ ".changes/cli-plugin-name-snake-case.md",
".changes/cli-pnpm.md",
".changes/cli-refactor-ipc-mobile.md",
".changes/cli-removed-new-version-check.md",
@@ -94,7 +96,10 @@
".changes/fix-clearmocks.md",
".changes/fix-dev-server-proxy-path.md",
".changes/fix-empty-identifier.md",
+ ".changes/fix-file-drop-event-payload.md",
+ ".changes/fix-global-event.md",
".changes/fix-icons-android.md",
+ ".changes/fix-ide-build-run.md",
".changes/fix-ios-cli-panic.md",
".changes/fix-ios-logs.md",
".changes/fix-ios-plugin-throws-command.md",
@@ -108,6 +113,7 @@
".changes/fix-plugin-ios-bool.md",
".changes/fix-plugin-removal.md",
".changes/fix-plugin-template-cargotoml.md",
+ ".changes/fix-plugin-template.md",
".changes/fix-proguard-injection.md",
".changes/fix-proguard-rules.md",
".changes/fix-shell-build.md",
@@ -115,6 +121,7 @@
".changes/fix-tray-icon-validation.md",
".changes/fix-windows-custom-protocol-url.md",
".changes/fix-windows-custom-protocol.md",
+ ".changes/fix-wix-output-filename-version.md",
".changes/fix-xcodescript-lib-path.md",
".changes/force-colored-logs.md",
".changes/generate-tauri-activity.md",
@@ -122,6 +129,7 @@
".changes/gtk018.md",
".changes/gtk16.md",
".changes/http-types-refactor.md",
+ ".changes/icon-svg.md",
".changes/improve-local-ip-detection.md",
".changes/improve-mobile-plugin-error-handling.md",
".changes/inject-config.md",
@@ -148,6 +156,7 @@
".changes/local-dev-path-mobile.md",
".changes/log-file-fix-for-linux-and-windows.md",
".changes/logcat-all-tags.md",
+ ".changes/merge-ios-plist.md",
".changes/migrate-cmd.md",
".changes/migrate-csp.md",
".changes/migrate-plugins.md",
@@ -211,6 +220,7 @@
".changes/refactor-setup.md",
".changes/refactor-tauri-android-dependency.md",
".changes/register_asynchronous_uri_scheme_protocol.md",
+ ".changes/relative-mobile-args.md",
".changes/remove-allowlist.md",
".changes/remove-attohttpc.md",
".changes/remove-clipboard.md",
@@ -249,12 +259,14 @@
".changes/skip-target-install-arg.md",
".changes/state-0.6.md",
".changes/submenu-and-menu-builders-item-and-id.md",
+ ".changes/syn-2.0.md",
".changes/system-tray-feat.md",
".changes/target-dir-detection.md",
".changes/tauri-api-removal.md",
".changes/tauri-app-handle-ref.md",
".changes/tauri-asset-protocol.md",
".changes/tauri-build-mobile.md",
+ ".changes/tauri-build-resource-compiler.md",
".changes/tauri-cleanup-before-exit.md",
".changes/tauri-defaultvbox.md",
".changes/tauri-env-args.md",
@@ -277,6 +289,7 @@
".changes/tauri-tray-on-tray-event.md",
".changes/tauri-utils-tabbed.md",
".changes/tauri-utils-tray-icon-id copy.md",
+ ".changes/tauri-utils-windows-version.md",
".changes/tauri-uuid-rand.md",
".changes/tempdir-api.md",
".changes/tempdir-core.md",
@@ -298,6 +311,7 @@
".changes/wry-0.32.md",
".changes/wry-0.34.md",
".changes/wry-navigate-method.md",
- ".changes/wry26.md"
+ ".changes/wry26.md",
+ ".changes/xcode-archive.md"
]
}
diff --git a/.changes/syn-2.0.md b/.changes/syn-2.0.md
new file mode 100644
index 000000000000..3ea03d003c14
--- /dev/null
+++ b/.changes/syn-2.0.md
@@ -0,0 +1,5 @@
+---
+"tauri-macros": patch:deps
+---
+
+Update to syn v2.
diff --git a/.changes/tauri-menu-predefined-close-wiwndow.md b/.changes/tauri-menu-predefined-close-wiwndow.md
new file mode 100644
index 000000000000..42c8b81fdd62
--- /dev/null
+++ b/.changes/tauri-menu-predefined-close-wiwndow.md
@@ -0,0 +1,5 @@
+---
+"tauri": "patch:bug"
+---
+
+Fix incorrect menu item for `PredefinedMenuItem::close_window`
\ No newline at end of file
diff --git a/.changes/tauri-resources-table.md b/.changes/tauri-resources-table.md
new file mode 100644
index 000000000000..2f98fc883048
--- /dev/null
+++ b/.changes/tauri-resources-table.md
@@ -0,0 +1,5 @@
+---
+'tauri': 'patch:feat'
+---
+
+Exposed `Manager::resources_table` to access the resources table used by tauri, which could be used by plugins or app authors to store their resources and retrieve it later using an id.
diff --git a/.changes/tauri-utils-windows-version.md b/.changes/tauri-utils-windows-version.md
new file mode 100644
index 000000000000..d755a8f4a8e6
--- /dev/null
+++ b/.changes/tauri-utils-windows-version.md
@@ -0,0 +1,5 @@
+---
+'tauri-utils': 'minor:breaking'
+---
+
+Removed `platform::windows_version` and `platform::is_windows_7`, use `windows-version` crate instead.
diff --git a/.changes/xcode-archive.md b/.changes/xcode-archive.md
new file mode 100644
index 000000000000..f7d2407e93b3
--- /dev/null
+++ b/.changes/xcode-archive.md
@@ -0,0 +1,6 @@
+---
+"tauri-cli": patch:bug
+"@tauri-apps/cli": patch:bug
+---
+
+Added support to Xcode's archive. This requires regenerating the Xcode project.
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 7e77aab2e672..1d856b7874fa 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -22,7 +22,7 @@ Hi! We, the maintainers, are really excited that you are interested in contribut
- Issues with no clear repro steps will not be triaged. If an issue labeled "need repro" receives no further input from the issue author for more than 5 days, it will be closed.
-- If your issue is resolved but still open, don’t hesitate to close it. In case you found a solution by yourself, it could be helpful to explain how you fixed it.
+- If your issue is resolved but still open, don't hesitate to close it. In case you found a solution by yourself, it could be helpful to explain how you fixed it.
- Most importantly, we beg your patience: the team must balance your request against many other responsibilities — fixing other bugs, answering other questions, new features, new documentation, etc. The issue list is not paid support and we cannot make guarantees about how fast your issue can be resolved.
diff --git a/.github/sponsors/crabnebula.svg b/.github/sponsors/crabnebula.svg
new file mode 100644
index 000000000000..40e24131b51e
--- /dev/null
+++ b/.github/sponsors/crabnebula.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.github/workflows/covector-version-or-publish.yml b/.github/workflows/covector-version-or-publish.yml
index 025e9772166d..f81ab3ab412b 100644
--- a/.github/workflows/covector-version-or-publish.yml
+++ b/.github/workflows/covector-version-or-publish.yml
@@ -112,14 +112,18 @@ jobs:
repository: tauri-apps/tauri-docs
event-type: update-docs
- - name: Process covector output
- id: covectorOutput
+ - name: Get `@tauri-apps/cli` release id
+ uses: actions/github-script@v6
+ id: cliReleaseId
if: |
steps.covector.outputs.successfulPublish == 'true' &&
contains(steps.covector.outputs.packagesPublished, '@tauri-apps/cli')
- run: |
- id=$(node .scripts/covector/parse-output.js '${{ toJSON(steps.covector.outputs) }}' "-tauri-apps-cli-releaseId")
- echo "cliReleaseId=$id" >> "$GITHUB_OUTPUT"
+ with:
+ result-encoding: string
+ script: |
+ const output = `${{ toJSON(steps.covector.outputs) }}`;
+ const [_, id] = /"-tauri-apps-cli-releaseId": "([0-9]+)"/g.exec(output);
+ return id;
- name: Trigger `@tauri-apps/cli` publishing workflow
if: |
@@ -130,7 +134,7 @@ jobs:
token: ${{ secrets.ORG_TAURI_BOT_PAT }}
repository: tauri-apps/tauri
event-type: publish-js-cli
- client-payload: '{"releaseId": "${{ steps.covectorOutput.outputs.cliReleaseId }}" }'
+ client-payload: '{"releaseId": "${{ steps.cliReleaseId.outputs.result }}" }'
- name: Trigger `tauri-cli` publishing workflow
if: |
diff --git a/.github/workflows/lint-js.yml b/.github/workflows/lint-js.yml
index 587d8285d4af..5fe27a62c6c2 100644
--- a/.github/workflows/lint-js.yml
+++ b/.github/workflows/lint-js.yml
@@ -49,6 +49,9 @@ jobs:
- name: install deps via yarn
working-directory: ./tooling/api/
run: yarn
+ - name: run ts:check
+ working-directory: ./tooling/api/
+ run: yarn ts:check
- name: run lint
working-directory: ./tooling/api/
run: yarn lint
diff --git a/.github/workflows/publish-cli-js.yml b/.github/workflows/publish-cli-js.yml
index 48f7803cf0e0..d7be5a5ad98f 100644
--- a/.github/workflows/publish-cli-js.yml
+++ b/.github/workflows/publish-cli-js.yml
@@ -294,14 +294,10 @@ jobs:
- name: List packages
run: ls -R .
shell: bash
- - name: Install system dependencies
- run: |
- apk add openssl-dev musl-dev glib-dev cairo-dev pkgconfig gdk-pixbuf-dev webkit2gtk-dev curl gtk+3.0-dev
- name: Setup and run tests
run: |
yarn tauri --help
ls -la
- # TODO: fix this test: https://github.com/tauri-apps/tauri/runs/5145729140?check_suite_focus=true#step:9:704
#- name: Setup and run tests
# run: |
# rustup install stable
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 3050d5d43f92..7178a5417264 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -11,7 +11,7 @@ if [ -z "$(git diff --name-only tooling/api)" ]; then
else
cd tooling/api
yarn format
- yarn lint-fix
+ yarn lint:fix
cd ../..
fi
diff --git a/.scripts/ci/check-license-header.js b/.scripts/ci/check-license-header.js
index a3a8b32c0f36..40da761054bf 100644
--- a/.scripts/ci/check-license-header.js
+++ b/.scripts/ci/check-license-header.js
@@ -13,6 +13,8 @@ SPDX-License-Identifier: Apache-2.0
SPDX-License-Identifier: MIT`
const bundlerLicense =
'// Copyright 2016-2019 Cargo-Bundle developers '
+const denoLicense =
+ '// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.'
const extensions = ['.rs', '.js', '.ts', '.yml', '.swift', '.kt']
const ignore = [
@@ -43,7 +45,8 @@ async function checkFile(file) {
line.length === 0 ||
line.startsWith('#!') ||
line.startsWith('// swift-tools-version:') ||
- line === bundlerLicense
+ line === bundlerLicense ||
+ line === denoLicense
) {
continue
}
diff --git a/README.md b/README.md
index 18903b7b5f03..25a4c7218184 100644
--- a/README.md
+++ b/README.md
@@ -9,121 +9,49 @@
[![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation)
[![support](https://img.shields.io/badge/sponsor-Open%20Collective-blue.svg)](https://opencollective.com/tauri)
-## Current Releases
-
-### Core
-
-| Component | Description | Version | Lin | Win | Mac |
-| -------------------------------------------------------------------------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------- | --- | --- | --- |
-| [**tauri**](https://github.com/tauri-apps/tauri/tree/dev/core/tauri) | runtime core | [![](https://img.shields.io/crates/v/tauri.svg)](https://crates.io/crates/tauri) | ✅ | ✅ | ✅ |
-| [**tauri-build**](https://github.com/tauri-apps/tauri/tree/dev/core/tauri-build) | applies macros at build-time | [![](https://img.shields.io/crates/v/tauri-build.svg)](https://crates.io/crates/tauri-build) | ✅ | ✅ | ✅ |
-| [**tauri-codegen**](https://github.com/tauri-apps/tauri/tree/dev/core/tauri-codegen) | handles assets, parses tauri.conf.json | [![](https://img.shields.io/crates/v/tauri-codegen.svg)](https://crates.io/crates/tauri-codegen) | ✅ | ✅ | ✅ |
-| [**tauri-macros**](https://github.com/tauri-apps/tauri/tree/dev/core/tauri-macros) | creates macros using tauri-codegen | [![](https://img.shields.io/crates/v/tauri-macros.svg)](https://crates.io/crates/tauri-macros) | ✅ | ✅ | ✅ |
-| [**tauri-runtime**](https://github.com/tauri-apps/tauri/tree/dev/core/tauri-runtime) | layer between Tauri and webview libraries | [![](https://img.shields.io/crates/v/tauri-runtime.svg)](https://crates.io/crates/tauri-runtime) | ✅ | ✅ | ✅ |
-| [**tauri-runtime-wry**](https://github.com/tauri-apps/tauri/tree/dev/core/tauri-runtime-wry) | enables system-level interaction via WRY | [![](https://img.shields.io/crates/v/tauri-runtime-wry.svg)](https://crates.io/crates/tauri-runtime-wry) | ✅ | ✅ | ✅ |
-| [**tauri-utils**](https://github.com/tauri-apps/tauri/tree/dev/core/tauri-utils) | common code used across the tauri crates | [![](https://img.shields.io/crates/v/tauri-utils.svg)](https://crates.io/crates/tauri-utils) | ✅ | ✅ | ✅ |
-
-### Tooling
-
-| Component | Description | Version | Lin | Win | Mac |
-| ------------------------------------------------------------------------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------ | --- | --- | --- |
-| [**bundler**](https://github.com/tauri-apps/tauri/tree/dev/tooling/bundler) | manufacture the final binaries | [![](https://img.shields.io/crates/v/tauri-bundler.svg)](https://crates.io/crates/tauri-bundler) | ✅ | ✅ | ✅ |
-| [**tauri-cli**](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli) | create, develop and build apps | [![](https://img.shields.io/crates/v/tauri-cli.svg)](https://crates.io/crates/tauri-cli) | ✅ | ✅ | ✅ |
-| [**@tauri-apps/cli**](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli/node) | Node.js CLI wrapper for `tauri-cli` | [![](https://img.shields.io/npm/v/@tauri-apps/cli.svg)](https://www.npmjs.com/package/@tauri-apps/cli) | ✅ | ✅ | ✅ |
-| [**@tauri-apps/api**](https://github.com/tauri-apps/tauri/tree/dev/tooling/api) | JS API for interaction with Rust backend | [![](https://img.shields.io/npm/v/@tauri-apps/api.svg)](https://www.npmjs.com/package/@tauri-apps/api) | ✅ | ✅ | ✅ |
-
-### Utilities and Plugins
-
-| Component | Description | Version | Lin | Win | Mac |
-| ------------------------------------------------------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | --- | --- | --- |
-| [**create-tauri-app**](https://github.com/tauri-apps/create-tauri-app) | Get started with your first Tauri app | [![](https://img.shields.io/npm/v/create-tauri-app.svg)](https://www.npmjs.com/package/create-tauri-app) | ✅ | ✅ | ✅ |
-| [**vue-cli-plugin-tauri**](https://github.com/tauri-apps/vue-cli-plugin-tauri/) | Vue CLI plugin for Tauri | [![](https://img.shields.io/npm/v/vue-cli-plugin-tauri.svg)](https://www.npmjs.com/package/vue-cli-plugin-tauri) | ✅ | ✅ | ✅ |
-
## Introduction
Tauri is a framework for building tiny, blazingly fast binaries for all major desktop platforms. Developers can integrate any front-end framework that compiles to HTML, JS and CSS for building their user interface. The backend of the application is a rust-sourced binary with an API that the front-end can interact with.
-The user interface in Tauri apps currently leverages [`tao`](https://docs.rs/tao) as a window handling library on macOS and Windows, and [`gtk`](https://gtk-rs.org/docs/gtk/) on Linux via the **Tauri-team** incubated and maintained [WRY](https://github.com/tauri-apps/wry), which creates a unified interface to the system webview (and other goodies like Menu and Taskbar), leveraging WebKit on macOS, WebView2 on Windows and WebKitGTK on Linux.
+The user interface in Tauri apps currently leverages [`tao`](https://docs.rs/tao) as a window handling library on macOS, Windows, Linux, Android and iOS. To render your application, Tauri uses [WRY](https://github.com/tauri-apps/wry), a library which provides a unified interface to the system webview, leveraging WKWebView on macOS & iOS, WebView2 on Windows, WebKitGTK on Linux and Android System WebView on Android.
To learn more about the details of how all of these pieces fit together, please consult this [ARCHITECTURE.md](https://github.com/tauri-apps/tauri/blob/dev/ARCHITECTURE.md) document.
-## Get Started
-
-If you are interested in making a tauri app, please visit the [documentation website](https://tauri.app). This README is directed towards those who are interested in contributing to the core library. But if you just want a quick overview about where `tauri` is at in its development, here's a quick burndown:
-
-### Platforms
-
-Tauri currently supports development and distribution on the following platforms:
-
-| Platform | Versions |
-| :----------------------- | :-------------- |
-| Windows | 7 and above |
-| macOS | 10.15 and above |
-| Linux | See below |
-| iOS/iPadOS (coming soon) | |
-| Android (coming soon) | |
-
-**Linux Support**
+## Getting Started
-For **developing** Tauri apps refer to the [Getting Started guide on tauri.app](https://tauri.app/v1/guides/getting-started/prerequisites#setting-up-linux).
+If you are interested in making a tauri app, please visit the [documentation website](https://tauri.app).
-For **running** Tauri apps we support the below configurations (these are automatically added as dependencies for .deb and are bundled for AppImage so that your users don't need to manually install them):
+The quickest way to get started is to install the [prerequisites](https://tauri.app/v1/guides/getting-started/prerequisites) for your system and create a new project with [`create-tauri-app`](https://github.com/tauri-apps/create-tauri-app/#usage). For example with `npm`:
-- Debian (Ubuntu 18.04 and above or equivalent) with the following packages installed:
- - `libwebkit2gtk-4.1-0`, `libgtk-3-0`, `libayatana-appindicator3-1`1
-- Arch with the following packages installed:
- - `webkit2gtk`, `gtk3`, `libayatana-appindicator`1
-- Fedora (latest 2 versions) with the following packages installed:
- - `webkit2gtk3`, `gtk3`, `libappindicator-gtk3`1
-- Void with the following packages installed:
- - `webkit2gtk`, `gtk+3`, `libappindicator`1
+```sh
+npm create tauri-app@latest
+```
-1 `appindicator` is only required if system trays are used
+## Features
-### Features
+The list of Tauri's features includes, but is not limited to:
-- [x] Desktop Bundler (.app, .dmg, .deb, AppImage, .msi)
-- [x] Self Updater
-- [x] App Signing
-- [x] Native Notifications (toast)
-- [x] App Tray
-- [x] Core Plugin System
-- [x] Scoped Filesystem
-- [x] Sidecar
+- Built-in app bundler to create app bundles in formats like `.app`, `.dmg`, `.deb`, `.AppImage` and Windows installers like `.exe` (via NSIS) and `.msi` (via WiX).
+- Built-in self updater (desktop only)
+- System tray icons
+- Native notifications
+- Localhost free (:fire:)
+- GitHub action for streamlined CI
+- VS Code extension
-### Security Features
-
-- [x] localhost-free (:fire:)
-- [x] custom protocol for secure mode
-- [x] Dynamic ahead of Time Compilation (dAoT) with functional tree-shaking
-- [x] functional Address Space Layout Randomization
-- [x] OTP salting of function names and messages at runtime
-- [x] CSP Injection
-
-### Utilities
-
-- [x] Rust-based CLI
-- [x] GH Action for creating binaries for all platforms
-- [x] VS Code Extension
-
-## Development
-
-Tauri is a system composed of a number of moving pieces:
-
-### Infrastructure
-
-- Git for code management
-- GitHub for project management
-- GitHub actions for CI and CD
-- Discord for discussions
-- Netlify-hosted documentation website
-- DigitalOcean Meilisearch instance
+### Platforms
-### Operating systems
+Tauri currently supports development and distribution on the following platforms:
-Tauri core can be developed on Mac, Linux and Windows, but you are encouraged to use the latest possible operating systems and build tools for your OS.
+| Platform | Versions |
+| :----------------- | :-------------------------------------------------------------------------------------------------------------- |
+| Windows | 7 and above |
+| macOS | 10.15 and above |
+| Linux | webkit2gtk 4.0 for Tauri v1 (for example Ubuntu 18.04). webkit2gtk 4.1 for Tauri v2 (for example Ubuntu 22.04). |
+| iOS/iPadOS (alpha) | 9 and above |
+| Android (alpha) | 7 and above |
-### Contributing
+## Contributing
Before you start working on something, it's best to check if there is an existing issue first. It's also a good idea to stop by the Discord server and confirm with the team if it makes sense or if someone else is already working on it.
@@ -133,32 +61,31 @@ Thank you to everyone contributing to Tauri!
### Documentation
-Documentation in a polyglot system is a tricky proposition. To this end, we prefer to use inline documentation of Rust code and at JSDoc in typescript / javascript code. We autocollect these and publish them using Docusaurus v2 and netlify. Here is the hosting repository for the documentation site: https://github.com/tauri-apps/tauri-docs
-
-### Testing & Linting
+Documentation in a polyglot system is a tricky proposition. To this end, we prefer to use inline documentation in the Rust & JS source code as much as possible. Check out the hosting repository for the documentation site for further information: https://github.com/tauri-apps/tauri-docs
-Test all the things! We have a number of test suites, but are always looking to improve our coverage:
+## Partners
-- Rust (`cargo test`) => sourced via inline `#[cfg(test)]` declarations
-- Typescript (`jest`) => via spec files
-- Smoke Tests (run on merges to latest)
-- eslint, clippy
+
+
+
+
+
+
+
+
+
+
+
-### CI/CD
-
-We recommend you read this article to understand better how we run our pipelines: https://www.jacobbolda.com/setting-up-ci-and-cd-for-tauri/
+For the complete list of sponsors please visit our [website](https://tauri.app#sponsors) and [Open Collective](https://opencollective.com/tauri).
## Organization
Tauri aims to be a sustainable collective based on principles that guide [sustainable free and open software communities](https://sfosc.org). To this end it has become a Programme within the [Commons Conservancy](https://commonsconservancy.org/), and you can contribute financially via [Open Collective](https://opencollective.com/tauri).
-## Semver
-
-**tauri** is following [Semantic Versioning 2.0](https://semver.org/).
-
## Licenses
-Code: (c) 2015 - 2021 - The Tauri Programme within The Commons Conservancy.
+Code: (c) 2015 - Present - The Tauri Programme within The Commons Conservancy.
MIT or MIT/Apache 2.0 where applicable.
diff --git a/core/tauri-build/CHANGELOG.md b/core/tauri-build/CHANGELOG.md
index f4957e7623c7..b956bcc281d1 100644
--- a/core/tauri-build/CHANGELOG.md
+++ b/core/tauri-build/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+## \[2.0.0-alpha.12]
+
+### Bug Fixes
+
+- [`a5479712`](https://www.github.com/tauri-apps/tauri/commit/a5479712095c224e2cb147d5c271acbc2fc97e79)([#8168](https://www.github.com/tauri-apps/tauri/pull/8168)) Fixed an issue that caused the resource compiler to not run on Windows when `package.version` was not set in `tauri.conf.json` preventing the app from starting.
+
+### Dependencies
+
+- Upgraded to `tauri-utils@2.0.0-alpha.11`
+- Upgraded to `tauri-codegen@2.0.0-alpha.11`
+
## \[2.0.0-alpha.11]
### Enhancements
diff --git a/core/tauri-build/Cargo.toml b/core/tauri-build/Cargo.toml
index a3725585b68f..e595322711c2 100644
--- a/core/tauri-build/Cargo.toml
+++ b/core/tauri-build/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-build"
-version = "2.0.0-alpha.11"
+version = "2.0.0-alpha.12"
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-alpha.10", path = "../tauri-codegen", optional = true }
-tauri-utils = { version = "2.0.0-alpha.10", path = "../tauri-utils", features = [ "build", "resources" ] }
+tauri-codegen = { version = "2.0.0-alpha.11", path = "../tauri-codegen", optional = true }
+tauri-utils = { version = "2.0.0-alpha.11", path = "../tauri-utils", features = [ "build", "resources" ] }
cargo_toml = "0.17"
serde = "1"
serde_json = "1"
diff --git a/core/tauri-codegen/CHANGELOG.md b/core/tauri-codegen/CHANGELOG.md
index 7e1eb6e9a76e..ce98eb166894 100644
--- a/core/tauri-codegen/CHANGELOG.md
+++ b/core/tauri-codegen/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## \[2.0.0-alpha.11]
+
+### Dependencies
+
+- Upgraded to `tauri-utils@2.0.0-alpha.11`
+
## \[2.0.0-alpha.10]
### Enhancements
diff --git a/core/tauri-codegen/Cargo.toml b/core/tauri-codegen/Cargo.toml
index 5df1be4531ed..0c724cd74a15 100644
--- a/core/tauri-codegen/Cargo.toml
+++ b/core/tauri-codegen/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-codegen"
-version = "2.0.0-alpha.10"
+version = "2.0.0-alpha.11"
description = "code generation meant to be consumed inside of `tauri` through `tauri-build` or `tauri-macros`"
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@@ -19,7 +19,7 @@ proc-macro2 = "1"
quote = "1"
serde = { version = "1", features = [ "derive" ] }
serde_json = "1"
-tauri-utils = { version = "2.0.0-alpha.10", path = "../tauri-utils", features = [ "build" ] }
+tauri-utils = { version = "2.0.0-alpha.11", path = "../tauri-utils", features = [ "build" ] }
thiserror = "1"
walkdir = "2"
brotli = { version = "3", optional = true, default-features = false, features = [ "std" ] }
diff --git a/core/tauri-config-schema/schema.json b/core/tauri-config-schema/schema.json
index b4a96b2847de..573d37924808 100644
--- a/core/tauri-config-schema/schema.json
+++ b/core/tauri-config-schema/schema.json
@@ -37,6 +37,20 @@
"deb": {
"files": {}
},
+ "dmg": {
+ "appPosition": {
+ "x": 180,
+ "y": 170
+ },
+ "applicationFolderPosition": {
+ "x": 480,
+ "y": 170
+ },
+ "windowSize": {
+ "height": 400,
+ "width": 660
+ }
+ },
"iOS": {},
"icon": [],
"identifier": "",
@@ -171,6 +185,20 @@
"deb": {
"files": {}
},
+ "dmg": {
+ "appPosition": {
+ "x": 180,
+ "y": 170
+ },
+ "applicationFolderPosition": {
+ "x": 480,
+ "y": 170
+ },
+ "windowSize": {
+ "height": 400,
+ "width": 660
+ }
+ },
"iOS": {},
"icon": [],
"identifier": "",
@@ -1003,6 +1031,28 @@
}
]
},
+ "dmg": {
+ "description": "DMG-specific settings.",
+ "default": {
+ "appPosition": {
+ "x": 180,
+ "y": 170
+ },
+ "applicationFolderPosition": {
+ "x": 480,
+ "y": 170
+ },
+ "windowSize": {
+ "height": 400,
+ "width": 660
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "#/definitions/DmgConfig"
+ }
+ ]
+ },
"macOS": {
"description": "Configuration for the macOS bundles.",
"default": {
@@ -1318,6 +1368,113 @@
},
"additionalProperties": false
},
+ "DmgConfig": {
+ "description": "Configuration for Apple Disk Image (.dmg) bundles.\n\nSee more: https://tauri.app/v1/api/config#dmgconfig",
+ "type": "object",
+ "properties": {
+ "background": {
+ "description": "Image to use as the background in dmg file. Accepted formats: `png`/`jpg`/`gif`.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "windowPosition": {
+ "description": "Position of volume window on screen.",
+ "anyOf": [
+ {
+ "$ref": "#/definitions/Position"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "windowSize": {
+ "description": "Size of volume window.",
+ "default": {
+ "height": 400,
+ "width": 660
+ },
+ "allOf": [
+ {
+ "$ref": "#/definitions/Size"
+ }
+ ]
+ },
+ "appPosition": {
+ "description": "Position of app file on window.",
+ "default": {
+ "x": 180,
+ "y": 170
+ },
+ "allOf": [
+ {
+ "$ref": "#/definitions/Position"
+ }
+ ]
+ },
+ "applicationFolderPosition": {
+ "description": "Position of application folder on window.",
+ "default": {
+ "x": 480,
+ "y": 170
+ },
+ "allOf": [
+ {
+ "$ref": "#/definitions/Position"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "Position": {
+ "description": "Position coordinates struct.",
+ "type": "object",
+ "required": [
+ "x",
+ "y"
+ ],
+ "properties": {
+ "x": {
+ "description": "X coordinate.",
+ "type": "integer",
+ "format": "uint32",
+ "minimum": 0.0
+ },
+ "y": {
+ "description": "Y coordinate.",
+ "type": "integer",
+ "format": "uint32",
+ "minimum": 0.0
+ }
+ },
+ "additionalProperties": false
+ },
+ "Size": {
+ "description": "Size of the window.",
+ "type": "object",
+ "required": [
+ "height",
+ "width"
+ ],
+ "properties": {
+ "width": {
+ "description": "Width of the window.",
+ "type": "integer",
+ "format": "uint32",
+ "minimum": 0.0
+ },
+ "height": {
+ "description": "Height of the window.",
+ "type": "integer",
+ "format": "uint32",
+ "minimum": 0.0
+ }
+ },
+ "additionalProperties": false
+ },
"MacConfig": {
"description": "Configuration for the macOS bundles.\n\nSee more: ",
"type": "object",
diff --git a/core/tauri-macros/CHANGELOG.md b/core/tauri-macros/CHANGELOG.md
index e451005e293d..e9ce1f2f8d61 100644
--- a/core/tauri-macros/CHANGELOG.md
+++ b/core/tauri-macros/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## \[2.0.0-alpha.11]
+
+### Dependencies
+
+- Upgraded to `tauri-utils@2.0.0-alpha.11`
+- Upgraded to `tauri-codegen@2.0.0-alpha.11`
+- [`b6ed4ecb`](https://www.github.com/tauri-apps/tauri/commit/b6ed4ecb3730c346c973f1b34aa0de1b7d43ac44)([#7634](https://www.github.com/tauri-apps/tauri/pull/7634)) Update to syn v2.
+
## \[2.0.0-alpha.10]
### Enhancements
diff --git a/core/tauri-macros/Cargo.toml b/core/tauri-macros/Cargo.toml
index 2119570bbdfc..f115c4ba4088 100644
--- a/core/tauri-macros/Cargo.toml
+++ b/core/tauri-macros/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-macros"
-version = "2.0.0-alpha.10"
+version = "2.0.0-alpha.11"
description = "Macros for the tauri crate."
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@@ -18,10 +18,10 @@ proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
-syn = { version = "1", features = [ "full" ] }
+syn = { version = "2", features = [ "full" ] }
heck = "0.4"
-tauri-codegen = { version = "2.0.0-alpha.10", default-features = false, path = "../tauri-codegen" }
-tauri-utils = { version = "2.0.0-alpha.10", path = "../tauri-utils" }
+tauri-codegen = { version = "2.0.0-alpha.11", default-features = false, path = "../tauri-codegen" }
+tauri-utils = { version = "2.0.0-alpha.11", path = "../tauri-utils" }
[features]
custom-protocol = [ ]
diff --git a/core/tauri-macros/src/command/handler.rs b/core/tauri-macros/src/command/handler.rs
index 62a9ace45840..62bf999a6488 100644
--- a/core/tauri-macros/src/command/handler.rs
+++ b/core/tauri-macros/src/command/handler.rs
@@ -31,7 +31,7 @@ pub struct Handler {
impl Parse for Handler {
fn parse(input: &ParseBuffer<'_>) -> syn::Result {
- let command_defs = input.parse_terminated::(CommandDef::parse)?;
+ let command_defs = input.parse_terminated(CommandDef::parse, Token![,])?;
// parse the command names and wrappers from the passed paths
let (commands, wrappers) = command_defs
diff --git a/core/tauri-macros/src/command/wrapper.rs b/core/tauri-macros/src/command/wrapper.rs
index fab592417eb4..ac56829f3b78 100644
--- a/core/tauri-macros/src/command/wrapper.rs
+++ b/core/tauri-macros/src/command/wrapper.rs
@@ -10,10 +10,28 @@ use syn::{
ext::IdentExt,
parse::{Parse, ParseStream},
parse_macro_input,
+ punctuated::Punctuated,
spanned::Spanned,
- FnArg, ItemFn, Lit, Meta, Pat, Token, Visibility,
+ Expr, ExprLit, FnArg, ItemFn, Lit, Meta, Pat, Token, Visibility,
};
+enum WrapperAttributeKind {
+ Meta(Meta),
+ Async,
+}
+
+impl Parse for WrapperAttributeKind {
+ fn parse(input: ParseStream) -> syn::Result {
+ match input.parse:: () {
+ Ok(m) => Ok(Self::Meta(m)),
+ Err(e) => match input.parse::() {
+ Ok(_) => Ok(Self::Async),
+ Err(_) => Err(e),
+ },
+ }
+ }
+}
+
struct WrapperAttributes {
root: TokenStream2,
execution_context: ExecutionContext,
@@ -28,12 +46,19 @@ impl Parse for WrapperAttributes {
argument_case: ArgumentCase::Camel,
};
- loop {
- match input.parse:: () {
- Ok(Meta::List(_)) => {}
- Ok(Meta::NameValue(v)) => {
+ let attrs = Punctuated::::parse_terminated(input)?;
+ for attr in attrs {
+ match attr {
+ WrapperAttributeKind::Meta(Meta::List(_)) => {
+ return Err(syn::Error::new(input.span(), "unexpected list input"));
+ }
+ WrapperAttributeKind::Meta(Meta::NameValue(v)) => {
if v.path.is_ident("rename_all") {
- if let Lit::Str(s) = v.lit {
+ if let Expr::Lit(ExprLit {
+ lit: Lit::Str(s),
+ attrs: _,
+ }) = v.value
+ {
wrapper_attributes.argument_case = match s.value().as_str() {
"snake_case" => ArgumentCase::Snake,
"camelCase" => ArgumentCase::Camel,
@@ -46,7 +71,11 @@ impl Parse for WrapperAttributes {
};
}
} else if v.path.is_ident("root") {
- if let Lit::Str(s) = v.lit {
+ if let Expr::Lit(ExprLit {
+ lit: Lit::Str(s),
+ attrs: _,
+ }) = v.value
+ {
let lit = s.value();
wrapper_attributes.root = if lit == "crate" {
@@ -58,22 +87,16 @@ impl Parse for WrapperAttributes {
}
}
}
- Ok(Meta::Path(p)) => {
- if p.is_ident("async") {
- wrapper_attributes.execution_context = ExecutionContext::Async;
- } else {
- return Err(syn::Error::new(p.span(), "expected `async`"));
- }
+ WrapperAttributeKind::Meta(Meta::Path(_)) => {
+ return Err(syn::Error::new(
+ input.span(),
+ "unexpected input, expected one of `rename_all`, `root`, `async`",
+ ));
}
- Err(_e) => {
- break;
+ WrapperAttributeKind::Async => {
+ wrapper_attributes.execution_context = ExecutionContext::Async;
}
}
-
- let lookahead = input.lookahead1();
- if lookahead.peek(Token![,]) {
- input.parse::()?;
- }
}
Ok(wrapper_attributes)
@@ -101,10 +124,15 @@ struct Invoke {
/// Create a new [`Wrapper`] from the function and the generated code parsed from the function.
pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
+ let mut attrs = parse_macro_input!(attributes as WrapperAttributes);
let function = parse_macro_input!(item as ItemFn);
let wrapper = super::format_command_wrapper(&function.sig.ident);
let visibility = &function.vis;
+ if function.sig.asyncness.is_some() {
+ attrs.execution_context = ExecutionContext::Async;
+ }
+
// macros used with `pub use my_macro;` need to be exported with `#[macro_export]`
let maybe_macro_export = match &function.vis {
Visibility::Public(_) => quote!(#[macro_export]),
@@ -173,28 +201,18 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
}
}
- // body to the command wrapper or a `compile_error!` of an error occurred while parsing it.
- let (body, attributes) = syn::parse::(attributes)
- .map(|mut attrs| {
- if function.sig.asyncness.is_some() {
- attrs.execution_context = ExecutionContext::Async;
- }
- attrs
- })
- .and_then(|attrs| {
- let body = match attrs.execution_context {
- ExecutionContext::Async => body_async(&function, &invoke, &attrs),
- ExecutionContext::Blocking => body_blocking(&function, &invoke, &attrs),
- };
- body.map(|b| (b, Some(attrs)))
- })
- .unwrap_or_else(|e| (syn::Error::into_compile_error(e), None));
+ let body = match attrs.execution_context {
+ ExecutionContext::Async => {
+ body_async(&function, &invoke, &attrs).unwrap_or_else(syn::Error::into_compile_error)
+ }
+ ExecutionContext::Blocking => {
+ body_blocking(&function, &invoke, &attrs).unwrap_or_else(syn::Error::into_compile_error)
+ }
+ };
let Invoke { message, resolver } = invoke;
- let root = attributes
- .map(|a| a.root)
- .unwrap_or_else(|| quote!(::tauri));
+ let root = attrs.root;
// Rely on rust 2018 edition to allow importing a macro from a path.
quote!(
diff --git a/core/tauri-macros/src/lib.rs b/core/tauri-macros/src/lib.rs
index 3962b1012d19..990dc6aff0fa 100644
--- a/core/tauri-macros/src/lib.rs
+++ b/core/tauri-macros/src/lib.rs
@@ -16,6 +16,7 @@ use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
mod command;
+mod menu;
mod mobile;
mod runtime;
@@ -89,3 +90,64 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre
let input = parse_macro_input!(input as DeriveInput);
runtime::default_runtime(attributes, input).into()
}
+
+/// Accepts a closure-like syntax to call arbitrary code on a menu item
+/// after matching against `kind` and retrieving it from `resources_table` using `rid`.
+///
+/// You can optionally pass a third parameter to select which item kinds
+/// to match against, by providing a `|` separated list of item kinds
+/// ```ignore
+/// do_menu_item!(|i| i.set_text(text), Check | Submenu);
+/// ```
+/// You could also provide a negated list
+/// ```ignore
+/// do_menu_item!(|i| i.set_text(text), !Check);
+/// do_menu_item!(|i| i.set_text(text), !Check | !Submenu);
+/// ```
+/// but you can't have mixed negations and positive kinds.
+/// ```ignore
+/// do_menu_item!(|i| i.set_text(text), !Check | Submeun);
+/// ```
+///
+/// #### Example
+///
+/// ```ignore
+/// let rid = 23;
+/// let kind = ItemKind::Check;
+/// let resources_table = app.resources_table();
+/// do_menu_item!(|i| i.set_text(text))
+/// ```
+/// which will expand into:
+/// ```ignore
+/// let rid = 23;
+/// let kind = ItemKind::Check;
+/// let resources_table = app.resources_table();
+/// match kind {
+/// ItemKind::Submenu => {
+/// let i = resources_table.get::>(rid)?;
+/// i.set_text(text)
+/// }
+/// ItemKind::MenuItem => {
+/// let i = resources_table.get::>(rid)?;
+/// i.set_text(text)
+/// }
+/// ItemKind::Predefined => {
+/// let i = resources_table.get::>(rid)?;
+/// i.set_text(text)
+/// }
+/// ItemKind::Check => {
+/// let i = resources_table.get::>(rid)?;
+/// i.set_text(text)
+/// }
+/// ItemKind::Icon => {
+/// let i = resources_table.get::>(rid)?;
+/// i.set_text(text)
+/// }
+/// _ => unreachable!(),
+/// }
+/// ```
+#[proc_macro]
+pub fn do_menu_item(input: TokenStream) -> TokenStream {
+ let tokens = parse_macro_input!(input as menu::DoMenuItemInput);
+ menu::do_menu_item(tokens).into()
+}
diff --git a/core/tauri-macros/src/menu.rs b/core/tauri-macros/src/menu.rs
new file mode 100644
index 000000000000..2e6dc9bd8714
--- /dev/null
+++ b/core/tauri-macros/src/menu.rs
@@ -0,0 +1,118 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::quote;
+use syn::{
+ parse::{Parse, ParseStream},
+ punctuated::Punctuated,
+ Expr, Token,
+};
+
+pub struct DoMenuItemInput {
+ resources_table: Ident,
+ rid: Ident,
+ kind: Ident,
+ var: Ident,
+ expr: Expr,
+ kinds: Vec,
+}
+
+#[derive(Clone)]
+struct NegatedIdent(bool, Ident);
+
+impl Parse for NegatedIdent {
+ fn parse(input: ParseStream) -> syn::Result {
+ let t = input.parse::();
+ let i: Ident = input.parse()?;
+ Ok(NegatedIdent(t.is_ok(), i))
+ }
+}
+
+impl Parse for DoMenuItemInput {
+ fn parse(input: ParseStream) -> syn::Result {
+ let resources_table: Ident = input.parse()?;
+ let _: Token![,] = input.parse()?;
+ let rid: Ident = input.parse()?;
+ let _: Token![,] = input.parse()?;
+ let kind: Ident = input.parse()?;
+ let _: Token![,] = input.parse()?;
+ let _: Token![|] = input.parse()?;
+ let var: Ident = input.parse()?;
+ let _: Token![|] = input.parse()?;
+ let expr: Expr = input.parse()?;
+ let _: syn::Result = input.parse();
+ let kinds = Punctuated::::parse_terminated(input)?;
+
+ Ok(Self {
+ resources_table,
+ rid,
+ kind,
+ var,
+ expr,
+ kinds: kinds.into_iter().collect(),
+ })
+ }
+}
+
+pub fn do_menu_item(input: DoMenuItemInput) -> TokenStream {
+ let DoMenuItemInput {
+ rid,
+ resources_table,
+ kind,
+ expr,
+ var,
+ mut kinds,
+ } = input;
+
+ let defaults = vec![
+ NegatedIdent(false, Ident::new("Submenu", Span::call_site())),
+ NegatedIdent(false, Ident::new("MenuItem", Span::call_site())),
+ NegatedIdent(false, Ident::new("Predefined", Span::call_site())),
+ NegatedIdent(false, Ident::new("Check", Span::call_site())),
+ NegatedIdent(false, Ident::new("Icon", Span::call_site())),
+ ];
+
+ if kinds.is_empty() {
+ kinds.extend(defaults.clone());
+ }
+
+ let has_negated = kinds.iter().any(|n| n.0);
+
+ if has_negated {
+ kinds.extend(defaults);
+ kinds.sort_by(|a, b| a.1.cmp(&b.1));
+ kinds.dedup_by(|a, b| a.1 == b.1);
+ }
+
+ let (kinds, types): (Vec, Vec) = kinds
+ .into_iter()
+ .filter_map(|nident| {
+ if nident.0 {
+ None
+ } else {
+ match nident.1 {
+ i if i == "MenuItem" => Some((i, Ident::new("MenuItem", Span::call_site()))),
+ i if i == "Submenu" => Some((i, Ident::new("Submenu", Span::call_site()))),
+ i if i == "Predefined" => Some((i, Ident::new("PredefinedMenuItem", Span::call_site()))),
+ i if i == "Check" => Some((i, Ident::new("CheckMenuItem", Span::call_site()))),
+ i if i == "Icon" => Some((i, Ident::new("IconMenuItem", Span::call_site()))),
+ _ => None,
+ }
+ }
+ })
+ .unzip();
+
+ quote! {
+ match #kind {
+ #(
+ ItemKind::#kinds => {
+ let #var = #resources_table.get::<#types>(#rid)?;
+ #expr
+ }
+ )*
+ _ => unreachable!(),
+ }
+ }
+}
diff --git a/core/tauri-runtime-wry/CHANGELOG.md b/core/tauri-runtime-wry/CHANGELOG.md
index dc2c33761e22..9d7834a60091 100644
--- a/core/tauri-runtime-wry/CHANGELOG.md
+++ b/core/tauri-runtime-wry/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## \[1.0.0-alpha.6]
+
+### Dependencies
+
+- Upgraded to `tauri-utils@2.0.0-alpha.11`
+- Upgraded to `tauri-runtime@1.0.0-alpha.5`
+
## \[1.0.0-alpha.5]
### New Features
diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml
index 1ba8e78f53cd..35d6bc118cbf 100644
--- a/core/tauri-runtime-wry/Cargo.toml
+++ b/core/tauri-runtime-wry/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-runtime-wry"
-version = "1.0.0-alpha.5"
+version = "1.0.0-alpha.6"
description = "Wry bindings to the Tauri runtime"
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@@ -13,17 +13,18 @@ edition = { workspace = true }
rust-version = { workspace = true }
[dependencies]
-wry = { version = "0.34.2", default-features = false, features = [ "tao", "file-drop", "protocol" ] }
-tauri-runtime = { version = "1.0.0-alpha.4", path = "../tauri-runtime" }
-tauri-utils = { version = "2.0.0-alpha.10", path = "../tauri-utils" }
+wry = { version = "0.35", default-features = false, features = [ "file-drop", "protocol", "os-webview" ] }
+tao = { version = "0.24", default-features = false, features = ["rwh_05"] }
+tauri-runtime = { version = "1.0.0-alpha.5", path = "../tauri-runtime" }
+tauri-utils = { version = "2.0.0-alpha.11", path = "../tauri-utils" }
raw-window-handle = "0.5"
http = "0.2"
[target."cfg(windows)".dependencies]
-webview2-com = "0.27"
+webview2-com = "0.28"
[target."cfg(windows)".dependencies.windows]
- version = "0.51"
+ version = "0.52"
features = [ "Win32_Foundation" ]
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
@@ -32,7 +33,7 @@ webkit2gtk = { version = "=2.0", features = [ "v2_38" ] }
percent-encoding = "2.1"
[target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies]
-cocoa = "0.24"
+cocoa = "0.25"
[target."cfg(target_os = \"android\")".dependencies]
jni = "0.21"
diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs
index 38b7abdb2752..cbde23909c75 100644
--- a/core/tauri-runtime-wry/src/lib.rs
+++ b/core/tauri-runtime-wry/src/lib.rs
@@ -24,66 +24,65 @@ use tauri_runtime::{
WindowEventId,
};
-#[cfg(windows)]
-use webview2_com::FocusChangedEventHandler;
-#[cfg(windows)]
-use windows::Win32::{Foundation::HWND, System::WinRT::EventRegistrationToken};
#[cfg(target_os = "macos")]
-use wry::application::platform::macos::EventLoopWindowTargetExtMacOS;
+use tao::platform::macos::EventLoopWindowTargetExtMacOS;
#[cfg(target_os = "macos")]
-use wry::application::platform::macos::WindowBuilderExtMacOS;
+use tao::platform::macos::WindowBuilderExtMacOS;
#[cfg(target_os = "linux")]
-use wry::application::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
+use tao::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
#[cfg(windows)]
-use wry::application::platform::windows::{WindowBuilderExtWindows, WindowExtWindows};
+use tao::platform::windows::{WindowBuilderExtWindows, WindowExtWindows};
+#[cfg(windows)]
+use webview2_com::FocusChangedEventHandler;
+#[cfg(windows)]
+use windows::Win32::{Foundation::HWND, System::WinRT::EventRegistrationToken};
#[cfg(windows)]
-use wry::webview::WebViewBuilderExtWindows;
+use wry::WebViewBuilderExtWindows;
+use tao::{
+ dpi::{
+ LogicalPosition as TaoLogicalPosition, LogicalSize as TaoLogicalSize,
+ PhysicalPosition as TaoPhysicalPosition, PhysicalSize as TaoPhysicalSize,
+ Position as TaoPosition, Size as TaoSize,
+ },
+ event::{Event, StartCause, WindowEvent as TaoWindowEvent},
+ event_loop::{
+ ControlFlow, DeviceEventFilter as TaoDeviceEventFilter, EventLoop, EventLoopBuilder,
+ EventLoopProxy as TaoEventLoopProxy, EventLoopWindowTarget,
+ },
+ monitor::MonitorHandle,
+ window::{
+ CursorIcon as TaoCursorIcon, Fullscreen, Icon as TaoWindowIcon,
+ ProgressBarState as TaoProgressBarState, ProgressState as TaoProgressState, Theme as TaoTheme,
+ UserAttentionType as TaoUserAttentionType,
+ },
+};
#[cfg(target_os = "macos")]
use tauri_utils::TitleBarStyle;
use tauri_utils::{
config::WindowConfig, debug_eprintln, ProgressBarState, ProgressBarStatus, Theme,
};
-use wry::{
- application::{
- dpi::{
- LogicalPosition as WryLogicalPosition, LogicalSize as WryLogicalSize,
- PhysicalPosition as WryPhysicalPosition, PhysicalSize as WryPhysicalSize,
- Position as WryPosition, Size as WrySize,
- },
- event::{Event, StartCause, WindowEvent as WryWindowEvent},
- event_loop::{
- ControlFlow, DeviceEventFilter as WryDeviceEventFilter, EventLoop, EventLoopBuilder,
- EventLoopProxy as WryEventLoopProxy, EventLoopWindowTarget,
- },
- monitor::MonitorHandle,
- window::{
- CursorIcon as WryCursorIcon, Fullscreen, Icon as WryWindowIcon,
- ProgressBarState as WryProgressBarState, ProgressState as WryProgressState,
- Theme as WryTheme, UserAttentionType as WryUserAttentionType,
- },
- },
- webview::{FileDropEvent as WryFileDropEvent, Url, WebContext, WebView, WebViewBuilder},
-};
+use wry::{FileDropEvent as WryFileDropEvent, Url, WebContext, WebView, WebViewBuilder};
+pub use tao;
+pub use tao::window::{Window, WindowBuilder as TaoWindowBuilder, WindowId};
pub use wry;
-pub use wry::application::window::{Window, WindowBuilder as WryWindowBuilder, WindowId};
-pub use wry::webview::webview_version;
+pub use wry::webview_version;
#[cfg(windows)]
-use wry::webview::WebviewExtWindows;
+use wry::WebViewExtWindows;
#[cfg(target_os = "android")]
-use wry::webview::{
+use wry::{
prelude::{dispatch, find_class},
- WebViewBuilderExtAndroid, WebviewExtAndroid,
+ WebViewBuilderExtAndroid, WebViewExtAndroid,
};
#[cfg(target_os = "macos")]
-use tauri_runtime::ActivationPolicy;
-#[cfg(target_os = "macos")]
-pub use wry::application::platform::macos::{
- ActivationPolicy as WryActivationPolicy, EventLoopExtMacOS, WindowExtMacOS,
+pub use tao::platform::macos::{
+ ActivationPolicy as TaoActivationPolicy, EventLoopExtMacOS, WindowExtMacOS,
};
+#[cfg(target_os = "macos")]
+use tauri_runtime::ActivationPolicy;
use std::{
cell::RefCell,
@@ -104,8 +103,8 @@ use std::{
};
pub type WebviewId = u32;
-type IpcHandler = dyn Fn(&Window, String) + 'static;
-type FileDropHandler = dyn Fn(&Window, WryFileDropEvent) -> bool + 'static;
+type IpcHandler = dyn Fn(String) + 'static;
+type FileDropHandler = dyn Fn(WryFileDropEvent) -> bool + 'static;
mod webview;
pub use webview::Webview;
@@ -172,7 +171,7 @@ pub(crate) fn send_user_message(
pub struct Context {
pub webview_id_map: WebviewIdStore,
main_thread_id: ThreadId,
- pub proxy: WryEventLoopProxy>,
+ pub proxy: TaoEventLoopProxy>,
main_thread: DispatcherMainThreadContext,
plugins: Arc + Send>>>>,
next_window_id: Arc,
@@ -275,29 +274,29 @@ impl fmt::Debug for Context {
}
}
-pub struct DeviceEventFilterWrapper(pub WryDeviceEventFilter);
+pub struct DeviceEventFilterWrapper(pub TaoDeviceEventFilter);
impl From for DeviceEventFilterWrapper {
fn from(item: DeviceEventFilter) -> Self {
match item {
- DeviceEventFilter::Always => Self(WryDeviceEventFilter::Always),
- DeviceEventFilter::Never => Self(WryDeviceEventFilter::Never),
- DeviceEventFilter::Unfocused => Self(WryDeviceEventFilter::Unfocused),
+ DeviceEventFilter::Always => Self(TaoDeviceEventFilter::Always),
+ DeviceEventFilter::Never => Self(TaoDeviceEventFilter::Never),
+ DeviceEventFilter::Unfocused => Self(TaoDeviceEventFilter::Unfocused),
}
}
}
-/// Wrapper around a [`wry::application::window::Icon`] that can be created from an [`Icon`].
-pub struct WryIcon(pub WryWindowIcon);
+/// Wrapper around a [`tao::window::Icon`] that can be created from an [`Icon`].
+pub struct TaoIcon(pub TaoWindowIcon);
fn icon_err(e: E) -> Error {
Error::InvalidIcon(Box::new(e))
}
-impl TryFrom for WryIcon {
+impl TryFrom for TaoIcon {
type Error = Error;
fn try_from(icon: Icon) -> std::result::Result {
- WryWindowIcon::from_rgba(icon.rgba, icon.width, icon.height)
+ TaoWindowIcon::from_rgba(icon.rgba, icon.width, icon.height)
.map(Self)
.map_err(icon_err)
}
@@ -306,11 +305,11 @@ impl TryFrom for WryIcon {
pub struct WindowEventWrapper(pub Option);
impl WindowEventWrapper {
- fn parse(webview: &Option, event: &WryWindowEvent<'_>) -> Self {
+ fn parse(webview: &Option, event: &TaoWindowEvent<'_>) -> Self {
match event {
// resized event from tao doesn't include a reliable size on macOS
// because wry replaces the NSView
- WryWindowEvent::Resized(_) => {
+ TaoWindowEvent::Resized(_) => {
if let Some(webview) = webview {
Self(Some(WindowEvent::Resized(
PhysicalSizeWrapper(webview.inner_size()).into(),
@@ -324,23 +323,23 @@ impl WindowEventWrapper {
}
}
-pub fn map_theme(theme: &WryTheme) -> Theme {
+pub fn map_theme(theme: &TaoTheme) -> Theme {
match theme {
- WryTheme::Light => Theme::Light,
- WryTheme::Dark => Theme::Dark,
+ TaoTheme::Light => Theme::Light,
+ TaoTheme::Dark => Theme::Dark,
_ => Theme::Light,
}
}
-impl<'a> From<&WryWindowEvent<'a>> for WindowEventWrapper {
- fn from(event: &WryWindowEvent<'a>) -> Self {
+impl<'a> From<&TaoWindowEvent<'a>> for WindowEventWrapper {
+ fn from(event: &TaoWindowEvent<'a>) -> Self {
let event = match event {
- WryWindowEvent::Resized(size) => WindowEvent::Resized(PhysicalSizeWrapper(*size).into()),
- WryWindowEvent::Moved(position) => {
+ TaoWindowEvent::Resized(size) => WindowEvent::Resized(PhysicalSizeWrapper(*size).into()),
+ TaoWindowEvent::Moved(position) => {
WindowEvent::Moved(PhysicalPositionWrapper(*position).into())
}
- WryWindowEvent::Destroyed => WindowEvent::Destroyed,
- WryWindowEvent::ScaleFactorChanged {
+ TaoWindowEvent::Destroyed => WindowEvent::Destroyed,
+ TaoWindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size,
} => WindowEvent::ScaleFactorChanged {
@@ -348,8 +347,8 @@ impl<'a> From<&WryWindowEvent<'a>> for WindowEventWrapper {
new_inner_size: PhysicalSizeWrapper(**new_inner_size).into(),
},
#[cfg(any(target_os = "linux", target_os = "macos"))]
- WryWindowEvent::Focused(focused) => WindowEvent::Focused(*focused),
- WryWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)),
+ TaoWindowEvent::Focused(focused) => WindowEvent::Focused(*focused),
+ TaoWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)),
_ => return Self(None),
};
Self(Some(event))
@@ -378,7 +377,7 @@ impl From for Monitor {
}
}
-pub struct PhysicalPositionWrapper(pub WryPhysicalPosition);
+pub struct PhysicalPositionWrapper(pub TaoPhysicalPosition);
impl From> for PhysicalPosition {
fn from(position: PhysicalPositionWrapper) -> Self {
@@ -391,25 +390,25 @@ impl From> for PhysicalPosition {
impl From> for PhysicalPositionWrapper {
fn from(position: PhysicalPosition) -> Self {
- Self(WryPhysicalPosition {
+ Self(TaoPhysicalPosition {
x: position.x,
y: position.y,
})
}
}
-struct LogicalPositionWrapper(WryLogicalPosition);
+struct LogicalPositionWrapper(TaoLogicalPosition);
impl From> for LogicalPositionWrapper {
fn from(position: LogicalPosition) -> Self {
- Self(WryLogicalPosition {
+ Self(TaoLogicalPosition {
x: position.x,
y: position.y,
})
}
}
-pub struct PhysicalSizeWrapper(pub WryPhysicalSize);
+pub struct PhysicalSizeWrapper(pub TaoPhysicalSize);
impl From> for PhysicalSize {
fn from(size: PhysicalSizeWrapper) -> Self {
@@ -422,127 +421,127 @@ impl From> for PhysicalSize {
impl From> for PhysicalSizeWrapper {
fn from(size: PhysicalSize) -> Self {
- Self(WryPhysicalSize {
+ Self(TaoPhysicalSize {
width: size.width,
height: size.height,
})
}
}
-struct LogicalSizeWrapper(WryLogicalSize);
+struct LogicalSizeWrapper(TaoLogicalSize);
impl From> for LogicalSizeWrapper {
fn from(size: LogicalSize) -> Self {
- Self(WryLogicalSize {
+ Self(TaoLogicalSize {
width: size.width,
height: size.height,
})
}
}
-pub struct SizeWrapper(pub WrySize);
+pub struct SizeWrapper(pub TaoSize);
impl From for SizeWrapper {
fn from(size: Size) -> Self {
match size {
- Size::Logical(s) => Self(WrySize::Logical(LogicalSizeWrapper::from(s).0)),
- Size::Physical(s) => Self(WrySize::Physical(PhysicalSizeWrapper::from(s).0)),
+ Size::Logical(s) => Self(TaoSize::Logical(LogicalSizeWrapper::from(s).0)),
+ Size::Physical(s) => Self(TaoSize::Physical(PhysicalSizeWrapper::from(s).0)),
}
}
}
-pub struct PositionWrapper(pub WryPosition);
+pub struct PositionWrapper(pub TaoPosition);
impl From for PositionWrapper {
fn from(position: Position) -> Self {
match position {
- Position::Logical(s) => Self(WryPosition::Logical(LogicalPositionWrapper::from(s).0)),
- Position::Physical(s) => Self(WryPosition::Physical(PhysicalPositionWrapper::from(s).0)),
+ Position::Logical(s) => Self(TaoPosition::Logical(LogicalPositionWrapper::from(s).0)),
+ Position::Physical(s) => Self(TaoPosition::Physical(PhysicalPositionWrapper::from(s).0)),
}
}
}
#[derive(Debug, Clone)]
-pub struct UserAttentionTypeWrapper(pub WryUserAttentionType);
+pub struct UserAttentionTypeWrapper(pub TaoUserAttentionType);
impl From for UserAttentionTypeWrapper {
fn from(request_type: UserAttentionType) -> Self {
let o = match request_type {
- UserAttentionType::Critical => WryUserAttentionType::Critical,
- UserAttentionType::Informational => WryUserAttentionType::Informational,
+ UserAttentionType::Critical => TaoUserAttentionType::Critical,
+ UserAttentionType::Informational => TaoUserAttentionType::Informational,
};
Self(o)
}
}
#[derive(Debug)]
-pub struct CursorIconWrapper(pub WryCursorIcon);
+pub struct CursorIconWrapper(pub TaoCursorIcon);
impl From for CursorIconWrapper {
fn from(icon: CursorIcon) -> Self {
use CursorIcon::*;
let i = match icon {
- Default => WryCursorIcon::Default,
- Crosshair => WryCursorIcon::Crosshair,
- Hand => WryCursorIcon::Hand,
- Arrow => WryCursorIcon::Arrow,
- Move => WryCursorIcon::Move,
- Text => WryCursorIcon::Text,
- Wait => WryCursorIcon::Wait,
- Help => WryCursorIcon::Help,
- Progress => WryCursorIcon::Progress,
- NotAllowed => WryCursorIcon::NotAllowed,
- ContextMenu => WryCursorIcon::ContextMenu,
- Cell => WryCursorIcon::Cell,
- VerticalText => WryCursorIcon::VerticalText,
- Alias => WryCursorIcon::Alias,
- Copy => WryCursorIcon::Copy,
- NoDrop => WryCursorIcon::NoDrop,
- Grab => WryCursorIcon::Grab,
- Grabbing => WryCursorIcon::Grabbing,
- AllScroll => WryCursorIcon::AllScroll,
- ZoomIn => WryCursorIcon::ZoomIn,
- ZoomOut => WryCursorIcon::ZoomOut,
- EResize => WryCursorIcon::EResize,
- NResize => WryCursorIcon::NResize,
- NeResize => WryCursorIcon::NeResize,
- NwResize => WryCursorIcon::NwResize,
- SResize => WryCursorIcon::SResize,
- SeResize => WryCursorIcon::SeResize,
- SwResize => WryCursorIcon::SwResize,
- WResize => WryCursorIcon::WResize,
- EwResize => WryCursorIcon::EwResize,
- NsResize => WryCursorIcon::NsResize,
- NeswResize => WryCursorIcon::NeswResize,
- NwseResize => WryCursorIcon::NwseResize,
- ColResize => WryCursorIcon::ColResize,
- RowResize => WryCursorIcon::RowResize,
- _ => WryCursorIcon::Default,
+ Default => TaoCursorIcon::Default,
+ Crosshair => TaoCursorIcon::Crosshair,
+ Hand => TaoCursorIcon::Hand,
+ Arrow => TaoCursorIcon::Arrow,
+ Move => TaoCursorIcon::Move,
+ Text => TaoCursorIcon::Text,
+ Wait => TaoCursorIcon::Wait,
+ Help => TaoCursorIcon::Help,
+ Progress => TaoCursorIcon::Progress,
+ NotAllowed => TaoCursorIcon::NotAllowed,
+ ContextMenu => TaoCursorIcon::ContextMenu,
+ Cell => TaoCursorIcon::Cell,
+ VerticalText => TaoCursorIcon::VerticalText,
+ Alias => TaoCursorIcon::Alias,
+ Copy => TaoCursorIcon::Copy,
+ NoDrop => TaoCursorIcon::NoDrop,
+ Grab => TaoCursorIcon::Grab,
+ Grabbing => TaoCursorIcon::Grabbing,
+ AllScroll => TaoCursorIcon::AllScroll,
+ ZoomIn => TaoCursorIcon::ZoomIn,
+ ZoomOut => TaoCursorIcon::ZoomOut,
+ EResize => TaoCursorIcon::EResize,
+ NResize => TaoCursorIcon::NResize,
+ NeResize => TaoCursorIcon::NeResize,
+ NwResize => TaoCursorIcon::NwResize,
+ SResize => TaoCursorIcon::SResize,
+ SeResize => TaoCursorIcon::SeResize,
+ SwResize => TaoCursorIcon::SwResize,
+ WResize => TaoCursorIcon::WResize,
+ EwResize => TaoCursorIcon::EwResize,
+ NsResize => TaoCursorIcon::NsResize,
+ NeswResize => TaoCursorIcon::NeswResize,
+ NwseResize => TaoCursorIcon::NwseResize,
+ ColResize => TaoCursorIcon::ColResize,
+ RowResize => TaoCursorIcon::RowResize,
+ _ => TaoCursorIcon::Default,
};
Self(i)
}
}
-pub struct ProgressStateWrapper(pub WryProgressState);
+pub struct ProgressStateWrapper(pub TaoProgressState);
impl From for ProgressStateWrapper {
fn from(status: ProgressBarStatus) -> Self {
let state = match status {
- ProgressBarStatus::None => WryProgressState::None,
- ProgressBarStatus::Normal => WryProgressState::Normal,
- ProgressBarStatus::Indeterminate => WryProgressState::Indeterminate,
- ProgressBarStatus::Paused => WryProgressState::Paused,
- ProgressBarStatus::Error => WryProgressState::Error,
+ ProgressBarStatus::None => TaoProgressState::None,
+ ProgressBarStatus::Normal => TaoProgressState::Normal,
+ ProgressBarStatus::Indeterminate => TaoProgressState::Indeterminate,
+ ProgressBarStatus::Paused => TaoProgressState::Paused,
+ ProgressBarStatus::Error => TaoProgressState::Error,
};
Self(state)
}
}
-pub struct ProgressBarStateWrapper(pub WryProgressBarState);
+pub struct ProgressBarStateWrapper(pub TaoProgressBarState);
impl From for ProgressBarStateWrapper {
fn from(progress_state: ProgressBarState) -> Self {
- Self(WryProgressBarState {
+ Self(TaoProgressBarState {
progress: progress_state.progress,
state: progress_state
.status
@@ -554,7 +553,7 @@ impl From for ProgressBarStateWrapper {
#[derive(Clone, Default)]
pub struct WindowBuilderWrapper {
- inner: WryWindowBuilder,
+ inner: TaoWindowBuilder,
center: bool,
#[cfg(target_os = "macos")]
tabbing_identifier: Option,
@@ -659,28 +658,28 @@ impl WindowBuilder for WindowBuilderWrapper {
}
fn position(mut self, x: f64, y: f64) -> Self {
- self.inner = self.inner.with_position(WryLogicalPosition::new(x, y));
+ self.inner = self.inner.with_position(TaoLogicalPosition::new(x, y));
self
}
fn inner_size(mut self, width: f64, height: f64) -> Self {
self.inner = self
.inner
- .with_inner_size(WryLogicalSize::new(width, height));
+ .with_inner_size(TaoLogicalSize::new(width, height));
self
}
fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
self.inner = self
.inner
- .with_min_inner_size(WryLogicalSize::new(min_width, min_height));
+ .with_min_inner_size(TaoLogicalSize::new(min_width, min_height));
self
}
fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
self.inner = self
.inner
- .with_max_inner_size(WryLogicalSize::new(max_width, max_height));
+ .with_max_inner_size(TaoLogicalSize::new(max_width, max_height));
self
}
@@ -834,7 +833,7 @@ impl WindowBuilder for WindowBuilderWrapper {
fn icon(mut self, icon: Icon) -> Result {
self.inner = self
.inner
- .with_window_icon(Some(WryIcon::try_from(icon)?.0));
+ .with_window_icon(Some(TaoIcon::try_from(icon)?.0));
Ok(self)
}
@@ -853,8 +852,8 @@ impl WindowBuilder for WindowBuilderWrapper {
fn theme(mut self, theme: Option) -> Self {
self.inner = self.inner.with_theme(if let Some(t) = theme {
match t {
- Theme::Dark => Some(WryTheme::Dark),
- _ => Some(WryTheme::Light),
+ Theme::Dark => Some(TaoTheme::Dark),
+ _ => Some(TaoTheme::Light),
}
} else {
None
@@ -902,11 +901,11 @@ impl From for FileDropEvent {
match event.0 {
WryFileDropEvent::Hovered { paths, position } => FileDropEvent::Hovered {
paths: paths.into_iter().map(decode_path).collect(),
- position: PhysicalPositionWrapper(position).into(),
+ position: PhysicalPosition::new(position.0 as f64, position.1 as f64),
},
WryFileDropEvent::Dropped { paths, position } => FileDropEvent::Dropped {
paths: paths.into_iter().map(decode_path).collect(),
- position: PhysicalPositionWrapper(position).into(),
+ position: PhysicalPosition::new(position.0 as f64, position.1 as f64),
},
// default to cancelled
// FIXME(maybe): Add `FileDropEvent::Unknown` event?
@@ -1038,7 +1037,7 @@ pub enum WindowMessage {
SetPosition(Position),
SetFullscreen(bool),
SetFocus,
- SetIcon(WryWindowIcon),
+ SetIcon(TaoWindowIcon),
SetSkipTaskbar(bool),
SetCursorGrab(bool),
SetCursorVisible(bool),
@@ -1077,7 +1076,7 @@ pub enum Message {
CreateWebview(WebviewId, CreateWebviewClosure),
CreateWindow(
WebviewId,
- Box (String, WryWindowBuilder) + Send>,
+ Box (String, TaoWindowBuilder) + Send>,
Sender>>,
),
UserEvent(T),
@@ -1093,7 +1092,7 @@ impl Clone for Message {
}
}
-/// The Tauri [`Dispatch`] for [`Wry`].
+/// The Tauri [`Dispatch`] for [`Tao`].
#[derive(Debug, Clone)]
pub struct WryDispatcher {
window_id: WebviewId,
@@ -1504,7 +1503,7 @@ impl Dispatch for WryDispatcher {
&self.context,
Message::Window(
self.window_id,
- WindowMessage::SetIcon(WryIcon::try_from(icon)?.0),
+ WindowMessage::SetIcon(TaoIcon::try_from(icon)?.0),
),
)
}
@@ -1585,6 +1584,7 @@ impl Dispatch for WryDispatcher {
#[derive(Clone)]
enum WindowHandle {
Webview {
+ window: Arc,
inner: Rc,
context_store: WebContextStore,
// the key of the WebContext if it's not shared
@@ -1597,6 +1597,7 @@ impl Drop for WindowHandle {
fn drop(&mut self) {
if let Self::Webview {
inner,
+ window: _,
context_store,
context_key,
} = self
@@ -1620,17 +1621,17 @@ impl Deref for WindowHandle {
#[inline(always)]
fn deref(&self) -> &Window {
match self {
- Self::Webview { inner, .. } => inner.window(),
+ Self::Webview { window, .. } => window,
Self::Window(w) => w,
}
}
}
impl WindowHandle {
- fn inner_size(&self) -> WryPhysicalSize {
+ fn inner_size(&self) -> TaoPhysicalSize {
match self {
WindowHandle::Window(w) => w.inner_size(),
- WindowHandle::Webview { inner, .. } => inner.inner_size(),
+ WindowHandle::Webview { window, .. } => window.inner_size(),
}
}
}
@@ -1651,7 +1652,7 @@ impl fmt::Debug for WindowWrapper {
}
#[derive(Debug, Clone)]
-pub struct EventProxy(WryEventLoopProxy>);
+pub struct EventProxy(TaoEventLoopProxy>);
#[cfg(target_os = "ios")]
#[allow(clippy::non_send_fields_in_send_ty)]
@@ -1676,7 +1677,7 @@ pub trait Plugin {
&mut self,
event: &Event>,
event_loop: &EventLoopWindowTarget>,
- proxy: &WryEventLoopProxy>,
+ proxy: &TaoEventLoopProxy>,
control_flow: &mut ControlFlow,
context: EventLoopIterationContext<'_, T>,
web_context: &WebContextStore,
@@ -1712,7 +1713,7 @@ unsafe impl Sync for WryHandle {}
impl WryHandle {
/// Creates a new tao window using a callback, and returns its window id.
- pub fn create_tao_window (String, WryWindowBuilder) + Send + 'static>(
+ pub fn create_tao_window (String, TaoWindowBuilder) + Send + 'static>(
&self,
f: F,
) -> Result> {
@@ -1845,7 +1846,7 @@ impl Wry {
) -> Result {
#[cfg(windows)]
if let Some(hook) = args.msg_hook {
- use wry::application::platform::windows::EventLoopBuilderExtWindows;
+ use tao::platform::windows::EventLoopBuilderExtWindows;
event_loop_builder.with_msg_hook(hook);
}
Self::init(event_loop_builder.build())
@@ -1897,7 +1898,7 @@ impl Runtime for Wry {
target_os = "openbsd"
))]
fn new_any_thread(args: RuntimeInitArgs) -> Result {
- use wry::application::platform::unix::EventLoopBuilderExtUnix;
+ use tao::platform::unix::EventLoopBuilderExtUnix;
let mut event_loop_builder = EventLoopBuilder::>::with_user_event();
event_loop_builder.with_any_thread(true);
Self::init_with_builder(event_loop_builder, args)
@@ -1905,7 +1906,7 @@ impl Runtime for Wry {
#[cfg(windows)]
fn new_any_thread(args: RuntimeInitArgs) -> Result {
- use wry::application::platform::windows::EventLoopBuilderExtWindows;
+ use tao::platform::windows::EventLoopBuilderExtWindows;
let mut event_loop_builder = EventLoopBuilder::>::with_user_event();
event_loop_builder.with_any_thread(true);
Self::init_with_builder(event_loop_builder, args)
@@ -1977,9 +1978,9 @@ impl Runtime for Wry {
self
.event_loop
.set_activation_policy(match activation_policy {
- ActivationPolicy::Regular => WryActivationPolicy::Regular,
- ActivationPolicy::Accessory => WryActivationPolicy::Accessory,
- ActivationPolicy::Prohibited => WryActivationPolicy::Prohibited,
+ ActivationPolicy::Regular => TaoActivationPolicy::Regular,
+ ActivationPolicy::Accessory => TaoActivationPolicy::Accessory,
+ ActivationPolicy::Prohibited => TaoActivationPolicy::Prohibited,
_ => unimplemented!(),
});
}
@@ -2002,7 +2003,7 @@ impl Runtime for Wry {
#[cfg(desktop)]
fn run_iteration) + 'static>(&mut self, mut callback: F) -> RunIteration {
- use wry::application::platform::run_return::EventLoopExtRunReturn;
+ use tao::platform::run_return::EventLoopExtRunReturn;
let windows = self.context.main_thread.windows.clone();
let webview_id_map = self.context.webview_id_map.clone();
let web_context = &self.context.main_thread.web_context;
@@ -2144,12 +2145,12 @@ fn handle_user_message(
target_os = "openbsd"
))]
{
- use wry::webview::WebviewExtUnix;
+ use wry::WebViewExtUnix;
f(w.webview());
}
#[cfg(target_os = "macos")]
{
- use wry::webview::WebviewExtMacOS;
+ use wry::WebViewExtMacOS;
f(Webview {
webview: w.webview(),
manager: w.manager(),
@@ -2158,12 +2159,13 @@ fn handle_user_message(
}
#[cfg(target_os = "ios")]
{
- use wry::{application::platform::ios::WindowExtIOS, webview::WebviewExtIOS};
+ use tao::platform::ios::WindowExtIOS;
+ use wry::WebViewExtIOS;
f(Webview {
webview: w.webview(),
manager: w.manager(),
- view_controller: w.window().ui_view_controller() as cocoa::base::id,
+ view_controller: window.ui_view_controller() as cocoa::base::id,
});
}
#[cfg(windows)]
@@ -2508,22 +2510,22 @@ fn handle_event_loop(
match event {
#[cfg(windows)]
- WryWindowEvent::ThemeChanged(theme) => {
+ TaoWindowEvent::ThemeChanged(theme) => {
if let Some(window) = windows.borrow().get(&window_id) {
if let Some(WindowHandle::Webview { inner, .. }) = &window.inner {
let theme = match theme {
- WryTheme::Dark => wry::webview::Theme::Dark,
- WryTheme::Light => wry::webview::Theme::Light,
- _ => wry::webview::Theme::Light,
+ TaoTheme::Dark => wry::Theme::Dark,
+ TaoTheme::Light => wry::Theme::Light,
+ _ => wry::Theme::Light,
};
inner.set_theme(theme);
}
}
}
- WryWindowEvent::CloseRequested => {
+ TaoWindowEvent::CloseRequested => {
on_close_requested(callback, window_id, windows.clone());
}
- WryWindowEvent::Destroyed => {
+ TaoWindowEvent::Destroyed => {
let removed = windows.borrow_mut().remove(&window_id).is_some();
if removed {
let is_empty = windows.borrow().is_empty();
@@ -2611,13 +2613,13 @@ fn on_window_close(window_id: WebviewId, windows: Rc) -> Result<()> {
+pub fn center_window(window: &Window, window_size: TaoPhysicalSize) -> Result<()> {
if let Some(monitor) = window.current_monitor() {
let screen_size = monitor.size();
let monitor_pos = monitor.position();
let x = (screen_size.width as i32 - window_size.width as i32) / 2;
let y = (screen_size.height as i32 - window_size.height as i32) / 2;
- window.set_outer_position(WryPhysicalPosition::new(
+ window.set_outer_position(TaoPhysicalPosition::new(
monitor_pos.x + x,
monitor_pos.y + y,
));
@@ -2707,8 +2709,26 @@ fn create_webview(
handler(raw);
}
- let mut webview_builder = WebViewBuilder::new(window)
- .map_err(|e| Error::CreateWebview(Box::new(e)))?
+ #[cfg(any(
+ target_os = "windows",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android"
+ ))]
+ let builder = WebViewBuilder::new(&window);
+ #[cfg(not(any(
+ target_os = "windows",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android"
+ )))]
+ let builder = {
+ use wry::WebViewBuilderExtUnix;
+ let vbox = window.default_vbox().unwrap();
+ WebViewBuilder::new_gtk(vbox)
+ };
+
+ let mut webview_builder = builder
.with_focused(focused)
.with_url(&url)
.unwrap() // safe to unwrap because we validate the URL beforehand
@@ -2732,8 +2752,8 @@ fn create_webview(
page_load_handler(
url,
match event {
- wry::webview::PageLoadEvent::Started => tauri_runtime::window::PageLoadEvent::Started,
- wry::webview::PageLoadEvent::Finished => tauri_runtime::window::PageLoadEvent::Finished,
+ wry::PageLoadEvent::Started => tauri_runtime::window::PageLoadEvent::Started,
+ wry::PageLoadEvent::Finished => tauri_runtime::window::PageLoadEvent::Finished,
},
)
});
@@ -2752,9 +2772,9 @@ fn create_webview(
if let Some(theme) = window_theme {
webview_builder = webview_builder.with_theme(match theme {
- WryTheme::Dark => wry::webview::Theme::Dark,
- WryTheme::Light => wry::webview::Theme::Light,
- _ => wry::webview::Theme::Light,
+ TaoTheme::Dark => wry::Theme::Dark,
+ TaoTheme::Light => wry::Theme::Light,
+ _ => wry::Theme::Light,
});
}
}
@@ -2765,8 +2785,12 @@ fn create_webview(
}
if let Some(handler) = ipc_handler {
- webview_builder =
- webview_builder.with_ipc_handler(create_ipc_handler(context.clone(), label.clone(), handler));
+ webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
+ window_id,
+ context.clone(),
+ label.clone(),
+ handler,
+ ));
}
for (scheme, protocol) in uri_scheme_protocols {
@@ -2810,11 +2834,11 @@ fn create_webview(
};
if webview_attributes.clipboard {
- webview_builder.webview.clipboard = true;
+ webview_builder.attrs.clipboard = true;
}
if webview_attributes.incognito {
- webview_builder.webview.incognito = true;
+ webview_builder.attrs.incognito = true;
}
#[cfg(any(debug_assertions, feature = "devtools"))]
@@ -2876,6 +2900,7 @@ fn create_webview(
Ok(WindowWrapper {
label,
inner: Some(WindowHandle::Webview {
+ window: Arc::new(window),
inner: Rc::new(webview),
context_store: web_context_store.clone(),
context_key: if automation_enabled {
@@ -2890,12 +2915,12 @@ fn create_webview(
/// Create a wry ipc handler from a tauri ipc handler.
fn create_ipc_handler(
+ window_id: u32,
context: Context,
label: String,
handler: WebviewIpcHandler>,
) -> Box {
- Box::new(move |window, request| {
- let window_id = context.webview_id_map.get(&window.id()).unwrap();
+ Box::new(move |request| {
handler(
DetachedWindow {
dispatcher: WryDispatcher {
@@ -2911,7 +2936,7 @@ fn create_ipc_handler(
/// Create a wry file drop handler.
fn create_file_drop_handler(window_event_listeners: WindowEventListeners) -> Box {
- Box::new(move |_window, event| {
+ Box::new(move |event| {
let event: FileDropEvent = FileDropEventWrapper(event).into();
let window_event = WindowEvent::FileDrop(event);
let listeners_map = window_event_listeners.lock().unwrap();
diff --git a/core/tauri-runtime-wry/src/webview.rs b/core/tauri-runtime-wry/src/webview.rs
index 283c32fb5c3b..fc01cdf2a6a6 100644
--- a/core/tauri-runtime-wry/src/webview.rs
+++ b/core/tauri-runtime-wry/src/webview.rs
@@ -10,9 +10,7 @@
target_os = "openbsd"
))]
mod imp {
- use std::rc::Rc;
-
- pub type Webview = Rc;
+ pub type Webview = webkit2gtk::WebView;
}
#[cfg(target_os = "macos")]
@@ -47,7 +45,7 @@ mod imp {
#[cfg(target_os = "android")]
mod imp {
- use wry::webview::JniHandle;
+ use wry::JniHandle;
pub type Webview = JniHandle;
}
diff --git a/core/tauri-runtime/CHANGELOG.md b/core/tauri-runtime/CHANGELOG.md
index eac633c1f2f1..d7fea7345601 100644
--- a/core/tauri-runtime/CHANGELOG.md
+++ b/core/tauri-runtime/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## \[1.0.0-alpha.5]
+
+### Dependencies
+
+- Upgraded to `tauri-utils@2.0.0-alpha.11`
+
## \[1.0.0-alpha.4]
### New Features
diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml
index 8c30ab13d5f2..1220495473fe 100644
--- a/core/tauri-runtime/Cargo.toml
+++ b/core/tauri-runtime/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-runtime"
-version = "1.0.0-alpha.4"
+version = "1.0.0-alpha.5"
description = "Runtime for Tauri applications"
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@@ -29,13 +29,13 @@ targets = [
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
thiserror = "1.0"
-tauri-utils = { version = "2.0.0-alpha.10", path = "../tauri-utils" }
+tauri-utils = { version = "2.0.0-alpha.11", path = "../tauri-utils" }
http = "0.2.4"
raw-window-handle = "0.5"
url = { version = "2" }
[target."cfg(windows)".dependencies.windows]
-version = "0.51"
+version = "0.52"
features = [ "Win32_Foundation" ]
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
diff --git a/core/tauri-utils/CHANGELOG.md b/core/tauri-utils/CHANGELOG.md
index 51d521f67ee2..0513f1c4bb83 100644
--- a/core/tauri-utils/CHANGELOG.md
+++ b/core/tauri-utils/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## \[2.0.0-alpha.11]
+
+### Breaking Changes
+
+- [`5e84e92e`](https://www.github.com/tauri-apps/tauri/commit/5e84e92e99376f24b730f8eba002239379b593e1)([#8243](https://www.github.com/tauri-apps/tauri/pull/8243)) Changed `platform::windows_version` to return a `(u32, u32, u32)` instead of `Option<(u32, u32, u32)>`
+
## \[2.0.0-alpha.10]
### Enhancements
diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml
index 010a183c740f..b9624529561f 100644
--- a/core/tauri-utils/Cargo.toml
+++ b/core/tauri-utils/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri-utils"
-version = "2.0.0-alpha.10"
+version = "2.0.0-alpha.11"
description = "Utilities for Tauri"
exclude = [ "CHANGELOG.md", "/target" ]
readme = "README.md"
@@ -43,16 +43,6 @@ log = "0.4.20"
[target."cfg(target_os = \"linux\")".dependencies]
heck = "0.4"
-[target."cfg(windows)".dependencies.windows]
-version = "0.51.1"
-features = [
- "implement",
- "Win32_Foundation",
- "Win32_System_Com",
- "Win32_System_LibraryLoader",
- "Win32_System_SystemInformation"
-]
-
[features]
build = [ "proc-macro2", "quote" ]
compression = [ "brotli" ]
diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs
index 5d054c9a2f86..34744989ae27 100644
--- a/core/tauri-utils/src/config.rs
+++ b/core/tauri-utils/src/config.rs
@@ -282,6 +282,81 @@ pub struct DebConfig {
pub desktop_template: Option,
}
+/// Position coordinates struct.
+#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct Position {
+ /// X coordinate.
+ pub x: u32,
+ /// Y coordinate.
+ pub y: u32,
+}
+
+/// Size of the window.
+#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct Size {
+ /// Width of the window.
+ pub width: u32,
+ /// Height of the window.
+ pub height: u32,
+}
+
+/// Configuration for Apple Disk Image (.dmg) bundles.
+///
+/// See more: https://tauri.app/v1/api/config#dmgconfig
+#[skip_serializing_none]
+#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
+#[cfg_attr(feature = "schema", derive(JsonSchema))]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct DmgConfig {
+ /// Image to use as the background in dmg file. Accepted formats: `png`/`jpg`/`gif`.
+ pub background: Option,
+ /// Position of volume window on screen.
+ pub window_position: Option,
+ /// Size of volume window.
+ #[serde(default = "dmg_window_size", alias = "window-size")]
+ pub window_size: Size,
+ /// Position of app file on window.
+ #[serde(default = "dmg_app_position", alias = "app-position")]
+ pub app_position: Position,
+ /// Position of application folder on window.
+ #[serde(
+ default = "dmg_application_folder_position",
+ alias = "application-folder-position"
+ )]
+ pub application_folder_position: Position,
+}
+
+impl Default for DmgConfig {
+ fn default() -> Self {
+ Self {
+ background: None,
+ window_position: None,
+ window_size: dmg_window_size(),
+ app_position: dmg_app_position(),
+ application_folder_position: dmg_application_folder_position(),
+ }
+ }
+}
+
+fn dmg_window_size() -> Size {
+ Size {
+ width: 660,
+ height: 400,
+ }
+}
+
+fn dmg_app_position() -> Position {
+ Position { x: 180, y: 170 }
+}
+
+fn dmg_application_folder_position() -> Position {
+ Position { x: 480, y: 170 }
+}
+
fn de_minimum_system_version<'de, D>(deserializer: D) -> Result, D::Error>
where
D: Deserializer<'de>,
@@ -850,6 +925,9 @@ pub struct BundleConfig {
/// Configuration for the Debian bundle.
#[serde(default)]
pub deb: DebConfig,
+ /// DMG-specific settings.
+ #[serde(default)]
+ pub dmg: DmgConfig,
/// Configuration for the macOS bundles.
#[serde(rename = "macOS", default)]
pub macos: MacConfig,
@@ -2440,6 +2518,7 @@ mod build {
let long_description = quote!(None);
let appimage = quote!(Default::default());
let deb = quote!(Default::default());
+ let dmg = quote!(Default::default());
let macos = quote!(Default::default());
let external_bin = opt_vec_str_lit(self.external_bin.as_ref());
let windows = &self.windows;
@@ -2463,6 +2542,7 @@ mod build {
long_description,
appimage,
deb,
+ dmg,
macos,
external_bin,
windows,
@@ -2771,6 +2851,7 @@ mod test {
long_description: None,
appimage: Default::default(),
deb: Default::default(),
+ dmg: Default::default(),
macos: Default::default(),
external_bin: None,
windows: Default::default(),
diff --git a/core/tauri-utils/src/platform.rs b/core/tauri-utils/src/platform.rs
index 248c5aa66cee..6659438b6097 100644
--- a/core/tauri-utils/src/platform.rs
+++ b/core/tauri-utils/src/platform.rs
@@ -255,87 +255,3 @@ pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result bool {
- if let Some(v) = windows_version() {
- // windows 7 is 6.1
- if v.0 == 6 && v.1 == 1 {
- return true;
- }
- }
- false
- }
-
- fn encode_wide(string: impl AsRef) -> Vec {
- string.as_ref().encode_wide().chain(once(0)).collect()
- }
-
- /// Helper function to dynamically load function pointer.
- /// `library` and `function` must be null-terminated.
- pub fn get_function_impl(library: &str, function: &str) -> Option {
- let library = encode_wide(library);
- assert_eq!(function.chars().last(), Some('\0'));
- let function = PCSTR::from_raw(function.as_ptr());
-
- // Library names we will use are ASCII so we can use the A version to avoid string conversion.
- let module = unsafe { LoadLibraryW(PCWSTR::from_raw(library.as_ptr())) }.unwrap_or_default();
- if module.is_invalid() {
- None
- } else {
- Some(unsafe { GetProcAddress(module, function) })
- }
- }
-
- macro_rules! get_function {
- ($lib:expr, $func:ident) => {
- get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
- .map(|f| unsafe { std::mem::transmute::(f) })
- };
- }
-
- /// Returns a tuple of (major, minor, buildnumber) for the Windows version.
- pub fn windows_version() -> Option<(u32, u32, u32)> {
- type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32;
- let handle = get_function!("ntdll.dll", RtlGetVersion);
- if let Some(rtl_get_version) = handle {
- unsafe {
- let mut vi = OSVERSIONINFOW {
- dwOSVersionInfoSize: 0,
- dwMajorVersion: 0,
- dwMinorVersion: 0,
- dwBuildNumber: 0,
- dwPlatformId: 0,
- szCSDVersion: [0; 128],
- };
-
- let status = (rtl_get_version)(&mut vi as _);
-
- if status >= 0 {
- Some((vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber))
- } else {
- None
- }
- }
- } else {
- None
- }
- }
-}
diff --git a/core/tauri/CHANGELOG.md b/core/tauri/CHANGELOG.md
index c82ac1d3a3b0..4f05f8ff7303 100644
--- a/core/tauri/CHANGELOG.md
+++ b/core/tauri/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+## \[2.0.0-alpha.18]
+
+### Bug Fixes
+
+- [`b5f40ae5`](https://www.github.com/tauri-apps/tauri/commit/b5f40ae58dded34b9ddef8e301d0e6e777d135d6)([#8147](https://www.github.com/tauri-apps/tauri/pull/8147)) Fixes global events not reaching to window listeners.
+
+### Dependencies
+
+- Upgraded to `tauri-macros@2.0.0-alpha.11`
+- Upgraded to `tauri-build@2.0.0-alpha.12`
+- Upgraded to `tauri-utils@2.0.0-alpha.11`
+- Upgraded to `tauri-runtime@1.0.0-alpha.5`
+- Upgraded to `tauri-runtime-wry@1.0.0-alpha.6`
+
## \[2.0.0-alpha.17]
### Enhancements
diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml
index e6f5abff15a9..35409c41469b 100644
--- a/core/tauri/Cargo.toml
+++ b/core/tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tauri"
-version = "2.0.0-alpha.17"
+version = "2.0.0-alpha.18"
description = "Make tiny, secure apps for all desktop platforms with Tauri"
exclude = [ "/test", "/.scripts", "CHANGELOG.md", "/target" ]
readme = "README.md"
@@ -49,11 +49,10 @@ uuid = { version = "1", features = [ "v4" ], optional = true }
url = { version = "2.4" }
anyhow = "1.0"
thiserror = "1.0"
-once_cell = "1"
-tauri-runtime = { version = "1.0.0-alpha.4", path = "../tauri-runtime" }
-tauri-macros = { version = "2.0.0-alpha.10", path = "../tauri-macros" }
-tauri-utils = { version = "2.0.0-alpha.10", features = [ "resources" ], path = "../tauri-utils" }
-tauri-runtime-wry = { version = "1.0.0-alpha.5", path = "../tauri-runtime-wry", optional = true }
+tauri-runtime = { version = "1.0.0-alpha.5", path = "../tauri-runtime" }
+tauri-macros = { version = "2.0.0-alpha.11", path = "../tauri-macros" }
+tauri-utils = { version = "2.0.0-alpha.11", features = [ "resources" ], path = "../tauri-utils" }
+tauri-runtime-wry = { version = "1.0.0-alpha.6", path = "../tauri-runtime-wry", optional = true }
getrandom = "0.2"
serde_repr = "0.1"
state = "0.6"
@@ -73,8 +72,8 @@ ico = { version = "0.3.0", optional = true }
http-range = { version = "0.1.5", optional = true }
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
-muda = { version = "0.10", default-features = false }
-tray-icon = { version = "0.10", default-features = false, optional = true }
+muda = { version = "0.11", default-features = false, features = [ "serde" ] }
+tray-icon = { version = "0.11", default-features = false, features = [ "serde" ], optional = true }
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
gtk = { version = "0.18", features = [ "v3_24" ] }
@@ -87,11 +86,11 @@ objc = "0.2"
window-vibrancy = "0.4"
[target."cfg(windows)".dependencies]
-webview2-com = "0.27"
+webview2-com = "0.28"
window-vibrancy = "0.4"
[target."cfg(windows)".dependencies.windows]
- version = "0.51"
+ version = "0.52"
features = [ "Win32_Foundation" ]
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
@@ -109,11 +108,10 @@ swift-rs = "1.0.6"
[build-dependencies]
heck = "0.4"
-once_cell = "1"
-tauri-build = { path = "../tauri-build/", version = "2.0.0-alpha.11" }
+tauri-build = { path = "../tauri-build/", version = "2.0.0-alpha.12" }
[dev-dependencies]
-proptest = "1.3.1"
+proptest = "1.4.0"
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
serde = { version = "1.0", features = [ "derive" ] }
diff --git a/core/tauri/build.rs b/core/tauri/build.rs
index 669de4270fdf..01a589c86409 100644
--- a/core/tauri/build.rs
+++ b/core/tauri/build.rs
@@ -4,8 +4,6 @@
use heck::AsShoutySnakeCase;
-use once_cell::sync::OnceCell;
-
use std::env::var_os;
use std::fs::read_dir;
use std::fs::read_to_string;
@@ -13,10 +11,10 @@ use std::fs::write;
use std::{
env::var,
path::{Path, PathBuf},
- sync::Mutex,
+ sync::{Mutex, OnceLock},
};
-static CHECKED_FEATURES: OnceCell>> = OnceCell::new();
+static CHECKED_FEATURES: OnceLock>> = OnceLock::new();
// checks if the given Cargo feature is enabled.
fn has_feature(feature: &str) -> bool {
diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift
index 798104c7a349..bb448bf9b985 100644
--- a/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift
+++ b/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift
@@ -9,7 +9,7 @@ let channelDataKey = CodingUserInfoKey(rawValue: "sendChannelData")!
public class Channel: Decodable {
public let id: UInt64
- let handler: (String) -> Void
+ let handler: (UInt64, String) -> Void
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
@@ -30,7 +30,7 @@ public class Channel: Decodable {
)
}
- guard let handler = decoder.userInfo[channelDataKey] as? (String) -> Void else {
+ guard let handler = decoder.userInfo[channelDataKey] as? (UInt64, String) -> Void else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "missing userInfo for Channel handler. This is a Tauri issue"
@@ -54,12 +54,12 @@ public class Channel: Decodable {
}
public func send(_ data: JsonValue) {
- handler(serialize(data))
+ handler(id, serialize(data))
}
public func send(_ data: T) throws {
let json = try JSONEncoder().encode(data)
- handler(String(decoding: json, as: UTF8.self))
+ handler(id, String(decoding: json, as: UTF8.self))
}
}
diff --git a/core/tauri/scripts/bundle.global.js b/core/tauri/scripts/bundle.global.js
index 29cf683faf99..3691845503da 100644
--- a/core/tauri/scripts/bundle.global.js
+++ b/core/tauri/scripts/bundle.global.js
@@ -1 +1 @@
-var __TAURI_IIFE__=function(e){"use strict";function t(e,t,n,i){if("a"===n&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?i:"a"===n?i.call(e):i?i.value:t.get(e)}var n;function i(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}"function"==typeof SuppressedError&&SuppressedError;class r{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),this.id=i((e=>{t(this,n,"f").call(this,e)}))}set onmessage(e){!function(e,t,n,i,r){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!r)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!r:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===i?r.call(e,n):r?r.value=n:t.set(e,n)}(this,n,e,"f")}get onmessage(){return t(this,n,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}n=new WeakMap;class a{constructor(e,t,n){this.plugin=e,this.event=t,this.channelId=n}async unregister(){return l(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function l(e,t={},n){return window.__TAURI_INTERNALS__.invoke(e,t,n)}var s=Object.freeze({__proto__:null,Channel:r,PluginListener:a,addPluginListener:async function(e,t,n){const i=new r;return i.onmessage=n,l(`plugin:${e}|register_listener`,{event:t,handler:i}).then((()=>new a(e,t,i.id)))},convertFileSrc:function(e,t="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(e,t)},invoke:l,transformCallback:i});var o,u=Object.freeze({__proto__:null,getName:async function(){return l("plugin:app|name")},getTauriVersion:async function(){return l("plugin:app|tauri_version")},getVersion:async function(){return l("plugin:app|version")},hide:async function(){return l("plugin:app|app_hide")},show:async function(){return l("plugin:app|app_show")}});async function c(e,t){await l("plugin:event|unlisten",{event:e,eventId:t})}async function p(e,t,n){return l("plugin:event|listen",{event:e,windowLabel:n?.target,handler:i(t)}).then((t=>async()=>c(e,t)))}async function h(e,t,n){return p(e,(n=>{t(n),c(e,n.id).catch((()=>{}))}),n)}async function d(e,t,n){await l("plugin:event|emit",{event:e,windowLabel:n?.target,payload:t})}!function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_FILE_DROP="tauri://file-drop",e.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",e.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",e.MENU="tauri://menu"}(o||(o={}));var y=Object.freeze({__proto__:null,get TauriEvent(){return o},emit:d,listen:p,once:h});class w{constructor(e,t){this.type="Logical",this.width=e,this.height=t}}class _{constructor(e,t){this.type="Physical",this.width=e,this.height=t}toLogical(e){return new w(this.width/e,this.height/e)}}class g{constructor(e,t){this.type="Logical",this.x=e,this.y=t}}class b{constructor(e,t){this.type="Physical",this.x=e,this.y=t}toLogical(e){return new g(this.x/e,this.y/e)}}var f,m,v=Object.freeze({__proto__:null,LogicalPosition:g,LogicalSize:w,PhysicalPosition:b,PhysicalSize:_});!function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"}(f||(f={}));class D{constructor(e){this._preventDefault=!1,this.event=e.event,this.windowLabel=e.windowLabel,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}}function E(){return new A(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}function L(){return window.__TAURI_INTERNALS__.metadata.windows.map((e=>new A(e.label,{skip:!0})))}!function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"}(m||(m={}));const I=["tauri://created","tauri://error"];class A{constructor(e,t={}){this.label=e,this.listeners=Object.create(null),t?.skip||l("plugin:window|create",{options:{...t,label:e}}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static getByLabel(e){return L().some((t=>t.label===e))?new A(e,{skip:!0}):null}static getCurrent(){return E()}static getAll(){return L()}static async getFocusedWindow(){for(const e of L())if(await e.isFocused())return e;return null}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):p(e,t,{target:this.label})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):h(e,t,{target:this.label})}async emit(e,t){if(I.includes(e)){for(const n of this.listeners[e]||[])n({event:e,id:-1,windowLabel:this.label,payload:t});return Promise.resolve()}return d(e,t,{target:this.label})}_handleTauriEvent(e,t){return!!I.includes(e)&&(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0)}async scaleFactor(){return l("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return l("plugin:window|inner_position",{label:this.label}).then((({x:e,y:t})=>new b(e,t)))}async outerPosition(){return l("plugin:window|outer_position",{label:this.label}).then((({x:e,y:t})=>new b(e,t)))}async innerSize(){return l("plugin:window|inner_size",{label:this.label}).then((({width:e,height:t})=>new _(e,t)))}async outerSize(){return l("plugin:window|outer_size",{label:this.label}).then((({width:e,height:t})=>new _(e,t)))}async isFullscreen(){return l("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return l("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return l("plugin:window|is_maximized",{label:this.label})}async isFocused(){return l("plugin:window|is_focused",{label:this.label})}async isDecorated(){return l("plugin:window|is_decorated",{label:this.label})}async isResizable(){return l("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return l("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return l("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return l("plugin:window|is_closable",{label:this.label})}async isVisible(){return l("plugin:window|is_visible",{label:this.label})}async title(){return l("plugin:window|title",{label:this.label})}async theme(){return l("plugin:window|theme",{label:this.label})}async center(){return l("plugin:window|center",{label:this.label})}async requestUserAttention(e){let t=null;return e&&(t=e===f.Critical?{type:"Critical"}:{type:"Informational"}),l("plugin:window|request_user_attention",{label:this.label,value:t})}async setResizable(e){return l("plugin:window|set_resizable",{label:this.label,value:e})}async setMaximizable(e){return l("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return l("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return l("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return l("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return l("plugin:window|maximize",{label:this.label})}async unmaximize(){return l("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return l("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return l("plugin:window|minimize",{label:this.label})}async unminimize(){return l("plugin:window|unminimize",{label:this.label})}async show(){return l("plugin:window|show",{label:this.label})}async hide(){return l("plugin:window|hide",{label:this.label})}async close(){return l("plugin:window|close",{label:this.label})}async setDecorations(e){return l("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return l("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return l("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return l("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return l("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return l("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return l("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return l("plugin:window|set_size",{label:this.label,value:{type:e.type,data:{width:e.width,height:e.height}}})}async setMinSize(e){if(e&&"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return l("plugin:window|set_min_size",{label:this.label,value:e?{type:e.type,data:{width:e.width,height:e.height}}:null})}async setMaxSize(e){if(e&&"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return l("plugin:window|set_max_size",{label:this.label,value:e?{type:e.type,data:{width:e.width,height:e.height}}:null})}async setPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return l("plugin:window|set_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setFullscreen(e){return l("plugin:window|set_fullscreen",{label:this.label,value:e})}async setFocus(){return l("plugin:window|set_focus",{label:this.label})}async setIcon(e){return l("plugin:window|set_icon",{label:this.label,value:"string"==typeof e?e:Array.from(e)})}async setSkipTaskbar(e){return l("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return l("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return l("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return l("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setCursorPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return l("plugin:window|set_cursor_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setIgnoreCursorEvents(e){return l("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return l("plugin:window|start_dragging",{label:this.label})}async setProgressBar(e){return l("plugin:window|set_progress_bar",{label:this.label,value:e})}async onResized(e){return this.listen(o.WINDOW_RESIZED,(t=>{t.payload=N(t.payload),e(t)}))}async onMoved(e){return this.listen(o.WINDOW_MOVED,(t=>{t.payload=W(t.payload),e(t)}))}async onCloseRequested(e){return this.listen(o.WINDOW_CLOSE_REQUESTED,(t=>{const n=new D(t);Promise.resolve(e(n)).then((()=>{if(!n.isPreventDefault())return this.close()}))}))}async onFocusChanged(e){const t=await this.listen(o.WINDOW_FOCUS,(t=>{e({...t,payload:!0})})),n=await this.listen(o.WINDOW_BLUR,(t=>{e({...t,payload:!1})}));return()=>{t(),n()}}async onScaleChanged(e){return this.listen(o.WINDOW_SCALE_FACTOR_CHANGED,e)}async onMenuClicked(e){return this.listen(o.MENU,e)}async onFileDropEvent(e){const t=await this.listen(o.WINDOW_FILE_DROP,(t=>{e({...t,payload:{type:"drop",paths:t.payload}})})),n=await this.listen(o.WINDOW_FILE_DROP_HOVER,(t=>{e({...t,payload:{type:"hover",paths:t.payload}})})),i=await this.listen(o.WINDOW_FILE_DROP_CANCELLED,(t=>{e({...t,payload:{type:"cancel"}})}));return()=>{t(),n(),i()}}async onThemeChanged(e){return this.listen(o.WINDOW_THEME_CHANGED,e)}}var z,C;function P(e){return null===e?null:{name:e.name,scaleFactor:e.scaleFactor,position:W(e.position),size:N(e.size)}}function W(e){return new b(e.x,e.y)}function N(e){return new _(e.width,e.height)}!function(e){e.AppearanceBased="appearanceBased",e.Light="light",e.Dark="dark",e.MediumLight="mediumLight",e.UltraDark="ultraDark",e.Titlebar="titlebar",e.Selection="selection",e.Menu="menu",e.Popover="popover",e.Sidebar="sidebar",e.HeaderView="headerView",e.Sheet="sheet",e.WindowBackground="windowBackground",e.HudWindow="hudWindow",e.FullScreenUI="fullScreenUI",e.Tooltip="tooltip",e.ContentBackground="contentBackground",e.UnderWindowBackground="underWindowBackground",e.UnderPageBackground="underPageBackground",e.Mica="mica",e.Blur="blur",e.Acrylic="acrylic",e.Tabbed="tabbed",e.TabbedDark="tabbedDark",e.TabbedLight="tabbedLight"}(z||(z={})),function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"}(C||(C={}));var T,O=Object.freeze({__proto__:null,CloseRequestedEvent:D,get Effect(){return z},get EffectState(){return C},LogicalPosition:g,LogicalSize:w,PhysicalPosition:b,PhysicalSize:_,get ProgressBarStatus(){return m},get UserAttentionType(){return f},Window:A,availableMonitors:async function(){return l("plugin:window|available_monitors").then((e=>e.map(P)))},currentMonitor:async function(){return l("plugin:window|current_monitor").then(P)},getAll:L,getCurrent:E,primaryMonitor:async function(){return l("plugin:window|primary_monitor").then(P)}});!function(e){e[e.Audio=1]="Audio",e[e.Cache=2]="Cache",e[e.Config=3]="Config",e[e.Data=4]="Data",e[e.LocalData=5]="LocalData",e[e.Document=6]="Document",e[e.Download=7]="Download",e[e.Picture=8]="Picture",e[e.Public=9]="Public",e[e.Video=10]="Video",e[e.Resource=11]="Resource",e[e.Temp=12]="Temp",e[e.AppConfig=13]="AppConfig",e[e.AppData=14]="AppData",e[e.AppLocalData=15]="AppLocalData",e[e.AppCache=16]="AppCache",e[e.AppLog=17]="AppLog",e[e.Desktop=18]="Desktop",e[e.Executable=19]="Executable",e[e.Font=20]="Font",e[e.Home=21]="Home",e[e.Runtime=22]="Runtime",e[e.Template=23]="Template"}(T||(T={}));var R=Object.freeze({__proto__:null,get BaseDirectory(){return T},appCacheDir:async function(){return l("plugin:path|resolve_directory",{directory:T.AppCache})},appConfigDir:async function(){return l("plugin:path|resolve_directory",{directory:T.AppConfig})},appDataDir:async function(){return l("plugin:path|resolve_directory",{directory:T.AppData})},appLocalDataDir:async function(){return l("plugin:path|resolve_directory",{directory:T.AppLocalData})},appLogDir:async function(){return l("plugin:path|resolve_directory",{directory:T.AppLog})},audioDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Audio})},basename:async function(e,t){return l("plugin:path|basename",{path:e,ext:t})},cacheDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Cache})},configDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Config})},dataDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Data})},delimiter:function(){return window.__TAURI_INTERNALS__.plugins.path.delimiter},desktopDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Desktop})},dirname:async function(e){return l("plugin:path|dirname",{path:e})},documentDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Document})},downloadDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Download})},executableDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Executable})},extname:async function(e){return l("plugin:path|extname",{path:e})},fontDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Font})},homeDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Home})},isAbsolute:async function(e){return l("plugin:path|isAbsolute",{path:e})},join:async function(...e){return l("plugin:path|join",{paths:e})},localDataDir:async function(){return l("plugin:path|resolve_directory",{directory:T.LocalData})},normalize:async function(e){return l("plugin:path|normalize",{path:e})},pictureDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Picture})},publicDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Public})},resolve:async function(...e){return l("plugin:path|resolve",{paths:e})},resolveResource:async function(e){return l("plugin:path|resolve_directory",{directory:T.Resource,path:e})},resourceDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Resource})},runtimeDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Runtime})},sep:function(){return window.__TAURI_INTERNALS__.plugins.path.sep},tempDir:async function(e){return l("plugin:path|resolve_directory",{directory:T.Temp})},templateDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Template})},videoDir:async function(){return l("plugin:path|resolve_directory",{directory:T.Video})}});return e.app=u,e.dpi=v,e.event=y,e.path=R,e.primitives=s,e.window=O,e}({});window.__TAURI__=__TAURI_IIFE__;
+var __TAURI_IIFE__=function(e){"use strict";function n(e,n,t,i){if("a"===t&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof n?e!==n||!i:!n.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?i:"a"===t?i.call(e):i?i.value:n.get(e)}function t(e,n,t,i,r){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!r)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof n?e!==n||!r:!n.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?r.call(e,t):r?r.value=t:n.set(e,t),t}var i;function r(e,n=!1){return window.__TAURI_INTERNALS__.transformCallback(e,n)}"function"==typeof SuppressedError&&SuppressedError;class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),this.id=r((e=>{n(this,i,"f").call(this,e)}))}set onmessage(e){t(this,i,e,"f")}get onmessage(){return n(this,i,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}i=new WeakMap;class s{constructor(e,n,t){this.plugin=e,this.event=n,this.channelId=t}async unregister(){return l(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function l(e,n={},t){return window.__TAURI_INTERNALS__.invoke(e,n,t)}var o=Object.freeze({__proto__:null,Channel:a,PluginListener:s,addPluginListener:async function(e,n,t){const i=new a;return i.onmessage=t,l(`plugin:${e}|register_listener`,{event:n,handler:i}).then((()=>new s(e,n,i.id)))},convertFileSrc:function(e,n="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(e,n)},invoke:l,transformCallback:r});var u,c=Object.freeze({__proto__:null,getName:async function(){return l("plugin:app|name")},getTauriVersion:async function(){return l("plugin:app|tauri_version")},getVersion:async function(){return l("plugin:app|version")},hide:async function(){return l("plugin:app|app_hide")},show:async function(){return l("plugin:app|app_show")}});async function d(e,n){await l("plugin:event|unlisten",{event:e,eventId:n})}async function p(e,n,t){return l("plugin:event|listen",{event:e,windowLabel:t?.target,handler:r(n)}).then((n=>async()=>d(e,n)))}async function h(e,n,t){return p(e,(t=>{n(t),d(e,t.id).catch((()=>{}))}),t)}async function y(e,n,t){await l("plugin:event|emit",{event:e,windowLabel:t?.target,payload:n})}!function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_FILE_DROP="tauri://file-drop",e.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",e.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled"}(u||(u={}));var g=Object.freeze({__proto__:null,get TauriEvent(){return u},emit:y,listen:p,once:h});class w{constructor(e,n){this.type="Logical",this.width=e,this.height=n}}class _{constructor(e,n){this.type="Physical",this.width=e,this.height=n}toLogical(e){return new w(this.width/e,this.height/e)}}class m{constructor(e,n){this.type="Logical",this.x=e,this.y=n}}class b{constructor(e,n){this.type="Physical",this.x=e,this.y=n}toLogical(e){return new m(this.x/e,this.y/e)}}var f,v,k=Object.freeze({__proto__:null,LogicalPosition:m,LogicalSize:w,PhysicalPosition:b,PhysicalSize:_});!function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"}(f||(f={}));class D{constructor(e){this._preventDefault=!1,this.event=e.event,this.windowLabel=e.windowLabel,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}}function A(){return new L(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}function E(){return window.__TAURI_INTERNALS__.metadata.windows.map((e=>new L(e.label,{skip:!0})))}!function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"}(v||(v={}));const I=["tauri://created","tauri://error"];class L{constructor(e,n={}){this.label=e,this.listeners=Object.create(null),n?.skip||l("plugin:window|create",{options:{...n,label:e}}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static getByLabel(e){return E().some((n=>n.label===e))?new L(e,{skip:!0}):null}static getCurrent(){return A()}static getAll(){return E()}static async getFocusedWindow(){for(const e of E())if(await e.isFocused())return e;return null}async listen(e,n){return this._handleTauriEvent(e,n)?Promise.resolve((()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)})):p(e,n,{target:this.label})}async once(e,n){return this._handleTauriEvent(e,n)?Promise.resolve((()=>{const t=this.listeners[e];t.splice(t.indexOf(n),1)})):h(e,n,{target:this.label})}async emit(e,n){if(I.includes(e)){for(const t of this.listeners[e]||[])t({event:e,id:-1,windowLabel:this.label,payload:n});return Promise.resolve()}return y(e,n,{target:this.label})}_handleTauriEvent(e,n){return!!I.includes(e)&&(e in this.listeners?this.listeners[e].push(n):this.listeners[e]=[n],!0)}async scaleFactor(){return l("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return l("plugin:window|inner_position",{label:this.label}).then((({x:e,y:n})=>new b(e,n)))}async outerPosition(){return l("plugin:window|outer_position",{label:this.label}).then((({x:e,y:n})=>new b(e,n)))}async innerSize(){return l("plugin:window|inner_size",{label:this.label}).then((({width:e,height:n})=>new _(e,n)))}async outerSize(){return l("plugin:window|outer_size",{label:this.label}).then((({width:e,height:n})=>new _(e,n)))}async isFullscreen(){return l("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return l("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return l("plugin:window|is_maximized",{label:this.label})}async isFocused(){return l("plugin:window|is_focused",{label:this.label})}async isDecorated(){return l("plugin:window|is_decorated",{label:this.label})}async isResizable(){return l("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return l("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return l("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return l("plugin:window|is_closable",{label:this.label})}async isVisible(){return l("plugin:window|is_visible",{label:this.label})}async title(){return l("plugin:window|title",{label:this.label})}async theme(){return l("plugin:window|theme",{label:this.label})}async center(){return l("plugin:window|center",{label:this.label})}async requestUserAttention(e){let n=null;return e&&(n=e===f.Critical?{type:"Critical"}:{type:"Informational"}),l("plugin:window|request_user_attention",{label:this.label,value:n})}async setResizable(e){return l("plugin:window|set_resizable",{label:this.label,value:e})}async setMaximizable(e){return l("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return l("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return l("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return l("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return l("plugin:window|maximize",{label:this.label})}async unmaximize(){return l("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return l("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return l("plugin:window|minimize",{label:this.label})}async unminimize(){return l("plugin:window|unminimize",{label:this.label})}async show(){return l("plugin:window|show",{label:this.label})}async hide(){return l("plugin:window|hide",{label:this.label})}async close(){return l("plugin:window|close",{label:this.label})}async setDecorations(e){return l("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return l("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return l("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return l("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return l("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return l("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return l("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return l("plugin:window|set_size",{label:this.label,value:{type:e.type,data:{width:e.width,height:e.height}}})}async setMinSize(e){if(e&&"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return l("plugin:window|set_min_size",{label:this.label,value:e?{type:e.type,data:{width:e.width,height:e.height}}:null})}async setMaxSize(e){if(e&&"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return l("plugin:window|set_max_size",{label:this.label,value:e?{type:e.type,data:{width:e.width,height:e.height}}:null})}async setPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return l("plugin:window|set_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setFullscreen(e){return l("plugin:window|set_fullscreen",{label:this.label,value:e})}async setFocus(){return l("plugin:window|set_focus",{label:this.label})}async setIcon(e){return l("plugin:window|set_icon",{label:this.label,value:"string"==typeof e?e:Array.from(e)})}async setSkipTaskbar(e){return l("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return l("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return l("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return l("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setCursorPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return l("plugin:window|set_cursor_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setIgnoreCursorEvents(e){return l("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return l("plugin:window|start_dragging",{label:this.label})}async setProgressBar(e){return l("plugin:window|set_progress_bar",{label:this.label,value:e})}async onResized(e){return this.listen(u.WINDOW_RESIZED,(n=>{n.payload=x(n.payload),e(n)}))}async onMoved(e){return this.listen(u.WINDOW_MOVED,(n=>{n.payload=T(n.payload),e(n)}))}async onCloseRequested(e){return this.listen(u.WINDOW_CLOSE_REQUESTED,(n=>{const t=new D(n);Promise.resolve(e(t)).then((()=>{if(!t.isPreventDefault())return this.close()}))}))}async onFocusChanged(e){const n=await this.listen(u.WINDOW_FOCUS,(n=>{e({...n,payload:!0})})),t=await this.listen(u.WINDOW_BLUR,(n=>{e({...n,payload:!1})}));return()=>{n(),t()}}async onScaleChanged(e){return this.listen(u.WINDOW_SCALE_FACTOR_CHANGED,e)}async onFileDropEvent(e){const n=await this.listen(u.WINDOW_FILE_DROP,(n=>{e({...n,payload:{type:"drop",paths:n.payload.paths,position:T(n.payload.position)}})})),t=await this.listen(u.WINDOW_FILE_DROP_HOVER,(n=>{e({...n,payload:{type:"hover",paths:n.payload.paths,position:T(n.payload.position)}})})),i=await this.listen(u.WINDOW_FILE_DROP_CANCELLED,(n=>{e({...n,payload:{type:"cancel"}})}));return()=>{n(),t(),i()}}async onThemeChanged(e){return this.listen(u.WINDOW_THEME_CHANGED,e)}}var S,P;function C(e){return null===e?null:{name:e.name,scaleFactor:e.scaleFactor,position:T(e.position),size:x(e.size)}}function T(e){return new b(e.x,e.y)}function x(e){return new _(e.width,e.height)}!function(e){e.AppearanceBased="appearanceBased",e.Light="light",e.Dark="dark",e.MediumLight="mediumLight",e.UltraDark="ultraDark",e.Titlebar="titlebar",e.Selection="selection",e.Menu="menu",e.Popover="popover",e.Sidebar="sidebar",e.HeaderView="headerView",e.Sheet="sheet",e.WindowBackground="windowBackground",e.HudWindow="hudWindow",e.FullScreenUI="fullScreenUI",e.Tooltip="tooltip",e.ContentBackground="contentBackground",e.UnderWindowBackground="underWindowBackground",e.UnderPageBackground="underPageBackground",e.Mica="mica",e.Blur="blur",e.Acrylic="acrylic",e.Tabbed="tabbed",e.TabbedDark="tabbedDark",e.TabbedLight="tabbedLight"}(S||(S={})),function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"}(P||(P={}));var F,z=Object.freeze({__proto__:null,CloseRequestedEvent:D,get Effect(){return S},get EffectState(){return P},LogicalPosition:m,LogicalSize:w,PhysicalPosition:b,PhysicalSize:_,get ProgressBarStatus(){return v},get UserAttentionType(){return f},Window:L,availableMonitors:async function(){return l("plugin:window|available_monitors").then((e=>e.map(C)))},currentMonitor:async function(){return l("plugin:window|current_monitor").then(C)},getAll:E,getCurrent:A,primaryMonitor:async function(){return l("plugin:window|primary_monitor").then(C)}});!function(e){e[e.Audio=1]="Audio",e[e.Cache=2]="Cache",e[e.Config=3]="Config",e[e.Data=4]="Data",e[e.LocalData=5]="LocalData",e[e.Document=6]="Document",e[e.Download=7]="Download",e[e.Picture=8]="Picture",e[e.Public=9]="Public",e[e.Video=10]="Video",e[e.Resource=11]="Resource",e[e.Temp=12]="Temp",e[e.AppConfig=13]="AppConfig",e[e.AppData=14]="AppData",e[e.AppLocalData=15]="AppLocalData",e[e.AppCache=16]="AppCache",e[e.AppLog=17]="AppLog",e[e.Desktop=18]="Desktop",e[e.Executable=19]="Executable",e[e.Font=20]="Font",e[e.Home=21]="Home",e[e.Runtime=22]="Runtime",e[e.Template=23]="Template"}(F||(F={}));var R,W=Object.freeze({__proto__:null,get BaseDirectory(){return F},appCacheDir:async function(){return l("plugin:path|resolve_directory",{directory:F.AppCache})},appConfigDir:async function(){return l("plugin:path|resolve_directory",{directory:F.AppConfig})},appDataDir:async function(){return l("plugin:path|resolve_directory",{directory:F.AppData})},appLocalDataDir:async function(){return l("plugin:path|resolve_directory",{directory:F.AppLocalData})},appLogDir:async function(){return l("plugin:path|resolve_directory",{directory:F.AppLog})},audioDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Audio})},basename:async function(e,n){return l("plugin:path|basename",{path:e,ext:n})},cacheDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Cache})},configDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Config})},dataDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Data})},delimiter:function(){return window.__TAURI_INTERNALS__.plugins.path.delimiter},desktopDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Desktop})},dirname:async function(e){return l("plugin:path|dirname",{path:e})},documentDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Document})},downloadDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Download})},executableDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Executable})},extname:async function(e){return l("plugin:path|extname",{path:e})},fontDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Font})},homeDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Home})},isAbsolute:async function(e){return l("plugin:path|isAbsolute",{path:e})},join:async function(...e){return l("plugin:path|join",{paths:e})},localDataDir:async function(){return l("plugin:path|resolve_directory",{directory:F.LocalData})},normalize:async function(e){return l("plugin:path|normalize",{path:e})},pictureDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Picture})},publicDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Public})},resolve:async function(...e){return l("plugin:path|resolve",{paths:e})},resolveResource:async function(e){return l("plugin:path|resolve_directory",{directory:F.Resource,path:e})},resourceDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Resource})},runtimeDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Runtime})},sep:function(){return window.__TAURI_INTERNALS__.plugins.path.sep},tempDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Temp})},templateDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Template})},videoDir:async function(){return l("plugin:path|resolve_directory",{directory:F.Video})}});class N{get rid(){return n(this,R,"f")}constructor(e){R.set(this,void 0),t(this,R,e,"f")}async close(){return l("plugin:resources|close",{rid:this.rid})}}R=new WeakMap;class O extends N{constructor(e,n){super(e),this.id=n}static async new(e){e?.menu&&(e.menu=[e.menu.rid,e.menu.kind]),e?.icon&&(e.icon="string"==typeof e.icon?e.icon:Array.from(e.icon));const n=new a;return e?.action&&(n.onmessage=e.action,delete e.action),l("plugin:tray|new",{options:e??{},handler:n}).then((([e,n])=>new O(e,n)))}async setIcon(e){let n=null;return e&&(n="string"==typeof e?e:Array.from(e)),l("plugin:tray|set_icon",{rid:this.rid,icon:n})}async setMenu(e){return e&&(e=[e.rid,e.kind]),l("plugin:tray|set_menu",{rid:this.rid,menu:e})}async setTooltip(e){return l("plugin:tray|set_tooltip",{rid:this.rid,tooltip:e})}async setTitle(e){return l("plugin:tray|set_title",{rid:this.rid,title:e})}async setVisible(e){return l("plugin:tray|set_visible",{rid:this.rid,visible:e})}async setTempDirPath(e){return l("plugin:tray|set_temp_dir_path",{rid:this.rid,path:e})}async setIconAsTemplate(e){return l("plugin:tray|set_icon_as_template",{rid:this.rid,asTemplate:e})}async setMenuOnLeftClick(e){return l("plugin:tray|set_show_menu_on_left_click",{rid:this.rid,onLeft:e})}}var M,U,B,V=Object.freeze({__proto__:null,TrayIcon:O});function H(e){if("items"in e)e.items=e.items?.map((e=>"rid"in e?e:H(e)));else if("action"in e&&e.action){const n=new a;return n.onmessage=e.action,delete e.action,{...e,handler:n}}return e}async function G(e,n){const t=new a;let i=null;return n&&"object"==typeof n&&("action"in n&&n.action&&(t.onmessage=n.action,delete n.action),"items"in n&&n.items&&(i=n.items.map((e=>"rid"in e?[e.rid,e.kind]:H(e))))),l("plugin:menu|new",{kind:e,options:n?{...n,items:i}:void 0,handler:t})}class j extends N{get id(){return n(this,M,"f")}get kind(){return n(this,U,"f")}constructor(e,n,i){super(e),M.set(this,void 0),U.set(this,void 0),t(this,M,n,"f"),t(this,U,i,"f")}}M=new WeakMap,U=new WeakMap;class q extends j{constructor(e,n){super(e,n,"MenuItem")}static async new(e){return G("MenuItem",e).then((([e,n])=>new q(e,n)))}async text(){return l("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return l("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return l("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return l("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return l("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}}class Q extends j{constructor(e,n){super(e,n,"Check")}static async new(e){return G("Check",e).then((([e,n])=>new Q(e,n)))}async text(){return l("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return l("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return l("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return l("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return l("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async isChecked(){return l("plugin:menu|is_checked",{rid:this.rid})}async setChecked(e){return l("plugin:menu|set_checked",{rid:this.rid,checked:e})}}!function(e){e.Add="Add",e.Advanced="Advanced",e.Bluetooth="Bluetooth",e.Bookmarks="Bookmarks",e.Caution="Caution",e.ColorPanel="ColorPanel",e.ColumnView="ColumnView",e.Computer="Computer",e.EnterFullScreen="EnterFullScreen",e.Everyone="Everyone",e.ExitFullScreen="ExitFullScreen",e.FlowView="FlowView",e.Folder="Folder",e.FolderBurnable="FolderBurnable",e.FolderSmart="FolderSmart",e.FollowLinkFreestanding="FollowLinkFreestanding",e.FontPanel="FontPanel",e.GoLeft="GoLeft",e.GoRight="GoRight",e.Home="Home",e.IChatTheater="IChatTheater",e.IconView="IconView",e.Info="Info",e.InvalidDataFreestanding="InvalidDataFreestanding",e.LeftFacingTriangle="LeftFacingTriangle",e.ListView="ListView",e.LockLocked="LockLocked",e.LockUnlocked="LockUnlocked",e.MenuMixedState="MenuMixedState",e.MenuOnState="MenuOnState",e.MobileMe="MobileMe",e.MultipleDocuments="MultipleDocuments",e.Network="Network",e.Path="Path",e.PreferencesGeneral="PreferencesGeneral",e.QuickLook="QuickLook",e.RefreshFreestanding="RefreshFreestanding",e.Refresh="Refresh",e.Remove="Remove",e.RevealFreestanding="RevealFreestanding",e.RightFacingTriangle="RightFacingTriangle",e.Share="Share",e.Slideshow="Slideshow",e.SmartBadge="SmartBadge",e.StatusAvailable="StatusAvailable",e.StatusNone="StatusNone",e.StatusPartiallyAvailable="StatusPartiallyAvailable",e.StatusUnavailable="StatusUnavailable",e.StopProgressFreestanding="StopProgressFreestanding",e.StopProgress="StopProgress",e.TrashEmpty="TrashEmpty",e.TrashFull="TrashFull",e.User="User",e.UserAccounts="UserAccounts",e.UserGroup="UserGroup",e.UserGuest="UserGuest"}(B||(B={}));class $ extends j{constructor(e,n){super(e,n,"Icon")}static async new(e){return G("Icon",e).then((([e,n])=>new $(e,n)))}async text(){return l("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return l("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return l("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return l("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return l("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async setIcon(e){return l("plugin:menu|set_icon",{rid:this.rid,icon:e})}}class Z extends j{constructor(e,n){super(e,n,"Predefined")}static async new(e){return G("Predefined",e).then((([e,n])=>new Z(e,n)))}async text(){return l("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return l("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}}function J([e,n,t]){switch(t){case"Submenu":return new K(e,n);case"Predefined":return new Z(e,n);case"Check":return new Q(e,n);case"Icon":return new $(e,n);default:return new q(e,n)}}class K extends j{constructor(e,n){super(e,n,"Submenu")}static async new(e){return G("Submenu",e).then((([e,n])=>new K(e,n)))}async text(){return l("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return l("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return l("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return l("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async append(e){return l("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async prepend(e){return l("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async insert(e,n){return l("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e)),position:n})}async remove(e){return l("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return l("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(J)}async items(){return l("plugin:menu|items",{rid:this.rid,kind:this.kind}).then((e=>e.map(J)))}async get(e){return l("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then((e=>e?J(e):null))}async popup(e,n){let t=null;return e&&(t={type:e instanceof b?"Physical":"Logical",data:e}),l("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:n?.label??null,at:t})}async setAsWindowsMenuForNSApp(){return l("plugin:menu|set_as_windows_menu_for_nsapp",{rid:this.rid})}async setAsHelpMenuForNSApp(){return l("plugin:menu|set_as_help_menu_for_nsapp",{rid:this.rid})}}function Y([e,n,t]){switch(t){case"Submenu":return new K(e,n);case"Predefined":return new Z(e,n);case"Check":return new Q(e,n);case"Icon":return new $(e,n);default:return new q(e,n)}}class X extends j{constructor(e,n){super(e,n,"Menu")}static async new(e){return G("Menu",e).then((([e,n])=>new X(e,n)))}static async default(){return l("plugin:menu|default").then((([e,n])=>new X(e,n)))}async append(e){return l("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async prepend(e){return l("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async insert(e,n){return l("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e)),position:n})}async remove(e){return l("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return l("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(Y)}async items(){return l("plugin:menu|items",{rid:this.rid,kind:this.kind}).then((e=>e.map(Y)))}async get(e){return l("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then((e=>e?Y(e):null))}async popup(e,n){let t=null;return e&&(t={type:e instanceof b?"Physical":"Logical",data:e}),l("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:n?.label??null,at:t})}async setAsAppMenu(){return l("plugin:menu|set_as_app_menu",{rid:this.rid}).then((e=>e?new X(e[0],e[1]):null))}async setAsWindowMenu(e){return l("plugin:menu|set_as_window_menu",{rid:this.rid,window:e?.label??null}).then((e=>e?new X(e[0],e[1]):null))}}var ee=Object.freeze({__proto__:null,CheckMenuItem:Q,IconMenuItem:$,Menu:X,MenuItem:q,get NativeIcon(){return B},PredefinedMenuItem:Z,Submenu:K});return e.app=c,e.core=o,e.dpi=k,e.event=g,e.menu=ee,e.path=W,e.tray=V,e.window=z,e}({});window.__TAURI__=__TAURI_IIFE__;
diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs
index 389858bff9a8..337c8843ae48 100644
--- a/core/tauri/src/app.rs
+++ b/core/tauri/src/app.rs
@@ -267,7 +267,7 @@ pub struct AppHandle {
impl AppHandle {
/// Create a new tao window using a callback. The event loop must be running at this point.
pub fn create_tao_window<
- F: FnOnce() -> (String, tauri_runtime_wry::WryWindowBuilder) + Send + 'static,
+ F: FnOnce() -> (String, tauri_runtime_wry::TaoWindowBuilder) + Send + 'static,
>(
&self,
f: F,
@@ -772,7 +772,8 @@ macro_rules! shared_app_impl {
/// **You should always exit the tauri app immediately after this function returns and not use any tauri-related APIs.**
pub fn cleanup_before_exit(&self) {
#[cfg(all(desktop, feature = "tray-icon"))]
- self.manager.tray.icons.lock().unwrap().clear()
+ self.manager.tray.icons.lock().unwrap().clear();
+ self.resources_table().clear();
}
}
};
@@ -787,6 +788,11 @@ impl App {
self.handle.plugin(crate::event::plugin::init())?;
self.handle.plugin(crate::window::plugin::init())?;
self.handle.plugin(crate::app::plugin::init())?;
+ self.handle.plugin(crate::resources::plugin::init())?;
+ #[cfg(desktop)]
+ self.handle.plugin(crate::menu::plugin::init())?;
+ #[cfg(all(desktop, feature = "tray-icon"))]
+ self.handle.plugin(crate::tray::plugin::init())?;
Ok(())
}
diff --git a/core/tauri/src/async_runtime.rs b/core/tauri/src/async_runtime.rs
index 01c7083be479..ebc65954befb 100644
--- a/core/tauri/src/async_runtime.rs
+++ b/core/tauri/src/async_runtime.rs
@@ -10,7 +10,6 @@
//! one you need isn't here, you could use types in [`tokio`] directly.
//! For custom command handlers, it's recommended to use a plain `async fn` command.
-use once_cell::sync::OnceCell;
pub use tokio::{
runtime::{Handle as TokioHandle, Runtime as TokioRuntime},
sync::{
@@ -23,10 +22,11 @@ pub use tokio::{
use std::{
future::Future,
pin::Pin,
+ sync::OnceLock,
task::{Context, Poll},
};
-static RUNTIME: OnceCell = OnceCell::new();
+static RUNTIME: OnceLock = OnceLock::new();
struct GlobalRuntime {
runtime: Option,
diff --git a/core/tauri/src/error.rs b/core/tauri/src/error.rs
index c8d8645dd48d..9af98ec35f82 100644
--- a/core/tauri/src/error.rs
+++ b/core/tauri/src/error.rs
@@ -130,6 +130,12 @@ pub enum Error {
/// window not found.
#[error("window not found")]
WindowNotFound,
+ /// The resource id is invalid.
+ #[error("The resource id {0} is invalid.")]
+ BadResourceId(crate::resources::ResourceId),
+ /// The anyhow crate error.
+ #[error(transparent)]
+ Anyhow(#[from] anyhow::Error),
}
/// `Result`
diff --git a/core/tauri/src/ipc/channel.rs b/core/tauri/src/ipc/channel.rs
index 923770711062..d650371961d2 100644
--- a/core/tauri/src/ipc/channel.rs
+++ b/core/tauri/src/ipc/channel.rs
@@ -4,13 +4,14 @@
use std::{
collections::HashMap,
+ str::FromStr,
sync::{
atomic::{AtomicU32, Ordering},
Arc, Mutex,
},
};
-use serde::{Deserialize, Serialize, Serializer};
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{
command,
@@ -50,6 +51,62 @@ impl Serialize for Channel {
}
}
+/// The ID of a channel that was defined on the JavaScript layer.
+///
+/// Useful when expecting [`Channel`] as part of a JSON object instead of a top-level command argument.
+///
+/// # Examples
+///
+/// ```rust
+/// use tauri::{ipc::JavaScriptChannelId, Runtime, Window};
+///
+/// #[derive(serde::Deserialize)]
+/// #[serde(rename_all = "camelCase")]
+/// struct Button {
+/// label: String,
+/// on_click: JavaScriptChannelId,
+/// }
+///
+/// #[tauri::command]
+/// fn add_button(window: Window, button: Button) {
+/// let channel = button.on_click.channel_on(window);
+/// channel.send("clicked").unwrap();
+/// }
+/// ```
+pub struct JavaScriptChannelId(CallbackFn);
+
+impl FromStr for JavaScriptChannelId {
+ type Err = &'static str;
+
+ fn from_str(s: &str) -> Result {
+ s.split_once(IPC_PAYLOAD_PREFIX)
+ .ok_or("invalid channel string")
+ .and_then(|(_prefix, id)| id.parse().map_err(|_| "invalid channel ID"))
+ .map(|id| Self(CallbackFn(id)))
+ }
+}
+
+impl JavaScriptChannelId {
+ /// Gets a [`Channel`] for this channel ID on the given [`Window`].
+ pub fn channel_on(&self, window: Window) -> Channel {
+ Channel::from_callback_fn(window, self.0)
+ }
+}
+
+impl<'de> Deserialize<'de> for JavaScriptChannelId {
+ fn deserialize(deserializer: D) -> std::result::Result
+ where
+ D: Deserializer<'de>,
+ {
+ let value: String = Deserialize::deserialize(deserializer)?;
+ Self::from_str(&value).map_err(|_| {
+ serde::de::Error::custom(format!(
+ "invalid channel value `{value}`, expected a string in the `{IPC_PAYLOAD_PREFIX}ID` format"
+ ))
+ })
+ }
+}
+
impl Channel {
/// Creates a new channel with the given message handler.
pub fn new crate::Result<()> + Send + Sync + 'static>(
@@ -58,7 +115,7 @@ impl Channel {
Self::new_with_id(CHANNEL_COUNTER.fetch_add(1, Ordering::Relaxed), on_message)
}
- pub(crate) fn new_with_id crate::Result<()> + Send + Sync + 'static>(
+ fn new_with_id crate::Result<()> + Send + Sync + 'static>(
id: u32,
on_message: F,
) -> Self {
@@ -74,7 +131,7 @@ impl Channel {
channel
}
- pub(crate) fn from_ipc(window: Window, callback: CallbackFn) -> Self {
+ pub(crate) fn from_callback_fn(window: Window, callback: CallbackFn) -> Self {
Channel::new_with_id(callback.0, move |body| {
let data_id = CHANNEL_DATA_COUNTER.fetch_add(1, Ordering::Relaxed);
window
@@ -90,23 +147,12 @@ impl Channel {
})
}
- pub(crate) fn load_from_ipc(
- window: Window,
- value: impl AsRef,
- ) -> Option {
- value
- .as_ref()
- .split_once(IPC_PAYLOAD_PREFIX)
- .and_then(|(_prefix, id)| id.parse().ok())
- .map(|callback_id| Self::from_ipc(window, CallbackFn(callback_id)))
- }
-
/// The channel identifier.
pub fn id(&self) -> u32 {
self.id
}
- /// Sends the given data through the channel.
+ /// Sends the given data through the channel.
pub fn send(&self, data: T) -> crate::Result<()> {
let body = data.body()?;
(self.on_message)(body)
@@ -121,11 +167,13 @@ impl<'de, R: Runtime> CommandArg<'de, R> for Channel {
let window = command.message.window();
let value: String =
Deserialize::deserialize(command).map_err(|e| crate::Error::InvalidArgs(name, arg, e))?;
- Channel::load_from_ipc(window, &value).ok_or_else(|| {
- InvokeError::from_anyhow(anyhow::anyhow!(
+ JavaScriptChannelId::from_str(&value)
+ .map(|id| id.channel_on(window))
+ .map_err(|_| {
+ InvokeError::from_anyhow(anyhow::anyhow!(
"invalid channel value `{value}`, expected a string in the `{IPC_PAYLOAD_PREFIX}ID` format"
))
- })
+ })
}
}
diff --git a/core/tauri/src/ipc/mod.rs b/core/tauri/src/ipc/mod.rs
index 02bf1f6a83c6..eb37295f959e 100644
--- a/core/tauri/src/ipc/mod.rs
+++ b/core/tauri/src/ipc/mod.rs
@@ -25,7 +25,7 @@ pub(crate) mod channel;
pub(crate) mod format_callback;
pub(crate) mod protocol;
-pub use channel::Channel;
+pub use channel::{Channel, JavaScriptChannelId};
/// A closure that is run every time Tauri receives a message it doesn't explicitly handle.
pub type InvokeHandler = dyn Fn(Invoke) -> bool + Send + Sync + 'static;
diff --git a/core/tauri/src/ipc/protocol.rs b/core/tauri/src/ipc/protocol.rs
index 61bf68ee9b4b..14e5780dec39 100644
--- a/core/tauri/src/ipc/protocol.rs
+++ b/core/tauri/src/ipc/protocol.rs
@@ -245,7 +245,7 @@ fn handle_ipc_message(message: String, manager: &AppManager, labe
if !(cfg!(target_os = "macos") || cfg!(target_os = "ios"))
&& matches!(v, JsonValue::Object(_) | JsonValue::Array(_))
{
- let _ = Channel::from_ipc(window, callback).send(v);
+ let _ = Channel::from_callback_fn(window, callback).send(v);
} else {
responder_eval(
&window,
@@ -262,7 +262,8 @@ fn handle_ipc_message(message: String, manager: &AppManager, labe
error,
);
} else {
- let _ = Channel::from_ipc(window, callback).send(InvokeBody::Raw(v.clone()));
+ let _ =
+ Channel::from_callback_fn(window, callback).send(InvokeBody::Raw(v.clone()));
}
}
InvokeResponse::Err(e) => responder_eval(
diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs
index 61b3338aac1c..59a197ff93dd 100644
--- a/core/tauri/src/lib.rs
+++ b/core/tauri/src/lib.rs
@@ -65,6 +65,7 @@ pub use cocoa;
#[doc(hidden)]
pub use embed_plist;
pub use error::{Error, Result};
+pub use resources::{Resource, ResourceId, ResourceTable};
#[cfg(target_os = "ios")]
#[doc(hidden)]
pub use swift_rs;
@@ -82,6 +83,7 @@ mod manager;
mod pattern;
pub mod plugin;
pub(crate) mod protocol;
+mod resources;
mod vibrancy;
pub mod window;
use tauri_runtime as runtime;
@@ -114,15 +116,30 @@ pub type Wry = tauri_runtime_wry::Wry;
#[macro_export]
macro_rules! android_binding {
($domain:ident, $package:ident, $main: ident, $wry: path) => {
- ::tauri::wry::android_binding!($domain, $package, $main, $wry);
- ::tauri::wry::application::android_fn!(
+ use $wry::{
+ android_setup,
+ prelude::{JClass, JNIEnv, JString},
+ };
+
+ ::tauri::wry::android_binding!($domain, $package, $wry);
+
+ ::tauri::tao::android_binding!(
+ $domain,
+ $package,
+ WryActivity,
+ android_setup,
+ $main,
+ ::tauri::tao
+ );
+
+ ::tauri::tao::platform::android::prelude::android_fn!(
app_tauri,
plugin,
PluginManager,
handlePluginResponse,
[i32, JString, JString],
);
- ::tauri::wry::application::android_fn!(
+ ::tauri::tao::platform::android::prelude::android_fn!(
app_tauri,
plugin,
PluginManager,
@@ -155,15 +172,16 @@ macro_rules! android_binding {
pub use plugin::mobile::{handle_android_plugin_response, send_channel_data};
#[cfg(all(feature = "wry", target_os = "android"))]
#[doc(hidden)]
-pub use tauri_runtime_wry::wry;
+pub use tauri_runtime_wry::{tao, wry};
/// A task to run on the main thread.
pub type SyncTask = Box;
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fmt::{self, Debug},
+ sync::MutexGuard,
};
#[cfg(feature = "wry")]
@@ -808,6 +826,11 @@ pub trait Manager: sealed::ManagerBase {
self.manager().state.try_get()
}
+ /// Get a reference to the resources table.
+ fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
+ self.manager().resources_table()
+ }
+
/// Gets the managed [`Env`].
fn env(&self) -> Env {
self.state::().inner().clone()
@@ -864,10 +887,9 @@ pub mod test;
#[cfg(test)]
mod tests {
use cargo_toml::Manifest;
- use once_cell::sync::OnceCell;
- use std::{env::var, fs::read_to_string, path::PathBuf};
+ use std::{env::var, fs::read_to_string, path::PathBuf, sync::OnceLock};
- static MANIFEST: OnceCell = OnceCell::new();
+ static MANIFEST: OnceLock = OnceLock::new();
const CHECKED_FEATURES: &str = include_str!(concat!(env!("OUT_DIR"), "/checked_features"));
fn get_manifest() -> &'static Manifest {
@@ -903,6 +925,40 @@ mod tests {
}
}
+#[derive(Deserialize)]
+#[serde(untagged)]
+pub(crate) enum IconDto {
+ #[cfg(any(feature = "icon-png", feature = "icon-ico"))]
+ File(std::path::PathBuf),
+ #[cfg(any(feature = "icon-png", feature = "icon-ico"))]
+ Raw(Vec),
+ Rgba {
+ rgba: Vec,
+ width: u32,
+ height: u32,
+ },
+}
+
+impl From for Icon {
+ fn from(icon: IconDto) -> Self {
+ match icon {
+ #[cfg(any(feature = "icon-png", feature = "icon-ico"))]
+ IconDto::File(path) => Self::File(path),
+ #[cfg(any(feature = "icon-png", feature = "icon-ico"))]
+ IconDto::Raw(raw) => Self::Raw(raw),
+ IconDto::Rgba {
+ rgba,
+ width,
+ height,
+ } => Self::Rgba {
+ rgba,
+ width,
+ height,
+ },
+ }
+ }
+}
+
#[allow(unused)]
macro_rules! run_main_thread {
($self:ident, $ex:expr) => {{
diff --git a/core/tauri/src/manager/mod.rs b/core/tauri/src/manager/mod.rs
index 92ed1b072494..a30fee400838 100644
--- a/core/tauri/src/manager/mod.rs
+++ b/core/tauri/src/manager/mod.rs
@@ -6,7 +6,7 @@ use std::{
borrow::Cow,
collections::HashMap,
fmt,
- sync::{Arc, Mutex},
+ sync::{Arc, Mutex, MutexGuard},
};
use serde::Serialize;
@@ -20,7 +20,6 @@ use tauri_utils::{
html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN},
};
-use crate::event::EmitArgs;
use crate::{
app::{AppHandle, GlobalWindowEventListener, OnPageLoad},
event::{assert_event_name_is_valid, Event, EventId, Listeners},
@@ -33,6 +32,7 @@ use crate::{
},
Context, Pattern, Runtime, StateManager, Window,
};
+use crate::{event::EmitArgs, resources::ResourceTable};
#[cfg(desktop)]
mod menu;
@@ -196,6 +196,9 @@ pub struct AppManager {
/// Application pattern.
pub pattern: Arc,
+
+ /// Application Resources Table
+ pub(crate) resources_table: Arc>,
}
impl fmt::Debug for AppManager {
@@ -274,6 +277,7 @@ impl AppManager {
app_icon: context.app_icon,
package_info: context.package_info,
pattern: Arc::new(context.pattern),
+ resources_table: Arc::default(),
}
}
@@ -535,6 +539,14 @@ impl AppManager {
pub fn windows(&self) -> HashMap> {
self.window.windows_lock().clone()
}
+
+ /// Resources table managed by the application.
+ pub(crate) fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
+ self
+ .resources_table
+ .lock()
+ .expect("poisoned window manager")
+ }
}
#[cfg(desktop)]
diff --git a/core/tauri/src/menu/builders/mod.rs b/core/tauri/src/menu/builders/mod.rs
index f86baee6b27b..172efc8c012f 100644
--- a/core/tauri/src/menu/builders/mod.rs
+++ b/core/tauri/src/menu/builders/mod.rs
@@ -6,8 +6,6 @@
//! A module containting menu builder types
-pub use muda::AboutMetadataBuilder;
-
mod menu;
pub use menu::MenuBuilder;
mod normal;
diff --git a/core/tauri/src/menu/check.rs b/core/tauri/src/menu/check.rs
index 28d90990f2e4..97fa639601e3 100644
--- a/core/tauri/src/menu/check.rs
+++ b/core/tauri/src/menu/check.rs
@@ -2,9 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-use crate::{menu::MenuId, run_main_thread, AppHandle, Manager, Runtime};
+use crate::{menu::MenuId, resources::Resource, run_main_thread, AppHandle, Manager, Runtime};
-/// A menu item inside a [`Menu`] or [`Submenu`] and contains only text.
+/// A menu item inside a [`Menu`] or [`Submenu`]
+/// and usually contains a text and a check mark or a similar toggle
+/// that corresponds to a checked and unchecked states.
///
/// [`Menu`]: super::Menu
/// [`Submenu`]: super::Submenu
@@ -31,7 +33,7 @@ unsafe impl Sync for CheckMenuItem {}
unsafe impl Send for CheckMenuItem {}
impl super::sealed::IsMenuItemBase for CheckMenuItem {
- fn inner(&self) -> &dyn muda::IsMenuItem {
+ fn inner_muda(&self) -> &dyn muda::IsMenuItem {
&self.inner
}
}
@@ -146,3 +148,5 @@ impl CheckMenuItem {
run_main_thread!(self, |self_: Self| self_.inner.set_checked(checked))
}
}
+
+impl Resource for CheckMenuItem {}
diff --git a/core/tauri/src/menu/icon.rs b/core/tauri/src/menu/icon.rs
index bdaab0203865..e2dd547e095c 100644
--- a/core/tauri/src/menu/icon.rs
+++ b/core/tauri/src/menu/icon.rs
@@ -3,9 +3,12 @@
// SPDX-License-Identifier: MIT
use super::NativeIcon;
-use crate::{menu::MenuId, run_main_thread, AppHandle, Icon, Manager, Runtime};
+use crate::{
+ menu::MenuId, resources::Resource, run_main_thread, AppHandle, Icon, Manager, Runtime,
+};
-/// A menu item inside a [`Menu`] or [`Submenu`] and contains only text.
+/// A menu item inside a [`Menu`] or [`Submenu`]
+/// and usually contains an icon and a text.
///
/// [`Menu`]: super::Menu
/// [`Submenu`]: super::Submenu
@@ -32,7 +35,7 @@ unsafe impl Sync for IconMenuItem {}
unsafe impl Send for IconMenuItem {}
impl super::sealed::IsMenuItemBase for IconMenuItem {
- fn inner(&self) -> &dyn muda::IsMenuItem {
+ fn inner_muda(&self) -> &dyn muda::IsMenuItem {
&self.inner
}
}
@@ -214,3 +217,5 @@ impl IconMenuItem