diff --git a/crates/tauri-cli/CHANGELOG.md b/crates/tauri-cli/CHANGELOG.md new file mode 100644 index 0000000..2860b0d --- /dev/null +++ b/crates/tauri-cli/CHANGELOG.md @@ -0,0 +1,2483 @@ +# Changelog + +## \[2.11.4] + +### Bug Fixes + +- [`67ffa192a`](https://www.github.com/tauri-apps/tauri/commit/67ffa192a499131e7e750f164fe61a40ca0fb2e7) ([#15596](https://www.github.com/tauri-apps/tauri/pull/15596) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue in the AppImage bundler that caused the `/.desktop` and `.DirIcon` files to be absolute symlinks instead of relative symlinks which caused problems with AppImage installers like `AppManager`. + +### Dependencies + +- Upgraded to `tauri-bundler@2.9.4` + +## \[2.11.3] + +### Bug Fixes + +- [`50b0237ed`](https://www.github.com/tauri-apps/tauri/commit/50b0237edb9ed683979b7954975b98a4d22a9f70) ([#15549](https://www.github.com/tauri-apps/tauri/pull/15549) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Escape special characters in `productName` when generating Android `strings.xml` +- [`728c8d4a5`](https://www.github.com/tauri-apps/tauri/commit/728c8d4a5d9e3badf4683eb2e493d950d27d6b66) ([#15473](https://www.github.com/tauri-apps/tauri/pull/15473) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Skip building bundles when using `tauri android run` +- [`828f710a5`](https://www.github.com/tauri-apps/tauri/commit/828f710a59fe41bd97e781410c48688a12e7e538) ([#15427](https://www.github.com/tauri-apps/tauri/pull/15427) by [@puneetdixit200](https://www.github.com/tauri-apps/tauri/../../puneetdixit200)) Prevent binaries gated by disabled Cargo features from being re-added by the `src/bin` scan. +- [`be0cb0d43`](https://www.github.com/tauri-apps/tauri/commit/be0cb0d4378ddf26bc33066b3750f2639ade15f5) ([#15344](https://www.github.com/tauri-apps/tauri/pull/15344) by [@raglady](https://www.github.com/tauri-apps/tauri/../../raglady)) Fix NDK_HOME environment variable not honored when set +- [`ed8fd411f`](https://www.github.com/tauri-apps/tauri/commit/ed8fd411fe10469da33f63ed5bd9d7ae19e77d84) ([#15552](https://www.github.com/tauri-apps/tauri/pull/15552) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Make `ureq_proto` show trace level logs only on `-vvv` instead of `-vv` +- [`fca4a31f9`](https://www.github.com/tauri-apps/tauri/commit/fca4a31f94f8ba709d1b28e073e69867b8704e6e) ([#15454](https://www.github.com/tauri-apps/tauri/pull/15454) by [@fallintoplace](https://www.github.com/tauri-apps/tauri/../../fallintoplace)) Fix `tauri migrate` generating invalid namespace imports for aliased pluginified imports from `@tauri-apps/api`. + + Inputs like `import { cli as superCli } from "@tauri-apps/api"` now migrate to `import * as superCli from "@tauri-apps/plugin-cli"` instead of producing invalid ESM syntax. The migration tests also reparse migrated JS, Svelte, and Vue output so syntax regressions are caught directly. + +### Dependencies + +- Upgraded to `tauri-utils@2.9.3` +- Upgraded to `tauri-bundler@2.9.3` + +## \[2.11.2] + +### Dependencies + +- Upgraded to `tauri-utils@2.9.2` +- Upgraded to `tauri-bundler@2.9.2` + +## \[2.11.1] + +### Dependencies + +- Upgraded to `tauri-utils@2.9.1` +- Upgraded to `tauri-bundler@2.9.1` + +## \[2.11.0] + +### New Features + +- [`926a57bb0`](https://www.github.com/tauri-apps/tauri/commit/926a57bb0851e45d47ad1ee68fc96a9c25754c7c) ([#15201](https://www.github.com/tauri-apps/tauri/pull/15201)) Added uninstaller icon and uninstaller header image support for NSIS installer. + + Notes: + + - For `tauri-bundler` lib users, the `NsisSettings` now has 2 new fields `uninstaller_icon` and `uninstaller_header_image` which can be a breaking change + - When bundling with NSIS, users can add `uninstallerIcon` and `uninstallerHeaderImage` under `bundle > windows > nsis` to configure them. +- [`cc5c97602`](https://www.github.com/tauri-apps/tauri/commit/cc5c976027b0ab2431c13ec5b2e201d4414a8a6e) ([#14486](https://www.github.com/tauri-apps/tauri/pull/14486)) Implement file association for Android and iOS. +- [`4ef5797f0`](https://www.github.com/tauri-apps/tauri/commit/4ef5797f0fb27fa2df3f39f4a54e48ef319560ec) ([#15061](https://www.github.com/tauri-apps/tauri/pull/15061)) Add `--no-sign` and `--archive-only` flags to `tauri ios build`. +- [`764b9139a`](https://www.github.com/tauri-apps/tauri/commit/764b9139a32de149d8a914a6b5ec6cd1937c64eb) ([#14313](https://www.github.com/tauri-apps/tauri/pull/14313)) Prompt to restart the Android emulator if it is not connected to adb. +- [`5dc2cee60`](https://www.github.com/tauri-apps/tauri/commit/5dc2cee60370665af88c185684432e425b1c987d) ([#14793](https://www.github.com/tauri-apps/tauri/pull/14793)) Added support for `minimumWebview2Version` option support for the MSI (Wix) installer, the old `bundle > windows > nsis > minimumWebview2Version` is now deprecated in favor of `bundle > windows > minimumWebview2Version` + + Notes: + + - For anyone relying on the `WVRTINSTALLED` `Property` tag in `main.wxs`, it is now renamed to `INSTALLED_WEBVIEW2_VERSION` + - For `tauri-bundler` lib users, the `WindowsSettings` now has a new field `minimum_webview2_version` which can be a breaking change + +### Enhancements + +- [`be0e4bd2d`](https://www.github.com/tauri-apps/tauri/commit/be0e4bd2da02eb6cc75a8dc7c81663277e64c590) ([#15218](https://www.github.com/tauri-apps/tauri/pull/15218)) Added Vietnamese translations for the NSIS installer +- [`8718d0816`](https://www.github.com/tauri-apps/tauri/commit/8718d08163f074dfc53387ebd1d823f9c28280ee) ([#15033](https://www.github.com/tauri-apps/tauri/pull/15033)) Show the context before prompting for updater signing key password + +### Bug Fixes + +- [`fcb702ec4`](https://www.github.com/tauri-apps/tauri/commit/fcb702ec4d924e81943efaeebea8d3edb7289c33) ([#14954](https://www.github.com/tauri-apps/tauri/pull/14954)) Fix `build --bundles` to allow `nsis` arg in linux+macOS +- [`80c1425af`](https://www.github.com/tauri-apps/tauri/commit/80c1425af86058b1fc9489a30f778b6288d79b6b) ([#14921](https://www.github.com/tauri-apps/tauri/pull/14921)) Fix iOS build failure when `Metal Toolchain` is installed by using explicit `$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain` path instead of `$(TOOLCHAIN_DIR)` for Swift library search paths. + +### What's Changed + +- [`9979cde1c`](https://www.github.com/tauri-apps/tauri/commit/9979cde1c5534dafb1a07cc4dc2bc280d15d2f66) ([#15175](https://www.github.com/tauri-apps/tauri/pull/15175)) Update NSIS installer Italian translations + +### Dependencies + +- Upgraded to `tauri-macos-sign@2.3.4` +- Upgraded to `tauri-bundler@2.9.0` +- Upgraded to `tauri-utils@2.9.0` + +## \[2.10.1] + +### Bug Fixes + +- [`35c35f27a`](https://www.github.com/tauri-apps/tauri/commit/35c35f27aedc430b602ec74059b271128c15ad36) ([#14931](https://www.github.com/tauri-apps/tauri/pull/14931) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Support comma-separated list of Cargo features on all commands. +- [`0d1cb83ba`](https://www.github.com/tauri-apps/tauri/commit/0d1cb83bab2aa482c7d73116893fd7ff6aa56283) ([#14932](https://www.github.com/tauri-apps/tauri/pull/14932) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix missing Cargo args when running mobile dev and build commands. +- [`33754ae5e`](https://www.github.com/tauri-apps/tauri/commit/33754ae5e3740d022483b6164511c5c001a3c24b) ([#15022](https://www.github.com/tauri-apps/tauri/pull/15022) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix updater signing private keys generated using `tauri signer generate` with empty password can't be used (The keys generated during tauri were broken between v2.9.3 and v2.10.0, you'll need to regenerate them) + +### What's Changed + +- [`7be58a1c6`](https://www.github.com/tauri-apps/tauri/commit/7be58a1c643a7ed6d0f484a7e1134022618eb2b2) ([#14894](https://www.github.com/tauri-apps/tauri/pull/14894) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Log patching bundle type information again + +### Dependencies + +- Upgraded to `tauri-utils@2.8.3` +- Upgraded to `tauri-bundler@2.8.1` + +## \[2.10.0] + +### Enhancements + +- [`f82594410`](https://www.github.com/tauri-apps/tauri/commit/f82594410cd57d6f794f58d4afea0ed335aa796f) ([#13253](https://www.github.com/tauri-apps/tauri/pull/13253) by [@Armaldio](https://www.github.com/tauri-apps/tauri/../../Armaldio)) Allow electron to run the CLI directly +- [`2d28e3143`](https://www.github.com/tauri-apps/tauri/commit/2d28e3143ee3d97d7570ea03877aa00a0d6e47d0) ([#14632](https://www.github.com/tauri-apps/tauri/pull/14632) by [@sftse](https://www.github.com/tauri-apps/tauri/../../sftse)) Small code refactors for improved code readability. No user facing changes. +- [`a2abe2e6b`](https://www.github.com/tauri-apps/tauri/commit/a2abe2e6bcb9e1eed8484240dfdb76a5bc28ae58) ([#14607](https://www.github.com/tauri-apps/tauri/pull/14607) by [@sftse](https://www.github.com/tauri-apps/tauri/../../sftse)) Simplified internal representation of `features: Option>` with `Vec`, no user facing changes +- [`84b04c4a8`](https://www.github.com/tauri-apps/tauri/commit/84b04c4a8d3310b7a7091d10e36244bf94996e51) ([#14759](https://www.github.com/tauri-apps/tauri/pull/14759) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Added new environment variables for `tauri signer sign` command, to align with existing environment variables used in `tauri build`, `tauri bundle` and `tauri signer generate` + + - `TAURI_SIGNING_PRIVATE_KEY` + - `TAURI_SIGNING_PRIVATE_KEY_PATH` + - `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` + + The old environment variables are deprecated and will be removed in a future release. + + - `TAURI_PRIVATE_KEY` + - `TAURI_PRIVATE_KEY_PATH` + - `TAURI_PRIVATE_KEY_PASSWORD` + +### Bug Fixes + +- [`62aa13a12`](https://www.github.com/tauri-apps/tauri/commit/62aa13a124ef46bb5ce9887a2a574dd35ef86d4f) ([#14629](https://www.github.com/tauri-apps/tauri/pull/14629) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix `android build`'s `--aab` and `--apk` flags requiring a value to be provided. +- [`eccff9758`](https://www.github.com/tauri-apps/tauri/commit/eccff97588232055bd0cafd83e6ee03d11a501fb) ([#14779](https://www.github.com/tauri-apps/tauri/pull/14779) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix empty associated-domains entitlements when domains are not configured for deep links. +- [`ea31b07f1`](https://www.github.com/tauri-apps/tauri/commit/ea31b07f19e0aa467ed0f921f60575cfe09809c8) ([#14789](https://www.github.com/tauri-apps/tauri/pull/14789) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fixed the command description for `tauri inspect` +- [`7fca58230`](https://www.github.com/tauri-apps/tauri/commit/7fca58230f97c3e6834134419514a0c7dbbe784b) ([#14830](https://www.github.com/tauri-apps/tauri/pull/14830) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Updated `nsis_tauri_utils` to 0.5.3: + + - Use an alternative method `CreateProcessWithTokenW` to run programs as user, this fixed a problem that the program launched with the previous method can't query its own handle +- [`53611c4d7`](https://www.github.com/tauri-apps/tauri/commit/53611c4d7bdaf89b9a5d7c46a9c4bf4e34216148) ([#14747](https://www.github.com/tauri-apps/tauri/pull/14747) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Only watch dependent workspace members when running `tauri dev` instead of watching on all members +- [`1b0e335d3`](https://www.github.com/tauri-apps/tauri/commit/1b0e335d3f3445948d6590f7e074275d97cd9859) ([#14713](https://www.github.com/tauri-apps/tauri/pull/14713) by [@wasuaje](https://www.github.com/tauri-apps/tauri/../../wasuaje)) `tauri signer sign` doesn't work for files without an extension + +### What's Changed + +- [`e3fdcb500`](https://www.github.com/tauri-apps/tauri/commit/e3fdcb5002b362b46cde2a1971e4e7f2a1161208) ([#14836](https://www.github.com/tauri-apps/tauri/pull/14836) by [@sftse](https://www.github.com/tauri-apps/tauri/../../sftse)) Continued refactors of tauri-cli, fix too weak atomics. +- [`0575dd287`](https://www.github.com/tauri-apps/tauri/commit/0575dd287e021b61d2aedf64d62ae84a2c925fb4) ([#14521](https://www.github.com/tauri-apps/tauri/pull/14521) by [@kandrelczyk](https://www.github.com/tauri-apps/tauri/../../kandrelczyk)) Change the way bundle type information is added to binary files. Instead of looking up the value of a variable we simply look for the default value. +- [`7f7d9aac2`](https://www.github.com/tauri-apps/tauri/commit/7f7d9aac214e22d9492490543f7a9bcae0a6659e) ([#14668](https://www.github.com/tauri-apps/tauri/pull/14668) by [@sftse](https://www.github.com/tauri-apps/tauri/../../sftse)) Refactored internal use of static on config and directory resolvings, no user facing changes, please report any regressions if you encounter any + +### Dependencies + +- Upgraded to `tauri-utils@2.8.2` +- Upgraded to `tauri-macos-sign@2.3.3` +- Upgraded to `tauri-bundler@2.8.0` + +## \[2.9.6] + +### What's Changed + +- [`7b1b3514d`](https://www.github.com/tauri-apps/tauri/commit/7b1b3514df771e6e9859b9f54fa4df332433948e) ([#14621](https://www.github.com/tauri-apps/tauri/pull/14621) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Errors like `Error Failed to parse version 2 for for NPM package tauri` when there was no `package-lock.json` file present yet or when using ones like `link:./tauri` are now only logged in `--verbose` mode. + +### Dependencies + +- Upgraded to `tauri-macos-sign@2.3.2` +- Upgraded to `tauri-bundler@2.7.5` + +## \[2.9.5] + +### Bug Fixes + +- [`f022b2d1a`](https://www.github.com/tauri-apps/tauri/commit/f022b2d1ae57612e39c75782926f2f341d9034a8) ([#14582](https://www.github.com/tauri-apps/tauri/pull/14582) by [@hrzlgnm](https://www.github.com/tauri-apps/tauri/../../hrzlgnm)) Fixed an issue that caused the cli to error out with missing private key, in case the option `--no-sign` was requested and the `tauri.config` has signing key set and the plugin `tauri-plugin-updater` is used. +- [`f855caf8a`](https://www.github.com/tauri-apps/tauri/commit/f855caf8a3830aa5dd6d0b039312866a5d9c3606) ([#14481](https://www.github.com/tauri-apps/tauri/pull/14481) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fixed the mismatched tauri package versions check didn't work for pnpm +- [`79a7d9ec0`](https://www.github.com/tauri-apps/tauri/commit/79a7d9ec01be1a371b8e923848140fea75e9caed) ([#14468](https://www.github.com/tauri-apps/tauri/pull/14468) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the cli to print errors like `Error Failed to parse version 2 for crate tauri` when there was no `Cargo.lock` file present yet. This will still be logged in `--verbose` mode. + +### Performance Improvements + +- [`ce98d87ce`](https://www.github.com/tauri-apps/tauri/commit/ce98d87ce0aaa907285852eb80691197424e03c3) ([#14474](https://www.github.com/tauri-apps/tauri/pull/14474) by [@Tunglies](https://www.github.com/tauri-apps/tauri/../../Tunglies)) refactor: remove needless collect. No user facing changes. +- [`ee3cc4a91`](https://www.github.com/tauri-apps/tauri/commit/ee3cc4a91bf1315ecaefe90f423ffd55ef6c40db) ([#14475](https://www.github.com/tauri-apps/tauri/pull/14475) by [@Tunglies](https://www.github.com/tauri-apps/tauri/../../Tunglies)) perf: remove needless clones in various files for improved performance. No user facing changes. + +### Dependencies + +- Upgraded to `tauri-bundler@2.7.4` +- Upgraded to `tauri-macos-sign@2.3.1` +- Upgraded to `tauri-utils@2.8.1` + +## \[2.9.4] + +### Bug Fixes + +- [`b586ecf1f`](https://www.github.com/tauri-apps/tauri/commit/b586ecf1f4b3b087f9aa6c4668c2c18b1b7925f4) ([#14416](https://www.github.com/tauri-apps/tauri/pull/14416) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Premultiply Alpha before Resizing which gets rid of the gray fringe around the icons for svg images. + +## \[2.9.3] + +### Enhancements + +- [`22edc65aa`](https://www.github.com/tauri-apps/tauri/commit/22edc65aad0b3e45515008e8e0866112da70c8a1) ([#14408](https://www.github.com/tauri-apps/tauri/pull/14408) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Set user-agent in bundler and cli http requests when fetching build tools. +- [`779612ac8`](https://www.github.com/tauri-apps/tauri/commit/779612ac8425a787626da4cefdb9eaf7d63bea18) ([#14379](https://www.github.com/tauri-apps/tauri/pull/14379) by [@moubctez](https://www.github.com/tauri-apps/tauri/../../moubctez)) Properly read the `required-features` field of binaries in Cargo.toml to prevent bundling issues when the features weren't enabled. + +### Bug Fixes + +- [`fd8c30b4f`](https://www.github.com/tauri-apps/tauri/commit/fd8c30b4f1bca8dd7165c5c0ebe7fbfd17662153) ([#14353](https://www.github.com/tauri-apps/tauri/pull/14353) by [@ChaseKnowlden](https://www.github.com/tauri-apps/tauri/../../ChaseKnowlden)) Premultiply Alpha before Resizing which gets rid of the gray fringe around the icons. + +### Dependencies + +- Upgraded to `tauri-bundler@2.7.3` + +## \[2.9.2] + +### Dependencies + +- Upgraded to `tauri-bundler@2.7.2` + +## \[2.9.1] + +### Dependencies + +- Upgraded to `tauri-macos-sign@2.3.0` +- Upgraded to `tauri-bundler@2.7.1` + +## \[2.9.0] + +### New Features + +- [`f5851ee00`](https://www.github.com/tauri-apps/tauri/commit/f5851ee00d6d1f4d560a220ca5a728fedd525092) ([#14089](https://www.github.com/tauri-apps/tauri/pull/14089)) Adds the `scrollBarStyle` option to the window configuration. +- [`2a06d1006`](https://www.github.com/tauri-apps/tauri/commit/2a06d10066a806e392efe8bfb16d943ee0b0b61d) ([#14052](https://www.github.com/tauri-apps/tauri/pull/14052)) Add a `--no-sign` flag to the `tauri build` and `tauri bundle` commands to skip the code signing step, improving the developer experience for local testing and development without requiring code signing keys. +- [`3b4fac201`](https://www.github.com/tauri-apps/tauri/commit/3b4fac2017832d426dd07c5e24e26684eda57f7b) ([#14194](https://www.github.com/tauri-apps/tauri/pull/14194)) Add `tauri.conf.json > bundle > android > autoIncrementVersionCode` config option to automatically increment the Android version code. +- [`673867aa0`](https://www.github.com/tauri-apps/tauri/commit/673867aa0e1ccd766ee879ffe96aba58c758613c) ([#14094](https://www.github.com/tauri-apps/tauri/pull/14094)) Try to detect ANDROID_HOME and NDK_HOME environment variables from default system locations and install them if needed using the Android Studio command line tools. +- [`3d6868d09`](https://www.github.com/tauri-apps/tauri/commit/3d6868d09c323d68a152f3c3f8c7256311bd020a) ([#14128](https://www.github.com/tauri-apps/tauri/pull/14128)) Added support to defining the content type of the declared file association on macOS (maps to LSItemContentTypes property). +- [`3d6868d09`](https://www.github.com/tauri-apps/tauri/commit/3d6868d09c323d68a152f3c3f8c7256311bd020a) ([#14128](https://www.github.com/tauri-apps/tauri/pull/14128)) Added support to defining the metadata for custom types declared in `tauri.conf.json > bundle > fileAssociations > exportedType` via the `UTExportedTypeDeclarations` Info.plist property. +- [`ed7c9a410`](https://www.github.com/tauri-apps/tauri/commit/ed7c9a4100e08c002212265549d12130d021ad1e) ([#14108](https://www.github.com/tauri-apps/tauri/pull/14108)) Added `bundle > macOS > infoPlist` and `bundle > iOS > infoPlist` configurations to allow defining custom Info.plist extensions. +- [`75082cc5b`](https://www.github.com/tauri-apps/tauri/commit/75082cc5b340e30e2c4b4cd4bd6a1fe5382164aa) ([#14120](https://www.github.com/tauri-apps/tauri/pull/14120)) Added `ios run` and `android run` commands to run the app in production mode. +- [`cc8c0b531`](https://www.github.com/tauri-apps/tauri/commit/cc8c0b53171173dbd1d01781a50de1a3ea159031) ([#14031](https://www.github.com/tauri-apps/tauri/pull/14031)) Added support to universal app links on macOS with the `plugins > deep-link > desktop > domains` configuration. + +### Enhancements + +- [`94cbd40fc`](https://www.github.com/tauri-apps/tauri/commit/94cbd40fc733e08c0bccd48149d22a0e9c2f1e5c) ([#14223](https://www.github.com/tauri-apps/tauri/pull/14223)) Add support for Android's adaptive and themed icons. +- [`b5aa01870`](https://www.github.com/tauri-apps/tauri/commit/b5aa018702bf45dc98297698f9b7d238705865a6) ([#14268](https://www.github.com/tauri-apps/tauri/pull/14268)) Update cargo-mobile2 to 0.21, enhancing error messages and opening Xcode when multiple apps are installed. +- [`55453e845`](https://www.github.com/tauri-apps/tauri/commit/55453e8453d927b8197f1ba9f26fd944482938f7) ([#14262](https://www.github.com/tauri-apps/tauri/pull/14262)) Check mismatched versions in `tauri info` +- [`1a6627ee7`](https://www.github.com/tauri-apps/tauri/commit/1a6627ee7d085a4e66784e2705254714d68c7244) ([#14122](https://www.github.com/tauri-apps/tauri/pull/14122)) Set a default log level filter when running `tauri add log`. +- [`b06b3bd09`](https://www.github.com/tauri-apps/tauri/commit/b06b3bd091b0fed26cdcfb23cacb0462a7a9cc2d) ([#14126](https://www.github.com/tauri-apps/tauri/pull/14126)) Improve error messages with more context. +- [`f6622a3e3`](https://www.github.com/tauri-apps/tauri/commit/f6622a3e342f5dd5fb3cf6e0f79fb309a10e9b3d) ([#14129](https://www.github.com/tauri-apps/tauri/pull/14129)) Prompt to install the iOS platform if it isn't installed yet. +- [`6bbb530fd`](https://www.github.com/tauri-apps/tauri/commit/6bbb530fd5edfc07b180a4f3782b8566872ca3b1) ([#14105](https://www.github.com/tauri-apps/tauri/pull/14105)) Warn if productName is empty when initializing mobile project. + +### Bug Fixes + +- [`19fb6f7cb`](https://www.github.com/tauri-apps/tauri/commit/19fb6f7cb0d702cb2f25f6f2d1e11014d9dada5d) ([#14146](https://www.github.com/tauri-apps/tauri/pull/14146)) Strip Windows-only extensions from the binary path so an Android project initialized on Windows can be used on UNIX systems. +- [`19fb6f7cb`](https://www.github.com/tauri-apps/tauri/commit/19fb6f7cb0d702cb2f25f6f2d1e11014d9dada5d) ([#14146](https://www.github.com/tauri-apps/tauri/pull/14146)) Enhance Android build script usage on Windows by attempting to run cmd, bat and exe formats. +- [`28a2f9bc5`](https://www.github.com/tauri-apps/tauri/commit/28a2f9bc55f658eb71ef1a970ff9f791346f7682) ([#14101](https://www.github.com/tauri-apps/tauri/pull/14101)) Fix iOS CLI usage after modifying the package name. +- [`d2938486e`](https://www.github.com/tauri-apps/tauri/commit/d2938486e9d974debd90c15d7160b8a17bf4d763) ([#14261](https://www.github.com/tauri-apps/tauri/pull/14261)) Replaced the non-standard nerd font character with ` ⱼₛ ` in `tarui info` +- [`25e920e16`](https://www.github.com/tauri-apps/tauri/commit/25e920e169db900ca4f07c2bb9eb290e9f9f2c7d) ([#14298](https://www.github.com/tauri-apps/tauri/pull/14298)) Wait for dev server to exit before exiting the CLI when the app is closed on `tauri dev --no-watch`. +- [`b0012424c`](https://www.github.com/tauri-apps/tauri/commit/b0012424c5f432debfa42ba145e2672966d5f6d5) ([#14115](https://www.github.com/tauri-apps/tauri/pull/14115)) Resolve local IP address when `tauri.conf.json > build > devUrl` host is `0.0.0.0`. +- [`abf7e8850`](https://www.github.com/tauri-apps/tauri/commit/abf7e8850ba41e7173e9e9a3fdd6dfb8f357d72d) ([#14118](https://www.github.com/tauri-apps/tauri/pull/14118)) Fixes mobile project initialization when using `pnpx` or `pnpm dlx`. + +### Dependencies + +- Upgraded to `tauri-utils@2.8.0` +- Upgraded to `tauri-bundler@2.7.0` + +## \[2.8.4] + +### Enhancements + +- [`f70b28529`](https://www.github.com/tauri-apps/tauri/commit/f70b28529d226a2dec2f41709d8934f8f5adab25) ([#14093](https://www.github.com/tauri-apps/tauri/pull/14093) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Ensure Rust targets for mobile are installed when running the dev and build commands (previously only checked on init). +- [`a9b342125`](https://www.github.com/tauri-apps/tauri/commit/a9b342125d5ac1bc9a4b2e8b5f73e8ca3cbcb8b2) ([#14114](https://www.github.com/tauri-apps/tauri/pull/14114) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix iOS dev and build targeting the simulator on Intel machines. +- [`61b9b681e`](https://www.github.com/tauri-apps/tauri/commit/61b9b681e88067a53b79d2318ae005dc25addcd6) ([#14111](https://www.github.com/tauri-apps/tauri/pull/14111) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Retain `RUST_*` environment variables when running the mobile commands. +- [`c23bec62d`](https://www.github.com/tauri-apps/tauri/commit/c23bec62d6d5724798869681aa1534423aae28e2) ([#14083](https://www.github.com/tauri-apps/tauri/pull/14083) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Tauri now ignores `macOS.minimumSystemVersion` in `tauri dev` to prevent forced rebuilds of macOS specific dependencies when using something like `rust-analyzer` at the same time as `tauri dev`. + +### Bug Fixes + +- [`c37a29833`](https://www.github.com/tauri-apps/tauri/commit/c37a298331d6d744b15d32d55a2db83c884a3d6a) ([#14112](https://www.github.com/tauri-apps/tauri/pull/14112) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix usage with Deno failing with `ReferenceError: require is not defined`. +- [`bcf000c0a`](https://www.github.com/tauri-apps/tauri/commit/bcf000c0a8607eedf488fb949b982f519abda43d) ([#14110](https://www.github.com/tauri-apps/tauri/pull/14110) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes running `ios` commands with `deno` crashing due to incorrect current working directory resolution. +- [`7db7142f9`](https://www.github.com/tauri-apps/tauri/commit/7db7142f9ff7dc2f5719602e199b77129ceb19d3) ([#14119](https://www.github.com/tauri-apps/tauri/pull/14119) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes empty device name when using an Android emulator causing the emulator to never be detected as running. +- [`956b4fd6f`](https://www.github.com/tauri-apps/tauri/commit/956b4fd6ffbb4312123b107ca96c87a001359b9d) ([#14106](https://www.github.com/tauri-apps/tauri/pull/14106) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Use the correct export method on Xcode < 15.4. + +## \[2.8.3] + +### Bug Fixes + +- [`0ac89d3b6`](https://www.github.com/tauri-apps/tauri/commit/0ac89d3b6c8c4a4826a4c42726e4f4a8941b3fde) ([#14078](https://www.github.com/tauri-apps/tauri/pull/14078) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Updated `cargo-mobile2` to allow running on iOS simulators that have a higher version than the XCode SDK. This fixes compatiblity issues with Apple's recent "iOS 18.5 + iOS 18.6 Simulator" platform support component. + +## \[2.8.1] + +### Dependencies + +- Upgraded to `tauri-bundler@2.6.1` + +## \[2.8.0] + +### New Features + +- [`91508c0b8`](https://www.github.com/tauri-apps/tauri/commit/91508c0b8d16ec61c7706e93b711c5a85aaffb4a) ([#13881](https://www.github.com/tauri-apps/tauri/pull/13881) by [@pepperoni505](https://www.github.com/tauri-apps/tauri/../../pepperoni505)) Introduces a new configuration option that allows you to specify custom folders to watch for changes when running `tauri dev`. +- [`bc4afe7dd`](https://www.github.com/tauri-apps/tauri/commit/bc4afe7dd4780f02c2d4b1f07d97185fbc5d2bba) ([#13993](https://www.github.com/tauri-apps/tauri/pull/13993) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Check installed plugin NPM/crate versions for incompatible releases. +- [`a9ec12843`](https://www.github.com/tauri-apps/tauri/commit/a9ec12843aa7d0eb774bd3a53e2e63da12cfa77b) ([#13521](https://www.github.com/tauri-apps/tauri/pull/13521) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Added a `--skip-stapling` option to make `tauri build|bundle` *not* wait for notarization to finish on macOS. +- [`0c402bfb6`](https://www.github.com/tauri-apps/tauri/commit/0c402bfb6bd0bec24d928fcabe2ffef1f5cff19a) ([#13997](https://www.github.com/tauri-apps/tauri/pull/13997) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Increase default iOS deployment target iOS to 14.0. +- [`d6d5f3707`](https://www.github.com/tauri-apps/tauri/commit/d6d5f3707768a094ff7e961ae75ba0398d772655) ([#13358](https://www.github.com/tauri-apps/tauri/pull/13358) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Added `--root-certificate-path` option to `android dev` and `ios dev` to be able to connect to HTTPS dev servers. + +### Enhancements + +- [`8b465a12b`](https://www.github.com/tauri-apps/tauri/commit/8b465a12ba73e94d7a3995defd9cc362d15eeebe) ([#13913](https://www.github.com/tauri-apps/tauri/pull/13913) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The bundler now pulls the latest AppImage linuxdeploy plugin instead of using the built-in one. This should remove the libfuse requirement. +- [`390cb9c36`](https://www.github.com/tauri-apps/tauri/commit/390cb9c36a4e2416891b64514e7ad5fc0a85ccf2) ([#13953](https://www.github.com/tauri-apps/tauri/pull/13953) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Reduced the log level of the binary patcher crate `goblin` to only show its debug logs in `-vv` and above. +- [`4475e93e1`](https://www.github.com/tauri-apps/tauri/commit/4475e93e136e9e2bd5f3c7817fa2040924f630f6) ([#13824](https://www.github.com/tauri-apps/tauri/pull/13824) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The bundler and cli will now read TLS Certificates installed on the system when downloading tools and checking versions. + +### Bug Fixes + +- [`f0dcf9637`](https://www.github.com/tauri-apps/tauri/commit/f0dcf9637cc0d42eda05fed7dd6c5ff98bbf19ae) ([#13980](https://www.github.com/tauri-apps/tauri/pull/13980) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix the generated plugin init code of `tauri add` for `tauri-plugin-autostart` and `tauri-plugin-single-instance` +- [`4d270a96a`](https://www.github.com/tauri-apps/tauri/commit/4d270a96a891ae83f7df751abcbe12b7072212d5) ([#13943](https://www.github.com/tauri-apps/tauri/pull/13943) by [@acx0](https://www.github.com/tauri-apps/tauri/../../acx0)) Fix codesigning verification failures caused by binary-patching during bundling +- [`b21d86a8a`](https://www.github.com/tauri-apps/tauri/commit/b21d86a8a3ef29f16628b7d4de17ce1214e9bf49) ([#13981](https://www.github.com/tauri-apps/tauri/pull/13981) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix `tauri permission add` could add duplicated permissions to the capability files +- [`9c938be45`](https://www.github.com/tauri-apps/tauri/commit/9c938be4520fce9204361f3b59439844bc5c91e8) ([#13912](https://www.github.com/tauri-apps/tauri/pull/13912) by [@takecchi](https://www.github.com/tauri-apps/tauri/../../takecchi)) Properly migrate svelte to v5 in the plugin example template + +### Dependencies + +- Upgraded to `tauri-utils@2.7.0` +- Upgraded to `tauri-bundler@2.6.0` +- Upgraded to `tauri-macos-sign@2.2.0` + +## \[2.7.1] + +### Dependencies + +- Upgraded to `tauri-bundler@2.5.2` + +## \[2.7.0] + +### New Features + +- [`33d079392`](https://www.github.com/tauri-apps/tauri/commit/33d079392ac4a5a153b7d8a6d82fefd6f54a2bdf) ([#13811](https://www.github.com/tauri-apps/tauri/pull/13811) by [@mhbagheri-99](https://www.github.com/tauri-apps/tauri/../../mhbagheri-99)) Allow runner configuration to be an object with cmd, cwd, and args properties. The runner can now be configured as `{ "cmd": "my_runner", "cwd": "/path", "args": ["--quiet"] }` while maintaining backwards compatibility with the existing string format. + +### Enhancements + +- [`232265c70`](https://www.github.com/tauri-apps/tauri/commit/232265c70e1c213bbb3f84b5541ddc07d330fce1) ([#13209](https://www.github.com/tauri-apps/tauri/pull/13209) by [@kandrelczyk](https://www.github.com/tauri-apps/tauri/../../kandrelczyk)) Binaries are patched before bundling to add the type of a bundle they will placed in. This information will be used during update process to select the correct target. + +### Bug Fixes + +- [`916aeaa48`](https://www.github.com/tauri-apps/tauri/commit/916aeaa48646a483a78e51cfe1633800ee62c37c) ([#13781](https://www.github.com/tauri-apps/tauri/pull/13781) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes Android dev and build commands reading `tauri.ios.conf.json` instead of `tauri.android.conf.json` to merge platform-specific configuration. +- [`acd757428`](https://www.github.com/tauri-apps/tauri/commit/acd7574284056f9c00894cdce6c07f948fd80c87) ([#13743](https://www.github.com/tauri-apps/tauri/pull/13743) by [@owjs3901](https://www.github.com/tauri-apps/tauri/../../owjs3901)) Fix type of CFBundleVersion generated by `tauri ios init` when `bundleVersion` is a single number (for example `1` instead of `1.0.0`). +- [`0f248b111`](https://www.github.com/tauri-apps/tauri/commit/0f248b111ffb8af934eaf64bd8f4591e628da786) ([#13799](https://www.github.com/tauri-apps/tauri/pull/13799) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Install iOS dependencies when needed. +- [`7a6fd5b75`](https://www.github.com/tauri-apps/tauri/commit/7a6fd5b75d61071e2771f6277c0376ec206d302a) ([#13863](https://www.github.com/tauri-apps/tauri/pull/13863) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The AppImage bundler now pulls the AppRun binaries from our GitHub mirror, fixing 404 errors. +- [`bda830410`](https://www.github.com/tauri-apps/tauri/commit/bda8304107da7ca60caaba5674faa793491898c6) ([#13833](https://www.github.com/tauri-apps/tauri/pull/13833) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fail with an error when trying to migrate from v2 alpha +- [`bda830410`](https://www.github.com/tauri-apps/tauri/commit/bda8304107da7ca60caaba5674faa793491898c6) ([#13833](https://www.github.com/tauri-apps/tauri/pull/13833) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Use v2 stable instead of v2-rc when migrating from v2-beta + +### What's Changed + +- [`cfc5bb819`](https://www.github.com/tauri-apps/tauri/commit/cfc5bb819637a97141cbda3285c5d772cfc0ebca) ([#13780](https://www.github.com/tauri-apps/tauri/pull/13780) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Enable edge to edge in `tauri android init` template +- [`12e359061`](https://www.github.com/tauri-apps/tauri/commit/12e3590613c7b4370aeddc16db7e29335e3a7684) ([#13759](https://www.github.com/tauri-apps/tauri/pull/13759) by [@owjs3901](https://www.github.com/tauri-apps/tauri/../../owjs3901)) Update compileSdk, targetSdk in android template to 36 + +### Dependencies + +- Upgraded to `tauri-bundler@2.5.1` +- Upgraded to `tauri-utils@2.6.0` + +## \[2.6.2] + +### Bug Fixes + +- [`cbd962972`](https://www.github.com/tauri-apps/tauri/commit/cbd9629729ed6eb208ba2234d014c11c4e9f1c8c) ([#13730](https://www.github.com/tauri-apps/tauri/pull/13730) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Load `--config` arguments when running the Xcode and Android Studio build scripts. + +## \[2.6.1] + +### Bug Fixes + +- [`4b7370e9e`](https://www.github.com/tauri-apps/tauri/commit/4b7370e9e0ba299361c8ad96c08882337e44f091) ([#13710](https://www.github.com/tauri-apps/tauri/pull/13710) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue in the plugin template (`tauri plugin init`) that prevented the js build command to fail. + +## \[2.6.0] + +### New Features + +- [`414619c36`](https://www.github.com/tauri-apps/tauri/commit/414619c36e94e21939534dd72c0438b93da75546) ([#13536](https://www.github.com/tauri-apps/tauri/pull/13536) by [@Tunglies](https://www.github.com/tauri-apps/tauri/../../Tunglies)) Added support for the `bundleName` property in the macOS bundler configuration. This allows specifying the `CFBundleName` value for generated macOS bundles. +- [`3242e1c94`](https://www.github.com/tauri-apps/tauri/commit/3242e1c946c441b58665ba5d612f3a3f1eafe0b6) ([#13659](https://www.github.com/tauri-apps/tauri/pull/13659) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Allow passing Cargo arguments to mobile dev and build commands. +- [`d1ce9af62`](https://www.github.com/tauri-apps/tauri/commit/d1ce9af62881e3f7d86a495c9c40df5b7f9d1c04) ([#13660](https://www.github.com/tauri-apps/tauri/pull/13660) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Allow passing `--config` arguments to the `ios init` and `android init` commands to tweak the configuration used to initialize the mobile projects. +- [`7322f0579`](https://www.github.com/tauri-apps/tauri/commit/7322f057923aaec88960ad5556776774b745762f) ([#13502](https://www.github.com/tauri-apps/tauri/pull/13502) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Allow using `CheckIfAppIsRunning` macro inside NSIS hooks, for example `!insertmacro CheckIfAppIsRunning "another-executable.exe" "Another Executable"`. +- [`4a880ca69`](https://www.github.com/tauri-apps/tauri/commit/4a880ca697bab6d63a2a51ea94e1988cc8c4ea4a) ([#13658](https://www.github.com/tauri-apps/tauri/pull/13658) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Synchronize Tauri config productName changes with the iOS Xcode project. +- [`8ee14a864`](https://www.github.com/tauri-apps/tauri/commit/8ee14a86480510c15823586cf28084e615cb7a9c) ([#13618](https://www.github.com/tauri-apps/tauri/pull/13618) by [@Sky-walkerX](https://www.github.com/tauri-apps/tauri/../../Sky-walkerX)) Warn the user that the app id shouldn't end in `.app` because it conflicts with the application bundle extension on macOS + +### Bug Fixes + +- [`574a4d4d3`](https://www.github.com/tauri-apps/tauri/commit/574a4d4d36762b5b09dc3fcfcbcae3a0df0b6d89) ([#13426](https://www.github.com/tauri-apps/tauri/pull/13426) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix `dev`, `build` and `bundle` commands always take 2 seconds to start +- [`35aa7e121`](https://www.github.com/tauri-apps/tauri/commit/35aa7e1218f34d0805e280e3ec32529d0cb0d733) ([#13294](https://www.github.com/tauri-apps/tauri/pull/13294) by [@kingsword09](https://www.github.com/tauri-apps/tauri/../../kingsword09)) fix: allow the target directory to be inside frontendDir as long as it is not the Rust target directory inside frontendDir. +- [`ec6065fa4`](https://www.github.com/tauri-apps/tauri/commit/ec6065fa4a6427266ecfb0c0f62f008574bb7880) ([#13625](https://www.github.com/tauri-apps/tauri/pull/13625) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes Android and iOS dev/build commands not working when the app identifier is being modified by the `--config` option. +- [`5a5291d66`](https://www.github.com/tauri-apps/tauri/commit/5a5291d66cb8a955c9d4f8e975782646ac0cc6e7) ([#13483](https://www.github.com/tauri-apps/tauri/pull/13483) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix simulator build detection on Xcode. + +### Dependencies + +- Upgraded to `tauri-bundler@2.5.0` +- Upgraded to `tauri-utils@2.5.0` +- [`9c16eefa3`](https://www.github.com/tauri-apps/tauri/commit/9c16eefa319b4697bac1d1019bbb5f93eca63173) ([#13629](https://www.github.com/tauri-apps/tauri/pull/13629) by [@sftse](https://www.github.com/tauri-apps/tauri/../../sftse)) Update html5ever to 0.29 and kuchikiki to version 0.8.8-speedreader. + +## \[2.5.0] + +### New Features + +- [`0aa48fb9e`](https://www.github.com/tauri-apps/tauri/commit/0aa48fb9e4b9d7b5bf3522000a76ebc1836394ed) ([#13030](https://www.github.com/tauri-apps/tauri/pull/13030)) Added `bundleVersion` to iOS and macOS configuration to support specifying a `CFBundleVersion`. + +### Enhancements + +- [`ad3fd3890`](https://www.github.com/tauri-apps/tauri/commit/ad3fd3890f1fa26a9f9be04ff1bc156d6dd2a8bc) ([#13152](https://www.github.com/tauri-apps/tauri/pull/13152)) Detect package manager from environment variable `npm_config_user_agent` first +- [`82406c61e`](https://www.github.com/tauri-apps/tauri/commit/82406c61e0fbb775ef00791ccab45349325bdd45) ([#13231](https://www.github.com/tauri-apps/tauri/pull/13231)) Improve iOS simulator usage, checking if Xcode iOS SDK is installed and allowing usage of Simulator for older iOS releases (previously only supported when running on Xcode via `ios dev --open`). + +### Bug Fixes + +- [`2dccfab53`](https://www.github.com/tauri-apps/tauri/commit/2dccfab5321fef55d45f3a4c674b6151b1c4424a) ([#13236](https://www.github.com/tauri-apps/tauri/pull/13236)) Fix `fileAssociations` missing `LSHandlerRank` on macOS. +- [`080252903`](https://www.github.com/tauri-apps/tauri/commit/0802529031c4fd309edff374a8694e93ddec161d) ([#13210](https://www.github.com/tauri-apps/tauri/pull/13210)) Fixes iOS dev not working on Xcode 16.3 simulators. To apply the fix, either regenerate the Xcode project with `rm -r src-tauri/gen/apple && tauri ios init` or remove the `arm64-sim` architecture from the Xcode project. + +### Dependencies + +- Upgraded to `tauri-utils@2.4.0` +- Upgraded to `tauri-bundler@2.4.0` + +## \[2.4.1] + +### Enhancements + +- [`f805061d1`](https://www.github.com/tauri-apps/tauri/commit/f805061d1152bc4790dbdb9475a506afcdd1de75) ([#13079](https://www.github.com/tauri-apps/tauri/pull/13079) by [@Pietagorh](https://www.github.com/tauri-apps/tauri/../../Pietagorh)) Add support for passing TOML and JSON5 config files to `--config` arg + +### Bug Fixes + +- [`30beb6fee`](https://www.github.com/tauri-apps/tauri/commit/30beb6fee7b052e588ee72c238e315a557b5d6f2) ([#13096](https://www.github.com/tauri-apps/tauri/pull/13096) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix `tauri info` can't find the latest version for rust crates +- [`30beb6fee`](https://www.github.com/tauri-apps/tauri/commit/30beb6fee7b052e588ee72c238e315a557b5d6f2) ([#13096](https://www.github.com/tauri-apps/tauri/pull/13096) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix `tauri info` logging network operations in `info` level instead of `debug` +- [`794af778e`](https://www.github.com/tauri-apps/tauri/commit/794af778e4915ffb6a4fe9bae8fba04bc880503d) ([#13117](https://www.github.com/tauri-apps/tauri/pull/13117) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix setting merge config value to null with `--config` arg no longer works + +### Dependencies + +- Upgraded to `tauri-bundler@2.3.1` +- Upgraded to `tauri-utils@2.3.1` + +## \[2.4.0] + +### New Features + +- [`d91bfa5cb`](https://www.github.com/tauri-apps/tauri/commit/d91bfa5cb921a078758edd45ef3eaff71358d1eb) ([#12970](https://www.github.com/tauri-apps/tauri/pull/12970) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Allow merging multiple configuration values on `tauri dev`, `tauri build`, `tauri bundle`, `tauri android dev`, `tauri android build`, `tauri ios dev` and `tauri ios build`. +- [`013f8f652`](https://www.github.com/tauri-apps/tauri/commit/013f8f652302f2d49c5ec0a075582033d8b074fb) ([#12890](https://www.github.com/tauri-apps/tauri/pull/12890) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Reads `build > removeUnusedCommands` from the config file and pass in the environment variables on the build command to trigger the build scripts and macros to remove unused commands based on the capabilities you defined. For this to work on inlined plugins you must add a `#![plugin()]` inside the `tauri::generate_handler![]` usage and the app manifest must be set. +- [`30f5a1553`](https://www.github.com/tauri-apps/tauri/commit/30f5a1553d3c0ce460c9006764200a9210915a44) ([#12366](https://www.github.com/tauri-apps/tauri/pull/12366) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Added `trafficLightPosition` window configuration to set the traffic light buttons position on macOS. + +### Enhancements + +- [`f981a5ee8`](https://www.github.com/tauri-apps/tauri/commit/f981a5ee8b292b9ea09329f60cecc7f688dda734) ([#12602](https://www.github.com/tauri-apps/tauri/pull/12602) by [@kxxt](https://www.github.com/tauri-apps/tauri/../../kxxt)) Add basic support for linux riscv64 platform. + +### Bug Fixes + +- [`0c4700e99`](https://www.github.com/tauri-apps/tauri/commit/0c4700e9907f242eabe579eb6149a1d75174185c) ([#12985](https://www.github.com/tauri-apps/tauri/pull/12985) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The cli will now accept `--bundles updater` again. It's still no-op as it has been for all v2 versions. If you want to build updater artifacts, enable `createUpdaterArtifacts` in `tauri.conf.json`. +- [`eec08a18b`](https://www.github.com/tauri-apps/tauri/commit/eec08a18b66525f5544cd30144d0553260ee3a70) ([#12998](https://www.github.com/tauri-apps/tauri/pull/12998) by [@jason89521](https://www.github.com/tauri-apps/tauri/../../jason89521)) For bun's lockfile, check both `bun.lock` and `bun.lockb`. +- [`b83921226`](https://www.github.com/tauri-apps/tauri/commit/b83921226cb3084992bb5357e7e39a09ea97843e) ([#12977](https://www.github.com/tauri-apps/tauri/pull/12977) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix `tauri ios` commands using the wrong working directory with `bun@>1.2`. +- [`f268b3dbd`](https://www.github.com/tauri-apps/tauri/commit/f268b3dbdf313484c85b4a1f69cd7cec63049f35) ([#12871](https://www.github.com/tauri-apps/tauri/pull/12871) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Ignore parent .gitignore files on the Tauri project path detection. + +### Dependencies + +- Upgraded to `tauri-utils@2.3.0` +- Upgraded to `tauri-bundler@2.3.0` + +## \[2.3.1] + +### Bug Fixes + +- [`22e9bf74a`](https://www.github.com/tauri-apps/tauri/commit/22e9bf74a4684c827279a85bb66548e83c1ea5cf) ([#12538](https://www.github.com/tauri-apps/tauri/pull/12538) by [@DeTeam](https://www.github.com/tauri-apps/tauri/../../DeTeam)) Set initialViewController for LaunchScreen (iOS). + +## \[2.3.0] + +### Enhancements + +- [`a2d36b8c3`](https://www.github.com/tauri-apps/tauri/commit/a2d36b8c34a8dcfc6736797ca5cd4665faf75e7e) ([#12181](https://www.github.com/tauri-apps/tauri/pull/12181) by [@bastiankistner](https://www.github.com/tauri-apps/tauri/../../bastiankistner)) Add an option to change the default background throttling policy (currently for WebKit only). +- [`6e417c943`](https://www.github.com/tauri-apps/tauri/commit/6e417c9435d8fae0eca9e8d42d6215e887a28d98) ([#12786](https://www.github.com/tauri-apps/tauri/pull/12786) by [@kandrelczyk](https://www.github.com/tauri-apps/tauri/../../kandrelczyk)) Added RPM to the list of package types for which signature file will be generated. + +### Dependencies + +- Upgraded to `tauri-utils@2.2.0` +- Upgraded to `tauri-bundler@2.2.4` +- Upgraded to `tauri-macos-sign@2.1.0` + +## \[2.2.7] + +### Bug Fixes + +- [`8e9134c4a`](https://www.github.com/tauri-apps/tauri/commit/8e9134c4a2047329be0dbb868b7ae061a9d3f190) ([#12511](https://www.github.com/tauri-apps/tauri/pull/12511) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused `tauri dev` to fail because of an incorrect `--bins` flag. + +## \[2.2.6] + +### Enhancements + +- [`1a86974aa`](https://www.github.com/tauri-apps/tauri/commit/1a86974aa3d09957c6b1142a17bbfed9998798fd) ([#12406](https://www.github.com/tauri-apps/tauri/pull/12406) by [@bradleat](https://www.github.com/tauri-apps/tauri/../../bradleat)) `ios build --open` will now let xcode start the rust build process. +- [`9a30bed98`](https://www.github.com/tauri-apps/tauri/commit/9a30bed98c2d8501328006fad5840eb9d533e1c2) ([#12423](https://www.github.com/tauri-apps/tauri/pull/12423) by [@tr3ysmith](https://www.github.com/tauri-apps/tauri/../../tr3ysmith)) Added conditional logic to MacOS codesigning where only executables get the entitlements file when being signed. This solves an issue where the app may not launch when using 3rd party frameworks if certain entitlements are added. Ex: multicast support (must be applied for through apple developer, and the framework would not have that capability). +- [`0b79af711`](https://www.github.com/tauri-apps/tauri/commit/0b79af711430934362602fb950c3e4cb5b59cf9c) ([#12438](https://www.github.com/tauri-apps/tauri/pull/12438) by [@3lpsy](https://www.github.com/tauri-apps/tauri/../../3lpsy)) Log the command used to start the rust app in development. + +### Bug Fixes + +- [`bc43c738b`](https://www.github.com/tauri-apps/tauri/commit/bc43c738baf686353690d3d9259b4976881718c8) ([#12442](https://www.github.com/tauri-apps/tauri/pull/12442) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that prevented `tauri add` to work for the `clipboard-manager` plugin. +- [`27096cdc0`](https://www.github.com/tauri-apps/tauri/commit/27096cdc05d89b61b2372b4e4a3018c87f240ab8) ([#12445](https://www.github.com/tauri-apps/tauri/pull/12445) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused Tauri's CLI to enable tauri's `native-tls` feature even though it wasn't needed. Moved `reqwest` to a mobile-only dependency in `tauri` and enabled its `rustls-tls` feature flag. + +### Dependencies + +- Upgraded to `tauri-bundler@2.2.3` + +## \[2.2.5] + +### Dependencies + +- Upgraded to `tauri-bundler@2.2.2` + +## \[2.2.4] + +### Bug Fixes + +- [`cad550445`](https://www.github.com/tauri-apps/tauri/commit/cad5504455ffa53e297cebff473c113b1afa5d29) ([#12354](https://www.github.com/tauri-apps/tauri/pull/12354) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed and issue that caused `tauri add` to try to install incorrect npm packages. + +## \[2.2.3] + +### Enhancements + +- [`a0f2c84d5`](https://www.github.com/tauri-apps/tauri/commit/a0f2c84d51f5086c5055867d6f61ea90c463a26c) ([#12204](https://www.github.com/tauri-apps/tauri/pull/12204) by [@pjf-dev](https://www.github.com/tauri-apps/tauri/../../pjf-dev)) Enhance `tauri icon` command by including 64x64 png size in default icon sizes. + +### Bug Fixes + +- [`98f62e65a`](https://www.github.com/tauri-apps/tauri/commit/98f62e65a27a375272c6b4d9f34c23e142b9d3a6) ([#12246](https://www.github.com/tauri-apps/tauri/pull/12246) by [@marcomq](https://www.github.com/tauri-apps/tauri/../../marcomq)) Properly add NPM packages for community plugins when using the `tauri add` command. +- [`b9a99a5c6`](https://www.github.com/tauri-apps/tauri/commit/b9a99a5c69d8a2a1a3ff30e500b46872258dca15) ([#12297](https://www.github.com/tauri-apps/tauri/pull/12297) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the built-in dev server to constantly refresh on Linux. This only affected users who do not have `devUrl` point to a URL. +- [`ef21ed9ac`](https://www.github.com/tauri-apps/tauri/commit/ef21ed9ac1c045c38b0c04e3d71a441694abc257) ([#12290](https://www.github.com/tauri-apps/tauri/pull/12290) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix iOS build failing when the development team contains spaces. + +### Dependencies + +- Upgraded to `tauri-bundler@2.2.1` + +## \[2.2.2] + +### Bug Fixes + +- [`26fc9558f`](https://www.github.com/tauri-apps/tauri/commit/26fc9558fe7b2fe649f61926da88f36110dd5707) ([#12178](https://www.github.com/tauri-apps/tauri/pull/12178) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the `tauri dev` file watcher to exit after detecting file changes. + +## \[2.2.1] + +### Bug Fixes + +- [`881729448`](https://www.github.com/tauri-apps/tauri/commit/881729448c9abd0d0c7941a8a31c94119ce827af) ([#12164](https://www.github.com/tauri-apps/tauri/pull/12164) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused `tauri dev` to crash before showing the app on Linux. + +## \[2.2.0] + +### New Features + +- [`cccb308c7`](https://www.github.com/tauri-apps/tauri/commit/cccb308c7b559b0838138d6cea280665f060c925) ([#11562](https://www.github.com/tauri-apps/tauri/pull/11562) by [@jLynx](https://www.github.com/tauri-apps/tauri/../../jLynx)) Generate signature for `.deb` packages when `createUpdaterArtifacts` option is enabled. +- [`74212d40d`](https://www.github.com/tauri-apps/tauri/commit/74212d40d80dba4501b3d4ae30104fa3d447bdf9) ([#11653](https://www.github.com/tauri-apps/tauri/pull/11653) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Include Linux destkop environment and session type in `tauri info` command. + +### Enhancements + +- [`93a3a043d`](https://www.github.com/tauri-apps/tauri/commit/93a3a043d39cc96515d51d98beeb14261d3a246b) ([#11727](https://www.github.com/tauri-apps/tauri/pull/11727) by [@Kiyozz](https://www.github.com/tauri-apps/tauri/../../Kiyozz)) Add support for `Portuguese` language for NSIS windows installer. + +### Bug Fixes + +- [`c8700656b`](https://www.github.com/tauri-apps/tauri/commit/c8700656be3001a0cc6e087f23aebd482430a85b) ([#11985](https://www.github.com/tauri-apps/tauri/pull/11985) by [@ShaunSHamilton](https://www.github.com/tauri-apps/tauri/../../ShaunSHamilton)) Fix `tauri remove` from removing object type (`{}`) permissions. +- [`0ae06c5ca`](https://www.github.com/tauri-apps/tauri/commit/0ae06c5ca89cecd24154affdc69668f5e1e67d85) ([#11914](https://www.github.com/tauri-apps/tauri/pull/11914) by [@wtto00](https://www.github.com/tauri-apps/tauri/../../wtto00)) Fix the exclude path in file `Cargo.toml` of plugin template generated by cli. Path changed in [#9346](https://github.com/tauri-apps/tauri/pull/9346) + +### Dependencies + +- Upgraded to `tauri-bundler@2.2.0` +- Upgraded to `tauri-utils@2.1.1` + +## \[2.1.0] + +### New Features + +- [`1b6b2cfaa`](https://www.github.com/tauri-apps/tauri/commit/1b6b2cfaa14ab1d418c676cedbf942a812377a30) ([#11521](https://www.github.com/tauri-apps/tauri/pull/11521) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Process `bundle > windows > wix > fragmentPaths` with Handlebars to interpolate expressions within it. +- [`6bf917941`](https://www.github.com/tauri-apps/tauri/commit/6bf917941ff0fcc49e86b3ba427340b75f3ce49c) ([#11322](https://www.github.com/tauri-apps/tauri/pull/11322) by [@ShaunSHamilton](https://www.github.com/tauri-apps/tauri/../../ShaunSHamilton)) Add `tauri remove` to remove plugins from projects. +- [`058c0db72`](https://www.github.com/tauri-apps/tauri/commit/058c0db72f43fbe1574d0db654560e693755cd7e) ([#11584](https://www.github.com/tauri-apps/tauri/pull/11584) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add `bundle > linux > rpm > compression` config option to control RPM bundle compression type and level. + +### Enhancements + +- [`1f311832a`](https://www.github.com/tauri-apps/tauri/commit/1f311832ab5b2d62a533dfcf9b1d78bddf249ae8) ([#11405](https://www.github.com/tauri-apps/tauri/pull/11405) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add more context for errors when decoding secret and public keys for signing updater artifacts. +- [`e0d1307d3`](https://www.github.com/tauri-apps/tauri/commit/e0d1307d3f78987d0059921a5ab01ea4b26e0ef1) ([#11414](https://www.github.com/tauri-apps/tauri/pull/11414) by [@Czxck001](https://www.github.com/tauri-apps/tauri/../../Czxck001)) Migrate the `$schema` Tauri configuration to the v2 format. +- [`c43d5df15`](https://www.github.com/tauri-apps/tauri/commit/c43d5df15828ecffa606482ea2b60350c488c981) ([#11512](https://www.github.com/tauri-apps/tauri/pull/11512) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Associate a newly created capability file with the `main` window on the `tauri add` and `tauri permission add` commands. + +### Bug Fixes + +- [`7af01ff2c`](https://www.github.com/tauri-apps/tauri/commit/7af01ff2ce623d727cd13a4c8a549c1c80031882) ([#11523](https://www.github.com/tauri-apps/tauri/pull/11523) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix `tauri migrate` failing to install NPM depenencies when running from Deno. +- [`100a4455a`](https://www.github.com/tauri-apps/tauri/commit/100a4455aa48df508510bbc08273215bdf70c012) ([#11529](https://www.github.com/tauri-apps/tauri/pull/11529) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix detecting yarn berry (v2 and higher) in various tauri cli commands. +- [`60e86d5f6`](https://www.github.com/tauri-apps/tauri/commit/60e86d5f6e0f0c769d34ef368cd8801a918d796d) ([#11624](https://www.github.com/tauri-apps/tauri/pull/11624) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Use the public network IP address on `android dev` by default on Windows. + +### Dependencies + +- Upgraded to `tauri-utils@2.1.0` +- Upgraded to `tauri-bundler@2.1.0` + +## \[2.0.4] + +### Enhancements + +- [`e4c9268b1`](https://www.github.com/tauri-apps/tauri/commit/e4c9268b19c614dc9ebb0895448fd16de7efee80) ([#11258](https://www.github.com/tauri-apps/tauri/pull/11258) by [@regexident](https://www.github.com/tauri-apps/tauri/../../regexident)) Support custom project directory structure where the Tauri app folder is not a subfolder of the frontend project. + The frontend and Tauri app project paths can be set with the `TAURI_FRONTEND_PATH` and the `TAURI_APP_PATH` environment variables respectively. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.4` + +## \[2.0.3] + +### New Features + +- [`eda5713ea`](https://www.github.com/tauri-apps/tauri/commit/eda5713eab78d28182071ea25ceca5f1994f37ea) ([#11242](https://www.github.com/tauri-apps/tauri/pull/11242) by [@alex-sandri](https://www.github.com/tauri-apps/tauri/../../alex-sandri)) Add `Italian` to supported NSIS installer languages +- [`b3563e3d6`](https://www.github.com/tauri-apps/tauri/commit/b3563e3d6ae8dc90ee68f25f575cd5538ab1915b) ([#11304](https://www.github.com/tauri-apps/tauri/pull/11304) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add Deno support in tauri-cli operations. + +### Bug Fixes + +- [`d609bef9f`](https://www.github.com/tauri-apps/tauri/commit/d609bef9fd7cd6eeb2bd701558100bd9cfb6e6f6) ([#11314](https://www.github.com/tauri-apps/tauri/pull/11314) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix android invalid proguard file when using an `identifier` that contains a component that is a reserved kotlin keyword, like `in`, `class`, etc +- [`069c05e44`](https://www.github.com/tauri-apps/tauri/commit/069c05e44fd6f30083fdc00dd6c0001278898592) ([#11315](https://www.github.com/tauri-apps/tauri/pull/11315) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix CLI crashing and failing to find a `.ico` file when `bundle > icon` option is using globs and doesn't have a string that ends with `.ico`. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.3` + +## \[2.0.2] + +### What's Changed + +- [`4475fbb50`](https://www.github.com/tauri-apps/tauri/commit/4475fbb502c5ffb3cea4de6bef1c7869be39bed6) ([#11208](https://www.github.com/tauri-apps/tauri/pull/11208) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Update cargo-mobile2 to 0.17.3, fixing lib name validation. +- [`a49a19ffa`](https://www.github.com/tauri-apps/tauri/commit/a49a19ffa304f031fb1a04d31a567cc7f42a380a) ([#11218](https://www.github.com/tauri-apps/tauri/pull/11218)) Fix bundling `appimage`, `deb` and `rpm` bundles failing to open when using `mainBinaryName` with spaces. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.2` + +## \[2.0.1] + +### What's Changed + +- [`0ab2b3306`](https://www.github.com/tauri-apps/tauri/commit/0ab2b330644b6419f6cee1d5377bfb5cdda2ccf9) ([#11205](https://www.github.com/tauri-apps/tauri/pull/11205) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.1` +- Upgraded to `tauri-macos-sign@2.0.1` +- Upgraded to `tauri-bundler@2.0.1` + +## \[2.0.0] + +### What's Changed + +- [`382ed482b`](https://www.github.com/tauri-apps/tauri/commit/382ed482bd08157c39e62f9a0aaad8802f1092cb) Bump MSRV to 1.78. +- [`637285790`](https://www.github.com/tauri-apps/tauri/commit/6372857905ae9c0aedb7f482ddf6cf9f9836c9f2) Promote to v2 stable! + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0` +- Upgraded to `tauri-macos-sign@2.0.0` +- Upgraded to `tauri-bundler@2.0.0` + +## \[2.0.0-rc.18] + +### Enhancements + +- [`a08e6ffa6`](https://www.github.com/tauri-apps/tauri/commit/a08e6ffa6fe499553be3c4c620726d6031cd6dd3) ([#11185](https://www.github.com/tauri-apps/tauri/pull/11185) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Enhance port forwarding on `android dev` to be more resilient and tolerate delays when booting up devices. +- [`6cfe7edf6`](https://www.github.com/tauri-apps/tauri/commit/6cfe7edf63636fdf66c429efdeb7bc9a0f404e9f) ([#11186](https://www.github.com/tauri-apps/tauri/pull/11186) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Retain logger verbosity on the `android-studio-script` and `xcode-script` commands. +- [`60a5aea53`](https://www.github.com/tauri-apps/tauri/commit/60a5aea53db02ae6af325812ab97555f2c013d70) ([#11181](https://www.github.com/tauri-apps/tauri/pull/11181) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Set the `TRUNK_SERVE_ADDRESS` environment variable when running on iOS physical devices to support Trunk. + +### Bug Fixes + +- [`f5d61822b`](https://www.github.com/tauri-apps/tauri/commit/f5d61822bf5988827776dd58bed75c19364e86bd) ([#11184](https://www.github.com/tauri-apps/tauri/pull/11184) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix iOS application not including the provided capabilities (entitlements). + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-rc.15` +- Upgraded to `tauri-macos-sign@0.1.1-rc.1` + +## \[2.0.0-rc.17] + +### New Features + +- [`06718b456`](https://www.github.com/tauri-apps/tauri/commit/06718b4569577a004daed9b8455e852a50b4b80f) ([#11096](https://www.github.com/tauri-apps/tauri/pull/11096) by [@thep0y](https://www.github.com/tauri-apps/tauri/../../thep0y)) Add the `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE` environment variable to specify a more accessible mirror template, facilitating companies, organizations, or individuals who cannot access GitHub to download the necessary files through their own mirror servers. +- [`a944b9b05`](https://www.github.com/tauri-apps/tauri/commit/a944b9b05bc5ae6125ff451e86c5b207c511f3d7) ([#11118](https://www.github.com/tauri-apps/tauri/pull/11118) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add `--github-workflows` flag for `tauri plugin new/init`. +- [`f57a729cd`](https://www.github.com/tauri-apps/tauri/commit/f57a729cd8f7e10d8daf0b9d5b85f9c7ad530496) ([#11039](https://www.github.com/tauri-apps/tauri/pull/11039) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add `tauri inspect wix-upgrade-code` to print default Upgrade Code for your MSI installer derived from `productName`. + +### Enhancements + +- [`67b8a9a17`](https://www.github.com/tauri-apps/tauri/commit/67b8a9a17a0315ccba681a646678dc1922f5ebf2) ([#10940](https://www.github.com/tauri-apps/tauri/pull/10940) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Generate `.sig` signature files for installers and bundles when `createUpdaterArtifacts` is set to `v1Compatible` + +### Bug Fixes + +- [`62b52f60a`](https://www.github.com/tauri-apps/tauri/commit/62b52f60a22ef84c4a2a2d9e662038b49f58e16c) ([#11064](https://www.github.com/tauri-apps/tauri/pull/11064) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix `tauri add` failing to add NPM depenency with `npm` package manager. +- [`56e087471`](https://www.github.com/tauri-apps/tauri/commit/56e087471a347f6bee7422221a956925c60b17e3) ([#11100](https://www.github.com/tauri-apps/tauri/pull/11100) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix iOS xcode-script usage with `bun`. +- [`b88e22a5f`](https://www.github.com/tauri-apps/tauri/commit/b88e22a5fe4e2e4376d6cad64d1e74d104ca8927) ([#11063](https://www.github.com/tauri-apps/tauri/pull/11063) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The cli now only sets the iOS deployment target environment variable when building for iOS. +- [`8d22c0c81`](https://www.github.com/tauri-apps/tauri/commit/8d22c0c814e7227d5e56ce9a08929045ccea1a1b) ([#11101](https://www.github.com/tauri-apps/tauri/pull/11101) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Only modify the iOS Xcode project "sign style" if we need to enforce manual signing. +- [`df24cb944`](https://www.github.com/tauri-apps/tauri/commit/df24cb944249ee398f6c8ba8c19757b398eec701) ([#11168](https://www.github.com/tauri-apps/tauri/pull/11168) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes Xcode pbxproj file parsing not expecting `_` in build configuration IDs. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-rc.14` +- Upgraded to `tauri-utils@2.0.0-rc.13` + +### Breaking Changes + +- [`a944b9b05`](https://www.github.com/tauri-apps/tauri/commit/a944b9b05bc5ae6125ff451e86c5b207c511f3d7) ([#11118](https://www.github.com/tauri-apps/tauri/pull/11118) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) `tauri plugin init/new` will no longer generate a `.github` directory with workflows by default, instead use the new `--github-workflows` flag. + +## \[2.0.0-rc.16] + +### New Features + +- [`9bb8fc618`](https://www.github.com/tauri-apps/tauri/commit/9bb8fc6189a93bcb811588b36e710d0f7818a1f9) ([#11030](https://www.github.com/tauri-apps/tauri/pull/11030) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add `--no-example` flag for `tauri plugin new` and `tauri plugin init` to disable creation of an example project. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.12` +- Upgraded to `tauri-bundler@2.0.1-rc.13` + +## \[2.0.0-rc.15] + +### Enhancements + +- [`5a0e922d4`](https://www.github.com/tauri-apps/tauri/commit/5a0e922d40dc3b7d9a8e3a65ccaf76d09f026cb8) ([#11007](https://www.github.com/tauri-apps/tauri/pull/11007) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Automatically discover the `src-tauri/src/main.rs` binary when it is not explicitly defined in the Cargo manifest bin array. + +### Bug Fixes + +- [`94e9d476e`](https://www.github.com/tauri-apps/tauri/commit/94e9d476ef506b1b8c09f55b81620c7839f98086) ([#11011](https://www.github.com/tauri-apps/tauri/pull/11011) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix `main_binary_name` in custom wix and nsis templates including `.exe` + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-rc.12` + +## \[2.0.0-rc.13] + +### New Features + +- [`656618225`](https://www.github.com/tauri-apps/tauri/commit/65661822580c31eb10a44be45842e259c598374c) ([#10866](https://www.github.com/tauri-apps/tauri/pull/10866) by [@thep0y](https://www.github.com/tauri-apps/tauri/../../thep0y)) Add `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR` environment variable to specify a GitHub mirror to download files and tools used by tauri bundler. This is designed for areas like Mainland China where GitHub access can be unreliable. +- [`35bd9dd3d`](https://www.github.com/tauri-apps/tauri/commit/35bd9dd3dc3d8972bbc4aa5f4a6c6fa14354e9bf) ([#10977](https://www.github.com/tauri-apps/tauri/pull/10977) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add `mainBinaryName` config option to set the file name for the main binary. + +### Enhancements + +- [`6c5340f8b`](https://www.github.com/tauri-apps/tauri/commit/6c5340f8b2549dfe89f19656304e65cd670afc92) ([#11004](https://www.github.com/tauri-apps/tauri/pull/11004) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Added the `log` plugin to the app template, which is required to visualize logs on Android and iOS. +- [`3ad2427dc`](https://www.github.com/tauri-apps/tauri/commit/3ad2427dc08f12c61edc726b587acce32eca1080) ([#10961](https://www.github.com/tauri-apps/tauri/pull/10961) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Only render app logs on iOS unless `-vv` is provided to the `ios dev` command. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-rc.11` +- Upgraded to `tauri-utils@2.0.0-rc.11` + +## \[2.0.0-rc.12] + +### Bug Fixes + +- [`a5848af65`](https://www.github.com/tauri-apps/tauri/commit/a5848af65b10d89686314cf737b7fd9d91f99dd8) ([#10944](https://www.github.com/tauri-apps/tauri/pull/10944) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Synchronize app version (`tauri.conf.json > version` or `Cargo.toml > package > version`) with the `CFBundleVersion` and `CFBundleShortVersionString` Info.plist values. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.10` +- Upgraded to `tauri-bundler@2.0.1-rc.10` + +## \[2.0.0-rc.11] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.9` +- Upgraded to `tauri-bundler@2.0.1-rc.9` + +## \[2.0.0-rc.10] + +### Enhancements + +- [`9c9644d15`](https://www.github.com/tauri-apps/tauri/commit/9c9644d155818d9efcad65b60aa985a59e767922) ([#10845](https://www.github.com/tauri-apps/tauri/pull/10845) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Enhance iOS library validation, checking libs built with link time optimization. + +### Bug Fixes + +- [`b42683592`](https://www.github.com/tauri-apps/tauri/commit/b42683592d446f25c2005b59e9e3ec551175906d) ([#10847](https://www.github.com/tauri-apps/tauri/pull/10847) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes `ios build --target [aarch64-sim | x86_64]` failing to generate the app bundle for the iOS simulator. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.8` +- Upgraded to `tauri-bundler@2.0.1-rc.8` + +## \[2.0.0-rc.9] + +### Bug Fixes + +- [`6faa03276`](https://www.github.com/tauri-apps/tauri/commit/6faa032766b23cd161503905d4c79365ff6c50d1) ([#10854](https://www.github.com/tauri-apps/tauri/pull/10854) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes iOS code signing failing on CI due to a missing development certificate. + +## \[2.0.0-rc.9] + +### Bug Fixes + +- [`5af1f5dec`](https://www.github.com/tauri-apps/tauri/commit/5af1f5dec1bb98f335169df8c5e30c19a24cae07) ([#10851](https://www.github.com/tauri-apps/tauri/pull/10851) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes `ios build` failing to build iOS app in CI when using an API key for automatic signing. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-rc.7` + +## \[2.0.0-rc.8] + +### New Features + +- [`91e9e784a`](https://www.github.com/tauri-apps/tauri/commit/91e9e784aa59634e3fe6359f8b78d071d76a9e42) ([#10729](https://www.github.com/tauri-apps/tauri/pull/10729) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add plugins information in `tauri info` output +- [`09e9dc1aa`](https://www.github.com/tauri-apps/tauri/commit/09e9dc1aab1b66aa6a3a009d5873db586abe76a0) ([#10752](https://www.github.com/tauri-apps/tauri/pull/10752) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Allow Xcode to manage iOS code sign and provisioning profiles by default. + On CI, the `APPLE_API_KEY`, `APPLE_API_ISSUER` and `APPLE_API_KEY_PATH` environment variables must be provided for authentication. + +### Enhancements + +- [`3a4972b39`](https://www.github.com/tauri-apps/tauri/commit/3a4972b394c65c32eefebfb2181ba56b0cfc08f7) ([#10793](https://www.github.com/tauri-apps/tauri/pull/10793) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Include architecture in the `tauri info` output. +- [`fd68b7fde`](https://www.github.com/tauri-apps/tauri/commit/fd68b7fdea3890d9f0a373a252a3682bd9d04138) ([#10785](https://www.github.com/tauri-apps/tauri/pull/10785) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Remove the `.cargo/config` file creation that used to fix mobile build caches. +- [`f67a9eb6d`](https://www.github.com/tauri-apps/tauri/commit/f67a9eb6de4567c2374b8cdbabadcf0ca44d28fb) ([#10802](https://www.github.com/tauri-apps/tauri/pull/10802) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Synchronize identifier, development team and lib name with the iOS Xcode project. + +### Bug Fixes + +- [`83ed090bf`](https://www.github.com/tauri-apps/tauri/commit/83ed090bfa58a1784495f474d93b16a568be513f) ([#10790](https://www.github.com/tauri-apps/tauri/pull/10790) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Do not quit `ios dev` and `android dev` process when we fail to attach the logger. +- [`2d31aef75`](https://www.github.com/tauri-apps/tauri/commit/2d31aef759f496f3afe46b7697176e61a8570511) ([#10751](https://www.github.com/tauri-apps/tauri/pull/10751) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Ensure gradlew is executable and does not use CRLF so it can be used on UNIX systems. +- [`02b2f964a`](https://www.github.com/tauri-apps/tauri/commit/02b2f964a70c61ff08b5052bd9fcde472d706d9c) ([#10795](https://www.github.com/tauri-apps/tauri/pull/10795) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix the `add` command NPM version specifier for known plugins from `2.0.0-rc` (unknown version requirement) to `^2.0.0-rc`. +- [`84070bae9`](https://www.github.com/tauri-apps/tauri/commit/84070bae92d234bc3630e795cfaf79f869f3a751) ([#10792](https://www.github.com/tauri-apps/tauri/pull/10792) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix `tauri plugin ios init` not generating the iOS folder. +- [`edb2ca31f`](https://www.github.com/tauri-apps/tauri/commit/edb2ca31f70a39004b6a09ae53425f22e243318e) ([#10794](https://www.github.com/tauri-apps/tauri/pull/10794) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Migrate v1 plugins NPM packages. +- [`9718dc9e8`](https://www.github.com/tauri-apps/tauri/commit/9718dc9e8c9bc91d9a5d9e0e06a7afab62492152) ([#10791](https://www.github.com/tauri-apps/tauri/pull/10791) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Reintroduce the `targetSdk` value in the Android application template. + +### What's Changed + +- [`fb6bf3142`](https://www.github.com/tauri-apps/tauri/commit/fb6bf314252c88dd49af74bdbb8499df370836ae) ([#10763](https://www.github.com/tauri-apps/tauri/pull/10763) by [@rdlabo](https://www.github.com/tauri-apps/tauri/../../rdlabo)) Update plugin template Android code to match documentation on Android package ID usage. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.7` +- Upgraded to `tauri-bundler@2.0.1-rc.6` + +### Breaking Changes + +- [`073bb4f45`](https://www.github.com/tauri-apps/tauri/commit/073bb4f459a923541b94970dfa7e087bccaa2cfd) ([#10772](https://www.github.com/tauri-apps/tauri/pull/10772) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Removed the deprecated `webview_fixed_runtime_path` config option, use the `webview_install_mode` instead. + +## \[2.0.0-rc.7] + +### Enhancements + +- [`da8c9a7d3`](https://www.github.com/tauri-apps/tauri/commit/da8c9a7d3069398c26826aeb082caa44b7c92809) ([#10669](https://www.github.com/tauri-apps/tauri/pull/10669) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Modify both ExportOptions.plist and project.pbxproj to reflect changes for the `IOS_CERTIFICATE`, `IOS_CERTIFICATE_PASSWORD` and `IOS_MOBILE_PROVISION` environment variables. + +### Bug Fixes + +- [`793ee0531`](https://www.github.com/tauri-apps/tauri/commit/793ee0531730597e6008c9c0dedabbab7a2bef53) ([#10700](https://www.github.com/tauri-apps/tauri/pull/10700) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Allow hyphens and underscores on app identifiers. +- [`da8c9a7d3`](https://www.github.com/tauri-apps/tauri/commit/da8c9a7d3069398c26826aeb082caa44b7c92809) ([#10669](https://www.github.com/tauri-apps/tauri/pull/10669) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Synchronize Xcode project changes with the ExportOptions.plist file so `ios build` calls can work with code signing changes made in Xcode. + +### What's Changed + +- [`f4d5241b3`](https://www.github.com/tauri-apps/tauri/commit/f4d5241b377d0f7a1b58100ee19f7843384634ac) ([#10731](https://www.github.com/tauri-apps/tauri/pull/10731) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Update documentation icon path. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.6` +- Upgraded to `tauri-bundler@2.0.1-rc.5` + +### Breaking Changes + +- [`da8c9a7d3`](https://www.github.com/tauri-apps/tauri/commit/da8c9a7d3069398c26826aeb082caa44b7c92809) ([#10669](https://www.github.com/tauri-apps/tauri/pull/10669) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) The `IOS_CERTIFICATE`, `IOS_CERTIFICATE_PASSWORD` and `IOS_MOBILE_PROVISION` environment variables are now read by the `ios build` command instead of `ios init`. + +## \[2.0.0-rc.6] + +### New Features + +- [`da381e07f`](https://www.github.com/tauri-apps/tauri/commit/da381e07f3770988fe6d0859a02331b87cc6723f) ([#10696](https://www.github.com/tauri-apps/tauri/pull/10696) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Inject configured resources on mobile apps. + +### Bug Fixes + +- [`1a60822a4`](https://www.github.com/tauri-apps/tauri/commit/1a60822a4220b6dbb1ad7295a2e37d6c3004edad) ([#10699](https://www.github.com/tauri-apps/tauri/pull/10699) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Changed the `add` command to use a version requirement that matches the CLI's stable and prerelease numbers. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.5` +- Upgraded to `tauri-bundler@2.0.1-rc.4` + +## \[2.0.0-rc.5] + +### New Features + +- [`8d148a9e2`](https://www.github.com/tauri-apps/tauri/commit/8d148a9e2566edebfea2d75f32df7c9396d765a4) ([#10634](https://www.github.com/tauri-apps/tauri/pull/10634) by [@anatawa12](https://www.github.com/tauri-apps/tauri/../../anatawa12)) Custom sign command with object notation for whitespaces in the command path and arguments. + +### Bug Fixes + +- [`8ae52a615`](https://www.github.com/tauri-apps/tauri/commit/8ae52a615a11d934930001da63ce6ac8442c7efc) ([#10676](https://www.github.com/tauri-apps/tauri/pull/10676) by [@rdlabo](https://www.github.com/tauri-apps/tauri/../../rdlabo)) Change plugin template call to `register_ios_plugin` params to snake case +- [`7796a8fc6`](https://www.github.com/tauri-apps/tauri/commit/7796a8fc649cd7397a67048c71f8d1fbf822122a) ([#10687](https://www.github.com/tauri-apps/tauri/pull/10687) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix Swift plugin compilation on older versions. +- [`9b99ebab1`](https://www.github.com/tauri-apps/tauri/commit/9b99ebab17d6a043d82a7aeecfb76c56a995c287) ([#10431](https://www.github.com/tauri-apps/tauri/pull/10431) by [@mrguiman](https://www.github.com/tauri-apps/tauri/../../mrguiman)) Do not include the target arch when building and archiving the iOS application, + which makes Xcode project modifications more flexible. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.4` +- Upgraded to `tauri-bundler@2.0.1-rc.3` + +## \[2.0.0-rc.4] + +### New Features + +- [`78e22bedc`](https://www.github.com/tauri-apps/tauri/commit/78e22bedcab5096f1a4e667321fc8b2817b79214) ([#10602](https://www.github.com/tauri-apps/tauri/pull/10602) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add necessary options to `AndroidManifest.xml` in android template to support AndroidTV. +- [`3bec7b159`](https://www.github.com/tauri-apps/tauri/commit/3bec7b1595e28630a22b9fb16540beafd5eb7969) ([#10544](https://www.github.com/tauri-apps/tauri/pull/10544) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) v1 migrate script now migrates Svelte and Vue.js code. + +### Enhancements + +- [`bba1a4419`](https://www.github.com/tauri-apps/tauri/commit/bba1a441917defcdf9e88221e9b0e1cdd744e77a) ([#10457](https://www.github.com/tauri-apps/tauri/pull/10457) by [@mmvanheusden](https://www.github.com/tauri-apps/tauri/../../mmvanheusden)) Added `--no-fmt` option to the `add` command to skip formatting the code after applying changes. +- [`71d00646a`](https://www.github.com/tauri-apps/tauri/commit/71d00646a9b7c52311ba087820e52fd19861b3d8) ([#10504](https://www.github.com/tauri-apps/tauri/pull/10504) by [@fu050409](https://www.github.com/tauri-apps/tauri/../../fu050409)) Improve the `init` command behavior by detecting the project NPM package manager. +- [`8deb1966a`](https://www.github.com/tauri-apps/tauri/commit/8deb1966ace93d1350f271d525a878ba4b0879ce) ([#10652](https://www.github.com/tauri-apps/tauri/pull/10652) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Infer macOS codesign identity from the `APPLE_CERTIFICATE` environment variable when provided, meaning the identity no longer needs to be provided when signing on CI using that option. If the imported certificate name does not match a provided signingIdentity configuration, an error is returned. +- [`f35bcda28`](https://www.github.com/tauri-apps/tauri/commit/f35bcda2895a1350df31853da76a051783b9fd3f) ([#10598](https://www.github.com/tauri-apps/tauri/pull/10598) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) `permission add` and `add` commands now check if the plugin is known and if it is either desktop or mobile only + we add the permission to a target-specific capability. + +### Bug Fixes + +- [`f712f31d1`](https://www.github.com/tauri-apps/tauri/commit/f712f31d1d21e85fab99194530702c70e45c63fc) ([#10639](https://www.github.com/tauri-apps/tauri/pull/10639) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Include notarization error output in the error message if it fails. +- [`9f75d0622`](https://www.github.com/tauri-apps/tauri/commit/9f75d06228fcb7036cf7a4e215abc7bc8d1a0a56) ([#10604](https://www.github.com/tauri-apps/tauri/pull/10604) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes `android dev` port forward failing under some conditions, add better logging and error handling. +- [`2d47352a0`](https://www.github.com/tauri-apps/tauri/commit/2d47352a07a7d742e62291a5e6810aed79fc8b50) ([#10418](https://www.github.com/tauri-apps/tauri/pull/10418) by [@samkearney](https://www.github.com/tauri-apps/tauri/../../samkearney)) CLI commands will now consistently search for the `app_dir` (the directory containing `package.json`) from the current working directory of the command invocation. +- [`f4cd68f04`](https://www.github.com/tauri-apps/tauri/commit/f4cd68f040635f019ff989667289cfe9061c7dfb) ([#10600](https://www.github.com/tauri-apps/tauri/pull/10600) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes `android dev` not working when using the builtin dev server. +- [`9089d9763`](https://www.github.com/tauri-apps/tauri/commit/9089d97637e49bebbe7dba8adc6351e04b53a44d) ([#10605](https://www.github.com/tauri-apps/tauri/pull/10605) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes `[android|ios] build --config ` failing to resolve. +- [`712f1049f`](https://www.github.com/tauri-apps/tauri/commit/712f1049fae74bfda5f360adcee7210cea92fe63) ([#10569](https://www.github.com/tauri-apps/tauri/pull/10569) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes running `ios dev` and `ios build` using `bun`. +- [`3998570fd`](https://www.github.com/tauri-apps/tauri/commit/3998570fd3d03c1bb282bd060a4aafb4ab5437f9) ([#10540](https://www.github.com/tauri-apps/tauri/pull/10540) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes v1 migration of Cargo.toml dependencies and features. +- [`3beba92b5`](https://www.github.com/tauri-apps/tauri/commit/3beba92b5bdc62ed00c9f6a9b8f8c05cfa78f8dc) ([#10542](https://www.github.com/tauri-apps/tauri/pull/10542) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes v1 frontend code migration when using plugin default imports. +- [`10fb027b7`](https://www.github.com/tauri-apps/tauri/commit/10fb027b7590cf2c020b5c220328b9051c05adca) ([#10656](https://www.github.com/tauri-apps/tauri/pull/10656) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Migrate v1 plugins to their v2 releases. +- [`10fb027b7`](https://www.github.com/tauri-apps/tauri/commit/10fb027b7590cf2c020b5c220328b9051c05adca) ([#10656](https://www.github.com/tauri-apps/tauri/pull/10656) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Prevent duplicate permissions on v1 migration. +- [`b160f9359`](https://www.github.com/tauri-apps/tauri/commit/b160f9359d6f661d280185d2a2a4bdf280b8e72c) ([#10638](https://www.github.com/tauri-apps/tauri/pull/10638) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Only validate the output iOS library on debug builds. +- [`4bfe4880f`](https://www.github.com/tauri-apps/tauri/commit/4bfe4880fbef42d1a115f840e712d4a2f59c8ab3) ([#10550](https://www.github.com/tauri-apps/tauri/pull/10550) by [@anatawa12](https://www.github.com/tauri-apps/tauri/../../anatawa12)) fails to build universal fat binary if main bin is renamed to another name in `Cargo.toml` +- [`f3837d5b9`](https://www.github.com/tauri-apps/tauri/commit/f3837d5b98f0caebc3337f9a9e8127e7b96c3fc5) ([#10539](https://www.github.com/tauri-apps/tauri/pull/10539) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Improve migration tooling by supporting TOML configs, handle nulls and properly check for updater migration. + +### What's Changed + +- [`794cf8234`](https://www.github.com/tauri-apps/tauri/commit/794cf8234f8b620c74cbd23cc4b81be9b2edc386) ([#10571](https://www.github.com/tauri-apps/tauri/pull/10571) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Change iOS template default export method from deprecated `development` to `debugging`. +- [`bfc49cc7a`](https://www.github.com/tauri-apps/tauri/commit/bfc49cc7a1d43e3378e93865b9b37ce4bddfa6e6) ([#10558](https://www.github.com/tauri-apps/tauri/pull/10558) by [@ahqsoftwares](https://www.github.com/tauri-apps/tauri/../../ahqsoftwares)) Remove targetSdk from gradle files + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.3` +- Upgraded to `tauri-bundler@2.0.1-rc.2` +- Upgraded to `tauri-macos-sign@0.1.1-rc.0` + +## \[2.0.0-rc.3] + +### Enhancements + +- [`5f56cb0a8`](https://www.github.com/tauri-apps/tauri/commit/5f56cb0a8b9c6f695bc6439a8db997c98b3a3997) ([#10507](https://www.github.com/tauri-apps/tauri/pull/10507) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Update gradle to 8.9 and the gradle android plugin to 8.5.1 in the android templates (requires latest Android Studio). This should add support for Java 21 but Java 17 keeps being the recommended version. + +### Bug Fixes + +- [`f5dfc0280`](https://www.github.com/tauri-apps/tauri/commit/f5dfc02800dbd3bdee671b032454c49ac7102fb4) ([#10533](https://www.github.com/tauri-apps/tauri/pull/10533) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue causing `tauri ios init` to fail if `iOS.minimumSystemVersion` was not configured explicitly. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.2` +- Upgraded to `tauri-bundler@2.0.1-rc.1` + +## \[2.0.0-rc.2] + +### New Features + +- [`8dc81b6cc`](https://www.github.com/tauri-apps/tauri/commit/8dc81b6cc2b8235b11f74a971d6aa3a5df5e9f68) ([#10496](https://www.github.com/tauri-apps/tauri/pull/10496) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Added `bundle > ios > template` configuration option for custom Xcode project YML Handlebars template using XcodeGen. +- [`02c00abc6`](https://www.github.com/tauri-apps/tauri/commit/02c00abc63cf86e9bf9179cbb143d5145a9397b6) ([#10495](https://www.github.com/tauri-apps/tauri/pull/10495) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Added `bundle > ios > minimumSystemVersion` configuration option. + +### Enhancements + +- [`8e1e15304`](https://www.github.com/tauri-apps/tauri/commit/8e1e15304e9dc98d7f875fc8dceb7d4ce19adc47) ([#10483](https://www.github.com/tauri-apps/tauri/pull/10483) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Check if the Rust library contains the symbols required at runtime for Android and iOS apps. +- [`ca6868956`](https://www.github.com/tauri-apps/tauri/commit/ca68689564cbc8dfa9a5220d3daf81a44ef81fcc) ([#10479](https://www.github.com/tauri-apps/tauri/pull/10479) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Check if identifier or lib name changed when running mobile commands. + +### Bug Fixes + +- [`2e8ab7bac`](https://www.github.com/tauri-apps/tauri/commit/2e8ab7bac12046d734fb07a1b4fe5e03004b305e) ([#10481](https://www.github.com/tauri-apps/tauri/pull/10481) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Migration from v1 to v2 now adds the updater plugin when it is active. + +### What's Changed + +- [`a3cd9779a`](https://www.github.com/tauri-apps/tauri/commit/a3cd9779a47428e306a628d658740669faf69ccd) ([#10480](https://www.github.com/tauri-apps/tauri/pull/10480) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Removed the `[android|ios] open` command. It is recommended to use `[android|ios] dev --open` or `[android|ios] build --open` instead. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-rc.0` +- Upgraded to `tauri-utils@2.0.0-rc.1` + +## \[2.0.0-rc.1] + +### Bug Fixes + +- [`fb1933f17`](https://www.github.com/tauri-apps/tauri/commit/fb1933f17442674e53374578e57a8cad241ac3c6) ([#10467](https://www.github.com/tauri-apps/tauri/pull/10467) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes running `android dev --open`. +- [`206914fe8`](https://www.github.com/tauri-apps/tauri/commit/206914fe8d97eb61a2ff2a80e94e65e7a42bcea5) ([#10466](https://www.github.com/tauri-apps/tauri/pull/10466) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fixes running `adb reverse` in Node.js context. + +## \[2.0.0-rc.0] + +### New Features + +- [`d5511c311`](https://www.github.com/tauri-apps/tauri/commit/d5511c3117b1a117cb0b7359c5fa09aa4795122b) ([#10395](https://www.github.com/tauri-apps/tauri/pull/10395)) Added migration from `2.0.0-beta` to `2.0.0-rc`. +- [`a5bfbaa62`](https://www.github.com/tauri-apps/tauri/commit/a5bfbaa62b8cd0aacbb33f730d4e30b43c461fe1)([#9962](https://www.github.com/tauri-apps/tauri/pull/9962)) Added `bundle > iOS > frameworks` configuration to define a list of frameworks that are linked to the Xcode project when it is generated. + +### Enhancements + +- [`a0841d509`](https://www.github.com/tauri-apps/tauri/commit/a0841d509abc43b62bb7c755e8727f3f461862d1) ([#10421](https://www.github.com/tauri-apps/tauri/pull/10421)) Changes the default behavior of the `dev` command to only expose to localhost (`127.0.0.1`) instead of the default system interface. + +### Security fixes + +- [`289ae5555`](https://www.github.com/tauri-apps/tauri/commit/289ae5555da3802741018015bfe4927729a2eb33) ([#10386](https://www.github.com/tauri-apps/tauri/pull/10386)) Re-enable TLS checks that were previously disabled to support an insecure HTTPS custom protocol on Android which is no longer used. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-rc.0` +- Upgraded to `tauri-bundler@2.0.0-rc.0` + +### Breaking Changes + +- [`758d28c8a`](https://www.github.com/tauri-apps/tauri/commit/758d28c8a2d5c9567158e339326b765f72da983e) ([#10390](https://www.github.com/tauri-apps/tauri/pull/10390)) Core plugin permissions are now prefixed with `core:`, the `core:default` permission set can now be used and the `core` plugin name is reserved. + The `tauri migrate` tool will automate the migration process, which involves prefixing all `app`, `event`, `image`, `menu`, `path`, `resources`, `tray`, `webview` and `window` permissions with `core:`. +- [`7ba67b4ac`](https://www.github.com/tauri-apps/tauri/commit/7ba67b4aca8d3f3b1aa5ad08819605029d36e6b4)([#10437](https://www.github.com/tauri-apps/tauri/pull/10437)) `ios dev` and `android dev` now uses localhost for the development server unless running on an iOS device, + which still requires connecting to the public network address. To conditionally check this on your frontend + framework's configuration you can check for the existence of the `TAURI_DEV_HOST` + environment variable instead of checking if the target is iOS or Android (previous recommendation). + +## \[2.0.0-beta.23] + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.19` + +## \[2.0.0-beta.22] + +### New Features + +- [`c734b9e3c`](https://www.github.com/tauri-apps/tauri/commit/c734b9e3cd6e5a22dfd84ec8a779c2ee9591751b) ([#10072](https://www.github.com/tauri-apps/tauri/pull/10072) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Upgraded the WiX version to 3.14 which fixes vulnerability issues and adds support for Arm targets. +- [`7c7fa0964`](https://www.github.com/tauri-apps/tauri/commit/7c7fa0964db3403037fdb9a34de2b877ddb8df1c) ([#9963](https://www.github.com/tauri-apps/tauri/pull/9963) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Added `--method` argument for `ios build` to select the export options' method. +- [`7c7fa0964`](https://www.github.com/tauri-apps/tauri/commit/7c7fa0964db3403037fdb9a34de2b877ddb8df1c) ([#9963](https://www.github.com/tauri-apps/tauri/pull/9963) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Setup iOS signing by reading `IOS_CERTIFICATE`, `IOS_CERTIFICATE_PASSWORD` and `IOS_MOBILE_PROVISION` environment variables. + +### Enhancements + +- [`c01e87ad4`](https://www.github.com/tauri-apps/tauri/commit/c01e87ad46e2a5b3fb8d018739e724ef932008d7) ([#10198](https://www.github.com/tauri-apps/tauri/pull/10198) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Enhance `tauri migrate` to also migrate variables like `appWindow`: + + ```ts + import { appWindow } from '@tauri-apps/api/window' + ``` + + will become: + + ```ts + import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow' + const appWindow = getCurrentWebviewWindow() + ``` + +### Bug Fixes + +- [`94136578b`](https://www.github.com/tauri-apps/tauri/commit/94136578bc89e4b973c471050ae9c2d83ffcb7c6) ([#10186](https://www.github.com/tauri-apps/tauri/pull/10186) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix `migrate` command, migrating incorrect permissions for `clipboard`. +- [`c01e87ad4`](https://www.github.com/tauri-apps/tauri/commit/c01e87ad46e2a5b3fb8d018739e724ef932008d7) ([#10198](https://www.github.com/tauri-apps/tauri/pull/10198) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix `tauri migrate` incorrectly migrating `@tauri-apps/api/tauri` module to just `core` and `@tauri-apps/api/window` to just `webviewWindow`. +- [`15e125996`](https://www.github.com/tauri-apps/tauri/commit/15e12599667b749c3d7cd2259e6cf7c7b5c6e2be) ([#10234](https://www.github.com/tauri-apps/tauri/pull/10234) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix cli failing to detect the correct cargo target directory when using cargo `--target-dir` flag with `tauri build` or `tauri dev` + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.18` +- Upgraded to `tauri-macos-sign@0.1.0-beta.0` +- Upgraded to `tauri-utils@2.0.0-beta.19` + +## \[2.0.0-beta.21] + +### New Features + +- [`656a64974`](https://www.github.com/tauri-apps/tauri/commit/656a64974468bc207bf39537e02ae179bdee9b83) ([#9318](https://www.github.com/tauri-apps/tauri/pull/9318)) Added a configuration option to disable hardened runtime on macOS codesign. + +### Enhancements + +- [`f44a2ec47`](https://www.github.com/tauri-apps/tauri/commit/f44a2ec47c13243d472fa08a9df8b20d8490d79f) ([#10030](https://www.github.com/tauri-apps/tauri/pull/10030)) Enhance the plugin template to include `permissions/default.toml` and default capabilities file for the example application. + +### Bug Fixes + +- [`019a74e97`](https://www.github.com/tauri-apps/tauri/commit/019a74e970958d29cf69a6f24669d603399dcbb3) ([#9931](https://www.github.com/tauri-apps/tauri/pull/9931)) Fix wrong migration of `clipboard` and `globalShortcut` modules +- [`27838365a`](https://www.github.com/tauri-apps/tauri/commit/27838365a6841b0d3fa645ba2528221d23d4aeb2) ([#10135](https://www.github.com/tauri-apps/tauri/pull/10135)) Fix parsing of cargo profile when using `--profile=` syntax. +- [`79542f4d4`](https://www.github.com/tauri-apps/tauri/commit/79542f4d4542bd97451da7605de16e8464d6a06c) ([#10039](https://www.github.com/tauri-apps/tauri/pull/10039)) Fixed an issue that prevented `tauri icon` from rendering `` nodes in SVG files. +- [`40c0f44e1`](https://www.github.com/tauri-apps/tauri/commit/40c0f44e1c74c18ed0d6c645724d650637725456) ([#9971](https://www.github.com/tauri-apps/tauri/pull/9971)) Changed the deployment target of plugin iOS Xcode project to 13.0 so it works on older iOS releases. +- [`f56cdc9e3`](https://www.github.com/tauri-apps/tauri/commit/f56cdc9e391c4d55e4d7e935203d0f891864f22d) ([#10016](https://www.github.com/tauri-apps/tauri/pull/10016)) Add missing dependency `libayatana-appindicator3.so.1` for rpm package. +- [`1601da5b5`](https://www.github.com/tauri-apps/tauri/commit/1601da5b525de05cb813002d611f22ea4217a4fb) ([#10114](https://www.github.com/tauri-apps/tauri/pull/10114)) Removed alpha channel from default icons in iOS template to comply with Apple's human interface guideline + (https://developer.apple.com/design/human-interface-guidelines/app-icons), because + transparent icons with alpha channel are not allowed, and will be rejected + upon upload to Apple appstore. + +### What's Changed + +- [`3cca5c2be`](https://www.github.com/tauri-apps/tauri/commit/3cca5c2be88bbd52139e7dda371e88510d28bc8e) ([#9924](https://www.github.com/tauri-apps/tauri/pull/9924)) Migrate to new Android buildFeatures.buildConfig format. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.17` +- Upgraded to `tauri-utils@2.0.0-beta.18` +- [`f955f7b49`](https://www.github.com/tauri-apps/tauri/commit/f955f7b4903bcea376c0a8b430736f66c8cebf56) ([#9929](https://www.github.com/tauri-apps/tauri/pull/9929)) Switch from `dirs_next` to `dirs` as `dirs_next` is now unmaintained while `dirs` is + +### Breaking Changes + +- [`911242f09`](https://www.github.com/tauri-apps/tauri/commit/911242f0928e0a2add3595fa9de27850fb875fa6) ([#9883](https://www.github.com/tauri-apps/tauri/pull/9883)) Move updater target from `bundle > targets` to a separate field `bundle > createUpdaterArtifacts` + +## \[2.0.0-beta.20] + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.16` + +## \[2.0.0-beta.19] + +### New Features + +- [`8a1ae2dea`](https://www.github.com/tauri-apps/tauri/commit/8a1ae2deaf3086e531ada25b1627f900e2e421fb)([#9843](https://www.github.com/tauri-apps/tauri/pull/9843)) Added an option to use a Xcode project for the iOS plugin instead of a plain SwiftPM project. +- [`9e4b2253f`](https://www.github.com/tauri-apps/tauri/commit/9e4b2253f6ddaccd0f5c88734287bd5c84d4936a)([#9734](https://www.github.com/tauri-apps/tauri/pull/9734)) Add `tauri bundle` subcommand which runs the bundle phase only, best paired with `tauri build --no-bundle` + +### Enhancements + +- [`8b032c3cf`](https://www.github.com/tauri-apps/tauri/commit/8b032c3cf638e64e50df9d9cf8bc789c7e285987)([#9896](https://www.github.com/tauri-apps/tauri/pull/9896)) Add a blank LaunchScreen.storyboard to the iOS project init template to pass the App Store validation. +- [`71a5e2ba2`](https://www.github.com/tauri-apps/tauri/commit/71a5e2ba24017b0b9a0e84e053167265b7eddcce)([#9799](https://www.github.com/tauri-apps/tauri/pull/9799)) On Android, allow using Kotlin keywords as identifiers and escape them in templates. +- [`9970d88be`](https://www.github.com/tauri-apps/tauri/commit/9970d88becee1560a4b2a7ffc1fe65991a42a8c9)([#9892](https://www.github.com/tauri-apps/tauri/pull/9892)) Update to latest gradle. + +### What's Changed + +- [`80aa50498`](https://www.github.com/tauri-apps/tauri/commit/80aa504987dd9cfa59aa5848c4d7960e1d58d0e6)([#9870](https://www.github.com/tauri-apps/tauri/pull/9870)) Updated Android target SDK to 34. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.15` +- Upgraded to `tauri-utils@2.0.0-beta.17` + +### Breaking Changes + +- [`265c23886`](https://www.github.com/tauri-apps/tauri/commit/265c23886ee5efbcc6d7188ff5c84cb32fa82aea)([#9375](https://www.github.com/tauri-apps/tauri/pull/9375)) Avoid renaming main binary to product name and perserve the name generated by cargo. +- [`1df5cdeb0`](https://www.github.com/tauri-apps/tauri/commit/1df5cdeb06f5464e0eec4055e21b7b7bc8739eed)([#9858](https://www.github.com/tauri-apps/tauri/pull/9858)) Use `tauri.conf.json > identifier` to set the `PackageName` in Android and `BundleId` in iOS. + +## \[2.0.0-beta.18] + +### Bug Fixes + +- [`beda18bce`](https://www.github.com/tauri-apps/tauri/commit/beda18bce95fd6e10543b2d8f1eca5fb7ca0655b)([#9855](https://www.github.com/tauri-apps/tauri/pull/9855)) Fixed an issue that caused `tauri add` to fail for multiple rust-only and platform-specific plugins. +- [`4a33bc6a6`](https://www.github.com/tauri-apps/tauri/commit/4a33bc6a62d2ed9371191c8a7f78ff3f33930455)([#9553](https://www.github.com/tauri-apps/tauri/pull/9553)) Fixes `pnpm` detection when initializing and running a mobile project. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.14` +- Upgraded to `tauri-utils@2.0.0-beta.16` + +## \[2.0.0-beta.17] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.15` +- Upgraded to `tauri-bundler@2.0.1-beta.13` + +## \[2.0.0-beta.16] + +### Bug Fixes + +- [`97ec422f2`](https://www.github.com/tauri-apps/tauri/commit/97ec422f22d069b9570931834241c7e47bc68cc3)([#9638](https://www.github.com/tauri-apps/tauri/pull/9638)) Exit `tauri icon` with non-zero code when it fails. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.14` +- Upgraded to `tauri-bundler@2.0.1-beta.12` + +## \[2.0.0-beta.15] + +### Bug Fixes + +- [`3f0805488`](https://www.github.com/tauri-apps/tauri/commit/3f0805488506e013e15cfb3cc1cfc8c2c5f84bd2)([#9603](https://www.github.com/tauri-apps/tauri/pull/9603)) Use `windows-sys` crate instead of `winapi` which fixes installing the published cli from crates.io using `cargo install tauri-cli --version "^2.0.0-beta"`. + +## \[2.0.0-beta.14] + +### Enhancements + +- [`8a63ceb4f`](https://www.github.com/tauri-apps/tauri/commit/8a63ceb4f31c422311b0f7dff173a9c8c0e1a604)([#9473](https://www.github.com/tauri-apps/tauri/pull/9473)) Ignore `.DS_Store` by default for `tauri dev` hot reloads. + +### Bug Fixes + +- [`e64b8f1dc`](https://www.github.com/tauri-apps/tauri/commit/e64b8f1dcedad3222f46755bf6f30392a7ec2f90)([#9479](https://www.github.com/tauri-apps/tauri/pull/9479)) Upgrade `heck` to v0.5 to better support Chinese and Japanese product name, because Chinese do not have word separation. +- [`aaa332c6e`](https://www.github.com/tauri-apps/tauri/commit/aaa332c6e78c956debd11efda021a0406621a01d)([#9540](https://www.github.com/tauri-apps/tauri/pull/9540)) Fix `tauri migrate` trying to migrate to a non-existing plugin. +- [`e64b8f1dc`](https://www.github.com/tauri-apps/tauri/commit/e64b8f1dcedad3222f46755bf6f30392a7ec2f90)([#9479](https://www.github.com/tauri-apps/tauri/pull/9479)) Fixed an issue causing the `build.runner` and `build.features` configs to not take effect. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.10` +- Upgraded to `tauri-utils@2.0.0-beta.13` + +## \[2.0.0-beta.13] + +### Bug Fixes + +- [`73c1c2d33`](https://www.github.com/tauri-apps/tauri/commit/73c1c2d33872651c32c761c838714b684980c668)([#9457](https://www.github.com/tauri-apps/tauri/pull/9457)) Gracefully handle Non-UTF8 files when using `tauri migrate` +- [`9331435a5`](https://www.github.com/tauri-apps/tauri/commit/9331435a50cc3769720bd2671da8510699d28671)([#9412](https://www.github.com/tauri-apps/tauri/pull/9412)) Fix `tauri info` crashing when Node.js is not installed. + +### What's Changed + +- [`8f4b1050c`](https://www.github.com/tauri-apps/tauri/commit/8f4b1050c4de0e9194680408ff3a6902b67045f8)([#9459](https://www.github.com/tauri-apps/tauri/pull/9459)) Show full expected path of `frontendDist` when if can't be found. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.9` +- Upgraded to `tauri-utils@2.0.0-beta.12` + +## \[2.0.0-beta.12] + +### New Features + +- [`93e0e1392`](https://www.github.com/tauri-apps/tauri/commit/93e0e1392ec341fcadf696c03e78f0ca1e73c941) Support specifying a version for `tauri add` subcommand, for example: `tauri add window-state@2.0.0-beta.2` + +### Enhancements + +- [`6703b7cbc`](https://www.github.com/tauri-apps/tauri/commit/6703b7cbca3ade84e534c121c63fb22c5f7abbfd)([#9310](https://www.github.com/tauri-apps/tauri/pull/9310)) Use `$CARGO_MANIFEST_DIR` when including templates at build-time. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.8` +- Upgraded to `tauri-utils@2.0.0-beta.11` + +## \[2.0.0-beta.11] + +### Enhancements + +- [`ac76a22f3`](https://www.github.com/tauri-apps/tauri/commit/ac76a22f383028d9bacdedebeb41d3fca5ec9dac)([#9183](https://www.github.com/tauri-apps/tauri/pull/9183)) Allow empty responses for `devUrl`, `beforeDevCommand` and `beforeBuildCommands` questions in `tauri init`. +- [`b525ddadf`](https://www.github.com/tauri-apps/tauri/commit/b525ddadf7e7588c3e195cf0f821c9862c545d06)([#9237](https://www.github.com/tauri-apps/tauri/pull/9237)) `openssl` is no longer a required dependency on macOS. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.7` + +## \[2.0.0-beta.10] + +### New Features + +- [`7213b9e47`](https://www.github.com/tauri-apps/tauri/commit/7213b9e47242bef814aa7257e0bf84631bf5fe7e)([#9124](https://www.github.com/tauri-apps/tauri/pull/9124)) Add default permission for a plugin to capabilities when using `tauri add `. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.10` +- Upgraded to `tauri-bundler@2.0.1-beta.6` + +## \[2.0.0-beta.9] + +### Bug Fixes + +- [`c3ea3a2b7`](https://www.github.com/tauri-apps/tauri/commit/c3ea3a2b7d2fe3085f05b63dd1feb962beb4b7b3)([#9126](https://www.github.com/tauri-apps/tauri/pull/9126)) Fix bundling when `plugins > updater > windows > installerArgs` are set in `tauri.conf.json` + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.9` +- Upgraded to `tauri-bundler@2.0.1-beta.5` + +## \[2.0.0-beta.8] + +### Enhancements + +- [`3e472d0af`](https://www.github.com/tauri-apps/tauri/commit/3e472d0afcd67545dd6d9f18d304580a3b2759a8)([#9115](https://www.github.com/tauri-apps/tauri/pull/9115)) Changed the permission and capability platforms to be optional. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.8` +- Upgraded to `tauri-bundler@2.0.1-beta.4` + +## \[2.0.0-beta.7] + +### Enhancements + +- [`c68218b36`](https://www.github.com/tauri-apps/tauri/commit/c68218b362c417b62e56c7a2b5b32c13fe035a83)([#8990](https://www.github.com/tauri-apps/tauri/pull/8990)) Add `--no-bundle` flag for `tauri build` command to skip bundling. Previously `none` was used to skip bundling, it will now be treated as invalid format and a warning will be emitted instead. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.7` +- Upgraded to `tauri-bundler@2.0.1-beta.3` +- [`4f7894176`](https://www.github.com/tauri-apps/tauri/commit/4f789417630b8a32dcf9c0daec448ea8182daca1)([#9034](https://www.github.com/tauri-apps/tauri/pull/9034)) Update dependencies, fix `log` compilation issue. + +## \[2.0.0-beta.6] + +### Bug Fixes + +- [`f5f3ed5f`](https://www.github.com/tauri-apps/tauri/commit/f5f3ed5f6faa0b51e83244acc15e9006299a03ba)([#9009](https://www.github.com/tauri-apps/tauri/pull/9009)) Fixes Android and iOS project initialization when the Tauri CLI is on a different disk partition. +- [`d7d03c71`](https://www.github.com/tauri-apps/tauri/commit/d7d03c7197212f3a5bebe08c929417d60927eb89)([#9017](https://www.github.com/tauri-apps/tauri/pull/9017)) Fixes dev watcher on mobile dev. +- [`b658ded6`](https://www.github.com/tauri-apps/tauri/commit/b658ded614cfc169228cb22ad5bfc64478dfe161)([#9015](https://www.github.com/tauri-apps/tauri/pull/9015)) Fixes truncation of existing BuildTask.kt when running `tauri android init`. + +### What's Changed + +- [`3657ad82`](https://www.github.com/tauri-apps/tauri/commit/3657ad82f88ce528551d032d521c52eed3f396b4)([#9008](https://www.github.com/tauri-apps/tauri/pull/9008)) Updates to new ACL manifest path. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.6` +- Upgraded to `tauri-bundler@2.0.1-beta.2` + +## \[2.0.0-beta.5] + +### New Features + +- [`06d63d67`](https://www.github.com/tauri-apps/tauri/commit/06d63d67a061459dd533ddcae755922427a6dfc5)([#8827](https://www.github.com/tauri-apps/tauri/pull/8827)) Add new subcommands for managing permissions and cababilities: + + - `tauri permission new` + - `tauri permission add` + - `tauri permission rm` + - `tauri permission ls` + - `tauri capability new` + +### Breaking Changes + +- [`b9e6a018`](https://www.github.com/tauri-apps/tauri/commit/b9e6a01879d9233040f3d3fab11c59e70563da7e)([#8937](https://www.github.com/tauri-apps/tauri/pull/8937)) The `custom-protocol` Cargo feature is no longer required on your application and is now ignored. To check if running on production, use `#[cfg(not(dev))]` instead of `#[cfg(feature = "custom-protocol")]`. + +### Enhancements + +- [`9be314f0`](https://www.github.com/tauri-apps/tauri/commit/9be314f07a4ca5d14433d41919492f3e91b5536a)([#8951](https://www.github.com/tauri-apps/tauri/pull/8951)) Add plugins to `Cargo.toml` when using `tauri migrate` + +### Bug Fixes + +- [`cbd9755e`](https://www.github.com/tauri-apps/tauri/commit/cbd9755e0926a7e47e59deb50f4bb93d621791a5)([#8977](https://www.github.com/tauri-apps/tauri/pull/8977)) Fixes process logs not showing on `ios dev`. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.5` +- Upgraded to `tauri-bundler@2.0.1-beta.1` + +## \[2.0.0-beta.4] + +### Bug Fixes + +- [`e538ba58`](https://www.github.com/tauri-apps/tauri/commit/e538ba586c5b8b50955586c8ef2704adb5d7cc43)([#8949](https://www.github.com/tauri-apps/tauri/pull/8949)) Fixes android and iOS process spawning not working on Node.js. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.1-beta.0` +- Upgraded to `tauri-utils@2.0.0-beta.4` + +### Breaking Changes + +- [`a76fb118`](https://www.github.com/tauri-apps/tauri/commit/a76fb118ce2de22e1bdb4216bf0ac01dfc3e5799)([#8950](https://www.github.com/tauri-apps/tauri/pull/8950)) Changed the capability format to allow configuring both `remote: { urls: Vec }` and `local: bool (default: true)` instead of choosing one on the `context` field. + +## \[2.0.0-beta.3] + +### Enhancements + +- [`a029b9f7`](https://www.github.com/tauri-apps/tauri/commit/a029b9f77e432533a403c292940fa3efba68692c)([#8910](https://www.github.com/tauri-apps/tauri/pull/8910)) Setting up code signing is no longer required on iOS when using the simulator. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.3` +- Upgraded to `tauri-bundler@2.0.0-beta.3` + +## \[2.0.0-beta.2] + +### Enhancements + +- [`83a68deb`](https://www.github.com/tauri-apps/tauri/commit/83a68deb5676d39cd4728d2e140f6b46d5f787ed)([#8797](https://www.github.com/tauri-apps/tauri/pull/8797)) Update app template following capabilities configuration change. + +### Bug Fixes + +- [`aa06a053`](https://www.github.com/tauri-apps/tauri/commit/aa06a0534cf224038866e0ddd6910ea873b2574d)([#8810](https://www.github.com/tauri-apps/tauri/pull/8810)) Fix `tauri plugin android init` printing invalid code that has a missing closing `"`. +- [`3cee26a5`](https://www.github.com/tauri-apps/tauri/commit/3cee26a58ab44639a12c7816f4096655daa327a4)([#8865](https://www.github.com/tauri-apps/tauri/pull/8865)) On Windows, fixed `tauri info` fails to detect the build tool when the system language is CJK. +- [`052e8b43`](https://www.github.com/tauri-apps/tauri/commit/052e8b4311d9f0f963a2866163be27bfd8f70c60)([#8838](https://www.github.com/tauri-apps/tauri/pull/8838)) Downgrade minisign dependency fixing updater signing key bug and prevent it from happening in the future. +- [`fb0d9971`](https://www.github.com/tauri-apps/tauri/commit/fb0d997117367e3387896bcd0fba004579475c40)([#8783](https://www.github.com/tauri-apps/tauri/pull/8783)) Fixes a regression on the `--config` argument not accepting file paths. +- [`baca704d`](https://www.github.com/tauri-apps/tauri/commit/baca704d4b5fae239fc320d10140f35bd705bfbb)([#8768](https://www.github.com/tauri-apps/tauri/pull/8768)) Do not migrate updater configuration if the active flag is set to false. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.2` +- Upgraded to `tauri-bundler@2.0.0-beta.2` + +## \[2.0.0-beta.1] + +### Enhancements + +- [`4e101f80`](https://www.github.com/tauri-apps/tauri/commit/4e101f801657e7d01ce8c22f9c6468067d0caab2)([#8756](https://www.github.com/tauri-apps/tauri/pull/8756)) Moved the capability JSON schema to the `src-tauri/gen` folder so it's easier to track changes on the `capabilities` folder. +- [`4e101f80`](https://www.github.com/tauri-apps/tauri/commit/4e101f801657e7d01ce8c22f9c6468067d0caab2)([#8756](https://www.github.com/tauri-apps/tauri/pull/8756)) Update app and plugin templates following generated files change from tauri-build and tauri-plugin. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.1` +- Upgraded to `tauri-bundler@2.0.0-beta.1` + +## \[2.0.0-beta.0] + +### New Features + +- [`7fcc0bcd`](https://www.github.com/tauri-apps/tauri/commit/7fcc0bcd3482bbc8771e330942ef6cd78cc8ec35)([#8490](https://www.github.com/tauri-apps/tauri/pull/8490)) Add plugin initialization rust code when using `tauri add` +- [`1878766f`](https://www.github.com/tauri-apps/tauri/commit/1878766f7f81a03b0f0b87ec33ee113d7aa7a902)([#8667](https://www.github.com/tauri-apps/tauri/pull/8667)) Migrate the allowlist config to the new capability file format. + +### Enhancements + +- [`d6c7568c`](https://www.github.com/tauri-apps/tauri/commit/d6c7568c27445653edf570f3969163bc358ba2ba)([#8720](https://www.github.com/tauri-apps/tauri/pull/8720)) Add `files` option to the AppImage Configuration. +- [`b3209bb2`](https://www.github.com/tauri-apps/tauri/commit/b3209bb28bb379d5046d577c7e42319d6e76ced0)([#8688](https://www.github.com/tauri-apps/tauri/pull/8688)) Ignore global `.gitignore` when searching for tauri directory. +- [`e691208e`](https://www.github.com/tauri-apps/tauri/commit/e691208e7b39bb8e3ffc9bf66cff731a5025ef16)([#7837](https://www.github.com/tauri-apps/tauri/pull/7837)) Prevent unneeded double Cargo.toml rewrite on `dev` and `build`. +- [`f492efd7`](https://www.github.com/tauri-apps/tauri/commit/f492efd7144fdd8d25cac0c4d2389a95ab75fb02)([#8666](https://www.github.com/tauri-apps/tauri/pull/8666)) Update app and plugin template following the new access control permission model. + +### Bug Fixes + +- [`9cb9aa79`](https://www.github.com/tauri-apps/tauri/commit/9cb9aa7978f231f7da238b33d6ab33fdd2d2c842)([#8672](https://www.github.com/tauri-apps/tauri/pull/8672)) Allow license field in Cargo.toml to be `{ workspace = true }` + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.0` +- Upgraded to `tauri-bundler@2.0.0-beta.0` + +### Breaking Changes + +- [`8de308d1`](https://www.github.com/tauri-apps/tauri/commit/8de308d1bf6a855d7a26af58bd0e744938ba47d8)([#8723](https://www.github.com/tauri-apps/tauri/pull/8723)) Restructured Tauri config per [RFC#5](https://github.com/tauri-apps/rfcs/blob/f3e82a6b0c5390401e855850d47dc7b7d9afd684/texts/0005-tauri-config-restructure.md): + + - Moved `package.productName`, `package.version` and `tauri.bundle.identifier` fields to the top-level. + - Removed `package` object. + - Renamed `tauri` object to `app`. + - Moved `tauri.bundle` object to the top-level. + - Renamed `build.distDir` field to `frontendDist`. + - Renamed `build.devPath` field to `devUrl` and will no longer accepts paths, it will only accept URLs. + - Moved `tauri.pattern` to `app.security.pattern`. + - Removed `tauri.bundle.updater` object, and its fields have been moved to the updater plugin under `plugins.updater` object. + - Moved `build.withGlobalTauri` to `app.withGlobalTauri`. + - Moved `tauri.bundle.dmg` object to `bundle.macOS.dmg`. + - Moved `tauri.bundle.deb` object to `bundle.linux.deb`. + - Moved `tauri.bundle.appimage` object to `bundle.linux.appimage`. + - Removed all license fields from each bundle configuration object and instead added `bundle.license` and `bundle.licenseFile`. + - Renamed `AppUrl` to `FrontendDist` and refactored its variants to be more explicit. + +## \[2.0.0-alpha.21] + +### New Features + +- [`27bad32d`](https://www.github.com/tauri-apps/tauri/commit/27bad32d4d4acca8155b20225d529d540fb9aaf4)([#7798](https://www.github.com/tauri-apps/tauri/pull/7798)) Add `files` object on the `tauri > bundle > macOS` configuration option. +- [`0ec28c39`](https://www.github.com/tauri-apps/tauri/commit/0ec28c39f478de7199a66dd75e8642e1aa1344e6)([#8529](https://www.github.com/tauri-apps/tauri/pull/8529)) Include tauri-build on the migration script. + +### Enhancements + +- [`091100ac`](https://www.github.com/tauri-apps/tauri/commit/091100acbb507b51de39fb1446f685926f888fd2)([#5202](https://www.github.com/tauri-apps/tauri/pull/5202)) Add RPM packaging + +### Bug Fixes + +- [`4f73057e`](https://www.github.com/tauri-apps/tauri/commit/4f73057e6fd4c137bc112367fb91f5fc0c8a39f6)([#8486](https://www.github.com/tauri-apps/tauri/pull/8486)) Prevent `Invalid target triple` warnings and correctly set `TAURI_ENV_` vars when target triple contains 4 components. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.0-alpha.14` +- Upgraded to `tauri-utils@2.0.0-alpha.13` + +### Breaking Changes + +- [`4f73057e`](https://www.github.com/tauri-apps/tauri/commit/4f73057e6fd4c137bc112367fb91f5fc0c8a39f6)([#8486](https://www.github.com/tauri-apps/tauri/pull/8486)) Removed `TAURI_ENV_PLATFORM_TYPE` which will not be set for CLI hook commands anymore, use `TAURI_ENV_PLATFORM` instead. Also Changed value of `TAURI_ENV_PLATFORM` and `TAURI_ENV_ARCH` values to match the target triple more accurately: + + - `darwin` and `androideabi` are no longer replaced with `macos` and `android` in `TAURI_ENV_PLATFORM`. + - `i686` and `i586` are no longer replaced with `x86` in `TAURI_ENV_ARCH`. + +## \[2.0.0-alpha.20] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-alpha.12` +- Upgraded to `tauri-bundler@2.0.0-alpha.13` + +## \[2.0.0-alpha.19] + +### Enhancements + +- [`803c3a79`](https://www.github.com/tauri-apps/tauri/commit/803c3a794d96c1f0b3361ca98f56e8bcf5038ede)([#8327](https://www.github.com/tauri-apps/tauri/pull/8327)) 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` + +## \[2.0.0-alpha.18] + +### New Features + +- [`50f7ccbb`](https://www.github.com/tauri-apps/tauri/commit/50f7ccbbf3467f33cc7dd1cca53125fec6eda1c6)([#6444](https://www.github.com/tauri-apps/tauri/pull/6444)) Add suport to SVG input image for the `tauri icon` command. +- [`25e5f91d`](https://www.github.com/tauri-apps/tauri/commit/25e5f91dae7fe2bbc1ba4317d5d829402bfd1d50)([#8200](https://www.github.com/tauri-apps/tauri/pull/8200)) Merge `src-tauri/Info.plist` and `src-tauri/Info.ios.plist` with the iOS project plist file. + +### Enhancements + +- [`01a7a983`](https://www.github.com/tauri-apps/tauri/commit/01a7a983aba2946b455a608b8a6a4b08cb25fc11)([#8128](https://www.github.com/tauri-apps/tauri/pull/8128)) Transform paths to relative to the mobile project for the IDE script runner script. + +### Bug Fixes + +- [`88dac86f`](https://www.github.com/tauri-apps/tauri/commit/88dac86f3b301d1919df6473a9e20f46b560f29b)([#8149](https://www.github.com/tauri-apps/tauri/pull/8149)) Ensure `tauri add` prints `rust_code` with plugin name in snake case. +- [`977d0e52`](https://www.github.com/tauri-apps/tauri/commit/977d0e52f14b1ad01c86371765ef25b36572459e)([#8202](https://www.github.com/tauri-apps/tauri/pull/8202)) Fixes `android build --open` and `ios build --open` IDE failing to read CLI options. +- [`bfbbefdb`](https://www.github.com/tauri-apps/tauri/commit/bfbbefdb9e13ed1f42f6db7fa9ceaa84db1267e9)([#8161](https://www.github.com/tauri-apps/tauri/pull/8161)) Fix invalid plugin template. +- [`92b50a3a`](https://www.github.com/tauri-apps/tauri/commit/92b50a3a398c9d55b6992a8f5c2571e4d72bdaaf)([#8209](https://www.github.com/tauri-apps/tauri/pull/8209)) Added support to Xcode's archive. This requires regenerating the Xcode project. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.0-alpha.12` +- Upgraded to `tauri-utils@2.0.0-alpha.11` + +## \[2.0.0-alpha.17] + +### Enhancements + +- [`c6c59cf2`](https://www.github.com/tauri-apps/tauri/commit/c6c59cf2373258b626b00a26f4de4331765dd487) Pull changes from Tauri 1.5 release. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.0-alpha.11` +- Upgraded to `tauri-utils@2.0.0-alpha.10` + +### Breaking Changes + +- [`198abe3c`](https://www.github.com/tauri-apps/tauri/commit/198abe3c2cae06dacab860b3a93f715dcf529a95)([#8076](https://www.github.com/tauri-apps/tauri/pull/8076)) Updated the mobile plugin templates following the tauri v2.0.0-alpha.17 changes. + +## \[2.0.0-alpha.16] + +### New Features + +- [`8b166e9b`](https://www.github.com/tauri-apps/tauri/commit/8b166e9bf82e69ddb3200a3a825614980bd8d433)([#7949](https://www.github.com/tauri-apps/tauri/pull/7949)) Add `--no-dev-server-wait` option to skip waiting for the dev server to start when using `tauri dev`. +- [`880266a7`](https://www.github.com/tauri-apps/tauri/commit/880266a7f697e1fe58d685de3bb6836ce5251e92)([#8031](https://www.github.com/tauri-apps/tauri/pull/8031)) Bump the MSRV to 1.70. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-alpha.9` +- Upgraded to `tauri-bundler@2.0.0-alpha.10` + +### Breaking Changes + +- [`8b166e9b`](https://www.github.com/tauri-apps/tauri/commit/8b166e9bf82e69ddb3200a3a825614980bd8d433)([#7949](https://www.github.com/tauri-apps/tauri/pull/7949)) Changed a number of environment variables used by tauri CLI for consistency and clarity: + + - `TAURI_PRIVATE_KEY` -> `TAURI_SIGNING_PRIVATE_KEY` + - `TAURI_KEY_PASSWORD` -> `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` + - `TAURI_SKIP_DEVSERVER_CHECK` -> `TAURI_CLI_NO_DEV_SERVER_WAIT` + - `TAURI_DEV_SERVER_PORT` -> `TAURI_CLI_PORT` + - `TAURI_PATH_DEPTH` -> `TAURI_CLI_CONFIG_DEPTH` + - `TAURI_FIPS_COMPLIANT` -> `TAURI_BUNDLER_WIX_FIPS_COMPLIANT` + - `TAURI_DEV_WATCHER_IGNORE_FILE` -> `TAURI_CLI_WATCHER_IGNORE_FILENAME` + - `TAURI_TRAY` -> `TAURI_LINUX_AYATANA_APPINDICATOR` + - `TAURI_APPLE_DEVELOPMENT_TEAM` -> `APPLE_DEVELOPMENT_TEAM` +- [`4caa1cca`](https://www.github.com/tauri-apps/tauri/commit/4caa1cca990806f2c2ef32d5dabaf56e82f349e6)([#7990](https://www.github.com/tauri-apps/tauri/pull/7990)) The `tauri plugin` subcommand is receving a couple of consitency and quality of life improvements: + + - Renamed `tauri plugin android/ios add` command to `tauri plugin android/ios init` to match the `tauri plugin init` command. + - Removed the `-n/--name` argument from the `tauri plugin init`, `tauri plugin android/ios init`, and is now parsed from the first positional argument. + - Added `tauri plugin new` to create a plugin in a new directory. + - Changed `tauri plugin init` to initalize a plugin in an existing directory (defaults to current directory) instead of creating a new one. + - Changed `tauri plugin init` to NOT generate mobile projects by default, you can opt-in to generate them using `--android` and `--ios` flags or `--mobile` flag or initalize them later using `tauri plugin android/ios init`. +- [`8b166e9b`](https://www.github.com/tauri-apps/tauri/commit/8b166e9bf82e69ddb3200a3a825614980bd8d433)([#7949](https://www.github.com/tauri-apps/tauri/pull/7949)) Removed checking for a new version of the CLI. +- [`ebcc21e4`](https://www.github.com/tauri-apps/tauri/commit/ebcc21e4b95f4e8c27639fb1bca545b432f52d5e)([#8057](https://www.github.com/tauri-apps/tauri/pull/8057)) Renamed the beforeDevCommand, beforeBuildCommand and beforeBundleCommand hooks environment variables from `TAURI_PLATFORM, TAURI_ARCH, TAURI_FAMILY, TAURI_PLATFORM_VERSION, TAURI_PLATFORM_TYPE and TAURI_DEBUG` to `TAURI_ENV_PLATFORM, TAURI_ENV_ARCH, TAURI_ENV_FAMILY, TAURI_ENV_PLATFORM_VERSION, TAURI_ENV_PLATFORM_TYPE and TAURI_ENV_DEBUG` to differentiate the prefix with other CLI environment variables. + +## \[2.0.0-alpha.15] + +### New Features + +- [`b2f17723`](https://www.github.com/tauri-apps/tauri/commit/b2f17723a415f04c2620132a6305eb138d7cb47f)([#7971](https://www.github.com/tauri-apps/tauri/pull/7971)) Use `devicectl` to connect to iOS 17+ devices on macOS 14+. + +### Bug Fixes + +- [`100d9ede`](https://www.github.com/tauri-apps/tauri/commit/100d9ede35995d9db21d2087dd5606adfafb89a5)([#7802](https://www.github.com/tauri-apps/tauri/pull/7802)) Properly read platform-specific configuration files for mobile targets. +- [`228e5a4c`](https://www.github.com/tauri-apps/tauri/commit/228e5a4c76ad5f97409c912d07699b49ba4bb162)([#7902](https://www.github.com/tauri-apps/tauri/pull/7902)) Fixes `icon` command not writing files to the correct Android project folders. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-alpha.8` +- Upgraded to `tauri-bundler@2.0.0-alpha.9` + +## \[2.0.0-alpha.14] + +### Breaking Changes + +- [`d5074af5`](https://www.github.com/tauri-apps/tauri/commit/d5074af562b2b5cb6c5711442097c4058af32db6)([#7801](https://www.github.com/tauri-apps/tauri/pull/7801)) The custom protocol on Android now uses the `http` scheme instead of `https`. + +## \[2.0.0-alpha.13] + +### Breaking Changes + +- [`4cb51a2d`](https://www.github.com/tauri-apps/tauri/commit/4cb51a2d56cfcae0749062c79ede5236bd8c02c2)([#7779](https://www.github.com/tauri-apps/tauri/pull/7779)) The custom protocol on Windows now uses the `http` scheme instead of `https`. +- [`974e38b4`](https://www.github.com/tauri-apps/tauri/commit/974e38b4ddc198530aa977ec77d513b76013b9f3)([#7744](https://www.github.com/tauri-apps/tauri/pull/7744)) Renamed the `plugin add` command to `add`. + +## \[2.0.0-alpha.12] + +### Bug Fixes + +- [`b75a1210`](https://www.github.com/tauri-apps/tauri/commit/b75a1210bed589187678861d7314ae6279bf7c87)([#7762](https://www.github.com/tauri-apps/tauri/pull/7762)) Fixes a regression on alpha.11 where iOS logs aren't being displayed when using `ios dev` with a real device. +- [`8faa5a4a`](https://www.github.com/tauri-apps/tauri/commit/8faa5a4a1238a44ca7b54d2084aaed553ac2a1ba)([#7765](https://www.github.com/tauri-apps/tauri/pull/7765)) Ensure asset directory exists on the iOS project. + +### Dependencies + +- Upgraded to `tauri-bundler@2.0.0-alpha.8` + +## \[2.0.0-alpha.11] + +### New Features + +- [`522de0e7`](https://www.github.com/tauri-apps/tauri/commit/522de0e78891d0bdf6387a5118985fc41a11baeb)([#7447](https://www.github.com/tauri-apps/tauri/pull/7447)) Expose an environment variable `TAURI_${PLUGIN_NAME}_PLUGIN_CONFIG` for each defined plugin configuration object. +- [`c7dacca4`](https://www.github.com/tauri-apps/tauri/commit/c7dacca4661c6ddf937c1a3dd3ace896d5baf40c)([#7446](https://www.github.com/tauri-apps/tauri/pull/7446)) Expose the `TAURI_IOS_PROJECT_PATH` and `TAURI_IOS_APP_NAME` environment variables when using `ios` commands. +- [`aa94f719`](https://www.github.com/tauri-apps/tauri/commit/aa94f7197e4345a7cab1617272b10895859674f9)([#7445](https://www.github.com/tauri-apps/tauri/pull/7445)) Generate empty entitlements file for the iOS project. +- [`d010bc07`](https://www.github.com/tauri-apps/tauri/commit/d010bc07b81116bef769b64cdc19b23dff762d48)([#7554](https://www.github.com/tauri-apps/tauri/pull/7554)) Set the iOS project PRODUCT_NAME value to the string under `tauri.conf.json > package > productName` if it is set. +- [`8af24974`](https://www.github.com/tauri-apps/tauri/commit/8af2497496f11ee481472dfdc3125757346c1a3e)([#7561](https://www.github.com/tauri-apps/tauri/pull/7561)) The `migrate` command now automatically reads all JavaScript files and updates `@tauri-apps/api` import paths and install the missing plugins. + +### Enhancements + +- [`fbeb5b91`](https://www.github.com/tauri-apps/tauri/commit/fbeb5b9185baeda19e865228179e3e44c165f1d9)([#7170](https://www.github.com/tauri-apps/tauri/pull/7170)) Update migrate command to update the configuration CSP to include `ipc:` on the `connect-src` directive, needed by the new IPC using custom protocols. + +### Bug Fixes + +- [`5eb85543`](https://www.github.com/tauri-apps/tauri/commit/5eb85543313eaf7ca2289898652b1b11dc776608)([#7282](https://www.github.com/tauri-apps/tauri/pull/7282)) Fix `tauri info` failing when there is no available iOS code signing certificate. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-alpha.7` +- Upgraded to `tauri-bundler@2.0.0-alpha.7` + +## \[2.0.0-alpha.10] + +### New Features + +- [`7e5905ae`](https://www.github.com/tauri-apps/tauri/commit/7e5905ae1d56b920de0e821be28036cbbe302518)([#7023](https://www.github.com/tauri-apps/tauri/pull/7023)) Added `tauri plugin add` command to add a plugin to the Tauri project. +- [`b0f94775`](https://www.github.com/tauri-apps/tauri/commit/b0f947752a315b7b89c5979de50157f997f1dd6e)([#7008](https://www.github.com/tauri-apps/tauri/pull/7008)) Added `migrate` command. + +### Enhancements + +- [`aa6c9164`](https://www.github.com/tauri-apps/tauri/commit/aa6c9164e63b5316d690f25b1c118f1b12310570)([#7007](https://www.github.com/tauri-apps/tauri/pull/7007)) Don't build library files when building desktop targets. +- [`a28fdf7e`](https://www.github.com/tauri-apps/tauri/commit/a28fdf7ec71bf6db2498569004de83318b6d25ac)([#7044](https://www.github.com/tauri-apps/tauri/pull/7044)) Skip Rust target installation if they are already installed. +- [`735db1ce`](https://www.github.com/tauri-apps/tauri/commit/735db1ce839a16ba998c9e6786c441e3bf6c90b3)([#7044](https://www.github.com/tauri-apps/tauri/pull/7044)) Add `--skip-targets-install` flag for `tauri android init` and `tauri ios init` to skip installing needed rust targets vie rustup. + +### Bug Fixes + +- [`3f4c4ce8`](https://www.github.com/tauri-apps/tauri/commit/3f4c4ce88b071e4e59f03887beb4dfe76f66e11b)([#7028](https://www.github.com/tauri-apps/tauri/pull/7028)) Fix `--split-per-abi` not building any targets unless specified by `--target` flag. +- [`1ed2600d`](https://www.github.com/tauri-apps/tauri/commit/1ed2600da67715908af857255305eaeb293d8791)([#6771](https://www.github.com/tauri-apps/tauri/pull/6771)) Set current directory to tauri directory before reading config file. +- [`4847b87b`](https://www.github.com/tauri-apps/tauri/commit/4847b87b1067dd8c6e73986059f51e6eee1f1121)([#7209](https://www.github.com/tauri-apps/tauri/pull/7209)) Fix `tauri (android|ios) (dev|build)` failing when using `npx tauri` +- [`655c714e`](https://www.github.com/tauri-apps/tauri/commit/655c714e4100f69d4265c5ea3f08f5bc11709446)([#7240](https://www.github.com/tauri-apps/tauri/pull/7240)) Fixes panic when exiting the `ios dev` command with Ctrl + C. +- [`6252380f`](https://www.github.com/tauri-apps/tauri/commit/6252380f4447c66913b0f06611e5949005b1eec2)([#7241](https://www.github.com/tauri-apps/tauri/pull/7241)) Exit `beforeDevCommand` process if the android or iOS `dev` command fails. + +## \[2.0.0-alpha.9] + +- [`19cd0e49`](https://www.github.com/tauri-apps/tauri/commit/19cd0e49603ad3500cd2180bfa16e1649e3a771a)([#6811](https://www.github.com/tauri-apps/tauri/pull/6811)) Add `key.properties` file to android's `.gitignore`. +- [`124d5c5a`](https://www.github.com/tauri-apps/tauri/commit/124d5c5adf67f0b68d2e41c7ddb07d9cb63f1996)([#6788](https://www.github.com/tauri-apps/tauri/pull/6788)) On mobile, fix regression introduced in `tauri-cli` version `2.0.0-alpha.3` where library not found error was thrown. +- [`31444ac1`](https://www.github.com/tauri-apps/tauri/commit/31444ac196add770f2ad18012d7c18bce7538f22)([#6725](https://www.github.com/tauri-apps/tauri/pull/6725)) Update mobile template to `wry@0.28` +- [`6d1fa49f`](https://www.github.com/tauri-apps/tauri/commit/6d1fa49fce3a03965ce7c656390e682ce5b776e3)([#6881](https://www.github.com/tauri-apps/tauri/pull/6881)) Clear Android plugin JSON file before building Rust library to ensure removed plugins are propagated to the Android project. +- [`5a9307d1`](https://www.github.com/tauri-apps/tauri/commit/5a9307d11c1643221bc2a280feb00024f8fa6030)([#6890](https://www.github.com/tauri-apps/tauri/pull/6890)) Update android template to gradle 8.0 +- [`73c803a5`](https://www.github.com/tauri-apps/tauri/commit/73c803a561181137f20366f5d52511392a619f2b)([#6837](https://www.github.com/tauri-apps/tauri/pull/6837)) Inject Tauri configuration in the Android assets. +- [`e1e85dc2`](https://www.github.com/tauri-apps/tauri/commit/e1e85dc2a5f656fc37867e278cae8042037740ac)([#6925](https://www.github.com/tauri-apps/tauri/pull/6925)) Moved the updater configuration to the `BundleConfig`. +- [`3188f376`](https://www.github.com/tauri-apps/tauri/commit/3188f3764978c6d1452ee31d5a91469691e95094)([#6883](https://www.github.com/tauri-apps/tauri/pull/6883)) Bump the MSRV to 1.65. +- [`2969d1cb`](https://www.github.com/tauri-apps/tauri/commit/2969d1cbba39301f9cc611d9f7d7051d80eef846)([#6773](https://www.github.com/tauri-apps/tauri/pull/6773)) Use absolute path to each Android plugin project instead of copying the files to enhance developer experience. +- [`d48aaa15`](https://www.github.com/tauri-apps/tauri/commit/d48aaa150a1ceeb65ec0ba18f1e3795f70c838e3)([#6894](https://www.github.com/tauri-apps/tauri/pull/6894)) Add Cargo manifest files for the plugin example templates. +- [`e1e85dc2`](https://www.github.com/tauri-apps/tauri/commit/e1e85dc2a5f656fc37867e278cae8042037740ac)([#6925](https://www.github.com/tauri-apps/tauri/pull/6925)) Removed the allowlist configuration. + +## \[2.0.0-alpha.8] + +- Do not gitignore the Android project's `buildSrc` folder by default since we removed absolute paths from it. + - [ee2d3b97](https://www.github.com/tauri-apps/tauri/commit/ee2d3b971df6d3630b8d935394fb4febcfa3a909) fix(cli): remove buildSrc from Android project gitignored paths ([#6702](https://www.github.com/tauri-apps/tauri/pull/6702)) on 2023-04-13 +- Fixes iOS build script using the wrong path for the app library file. + - [abc5f91f](https://www.github.com/tauri-apps/tauri/commit/abc5f91fa3569efc9dfdee46d1c501eda8755944) fix(cli): iOS Xcode script using incorrect library path ([#6699](https://www.github.com/tauri-apps/tauri/pull/6699)) on 2023-04-13 + +## \[2.0.0-alpha.7] + +- Add `--release` flag for `tauri android dev` however you will need to sign your Android app, see https://next--tauri.netlify.app/next/guides/distribution/sign-android + - [63f088e5](https://www.github.com/tauri-apps/tauri/commit/63f088e5fc9701fd7fb329dad7ffb27a2d8fd5aa) feat(cli): add `--release` for `android dev` ([#6638](https://www.github.com/tauri-apps/tauri/pull/6638)) on 2023-04-05 +- Build only specified rust targets for `tauri android build` instead of all. + - [d03e47d1](https://www.github.com/tauri-apps/tauri/commit/d03e47d141c3917520975be9081775dbc4e9d4fd) fix: only build specified rust targets for aab/apk build ([#6625](https://www.github.com/tauri-apps/tauri/pull/6625)) on 2023-04-05 +- Use local ip address for built-in dev server on mobile. + - [7fec0f08](https://www.github.com/tauri-apps/tauri/commit/7fec0f083c932dc63ccb8716080d97e2ab985b25) fix(cli): use local ip addr for built-in server on mobile, closes [#6454](https://www.github.com/tauri-apps/tauri/pull/6454) ([#6631](https://www.github.com/tauri-apps/tauri/pull/6631)) on 2023-04-04 +- Change minimum Android SDK version to 21 for the plugin library. + - [db4c9dc6](https://www.github.com/tauri-apps/tauri/commit/db4c9dc655e07ee2184fe04571f500f7910890cd) feat(core): add option to configure Android's minimum SDK version ([#6651](https://www.github.com/tauri-apps/tauri/pull/6651)) on 2023-04-07 +- Readd the Cargo.toml file to the plugin template. + - [5288a386](https://www.github.com/tauri-apps/tauri/commit/5288a386f1bf8ac11f991350463c3f5c20983f43) fix(cli): readd Cargo.toml to the plugin template ([#6637](https://www.github.com/tauri-apps/tauri/pull/6637)) on 2023-04-04 + +## \[2.0.0-alpha.6] + +- Use Ubuntu 20.04 to compile the CLI for cargo-binstall, increasing the minimum libc required. +- Automatically enable the `rustls-tls` tauri feature on mobile and `native-tls` on desktop if `rustls-tls` is not enabled. + - [cfdee00f](https://www.github.com/tauri-apps/tauri/commit/cfdee00f2b1455a9719bc44823fdaeabbe4c1cb2) refactor(core): fix tls features, use rustls on mobile ([#6591](https://www.github.com/tauri-apps/tauri/pull/6591)) on 2023-03-30 + +## \[2.0.0-alpha.5] + +- Fixes the iOS project script to build the Rust library. + - [6e3e4c22](https://www.github.com/tauri-apps/tauri/commit/6e3e4c22be51500bec7856d90dcb2e40ef7fe1b4) fix(cli): use correct variable on script to build Rust iOS code ([#6581](https://www.github.com/tauri-apps/tauri/pull/6581)) on 2023-03-29 +- Fix `tauri android build/dev` crashing when used with standalone `pnpm` executable on Windows. + - [39df2c98](https://www.github.com/tauri-apps/tauri/commit/39df2c982e5e2ee8617b40f829a2f2e4abfce412) fix(cli/android): fallback to `${program}.cmd` ([#6576](https://www.github.com/tauri-apps/tauri/pull/6576)) on 2023-03-29 + +## \[2.0.0-alpha.4] + +- Fix android project build crashing when using `pnpm` caused by extra `--`. + - [c787f749](https://www.github.com/tauri-apps/tauri/commit/c787f749de01b79d891615aad8c37b23037fff4c) fix(cli): only add `--` to generated android template for npm ([#6508](https://www.github.com/tauri-apps/tauri/pull/6508)) on 2023-03-21 +- Fixes the Android build gradle plugin implementation on Windows. + - [00241fa9](https://www.github.com/tauri-apps/tauri/commit/00241fa92d104870068a701519340633cc35b716) fix(cli): append .cmd on the gradle plugin binary on Windows, fix [#6502](https://www.github.com/tauri-apps/tauri/pull/6502) ([#6503](https://www.github.com/tauri-apps/tauri/pull/6503)) on 2023-03-21 + +## \[2.0.0-alpha.3] + +- Added `plugin android add` and `plugin ios add` commands to add mobile plugin functionality to existing projects. + - [14d03d42](https://www.github.com/tauri-apps/tauri/commit/14d03d426e86d966950a790926c04560c76203b3) refactor(cli): enhance plugin commands for mobile ([#6289](https://www.github.com/tauri-apps/tauri/pull/6289)) on 2023-02-16 +- Add `--port` to specify the port used for static files dev server. It can also be specified through `TAURI_DEV_SERVER_PORT` env var. + - [b7a2ce2c](https://www.github.com/tauri-apps/tauri/commit/b7a2ce2c633c8383851ec9ec3c2cafda39f19745) feat(cli): add --port, closes [#6186](https://www.github.com/tauri-apps/tauri/pull/6186) ([#6283](https://www.github.com/tauri-apps/tauri/pull/6283)) on 2023-03-16 +- Auto select an external IP for mobile development and fallback to prompting the user. Use `--force-ip-prompt` to force prompting. + - [ec007ef0](https://www.github.com/tauri-apps/tauri/commit/ec007ef0d0852d8ee6e3247049916285c98a945f) feat: use `local_ip()` and fallback to prompt ([#6290](https://www.github.com/tauri-apps/tauri/pull/6290)) on 2023-02-16 + - [4d090744](https://www.github.com/tauri-apps/tauri/commit/4d09074454068ae282ccd4670ae0b1df30510a3a) feat(cli): add `--force-ip-prompt` ([#6406](https://www.github.com/tauri-apps/tauri/pull/6406)) on 2023-03-16 +- Add commands to add native Android and iOS functionality to plugins. + - [05dad087](https://www.github.com/tauri-apps/tauri/commit/05dad0876842e2a7334431247d49365cee835d3e) feat: initial work for iOS plugins ([#6205](https://www.github.com/tauri-apps/tauri/pull/6205)) on 2023-02-11 +- In mobile commands, correctly detect when nodejs binary has the version in its name, for example `node-18` + - [58d4709f](https://www.github.com/tauri-apps/tauri/commit/58d4709f7eba07e77d3afd59c0df47c085df9e2c) fix: update nodejs detection in mobile commands ([#6451](https://www.github.com/tauri-apps/tauri/pull/6451)) on 2023-03-16 +- Use temp file instead of environment variable to pass CLI IPC websocket address to the IDE. + - [894a8d06](https://www.github.com/tauri-apps/tauri/commit/894a8d060c12a482a0fc5b3714f3848189b809de) refactor(cli): use temp file to communicate IPC websocket address ([#6219](https://www.github.com/tauri-apps/tauri/pull/6219)) on 2023-02-08 +- Change the Android template to enable minification on release and pull ProGuard rules from proguard-tauri.pro. + - [bef4ef51](https://www.github.com/tauri-apps/tauri/commit/bef4ef51bc2c633b88db121c2087a38dddb7d6bf) feat(android): enable minify on release, add proguard rules ([#6257](https://www.github.com/tauri-apps/tauri/pull/6257)) on 2023-02-13 +- Print an error if the Android project was generated with an older bundle identifier or package name. + - [79eb0542](https://www.github.com/tauri-apps/tauri/commit/79eb054292b04bb089a0e4df401a5986b33b691e) feat(cli): handle Android package identifier change ([#6314](https://www.github.com/tauri-apps/tauri/pull/6314)) on 2023-02-19 +- Fixes the generated mobile build script when using an NPM runner. + - [62f15265](https://www.github.com/tauri-apps/tauri/commit/62f152659204ce1218178596f463f0bcfbd4e6dc) fix(cli): generate build script using NPM runner if it was used ([#6233](https://www.github.com/tauri-apps/tauri/pull/6233)) on 2023-02-10 +- Resolve Android package name from single word bundle identifiers. + - [60a8b07d](https://www.github.com/tauri-apps/tauri/commit/60a8b07dc7c56c9c45331cb57d9afb410e7eadf3) fix: handle single word bundle identifier when resolving Android domain ([#6313](https://www.github.com/tauri-apps/tauri/pull/6313)) on 2023-02-19 +- Update Android project template with fix to crash on orientation change. + - [947eb391](https://www.github.com/tauri-apps/tauri/commit/947eb391ca41cebdb11abd9ffaec642baffbf44a) fix(android): crash on orientation change due to activity recreation ([#6261](https://www.github.com/tauri-apps/tauri/pull/6261)) on 2023-02-13 +- Added `--ios-color` option to the `tauri icon` command. + - [67755425](https://www.github.com/tauri-apps/tauri/commit/677554257e40e05b1af0dd61c982d6be8a8a033c) feat(cli): add `--ios-color` option to set iOS icon background color ([#6247](https://www.github.com/tauri-apps/tauri/pull/6247)) on 2023-02-12 +- Fixes HMR on mobile when devPath is configured to load a filesystem path. + - [4a82da29](https://www.github.com/tauri-apps/tauri/commit/4a82da2919e0564ec993b2005dc65b5b49407b36) fix(cli): use local ip address for reload ([#6285](https://www.github.com/tauri-apps/tauri/pull/6285)) on 2023-02-16 +- Ignore the `gen` folder on the dev watcher. + - [cab4ff95](https://www.github.com/tauri-apps/tauri/commit/cab4ff95b98aeac88401c1fed2d8b8940e4180cb) fix(cli): ignore the `gen` folder on the dev watcher ([#6232](https://www.github.com/tauri-apps/tauri/pull/6232)) on 2023-02-09 +- Correctly pass arguments from `npm run` to `tauri`. + - [1b343bd1](https://www.github.com/tauri-apps/tauri/commit/1b343bd11686f47f24a87298d8192097c66250f6) fix(cli): use `npm run tauri -- foo` for correctly passing args to tauri ([#6448](https://www.github.com/tauri-apps/tauri/pull/6448)) on 2023-03-16 +- Changed the `--api` flag on `plugin init` to `--no-api`. + - [14d03d42](https://www.github.com/tauri-apps/tauri/commit/14d03d426e86d966950a790926c04560c76203b3) refactor(cli): enhance plugin commands for mobile ([#6289](https://www.github.com/tauri-apps/tauri/pull/6289)) on 2023-02-16 + +## \[2.0.0-alpha.2] + +- Fixes `TAURI_*` environment variables for hook scripts on mobile commands. + - [1af9be90](https://www.github.com/tauri-apps/tauri/commit/1af9be904a309138b9f79dc741391000b1652c75) feat(cli): properly fill target for TAURI\_ env vars on mobile ([#6116](https://www.github.com/tauri-apps/tauri/pull/6116)) on 2023-01-23 +- Force colored logs on mobile commands. + - [2c4a0bbd](https://www.github.com/tauri-apps/tauri/commit/2c4a0bbd1fbe15d7500264e6490772397e1917ed) feat(cli): force colored logs on mobile commands ([#5934](https://www.github.com/tauri-apps/tauri/pull/5934)) on 2022-12-28 +- Keep the process alive even when the iOS application is closed. + - [dee9460f](https://www.github.com/tauri-apps/tauri/commit/dee9460f9c9bc92e9c638e7691e616849ac2085b) feat: keep CLI alive when iOS app exits, show logs, closes [#5855](https://www.github.com/tauri-apps/tauri/pull/5855) ([#5902](https://www.github.com/tauri-apps/tauri/pull/5902)) on 2022-12-27 +- Show all application logs on iOS. + - [dee9460f](https://www.github.com/tauri-apps/tauri/commit/dee9460f9c9bc92e9c638e7691e616849ac2085b) feat: keep CLI alive when iOS app exits, show logs, closes [#5855](https://www.github.com/tauri-apps/tauri/pull/5855) ([#5902](https://www.github.com/tauri-apps/tauri/pull/5902)) on 2022-12-27 +- Print log output for all tags on Android development. + - [8cc11149](https://www.github.com/tauri-apps/tauri/commit/8cc111494d74161e489152e52191e1442dd99759) fix(cli): print Android logs for all tags on 2023-01-17 +- Add support to custom and kebab case library names for mobile apps. + - [50f6dd87](https://www.github.com/tauri-apps/tauri/commit/50f6dd87b1ac2c99f8794b055f1acba4ef7d34d3) feat: improvements to support hyphens in crate name ([#5989](https://www.github.com/tauri-apps/tauri/pull/5989)) on 2023-01-06 +- Bump the MSRV to 1.64. + - [7eb9aa75](https://www.github.com/tauri-apps/tauri/commit/7eb9aa75cfd6a3176d3f566fdda02d88aa529b0f) Update gtk to 0.16 ([#6155](https://www.github.com/tauri-apps/tauri/pull/6155)) on 2023-01-30 +- Fix target directory detection when compiling for Android. + - [e873bae0](https://www.github.com/tauri-apps/tauri/commit/e873bae09f0f27517f720a753f51c1dcb903f883) fix(cli): Cargo target dir detection on Android, closes [#5865](https://www.github.com/tauri-apps/tauri/pull/5865) ([#5932](https://www.github.com/tauri-apps/tauri/pull/5932)) on 2022-12-28 + +## \[2.0.0-alpha.1] + +- Fixes running on device using Xcode 14. + - [1e4a6758](https://www.github.com/tauri-apps/tauri/commit/1e4a675843c486bddc11292d09fb766e98758514) fix(cli): run on iOS device on Xcode 14 ([#5807](https://www.github.com/tauri-apps/tauri/pull/5807)) on 2022-12-12 +- Improve local IP address detection with user selection. + - [76204b89](https://www.github.com/tauri-apps/tauri/commit/76204b893846a04552f8f8b87ad2c9b55e1b417f) feat(cli): improve local IP detection ([#5817](https://www.github.com/tauri-apps/tauri/pull/5817)) on 2022-12-12 + +## \[2.0.0-alpha.0] + +- Added `android build` command. + - [4c9ea450](https://www.github.com/tauri-apps/tauri/commit/4c9ea450c3b47c6b8c825ba32e9837909945ccd7) feat(cli): add `android build` command ([#4999](https://www.github.com/tauri-apps/tauri/pull/4999)) on 2022-08-22 +- Added `ios build` command. + - [403859d4](https://www.github.com/tauri-apps/tauri/commit/403859d47e1a9bf978b353fa58e4b971e66337a3) feat(cli): add `ios build` command ([#5002](https://www.github.com/tauri-apps/tauri/pull/5002)) on 2022-08-22 +- Added `android dev` and `ios dev` commands. + - [6f061504](https://www.github.com/tauri-apps/tauri/commit/6f0615044d09ec58393a7ebca5e45bb175e20db3) feat(cli): add `android dev` and `ios dev` commands ([#4982](https://www.github.com/tauri-apps/tauri/pull/4982)) on 2022-08-20 +- Added `android init` and `ios init` commands. + - [d44f67f7](https://www.github.com/tauri-apps/tauri/commit/d44f67f7afd30a81d53a973ec603b2a253150bde) feat: add `android init` and `ios init` commands ([#4942](https://www.github.com/tauri-apps/tauri/pull/4942)) on 2022-08-15 +- Added `android open` and `ios open` commands. + - [a9c8e565](https://www.github.com/tauri-apps/tauri/commit/a9c8e565c6495961940877df7090f307be16b554) feat: add `android open` and `ios open` commands ([#4946](https://www.github.com/tauri-apps/tauri/pull/4946)) on 2022-08-15 +- First mobile alpha release! + - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 + +## \[1.5.11] + +### Bug Fixes + +- [`97a05145f`](https://www.github.com/tauri-apps/tauri/commit/97a05145fbb24533526eba6589594f03046e11df)([#9119](https://www.github.com/tauri-apps/tauri/pull/9119)) Fix compilation error due to dependency on unstable features of `log` crate. +- [`b15948b11`](https://www.github.com/tauri-apps/tauri/commit/b15948b11c0e362eea7ef57a4606f15f7dbd886b)([#8903](https://www.github.com/tauri-apps/tauri/pull/8903)) Fix `.taurignore` failing to ignore in some cases. + +### Dependencies + +- Upgraded to `tauri-bundler@1.5.1` + +## \[1.5.10] + +### New Features + +- [`89911296`](https://www.github.com/tauri-apps/tauri/commit/89911296e475d5c36f3486b9b75232505846e767)([#8259](https://www.github.com/tauri-apps/tauri/pull/8259)) On macOS, support for signing nested .dylib, .app, .xpc and .framework under predefined directories inside the bundled frameworks ("MacOS", "Frameworks", "Plugins", "Helpers", "XPCServices" and "Libraries"). + +### Bug Fixes + +- [`b0f27814`](https://www.github.com/tauri-apps/tauri/commit/b0f27814b90ded2f1ed44b7852080eedbff0d9e4)([#8776](https://www.github.com/tauri-apps/tauri/pull/8776)) Fix `fail to rename app` when using `--profile dev`. +- [`0bff8c32`](https://www.github.com/tauri-apps/tauri/commit/0bff8c325d004fdead2023f58e0f5fd73a9c22ba)([#8697](https://www.github.com/tauri-apps/tauri/pull/8697)) Fix the built-in dev server failing to serve files when URL had queries `?` and other url components. +- [`67d7877f`](https://www.github.com/tauri-apps/tauri/commit/67d7877f27f265c133a70d48a46c83ffff31d571)([#8520](https://www.github.com/tauri-apps/tauri/pull/8520)) The cli now also watches cargo workspace members if the tauri folder is the workspace root. + +### Dependencies + +- Upgraded to `tauri-bundler@1.5.0` + +## \[1.5.9] + +### Bug Fixes + +- [`0a2175ea`](https://www.github.com/tauri-apps/tauri/commit/0a2175eabb736b2a4cd01ab682e08be0b5ebb2b9)([#8439](https://www.github.com/tauri-apps/tauri/pull/8439)) Expand glob patterns in workspace member paths so the CLI would watch all matching pathhs. + +### Dependencies + +- Upgraded to `tauri-bundler@1.4.8` +- Upgraded to `tauri-utils@1.5.2` + +## \[1.5.8] + +### Dependencies + +- Upgraded to `tauri-bundler@1.4.7` + +## \[1.5.7] + +### Bug Fixes + +- [`1d5aa38a`](https://www.github.com/tauri-apps/tauri/commit/1d5aa38ae418ea31f593590b6d32cf04d3bfd8c1)([#8162](https://www.github.com/tauri-apps/tauri/pull/8162)) Fixes errors on command output, occuring when the output stream contains an invalid UTF-8 character, or ends with a multi-bytes UTF-8 character. +- [`f26d9f08`](https://www.github.com/tauri-apps/tauri/commit/f26d9f0884f63f61b9f4d4fac15e6b251163793e)([#8263](https://www.github.com/tauri-apps/tauri/pull/8263)) Fixes an issue in the NSIS installer which caused the uninstallation to leave empty folders on the system if the `resources` feature was used. +- [`92bc7d0e`](https://www.github.com/tauri-apps/tauri/commit/92bc7d0e16157434330a1bcf1eefda6f0f1e5f85)([#8233](https://www.github.com/tauri-apps/tauri/pull/8233)) Fixes an issue in the NSIS installer which caused the installation to take much longer than expected when many `resources` were added to the bundle. + +### Dependencies + +- Upgraded to `tauri-bundler@1.4.6` + +## \[1.5.6] + +### Bug Fixes + +- [`5264e41d`](https://www.github.com/tauri-apps/tauri/commit/5264e41db3763e4c2eb0c3c21bd423fb7bece3e2)([#8082](https://www.github.com/tauri-apps/tauri/pull/8082)) Downgraded `rust-minisign` to `0.7.3` to fix signing updater bundles with empty passwords. + +### Dependencies + +- Upgraded to `tauri-bundler@1.4.5` + +## \[1.5.5] + +### Enhancements + +- [`9bead42d`](https://www.github.com/tauri-apps/tauri/commit/9bead42dbca0fb6dd7ea0b6bfb2f2308a5c5f992)([#8059](https://www.github.com/tauri-apps/tauri/pull/8059)) Allow rotating the updater private key. + +### Bug Fixes + +- [`be8e5aa3`](https://www.github.com/tauri-apps/tauri/commit/be8e5aa3071d9bc5d0bd24647e8168f312d11c8d)([#8042](https://www.github.com/tauri-apps/tauri/pull/8042)) Fixes duplicated newlines on command outputs. + +### Dependencies + +- Upgraded to `tauri-bundler@1.4.4` + +## \[1.5.4] + +### Dependencies + +- Upgraded to `tauri-bundler@1.4.3` + +## \[1.5.3] + +### Dependencies + +- Upgraded to `tauri-bundler@1.4.2` + +## \[1.5.2] + +### Dependencies + +- Upgraded to `tauri-bundler@1.4.1` + +## \[1.5.1] + +### Bug Fixes + +- [`d6eb46cf`](https://www.github.com/tauri-apps/tauri/commit/d6eb46cf1116d147121f6b6db9d390b5e2fb238d)([#7934](https://www.github.com/tauri-apps/tauri/pull/7934)) On macOS, fix the `apple-id` option name when using `notarytools submit`. + +## \[1.5.0] + +### New Features + +- [`e1526626`](https://www.github.com/tauri-apps/tauri/commit/e152662687ece7a62d383923a50751cc0dd34331)([#7723](https://www.github.com/tauri-apps/tauri/pull/7723)) Support Bun package manager in CLI + +### Enhancements + +- [`13279917`](https://www.github.com/tauri-apps/tauri/commit/13279917d4cae071d0ce3a686184d48af079f58a)([#7713](https://www.github.com/tauri-apps/tauri/pull/7713)) Add version of Rust Tauri CLI installed with Cargo to `tauri info` command. + +### Bug Fixes + +- [`dad4f54e`](https://www.github.com/tauri-apps/tauri/commit/dad4f54eec9773d2ea6233a7d9fd218741173823)([#7277](https://www.github.com/tauri-apps/tauri/pull/7277)) Removed the automatic version check of the CLI that ran after `tauri` commands which caused various issues. + +### Dependencies + +- Upgraded to `tauri-bundler@1.4.0` +- Upgraded to `tauri-utils@1.5.0` + +## \[1.4.0] + +### New Features + +- [`0ddbb3a1`](https://www.github.com/tauri-apps/tauri/commit/0ddbb3a1dc1961ba5c6c1a60081513c1380c8af1)([#7015](https://www.github.com/tauri-apps/tauri/pull/7015)) Provide prebuilt CLIs for Windows ARM64 targets. +- [`35cd751a`](https://www.github.com/tauri-apps/tauri/commit/35cd751adc6fef1f792696fa0cfb471b0bf99374)([#5176](https://www.github.com/tauri-apps/tauri/pull/5176)) Added the `desktop_template` option on `tauri.conf.json > tauri > bundle > deb`. +- [`6c5ade08`](https://www.github.com/tauri-apps/tauri/commit/6c5ade08d97844bb685789d30e589400bbe3e04c)([#4537](https://www.github.com/tauri-apps/tauri/pull/4537)) Added `tauri completions` to generate shell completions scripts. +- [`29488205`](https://www.github.com/tauri-apps/tauri/commit/2948820579d20dfaa0861c2f0a58bd7737a7ffd1)([#6867](https://www.github.com/tauri-apps/tauri/pull/6867)) Allow specifying custom language files of Tauri's custom messages for the NSIS installer +- [`e092f799`](https://www.github.com/tauri-apps/tauri/commit/e092f799469ff32c7d1595d0f07d06fd2dab5c29)([#6887](https://www.github.com/tauri-apps/tauri/pull/6887)) Add `nsis > template` option to specify custom NSIS installer template. + +### Enhancements + +- [`d75c1b82`](https://www.github.com/tauri-apps/tauri/commit/d75c1b829bd96d9e3a672bcc79120597d5ada4a0)([#7181](https://www.github.com/tauri-apps/tauri/pull/7181)) Print a useful error when `updater` bundle target is specified without an updater-enabled target. +- [`52474e47`](https://www.github.com/tauri-apps/tauri/commit/52474e479d695865299d8c8d868fb98b99731020)([#7141](https://www.github.com/tauri-apps/tauri/pull/7141)) Enhance injection of Cargo features. +- [`2659ca1a`](https://www.github.com/tauri-apps/tauri/commit/2659ca1ab4799a5bda65c229c149e98bd01eb1ee)([#6900](https://www.github.com/tauri-apps/tauri/pull/6900)) Add `rustls` as default Cargo feature. +- [`c7056d1b`](https://www.github.com/tauri-apps/tauri/commit/c7056d1b202927d025aa479818c2fb20bf5d6113)([#6982](https://www.github.com/tauri-apps/tauri/pull/6982)) Improve Visual Studio installation detection in `tauri info` command to check for the necessary components instead of whole workloads. This also fixes the detection of minimal installations and auto-installations done by `rustup`. + +### Bug Fixes + +- [`3cb7a3e6`](https://www.github.com/tauri-apps/tauri/commit/3cb7a3e642bb10ee90dc1d24daa48b8c8c15c9ce)([#6997](https://www.github.com/tauri-apps/tauri/pull/6997)) Fix built-in devserver adding hot-reload code to non-html files. +- [`fd3b5a16`](https://www.github.com/tauri-apps/tauri/commit/fd3b5a16b13d62f62c850e68763401916dde26f2)([#6954](https://www.github.com/tauri-apps/tauri/pull/6954)) Fix building with a custom cargo profile +- [`1253bbf7`](https://www.github.com/tauri-apps/tauri/commit/1253bbf7ae11a87887e0b3bd98cc26dbb98c8130)([#7013](https://www.github.com/tauri-apps/tauri/pull/7013)) Fixes Cargo.toml feature rewriting. + +## \[1.3.1] + +- Correctly escape XML for resource files in WiX bundler. + - Bumped due to a bump in tauri-bundler. + - [6a6b1388](https://www.github.com/tauri-apps/tauri/commit/6a6b1388ea5787aea4c0e0b0701a4772087bbc0f) fix(bundler): correctly escape resource xml, fixes [#6853](https://www.github.com/tauri-apps/tauri/pull/6853) ([#6855](https://www.github.com/tauri-apps/tauri/pull/6855)) on 2023-05-04 + +- Added the following languages to the NSIS bundler: + +- `Spanish` + +- `SpanishInternational` + +- Bumped due to a bump in tauri-bundler. + +- [422b4817](https://www.github.com/tauri-apps/tauri/commit/422b48179856504e980a156500afa8e22c44d357) Add Spanish and SpanishInternational languages ([#6871](https://www.github.com/tauri-apps/tauri/pull/6871)) on 2023-05-06 + +- Correctly escape arguments in NSIS script to fix bundling apps that use non-default WebView2 install modes. + - Bumped due to a bump in tauri-bundler. + - [2915bd06](https://www.github.com/tauri-apps/tauri/commit/2915bd068ed40dc01a363b69212c6b6f2d3ec01e) fix(bundler): Fix webview install modes in NSIS bundler ([#6854](https://www.github.com/tauri-apps/tauri/pull/6854)) on 2023-05-04 + +## \[1.3.0] + +- Look for available port when using the built-in dev server for static files. + - [a7ee5ca7](https://www.github.com/tauri-apps/tauri/commit/a7ee5ca7c348d33bdfdc1213b6850bcf5c39d6e6) fix(cli): look for available ports for built-in dev server, closes [#6511](https://www.github.com/tauri-apps/tauri/pull/6511) ([#6514](https://www.github.com/tauri-apps/tauri/pull/6514)) on 2023-03-31 +- Add `--port` to specify the port used for static files dev server. It can also be specified through `TAURI_DEV_SERVER_PORT` env var. + - [b7a2ce2c](https://www.github.com/tauri-apps/tauri/commit/b7a2ce2c633c8383851ec9ec3c2cafda39f19745) feat(cli): add --port, closes [#6186](https://www.github.com/tauri-apps/tauri/pull/6186) ([#6283](https://www.github.com/tauri-apps/tauri/pull/6283)) on 2023-03-16 +- Fix `tauri info` panicking when parsing crates version on a newly created project without a `Cargo.lock` file. + - [c2608423](https://www.github.com/tauri-apps/tauri/commit/c2608423b6eec5eb91d0ffc861714c011ad3988b) fix(cli): don't panic when a crate version couldn't be parsed ([#5873](https://www.github.com/tauri-apps/tauri/pull/5873)) on 2022-12-26 +- Improve the error message when `rustc` couldn't be found. + - [7aab3e20](https://www.github.com/tauri-apps/tauri/commit/7aab3e2076272c14c78a563e288a1b04ed3cfd41) fix(cli.rs): improve `rustc` not found error msg ([#6021](https://www.github.com/tauri-apps/tauri/pull/6021)) on 2023-01-17 +- Add `--ci` flag and respect the `CI` environment variable on the `signer generate` command. In this case the default password will be an empty string and the CLI will not prompt for a value. + - [8fb1df8a](https://www.github.com/tauri-apps/tauri/commit/8fb1df8aa65a52cdb4a7e1bb9dda9b912a7a2895) feat(cli): add `--ci` flag to `signer generate`, closes [#6089](https://www.github.com/tauri-apps/tauri/pull/6089) ([#6097](https://www.github.com/tauri-apps/tauri/pull/6097)) on 2023-01-19 +- Fix Outdated Github Actions in the Plugin Templates `with-api` and `backend` + - [a926b49a](https://www.github.com/tauri-apps/tauri/commit/a926b49a01925ca757d391994bfac3beea29599b) Fix Github Actions of Tauri Plugin with-api template ([#6603](https://www.github.com/tauri-apps/tauri/pull/6603)) on 2023-04-03 +- Do not crash on Cargo.toml watcher. + - [e8014a7f](https://www.github.com/tauri-apps/tauri/commit/e8014a7f612a1094461ddad63aacc498a2682ff5) fix(cli): do not crash on watcher ([#6303](https://www.github.com/tauri-apps/tauri/pull/6303)) on 2023-02-17 +- On Windows, printing consistent paths on Windows with backslashs only. + - [9da99607](https://www.github.com/tauri-apps/tauri/commit/9da996073ff07d4b59668a5315d40e9bc578e340) fix(cli): fix printing paths on Windows ([#6137](https://www.github.com/tauri-apps/tauri/pull/6137)) on 2023-01-26 +- Add `--png` option for the `icon` command to generate custom icon sizes. + - [9d214412](https://www.github.com/tauri-apps/tauri/commit/9d2144128fc5fad67d8404bce95f82297ebb0e4a) feat(cli): add option to make custom icon sizes, closes [#5121](https://www.github.com/tauri-apps/tauri/pull/5121) ([#5246](https://www.github.com/tauri-apps/tauri/pull/5246)) on 2022-12-27 +- Skip the password prompt on the build command when `TAURI_KEY_PASSWORD` environment variable is empty and the `--ci` argument is provided or the `CI` environment variable is set. + - [d4f89af1](https://www.github.com/tauri-apps/tauri/commit/d4f89af18d69fd95a4d8a1ede8442547c6a6d0ee) feat: skip password prompt on the build command if CI is set fixes [#6089](https://www.github.com/tauri-apps/tauri/pull/6089) on 2023-01-18 +- Fix `default-run` not deserialized. + - [57c6bf07](https://www.github.com/tauri-apps/tauri/commit/57c6bf07bb380847abdf27c3fff9891d99c1c98c) fix(cli): fix default-run not deserialized ([#6584](https://www.github.com/tauri-apps/tauri/pull/6584)) on 2023-03-30 +- Fixes HTML serialization removing template tags on the dev server. + - [314f0e21](https://www.github.com/tauri-apps/tauri/commit/314f0e212fd2b9e452bfe3424cdce2b0bf37b5d7) fix(cli): web_dev_server html template serialization (fix [#6165](https://www.github.com/tauri-apps/tauri/pull/6165)) ([#6166](https://www.github.com/tauri-apps/tauri/pull/6166)) on 2023-01-29 +- Use escaping on Handlebars templates. + - [6d6b6e65](https://www.github.com/tauri-apps/tauri/commit/6d6b6e653ea70fc02794f723092cdc860995c259) feat: configure escaping on handlebars templates ([#6678](https://www.github.com/tauri-apps/tauri/pull/6678)) on 2023-05-02 +- Fix building apps with unicode characters in their `productName`. + - [72621892](https://www.github.com/tauri-apps/tauri/commit/72621892fe8195bad67b4237467ebd7e89f6af7f) fix(cli): use `unicode` feature for `heck` crate, closes [#5860](https://www.github.com/tauri-apps/tauri/pull/5860) ([#5872](https://www.github.com/tauri-apps/tauri/pull/5872)) on 2022-12-26 +- Bump minimum supported Rust version to 1.60. + - [5fdc616d](https://www.github.com/tauri-apps/tauri/commit/5fdc616df9bea633810dcb814ac615911d77222c) feat: Use the zbus-backed of notify-rust ([#6332](https://www.github.com/tauri-apps/tauri/pull/6332)) on 2023-03-31 +- Add initial support for building `nsis` bundles on non-Windows platforms. + - [60e6f6c3](https://www.github.com/tauri-apps/tauri/commit/60e6f6c3f1605f3064b5bb177992530ff788ccf0) feat(bundler): Add support for creating NSIS bundles on unix hosts ([#5788](https://www.github.com/tauri-apps/tauri/pull/5788)) on 2023-01-19 +- Add `nsis` bundle target + - [c94e1326](https://www.github.com/tauri-apps/tauri/commit/c94e1326a7c0767a13128a8b1d327a00156ece12) feat(bundler): add `nsis`, closes [#4450](https://www.github.com/tauri-apps/tauri/pull/4450), closes [#2319](https://www.github.com/tauri-apps/tauri/pull/2319) ([#4674](https://www.github.com/tauri-apps/tauri/pull/4674)) on 2023-01-03 +- Remove default features from Cargo.toml template. + - [b08ae637](https://www.github.com/tauri-apps/tauri/commit/b08ae637a0f58b38cbce9b8a1fa0b6c5dc0cfd05) fix(cli): remove default features from template ([#6074](https://www.github.com/tauri-apps/tauri/pull/6074)) on 2023-01-17 +- Added support for Cargo's workspace inheritance for package information. The cli now also detects inherited `tauri` and `tauri-build` dependencies and disables manifest rewrites accordingly. + - [cd8c074a](https://www.github.com/tauri-apps/tauri/commit/cd8c074ae6592303d3f6844a4fb6d262eae913b2) feat(cli): add support for Cargo's workspace inheritance for the package version, closes [#5070](https://www.github.com/tauri-apps/tauri/pull/5070) ([#5775](https://www.github.com/tauri-apps/tauri/pull/5775)) on 2022-12-14 + - [d20a7288](https://www.github.com/tauri-apps/tauri/commit/d20a728892eee1858ab525ab6216cd721f473ab5) feat: Further improve workspace inheritance, closes [#6122](https://www.github.com/tauri-apps/tauri/pull/6122), [#5070](https://www.github.com/tauri-apps/tauri/pull/5070) ([#6144](https://www.github.com/tauri-apps/tauri/pull/6144)) on 2023-01-26 +- Use Ubuntu 20.04 to compile the CLI for cargo-binstall, increasing the minimum libc required. + +## \[1.2.3] + +- Pin `ignore` to `=0.4.18`. + - [adcb082b](https://www.github.com/tauri-apps/tauri/commit/adcb082b1651ecb2a6208b093e12f4185aa3fc98) chore(deps): pin `ignore` to =0.4.18 on 2023-01-17 + +## \[1.2.2] + +- Detect SvelteKit and Vite for the init and info commands. + - [9d872ab8](https://www.github.com/tauri-apps/tauri/commit/9d872ab8728b1b121909af434adcd5936e5afb7d) feat(cli): detect SvelteKit and Vite ([#5742](https://www.github.com/tauri-apps/tauri/pull/5742)) on 2022-12-02 +- Detect SolidJS and SolidStart for the init and info commands. + - [9e7ce0a8](https://www.github.com/tauri-apps/tauri/commit/9e7ce0a8eef4bf3536645976e3e09162fbf772ab) feat(cli): detect SolidJS and SolidStart ([#5758](https://www.github.com/tauri-apps/tauri/pull/5758)) on 2022-12-08 +- Use older icon types to work around a macOS bug resulting in corrupted 16x16px and 32x32px icons in bundled apps. + - [2d545eff](https://www.github.com/tauri-apps/tauri/commit/2d545eff58734ec70f23f11a429d35435cdf090e) fix(cli): corrupted icons in bundled macOS icons ([#5698](https://www.github.com/tauri-apps/tauri/pull/5698)) on 2022-11-28 +- Add `--no-dev-server` flag to the cli to disable the dev server for static files in dev mode. + - [c0989848](https://www.github.com/tauri-apps/tauri/commit/c0989848b9421fb19070ae652a89a5d5675deab8) feat(cli/dev): add `--no-dev-server`, ref [#5708](https://www.github.com/tauri-apps/tauri/pull/5708) ([#5722](https://www.github.com/tauri-apps/tauri/pull/5722)) on 2022-11-30 + +## \[1.2.1] + +- Fixes injection of Cargo features defined in the configuration file. + - [1ecaeb29](https://www.github.com/tauri-apps/tauri/commit/1ecaeb29aa798f591f6488dc6c3a7a8d22f6073e) fix(cli): inject config feature flags when features arg is not provided on 2022-11-18 + +## \[1.2.0] + +- Keep `tauri dev` watcher alive when the configuration is invalid. + - [cc186c7a](https://www.github.com/tauri-apps/tauri/commit/cc186c7a0eab1c364f8b58101f86979ae4ed3d03) fix(cli): keep dev watcher alive if config is incorrect, closes [#5173](https://www.github.com/tauri-apps/tauri/pull/5173) ([#5495](https://www.github.com/tauri-apps/tauri/pull/5495)) on 2022-10-28 +- Ignore workspace members in dev watcher if they are ignored by `.taurignore` + - [9417ce40](https://www.github.com/tauri-apps/tauri/commit/9417ce401c4985e97245ce02d3b7cc31fb4bf59e) fix(cli): apply `.taurignore` rules to workspace members, closes [#5355](https://www.github.com/tauri-apps/tauri/pull/5355) ([#5460](https://www.github.com/tauri-apps/tauri/pull/5460)) on 2022-10-28 +- Detect JSON5 and TOML configuration files in the dev watcher. + - [e7ccbd85](https://www.github.com/tauri-apps/tauri/commit/e7ccbd8573f6b9124e80c0b67fa2365729c3c196) feat(cli): detect JSON5 and TOML configuration files in the dev watcher ([#5439](https://www.github.com/tauri-apps/tauri/pull/5439)) on 2022-10-19 +- Fix cli passing `--no-default-features` to the app instead of the runner (Cargo). + - [a3a70218](https://www.github.com/tauri-apps/tauri/commit/a3a70218f3cc438b4875a046a182ca44dab357ae) fix(cli): pass `--no-default-features` to runner instead of app, closes [#5415](https://www.github.com/tauri-apps/tauri/pull/5415) ([#5474](https://www.github.com/tauri-apps/tauri/pull/5474)) on 2022-10-25 +- Validate `package > productName` in the tauri config and produce errors if it contains one of the following characters `/\:*?\"<>|` + - [b9316a64](https://www.github.com/tauri-apps/tauri/commit/b9316a64eaa9348c79efafb8b94960d9b4d5b27a) fix(cli): validate `productName` in config, closes [#5233](https://www.github.com/tauri-apps/tauri/pull/5233) ([#5262](https://www.github.com/tauri-apps/tauri/pull/5262)) on 2022-09-28 +- Hot-reload the frontend when `tauri.conf.json > build > devPath` points to a directory. + - [54c337e0](https://www.github.com/tauri-apps/tauri/commit/54c337e06f3bc624c4780cf002bc54790f446c90) feat(cli): hotreload support for frontend static files, closes [#2173](https://www.github.com/tauri-apps/tauri/pull/2173) ([#5256](https://www.github.com/tauri-apps/tauri/pull/5256)) on 2022-09-28 +- Expose `TAURI_TARGET_TRIPLE` to `beforeDevCommand`, `beforeBuildCommand` and `beforeBundleCommand` + - [a4aec9f0](https://www.github.com/tauri-apps/tauri/commit/a4aec9f0a864ec6d7712db8bd50989d9c2e2fd2e) feat(cli): expose `TAURI_TARGET_TRIPLE` to before\*Commands, closes [#5091](https://www.github.com/tauri-apps/tauri/pull/5091) ([#5101](https://www.github.com/tauri-apps/tauri/pull/5101)) on 2022-10-03 +- Log dev watcher file change detection. + - [9076d5d2](https://www.github.com/tauri-apps/tauri/commit/9076d5d2e76d432aef475ba403e9ab5bd3b9d2b0) feat(cli): add prompt information when file changing detected, closes [#5417](https://www.github.com/tauri-apps/tauri/pull/5417) ([#5428](https://www.github.com/tauri-apps/tauri/pull/5428)) on 2022-10-19 +- Set `TAURI_PLATFORM_TYPE`, `TAURI_FAMILY`, `TAURI_ARCH` and `TAURI_PLATFORM` env vars for hook commands to based on the app not the cli. + - [a4aec9f0](https://www.github.com/tauri-apps/tauri/commit/a4aec9f0a864ec6d7712db8bd50989d9c2e2fd2e) feat(cli): expose `TAURI_TARGET_TRIPLE` to before\*Commands, closes [#5091](https://www.github.com/tauri-apps/tauri/pull/5091) ([#5101](https://www.github.com/tauri-apps/tauri/pull/5101)) on 2022-10-03 +- - [7d9aa398](https://www.github.com/tauri-apps/tauri/commit/7d9aa3987efce2d697179ffc33646d086c68030c) feat: bump MSRV to 1.59 ([#5296](https://www.github.com/tauri-apps/tauri/pull/5296)) on 2022-09-28 +- Add `tauri.conf.json > bundle > publisher` field to specify the app publisher. + - [628285c1](https://www.github.com/tauri-apps/tauri/commit/628285c1cf43f03ed62378f3b6cc0c991317526f) feat(bundler): add `publisher` field, closes [#5273](https://www.github.com/tauri-apps/tauri/pull/5273) ([#5283](https://www.github.com/tauri-apps/tauri/pull/5283)) on 2022-09-28 +- Changed the project template to not enable all APIs by default. + - [582c25a0](https://www.github.com/tauri-apps/tauri/commit/582c25a0f0fa2725d786ec4edd0defe7811ad6e8) refactor(cli): disable api-all on templates ([#5538](https://www.github.com/tauri-apps/tauri/pull/5538)) on 2022-11-03 + +## \[1.1.1] + +- Fix wrong cli metadata that caused new projects (created through `tauri init`) fail to build + - [db26aaf2](https://www.github.com/tauri-apps/tauri/commit/db26aaf2b44ce5335c9223c571ef2b2175e0cd6d) fix: fix wrong cli metadata ([#5214](https://www.github.com/tauri-apps/tauri/pull/5214)) on 2022-09-16 + +## \[1.1.0] + +- Allow adding `build > beforeBundleCommand` in tauri.conf.json to run a shell command before the bundling phase. + - [57ab9847](https://www.github.com/tauri-apps/tauri/commit/57ab9847eb2d8c9a5da584b873b7c072e9ee26bf) feat(cli): add `beforeBundleCommand`, closes [#4879](https://www.github.com/tauri-apps/tauri/pull/4879) ([#4893](https://www.github.com/tauri-apps/tauri/pull/4893)) on 2022-08-09 +- Change `before_dev_command` and `before_build_command` config value to allow configuring the current working directory. + - [d6f7d3cf](https://www.github.com/tauri-apps/tauri/commit/d6f7d3cfe8a7ec8d68c8341016c4e0a3103da587) Add cwd option to `before` commands, add wait option to dev [#4740](https://www.github.com/tauri-apps/tauri/pull/4740) [#3551](https://www.github.com/tauri-apps/tauri/pull/3551) ([#4834](https://www.github.com/tauri-apps/tauri/pull/4834)) on 2022-08-02 +- Allow configuring the `before_dev_command` to force the CLI to wait for the command to finish before proceeding. + - [d6f7d3cf](https://www.github.com/tauri-apps/tauri/commit/d6f7d3cfe8a7ec8d68c8341016c4e0a3103da587) Add cwd option to `before` commands, add wait option to dev [#4740](https://www.github.com/tauri-apps/tauri/pull/4740) [#3551](https://www.github.com/tauri-apps/tauri/pull/3551) ([#4834](https://www.github.com/tauri-apps/tauri/pull/4834)) on 2022-08-02 +- Check if the default build target is set in the Cargo configuration. + - [436f3d8d](https://www.github.com/tauri-apps/tauri/commit/436f3d8d66727f5b64165522f0b55f4ab54bd1ae) feat(cli): load Cargo configuration to check default build target ([#4990](https://www.github.com/tauri-apps/tauri/pull/4990)) on 2022-08-21 +- Add support to cargo-binstall. + - [90d5929f](https://www.github.com/tauri-apps/tauri/commit/90d5929fea6df575a2aa3c0a749374358f1ddb9b) feat(cli.rs): add support to cargo-binstall, closes [#4651](https://www.github.com/tauri-apps/tauri/pull/4651) ([#4817](https://www.github.com/tauri-apps/tauri/pull/4817)) on 2022-08-02 +- Use `cargo metadata` to detect the workspace root and target directory. + - [fea70eff](https://www.github.com/tauri-apps/tauri/commit/fea70effad219c0794d919f8834fa1a1ffd204c7) refactor(cli): Use `cargo metadata` to detect the workspace root and target directory, closes [#4632](https://www.github.com/tauri-apps/tauri/pull/4632), [#4928](https://www.github.com/tauri-apps/tauri/pull/4928). ([#4932](https://www.github.com/tauri-apps/tauri/pull/4932)) on 2022-08-21 +- Prompt for `beforeDevCommand` and `beforeBuildCommand` in `tauri init`. + - [6d4945c9](https://www.github.com/tauri-apps/tauri/commit/6d4945c9f06cd1f7018e1c48686ba682aae817df) feat(cli): prompt for before\*Command, closes [#4691](https://www.github.com/tauri-apps/tauri/pull/4691) ([#4721](https://www.github.com/tauri-apps/tauri/pull/4721)) on 2022-07-25 +- Add `icon` command to generate icons. + - [12e9d811](https://www.github.com/tauri-apps/tauri/commit/12e9d811e69813e7e9db5344b422101ddc09589f) feat(cli): Add `icon` command (tauricon) ([#4992](https://www.github.com/tauri-apps/tauri/pull/4992)) on 2022-09-03 +- Added support to configuration files in TOML format (Tauri.toml file). + - [ae83d008](https://www.github.com/tauri-apps/tauri/commit/ae83d008f9e1b89bfc8dddaca42aa5c1fbc36f6d) feat: add support to TOML config file `Tauri.toml`, closes [#4806](https://www.github.com/tauri-apps/tauri/pull/4806) ([#4813](https://www.github.com/tauri-apps/tauri/pull/4813)) on 2022-08-02 +- Automatically use any `.taurignore` file as ignore rules for dev watcher and app path finder. + - [596fa08d](https://www.github.com/tauri-apps/tauri/commit/596fa08d48e371c7bd29e1ef799119ac8fca0d0b) feat(cli): automatically use `.taurignore`, ref [#4617](https://www.github.com/tauri-apps/tauri/pull/4617) ([#4623](https://www.github.com/tauri-apps/tauri/pull/4623)) on 2022-07-28 +- Enable WiX FIPS compliance when the `TAURI_FIPS_COMPLIANT` environment variable is set to `true`. + - [d88b9de7](https://www.github.com/tauri-apps/tauri/commit/d88b9de7aaeaaa2e42e4795dbc2b8642b5ae7a50) feat(core): add `fips_compliant` wix config option, closes [#4541](https://www.github.com/tauri-apps/tauri/pull/4541) ([#4843](https://www.github.com/tauri-apps/tauri/pull/4843)) on 2022-08-04 +- Fixes dev watcher incorrectly exiting the CLI when sequential file updates are detected. + - [47fab680](https://www.github.com/tauri-apps/tauri/commit/47fab6809a1e23b3b9a84695e2d91ff0826ba79a) fix(cli): dev watcher incorrectly killing process on multiple file write ([#4684](https://www.github.com/tauri-apps/tauri/pull/4684)) on 2022-07-25 +- Set the `MACOSX_DEPLOYMENT_TARGET` environment variable with the configuration `minimum_system_version` value. + - [fa23310f](https://www.github.com/tauri-apps/tauri/commit/fa23310f23cb9e6a02ec2524f1ef394a5b42990e) fix(cli): set MACOSX_DEPLOYMENT_TARGET env var, closes [#4704](https://www.github.com/tauri-apps/tauri/pull/4704) ([#4842](https://www.github.com/tauri-apps/tauri/pull/4842)) on 2022-08-02 +- Added `--no-watch` argument to the `dev` command to disable the file watcher. + - [0983d7ce](https://www.github.com/tauri-apps/tauri/commit/0983d7ce7f24ab43f9ae7b5e1177ff244d8885a8) feat(cli): add `--no-watch` argument to the dev command, closes [#4617](https://www.github.com/tauri-apps/tauri/pull/4617) ([#4793](https://www.github.com/tauri-apps/tauri/pull/4793)) on 2022-07-29 +- Validate updater signature matches configured public key. + - [b2a8930b](https://www.github.com/tauri-apps/tauri/commit/b2a8930b3c4b72c50ce72e73575f42c9cbe91bad) feat(cli): validate updater private key when signing ([#4754](https://www.github.com/tauri-apps/tauri/pull/4754)) on 2022-07-25 + +## \[1.0.5] + +- Correctly fill the architecture when building Debian packages targeting ARM64 (aarch64). + - Bumped due to a bump in tauri-bundler. + - [635f23b8](https://www.github.com/tauri-apps/tauri/commit/635f23b88adbb8726d628f67840709cd870836dc) fix(bundler): correctly set debian architecture for aarch64 ([#4700](https://www.github.com/tauri-apps/tauri/pull/4700)) on 2022-07-17 + +## \[1.0.4] + +- Do not capture and force colors of `cargo build` output. + - [c635a0da](https://www.github.com/tauri-apps/tauri/commit/c635a0dad437860d54109adffaf245b7c21bc684) refactor(cli): do not capture and force colors of cargo build output ([#4627](https://www.github.com/tauri-apps/tauri/pull/4627)) on 2022-07-12 +- Reduce the amount of allocations when converting cases. + - [bc370e32](https://www.github.com/tauri-apps/tauri/commit/bc370e326810446e15b1f50fb962b980114ba16b) feat: reduce the amount of `heck`-related allocations ([#4634](https://www.github.com/tauri-apps/tauri/pull/4634)) on 2022-07-11 + +## \[1.0.3] + +- Changed the app template to not set the default app menu as it is now set automatically on macOS which is the platform that needs a menu to function properly. + - [91055883](https://www.github.com/tauri-apps/tauri/commit/9105588373cc8401bd9ad79bdef26f509b2d76b7) feat: add implicit default menu for macOS only, closes [#4551](https://www.github.com/tauri-apps/tauri/pull/4551) ([#4570](https://www.github.com/tauri-apps/tauri/pull/4570)) on 2022-07-04 +- Improved bundle identifier validation showing the exact source of the configuration value. + - [8e3e7fc6](https://www.github.com/tauri-apps/tauri/commit/8e3e7fc64641afc7a6833bc93205e6f525562545) feat(cli): improve bundle identifier validation, closes [#4589](https://www.github.com/tauri-apps/tauri/pull/4589) ([#4596](https://www.github.com/tauri-apps/tauri/pull/4596)) on 2022-07-05 +- Improve configuration deserialization error messages. + - [9170c920](https://www.github.com/tauri-apps/tauri/commit/9170c9207044fa561535f624916dfdbaa41ff79d) feat(core): improve config deserialization error messages ([#4607](https://www.github.com/tauri-apps/tauri/pull/4607)) on 2022-07-06 +- Skip the static link of the `vcruntime140.dll` if the `STATIC_VCRUNTIME` environment variable is set to `false`. + - [2e61abaa](https://www.github.com/tauri-apps/tauri/commit/2e61abaa9ae5d7a41ca1fa6505b5d6c368625ce5) feat(cli): allow dynamic link vcruntime, closes [#4565](https://www.github.com/tauri-apps/tauri/pull/4565) ([#4601](https://www.github.com/tauri-apps/tauri/pull/4601)) on 2022-07-06 +- The `TAURI_CONFIG` environment variable now represents the configuration to be merged instead of the entire JSON. + - [fa028ebf](https://www.github.com/tauri-apps/tauri/commit/fa028ebf3c8ca7b43a70d283a01dbea86217594f) refactor: do not pass entire config from CLI to core, send patch instead ([#4598](https://www.github.com/tauri-apps/tauri/pull/4598)) on 2022-07-06 +- Watch for Cargo workspace members in the `dev` file watcher. + - [dbb8c87b](https://www.github.com/tauri-apps/tauri/commit/dbb8c87b96dec9942b1bf877b29bafb8246514d4) feat(cli): watch Cargo workspaces in the dev command, closes [#4222](https://www.github.com/tauri-apps/tauri/pull/4222) ([#4572](https://www.github.com/tauri-apps/tauri/pull/4572)) on 2022-07-03 + +## \[1.0.2] + +- Fixes a crash on the `signer sign` command. + - [8e808fec](https://www.github.com/tauri-apps/tauri/commit/8e808fece95f2e506acf2c446d37b9913fd67d50) fix(cli.rs): conflicts_with arg doesn't exist closes ([#4538](https://www.github.com/tauri-apps/tauri/pull/4538)) on 2022-06-30 + +## \[1.0.1] + +- No longer adds the `pkg-config` dependency to `.deb` packages when the `systemTray` is used. + This only works with recent versions of `libappindicator-sys` (including https://github.com/tauri-apps/libappindicator-rs/pull/38), + so a `cargo update` may be necessary if you create `.deb` bundles and use the tray feature. + - [0e6edeb1](https://www.github.com/tauri-apps/tauri/commit/0e6edeb14f379af1e02a7cebb4e3a5c9e87ebf7f) fix(cli): Don't add `pkg-config` to `deb` ([#4508](https://www.github.com/tauri-apps/tauri/pull/4508)) on 2022-06-29 +- AppImage bundling will now prefer bundling correctly named appindicator library (including `.1` version suffix). With a symlink for compatibility with the old naming. + - [bf45ca1d](https://www.github.com/tauri-apps/tauri/commit/bf45ca1df6691c05bdf72c5716cc01e89a7791d4) fix(cli,bundler): prefer AppImage libraries with ABI version ([#4505](https://www.github.com/tauri-apps/tauri/pull/4505)) on 2022-06-29 +- Improve error message when `cargo` is not installed. + - [e0e5f772](https://www.github.com/tauri-apps/tauri/commit/e0e5f772430f6349ec99ba891e601331e376e3c7) feat(cli): improve `cargo not found` error message, closes [#4428](https://www.github.com/tauri-apps/tauri/pull/4428) ([#4430](https://www.github.com/tauri-apps/tauri/pull/4430)) on 2022-06-21 +- The app template now only sets the default menu on macOS. + - [5105b428](https://www.github.com/tauri-apps/tauri/commit/5105b428c4726b2179cd4b3244350d1a1ee73734) feat(cli): change app template to only set default menu on macOS ([#4518](https://www.github.com/tauri-apps/tauri/pull/4518)) on 2022-06-29 +- Warn if updater is enabled but not in the bundle target list. + - [31c15cd2](https://www.github.com/tauri-apps/tauri/commit/31c15cd2bd94dbe39fb94982a15cbe02ac5d8925) docs(config): enhance documentation for bundle targets, closes [#3251](https://www.github.com/tauri-apps/tauri/pull/3251) ([#4418](https://www.github.com/tauri-apps/tauri/pull/4418)) on 2022-06-21 +- Check if target exists and is installed on dev and build commands. + - [13b8a240](https://www.github.com/tauri-apps/tauri/commit/13b8a2403d1353a8c3a643fbc6b6e862af68376e) feat(cli): validate target argument ([#4458](https://www.github.com/tauri-apps/tauri/pull/4458)) on 2022-06-24 +- Fixes the covector configuration on the plugin templates. + - [b8a64d01](https://www.github.com/tauri-apps/tauri/commit/b8a64d01bab11f955b7bbdf323d0afa1a3db4b64) fix(cli): add prepublish scripts to the plugin templates on 2022-06-19 +- Set the binary name to the product name in development. + - [b025b9f5](https://www.github.com/tauri-apps/tauri/commit/b025b9f581ac1a6ae0a26789c2be1e9928fb0282) refactor(cli): set binary name on dev ([#4447](https://www.github.com/tauri-apps/tauri/pull/4447)) on 2022-06-23 +- Allow registering a `.gitignore` file to skip watching some project files and directories via the `TAURI_DEV_WATCHER_IGNORE_FILE` environment variable. + - [83186dd8](https://www.github.com/tauri-apps/tauri/commit/83186dd89768407984db35fb67c3cc51f50ea8f5) Read extra ignore file for dev watcher, closes [#4406](https://www.github.com/tauri-apps/tauri/pull/4406) ([#4409](https://www.github.com/tauri-apps/tauri/pull/4409)) on 2022-06-20 +- Fix shebang for `kill-children.sh`. + - [35dd51db](https://www.github.com/tauri-apps/tauri/commit/35dd51db6826ec1eed7b90082b9eb6b2a699b627) fix(cli): add shebang for kill-children.sh, closes [#4262](https://www.github.com/tauri-apps/tauri/pull/4262) ([#4416](https://www.github.com/tauri-apps/tauri/pull/4416)) on 2022-06-22 +- Update plugin templates to use newer `tauri-apps/create-pull-request` GitHub action. + - [07f90795](https://www.github.com/tauri-apps/tauri/commit/07f9079532a42f3517d96faeaf46cad6176b31ac) chore(cli): update plugin template tauri-apps/create-pull-request on 2022-06-19 +- Use UNIX path separator on the init `$schema` field. + - [01053045](https://www.github.com/tauri-apps/tauri/commit/010530459ef62c48eed68ca965f2688accabcf69) chore(cli): use unix path separator on $schema ([#4384](https://www.github.com/tauri-apps/tauri/pull/4384)) on 2022-06-19 +- The `info` command now can check the Cargo lockfile on workspaces. + - [12f65219](https://www.github.com/tauri-apps/tauri/commit/12f65219ea75a51ebd38659ddce1563e015a036c) fix(cli): read lockfile from workspace on the info command, closes [#4232](https://www.github.com/tauri-apps/tauri/pull/4232) ([#4423](https://www.github.com/tauri-apps/tauri/pull/4423)) on 2022-06-21 +- Preserve the `Cargo.toml` formatting when the features array is not changed. + - [6650e5d6](https://www.github.com/tauri-apps/tauri/commit/6650e5d6720c215530ca1fdccd19bd2948dd6ca3) fix(cli): preserve Cargo manifest formatting when possible ([#4431](https://www.github.com/tauri-apps/tauri/pull/4431)) on 2022-06-21 +- Change the updater signature metadata to include the file name instead of its full path. + - [094b3eb3](https://www.github.com/tauri-apps/tauri/commit/094b3eb352bcf5de28414015e7c44290d619ea8c) fix(cli): file name instead of path on updater sig comment, closes [#4467](https://www.github.com/tauri-apps/tauri/pull/4467) ([#4484](https://www.github.com/tauri-apps/tauri/pull/4484)) on 2022-06-27 +- Validate bundle identifier as it must only contain alphanumeric characters, hyphens and periods. + - [0674a801](https://www.github.com/tauri-apps/tauri/commit/0674a80129d7c31bc93257849afc0a5069129fed) fix: assert config.bundle.identifier to be only alphanumeric, hyphens or dots. closes [#4359](https://www.github.com/tauri-apps/tauri/pull/4359) ([#4363](https://www.github.com/tauri-apps/tauri/pull/4363)) on 2022-06-17 + +## \[1.0.0] + +- Upgrade to `stable`! + - [f4bb30cc](https://www.github.com/tauri-apps/tauri/commit/f4bb30cc73d6ba9b9ef19ef004dc5e8e6bb901d3) feat(covector): prepare for v1 ([#4351](https://www.github.com/tauri-apps/tauri/pull/4351)) on 2022-06-15 + +## \[1.0.0-rc.16] + +- Use the default window menu in the app template. + - [4c4acc30](https://www.github.com/tauri-apps/tauri/commit/4c4acc3094218dd9cee0f1ad61810c979e0b41fa) feat: implement `Default` for `Menu`, closes [#2398](https://www.github.com/tauri-apps/tauri/pull/2398) ([#4291](https://www.github.com/tauri-apps/tauri/pull/4291)) on 2022-06-15 + +## \[1.0.0-rc.15] + +- Removed the tray icon from the Debian and AppImage bundles since they are embedded in the binary now. + - [4ce8e228](https://www.github.com/tauri-apps/tauri/commit/4ce8e228134cd3f22973b74ef26ca0d165fbbbd9) refactor(core): use `Icon` for tray icons ([#4342](https://www.github.com/tauri-apps/tauri/pull/4342)) on 2022-06-14 + +## \[1.0.0-rc.14] + +- Set the `TRAY_LIBRARY_PATH` environment variable to make the bundle copy the appindicator library to the AppImage. + - [34552444](https://www.github.com/tauri-apps/tauri/commit/3455244436578003a5fbb447b039e5c8971152ec) feat(cli): bundle appindicator library in the AppImage, closes [#3859](https://www.github.com/tauri-apps/tauri/pull/3859) ([#4267](https://www.github.com/tauri-apps/tauri/pull/4267)) on 2022-06-07 +- Set the `APPIMAGE_BUNDLE_GSTREAMER` environment variable to make the bundler copy additional gstreamer files to the AppImage. + - [d335fae9](https://www.github.com/tauri-apps/tauri/commit/d335fae92cdcbb0ee18aad4e54558914afa3e778) feat(bundler): bundle additional gstreamer files, closes [#4092](https://www.github.com/tauri-apps/tauri/pull/4092) ([#4271](https://www.github.com/tauri-apps/tauri/pull/4271)) on 2022-06-10 +- Configure the AppImage bundler to copy the `/usr/bin/xdg-open` binary if it exists and the shell `open` API is enabled. + - [2322ac11](https://www.github.com/tauri-apps/tauri/commit/2322ac11cf6290c6bf65413048a049c8072f863b) fix(bundler): bundle `/usr/bin/xdg-open` in appimage if open API enabled ([#4265](https://www.github.com/tauri-apps/tauri/pull/4265)) on 2022-06-04 +- Fixes multiple occurrences handling of the `bundles` and `features` arguments. + - [f685df39](https://www.github.com/tauri-apps/tauri/commit/f685df399a5a05480b6e4f5d92da71f3b87895ef) fix(cli): parsing of arguments with multiple values, closes [#4231](https://www.github.com/tauri-apps/tauri/pull/4231) ([#4233](https://www.github.com/tauri-apps/tauri/pull/4233)) on 2022-05-29 +- Log command output in real time instead of waiting for it to finish. + - [76d1eaae](https://www.github.com/tauri-apps/tauri/commit/76d1eaaebda5c8f6b0d41bf6587945e98cd441f3) feat(cli): debug command output in real time ([#4318](https://www.github.com/tauri-apps/tauri/pull/4318)) on 2022-06-12 +- Configure the `STATIC_VCRUNTIME` environment variable so `tauri-build` statically links it on the build command. + - [d703d27a](https://www.github.com/tauri-apps/tauri/commit/d703d27a707edc028f13b35603205da1133fcc2b) fix(build): statically link VC runtime only on `tauri build` ([#4292](https://www.github.com/tauri-apps/tauri/pull/4292)) on 2022-06-07 +- Use the `TAURI_TRAY` environment variable to determine which package should be added to the Debian `depends` section. Possible values are `ayatana` and `gtk`. + - [6216eb49](https://www.github.com/tauri-apps/tauri/commit/6216eb49e72863bfb6d4c9edb8827b21406ac393) refactor(core): drop `ayatana-tray` and `gtk-tray` Cargo features ([#4247](https://www.github.com/tauri-apps/tauri/pull/4247)) on 2022-06-02 + +## \[1.0.0-rc.13] + +- Check if `$CWD/src-tauri/tauri.conf.json` exists before walking through the file tree to find the tauri dir in case the whole project is gitignored. + - [bd8f3e29](https://www.github.com/tauri-apps/tauri/commit/bd8f3e298a0cb71809f2e93cc3ebc8e6e5b6a626) fix(cli): manual config lookup to handle gitignored folders, fixes [#3527](https://www.github.com/tauri-apps/tauri/pull/3527) ([#4224](https://www.github.com/tauri-apps/tauri/pull/4224)) on 2022-05-26 +- Statically link the Visual C++ runtime instead of using a merge module on the installer. + - [bb061509](https://www.github.com/tauri-apps/tauri/commit/bb061509fb674bef86ecbc1de3aa8f3e367a9907) refactor(core): statically link vcruntime, closes [#4122](https://www.github.com/tauri-apps/tauri/pull/4122) ([#4227](https://www.github.com/tauri-apps/tauri/pull/4227)) on 2022-05-27 + +## \[1.0.0-rc.12] + +- Properly fetch the NPM dependency information when using Yarn 2+. + - [cdfa6255](https://www.github.com/tauri-apps/tauri/commit/cdfa62551115586725bd3e4c04f12c5256f20790) fix(cli): properly read info when using yarn 2+, closes [#4106](https://www.github.com/tauri-apps/tauri/pull/4106) ([#4193](https://www.github.com/tauri-apps/tauri/pull/4193)) on 2022-05-23 + +## \[1.0.0-rc.11] + +- Allow configuring the display options for the MSI execution allowing quieter updates. + - [9f2c3413](https://www.github.com/tauri-apps/tauri/commit/9f2c34131952ea83c3f8e383bc3cec7e1450429f) feat(core): configure msiexec display options, closes [#3951](https://www.github.com/tauri-apps/tauri/pull/3951) ([#4061](https://www.github.com/tauri-apps/tauri/pull/4061)) on 2022-05-15 + +## \[1.0.0-rc.10] + +- Resolve binary file extension from target triple instead of compile-time checks to allow cross compilation. + - [4562e671](https://www.github.com/tauri-apps/tauri/commit/4562e671e4795e9386429348bf738f7078706945) fix(build): append .exe binary based on target triple instead of running OS, closes [#3870](https://www.github.com/tauri-apps/tauri/pull/3870) ([#4032](https://www.github.com/tauri-apps/tauri/pull/4032)) on 2022-05-03 +- Fixes text overflow on `tauri dev` on Windows. + - [094534d1](https://www.github.com/tauri-apps/tauri/commit/094534d138a9286e4746b61adff2da616e3b6a61) fix(cli): dev command stderr text overflow on Windows, closes [#3995](https://www.github.com/tauri-apps/tauri/pull/3995) ([#4000](https://www.github.com/tauri-apps/tauri/pull/4000)) on 2022-04-29 +- Improve CLI's logging output, making use of the standard rust `log` system. + - [35f21471](https://www.github.com/tauri-apps/tauri/commit/35f2147161e6697cbd2824681eeaf870b5a991c2) feat(cli): Improve CLI logging ([#4060](https://www.github.com/tauri-apps/tauri/pull/4060)) on 2022-05-07 +- Don't override the default keychain on macOS while code signing. + - [a4fcaf1d](https://www.github.com/tauri-apps/tauri/commit/a4fcaf1d04aafc3b4d42186f0fb386797d959a9d) fix: don't override default keychain, closes [#4008](https://www.github.com/tauri-apps/tauri/pull/4008) ([#4053](https://www.github.com/tauri-apps/tauri/pull/4053)) on 2022-05-05 +- - Remove startup delay in `tauri dev` caused by checking for a newer cli version. The check is now done upon process exit. +- Add `TAURI_SKIP_UPDATE_CHECK` env variable to skip checking for a newer CLI version. +- [bbabc8cd](https://www.github.com/tauri-apps/tauri/commit/bbabc8cd1ea2c1f6806610fd2d533c99305d320c) fix(cli.rs): remove startup delay in `tauri dev` ([#3999](https://www.github.com/tauri-apps/tauri/pull/3999)) on 2022-04-29 +- Fix `tauri info` panic when a package isn't installed. + - [4f0f3187](https://www.github.com/tauri-apps/tauri/commit/4f0f3187c9e69262ef28350331b368c831ab930a) fix(cli.rs): fix `tauri info` panic when a package isn't installed, closes [#3985](https://www.github.com/tauri-apps/tauri/pull/3985) ([#3996](https://www.github.com/tauri-apps/tauri/pull/3996)) on 2022-04-29 +- Added `$schema` support to `tauri.conf.json`. + - [715cbde3](https://www.github.com/tauri-apps/tauri/commit/715cbde3842a916c4ebeab2cab348e1774b5c192) feat(config): add `$schema` to `tauri.conf.json`, closes [#3464](https://www.github.com/tauri-apps/tauri/pull/3464) ([#4031](https://www.github.com/tauri-apps/tauri/pull/4031)) on 2022-05-03 +- **Breaking change:** The `dev` command now reads the custom config file from CWD instead of the Tauri folder. + - [a1929c6d](https://www.github.com/tauri-apps/tauri/commit/a1929c6dacccd00af4cdbcc4d29cfb98d8428f55) fix(cli): always read custom config file from CWD, closes [#4067](https://www.github.com/tauri-apps/tauri/pull/4067) ([#4074](https://www.github.com/tauri-apps/tauri/pull/4074)) on 2022-05-07 +- Fixes a Powershell crash when sending SIGINT to the dev command. + - [32048486](https://www.github.com/tauri-apps/tauri/commit/320484866b83ecabb01eb58d158e0fedd9dd08be) fix(cli): powershell crashing on SIGINT, closes [#3997](https://www.github.com/tauri-apps/tauri/pull/3997) ([#4007](https://www.github.com/tauri-apps/tauri/pull/4007)) on 2022-04-29 +- Prevent building when the bundle identifier is the default `com.tauri.dev`. + - [95726ebb](https://www.github.com/tauri-apps/tauri/commit/95726ebb6180d371be44bff9f16ca1eee049006a) feat(cli): prevent default bundle identifier from building, closes [#4041](https://www.github.com/tauri-apps/tauri/pull/4041) ([#4042](https://www.github.com/tauri-apps/tauri/pull/4042)) on 2022-05-04 + +## \[1.0.0-rc.9] + +- Exit CLI when Cargo returns a non-compilation error in `tauri dev`. + - [b5622882](https://www.github.com/tauri-apps/tauri/commit/b5622882cf3748e1e5a90915f415c0cd922aaaf8) fix(cli): exit on non-compilation Cargo errors, closes [#3930](https://www.github.com/tauri-apps/tauri/pull/3930) ([#3942](https://www.github.com/tauri-apps/tauri/pull/3942)) on 2022-04-22 +- Notify CLI update when running `tauri dev`. + - [a649aad7](https://www.github.com/tauri-apps/tauri/commit/a649aad7ad26d4578699370d6e63d80edeca1f97) feat(cli): check and notify about updates on `tauri dev`, closes [#3789](https://www.github.com/tauri-apps/tauri/pull/3789) ([#3960](https://www.github.com/tauri-apps/tauri/pull/3960)) on 2022-04-25 +- The CLI will not automatically run `strip` on release binaries anymore. Use the \[`strip`]\[strip] profile setting stabilized with Cargo 1.59. + +[`strip`]: https://doc.rust-lang.org/cargo/reference/profiles.html#strip + +- [62106224](https://www.github.com/tauri-apps/tauri/commit/621062246d065f21800d340126cf58315177f97e) refactor: drop strip from build command. closes [#3559](https://www.github.com/tauri-apps/tauri/pull/3559) ([#3863](https://www.github.com/tauri-apps/tauri/pull/3863)) on 2022-04-06 +- Kill the `beforeDevCommand` and app processes if the dev command returns an error. + - [485c9743](https://www.github.com/tauri-apps/tauri/commit/485c97438ac956d86bcf3794ceaa626bef968a4e) fix(cli): kill beforeDevCommand if dev code returns an error ([#3907](https://www.github.com/tauri-apps/tauri/pull/3907)) on 2022-04-19 +- Fix `info` command showing outdated text for latest versions. + - [73a4b74a](https://www.github.com/tauri-apps/tauri/commit/73a4b74aea8544e6fda51c1f6697630b0768072c) fix(cli.rs/info): don't show outdated text for latest versions ([#3829](https://www.github.com/tauri-apps/tauri/pull/3829)) on 2022-04-02 +- **Breaking change:** Enable default Cargo features except `tauri/custom-protocol` on the dev command. + - [f2a30d8b](https://www.github.com/tauri-apps/tauri/commit/f2a30d8bc54fc3ba49e16f69a413eca5f61a9b1f) refactor(core): use ayatana appindicator by default, keep option to use gtk ([#3916](https://www.github.com/tauri-apps/tauri/pull/3916)) on 2022-04-19 +- Kill the `beforeDevCommand` process recursively on Unix. + - [e251e1b0](https://www.github.com/tauri-apps/tauri/commit/e251e1b0991d26ab10aea33cfb228f3e7f0f85b5) fix(cli): kill before dev command recursively on Unix, closes [#2794](https://www.github.com/tauri-apps/tauri/pull/2794) ([#3848](https://www.github.com/tauri-apps/tauri/pull/3848)) on 2022-04-03 + +## \[1.0.0-rc.8] + +- Allows the `tauri.conf.json` file to be git ignored on the path lookup function. + - [cc7c2d77](https://www.github.com/tauri-apps/tauri/commit/cc7c2d77da2e4a39ec2a97b080d41a719e6d0161) feat(cli): allow conf path to be gitignored, closes [#3636](https://www.github.com/tauri-apps/tauri/pull/3636) ([#3683](https://www.github.com/tauri-apps/tauri/pull/3683)) on 2022-03-13 +- Remove `minimumSystemVersion: null` from the application template configuration. + - [c81534eb](https://www.github.com/tauri-apps/tauri/commit/c81534ebd873c358e0346c7949aeb171803149a5) feat(cli): use default macOS minimum system version when it is empty ([#3658](https://www.github.com/tauri-apps/tauri/pull/3658)) on 2022-03-13 +- Improve readability of the `info` subcommand output. + - [49d2f13f](https://www.github.com/tauri-apps/tauri/commit/49d2f13fc07d763d5de9bf4b19d00c901776c11d) feat(cli): colorful cli ([#3635](https://www.github.com/tauri-apps/tauri/pull/3635)) on 2022-03-08 +- Properly terminate the `beforeDevCommand` process. + - [94d78efb](https://www.github.com/tauri-apps/tauri/commit/94d78efbe542e7be3c00e3b2355c22803816715f) fix(cli.rs): terminate the beforeDevCommand, closes [#2794](https://www.github.com/tauri-apps/tauri/pull/2794) ([#2883](https://www.github.com/tauri-apps/tauri/pull/2883)) on 2022-03-27 +- Fixes DMG bundling on macOS 12.3. + - [348a1ab5](https://www.github.com/tauri-apps/tauri/commit/348a1ab59d2697478a594016016f1fccbf1ac054) fix(bundler): DMG bundling on macOS 12.3 cannot use bless, closes [#3719](https://www.github.com/tauri-apps/tauri/pull/3719) ([#3721](https://www.github.com/tauri-apps/tauri/pull/3721)) on 2022-03-18 +- Fixes resources bundling on Windows when the path is on the root of the Tauri folder. + - [4c84559e](https://www.github.com/tauri-apps/tauri/commit/4c84559e1f3019e7aa2666b10a1a0bd97bb09d24) fix(cli): root resource bundling on Windows, closes [#3539](https://www.github.com/tauri-apps/tauri/pull/3539) ([#3685](https://www.github.com/tauri-apps/tauri/pull/3685)) on 2022-03-13 + +## \[1.0.0-rc.7] + +- Added `tsp` config option under `tauri > bundle > windows`, which enables Time-Stamp Protocol (RFC 3161) for the timestamping + server under code signing on Windows if set to `true`. + - [bdd5f7c2](https://www.github.com/tauri-apps/tauri/commit/bdd5f7c2f03af4af8b60a9527e55bb18525d989b) fix: add support for Time-Stamping Protocol for Windows codesigning (fix [#3563](https://www.github.com/tauri-apps/tauri/pull/3563)) ([#3570](https://www.github.com/tauri-apps/tauri/pull/3570)) on 2022-03-07 +- Change the `plugin init` templates to use the new `tauri::plugin::Builder` syntax. + - [f7acb061](https://www.github.com/tauri-apps/tauri/commit/f7acb061e4d1ecdbfe182793587632d7ba6d8eff) feat(cli): use plugin::Builder syntax on the plugin template ([#3606](https://www.github.com/tauri-apps/tauri/pull/3606)) on 2022-03-03 + +## \[1.0.0-rc.6] + +- Improve "waiting for your dev server to start" message. + - [5999379f](https://www.github.com/tauri-apps/tauri/commit/5999379fb06052a115f04f99274ab46d1eefd659) chore(cli): improve "waiting for dev server" message, closes [#3491](https://www.github.com/tauri-apps/tauri/pull/3491) ([#3504](https://www.github.com/tauri-apps/tauri/pull/3504)) on 2022-02-18 +- Do not panic if the updater private key password is wrong. + - [17f17a80](https://www.github.com/tauri-apps/tauri/commit/17f17a80f818bcc20c387583a6ff00a8e07ec533) fix(cli): do not panic if private key password is wrong, closes [#3449](https://www.github.com/tauri-apps/tauri/pull/3449) ([#3495](https://www.github.com/tauri-apps/tauri/pull/3495)) on 2022-02-17 +- Check the current folder before checking the directories on the app and tauri dir path lookup function. + - [a06de376](https://www.github.com/tauri-apps/tauri/commit/a06de3760184caa71acfe7a2fe2189a033b565f5) fix(cli): path lookup should not check subfolder before the current one ([#3465](https://www.github.com/tauri-apps/tauri/pull/3465)) on 2022-02-15 +- Fixes the signature of the `signer sign` command to not have duplicated short flags. + - [a9755514](https://www.github.com/tauri-apps/tauri/commit/a975551461f3698db3f3b6afa5101189aaeeada9) fix(cli): duplicated short flag for `signer sign`, closes [#3483](https://www.github.com/tauri-apps/tauri/pull/3483) ([#3492](https://www.github.com/tauri-apps/tauri/pull/3492)) on 2022-02-17 + +## \[1.0.0-rc.5] + +- Allow passing arguments to the `build` runner (`tauri build -- ...`). + - [679fe1fe](https://www.github.com/tauri-apps/tauri/commit/679fe1fedd6ed016ab1140c8087c2d1404504bfb) feat(cli.rs): allow passing arguments to the build runner, closes [#3398](https://www.github.com/tauri-apps/tauri/pull/3398) ([#3431](https://www.github.com/tauri-apps/tauri/pull/3431)) on 2022-02-13 +- Improve error message when the dev runner command fails. + - [759d1afb](https://www.github.com/tauri-apps/tauri/commit/759d1afb86f3657f6071a2ae39c9be21e20ed22c) feat(cli): improve error message when dev runner command fails ([#3447](https://www.github.com/tauri-apps/tauri/pull/3447)) on 2022-02-13 +- Increase `tauri.conf.json` directory lookup depth to `3` and allow changing it with the `TAURI_PATH_DEPTH` environment variable. + - [c6031c70](https://www.github.com/tauri-apps/tauri/commit/c6031c7070c6bb7539bbfdfe42cb73012829c910) feat(cli): increase lookup depth, add env var option ([#3451](https://www.github.com/tauri-apps/tauri/pull/3451)) on 2022-02-13 +- Added `tauri-build`, `tao` and `wry` version to the `info` command output. + - [16f1173f](https://www.github.com/tauri-apps/tauri/commit/16f1173f456b1db543d0160df2c9828708bfc68a) feat(cli): add tao and wry version to the `info` output ([#3443](https://www.github.com/tauri-apps/tauri/pull/3443)) on 2022-02-13 +- **Breaking change:** The extra arguments passed to `tauri dev` using `-- ...` are now propagated to the runner (defaults to cargo). To pass arguments to your binary using Cargo, you now need to run `tauri dev -- -- ...` (notice the double `--`). + - [679fe1fe](https://www.github.com/tauri-apps/tauri/commit/679fe1fedd6ed016ab1140c8087c2d1404504bfb) feat(cli.rs): allow passing arguments to the build runner, closes [#3398](https://www.github.com/tauri-apps/tauri/pull/3398) ([#3431](https://www.github.com/tauri-apps/tauri/pull/3431)) on 2022-02-13 +- Change the `init` template configuration to disable CSP for better usability for new users. + - [102a5e9b](https://www.github.com/tauri-apps/tauri/commit/102a5e9bb83c5d8388dc9aedc7f03cc57bdae8cb) refactor(cli.rs): change template config CSP to null, closes [#3427](https://www.github.com/tauri-apps/tauri/pull/3427) ([#3429](https://www.github.com/tauri-apps/tauri/pull/3429)) on 2022-02-13 + +## \[1.0.0-rc.4] + +- Change default value for the `freezePrototype` configuration to `false`. + - Bumped due to a bump in tauri-utils. + - [3a4c0160](https://www.github.com/tauri-apps/tauri/commit/3a4c01606184be762adee055ddac803de0d28527) fix(core): change default `freezePrototype` to false, closes [#3416](https://www.github.com/tauri-apps/tauri/pull/3416) [#3406](https://www.github.com/tauri-apps/tauri/pull/3406) ([#3423](https://www.github.com/tauri-apps/tauri/pull/3423)) on 2022-02-12 + +## \[1.0.0-rc.3] + +- Fixes Tauri path resolution on projects without Git or a `.gitignore` file. + - [d8acbe11](https://www.github.com/tauri-apps/tauri/commit/d8acbe11492bd990e6983c7e63e0f1a8f1ea5c7c) fix(cli.rs): app path resolution on projects without git, closes [#3409](https://www.github.com/tauri-apps/tauri/pull/3409) ([#3410](https://www.github.com/tauri-apps/tauri/pull/3410)) on 2022-02-11 + +## \[1.0.0-rc.2] + +- Fix `init` command prompting for values even if the argument has been provided on the command line. + - [def76840](https://www.github.com/tauri-apps/tauri/commit/def76840257a1447723ecda13c807cf0c881f083) fix(cli.rs): do not prompt for `init` values if arg set ([#3400](https://www.github.com/tauri-apps/tauri/pull/3400)) on 2022-02-11 + - [41052dee](https://www.github.com/tauri-apps/tauri/commit/41052deeda2a00ee2b8ec2041c9c87c11de82ab2) fix(covector): add cli.js to change files on 2022-02-11 +- Fixes CLI freezing when running `light.exe` on Windows without the `--verbose` flag. + - [8beab636](https://www.github.com/tauri-apps/tauri/commit/8beab6363491e2a8757cc9fc0fa1eccc98ece916) fix(cli): build freezing on Windows, closes [#3399](https://www.github.com/tauri-apps/tauri/pull/3399) ([#3402](https://www.github.com/tauri-apps/tauri/pull/3402)) on 2022-02-11 +- Respect `.gitignore` configuration when looking for the folder with the `tauri.conf.json` file. + - [9c6c5a8c](https://www.github.com/tauri-apps/tauri/commit/9c6c5a8c52c6460d0b0a1a55300e1828262994ba) perf(cli.rs): improve performance of tauri dir lookup reading .gitignore ([#3405](https://www.github.com/tauri-apps/tauri/pull/3405)) on 2022-02-11 + - [41052dee](https://www.github.com/tauri-apps/tauri/commit/41052deeda2a00ee2b8ec2041c9c87c11de82ab2) fix(covector): add cli.js to change files on 2022-02-11 + +## \[1.0.0-rc.1] + +- Include `vswhere.exe` on the published package. + - [3227502e](https://www.github.com/tauri-apps/tauri/commit/3227502e8c9f137e5783cba2e0c692473cc5456d) fix(cli.rs): package `vswhere.exe` on 2022-02-10 + +## \[1.0.0-rc.0] + +- Do not force Tauri application code on `src-tauri` folder and use a glob pattern to look for a subfolder with a `tauri.conf.json` file. + - [a8cff6b3](https://www.github.com/tauri-apps/tauri/commit/a8cff6b3bc3288a53d7cdc5b3cb95d371309d2d6) feat(cli): do not enforce `src-tauri` folder structure, closes [#2643](https://www.github.com/tauri-apps/tauri/pull/2643) ([#2654](https://www.github.com/tauri-apps/tauri/pull/2654)) on 2021-09-27 +- Define `TAURI_PLATFORM`, `TAURI_ARCH`, `TAURI_FAMILY`, `TAURI_PLATFORM_TYPE`, `TAURI_PLATFORM_VERSION` and `TAURI_DEBUG` environment variables for the `beforeDevCommand` and `beforeBuildCommand` scripts. + - [8599313a](https://www.github.com/tauri-apps/tauri/commit/8599313a0f56d9777d335426467e79ba687be1d4) feat(cli.rs): env vars for beforeDev/beforeBuild commands, closes [#2610](https://www.github.com/tauri-apps/tauri/pull/2610) ([#2655](https://www.github.com/tauri-apps/tauri/pull/2655)) on 2021-09-26 + - [b5ee03a1](https://www.github.com/tauri-apps/tauri/commit/b5ee03a13a1c0d0ff677cf9c8d7ef28516fffa5b) feat(cli.rs): expose debug flag to beforeDev/beforeBuild commands ([#2727](https://www.github.com/tauri-apps/tauri/pull/2727)) on 2021-10-08 + - [9bb68973](https://www.github.com/tauri-apps/tauri/commit/9bb68973dd10f3cb98d2a95e5432bfc765d77064) fix(cli.rs): prefix the "before script" env vars with `TAURI_` ([#3274](https://www.github.com/tauri-apps/tauri/pull/3274)) on 2022-01-24 +- Allow `config` arg to be a path to a JSON file on the `dev` and `build` commands. + - [7b81e5b8](https://www.github.com/tauri-apps/tauri/commit/7b81e5b82e665fe0562b91ac33b63a871af9e111) feat(cli.rs): allow config argument to be a path to a JSON file ([#2938](https://www.github.com/tauri-apps/tauri/pull/2938)) on 2021-11-22 +- Add `rustup` version and active rust toolchain to the `info` command output. + - [28aaec87](https://www.github.com/tauri-apps/tauri/commit/28aaec87e2f6445859e9dbaaf2231d02d1e1d4b5) feat(cli.rs): add active toolchain and rustup to `tauri info`, closes [#2730](https://www.github.com/tauri-apps/tauri/pull/2730) ([#2986](https://www.github.com/tauri-apps/tauri/pull/2986)) on 2021-12-09 +- Add `Visual Studio Build Tools` installed versions to the `info` command output. + - [d5f07d14](https://www.github.com/tauri-apps/tauri/commit/d5f07d14f31954fe89ff5045fd4ef72cfc2b9ac1) feat(cli.rs): build tools info ([#2618](https://www.github.com/tauri-apps/tauri/pull/2618)) on 2021-09-21 +- The inferred development server port for Svelte is now `8080` (assumes latest Svelte with `sirv-cli >= 2.0.0`). + - [de0543f3](https://www.github.com/tauri-apps/tauri/commit/de0543f3e052d2981a95ce3baa8470592740a1a2) feat(cli.rs): change inferred dev server port to 8080 for Svelte apps on 2022-02-05 +- Detect if tauri is used from git in the `info` command. + - [65ad5b5e](https://www.github.com/tauri-apps/tauri/commit/65ad5b5ef923bf1e6b6f078d794d071a04fcdf57) feat(cli.rs/info): detect if tauri is used from git ([#3309](https://www.github.com/tauri-apps/tauri/pull/3309)) on 2022-02-05 +- Drop the `dialoguer` soft fork and use the published version instead. + - [b1f5c6d7](https://www.github.com/tauri-apps/tauri/commit/b1f5c6d7ac48c7407f28402afef0d3e521314127) refactor(cli.rs): drop `dialoguer` and `console` soft fork ([#2790](https://www.github.com/tauri-apps/tauri/pull/2790)) on 2021-10-22 +- Fix `build` command when executed on a 32-bit Windows machine when pulling from the `binary-releases` repo. + - [35588b2e](https://www.github.com/tauri-apps/tauri/commit/35588b2e04d5be8e5708583bdc52a012341bc75e) fix(cli.rs): check default arch at runtime, closes [#3067](https://www.github.com/tauri-apps/tauri/pull/3067) ([#3078](https://www.github.com/tauri-apps/tauri/pull/3078)) on 2021-12-27 +- The `generate` and `sign` commands are now available under a `signer` subcommand. + - [1458ab3c](https://www.github.com/tauri-apps/tauri/commit/1458ab3c535637ada996ab0ff3494cd75fe40bf7) refactor(cli.rs): `signer` and `plugin` subcommands, use new clap derive syntax ([#2928](https://www.github.com/tauri-apps/tauri/pull/2928)) on 2021-12-09 +- Use `tauri-utils` to get the `Config` types. + - [4de285c3](https://www.github.com/tauri-apps/tauri/commit/4de285c3967d32250d73acdd5d171a6fd332d2b3) feat(core): validate Cargo features matching allowlist \[TRI-023] on 2022-01-09 +- Print warning and exit if `distDir` contains `node_modules`, `src-tauri` or `target` folders. + - [7ed3f3b7](https://www.github.com/tauri-apps/tauri/commit/7ed3f3b7e4268708bbe8f83c45653e5d6704824b) feat(cli.rs): validate `distDir`, closes [#2554](https://www.github.com/tauri-apps/tauri/pull/2554) ([#2701](https://www.github.com/tauri-apps/tauri/pull/2701)) on 2021-10-04 +- Fix `tauri build` failing on Windows if `tauri.conf.json > tauri > bundle > Windows > wix > license` is used. + - [17a1ad68](https://www.github.com/tauri-apps/tauri/commit/17a1ad682363e51365b57899c8d7557b1b65201c) fix(cli.rs): ensure `target/release/wix` exists, closes [#2927](https://www.github.com/tauri-apps/tauri/pull/2927) ([#2987](https://www.github.com/tauri-apps/tauri/pull/2987)) on 2021-12-07 +- Added `dev_csp` to the `security` configuration object. + - [cf54dcf9](https://www.github.com/tauri-apps/tauri/commit/cf54dcf9c81730e42c9171daa9c8aa474c95b522) feat: improve `CSP` security with nonces and hashes, add `devCsp` \[TRI-004] ([#8](https://www.github.com/tauri-apps/tauri/pull/8)) on 2022-01-09 +- Kill process if `beforeDevCommand` exits with a non-zero status code. + - [a2d5929a](https://www.github.com/tauri-apps/tauri/commit/a2d5929a8f1f6310d186199cc54246fcb0a01b46) feat(cli.rs): wait for dev URL to be reachable, exit if command fails ([#3358](https://www.github.com/tauri-apps/tauri/pull/3358)) on 2022-02-08 +- Fixes output directory detection when using Cargo workspaces. + - [8d630bc8](https://www.github.com/tauri-apps/tauri/commit/8d630bc8c494cba6ac1604b7777b89b763044471) fix(cli.rs): fix workspace detection, fixes [#2614](https://www.github.com/tauri-apps/tauri/pull/2614), closes [#2515](https://www.github.com/tauri-apps/tauri/pull/2515) ([#2644](https://www.github.com/tauri-apps/tauri/pull/2644)) on 2021-09-23 +- Allow using a fixed version for the Webview2 runtime via the `tauri > bundle > windows > webviewFixedRuntimePath` config option. + - [85df94f2](https://www.github.com/tauri-apps/tauri/commit/85df94f2b0d40255812b42c5e32a70c4b45392df) feat(core): config for fixed webview2 runtime version path ([#27](https://www.github.com/tauri-apps/tauri/pull/27)) on 2021-11-02 +- Adds support for using JSON5 format for the `tauri.conf.json` file, along with also supporting the `.json5` extension. + +Here is the logic flow that determines if JSON or JSON5 will be used to parse the config: + +1. Check if `tauri.conf.json` exists + a. Parse it with `serde_json` + b. Parse it with `json5` if `serde_json` fails + c. Return original `serde_json` error if all above steps failed +2. Check if `tauri.conf.json5` exists + a. Parse it with `json5` + b. Return error if all above steps failed +3. Return error if all above steps failed + +- [995de57a](https://www.github.com/tauri-apps/tauri/commit/995de57a76cf51215277673e526d7ec32b86b564) Add seamless support for using JSON5 in the config file ([#47](https://www.github.com/tauri-apps/tauri/pull/47)) on 2022-02-03 +- Added `$ tauri plugin init` command, which initializes a Tauri plugin. + - [ac8e69a9](https://www.github.com/tauri-apps/tauri/commit/ac8e69a98ca1d6f646344ffdef1876cc9274323a) feat(cli.rs): add `init plugin` command, bootstraps a plugin project ([#2669](https://www.github.com/tauri-apps/tauri/pull/2669)) on 2021-09-27 + - [db275f0b](https://www.github.com/tauri-apps/tauri/commit/db275f0b633f44fb2f85755d32929dfb7893b1e0) refactor(cli.rs): rename `init plugin` subcommand to `plugin init` ([#2885](https://www.github.com/tauri-apps/tauri/pull/2885)) on 2021-11-13 +- **Breaking change:** Add `macos-private-api` feature flag, enabled via `tauri.conf.json > tauri > macOSPrivateApi`. + - [6ac21b3c](https://www.github.com/tauri-apps/tauri/commit/6ac21b3cef7f14358df38cc69ea3d277011accaf) feat: add private api feature flag ([#7](https://www.github.com/tauri-apps/tauri/pull/7)) on 2022-01-09 +- Move the copying of resources and sidecars from `cli.rs` to `tauri-build` so using the Cargo CLI directly processes the files for the application execution in development. + - [5eb72c24](https://www.github.com/tauri-apps/tauri/commit/5eb72c24deddf5a01093bea96b90c0d8806afc3f) refactor: copy resources and sidecars on the Cargo build script ([#3357](https://www.github.com/tauri-apps/tauri/pull/3357)) on 2022-02-08 +- The minimum Rust version is now `1.56`. + - [a9dfc015](https://www.github.com/tauri-apps/tauri/commit/a9dfc015505afe91281c2027954ffcc588b1a59c) feat: update to edition 2021 and set minimum rust to 1.56 ([#2789](https://www.github.com/tauri-apps/tauri/pull/2789)) on 2021-10-22 +- Automatically `strip` the built binary on Linux and macOS if `--debug` is not specified. + - [2f3a582c](https://www.github.com/tauri-apps/tauri/commit/2f3a582c69994d66f2035bbe62825eafc869d90f) feat(cli.rs): strip release binaries \[TRI-031] ([#22](https://www.github.com/tauri-apps/tauri/pull/22)) on 2022-01-09 +- Fixes pnpm error when running `pnpm tauri info`. + - [2026134f](https://www.github.com/tauri-apps/tauri/commit/2026134f471a212aea9b227a873ecc937dda1acc) fix(cli.rs): pnpm tauri info exits with error (fix [#2509](https://www.github.com/tauri-apps/tauri/pull/2509)) ([#2510](https://www.github.com/tauri-apps/tauri/pull/2510)) on 2021-08-24 +- Add support to building Universal macOS Binaries through the virtual target `universal-apple-darwin` (run `tauri build --target universal-apple-darwin`). + - [83f52fdb](https://www.github.com/tauri-apps/tauri/commit/83f52fdbe3a9ffd98dffc752e5f9e14322b56e60) feat: Add `universal-darwin-macos` build target, closes [#3317](https://www.github.com/tauri-apps/tauri/pull/3317) ([#3318](https://www.github.com/tauri-apps/tauri/pull/3318)) on 2022-02-04 +- Wait for `devPath` URL to be reachable before starting the application. Skipped if the `TAURI_SKIP_DEVSERVER_CHECK` environment variable is set to `true`. + - [a2d5929a](https://www.github.com/tauri-apps/tauri/commit/a2d5929a8f1f6310d186199cc54246fcb0a01b46) feat(cli.rs): wait for dev URL to be reachable, exit if command fails ([#3358](https://www.github.com/tauri-apps/tauri/pull/3358)) on 2022-02-08 +- On Windows, Fix `beforeDevCommand` and `beforeBuildCommand` not executing the expected command if it contains quotes. This is done by executing them with `CMD /S /C {command}` instead of `CMD /C {command}` on Windows. + - [52e9a6d8](https://www.github.com/tauri-apps/tauri/commit/52e9a6d81a701a66a8cf6a67c2b37d135583543f) fix: Make CMD handle quotes `"` properly. ([#3334](https://www.github.com/tauri-apps/tauri/pull/3334)) on 2022-02-06 +- Allow setting the localization file for WiX. + - [af329f27](https://www.github.com/tauri-apps/tauri/commit/af329f2722d6194c6d70e976fc970dc2c9e4de2b) feat(bundler): wix localization, closes [#3174](https://www.github.com/tauri-apps/tauri/pull/3174) ([#3179](https://www.github.com/tauri-apps/tauri/pull/3179)) on 2022-02-05 + +## \[1.0.0-beta.7] + +- Update cli.yml to pass clap ArgSettings::MultipleValues assertion. + - [0391ac3d](https://www.github.com/tauri-apps/tauri/commit/0391ac3dc96d9c74c34a957e4cb70da88a0a85b7) fix: Update cli.yml to pass clap ArgSettings::MultipleValues assertion. ([#2506](https://www.github.com/tauri-apps/tauri/pull/2506)) ([#2507](https://www.github.com/tauri-apps/tauri/pull/2507)) on 2021-08-22 + +## \[1.0.0-beta.6] + +- Added `APPLE_SIGNING_IDENTITY` as supported environment variable for the bundler. + - [44f6ee4c](https://www.github.com/tauri-apps/tauri/commit/44f6ee4cfdfad5fb21d96e69f8776c0e68685682) chore(ci): add step to detect code signing ([#2245](https://www.github.com/tauri-apps/tauri/pull/2245)) on 2021-08-08 +- Added configuration for the WiX banner icon under `tauri.conf.json > tauri > bundle > windows > wix > bannerPath`. + - [13003ec7](https://www.github.com/tauri-apps/tauri/commit/13003ec761b1530705d6129519dc4e226eb992c7) feat(bundler): add config for WiX banner path, closes [#2175](https://www.github.com/tauri-apps/tauri/pull/2175) ([#2448](https://www.github.com/tauri-apps/tauri/pull/2448)) on 2021-08-16 +- Added configuration for the WiX dialog background bitmap under `tauri.conf.json > tauri > bundle > windows > wix > dialogImagePath`. + - [9bfdeb42](https://www.github.com/tauri-apps/tauri/commit/9bfdeb42effeeec27aa15bbc5b05040eadfda5ba) feat(bundler): add config for WiX dialog image path ([#2449](https://www.github.com/tauri-apps/tauri/pull/2449)) on 2021-08-16 +- Only convert package name and binary name to kebab-case, keeping the `.desktop` `Name` field with the original configured value. + - [3f039cb8](https://www.github.com/tauri-apps/tauri/commit/3f039cb8a308b0f18deaa37d7cfb1cc50d308d0e) fix: keep original `productName` for .desktop `Name` field, closes [#2295](https://www.github.com/tauri-apps/tauri/pull/2295) ([#2384](https://www.github.com/tauri-apps/tauri/pull/2384)) on 2021-08-10 +- Merge platform-specific `tauri.linux.conf.json`, `tauri.windows.conf.json` and `tauri.macos.conf.json` into the config JSON from `tauri.conf.json`. + - [71d687b7](https://www.github.com/tauri-apps/tauri/commit/71d687b787cd722c60879adda543897826bf42c9) feat(cli.rs): platform-specific conf.json ([#2309](https://www.github.com/tauri-apps/tauri/pull/2309)) on 2021-07-28 +- Update minimum Rust version to 1.54.0. + - [a5394716](https://www.github.com/tauri-apps/tauri/commit/a53947160985a4f5b0ad1fbb4aa6865d6f852c66) chore: update rust to 1.54.0 ([#2434](https://www.github.com/tauri-apps/tauri/pull/2434)) on 2021-08-15 + +## \[1.0.0-beta.5] + +- Run powershell commands with `-NoProfile` flag + - [3e6f3416](https://www.github.com/tauri-apps/tauri/commit/3e6f34160deab4f774d90aba28122e5b6b6f9db2) fix(cli.rs): run powershell kill command without profile ([#2130](https://www.github.com/tauri-apps/tauri/pull/2130)) on 2021-06-30 +- Adds `release` argument to the `dev` command. Allowing to run the backend in release mode during development. + - [7ee2dc8b](https://www.github.com/tauri-apps/tauri/commit/7ee2dc8b690703f509ab2d6ecdf9dafd6b72cd0b) feat(cli.rs): add release argument to the dev command ([#2192](https://www.github.com/tauri-apps/tauri/pull/2192)) on 2021-07-12 +- Fixes `center` and `focus` not being allowed in `tauri.conf.json > tauri > windows` and ignored in `WindowBuilderWrapper`. + - [bc2c331d](https://www.github.com/tauri-apps/tauri/commit/bc2c331dec3dec44c79e659b082b5fb6b65cc5ea) fix: center and focus not being allowed in config ([#2199](https://www.github.com/tauri-apps/tauri/pull/2199)) on 2021-07-12 + +## \[1.0.0-beta.4] + +- Improve error message when the product name is invalid. + - [1a41e9f0](https://www.github.com/tauri-apps/tauri/commit/1a41e9f040cfa18b6cc1380dfe21251d56e3f973) feat(cli.rs): improve error message on app rename, closes [#2101](https://www.github.com/tauri-apps/tauri/pull/2101) ([#2114](https://www.github.com/tauri-apps/tauri/pull/2114)) on 2021-06-28 + +## \[1.0.0-beta.3] + +- Properly detect target platform's architecture. + - [628a53eb](https://www.github.com/tauri-apps/tauri/commit/628a53eb6176f811d22d7730f08a99e5c370dbf4) fix(cli): properly detect target architecture, closes [#2040](https://www.github.com/tauri-apps/tauri/pull/2040) ([#2102](https://www.github.com/tauri-apps/tauri/pull/2102)) on 2021-06-28 +- Fixes `build` command when the `target` arg is set. + - [8e238701](https://www.github.com/tauri-apps/tauri/commit/8e2387018940e9e1421948d74a82156661ce2e4b) fix(cli.rs): fix out dir detection when target arg is set, closes [#2040](https://www.github.com/tauri-apps/tauri/pull/2040) ([#2098](https://www.github.com/tauri-apps/tauri/pull/2098)) on 2021-06-27 + +## \[1.0.0-beta.2] + +- Support `cargo tauri build` on Apple M1 chip. + - [3bf853d7](https://www.github.com/tauri-apps/tauri/commit/3bf853d782b491ad4965a1da25d19337eeac161f) feat(cli.rs): support tauri build on M1 chip ([#1915](https://www.github.com/tauri-apps/tauri/pull/1915)) on 2021-05-29 +- Infer `app name` and `window title` from `package.json > productName` or `package.json > name`. + Infer `distDir` and `devPath` by reading the package.json and trying to determine the UI framework (Vue.js, Angular, React, Svelte and some UI frameworks). + - [21a971c3](https://www.github.com/tauri-apps/tauri/commit/21a971c3b76bf0c26d00b2520b4976fa526738f5) feat(cli.rs): infer devPath/distDir/appName from package.json ([#1930](https://www.github.com/tauri-apps/tauri/pull/1930)) on 2021-05-31 +- Watch workspace crates on `dev` command. + - [86a23ff3](https://www.github.com/tauri-apps/tauri/commit/86a23ff30b4f18effa39c87b7cae6b7e324d131c) added support for cargo workspaces for `dev` command ([#1827](https://www.github.com/tauri-apps/tauri/pull/1827)) on 2021-05-13 +- Adds `features` argument to the `dev` and `build` commands. + - [6ec8e84d](https://www.github.com/tauri-apps/tauri/commit/6ec8e84d9172c090ee1549db56c98c66f12436ff) feat(cli.rs): add `features` arg to dev/build ([#1828](https://www.github.com/tauri-apps/tauri/pull/1828)) on 2021-05-13 +- Fixes the libwebkit2gtk package name. + - [e08065d7](https://www.github.com/tauri-apps/tauri/commit/e08065d7fe8398b41180b3a64854ec8e71174d42) fix: deb installation error ([#1844](https://www.github.com/tauri-apps/tauri/pull/1844)) on 2021-05-18 +- Properly keep all `tauri` features that are not managed by the CLI. + - [17c7c439](https://www.github.com/tauri-apps/tauri/commit/17c7c4396ff2d5e13fc8726c2965b4e810fad6b9) refactor(core): use `attohttpc` by default ([#1861](https://www.github.com/tauri-apps/tauri/pull/1861)) on 2021-05-19 +- Copy resources and binaries to `OUT_DIR` on `tauri dev` command. + - [8f29a260](https://www.github.com/tauri-apps/tauri/commit/8f29a260e67aa111f6aeb262bd846a46d2858ce9) fix(cli.rs): copy resources and binaries on dev, closes [#1298](https://www.github.com/tauri-apps/tauri/pull/1298) ([#1946](https://www.github.com/tauri-apps/tauri/pull/1946)) on 2021-06-04 +- Read cargo features from `tauri.conf.json > build > features` and propagate them on `dev` and `build`. + - [2b814e9c](https://www.github.com/tauri-apps/tauri/commit/2b814e9c937489af0acb56051bd01c0d7fca2413) added cargo features to tauri config ([#1824](https://www.github.com/tauri-apps/tauri/pull/1824)) on 2021-05-13 +- Fixes `tauri.conf.json > tauri > bundle > targets` not applying to the bundler. + - [8be35ced](https://www.github.com/tauri-apps/tauri/commit/8be35ced78658de732360e3b20d7d70108c9b32d) fix(cli.rs): `tauri.conf.json > tauri > bundle > targets` being ignored ([#1945](https://www.github.com/tauri-apps/tauri/pull/1945)) on 2021-06-04 +- Fixes `info` command not striping `\r` from child process version output. + - [6a95d7ac](https://www.github.com/tauri-apps/tauri/commit/6a95d7acc378b40230bab18d00ea32de40a5818c) fix(cli.rs): `info` version checks not striping `\r` on Windows ([#1952](https://www.github.com/tauri-apps/tauri/pull/1952)) on 2021-06-05 +- Allow setting a path to a license file for the Windows Installer (`tauri.conf.json > bundle > windows > wix > license`). + - [b769c7f7](https://www.github.com/tauri-apps/tauri/commit/b769c7f7da4064b6133bf39a82127863d0d35531) feat(bundler): windows installer license, closes [#2009](https://www.github.com/tauri-apps/tauri/pull/2009) ([#2027](https://www.github.com/tauri-apps/tauri/pull/2027)) on 2021-06-21 +- Change the `csp` value on the template to include `wss:` and `tauri:` to the `default-src` attribute. + - [463fd00d](https://www.github.com/tauri-apps/tauri/commit/463fd00d06241c734994fe8e1882788dc30cc993) fix(csp): add wss and tauri to conf template ([#1974](https://www.github.com/tauri-apps/tauri/pull/1974)) on 2021-06-15 +- Adds `tauri > bundle > windows > wix > language` config option. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables. + - [47919619](https://www.github.com/tauri-apps/tauri/commit/47919619815900fc3af47ec5873e31afb778b0ad) feat(bundler): allow setting wix language, closes [#1976](https://www.github.com/tauri-apps/tauri/pull/1976) ([#1988](https://www.github.com/tauri-apps/tauri/pull/1988)) on 2021-06-15 + +## \[1.0.0-beta.1] + +- Add `'self'` to default CSP because otherwise no joy on macOS. + - [12268e6](https://www.github.com/tauri-apps/tauri/commit/12268e6e69dc9a7034652f50316d3545cac687c7) fix(csp): add 'self' ([#1794](https://www.github.com/tauri-apps/tauri/pull/1794)) on 2021-05-12 +- Fix a typo that would result in bundle arg being ignored. + - [71f6a5e](https://www.github.com/tauri-apps/tauri/commit/71f6a5ed442a43bf1008043c95a1a90effdd2f81) fix(cli.rs/build): fix typo getting bundle arg ([#1783](https://www.github.com/tauri-apps/tauri/pull/1783)) on 2021-05-12 + +## \[1.0.0-beta.0] + +- Fixes a cargo `target/` cache issue. + - [79feb6a](https://www.github.com/tauri-apps/tauri/commit/79feb6a918c2b40af771b5dccc94c8f6f4176986) fix(cli.rs): cargo build failed due to cache issue, closes [#1543](https://www.github.com/tauri-apps/tauri/pull/1543) ([#1741](https://www.github.com/tauri-apps/tauri/pull/1741)) on 2021-05-07 +- Improve error logging. + - [5cc4b11](https://www.github.com/tauri-apps/tauri/commit/5cc4b11f5d00a1e7e580e31785b31c491c06d8d7) feat(cli.rs): add context to errors ([#1674](https://www.github.com/tauri-apps/tauri/pull/1674)) on 2021-05-01 +- Adds Webview2 version on `info` command. + - [2b4e2b7](https://www.github.com/tauri-apps/tauri/commit/2b4e2b7560515b76002d0c724bcca1f470ed106f) feat(cli.rs/info): get webview2 version on windows ([#1669](https://www.github.com/tauri-apps/tauri/pull/1669)) on 2021-05-04 +- Adds `--runner [PROGRAM]` argument on the `dev` and `build` command, allowing using the specified program to run and build the application (example program: `cross`). + - [5c1fe52](https://www.github.com/tauri-apps/tauri/commit/5c1fe52c2bd74e2a8f6c99c2870af967e6309e8d) feat(cli.rs): allow using cross instead of cargo, add target triple arg ([#1664](https://www.github.com/tauri-apps/tauri/pull/1664)) on 2021-04-30 +- Adds `--target [TARGET_TRIPLE]` option to the `build` command (example: `--target arm-unknown-linux-gnueabihf`). + - [5c1fe52](https://www.github.com/tauri-apps/tauri/commit/5c1fe52c2bd74e2a8f6c99c2870af967e6309e8d) feat(cli.rs): allow using cross instead of cargo, add target triple arg ([#1664](https://www.github.com/tauri-apps/tauri/pull/1664)) on 2021-04-30 +- Rename `--target` option on the `build` command to `--bundle`. + - [5c1fe52](https://www.github.com/tauri-apps/tauri/commit/5c1fe52c2bd74e2a8f6c99c2870af967e6309e8d) feat(cli.rs): allow using cross instead of cargo, add target triple arg ([#1664](https://www.github.com/tauri-apps/tauri/pull/1664)) on 2021-04-30 +- Automatically add Tauri dependencies to the debian package `Depends` section. + - [72b8048](https://www.github.com/tauri-apps/tauri/commit/72b8048b5ada7a18d71b0fd8a4a0177109b43db7) feat(cli.rs): fill debian `depends` with tauri dependencies ([#1767](https://www.github.com/tauri-apps/tauri/pull/1767)) on 2021-05-10 +- Properly kill `beforeDevCommand` process. + - [ac2cbcb](https://www.github.com/tauri-apps/tauri/commit/ac2cbcb131819e01074e1ed8fb6808260c56a027) fix(cli.rs): `before dev` process kill, closes [#1626](https://www.github.com/tauri-apps/tauri/pull/1626) ([#1700](https://www.github.com/tauri-apps/tauri/pull/1700)) on 2021-05-04 +- Adds support to `tauri` dependency as string and table on `Cargo.toml`. + - [df8bdcf](https://www.github.com/tauri-apps/tauri/commit/df8bdcf0631fd4e1e7035eb20a954574da96de66) feat(cli.rs): add support to string and table dependency, closes [#1653](https://www.github.com/tauri-apps/tauri/pull/1653) ([#1654](https://www.github.com/tauri-apps/tauri/pull/1654)) on 2021-04-29 +- Show `framework` and `bundler` on the `info` command by reading the `package.json` file and matching known dependencies. + - [152c755](https://www.github.com/tauri-apps/tauri/commit/152c755c4787b323ca3469c45934cc1e4d368cfa) feat(cli.rs): `framework` and `bundler` on info cmd, closes [#1681](https://www.github.com/tauri-apps/tauri/pull/1681) ([#1682](https://www.github.com/tauri-apps/tauri/pull/1682)) on 2021-05-02 + +## \[1.0.0-beta-rc.4] + +- Fixes the Message `command` name value on plugin invoke handler. + - Bumped due to a bump in tauri. + - [422dd5e](https://www.github.com/tauri-apps/tauri/commit/422dd5e2a0a03bb1556915c78f110bfab092c874) fix(core): command name on plugin invoke handler ([#1577](https://www.github.com/tauri-apps/tauri/pull/1577)) on 2021-04-21 + - [f575aaa](https://www.github.com/tauri-apps/tauri/commit/f575aaad71f23d44b2f89cf9ee5d84817dc3bb7a) fix: change files not referencing core packages ([#1619](https://www.github.com/tauri-apps/tauri/pull/1619)) on 2021-04-25 +- The package info APIs now checks the `package` object on `tauri.conf.json`. + - Bumped due to a bump in tauri. + - [8fd1baf](https://www.github.com/tauri-apps/tauri/commit/8fd1baf69b14bb81d7be9d31605ed7f02058b392) fix(core): pull package info from tauri.conf.json if set ([#1581](https://www.github.com/tauri-apps/tauri/pull/1581)) on 2021-04-22 + - [f575aaa](https://www.github.com/tauri-apps/tauri/commit/f575aaad71f23d44b2f89cf9ee5d84817dc3bb7a) fix: change files not referencing core packages ([#1619](https://www.github.com/tauri-apps/tauri/pull/1619)) on 2021-04-25 + +## \[1.0.0-beta-rc.3] + +- Check if distDir assets are built after running `beforeDevCommand`. + - [a670d3a](https://www.github.com/tauri-apps/tauri/commit/a670d3a457bc0c0135b879c746d26a5f121c87a7) fix(cli.rs): check if distDir exists after running `beforeDevCommand` ([#1586](https://www.github.com/tauri-apps/tauri/pull/1586)) on 2021-04-22 +- Fixes `tauri info` display version for the `@tauri-apps/api` package. + - [0012782](https://www.github.com/tauri-apps/tauri/commit/0012782e43bd4e7e49528853c226b8e0e24b8794) fix(cli.rs): `info` command `npm_package_version` parsing `beta-rc` ([#1587](https://www.github.com/tauri-apps/tauri/pull/1587)) on 2021-04-22 +- Fixes crash on usage of modifier keys on Windows when running `tauri init`. + - [d623d95](https://www.github.com/tauri-apps/tauri/commit/d623d95fcb67736bc0862866b347c7102cde66aa) fix(cli.rs): inliner dialoguer & console until they publish, fixes [#1492](https://www.github.com/tauri-apps/tauri/pull/1492) ([#1610](https://www.github.com/tauri-apps/tauri/pull/1610)) on 2021-04-25 +- Enable `tauri` `updater` feature when `tauri.conf.json > tauri > updater > active` is set to `true`. + - [9490b25](https://www.github.com/tauri-apps/tauri/commit/9490b257d2564840eb0c9167340bf444bca84699) fix(cli.rs): enable the `updater` feature on cli ([#1597](https://www.github.com/tauri-apps/tauri/pull/1597)) on 2021-04-23 + +## \[1.0.0-beta-rc.2] + +- Add missing camelcase rename for config + - [bdf7072](https://www.github.com/tauri-apps/tauri/commit/bdf707285e3d307ab083009c274ccb56d5053ff2) fix(cli.rs/info): add missing camelCase rename ([#1505](https://www.github.com/tauri-apps/tauri/pull/1505)) on 2021-04-14 +- Fix `tauri info` +- Properly detect `yarn` and `npm` versions on windows. +- Fix a panic caused by a wrong field name in `metadata.json` +- [71666e9](https://www.github.com/tauri-apps/tauri/commit/71666e9f9cfb5499a727b3f95182e89073f67d7b) fix(cli.rs): fix panic & use `cmd` to run `yarn`&`npm` on windows ([#1511](https://www.github.com/tauri-apps/tauri/pull/1511)) on 2021-04-17 +- Sync `metadata.json` via script to update version reference to cli.js, tauri (core) and tauri-build. + - [1f64927](https://www.github.com/tauri-apps/tauri/commit/1f64927362ef20761d7cd3591281519eb292aa33) chore: sync cli.rs metadata.json file versions ([#1534](https://www.github.com/tauri-apps/tauri/pull/1534)) on 2021-04-19 + +## \[1.0.0-beta-rc.1] + +- Missing the `files` property in the package.json which mean that the `dist` directory was not published and used. + - Bumped due to a bump in api. + - [b2569a7](https://www.github.com/tauri-apps/tauri/commit/b2569a729a3caa88bdba62abc31f0665e1323aaa) fix(js-api): dist ([#1498](https://www.github.com/tauri-apps/tauri/pull/1498)) on 2021-04-15 + +## \[1.0.0-beta-rc.0] + +- You can now run `cargo tauri build -t none` to speed up the build if you don't need executables. + - [4d507f9](https://www.github.com/tauri-apps/tauri/commit/4d507f9adfb26819f9d6406b191fdaa6188145f4) feat(cli/core): add support for building without targets ([#1203](https://www.github.com/tauri-apps/tauri/pull/1203)) on 2021-02-10 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 +- The `dev` and `build` pipeline is now written in Rust. + - [3e8abe3](https://www.github.com/tauri-apps/tauri/commit/3e8abe376407bb0ca8893602590ed9edf7aa71a1) feat(cli) rewrite the core CLI in Rust ([#851](https://www.github.com/tauri-apps/tauri/pull/851)) on 2021-01-30 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 +- Run `beforeDevCommand` and `beforeBuildCommand` in a shell. + - [32eb0d5](https://www.github.com/tauri-apps/tauri/commit/32eb0d562b135d8df19c78ff22aa53c73f459c76) feat(cli): run beforeDev and beforeBuild in a shell, closes [#1295](https://www.github.com/tauri-apps/tauri/pull/1295) ([#1399](https://www.github.com/tauri-apps/tauri/pull/1399)) on 2021-03-28 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 +- Fixes `` polyfill. + - [4ee044a](https://www.github.com/tauri-apps/tauri/commit/4ee044a3e662a0ac2be98f7e1286088d721c3307) fix(cli): use correct arg in `_blanks` links polyfill ([#1362](https://www.github.com/tauri-apps/tauri/pull/1362)) on 2021-03-17 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 +- Update all code files to have our license header. + - [bf82136](https://www.github.com/tauri-apps/tauri/commit/bf8213646689175f8a158b956911f3a43e360690) feat(license): SPDX Headers ([#1449](https://www.github.com/tauri-apps/tauri/pull/1449)) on 2021-04-11 + - [a6def70](https://www.github.com/tauri-apps/tauri/commit/a6def7066eec19c889b0f14cc1e475bf209a332e) Refactor(tauri): move tauri-api and tauri-updater to tauri ([#1455](https://www.github.com/tauri-apps/tauri/pull/1455)) on 2021-04-11 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 +- Adds `productName` and `version` configs on `tauri.conf.json > package`. + - [5b3d9b2](https://www.github.com/tauri-apps/tauri/commit/5b3d9b2c07da766f81981ba7c4961cd354d51340) feat(config): allow setting product name and version on tauri.conf.json ([#1358](https://www.github.com/tauri-apps/tauri/pull/1358)) on 2021-03-22 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 +- The `info` command was rewritten in Rust. + - [c3e06ee](https://www.github.com/tauri-apps/tauri/commit/c3e06ee9e88b3631da6eeb17d61ddd41cd5c6fe9) refactor(cli): rewrite info in Rust ([#1389](https://www.github.com/tauri-apps/tauri/pull/1389)) on 2021-03-25 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 +- The `init` command was rewritten in Rust. + - [f72b93b](https://www.github.com/tauri-apps/tauri/commit/f72b93b676ba8c48fd9273c187de3dbbc410fa0f) refactor(cli): rewrite init command in Rust ([#1382](https://www.github.com/tauri-apps/tauri/pull/1382)) on 2021-03-24 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 +- All the arguments passed after `tauri dev --` are now propagated to the binary. + - [4e9d31c](https://www.github.com/tauri-apps/tauri/commit/4e9d31c70ba13f1cabe830c6519a1b5f4789fd7b) feat(cli): propagate args passed after `dev --`, closes [#1406](https://www.github.com/tauri-apps/tauri/pull/1406) ([#1407](https://www.github.com/tauri-apps/tauri/pull/1407)) on 2021-03-30 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 +- Alpha version of tauri-updater. Please refer to the `README` for more details. + - [6d70c8e](https://www.github.com/tauri-apps/tauri/commit/6d70c8e1e256fe839c4a947375bb529d7b4f7301) feat(updater): Alpha version ([#643](https://www.github.com/tauri-apps/tauri/pull/643)) on 2021-04-05 + - [a6def70](https://www.github.com/tauri-apps/tauri/commit/a6def7066eec19c889b0f14cc1e475bf209a332e) Refactor(tauri): move tauri-api and tauri-updater to tauri ([#1455](https://www.github.com/tauri-apps/tauri/pull/1455)) on 2021-04-11 + - [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12 diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs new file mode 100644 index 0000000..bbad869 --- /dev/null +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -0,0 +1,5432 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +//! The [`wry`] Tauri [`Runtime`]. +//! +//! None of the exposed API of this crate is stable, and it may break semver +//! compatibility in the future. The major version only signifies the intended Tauri version. + +#![doc( + html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png", + html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png" +)] + +use self::monitor::MonitorExt; +use http::Request; +#[cfg(target_os = "macos")] +use objc2::ClassType; +use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle}; + +#[cfg(windows)] +use tauri_runtime::webview::ScrollBarStyle; +use tauri_runtime::{ + Cookie, DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, + ProgressBarState, ProgressBarStatus, Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, + UserAttentionType, UserEvent, WebviewDispatch, WebviewEventId, WindowDispatch, WindowEventId, + dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, + monitor::Monitor, + webview::{DetachedWebview, DownloadEvent, PendingWebview, WebviewIpcHandler}, + window::{ + CursorIcon, DetachedWindow, DetachedWindowWebview, DragDropEvent, PendingWindow, RawWindow, + WebviewEvent, WindowBuilder, WindowBuilderBase, WindowEvent, WindowId, WindowSizeConstraints, + }, +}; + +#[cfg(target_vendor = "apple")] +use objc2::rc::Retained; +#[cfg(target_os = "android")] +use tao::platform::android::{WindowBuilderExtAndroid, WindowExtAndroid}; +#[cfg(target_os = "macos")] +use tao::platform::macos::{EventLoopWindowTargetExtMacOS, WindowBuilderExtMacOS}; +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +use tao::platform::unix::{WindowBuilderExtUnix, WindowExtUnix}; +#[cfg(windows)] +use tao::platform::windows::{WindowBuilderExtWindows, WindowExtWindows}; +#[cfg(windows)] +use webview2_com::{ + ContainsFullScreenElementChangedEventHandler, FocusChangedEventHandler, + Microsoft::Web::WebView2::Win32::ICoreWebView2Controller, +}; +#[cfg(windows)] +use windows::Win32::Foundation::HWND; +#[cfg(target_os = "ios")] +use wry::WebViewBuilderExtIos; +#[cfg(target_os = "macos")] +use wry::WebViewBuilderExtMacos; +#[cfg(windows)] +use wry::WebViewBuilderExtWindows; +#[cfg(target_vendor = "apple")] +use wry::{WebViewBuilderExtDarwin, WebViewExtDarwin}; + +use tao::{ + 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::PreventOverflowConfig; +use tauri_utils::{ + Theme, + config::{Color, WindowConfig}, +}; +use url::Url; +#[cfg(windows)] +use wry::ScrollBarStyle as WryScrollBarStyle; +use wry::{ + DragDropEvent as WryDragDropEvent, ProxyConfig, ProxyEndpoint, WebContext as WryWebContext, + WebView, WebViewBuilder, +}; + +pub use tao; +pub use tao::window::{Window, WindowBuilder as TaoWindowBuilder, WindowId as TaoWindowId}; +pub use wry; +pub use wry::webview_version; + +#[cfg(windows)] +use wry::WebViewExtWindows; +#[cfg(target_os = "android")] +use wry::{ + WebViewBuilderExtAndroid, WebViewExtAndroid, + prelude::{dispatch, find_class}, +}; +#[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" +)))] +use wry::{WebViewBuilderExtUnix, WebViewExtUnix}; + +#[cfg(target_os = "ios")] +pub use tao::platform::ios::{WindowBuilderExtIOS, WindowExtIOS}; +#[cfg(target_os = "macos")] +pub use tao::platform::macos::{ + ActivationPolicy as TaoActivationPolicy, EventLoopExtMacOS, WindowExtMacOS, +}; +#[cfg(target_os = "macos")] +use tauri_runtime::ActivationPolicy; + +use std::{ + cell::RefCell, + collections::{ + BTreeMap, HashMap, HashSet, + hash_map::Entry::{Occupied, Vacant}, + }, + fmt, + ops::Deref, + path::PathBuf, + rc::Rc, + sync::{ + Arc, Mutex, Weak, + atomic::{AtomicBool, AtomicU32, Ordering}, + mpsc::{Sender, channel}, + }, + thread::{ThreadId, current as current_thread}, +}; + +pub type WebviewId = u32; +type IpcHandler = dyn Fn(Request) + 'static; + +#[cfg(not(debug_assertions))] +mod dialog; +mod monitor; +#[cfg(any( + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +mod undecorated_resizing; +mod util; +mod webview; +mod webview_permissions; +mod window; + +pub use webview::Webview; +use window::WindowExt as _; + +#[derive(Debug)] +pub struct WebContext { + pub inner: WryWebContext, + pub referenced_by_webviews: HashSet, + // on Linux the custom protocols are associated with the context + // and you cannot register a URI scheme more than once + pub registered_custom_protocols: HashSet, +} + +pub type WebContextStore = Arc, WebContext>>>; +// window +pub type WindowEventHandler = Box; +pub type WindowEventListeners = Arc>>; +pub type WebviewEventHandler = Box; +pub type WebviewEventListeners = Arc>>; + +#[derive(Debug, Clone, Default)] +pub struct WindowIdStore(Arc>>); + +impl WindowIdStore { + pub fn insert(&self, w: TaoWindowId, id: WindowId) { + self.0.lock().unwrap().insert(w, id); + } + + pub fn get(&self, w: &TaoWindowId) -> Option { + self.0.lock().unwrap().get(w).copied() + } +} + +#[macro_export] +macro_rules! getter { + ($self: ident, $rx: expr, $message: expr) => {{ + $self.context.send_user_message($message)?; + $rx + .recv() + .map_err(|_| $crate::Error::FailedToReceiveMessage) + }}; +} + +macro_rules! window_getter { + ($self: ident, $message: expr) => {{ + let (tx, rx) = channel(); + getter!($self, rx, Message::Window($self.window_id, $message(tx))) + }}; +} + +macro_rules! event_loop_window_getter { + ($self: ident, $message: expr) => {{ + let (tx, rx) = channel(); + getter!($self, rx, Message::EventLoopWindowTarget($message(tx))) + }}; +} + +macro_rules! webview_getter { + ($self: ident, $message: expr) => {{ + let (tx, rx) = channel(); + getter!( + $self, + rx, + Message::Webview( + *$self.window_id.lock().unwrap(), + $self.webview_id, + $message(tx) + ) + ) + }}; +} + +#[derive(Clone)] +pub struct Context { + pub window_id_map: WindowIdStore, + main_thread_id: ThreadId, + pub proxy: TaoEventLoopProxy>, + main_thread: DispatcherMainThreadContext, + plugins: Arc + Send>>>>, + next_window_id: Arc, + next_webview_id: Arc, + next_window_event_id: Arc, + next_webview_event_id: Arc, + webview_runtime_installed: bool, +} + +unsafe impl Send for Context {} +unsafe impl Sync for Context {} + +impl Context { + pub fn run_threaded(&self, f: F) -> R + where + F: FnOnce(Option<&DispatcherMainThreadContext>) -> R, + { + f(if current_thread().id() == self.main_thread_id { + Some(&self.main_thread) + } else { + None + }) + } + + fn send_user_message(&self, message: Message) -> Result<()> { + if current_thread().id() == self.main_thread_id { + handle_user_message( + &self.main_thread.window_target, + message, + UserMessageContext { + window_id_map: &self.window_id_map, + windows: &self.main_thread.windows, + }, + ); + Ok(()) + } else { + self + .proxy + .send_event(message) + .map_err(|_| Error::FailedToSendMessage) + } + } + + fn next_window_id(&self) -> WindowId { + self.next_window_id.fetch_add(1, Ordering::Relaxed).into() + } + + fn next_webview_id(&self) -> WebviewId { + self.next_webview_id.fetch_add(1, Ordering::Relaxed) + } + + fn next_window_event_id(&self) -> u32 { + self.next_window_event_id.fetch_add(1, Ordering::Relaxed) + } + + fn next_webview_event_id(&self) -> u32 { + self.next_webview_event_id.fetch_add(1, Ordering::Relaxed) + } +} + +impl Context { + fn create_window( + &self, + pending: PendingWindow>, + after_window_creation: Option, + ) -> Result>> { + let label = pending.label.clone(); + let context = self.clone(); + let window_id = self.next_window_id(); + let (webview_id, use_https_scheme) = pending + .webview + .as_ref() + .map(|w| { + ( + Some(context.next_webview_id()), + w.webview_attributes.use_https_scheme, + ) + }) + .unwrap_or((None, false)); + + let (tx, rx) = channel(); + self.send_user_message(Message::CreateWindow( + window_id, + Box::new(move |event_loop| { + create_window( + window_id, + webview_id.unwrap_or_default(), + event_loop, + &context, + pending, + after_window_creation, + ) + }), + tx, + ))?; + rx.recv() + .map_err(|_| crate::Error::FailedToReceiveMessage)??; + + let dispatcher = WryWindowDispatcher { + window_id, + context: self.clone(), + }; + + let detached_webview = webview_id.map(|id| { + let webview = DetachedWebview { + label: label.clone(), + dispatcher: WryWebviewDispatcher { + window_id: Arc::new(Mutex::new(window_id)), + webview_id: id, + context: self.clone(), + }, + }; + DetachedWindowWebview { + webview, + use_https_scheme, + } + }); + + Ok(DetachedWindow { + id: window_id, + label, + dispatcher, + webview: detached_webview, + }) + } + + fn create_webview( + &self, + window_id: WindowId, + pending: PendingWebview>, + ) -> Result>> { + let label = pending.label.clone(); + let context = self.clone(); + + let webview_id = self.next_webview_id(); + + let window_id_wrapper = Arc::new(Mutex::new(window_id)); + let window_id_wrapper_ = window_id_wrapper.clone(); + + let (tx, rx) = channel(); + self.send_user_message(Message::CreateWebview( + window_id, + Box::new(move |window, _options| { + create_webview( + WebviewKind::WindowChild, + window, + window_id_wrapper_, + webview_id, + &context, + pending, + #[cfg(windows)] + _options.focused_webview, + ) + }), + tx, + ))?; + rx.recv() + .map_err(|_| crate::Error::FailedToReceiveMessage)??; + + let dispatcher = WryWebviewDispatcher { + window_id: window_id_wrapper, + webview_id, + context: self.clone(), + }; + + Ok(DetachedWebview { label, dispatcher }) + } +} + +#[cfg(feature = "tracing")] +#[derive(Clone, Default)] +pub struct ActiveTraceSpanStore(Rc>>); + +// Deliberately does not borrow the inner `RefCell`: formatting can happen re-entrantly +// while the store is already borrowed (e.g. from an event loop callback), +// which would panic with "already borrowed". +#[cfg(feature = "tracing")] +impl fmt::Debug for ActiveTraceSpanStore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ActiveTraceSpanStore") + .finish_non_exhaustive() + } +} + +#[cfg(feature = "tracing")] +impl ActiveTraceSpanStore { + pub fn remove_window_draw(&self) { + self + .0 + .borrow_mut() + .retain(|t| !matches!(t, ActiveTracingSpan::WindowDraw { id: _, span: _ })); + } +} + +#[cfg(feature = "tracing")] +#[derive(Debug)] +pub enum ActiveTracingSpan { + WindowDraw { + id: TaoWindowId, + span: tracing::span::EnteredSpan, + }, +} + +pub struct WindowsStore(pub RefCell>); + +// Deliberately does not borrow the inner `RefCell`: formatting can happen re-entrantly +// while the store is already borrowed (e.g. from an event loop callback), +// which would panic with "already borrowed". +impl fmt::Debug for WindowsStore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WindowsStore").finish_non_exhaustive() + } +} + +#[derive(Debug, Clone)] +pub struct DispatcherMainThreadContext { + pub window_target: EventLoopWindowTarget>, + pub web_context: WebContextStore, + // changing this to an Rc will cause frequent app crashes. + pub windows: Arc, + #[cfg(feature = "tracing")] + pub active_tracing_spans: ActiveTraceSpanStore, +} + +impl fmt::Debug for Context { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Context") + .field("main_thread_id", &self.main_thread_id) + .field("proxy", &self.proxy) + .field("main_thread", &self.main_thread) + .finish() + } +} + +pub struct DeviceEventFilterWrapper(pub TaoDeviceEventFilter); + +impl From for DeviceEventFilterWrapper { + fn from(item: DeviceEventFilter) -> Self { + match item { + DeviceEventFilter::Always => Self(TaoDeviceEventFilter::Always), + DeviceEventFilter::Never => Self(TaoDeviceEventFilter::Never), + DeviceEventFilter::Unfocused => Self(TaoDeviceEventFilter::Unfocused), + } + } +} + +pub struct RectWrapper(pub wry::Rect); +impl From for RectWrapper { + fn from(value: tauri_runtime::dpi::Rect) -> Self { + RectWrapper(wry::Rect { + position: value.position, + size: value.size, + }) + } +} + +/// Wrapper around a [`tao::window::Icon`] that can be created from an [`Icon`]. +pub struct TaoIcon(pub TaoWindowIcon); + +impl TryFrom> for TaoIcon { + type Error = Error; + fn try_from(icon: Icon<'_>) -> std::result::Result { + TaoWindowIcon::from_rgba(icon.rgba.to_vec(), icon.width, icon.height) + .map(Self) + .map_err(|e| Error::InvalidIcon(Box::new(e))) + } +} + +pub struct WindowEventWrapper(pub Option); + +impl WindowEventWrapper { + fn map_from_tao(event: &TaoWindowEvent<'_>, #[cfg(windows)] window: &WindowWrapper) -> Self { + let event = match event { + TaoWindowEvent::Resized(size) => WindowEvent::Resized(*size), + TaoWindowEvent::Moved(position) => WindowEvent::Moved(*position), + TaoWindowEvent::Destroyed => WindowEvent::Destroyed, + TaoWindowEvent::ScaleFactorChanged { + scale_factor, + new_inner_size, + } => WindowEvent::ScaleFactorChanged { + scale_factor: *scale_factor, + new_inner_size: **new_inner_size, + }, + TaoWindowEvent::Focused(focused) => { + #[cfg(not(windows))] + return Self(Some(WindowEvent::Focused(*focused))); + // on multiwebview mode, if there's no focused webview, it means we're receiving a direct window focus change + // (without receiving a webview focus, such as when clicking the taskbar app icon or using Alt + Tab) + // in this case we must send the focus change event here + #[cfg(windows)] + if window.has_children.load(Ordering::Relaxed) { + if !*focused { + // Blur events are handled in the webview side (add_LostFocus) + return Self(None); + } + + let mut focused_webview = window.focused_webview.lock().unwrap(); + if let FocusState::Blured { + last_focused_webview_label, + } = &*focused_webview + { + let should_focus_webview = + last_focused_webview_label + .as_deref() + .and_then(|last_focused_webview_label| { + window + .webviews + .iter() + .find(|w| w.label == last_focused_webview_label) + }); + *focused_webview = FocusState::WindowFocused; + if let Some(should_focus_webview) = should_focus_webview { + drop(focused_webview); + let _ = should_focus_webview.focus(); + } + WindowEvent::Focused(true) + } else { + // Already focused + return Self(None); + } + } else if window.webviews.is_empty() { + // Raw tao window without webviews, forward the event + WindowEvent::Focused(*focused) + } else { + // when not on multiwebview mode, wry will set focus to the webview, + // and we will handle focus change events on the webview (add_GotFocus and add_LostFocus) + return Self(None); + } + } + TaoWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)), + #[cfg(mobile)] + TaoWindowEvent::Suspended => WindowEvent::Suspended, + #[cfg(mobile)] + TaoWindowEvent::Resumed => WindowEvent::Resumed, + _ => return Self(None), + }; + Self(Some(event)) + } + + fn parse(window: &WindowWrapper, event: &TaoWindowEvent<'_>) -> Self { + match event { + // resized event from tao doesn't include a reliable size on macOS + // because wry replaces the NSView + TaoWindowEvent::Resized(_) => { + if let Some(w) = &window.inner { + let size = inner_size( + w, + &window.webviews, + window.has_children.load(Ordering::Relaxed), + ); + Self(Some(WindowEvent::Resized(size))) + } else { + Self(None) + } + } + e => Self::map_from_tao( + e, + #[cfg(windows)] + window, + ), + } + } +} + +pub fn map_theme(theme: &TaoTheme) -> Theme { + match theme { + TaoTheme::Light => Theme::Light, + TaoTheme::Dark => Theme::Dark, + _ => Theme::Light, + } +} + +#[cfg(target_os = "macos")] +fn tao_activation_policy(activation_policy: ActivationPolicy) -> TaoActivationPolicy { + match activation_policy { + ActivationPolicy::Regular => TaoActivationPolicy::Regular, + ActivationPolicy::Accessory => TaoActivationPolicy::Accessory, + ActivationPolicy::Prohibited => TaoActivationPolicy::Prohibited, + _ => unimplemented!(), + } +} + +pub struct MonitorHandleWrapper(pub MonitorHandle); + +impl From for Monitor { + fn from(monitor: MonitorHandleWrapper) -> Monitor { + Self { + name: monitor.0.name(), + position: monitor.0.position(), + size: monitor.0.size(), + work_area: monitor.0.work_area(), + scale_factor: monitor.0.scale_factor(), + } + } +} + +fn find_monitor_for_position( + monitors: impl Iterator, + window_position: Position, +) -> Option { + monitors.into_iter().find(|m| { + let monitor_pos = m.position(); + let monitor_size = m.size(); + + // type annotations required for 32bit targets. + let window_position = window_position.to_physical::(m.scale_factor()); + + monitor_pos.x <= window_position.x + && window_position.x < monitor_pos.x + monitor_size.width as i32 + && monitor_pos.y <= window_position.y + && window_position.y < monitor_pos.y + monitor_size.height as i32 + }) +} + +#[derive(Debug, Clone)] +pub struct UserAttentionTypeWrapper(pub TaoUserAttentionType); + +impl From for UserAttentionTypeWrapper { + fn from(request_type: UserAttentionType) -> Self { + let o = match request_type { + UserAttentionType::Critical => TaoUserAttentionType::Critical, + UserAttentionType::Informational => TaoUserAttentionType::Informational, + }; + Self(o) + } +} + +#[derive(Debug)] +pub struct CursorIconWrapper(pub TaoCursorIcon); + +impl From for CursorIconWrapper { + fn from(icon: CursorIcon) -> Self { + use CursorIcon::*; + let i = match icon { + 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 TaoProgressState); + +impl From for ProgressStateWrapper { + fn from(status: ProgressBarStatus) -> Self { + let state = match status { + 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 TaoProgressBarState); + +impl From for ProgressBarStateWrapper { + fn from(progress_state: ProgressBarState) -> Self { + Self(TaoProgressBarState { + progress: progress_state.progress, + state: progress_state + .status + .map(|state| ProgressStateWrapper::from(state).0), + desktop_filename: progress_state.desktop_filename, + }) + } +} + +#[derive(Clone, Default)] +pub struct WindowBuilderWrapper { + inner: TaoWindowBuilder, + center: bool, + prevent_overflow: Option, + #[cfg(target_os = "macos")] + tabbing_identifier: Option, +} + +impl std::fmt::Debug for WindowBuilderWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = f.debug_struct("WindowBuilderWrapper"); + s.field("inner", &self.inner) + .field("center", &self.center) + .field("prevent_overflow", &self.prevent_overflow); + #[cfg(target_os = "macos")] + { + s.field("tabbing_identifier", &self.tabbing_identifier); + } + s.finish() + } +} + +// SAFETY: this type is `Send` since `menu_items` are read only here +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for WindowBuilderWrapper {} + +impl WindowBuilderBase for WindowBuilderWrapper {} +impl WindowBuilder for WindowBuilderWrapper { + fn new() -> Self { + #[allow(unused_mut)] + let mut builder = Self::default().focused(true); + + #[cfg(target_os = "macos")] + { + // TODO: find a proper way to prevent webview being pushed out of the window. + // Workaround for issue: https://github.com/tauri-apps/tauri/issues/10225 + // The window requires `NSFullSizeContentViewWindowMask` flag to prevent devtools + // pushing the content view out of the window. + // By setting the default style to `TitleBarStyle::Visible` should fix the issue for most of the users. + builder = builder.title_bar_style(TitleBarStyle::Visible); + } + + builder = builder.title("Tauri App"); + + #[cfg(windows)] + { + builder = builder.window_classname("Tauri Window"); + } + + builder + } + + fn with_config(config: &WindowConfig) -> Self { + let mut window = WindowBuilderWrapper::new(); + + #[cfg(target_os = "macos")] + { + window = window + .hidden_title(config.hidden_title) + .title_bar_style(config.title_bar_style); + if let Some(identifier) = &config.tabbing_identifier { + window = window.tabbing_identifier(identifier); + } + if let Some(position) = &config.traffic_light_position { + window = window.traffic_light_position(tauri_runtime::dpi::LogicalPosition::new( + position.x, position.y, + )); + } + } + + #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))] + { + window = window.transparent(config.transparent); + } + #[cfg(all( + target_os = "macos", + not(feature = "macos-private-api"), + debug_assertions + ))] + if config.transparent { + eprintln!( + "The window is set to be transparent but the `macos-private-api` is not enabled. + This can be enabled via the `tauri.macOSPrivateApi` configuration property + "); + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + { + // Mouse event is disabled on Linux since sudden event bursts could block event loop. + window.inner = window.inner.with_cursor_moved_event(false); + } + + #[cfg(target_os = "android")] + { + if let Some(activity_name) = &config.activity_name { + window.inner = window.inner.with_activity_name(activity_name.clone()); + } + if let Some(activity_name) = &config.created_by_activity_name { + window.inner = window + .inner + .with_created_by_activity_name(activity_name.clone()); + } + } + + #[cfg(target_os = "ios")] + { + if let Some(scene_identifier) = &config.requested_by_scene_identifier { + window.inner = window + .inner + .with_requesting_scene_identifier(scene_identifier.clone()); + } + } + + // ignore size from config for mobile for backward compatibility + #[cfg(not(any(target_os = "ios", target_os = "android")))] + { + window = window.inner_size(config.width, config.height); + } + + window = window + .title(config.title.to_string()) + .focused(config.focus) + .focusable(config.focusable) + .visible(config.visible) + .resizable(config.resizable) + .fullscreen(config.fullscreen) + .decorations(config.decorations) + .maximized(config.maximized) + .always_on_bottom(config.always_on_bottom) + .always_on_top(config.always_on_top) + .visible_on_all_workspaces(config.visible_on_all_workspaces) + .content_protected(config.content_protected) + .skip_taskbar(config.skip_taskbar) + .theme(config.theme) + .no_redirection_bitmap(config.no_redirection_bitmap) + .closable(config.closable) + .maximizable(config.maximizable) + .minimizable(config.minimizable) + .shadow(config.shadow); + + let mut constraints = WindowSizeConstraints::default(); + + if let Some(min_width) = config.min_width { + constraints.min_width = Some(tao::dpi::LogicalUnit::new(min_width).into()); + } + if let Some(min_height) = config.min_height { + constraints.min_height = Some(tao::dpi::LogicalUnit::new(min_height).into()); + } + if let Some(max_width) = config.max_width { + constraints.max_width = Some(tao::dpi::LogicalUnit::new(max_width).into()); + } + if let Some(max_height) = config.max_height { + constraints.max_height = Some(tao::dpi::LogicalUnit::new(max_height).into()); + } + if let Some(color) = config.background_color { + window = window.background_color(color); + } + window = window.inner_size_constraints(constraints); + + if let (Some(x), Some(y)) = (config.x, config.y) { + window = window.position(x, y); + } + + if config.center { + window = window.center(); + } + + if let Some(window_classname) = &config.window_classname { + window = window.window_classname(window_classname); + } + + if let Some(prevent_overflow) = &config.prevent_overflow { + window = match prevent_overflow { + PreventOverflowConfig::Enable(true) => window.prevent_overflow(), + PreventOverflowConfig::Margin(margin) => { + window.prevent_overflow_with_margin(PhysicalSize::new(margin.width, margin.height).into()) + } + _ => window, + }; + } + + window + } + + fn center(mut self) -> Self { + self.center = true; + self + } + + fn position(mut self, x: f64, y: f64) -> Self { + self.inner = self.inner.with_position(LogicalPosition::new(x, y)); + self + } + + fn inner_size(mut self, width: f64, height: f64) -> Self { + self.inner = self.inner.with_inner_size(LogicalSize::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(LogicalSize::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(LogicalSize::new(max_width, max_height)); + self + } + + fn inner_size_constraints(mut self, constraints: WindowSizeConstraints) -> Self { + self.inner.window.inner_size_constraints = tao::window::WindowSizeConstraints { + min_width: constraints.min_width, + min_height: constraints.min_height, + max_width: constraints.max_width, + max_height: constraints.max_height, + }; + self + } + + /// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size) on creation + /// + /// ## Platform-specific + /// + /// - **iOS / Android:** Unsupported. + fn prevent_overflow(mut self) -> Self { + self + .prevent_overflow + .replace(PhysicalSize::new(0, 0).into()); + self + } + + /// Prevent the window from overflowing the working area (e.g. monitor size - taskbar size) + /// on creation with a margin + /// + /// ## Platform-specific + /// + /// - **iOS / Android:** Unsupported. + fn prevent_overflow_with_margin(mut self, margin: Size) -> Self { + self.prevent_overflow.replace(margin); + self + } + + fn resizable(mut self, resizable: bool) -> Self { + self.inner = self.inner.with_resizable(resizable); + self + } + + fn maximizable(mut self, maximizable: bool) -> Self { + self.inner = self.inner.with_maximizable(maximizable); + self + } + + fn minimizable(mut self, minimizable: bool) -> Self { + self.inner = self.inner.with_minimizable(minimizable); + self + } + + fn closable(mut self, closable: bool) -> Self { + self.inner = self.inner.with_closable(closable); + self + } + + fn title>(mut self, title: S) -> Self { + self.inner = self.inner.with_title(title.into()); + self + } + + fn fullscreen(mut self, fullscreen: bool) -> Self { + self.inner = if fullscreen { + self + .inner + .with_fullscreen(Some(Fullscreen::Borderless(None))) + } else { + self.inner.with_fullscreen(None) + }; + self + } + + fn focused(mut self, focused: bool) -> Self { + self.inner = self.inner.with_focused(focused); + self + } + + fn focusable(mut self, focusable: bool) -> Self { + self.inner = self.inner.with_focusable(focusable); + self + } + + fn maximized(mut self, maximized: bool) -> Self { + self.inner = self.inner.with_maximized(maximized); + self + } + + fn visible(mut self, visible: bool) -> Self { + self.inner = self.inner.with_visible(visible); + self + } + + #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))] + fn transparent(mut self, transparent: bool) -> Self { + self.inner = self.inner.with_transparent(transparent); + self + } + + fn decorations(mut self, decorations: bool) -> Self { + self.inner = self.inner.with_decorations(decorations); + self + } + + fn always_on_bottom(mut self, always_on_bottom: bool) -> Self { + self.inner = self.inner.with_always_on_bottom(always_on_bottom); + self + } + + fn always_on_top(mut self, always_on_top: bool) -> Self { + self.inner = self.inner.with_always_on_top(always_on_top); + self + } + + fn visible_on_all_workspaces(mut self, visible_on_all_workspaces: bool) -> Self { + self.inner = self + .inner + .with_visible_on_all_workspaces(visible_on_all_workspaces); + self + } + + fn content_protected(mut self, protected: bool) -> Self { + self.inner = self.inner.with_content_protection(protected); + self + } + + fn shadow(#[allow(unused_mut)] mut self, _enable: bool) -> Self { + #[cfg(windows)] + { + self.inner = self.inner.with_undecorated_shadow(_enable); + } + #[cfg(target_os = "macos")] + { + self.inner = self.inner.with_has_shadow(_enable); + } + self + } + + #[cfg(windows)] + fn owner(mut self, owner: HWND) -> Self { + self.inner = self.inner.with_owner_window(owner.0 as _); + self + } + + #[cfg(windows)] + fn parent(mut self, parent: HWND) -> Self { + self.inner = self.inner.with_parent_window(parent.0 as _); + self + } + + #[cfg(target_os = "macos")] + fn parent(mut self, parent: *mut std::ffi::c_void) -> Self { + self.inner = self.inner.with_parent_window(parent); + self + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + fn transient_for(mut self, parent: &impl gtk::glib::IsA) -> Self { + self.inner = self.inner.with_transient_for(parent); + self + } + + #[cfg(windows)] + fn drag_and_drop(mut self, enabled: bool) -> Self { + self.inner = self.inner.with_drag_and_drop(enabled); + self + } + + #[cfg(target_os = "macos")] + fn title_bar_style(mut self, style: TitleBarStyle) -> Self { + match style { + TitleBarStyle::Visible => { + self.inner = self.inner.with_titlebar_transparent(false); + // Fixes rendering issue when resizing window with devtools open (https://github.com/tauri-apps/tauri/issues/3914) + self.inner = self.inner.with_fullsize_content_view(true); + } + TitleBarStyle::Transparent => { + self.inner = self.inner.with_titlebar_transparent(true); + self.inner = self.inner.with_fullsize_content_view(false); + } + TitleBarStyle::Overlay => { + self.inner = self.inner.with_titlebar_transparent(true); + self.inner = self.inner.with_fullsize_content_view(true); + } + unknown => { + #[cfg(feature = "tracing")] + tracing::warn!("unknown title bar style applied: {unknown}"); + + #[cfg(not(feature = "tracing"))] + eprintln!("unknown title bar style applied: {unknown}"); + } + } + self + } + + #[cfg(target_os = "macos")] + fn traffic_light_position>(mut self, position: P) -> Self { + self.inner = self.inner.with_traffic_light_inset(position.into()); + self + } + + #[cfg(target_os = "macos")] + fn hidden_title(mut self, hidden: bool) -> Self { + self.inner = self.inner.with_title_hidden(hidden); + self + } + + #[cfg(target_os = "macos")] + fn tabbing_identifier(mut self, identifier: &str) -> Self { + self.inner = self.inner.with_tabbing_identifier(identifier); + self.tabbing_identifier.replace(identifier.into()); + self + } + + fn icon(mut self, icon: Icon) -> Result { + self.inner = self + .inner + .with_window_icon(Some(TaoIcon::try_from(icon)?.0)); + Ok(self) + } + + fn background_color(mut self, color: Color) -> Self { + self.inner = self.inner.with_background_color(color.into()); + self + } + + #[cfg(any( + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + fn skip_taskbar(mut self, skip: bool) -> Self { + self.inner = self.inner.with_skip_taskbar(skip); + self + } + + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))] + fn skip_taskbar(self, _skip: bool) -> Self { + self + } + + fn theme(mut self, theme: Option) -> Self { + self.inner = self.inner.with_theme(if let Some(t) = theme { + match t { + Theme::Dark => Some(TaoTheme::Dark), + _ => Some(TaoTheme::Light), + } + } else { + None + }); + + self + } + + fn has_icon(&self) -> bool { + self.inner.window.window_icon.is_some() + } + + fn get_theme(&self) -> Option { + self.inner.window.preferred_theme.map(|theme| match theme { + TaoTheme::Dark => Theme::Dark, + _ => Theme::Light, + }) + } + + #[cfg(windows)] + fn window_classname>(mut self, window_classname: S) -> Self { + self.inner = self.inner.with_window_classname(window_classname); + self + } + #[cfg(not(windows))] + fn window_classname>(self, _window_classname: S) -> Self { + self + } + + fn no_redirection_bitmap(#[allow(unused_mut)] mut self, _enable: bool) -> Self { + #[cfg(windows)] + { + self.inner = self.inner.with_no_redirection_bitmap(_enable); + } + self + } + + #[cfg(target_os = "android")] + fn activity_name>(mut self, class_name: S) -> Self { + self.inner = self.inner.with_activity_name(class_name.into()); + self + } + + #[cfg(target_os = "android")] + fn created_by_activity_name>(mut self, class_name: S) -> Self { + self.inner = self.inner.with_created_by_activity_name(class_name.into()); + self + } + + #[cfg(target_os = "ios")] + fn requested_by_scene_identifier>(mut self, identifier: S) -> Self { + self.inner = self + .inner + .with_requesting_scene_identifier(identifier.into()); + self + } +} + +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +pub struct GtkWindow(pub gtk::ApplicationWindow); +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for GtkWindow {} + +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +pub struct GtkBox(pub gtk::Box); +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for GtkBox {} + +pub struct SendRawWindowHandle(pub raw_window_handle::RawWindowHandle); +unsafe impl Send for SendRawWindowHandle {} + +pub enum ApplicationMessage { + #[cfg(target_os = "macos")] + Show, + #[cfg(target_os = "macos")] + Hide, + #[cfg(any(target_os = "macos", target_os = "ios"))] + FetchDataStoreIdentifiers(Box) + Send + 'static>), + #[cfg(any(target_os = "macos", target_os = "ios"))] + RemoveDataStore([u8; 16], Box) + Send + 'static>), +} + +pub enum WindowMessage { + AddEventListener(WindowEventId, Box), + // Getters + ScaleFactor(Sender), + InnerPosition(Sender>>), + OuterPosition(Sender>>), + InnerSize(Sender>), + OuterSize(Sender>), + IsFullscreen(Sender), + IsMinimized(Sender), + IsMaximized(Sender), + IsFocused(Sender), + IsDecorated(Sender), + IsResizable(Sender), + IsMaximizable(Sender), + IsMinimizable(Sender), + IsClosable(Sender), + IsVisible(Sender), + Title(Sender), + // Monitors are converted on the main thread: on iOS tao's `MonitorHandle` + // can only be queried (and dropped) there. + CurrentMonitor(Sender>), + PrimaryMonitor(Sender>), + MonitorFromPoint(Sender>, (f64, f64)), + AvailableMonitors(Sender>), + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + GtkWindow(Sender), + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + GtkBox(Sender), + #[cfg(target_os = "android")] + ActivityName(Sender), + #[cfg(target_os = "ios")] + SceneIdentifier(Sender), + RawWindowHandle(Sender>), + Theme(Sender), + IsEnabled(Sender), + IsAlwaysOnTop(Sender), + // Setters + Center, + RequestUserAttention(Option), + SetEnabled(bool), + SetResizable(bool), + SetMaximizable(bool), + SetMinimizable(bool), + SetClosable(bool), + SetTitle(String), + Maximize, + Unmaximize, + Minimize, + Unminimize, + Show, + Hide, + Close, + Destroy, + SetDecorations(bool), + SetShadow(bool), + SetAlwaysOnBottom(bool), + SetAlwaysOnTop(bool), + SetVisibleOnAllWorkspaces(bool), + SetContentProtected(bool), + SetSize(Size), + SetMinSize(Option), + SetMaxSize(Option), + SetSizeConstraints(WindowSizeConstraints), + SetPosition(Position), + SetFullscreen(bool), + SetFullscreenOnMonitor(PhysicalPosition), + #[cfg(target_os = "macos")] + SetSimpleFullscreen(bool), + SetFocus, + SetFocusable(bool), + SetIcon(TaoWindowIcon), + SetSkipTaskbar(bool), + SetCursorGrab(bool), + SetCursorVisible(bool), + SetCursorIcon(CursorIcon), + SetCursorPosition(Position), + SetIgnoreCursorEvents(bool), + SetBadgeCount(Option, Option), + SetBadgeLabel(Option), + SetOverlayIcon(Option), + SetProgressBar(ProgressBarState), + SetTitleBarStyle(tauri_utils::TitleBarStyle), + SetTrafficLightPosition(Position), + SetTheme(Option), + SetBackgroundColor(Option), + DragWindow, + ResizeDragWindow(tauri_runtime::ResizeDirection), + RequestRedraw, +} + +#[derive(Debug, Clone)] +pub enum SynthesizedWindowEvent { + Focused(bool), + DragDrop(DragDropEvent), +} + +impl From for WindowEventWrapper { + fn from(event: SynthesizedWindowEvent) -> Self { + let event = match event { + SynthesizedWindowEvent::Focused(focused) => WindowEvent::Focused(focused), + SynthesizedWindowEvent::DragDrop(event) => WindowEvent::DragDrop(event), + }; + Self(Some(event)) + } +} + +pub enum WebviewMessage { + AddEventListener(WebviewEventId, Box), + #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] + EvaluateScript(String), + #[cfg(all(feature = "tracing", not(target_os = "android")))] + EvaluateScript(String, Sender<()>, tracing::Span), + #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] + EvaluateScriptWithCallback(String, Box), + #[cfg(all(feature = "tracing", not(target_os = "android")))] + EvaluateScriptWithCallback( + String, + Box, + Sender<()>, + tracing::Span, + ), + CookiesForUrl(Url, Sender>>>), + Cookies(Sender>>>), + SetCookie(tauri_runtime::Cookie<'static>), + DeleteCookie(tauri_runtime::Cookie<'static>), + WebviewEvent(WebviewEvent), + SynthesizedWindowEvent(SynthesizedWindowEvent), + Navigate(Url), + Reload, + Print, + Close, + Show, + Hide, + SetPosition(Position), + SetSize(Size), + SetBounds(tauri_runtime::dpi::Rect), + SetFocus, + Reparent(WindowId, Sender>), + SetAutoResize(bool), + SetZoom(f64), + SetBackgroundColor(Option), + ClearAllBrowsingData, + // Getters + Url(Sender>), + Bounds(Sender>), + Position(Sender>>), + Size(Sender>>), + WithWebview(Box), + // Devtools + #[cfg(any(debug_assertions, feature = "devtools"))] + OpenDevTools, + #[cfg(any(debug_assertions, feature = "devtools"))] + CloseDevTools, + #[cfg(any(debug_assertions, feature = "devtools"))] + IsDevToolsOpen(Sender), +} + +pub enum EventLoopWindowTargetMessage { + CursorPosition(Sender>>), + // See `WindowMessage::CurrentMonitor`. + PrimaryMonitor(Sender>), + MonitorFromPoint(Sender>, (f64, f64)), + AvailableMonitors(Sender>), + SetTheme(Option), + SetDeviceEventFilter(DeviceEventFilter), +} + +pub type CreateWindowClosure = + Box>) -> Result + Send>; + +pub type CreateWebviewClosure = + Box Result + Send>; + +pub struct CreateWebviewOptions { + #[cfg(windows)] + pub focused_webview: Arc>, +} + +pub enum Message { + Task(Box), + #[cfg(target_os = "macos")] + SetActivationPolicy(ActivationPolicy), + #[cfg(target_os = "macos")] + SetDockVisibility(bool), + RequestExit(i32), + Application(ApplicationMessage), + Window(WindowId, WindowMessage), + Webview(WindowId, WebviewId, WebviewMessage), + EventLoopWindowTarget(EventLoopWindowTargetMessage), + CreateWebview(WindowId, CreateWebviewClosure, Sender>), + CreateWindow(WindowId, CreateWindowClosure, Sender>), + CreateRawWindow( + WindowId, + Box (String, TaoWindowBuilder) + Send>, + Sender>>, + ), + UserEvent(T), +} + +impl Clone for Message { + fn clone(&self) -> Self { + match self { + Self::UserEvent(t) => Self::UserEvent(t.clone()), + _ => unimplemented!(), + } + } +} + +/// The Tauri [`WebviewDispatch`] for [`Wry`]. +#[derive(Debug, Clone)] +pub struct WryWebviewDispatcher { + window_id: Arc>, + webview_id: WebviewId, + context: Context, +} + +impl WebviewDispatch for WryWebviewDispatcher { + type Runtime = Wry; + + fn run_on_main_thread(&self, f: F) -> Result<()> { + self.context.send_user_message(Message::Task(Box::new(f))) + } + + fn on_webview_event(&self, f: F) -> WindowEventId { + let id = self.context.next_webview_event_id(); + let _ = self.context.proxy.send_event(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::AddEventListener(id, Box::new(f)), + )); + id + } + + fn with_webview) + Send + 'static>(&self, f: F) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::WithWebview(Box::new(move |webview| f(Box::new(webview)))), + )) + } + + #[cfg(any(debug_assertions, feature = "devtools"))] + fn open_devtools(&self) { + let _ = self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::OpenDevTools, + )); + } + + #[cfg(any(debug_assertions, feature = "devtools"))] + fn close_devtools(&self) { + let _ = self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::CloseDevTools, + )); + } + + /// Gets the devtools window's current open state. + #[cfg(any(debug_assertions, feature = "devtools"))] + fn is_devtools_open(&self) -> Result { + webview_getter!(self, WebviewMessage::IsDevToolsOpen) + } + + // Getters + + fn url(&self) -> Result { + webview_getter!(self, WebviewMessage::Url)? + } + + fn bounds(&self) -> Result { + webview_getter!(self, WebviewMessage::Bounds)? + } + + fn position(&self) -> Result> { + webview_getter!(self, WebviewMessage::Position)? + } + + fn size(&self) -> Result> { + webview_getter!(self, WebviewMessage::Size)? + } + + // Setters + + fn navigate(&self, url: Url) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::Navigate(url), + )) + } + + fn reload(&self) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::Reload, + )) + } + + fn print(&self) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::Print, + )) + } + + fn close(&self) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::Close, + )) + } + + fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetBounds(bounds), + )) + } + + fn set_size(&self, size: Size) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetSize(size), + )) + } + + fn set_position(&self, position: Position) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetPosition(position), + )) + } + + fn set_focus(&self) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetFocus, + )) + } + + fn reparent(&self, window_id: WindowId) -> Result<()> { + let mut current_window_id = self.window_id.lock().unwrap(); + let (tx, rx) = channel(); + self.context.send_user_message(Message::Webview( + *current_window_id, + self.webview_id, + WebviewMessage::Reparent(window_id, tx), + ))?; + + rx.recv().unwrap()?; + + *current_window_id = window_id; + Ok(()) + } + + fn cookies_for_url(&self, url: Url) -> Result>> { + let (tx, rx) = channel(); + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::CookiesForUrl(url, tx), + ))?; + + rx.recv().unwrap() + } + + fn cookies(&self) -> Result>> { + webview_getter!(self, WebviewMessage::Cookies)? + } + + fn set_cookie(&self, cookie: Cookie<'_>) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetCookie(cookie.into_owned()), + ))?; + Ok(()) + } + + fn delete_cookie(&self, cookie: Cookie<'_>) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::DeleteCookie(cookie.into_owned()), + ))?; + Ok(()) + } + + fn set_auto_resize(&self, auto_resize: bool) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetAutoResize(auto_resize), + )) + } + + #[cfg(all(feature = "tracing", not(target_os = "android")))] + fn eval_script>(&self, script: S) -> Result<()> { + // use a channel so the EvaluateScript task uses the current span as parent + let (tx, rx) = channel(); + getter!( + self, + rx, + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::EvaluateScript(script.into(), tx, tracing::Span::current()), + ) + ) + } + + #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] + fn eval_script>(&self, script: S) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::EvaluateScript(script.into()), + )) + } + + #[cfg(all(feature = "tracing", not(target_os = "android")))] + fn eval_script_with_callback>( + &self, + script: S, + callback: impl Fn(String) + Send + 'static, + ) -> Result<()> { + // use a channel so the EvaluateScript task uses the current span as parent + let (tx, rx) = channel(); + getter!( + self, + rx, + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::EvaluateScriptWithCallback( + script.into(), + Box::new(callback), + tx, + tracing::Span::current(), + ), + ) + ) + } + + #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] + fn eval_script_with_callback>( + &self, + script: S, + callback: impl Fn(String) + Send + 'static, + ) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::EvaluateScriptWithCallback(script.into(), Box::new(callback)), + )) + } + + fn set_zoom(&self, scale_factor: f64) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetZoom(scale_factor), + )) + } + + fn clear_all_browsing_data(&self) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::ClearAllBrowsingData, + )) + } + + fn hide(&self) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::Hide, + )) + } + + fn show(&self) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::Show, + )) + } + + fn set_background_color(&self, color: Option) -> Result<()> { + self.context.send_user_message(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetBackgroundColor(color), + )) + } +} + +/// The Tauri [`WindowDispatch`] for [`Wry`]. +#[derive(Debug, Clone)] +pub struct WryWindowDispatcher { + window_id: WindowId, + context: Context, +} + +// SAFETY: this is safe since the `Context` usage is guarded by `Context::send_user_message`. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Sync for WryWindowDispatcher {} + +fn get_raw_window_handle( + dispatcher: &WryWindowDispatcher, +) -> Result> { + window_getter!(dispatcher, WindowMessage::RawWindowHandle) +} + +impl WindowDispatch for WryWindowDispatcher { + type Runtime = Wry; + type WindowBuilder = WindowBuilderWrapper; + + fn run_on_main_thread(&self, f: F) -> Result<()> { + self.context.send_user_message(Message::Task(Box::new(f))) + } + + fn on_window_event(&self, f: F) -> WindowEventId { + let id = self.context.next_window_event_id(); + let _ = self.context.proxy.send_event(Message::Window( + self.window_id, + WindowMessage::AddEventListener(id, Box::new(f)), + )); + id + } + + // Getters + + fn scale_factor(&self) -> Result { + window_getter!(self, WindowMessage::ScaleFactor) + } + + fn inner_position(&self) -> Result> { + window_getter!(self, WindowMessage::InnerPosition)? + } + + fn outer_position(&self) -> Result> { + window_getter!(self, WindowMessage::OuterPosition)? + } + + fn inner_size(&self) -> Result> { + window_getter!(self, WindowMessage::InnerSize) + } + + fn outer_size(&self) -> Result> { + window_getter!(self, WindowMessage::OuterSize) + } + + fn is_fullscreen(&self) -> Result { + window_getter!(self, WindowMessage::IsFullscreen) + } + + fn is_minimized(&self) -> Result { + window_getter!(self, WindowMessage::IsMinimized) + } + + fn is_maximized(&self) -> Result { + window_getter!(self, WindowMessage::IsMaximized) + } + + fn is_focused(&self) -> Result { + window_getter!(self, WindowMessage::IsFocused) + } + + /// Gets the window's current decoration state. + fn is_decorated(&self) -> Result { + window_getter!(self, WindowMessage::IsDecorated) + } + + /// Gets the window's current resizable state. + fn is_resizable(&self) -> Result { + window_getter!(self, WindowMessage::IsResizable) + } + + /// Gets the current native window's maximize button state + fn is_maximizable(&self) -> Result { + window_getter!(self, WindowMessage::IsMaximizable) + } + + /// Gets the current native window's minimize button state + fn is_minimizable(&self) -> Result { + window_getter!(self, WindowMessage::IsMinimizable) + } + + /// Gets the current native window's close button state + fn is_closable(&self) -> Result { + window_getter!(self, WindowMessage::IsClosable) + } + + fn is_visible(&self) -> Result { + window_getter!(self, WindowMessage::IsVisible) + } + + fn title(&self) -> Result { + window_getter!(self, WindowMessage::Title) + } + + fn current_monitor(&self) -> Result> { + window_getter!(self, WindowMessage::CurrentMonitor) + } + + fn primary_monitor(&self) -> Result> { + window_getter!(self, WindowMessage::PrimaryMonitor) + } + + fn monitor_from_point(&self, x: f64, y: f64) -> Result> { + let (tx, rx) = channel(); + + let _ = self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::MonitorFromPoint(tx, (x, y)), + )); + + rx.recv().map_err(|_| crate::Error::FailedToReceiveMessage) + } + + fn available_monitors(&self) -> Result> { + window_getter!(self, WindowMessage::AvailableMonitors) + } + + fn theme(&self) -> Result { + window_getter!(self, WindowMessage::Theme) + } + + fn is_enabled(&self) -> Result { + window_getter!(self, WindowMessage::IsEnabled) + } + + fn is_always_on_top(&self) -> Result { + window_getter!(self, WindowMessage::IsAlwaysOnTop) + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + fn gtk_window(&self) -> Result { + window_getter!(self, WindowMessage::GtkWindow).map(|w| w.0) + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + fn default_vbox(&self) -> Result { + window_getter!(self, WindowMessage::GtkBox).map(|w| w.0) + } + + /// Returns the name of the Android activity associated with this window. + #[cfg(target_os = "android")] + fn activity_name(&self) -> Result { + window_getter!(self, WindowMessage::ActivityName) + } + + /// Returns the identifier of the UIScene tied to this UIWindow. + #[cfg(target_os = "ios")] + fn scene_identifier(&self) -> Result { + window_getter!(self, WindowMessage::SceneIdentifier) + } + + fn window_handle( + &self, + ) -> std::result::Result, raw_window_handle::HandleError> { + get_raw_window_handle(self) + .map_err(|_| raw_window_handle::HandleError::Unavailable) + .and_then(|r| r.map(|h| unsafe { raw_window_handle::WindowHandle::borrow_raw(h.0) })) + } + + // Setters + + fn center(&self) -> Result<()> { + self + .context + .send_user_message(Message::Window(self.window_id, WindowMessage::Center)) + } + + fn request_user_attention(&self, request_type: Option) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::RequestUserAttention(request_type.map(Into::into)), + )) + } + + // Creates a window by dispatching a message to the event loop. + // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock. + fn create_window( + &mut self, + pending: PendingWindow, + after_window_creation: Option, + ) -> Result> { + self.context.create_window(pending, after_window_creation) + } + + // Creates a webview by dispatching a message to the event loop. + // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock. + fn create_webview( + &mut self, + pending: PendingWebview, + ) -> Result> { + self.context.create_webview(self.window_id, pending) + } + + fn set_resizable(&self, resizable: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetResizable(resizable), + )) + } + + fn set_enabled(&self, enabled: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetEnabled(enabled), + )) + } + + fn set_maximizable(&self, maximizable: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetMaximizable(maximizable), + )) + } + + fn set_minimizable(&self, minimizable: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetMinimizable(minimizable), + )) + } + + fn set_closable(&self, closable: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetClosable(closable), + )) + } + + fn set_title>(&self, title: S) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetTitle(title.into()), + )) + } + + fn maximize(&self) -> Result<()> { + self + .context + .send_user_message(Message::Window(self.window_id, WindowMessage::Maximize)) + } + + fn unmaximize(&self) -> Result<()> { + self + .context + .send_user_message(Message::Window(self.window_id, WindowMessage::Unmaximize)) + } + + fn minimize(&self) -> Result<()> { + self + .context + .send_user_message(Message::Window(self.window_id, WindowMessage::Minimize)) + } + + fn unminimize(&self) -> Result<()> { + self + .context + .send_user_message(Message::Window(self.window_id, WindowMessage::Unminimize)) + } + + fn show(&self) -> Result<()> { + self + .context + .send_user_message(Message::Window(self.window_id, WindowMessage::Show)) + } + + fn hide(&self) -> Result<()> { + self + .context + .send_user_message(Message::Window(self.window_id, WindowMessage::Hide)) + } + + fn close(&self) -> Result<()> { + // NOTE: close cannot use `Context::send_user_message` because it accesses the event loop callback + self + .context + .proxy + .send_event(Message::Window(self.window_id, WindowMessage::Close)) + .map_err(|_| Error::FailedToSendMessage) + } + + fn destroy(&self) -> Result<()> { + // NOTE: destroy cannot use `Context::send_user_message` because it accesses the event loop callback + self + .context + .proxy + .send_event(Message::Window(self.window_id, WindowMessage::Destroy)) + .map_err(|_| Error::FailedToSendMessage) + } + + fn set_decorations(&self, decorations: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetDecorations(decorations), + )) + } + + fn set_shadow(&self, enable: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetShadow(enable), + )) + } + + fn set_always_on_bottom(&self, always_on_bottom: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetAlwaysOnBottom(always_on_bottom), + )) + } + + fn set_always_on_top(&self, always_on_top: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetAlwaysOnTop(always_on_top), + )) + } + + fn set_visible_on_all_workspaces(&self, visible_on_all_workspaces: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetVisibleOnAllWorkspaces(visible_on_all_workspaces), + )) + } + + fn set_content_protected(&self, protected: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetContentProtected(protected), + )) + } + + fn set_size(&self, size: Size) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetSize(size), + )) + } + + fn set_min_size(&self, size: Option) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetMinSize(size), + )) + } + + fn set_max_size(&self, size: Option) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetMaxSize(size), + )) + } + + fn set_size_constraints(&self, constraints: WindowSizeConstraints) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetSizeConstraints(constraints), + )) + } + + fn set_position(&self, position: Position) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetPosition(position), + )) + } + + fn set_fullscreen_on_monitor(&self, position: PhysicalPosition) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetFullscreenOnMonitor(position), + )) + } + + fn set_fullscreen(&self, fullscreen: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetFullscreen(fullscreen), + )) + } + + #[cfg(target_os = "macos")] + fn set_simple_fullscreen(&self, enable: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetSimpleFullscreen(enable), + )) + } + + fn set_focus(&self) -> Result<()> { + self + .context + .send_user_message(Message::Window(self.window_id, WindowMessage::SetFocus)) + } + + fn set_focusable(&self, focusable: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetFocusable(focusable), + )) + } + + fn set_icon(&self, icon: Icon) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetIcon(TaoIcon::try_from(icon)?.0), + )) + } + + fn set_skip_taskbar(&self, skip: bool) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetSkipTaskbar(skip), + )) + } + + fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetCursorGrab(grab), + )) + } + + fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetCursorVisible(visible), + )) + } + + fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetCursorIcon(icon), + )) + } + + fn set_cursor_position>(&self, position: Pos) -> crate::Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetCursorPosition(position.into()), + )) + } + + fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetIgnoreCursorEvents(ignore), + )) + } + + fn start_dragging(&self) -> Result<()> { + self + .context + .send_user_message(Message::Window(self.window_id, WindowMessage::DragWindow)) + } + + fn start_resize_dragging(&self, direction: tauri_runtime::ResizeDirection) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::ResizeDragWindow(direction), + )) + } + + fn set_badge_count(&self, count: Option, desktop_filename: Option) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetBadgeCount(count, desktop_filename), + )) + } + + fn set_badge_label(&self, label: Option) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetBadgeLabel(label), + )) + } + + fn set_overlay_icon(&self, icon: Option) -> Result<()> { + let icon: Result> = icon.map_or(Ok(None), |x| Ok(Some(TaoIcon::try_from(x)?))); + + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetOverlayIcon(icon?), + )) + } + + fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetProgressBar(progress_state), + )) + } + + fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetTitleBarStyle(style), + )) + } + + fn set_traffic_light_position(&self, position: Position) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetTrafficLightPosition(position), + )) + } + + fn set_theme(&self, theme: Option) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetTheme(theme), + )) + } + + fn set_background_color(&self, color: Option) -> Result<()> { + self.context.send_user_message(Message::Window( + self.window_id, + WindowMessage::SetBackgroundColor(color), + )) + } +} + +#[derive(Clone)] +pub struct WebviewWrapper { + label: String, + id: WebviewId, + inner: Rc, + context_store: WebContextStore, + webview_event_listeners: WebviewEventListeners, + // the key of the WebContext if it's not shared + context_key: Option, + bounds: Arc>>, +} + +impl Deref for WebviewWrapper { + type Target = WebView; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Drop for WebviewWrapper { + fn drop(&mut self) { + if Rc::get_mut(&mut self.inner).is_some() { + let mut context_store = self.context_store.lock().unwrap(); + + if let Some(web_context) = context_store.get_mut(&self.context_key) { + web_context.referenced_by_webviews.remove(&self.label); + + // https://github.com/tauri-apps/tauri/issues/14626 + // Because WebKit does not close its network process even when no webviews are running, + // we need to ensure to re-use the existing process on Linux by keeping the WebContext + // alive for the lifetime of the app. + // WebKit on macOS handles this itself. + #[cfg(not(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + )))] + if web_context.referenced_by_webviews.is_empty() { + context_store.remove(&self.context_key); + } + } + } + } +} + +#[cfg(windows)] +#[derive(Debug)] +pub enum FocusState { + WindowFocused, + WebviewFocused { + webview_label: String, + }, + Blured { + last_focused_webview_label: Option, + }, +} + +#[cfg(windows)] +impl Default for FocusState { + fn default() -> Self { + Self::Blured { + last_focused_webview_label: None, + } + } +} + +pub struct WindowWrapper { + label: String, + inner: Option>, + // whether this window has child webviews + // or it's just a container for a single webview + has_children: AtomicBool, + webviews: Vec, + window_event_listeners: WindowEventListeners, + #[cfg(windows)] + background_color: Option, + #[cfg(windows)] + is_window_transparent: bool, + #[cfg(windows)] + surface: Option, Arc>>, + #[cfg(windows)] + focused_webview: Arc>, +} + +impl WindowWrapper { + pub fn label(&self) -> &str { + &self.label + } +} + +impl fmt::Debug for WindowWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WindowWrapper") + .field("label", &self.label) + .field("inner", &self.inner) + .finish() + } +} + +#[derive(Debug, Clone)] +pub struct EventProxy(TaoEventLoopProxy>); + +#[cfg(target_os = "ios")] +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Sync for EventProxy {} + +impl EventLoopProxy for EventProxy { + fn send_event(&self, event: T) -> Result<()> { + self + .0 + .send_event(Message::UserEvent(event)) + .map_err(|_| Error::EventLoopClosed) + } +} + +pub trait PluginBuilder { + type Plugin: Plugin; + fn build(self, context: Context) -> Self::Plugin; +} + +pub trait Plugin { + fn on_event( + &mut self, + event: &Event>, + event_loop: &EventLoopWindowTarget>, + proxy: &TaoEventLoopProxy>, + control_flow: &mut ControlFlow, + context: EventLoopIterationContext<'_, T>, + web_context: &WebContextStore, + ) -> bool; +} + +/// A Tauri [`Runtime`] wrapper around wry. +pub struct Wry { + context: Context, + event_loop: EventLoop>, +} + +impl fmt::Debug for Wry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Wry") + .field("main_thread_id", &self.context.main_thread_id) + .field("event_loop", &self.event_loop) + .field("windows", &self.context.main_thread.windows) + .field("web_context", &self.context.main_thread.web_context) + .finish() + } +} + +/// A handle to the Wry runtime. +#[derive(Debug, Clone)] +pub struct WryHandle { + context: Context, +} + +// SAFETY: this is safe since the `Context` usage is guarded by `Context::send_user_message`. +#[allow(clippy::non_send_fields_in_send_ty)] +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, TaoWindowBuilder) + Send + 'static>( + &self, + f: F, + ) -> Result> { + let id = self.context.next_window_id(); + let (tx, rx) = channel(); + self + .context + .send_user_message(Message::CreateRawWindow(id, Box::new(f), tx))?; + rx.recv().unwrap() + } + + /// Gets the [`WindowId`] associated with the given [`TaoWindowId`]. + pub fn window_id(&self, window_id: TaoWindowId) -> WindowId { + self.context.window_id_map.get(&window_id).unwrap() + } + + /// Send a message to the event loop. + pub fn send_event(&self, message: Message) -> Result<()> { + self + .context + .proxy + .send_event(message) + .map_err(|_| Error::FailedToSendMessage)?; + Ok(()) + } + + pub fn plugin + 'static>(&mut self, plugin: P) + where +

>::Plugin: Send, + { + self + .context + .plugins + .lock() + .unwrap() + .push(Box::new(plugin.build(self.context.clone()))); + } +} + +impl RuntimeHandle for WryHandle { + type Runtime = Wry; + + fn create_proxy(&self) -> EventProxy { + EventProxy(self.context.proxy.clone()) + } + + #[cfg(target_os = "macos")] + fn set_activation_policy(&self, activation_policy: ActivationPolicy) -> Result<()> { + self + .context + .send_user_message(Message::SetActivationPolicy(activation_policy)) + } + + #[cfg(target_os = "macos")] + fn set_dock_visibility(&self, visible: bool) -> Result<()> { + self + .context + .send_user_message(Message::SetDockVisibility(visible)) + } + + fn request_exit(&self, code: i32) -> Result<()> { + // NOTE: request_exit cannot use `Context::send_user_message` because it accesses the event loop callback + self + .context + .proxy + .send_event(Message::RequestExit(code)) + .map_err(|_| Error::FailedToSendMessage) + } + + // Creates a window by dispatching a message to the event loop. + // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock. + fn create_window( + &self, + pending: PendingWindow, + after_window_creation: Option, + ) -> Result> { + self.context.create_window(pending, after_window_creation) + } + + // Creates a webview by dispatching a message to the event loop. + // Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock. + fn create_webview( + &self, + window_id: WindowId, + pending: PendingWebview, + ) -> Result> { + self.context.create_webview(window_id, pending) + } + + fn run_on_main_thread(&self, f: F) -> Result<()> { + self.context.send_user_message(Message::Task(Box::new(f))) + } + + fn display_handle( + &self, + ) -> std::result::Result, raw_window_handle::HandleError> { + self.context.main_thread.window_target.display_handle() + } + + fn primary_monitor(&self) -> Result> { + event_loop_window_getter!(self, EventLoopWindowTargetMessage::PrimaryMonitor) + } + + fn monitor_from_point(&self, x: f64, y: f64) -> Result> { + let (tx, rx) = channel(); + self + .context + .send_user_message(Message::EventLoopWindowTarget( + EventLoopWindowTargetMessage::MonitorFromPoint(tx, (x, y)), + ))?; + Ok(rx.recv().unwrap()) + } + + fn available_monitors(&self) -> Result> { + event_loop_window_getter!(self, EventLoopWindowTargetMessage::AvailableMonitors) + } + + fn cursor_position(&self) -> Result> { + event_loop_window_getter!(self, EventLoopWindowTargetMessage::CursorPosition)? + .map_err(|_| Error::FailedToGetCursorPosition) + } + + fn set_theme(&self, theme: Option) { + let _ = self + .context + .send_user_message(Message::EventLoopWindowTarget( + EventLoopWindowTargetMessage::SetTheme(theme), + )); + } + + #[cfg(target_os = "macos")] + fn show(&self) -> tauri_runtime::Result<()> { + self + .context + .send_user_message(Message::Application(ApplicationMessage::Show)) + } + + #[cfg(target_os = "macos")] + fn hide(&self) -> tauri_runtime::Result<()> { + self + .context + .send_user_message(Message::Application(ApplicationMessage::Hide)) + } + + fn set_device_event_filter(&self, filter: DeviceEventFilter) { + let _ = self + .context + .send_user_message(Message::EventLoopWindowTarget( + EventLoopWindowTargetMessage::SetDeviceEventFilter(filter), + )); + } + + #[cfg(target_os = "android")] + fn find_class<'a>( + &self, + env: &mut jni::JNIEnv<'a>, + activity: &jni::objects::JObject<'_>, + name: impl Into, + ) -> std::result::Result, jni::errors::Error> { + find_class(env, activity, name.into()) + } + + #[cfg(target_os = "android")] + fn run_on_android_context(&self, f: F) + where + F: FnOnce(&mut jni::JNIEnv<'_>, &jni::objects::JObject<'_>, &jni::objects::JObject<'_>) + + Send + + 'static, + { + dispatch(f) + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + fn fetch_data_store_identifiers) + Send + 'static>( + &self, + cb: F, + ) -> Result<()> { + self.context.send_user_message(Message::Application( + ApplicationMessage::FetchDataStoreIdentifiers(Box::new(cb)), + )) + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + fn remove_data_store) + Send + 'static>( + &self, + uuid: [u8; 16], + cb: F, + ) -> Result<()> { + self + .context + .send_user_message(Message::Application(ApplicationMessage::RemoveDataStore( + uuid, + Box::new(cb), + ))) + } +} + +impl Wry { + fn init_with_builder( + mut event_loop_builder: EventLoopBuilder>, + #[allow(unused_variables)] args: RuntimeInitArgs, + ) -> Result { + #[cfg(windows)] + if let Some(hook) = args.msg_hook { + use tao::platform::windows::EventLoopBuilderExtWindows; + event_loop_builder.with_msg_hook(hook); + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + if let Some(app_id) = args.app_id { + use tao::platform::unix::EventLoopBuilderExtUnix; + event_loop_builder.with_app_id(app_id); + } + Self::init(event_loop_builder.build()) + } + + fn init(event_loop: EventLoop>) -> Result { + let main_thread_id = current_thread().id(); + let web_context = WebContextStore::default(); + + #[allow(clippy::arc_with_non_send_sync)] + let windows = Arc::new(WindowsStore(RefCell::new(BTreeMap::default()))); + let window_id_map = WindowIdStore::default(); + + let context = Context { + window_id_map, + main_thread_id, + proxy: event_loop.create_proxy(), + main_thread: DispatcherMainThreadContext { + window_target: event_loop.deref().clone(), + web_context, + windows, + #[cfg(feature = "tracing")] + active_tracing_spans: Default::default(), + }, + plugins: Default::default(), + next_window_id: Default::default(), + next_webview_id: Default::default(), + next_window_event_id: Default::default(), + next_webview_event_id: Default::default(), + webview_runtime_installed: wry::webview_version().is_ok(), + }; + + Ok(Self { + context, + event_loop, + }) + } +} + +impl Runtime for Wry { + type WindowDispatcher = WryWindowDispatcher; + type WebviewDispatcher = WryWebviewDispatcher; + type Handle = WryHandle; + + type EventLoopProxy = EventProxy; + + fn new(args: RuntimeInitArgs) -> Result { + Self::init_with_builder(EventLoopBuilder::>::with_user_event(), args) + } + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + fn new_any_thread(args: RuntimeInitArgs) -> Result { + 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) + } + + #[cfg(windows)] + fn new_any_thread(args: RuntimeInitArgs) -> Result { + 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) + } + + fn create_proxy(&self) -> EventProxy { + EventProxy(self.event_loop.create_proxy()) + } + + fn handle(&self) -> Self::Handle { + WryHandle { + context: self.context.clone(), + } + } + + fn create_window( + &self, + pending: PendingWindow, + after_window_creation: Option, + ) -> Result> { + let label = pending.label.clone(); + let window_id = self.context.next_window_id(); + let (webview_id, use_https_scheme) = pending + .webview + .as_ref() + .map(|w| { + ( + Some(self.context.next_webview_id()), + w.webview_attributes.use_https_scheme, + ) + }) + .unwrap_or((None, false)); + + let window = create_window( + window_id, + webview_id.unwrap_or_default(), + &self.event_loop, + &self.context, + pending, + after_window_creation, + )?; + + let dispatcher = WryWindowDispatcher { + window_id, + context: self.context.clone(), + }; + + self + .context + .main_thread + .windows + .0 + .borrow_mut() + .insert(window_id, window); + + let detached_webview = webview_id.map(|id| { + let webview = DetachedWebview { + label: label.clone(), + dispatcher: WryWebviewDispatcher { + window_id: Arc::new(Mutex::new(window_id)), + webview_id: id, + context: self.context.clone(), + }, + }; + DetachedWindowWebview { + webview, + use_https_scheme, + } + }); + + Ok(DetachedWindow { + id: window_id, + label, + dispatcher, + webview: detached_webview, + }) + } + + fn create_webview( + &self, + window_id: WindowId, + pending: PendingWebview, + ) -> Result> { + let label = pending.label.clone(); + + let window = self + .context + .main_thread + .windows + .0 + .borrow() + .get(&window_id) + .map(|w| { + ( + w.inner.clone(), + CreateWebviewOptions { + #[cfg(windows)] + focused_webview: w.focused_webview.clone(), + }, + ) + }); + if let Some((Some(window), _options)) = window { + let window_id_wrapper = Arc::new(Mutex::new(window_id)); + + let webview_id = self.context.next_webview_id(); + + let webview = create_webview( + WebviewKind::WindowChild, + &window, + window_id_wrapper.clone(), + webview_id, + &self.context, + pending, + #[cfg(windows)] + _options.focused_webview, + )?; + + if let Some(w) = self + .context + .main_thread + .windows + .0 + .borrow_mut() + .get_mut(&window_id) + { + w.webviews.push(webview); + w.has_children.store(true, Ordering::Relaxed); + } + + let dispatcher = WryWebviewDispatcher { + window_id: window_id_wrapper, + webview_id, + context: self.context.clone(), + }; + + Ok(DetachedWebview { label, dispatcher }) + } else { + Err(Error::WindowNotFound) + } + } + + fn primary_monitor(&self) -> Option { + self + .context + .main_thread + .window_target + .primary_monitor() + .map(|m| MonitorHandleWrapper(m).into()) + } + + fn monitor_from_point(&self, x: f64, y: f64) -> Option { + self + .context + .main_thread + .window_target + .monitor_from_point(x, y) + .map(|m| MonitorHandleWrapper(m).into()) + } + + fn available_monitors(&self) -> Vec { + self + .context + .main_thread + .window_target + .available_monitors() + .map(|m| MonitorHandleWrapper(m).into()) + .collect() + } + + fn cursor_position(&self) -> Result> { + self + .context + .main_thread + .window_target + .cursor_position() + .map_err(|_| Error::FailedToGetCursorPosition) + } + + fn set_theme(&self, theme: Option) { + self.event_loop.set_theme(to_tao_theme(theme)); + } + + #[cfg(target_os = "macos")] + fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) { + self + .event_loop + .set_activation_policy(tao_activation_policy(activation_policy)); + } + + #[cfg(target_os = "macos")] + fn set_activate_ignoring_other_apps(&mut self, ignore: bool) { + self.event_loop.set_activate_ignoring_other_apps(ignore); + } + + #[cfg(target_os = "macos")] + fn set_dock_visibility(&mut self, visible: bool) { + self.event_loop.set_dock_visibility(visible); + } + + #[cfg(target_os = "macos")] + fn show(&self) { + self.event_loop.show_application(); + } + + #[cfg(target_os = "macos")] + fn hide(&self) { + self.event_loop.hide_application(); + } + + fn set_device_event_filter(&mut self, filter: DeviceEventFilter) { + self + .event_loop + .set_device_event_filter(DeviceEventFilterWrapper::from(filter).0); + } + + #[cfg(desktop)] + fn run_iteration) + 'static>(&mut self, mut callback: F) { + use tao::platform::run_return::EventLoopExtRunReturn; + let windows = &self.context.main_thread.windows; + let window_id_map = &self.context.window_id_map; + let web_context = &self.context.main_thread.web_context; + let plugins = &self.context.plugins; + + #[cfg(feature = "tracing")] + let active_tracing_spans = &self.context.main_thread.active_tracing_spans; + + let proxy = self.event_loop.create_proxy(); + + self + .event_loop + .run_return(|event, event_loop, control_flow| { + *control_flow = ControlFlow::Wait; + if let Event::MainEventsCleared = &event { + *control_flow = ControlFlow::Exit; + } + + for p in plugins.lock().unwrap().iter_mut() { + let prevent_default = p.on_event( + &event, + event_loop, + &proxy, + control_flow, + EventLoopIterationContext { + callback: &mut callback, + window_id_map, + windows, + #[cfg(feature = "tracing")] + active_tracing_spans, + }, + web_context, + ); + if prevent_default { + return; + } + } + + handle_event_loop( + event, + event_loop, + control_flow, + EventLoopIterationContext { + callback: &mut callback, + windows, + window_id_map, + #[cfg(feature = "tracing")] + active_tracing_spans, + }, + ); + }); + } + + fn run) + 'static>(self, callback: F) { + let event_handler = make_event_handler(self.context, callback); + self.event_loop.run(event_handler) + } + + #[cfg(not(target_os = "ios"))] + fn run_return) + 'static>(mut self, callback: F) -> i32 { + use tao::platform::run_return::EventLoopExtRunReturn; + + let event_handler = make_event_handler(self.context, callback); + self.event_loop.run_return(event_handler) + } + + #[cfg(target_os = "ios")] + fn run_return) + 'static>(self, callback: F) -> i32 { + self.run(callback); + 0 + } +} + +fn make_event_handler) + 'static>( + context: Context, + mut callback: F, +) -> impl FnMut(Event<'_, Message>, &EventLoopWindowTarget>, &mut ControlFlow) { + let windows = context.main_thread.windows; + let window_id_map = context.window_id_map; + let web_context = context.main_thread.web_context; + let plugins = context.plugins; + + #[cfg(feature = "tracing")] + let active_tracing_spans = context.main_thread.active_tracing_spans; + let proxy = context.proxy; + + move |event, event_loop, control_flow| { + for p in plugins.lock().unwrap().iter_mut() { + let prevent_default = p.on_event( + &event, + event_loop, + &proxy, + control_flow, + EventLoopIterationContext { + callback: &mut callback, + window_id_map: &window_id_map, + windows: &windows, + #[cfg(feature = "tracing")] + active_tracing_spans: &active_tracing_spans, + }, + &web_context, + ); + if prevent_default { + return; + } + } + handle_event_loop( + event, + event_loop, + control_flow, + EventLoopIterationContext { + callback: &mut callback, + window_id_map: &window_id_map, + windows: &windows, + #[cfg(feature = "tracing")] + active_tracing_spans: &active_tracing_spans, + }, + ); + } +} + +pub struct EventLoopIterationContext<'a, T: UserEvent> { + pub callback: &'a mut (dyn FnMut(RunEvent) + 'static), + pub window_id_map: &'a WindowIdStore, + pub windows: &'a WindowsStore, + #[cfg(feature = "tracing")] + pub active_tracing_spans: &'a ActiveTraceSpanStore, +} + +struct UserMessageContext<'a> { + windows: &'a WindowsStore, + window_id_map: &'a WindowIdStore, +} + +fn handle_user_message( + event_loop: &EventLoopWindowTarget>, + message: Message, + context: UserMessageContext, +) { + let UserMessageContext { + window_id_map, + windows, + } = context; + match message { + Message::Task(task) => task(), + #[cfg(target_os = "macos")] + Message::SetActivationPolicy(activation_policy) => { + event_loop.set_activation_policy_at_runtime(tao_activation_policy(activation_policy)) + } + #[cfg(target_os = "macos")] + Message::SetDockVisibility(visible) => event_loop.set_dock_visibility(visible), + Message::RequestExit(_code) => panic!("cannot handle RequestExit on the main thread"), + Message::Application(application_message) => match application_message { + #[cfg(target_os = "macos")] + ApplicationMessage::Show => { + event_loop.show_application(); + } + #[cfg(target_os = "macos")] + ApplicationMessage::Hide => { + event_loop.hide_application(); + } + #[cfg(any(target_os = "macos", target_os = "ios"))] + ApplicationMessage::FetchDataStoreIdentifiers(cb) => { + if let Err(e) = WebView::fetch_data_store_identifiers(cb) { + // this shouldn't ever happen because we're running on the main thread + // but let's be safe and warn here + log::error!("failed to fetch data store identifiers: {e}"); + } + } + #[cfg(any(target_os = "macos", target_os = "ios"))] + ApplicationMessage::RemoveDataStore(uuid, cb) => { + WebView::remove_data_store(&uuid, move |res| { + cb(res.map_err(|_| Error::FailedToRemoveDataStore)) + }) + } + }, + Message::Window(id, window_message) => { + let w = windows.0.borrow().get(&id).map(|w| { + #[cfg(windows)] + let focused_webview = w.focused_webview.clone(); + #[cfg(not(windows))] + let focused_webview = (); + ( + w.inner.clone(), + w.webviews.clone(), + w.has_children.load(Ordering::Relaxed), + w.window_event_listeners.clone(), + focused_webview, + ) + }); + if let Some(( + Some(window), + webviews, + has_children, + window_event_listeners, + _focused_webview, + )) = w + { + match window_message { + WindowMessage::AddEventListener(id, listener) => { + window_event_listeners.lock().unwrap().insert(id, listener); + } + + // Getters + WindowMessage::ScaleFactor(tx) => tx.send(window.scale_factor()).unwrap(), + WindowMessage::InnerPosition(tx) => tx + .send( + window + .inner_position() + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(), + WindowMessage::OuterPosition(tx) => tx + .send( + window + .outer_position() + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(), + WindowMessage::InnerSize(tx) => tx + .send(inner_size(&window, &webviews, has_children)) + .unwrap(), + WindowMessage::OuterSize(tx) => tx.send(window.outer_size()).unwrap(), + WindowMessage::IsFullscreen(tx) => tx.send(window.fullscreen().is_some()).unwrap(), + WindowMessage::IsMinimized(tx) => tx.send(window.is_minimized()).unwrap(), + WindowMessage::IsMaximized(tx) => tx.send(window.is_maximized()).unwrap(), + #[cfg(not(windows))] + WindowMessage::IsFocused(tx) => tx.send(window.is_focused()).unwrap(), + #[cfg(windows)] + WindowMessage::IsFocused(tx) => { + let focused = if has_children { + // on multiwebview mode, get the focused state from cache, + // as the window might not have direct focus + matches!( + *_focused_webview.lock().unwrap(), + FocusState::WindowFocused | FocusState::WebviewFocused { .. } + ) + } else { + window.is_focused() + }; + tx.send(focused).unwrap() + } + WindowMessage::IsDecorated(tx) => tx.send(window.is_decorated()).unwrap(), + WindowMessage::IsResizable(tx) => tx.send(window.is_resizable()).unwrap(), + WindowMessage::IsMaximizable(tx) => tx.send(window.is_maximizable()).unwrap(), + WindowMessage::IsMinimizable(tx) => tx.send(window.is_minimizable()).unwrap(), + WindowMessage::IsClosable(tx) => tx.send(window.is_closable()).unwrap(), + WindowMessage::IsVisible(tx) => tx.send(window.is_visible()).unwrap(), + WindowMessage::Title(tx) => tx.send(window.title()).unwrap(), + WindowMessage::CurrentMonitor(tx) => tx + .send( + window + .current_monitor() + .map(|m| MonitorHandleWrapper(m).into()), + ) + .unwrap(), + WindowMessage::PrimaryMonitor(tx) => tx + .send( + window + .primary_monitor() + .map(|m| MonitorHandleWrapper(m).into()), + ) + .unwrap(), + WindowMessage::MonitorFromPoint(tx, (x, y)) => tx + .send( + window + .monitor_from_point(x, y) + .map(|m| MonitorHandleWrapper(m).into()), + ) + .unwrap(), + WindowMessage::AvailableMonitors(tx) => tx + .send( + window + .available_monitors() + .map(|m| MonitorHandleWrapper(m).into()) + .collect(), + ) + .unwrap(), + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + WindowMessage::GtkWindow(tx) => tx.send(GtkWindow(window.gtk_window().clone())).unwrap(), + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + WindowMessage::GtkBox(tx) => tx + .send(GtkBox(window.default_vbox().unwrap().clone())) + .unwrap(), + #[cfg(target_os = "android")] + WindowMessage::ActivityName(tx) => { + tx.send(window.activity_name()).unwrap(); + } + #[cfg(target_os = "ios")] + WindowMessage::SceneIdentifier(tx) => { + tx.send(window.scene_identifier()).unwrap(); + } + WindowMessage::RawWindowHandle(tx) => tx + .send( + window + .window_handle() + .map(|h| SendRawWindowHandle(h.as_raw())), + ) + .unwrap(), + WindowMessage::Theme(tx) => { + tx.send(map_theme(&window.theme())).unwrap(); + } + WindowMessage::IsEnabled(tx) => tx.send(window.is_enabled()).unwrap(), + WindowMessage::IsAlwaysOnTop(tx) => tx.send(window.is_always_on_top()).unwrap(), + // Setters + WindowMessage::Center => window.center(), + WindowMessage::RequestUserAttention(request_type) => { + window.request_user_attention(request_type.map(|r| r.0)); + } + WindowMessage::SetResizable(resizable) => { + window.set_resizable(resizable); + #[cfg(windows)] + if !resizable { + undecorated_resizing::detach_resize_handler(window.hwnd()); + } else if !window.is_decorated() { + undecorated_resizing::attach_resize_handler( + window.hwnd(), + window.has_undecorated_shadow(), + ); + } + } + WindowMessage::SetMaximizable(maximizable) => window.set_maximizable(maximizable), + WindowMessage::SetMinimizable(minimizable) => window.set_minimizable(minimizable), + WindowMessage::SetClosable(closable) => window.set_closable(closable), + WindowMessage::SetTitle(title) => window.set_title(&title), + WindowMessage::Maximize => window.set_maximized(true), + WindowMessage::Unmaximize => window.set_maximized(false), + WindowMessage::Minimize => window.set_minimized(true), + WindowMessage::Unminimize => window.set_minimized(false), + WindowMessage::SetEnabled(enabled) => window.set_enabled(enabled), + WindowMessage::Show => window.set_visible(true), + WindowMessage::Hide => window.set_visible(false), + WindowMessage::Close => { + panic!("cannot handle `WindowMessage::Close` on the main thread") + } + WindowMessage::Destroy => { + panic!("cannot handle `WindowMessage::Destroy` on the main thread") + } + WindowMessage::SetDecorations(decorations) => { + window.set_decorations(decorations); + #[cfg(windows)] + if decorations { + undecorated_resizing::detach_resize_handler(window.hwnd()); + } else if window.is_resizable() { + undecorated_resizing::attach_resize_handler( + window.hwnd(), + window.has_undecorated_shadow(), + ); + } + } + WindowMessage::SetShadow(_enable) => { + #[cfg(windows)] + { + window.set_undecorated_shadow(_enable); + undecorated_resizing::update_drag_hwnd_rgn_for_undecorated(window.hwnd(), _enable); + } + #[cfg(target_os = "macos")] + window.set_has_shadow(_enable); + } + WindowMessage::SetAlwaysOnBottom(always_on_bottom) => { + window.set_always_on_bottom(always_on_bottom) + } + WindowMessage::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top), + WindowMessage::SetVisibleOnAllWorkspaces(visible_on_all_workspaces) => { + window.set_visible_on_all_workspaces(visible_on_all_workspaces) + } + WindowMessage::SetContentProtected(protected) => window.set_content_protection(protected), + WindowMessage::SetSize(size) => { + window.set_inner_size(size); + } + WindowMessage::SetMinSize(size) => { + window.set_min_inner_size(size); + } + WindowMessage::SetMaxSize(size) => { + window.set_max_inner_size(size); + } + WindowMessage::SetSizeConstraints(constraints) => { + window.set_inner_size_constraints(tao::window::WindowSizeConstraints { + min_width: constraints.min_width, + min_height: constraints.min_height, + max_width: constraints.max_width, + max_height: constraints.max_height, + }); + } + WindowMessage::SetPosition(position) => window.set_outer_position(position), + WindowMessage::SetFullscreen(fullscreen) => { + if fullscreen { + window.set_fullscreen(Some(Fullscreen::Borderless(None))) + } else { + window.set_fullscreen(None) + } + } + WindowMessage::SetFullscreenOnMonitor(position) => { + // Not `Window::monitor_from_point`: on macOS and Linux (GTK) it takes logical + // coordinates, while callers pass physical ones (e.g. `Monitor::position`). + if let Some(monitor) = + find_monitor_for_position(window.available_monitors(), position.into()) + { + window.set_fullscreen(Some(Fullscreen::Borderless(Some(monitor)))) + } + } + + #[cfg(target_os = "macos")] + WindowMessage::SetSimpleFullscreen(enable) => { + window.set_simple_fullscreen(enable); + } + + WindowMessage::SetFocus => { + window.set_focus(); + } + WindowMessage::SetFocusable(focusable) => { + window.set_focusable(focusable); + } + WindowMessage::SetIcon(icon) => { + window.set_window_icon(Some(icon)); + } + #[allow(unused_variables)] + WindowMessage::SetSkipTaskbar(skip) => { + #[cfg(any( + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + let _ = window.set_skip_taskbar(skip); + } + WindowMessage::SetCursorGrab(grab) => { + let _ = window.set_cursor_grab(grab); + } + WindowMessage::SetCursorVisible(visible) => { + window.set_cursor_visible(visible); + } + WindowMessage::SetCursorIcon(icon) => { + window.set_cursor_icon(CursorIconWrapper::from(icon).0); + } + WindowMessage::SetCursorPosition(position) => { + let _ = window.set_cursor_position(position); + } + WindowMessage::SetIgnoreCursorEvents(ignore) => { + let _ = window.set_ignore_cursor_events(ignore); + } + WindowMessage::DragWindow => { + let _ = window.drag_window(); + } + WindowMessage::ResizeDragWindow(direction) => { + let _ = window.drag_resize_window(match direction { + tauri_runtime::ResizeDirection::East => tao::window::ResizeDirection::East, + tauri_runtime::ResizeDirection::North => tao::window::ResizeDirection::North, + tauri_runtime::ResizeDirection::NorthEast => tao::window::ResizeDirection::NorthEast, + tauri_runtime::ResizeDirection::NorthWest => tao::window::ResizeDirection::NorthWest, + tauri_runtime::ResizeDirection::South => tao::window::ResizeDirection::South, + tauri_runtime::ResizeDirection::SouthEast => tao::window::ResizeDirection::SouthEast, + tauri_runtime::ResizeDirection::SouthWest => tao::window::ResizeDirection::SouthWest, + tauri_runtime::ResizeDirection::West => tao::window::ResizeDirection::West, + }); + } + WindowMessage::RequestRedraw => { + window.request_redraw(); + } + WindowMessage::SetBadgeCount(_count, _desktop_filename) => { + #[cfg(target_os = "ios")] + window.set_badge_count( + _count.map_or(0, |x| x.clamp(i32::MIN as i64, i32::MAX as i64) as i32), + ); + + #[cfg(target_os = "macos")] + window.set_badge_label(_count.map(|x| x.to_string())); + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + window.set_badge_count(_count, _desktop_filename); + } + WindowMessage::SetBadgeLabel(_label) => { + #[cfg(target_os = "macos")] + window.set_badge_label(_label); + } + WindowMessage::SetOverlayIcon(_icon) => { + #[cfg(windows)] + window.set_overlay_icon(_icon.map(|x| x.0).as_ref()); + } + WindowMessage::SetProgressBar(progress_state) => { + window.set_progress_bar(ProgressBarStateWrapper::from(progress_state).0); + } + WindowMessage::SetTitleBarStyle(_style) => { + #[cfg(target_os = "macos")] + match _style { + TitleBarStyle::Visible => { + window.set_titlebar_transparent(false); + window.set_fullsize_content_view(true); + } + TitleBarStyle::Transparent => { + window.set_titlebar_transparent(true); + window.set_fullsize_content_view(false); + } + TitleBarStyle::Overlay => { + window.set_titlebar_transparent(true); + window.set_fullsize_content_view(true); + } + unknown => { + #[cfg(feature = "tracing")] + tracing::warn!("unknown title bar style applied: {unknown}"); + + #[cfg(not(feature = "tracing"))] + eprintln!("unknown title bar style applied: {unknown}"); + } + }; + } + WindowMessage::SetTrafficLightPosition(_position) => { + #[cfg(target_os = "macos")] + window.set_traffic_light_inset(_position); + } + WindowMessage::SetTheme(theme) => { + window.set_theme(to_tao_theme(theme)); + } + WindowMessage::SetBackgroundColor(color) => { + window.set_background_color(color.map(Into::into)) + } + } + } + } + Message::Webview(window_id, webview_id, webview_message) => { + #[cfg(any( + target_os = "macos", + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + if let WebviewMessage::Reparent(new_parent_window_id, tx) = webview_message { + let webview_handle = windows.0.borrow_mut().get_mut(&window_id).and_then(|w| { + w.webviews + .iter() + .position(|w| w.id == webview_id) + .map(|webview_index| w.webviews.remove(webview_index)) + }); + + if let Some(webview) = webview_handle { + if let Some((Some(new_parent_window), new_parent_window_webviews)) = windows + .0 + .borrow_mut() + .get_mut(&new_parent_window_id) + .map(|w| (w.inner.clone(), &mut w.webviews)) + { + #[cfg(target_os = "macos")] + let reparent_result = { + use wry::WebViewExtMacOS; + webview.inner.reparent(new_parent_window.ns_window() as _) + }; + #[cfg(windows)] + let reparent_result = { webview.inner.reparent(new_parent_window.hwnd()) }; + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + let reparent_result = { + if let Some(container) = new_parent_window.default_vbox() { + webview.inner.reparent(container) + } else { + Err(wry::Error::MessageSender) + } + }; + + match reparent_result { + Ok(_) => { + new_parent_window_webviews.push(webview); + tx.send(Ok(())).unwrap(); + } + Err(e) => { + log::error!("failed to reparent webview: {e}"); + tx.send(Err(Error::FailedToSendMessage)).unwrap(); + } + } + } + } else { + tx.send(Err(Error::FailedToSendMessage)).unwrap(); + } + + return; + } + + let webview_handle = windows.0.borrow().get(&window_id).map(|w| { + ( + w.inner.clone(), + w.webviews.iter().find(|w| w.id == webview_id).cloned(), + ) + }); + if let Some((Some(window), Some(webview))) = webview_handle { + match webview_message { + WebviewMessage::WebviewEvent(_) => { /* already handled */ } + WebviewMessage::SynthesizedWindowEvent(_) => { /* already handled */ } + WebviewMessage::Reparent(_window_id, _tx) => { /* already handled */ } + WebviewMessage::AddEventListener(id, listener) => { + webview + .webview_event_listeners + .lock() + .unwrap() + .insert(id, listener); + } + + #[cfg(all(feature = "tracing", not(target_os = "android")))] + WebviewMessage::EvaluateScript(script, tx, span) => { + let _span = span.entered(); + if let Err(e) = webview.evaluate_script(&script) { + log::error!("{e}"); + } + tx.send(()).unwrap(); + } + #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] + WebviewMessage::EvaluateScript(script) => { + if let Err(e) = webview.evaluate_script(&script) { + log::error!("{e}"); + } + } + #[cfg(all(feature = "tracing", not(target_os = "android")))] + WebviewMessage::EvaluateScriptWithCallback(script, callback, tx, span) => { + let _span = span.entered(); + if let Err(e) = webview.evaluate_script_with_callback(&script, callback) { + log::error!("{e}"); + } + tx.send(()).unwrap(); + } + #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] + WebviewMessage::EvaluateScriptWithCallback(script, callback) => { + if let Err(e) = webview.evaluate_script_with_callback(&script, callback) { + log::error!("{e}"); + } + } + WebviewMessage::Navigate(url) => { + if let Err(e) = webview.load_url(url.as_str()) { + log::error!("failed to navigate to url {}: {}", url, e); + } + } + WebviewMessage::Reload => { + if let Err(e) = webview.reload() { + log::error!("failed to reload: {e}"); + } + } + WebviewMessage::Show => { + if let Err(e) = webview.set_visible(true) { + log::error!("failed to change webview visibility: {e}"); + } + } + WebviewMessage::Hide => { + if let Err(e) = webview.set_visible(false) { + log::error!("failed to change webview visibility: {e}"); + } + } + WebviewMessage::Print => { + let _ = webview.print(); + } + WebviewMessage::Close => { + #[allow(unknown_lints, clippy::manual_inspect)] + windows.0.borrow_mut().get_mut(&window_id).map(|window| { + if let Some(i) = window.webviews.iter().position(|w| w.id == webview.id) { + window.webviews.remove(i); + } + window + }); + } + WebviewMessage::SetBounds(bounds) => { + let bounds: RectWrapper = bounds.into(); + let bounds = bounds.0; + + if let Some(b) = &mut *webview.bounds.lock().unwrap() { + let scale_factor = window.scale_factor(); + let size = bounds.size.to_logical::(scale_factor); + let position = bounds.position.to_logical::(scale_factor); + let window_size = window.inner_size().to_logical::(scale_factor); + b.width_rate = size.width / window_size.width; + b.height_rate = size.height / window_size.height; + b.x_rate = position.x / window_size.width; + b.y_rate = position.y / window_size.height; + } + + if let Err(e) = webview.set_bounds(bounds) { + log::error!("failed to set webview size: {e}"); + } + } + WebviewMessage::SetSize(size) => match webview.bounds() { + Ok(mut bounds) => { + bounds.size = size; + + let scale_factor = window.scale_factor(); + let size = size.to_logical::(scale_factor); + + if let Some(b) = &mut *webview.bounds.lock().unwrap() { + let window_size = window.inner_size().to_logical::(scale_factor); + b.width_rate = size.width / window_size.width; + b.height_rate = size.height / window_size.height; + } + + if let Err(e) = webview.set_bounds(bounds) { + log::error!("failed to set webview size: {e}"); + } + } + Err(e) => { + log::error!("failed to get webview bounds: {e}"); + } + }, + WebviewMessage::SetPosition(position) => match webview.bounds() { + Ok(mut bounds) => { + bounds.position = position; + + let scale_factor = window.scale_factor(); + let position = position.to_logical::(scale_factor); + + if let Some(b) = &mut *webview.bounds.lock().unwrap() { + let window_size = window.inner_size().to_logical::(scale_factor); + b.x_rate = position.x / window_size.width; + b.y_rate = position.y / window_size.height; + } + + if let Err(e) = webview.set_bounds(bounds) { + log::error!("failed to set webview position: {e}"); + } + } + Err(e) => { + log::error!("failed to get webview bounds: {e}"); + } + }, + WebviewMessage::SetZoom(scale_factor) => { + if let Err(e) = webview.zoom(scale_factor) { + log::error!("failed to set webview zoom: {e}"); + } + } + WebviewMessage::SetBackgroundColor(color) => { + if let Err(e) = + webview.set_background_color(color.map(Into::into).unwrap_or((255, 255, 255, 255))) + { + log::error!("failed to set webview background color: {e}"); + } + } + WebviewMessage::ClearAllBrowsingData => { + if let Err(e) = webview.clear_all_browsing_data() { + log::error!("failed to clear webview browsing data: {e}"); + } + } + // Getters + WebviewMessage::Url(tx) => { + tx.send( + webview + .url() + .map(|u| u.parse().expect("invalid webview URL")) + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(); + } + + WebviewMessage::Cookies(tx) => { + tx.send(webview.cookies().map_err(|_| Error::FailedToSendMessage)) + .unwrap(); + } + + WebviewMessage::SetCookie(cookie) => { + if let Err(e) = webview.set_cookie(&cookie) { + log::error!("failed to set webview cookie: {e}"); + } + } + + WebviewMessage::DeleteCookie(cookie) => { + if let Err(e) = webview.delete_cookie(&cookie) { + log::error!("failed to delete webview cookie: {e}"); + } + } + + WebviewMessage::CookiesForUrl(url, tx) => { + let webview_cookies = webview + .cookies_for_url(url.as_str()) + .map_err(|_| Error::FailedToSendMessage); + tx.send(webview_cookies).unwrap(); + } + + WebviewMessage::Bounds(tx) => { + tx.send( + webview + .bounds() + .map(|bounds| tauri_runtime::dpi::Rect { + size: bounds.size, + position: bounds.position, + }) + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(); + } + WebviewMessage::Position(tx) => { + tx.send( + webview + .bounds() + .map(|bounds| bounds.position.to_physical(window.scale_factor())) + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(); + } + WebviewMessage::Size(tx) => { + tx.send( + webview + .bounds() + .map(|bounds| bounds.size.to_physical(window.scale_factor())) + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(); + } + WebviewMessage::SetFocus => { + if let Err(e) = webview.focus() { + log::error!("failed to focus webview: {e}"); + } + } + WebviewMessage::SetAutoResize(auto_resize) => match webview.bounds() { + Ok(bounds) => { + let scale_factor = window.scale_factor(); + let window_size = window.inner_size().to_logical::(scale_factor); + *webview.bounds.lock().unwrap() = if auto_resize { + let size = bounds.size.to_logical::(scale_factor); + let position = bounds.position.to_logical::(scale_factor); + Some(WebviewBounds { + x_rate: position.x / window_size.width, + y_rate: position.y / window_size.height, + width_rate: size.width / window_size.width, + height_rate: size.height / window_size.height, + }) + } else { + None + }; + } + Err(e) => { + log::error!("failed to get webview bounds: {e}"); + } + }, + WebviewMessage::WithWebview(f) => { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + { + f(webview.webview()); + } + #[cfg(target_os = "macos")] + { + use wry::WebViewExtMacOS; + let platform_webview = webview.webview(); + let manager = webview.manager(); + let ns_window = webview.ns_window(); + f(Webview { + webview: Retained::as_ptr(&platform_webview).cast_mut() as *mut std::ffi::c_void, + manager: Retained::as_ptr(&manager).cast_mut() as *mut std::ffi::c_void, + ns_window: Retained::as_ptr(&ns_window).cast_mut() as *mut std::ffi::c_void, + }); + } + #[cfg(target_os = "ios")] + { + use wry::WebViewExtIOS; + let platform_webview = webview.inner.webview(); + let manager = webview.inner.manager(); + + f(Webview { + webview: Retained::as_ptr(&platform_webview).cast_mut() as *mut std::ffi::c_void, + manager: Retained::as_ptr(&manager).cast_mut() as *mut std::ffi::c_void, + view_controller: window.ui_view_controller(), + }); + } + #[cfg(windows)] + { + f(Webview { + controller: webview.controller(), + environment: webview.environment(), + }); + } + #[cfg(target_os = "android")] + { + f(webview.handle()) + } + } + #[cfg(any(debug_assertions, feature = "devtools"))] + WebviewMessage::OpenDevTools => { + webview.open_devtools(); + } + #[cfg(any(debug_assertions, feature = "devtools"))] + WebviewMessage::CloseDevTools => { + webview.close_devtools(); + } + #[cfg(any(debug_assertions, feature = "devtools"))] + WebviewMessage::IsDevToolsOpen(tx) => { + tx.send(webview.is_devtools_open()).unwrap(); + } + } + } + } + Message::CreateWebview(window_id, handler, sender) => { + let window = windows.0.borrow().get(&window_id).map(|w| { + ( + w.inner.clone(), + CreateWebviewOptions { + #[cfg(windows)] + focused_webview: w.focused_webview.clone(), + }, + ) + }); + if let Some((Some(window), options)) = window { + match handler(&window, options) { + Ok(webview) => { + if let Some(w) = windows.0.borrow_mut().get_mut(&window_id) { + w.webviews.push(webview); + w.has_children.store(true, Ordering::Relaxed); + } + // SAFETY: The caller calls blocking `rx.recv()` so the receiver will never be dropped before this + sender.send(Ok(())).unwrap(); + } + Err(e) => { + // SAFETY: The caller calls blocking `rx.recv()` so the receiver will never be dropped before this + sender.send(Err(e)).unwrap(); + } + } + } + } + Message::CreateWindow(window_id, handler, sender) => match handler(event_loop) { + Ok(webview) => { + windows.0.borrow_mut().insert(window_id, webview); + // SAFETY: The caller calls blocking `rx.recv()` so the receiver will never be dropped before this + sender.send(Ok(())).unwrap(); + } + Err(e) => { + // SAFETY: The caller calls blocking `rx.recv()` so the receiver will never be dropped before this + sender.send(Err(e)).unwrap(); + } + }, + Message::CreateRawWindow(window_id, handler, sender) => { + let (label, builder) = handler(); + + #[cfg(windows)] + let background_color = builder.window.background_color; + #[cfg(windows)] + let is_window_transparent = builder.window.transparent; + + if let Ok(window) = builder.build(event_loop) { + window_id_map.insert(window.id(), window_id); + + let window = Arc::new(window); + + #[cfg(windows)] + let surface = if is_window_transparent { + if let Ok(context) = softbuffer::Context::new(window.clone()) { + if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) { + window.draw_surface(&mut surface, background_color); + Some(surface) + } else { + None + } + } else { + None + } + } else { + None + }; + + windows.0.borrow_mut().insert( + window_id, + WindowWrapper { + label, + has_children: AtomicBool::new(false), + inner: Some(window.clone()), + window_event_listeners: Default::default(), + webviews: Vec::new(), + #[cfg(windows)] + background_color, + #[cfg(windows)] + is_window_transparent, + #[cfg(windows)] + surface, + #[cfg(windows)] + focused_webview: Default::default(), + }, + ); + sender.send(Ok(Arc::downgrade(&window))).unwrap(); + } else { + sender.send(Err(Error::CreateWindow)).unwrap(); + } + } + + Message::UserEvent(_) => (), + Message::EventLoopWindowTarget(message) => match message { + EventLoopWindowTargetMessage::CursorPosition(sender) => { + let pos = event_loop + .cursor_position() + .map_err(|_| Error::FailedToSendMessage); + sender.send(pos).unwrap(); + } + EventLoopWindowTargetMessage::PrimaryMonitor(sender) => { + sender + .send( + event_loop + .primary_monitor() + .map(|m| MonitorHandleWrapper(m).into()), + ) + .unwrap(); + } + EventLoopWindowTargetMessage::MonitorFromPoint(sender, (x, y)) => { + sender + .send( + event_loop + .monitor_from_point(x, y) + .map(|m| MonitorHandleWrapper(m).into()), + ) + .unwrap(); + } + EventLoopWindowTargetMessage::AvailableMonitors(sender) => { + sender + .send( + event_loop + .available_monitors() + .map(|m| MonitorHandleWrapper(m).into()) + .collect(), + ) + .unwrap(); + } + EventLoopWindowTargetMessage::SetTheme(theme) => { + event_loop.set_theme(to_tao_theme(theme)); + // On macOS tao caches each window's theme and only refreshes it from the + // system-wide appearance change notification, which the app-level + // `NSApp.setAppearance` call above never posts, so `Window::theme()` + // would keep reporting the previous value. tao's window-level setter + // does update the cache, so push the theme through it as well. + #[cfg(target_os = "macos")] + for window in windows.0.borrow().values() { + if let Some(inner) = &window.inner { + inner.set_theme(to_tao_theme(theme)); + } + } + } + EventLoopWindowTargetMessage::SetDeviceEventFilter(filter) => { + event_loop.set_device_event_filter(DeviceEventFilterWrapper::from(filter).0); + } + }, + } +} + +fn handle_event_loop( + event: Event<'_, Message>, + event_loop: &EventLoopWindowTarget>, + control_flow: &mut ControlFlow, + context: EventLoopIterationContext<'_, T>, +) { + let EventLoopIterationContext { + callback, + window_id_map, + windows, + #[cfg(feature = "tracing")] + active_tracing_spans, + } = context; + if *control_flow != ControlFlow::Exit { + *control_flow = ControlFlow::Wait; + } + + match event { + Event::NewEvents(StartCause::Init) => { + callback(RunEvent::Ready); + } + + Event::NewEvents(StartCause::Poll) => { + callback(RunEvent::Resumed); + } + + Event::MainEventsCleared => { + callback(RunEvent::MainEventsCleared); + } + + Event::LoopDestroyed => { + callback(RunEvent::Exit); + } + + #[cfg(windows)] + Event::RedrawRequested(id) => { + if let Some(window_id) = window_id_map.get(&id) { + let mut windows_ref = windows.0.borrow_mut(); + if let Some(window) = windows_ref.get_mut(&window_id) { + if window.is_window_transparent { + let background_color = window.background_color; + if let Some(surface) = &mut window.surface { + if let Some(window) = &window.inner { + window.draw_surface(surface, background_color); + } + } + } + } + } + } + + #[cfg(feature = "tracing")] + Event::RedrawEventsCleared => { + active_tracing_spans.remove_window_draw(); + } + + Event::UserEvent(Message::Webview( + window_id, + webview_id, + WebviewMessage::WebviewEvent(event), + )) => { + let windows_ref = windows.0.borrow(); + if let Some(window) = windows_ref.get(&window_id) { + if let Some(webview) = window.webviews.iter().find(|w| w.id == webview_id) { + let label = webview.label.clone(); + let webview_event_listeners = webview.webview_event_listeners.clone(); + + drop(windows_ref); + + callback(RunEvent::WebviewEvent { + label, + event: event.clone(), + }); + let listeners = webview_event_listeners.lock().unwrap(); + let handlers = listeners.values(); + for handler in handlers { + handler(&event); + } + } + } + } + + Event::UserEvent(Message::Webview( + window_id, + _webview_id, + WebviewMessage::SynthesizedWindowEvent(event), + )) => { + if let Some(event) = WindowEventWrapper::from(event).0 { + let windows_ref = windows.0.borrow(); + let window = windows_ref.get(&window_id); + if let Some(window) = window { + let label = window.label.clone(); + let window_event_listeners = window.window_event_listeners.clone(); + + drop(windows_ref); + + callback(RunEvent::WindowEvent { + label, + event: event.clone(), + }); + + let listeners = window_event_listeners.lock().unwrap(); + let handlers = listeners.values(); + for handler in handlers { + handler(&event); + } + } + } + } + + Event::WindowEvent { + event, window_id, .. + } => { + if let Some(window_id) = window_id_map.get(&window_id) { + { + let windows_ref = windows.0.borrow(); + if let Some(window) = windows_ref.get(&window_id) { + if let Some(event) = WindowEventWrapper::parse(window, &event).0 { + let label = window.label.clone(); + let window_event_listeners = window.window_event_listeners.clone(); + + drop(windows_ref); + + callback(RunEvent::WindowEvent { + label, + event: event.clone(), + }); + let listeners = window_event_listeners.lock().unwrap(); + let handlers = listeners.values(); + for handler in handlers { + handler(&event); + } + } + } + } + + match event { + #[cfg(windows)] + TaoWindowEvent::ThemeChanged(theme) => { + if let Some(window) = windows.0.borrow().get(&window_id) { + for webview in &window.webviews { + let theme = match theme { + TaoTheme::Dark => wry::Theme::Dark, + TaoTheme::Light => wry::Theme::Light, + _ => wry::Theme::Light, + }; + if let Err(e) = webview.set_theme(theme) { + log::error!("failed to set theme: {e}"); + } + } + } + } + TaoWindowEvent::CloseRequested => { + on_close_requested(callback, window_id, windows); + } + TaoWindowEvent::Destroyed => { + let removed = windows.0.borrow_mut().remove(&window_id).is_some(); + if removed { + let is_empty = windows.0.borrow().is_empty(); + if is_empty { + let (tx, rx) = channel(); + callback(RunEvent::ExitRequested { code: None, tx }); + + let recv = rx.try_recv(); + let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent)); + + if !should_prevent { + *control_flow = ControlFlow::Exit; + } + } + } + } + TaoWindowEvent::Resized(size) => { + if let Some((Some(window), webviews)) = windows + .0 + .borrow() + .get(&window_id) + .map(|w| (w.inner.clone(), w.webviews.clone())) + { + let size = size.to_logical::(window.scale_factor()); + for webview in webviews { + if let Some(b) = &*webview.bounds.lock().unwrap() { + if let Err(e) = webview.set_bounds(wry::Rect { + position: LogicalPosition::new(size.width * b.x_rate, size.height * b.y_rate) + .into(), + size: LogicalSize::new(size.width * b.width_rate, size.height * b.height_rate) + .into(), + }) { + log::error!("failed to autoresize webview: {e}"); + } + } + } + } + } + _ => {} + } + } + } + Event::UserEvent(message) => match message { + Message::RequestExit(code) => { + let (tx, rx) = channel(); + callback(RunEvent::ExitRequested { + code: Some(code), + tx, + }); + + let recv = rx.try_recv(); + let should_prevent = matches!(recv, Ok(ExitRequestedEventAction::Prevent)); + + if !should_prevent { + *control_flow = ControlFlow::ExitWithCode(code); + } + } + Message::Window(id, WindowMessage::Close) => { + on_close_requested(callback, id, windows); + } + Message::Window(id, WindowMessage::Destroy) => { + on_window_close(id, windows); + } + Message::UserEvent(t) => callback(RunEvent::UserEvent(t)), + message => { + handle_user_message( + event_loop, + message, + UserMessageContext { + window_id_map, + windows, + }, + ); + } + }, + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))] + Event::Opened { urls } => { + callback(RunEvent::Opened { urls }); + } + #[cfg(target_os = "macos")] + Event::Reopen { + has_visible_windows, + .. + } => callback(RunEvent::Reopen { + has_visible_windows, + }), + #[cfg(target_os = "ios")] + Event::SceneRequested { scene, options } => { + callback(RunEvent::SceneRequested { scene, options }); + } + _ => (), + } +} + +fn on_close_requested<'a, T: UserEvent>( + callback: &'a mut (dyn FnMut(RunEvent) + 'static), + window_id: WindowId, + windows: &WindowsStore, +) { + let (tx, rx) = channel(); + let windows_ref = windows.0.borrow(); + if let Some(w) = windows_ref.get(&window_id) { + let label = w.label.clone(); + let window_event_listeners = w.window_event_listeners.clone(); + + drop(windows_ref); + + let listeners = window_event_listeners.lock().unwrap(); + let handlers = listeners.values(); + for handler in handlers { + handler(&WindowEvent::CloseRequested { + signal_tx: tx.clone(), + }); + } + callback(RunEvent::WindowEvent { + label, + event: WindowEvent::CloseRequested { signal_tx: tx }, + }); + if let Ok(true) = rx.try_recv() { + } else { + on_window_close(window_id, windows); + } + } +} + +fn on_window_close(window_id: WindowId, windows: &WindowsStore) { + if let Some(window_wrapper) = windows.0.borrow_mut().get_mut(&window_id) { + window_wrapper.inner = None; + #[cfg(windows)] + window_wrapper.surface.take(); + } +} + +fn parse_proxy_url(url: &Url) -> Result { + let host = url.host().map(|h| h.to_string()).unwrap_or_default(); + let port = url.port().map(|p| p.to_string()).unwrap_or_default(); + + if url.scheme() == "http" { + let config = ProxyConfig::Http(ProxyEndpoint { host, port }); + + Ok(config) + } else if url.scheme() == "socks5" { + let config = ProxyConfig::Socks5(ProxyEndpoint { host, port }); + + Ok(config) + } else { + Err(Error::InvalidProxyUrl) + } +} + +fn create_window( + window_id: WindowId, + webview_id: u32, + event_loop: &EventLoopWindowTarget>, + context: &Context, + pending: PendingWindow>, + after_window_creation: Option, +) -> Result { + #[allow(unused_mut)] + let PendingWindow { + mut window_builder, + label, + webview, + } = pending; + + #[cfg(feature = "tracing")] + let _webview_create_span = tracing::debug_span!("wry::webview::create").entered(); + #[cfg(feature = "tracing")] + let window_draw_span = tracing::debug_span!("wry::window::draw").entered(); + #[cfg(feature = "tracing")] + let window_create_span = + tracing::debug_span!(parent: &window_draw_span, "wry::window::create").entered(); + + let window_event_listeners = WindowEventListeners::default(); + + #[cfg(windows)] + let background_color = window_builder.inner.window.background_color; + #[cfg(windows)] + let is_window_transparent = window_builder.inner.window.transparent; + + #[cfg(target_os = "macos")] + { + if window_builder.tabbing_identifier.is_none() + || window_builder.inner.window.transparent + || !window_builder.inner.window.decorations + { + window_builder.inner = window_builder.inner.with_automatic_window_tabbing(false); + } + } + + #[cfg(desktop)] + if window_builder.prevent_overflow.is_some() || window_builder.center { + let monitor = if let Some(window_position) = &window_builder.inner.window.position { + find_monitor_for_position(event_loop.available_monitors(), *window_position) + } else { + event_loop.primary_monitor() + }; + if let Some(monitor) = monitor { + let scale_factor = monitor.scale_factor(); + let desired_size = window_builder + .inner + .window + .inner_size + .unwrap_or_else(|| PhysicalSize::new(800, 600).into()); + let mut inner_size = window_builder + .inner + .window + .inner_size_constraints + .clamp(desired_size, scale_factor) + .to_physical::(scale_factor); + let mut window_size = inner_size; + #[allow(unused_mut)] + // Left and right window shadow counts as part of the window on Windows + // We need to include it when calculating positions, but not size + let mut shadow_width = 0; + #[cfg(windows)] + if window_builder.inner.window.decorations { + use windows::Win32::UI::WindowsAndMessaging::{AdjustWindowRect, WS_OVERLAPPEDWINDOW}; + let mut rect = windows::Win32::Foundation::RECT::default(); + let result = unsafe { AdjustWindowRect(&mut rect, WS_OVERLAPPEDWINDOW, false) }; + if result.is_ok() { + shadow_width = (rect.right - rect.left) as u32; + // rect.bottom is made out of shadow, and we don't care about it + window_size.height += -rect.top as u32; + } + } + + if let Some(margin) = window_builder.prevent_overflow { + let work_area = monitor.work_area(); + let margin = margin.to_physical::(scale_factor); + let constraint = PhysicalSize::new( + work_area.size.width - margin.width, + work_area.size.height - margin.height, + ); + if window_size.width > constraint.width || window_size.height > constraint.height { + if window_size.width > constraint.width { + inner_size.width = inner_size + .width + .saturating_sub(window_size.width - constraint.width); + window_size.width = constraint.width; + } + if window_size.height > constraint.height { + inner_size.height = inner_size + .height + .saturating_sub(window_size.height - constraint.height); + window_size.height = constraint.height; + } + window_builder.inner.window.inner_size = Some(inner_size.into()); + } + } + + if window_builder.center { + window_size.width += shadow_width; + let position = window::calculate_window_center_position(window_size, monitor); + let logical_position = position.to_logical::(scale_factor); + window_builder = window_builder.position(logical_position.x, logical_position.y); + } + } + }; + + #[cfg(any(target_os = "macos", target_os = "linux"))] + let (initial_position, is_fullscreen) = ( + window_builder.inner.window.position, + window_builder.inner.window.fullscreen.is_some(), + ); + + // If fullscreen is requested with an explicit position, resolve the target + // monitor up front so the window is created fullscreen on that display. + #[cfg(any(target_os = "macos", target_os = "linux"))] + if let (true, Some(position)) = (is_fullscreen, initial_position) { + if let Some(target_monitor) = + find_monitor_for_position(event_loop.available_monitors(), position) + { + window_builder.inner.window.fullscreen = Some(Fullscreen::Borderless(Some(target_monitor))); + } + } + + let window = window_builder + .inner + .build(event_loop) + .inspect_err(|e| log::error!("Error creating window: {e:?}")) + .map_err(|_| Error::CreateWindow)?; + + // On macOS, `with_position` uses the content origin; the title bar is added + // above it. `set_outer_position` is needed for precise window placement. + #[cfg(target_os = "macos")] + if !is_fullscreen { + if let Some(position) = initial_position { + window.set_outer_position(position); + } + } + + #[cfg(feature = "tracing")] + { + drop(window_create_span); + + context + .main_thread + .active_tracing_spans + .0 + .borrow_mut() + .push(ActiveTracingSpan::WindowDraw { + id: window.id(), + span: window_draw_span, + }); + } + + context.window_id_map.insert(window.id(), window_id); + + if let Some(handler) = after_window_creation { + let raw = RawWindow { + #[cfg(windows)] + hwnd: window.hwnd(), + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + gtk_window: window.gtk_window(), + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + default_vbox: window.default_vbox(), + _marker: &std::marker::PhantomData, + }; + handler(raw); + } + + let mut webviews = Vec::new(); + + #[cfg(windows)] + let focused_webview = Arc::new(Mutex::new(FocusState::default())); + + #[cfg(feature = "unstable")] + let has_children = webview.is_some(); + #[cfg(not(feature = "unstable"))] + let has_children = false; + + if let Some(webview) = webview { + webviews.push(create_webview( + #[cfg(feature = "unstable")] + WebviewKind::WindowChild, + #[cfg(not(feature = "unstable"))] + WebviewKind::WindowContent, + &window, + Arc::new(Mutex::new(window_id)), + webview_id, + context, + webview, + #[cfg(windows)] + focused_webview.clone(), + )?); + } + + let window = Arc::new(window); + + #[cfg(windows)] + let surface = if is_window_transparent { + if let Ok(context) = softbuffer::Context::new(window.clone()) { + if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) { + window.draw_surface(&mut surface, background_color); + Some(surface) + } else { + None + } + } else { + None + } + } else { + None + }; + + Ok(WindowWrapper { + label, + has_children: AtomicBool::new(has_children), + inner: Some(window), + webviews, + window_event_listeners, + #[cfg(windows)] + background_color, + #[cfg(windows)] + is_window_transparent, + #[cfg(windows)] + surface, + #[cfg(windows)] + focused_webview, + }) +} + +/// the kind of the webview +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +enum WebviewKind { + /// webview is the entire window content + WindowContent, + /// webview is a child of the window, which can contain other webviews too + WindowChild, +} + +#[derive(Debug, Clone)] +struct WebviewBounds { + x_rate: f32, + y_rate: f32, + width_rate: f32, + height_rate: f32, +} + +fn create_webview( + kind: WebviewKind, + window: &Window, + window_id: Arc>, + id: WebviewId, + context: &Context, + pending: PendingWebview>, + #[cfg(windows)] focused_webview: Arc>, +) -> Result { + if !context.webview_runtime_installed { + #[cfg(all(not(debug_assertions), windows))] + dialog::error( + r#"Could not find the WebView2 Runtime. + +Make sure it is installed or download it from https://developer.microsoft.com/en-us/microsoft-edge/webview2 + +You may have it installed on another user account, but it is not available for this one. +"#, + ); + + if cfg!(target_os = "macos") { + log::warn!("WebKit webview runtime not found, attempting to create webview anyway."); + } else { + return Err(Error::WebviewRuntimeNotInstalled); + } + } + + #[allow(unused_mut)] + let PendingWebview { + webview_attributes, + uri_scheme_protocols, + label, + ipc_handler, + url, + .. + } = pending; + + let mut web_context = context + .main_thread + .web_context + .lock() + .expect("poisoned WebContext store"); + let is_first_context = web_context.is_empty(); + // the context must be stored on the HashMap because it must outlive the WebView on macOS + let automation_enabled = std::env::var("TAURI_WEBVIEW_AUTOMATION").as_deref() == Ok("true"); + let web_context_key = webview_attributes.data_directory; + let entry = web_context.entry(web_context_key.clone()); + let web_context = match entry { + Occupied(occupied) => { + let occupied = occupied.into_mut(); + occupied.referenced_by_webviews.insert(label.clone()); + occupied + } + Vacant(vacant) => { + let mut web_context = WryWebContext::new(web_context_key.clone()); + web_context.set_allows_automation(if automation_enabled { + is_first_context + } else { + false + }); + vacant.insert(WebContext { + inner: web_context, + referenced_by_webviews: [label.clone()].into(), + registered_custom_protocols: HashSet::new(), + }) + } + }; + + let mut webview_builder = WebViewBuilder::new_with_web_context(&mut web_context.inner) + .with_id(&label) + .with_focused(webview_attributes.focus) + .with_transparent(webview_attributes.transparent) + .with_accept_first_mouse(webview_attributes.accept_first_mouse) + .with_incognito(webview_attributes.incognito) + .with_clipboard(webview_attributes.clipboard) + .with_hotkeys_zoom(webview_attributes.zoom_hotkeys_enabled) + .with_general_autofill_enabled(webview_attributes.general_autofill_enabled); + + if url != "about:blank" { + webview_builder = webview_builder.with_url(&url); + } + + #[cfg(target_os = "macos")] + if let Some(webview_configuration) = webview_attributes.webview_configuration { + webview_builder = webview_builder.with_webview_configuration(webview_configuration); + } + + #[cfg(any(target_os = "windows", target_os = "android"))] + { + webview_builder = webview_builder.with_https_scheme(webview_attributes.use_https_scheme); + } + + if let Some(background_throttling) = webview_attributes.background_throttling { + webview_builder = webview_builder.with_background_throttling(match background_throttling { + tauri_utils::config::BackgroundThrottlingPolicy::Disabled => { + wry::BackgroundThrottlingPolicy::Disabled + } + tauri_utils::config::BackgroundThrottlingPolicy::Suspend => { + wry::BackgroundThrottlingPolicy::Suspend + } + tauri_utils::config::BackgroundThrottlingPolicy::Throttle => { + wry::BackgroundThrottlingPolicy::Throttle + } + }); + } + + if webview_attributes.javascript_disabled { + webview_builder = webview_builder.with_javascript_disabled(); + } + + if let Some(color) = webview_attributes.background_color { + webview_builder = webview_builder.with_background_color(color.into()); + } + + if webview_attributes.drag_drop_handler_enabled { + let proxy = context.proxy.clone(); + let window_id_ = window_id.clone(); + webview_builder = webview_builder.with_drag_drop_handler(move |event| { + let event = match event { + WryDragDropEvent::Enter { + paths, + position: (x, y), + } => DragDropEvent::Enter { + paths, + position: PhysicalPosition::new(x as _, y as _), + }, + WryDragDropEvent::Over { position: (x, y) } => DragDropEvent::Over { + position: PhysicalPosition::new(x as _, y as _), + }, + WryDragDropEvent::Drop { + paths, + position: (x, y), + } => DragDropEvent::Drop { + paths, + position: PhysicalPosition::new(x as _, y as _), + }, + WryDragDropEvent::Leave => DragDropEvent::Leave, + _ => unimplemented!(), + }; + + let message = if kind == WebviewKind::WindowContent { + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::DragDrop(event)) + } else { + WebviewMessage::WebviewEvent(WebviewEvent::DragDrop(event)) + }; + + let _ = proxy.send_event(Message::Webview(*window_id_.lock().unwrap(), id, message)); + true + }); + } + + if let Some(navigation_handler) = pending.navigation_handler { + webview_builder = webview_builder.with_navigation_handler(move |url| { + url + .parse() + .map(|url| navigation_handler(&url)) + .unwrap_or(true) + }); + } + + if let Some(new_window_handler) = pending.new_window_handler { + #[cfg(desktop)] + let context = context.clone(); + webview_builder = webview_builder.with_new_window_req_handler(move |url, features| { + let Ok(url) = url.parse() else { + return wry::NewWindowResponse::Deny; + }; + let response = new_window_handler( + url, + tauri_runtime::webview::NewWindowFeatures::new( + features.size, + features.position, + tauri_runtime::webview::NewWindowOpener { + #[cfg(desktop)] + webview: features.opener.webview, + #[cfg(windows)] + environment: features.opener.environment, + #[cfg(target_os = "macos")] + target_configuration: features.opener.target_configuration, + }, + ), + ); + match response { + tauri_runtime::webview::NewWindowResponse::Allow => wry::NewWindowResponse::Allow, + #[cfg(desktop)] + tauri_runtime::webview::NewWindowResponse::Create { window_id } => { + let windows = &context.main_thread.windows.0; + let webview = windows + .borrow() + .get(&window_id) + .unwrap() + .webviews + .first() + .unwrap() + .clone(); + + #[cfg(desktop)] + wry::NewWindowResponse::Create { + #[cfg(target_os = "macos")] + webview: wry::WebViewExtMacOS::webview(&*webview).as_super().into(), + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + webview: webview.webview(), + #[cfg(windows)] + webview: webview.webview(), + } + } + tauri_runtime::webview::NewWindowResponse::Deny => wry::NewWindowResponse::Deny, + } + }); + } + + if let Some(document_title_changed_handler) = pending.document_title_changed_handler { + webview_builder = + webview_builder.with_document_title_changed_handler(document_title_changed_handler) + } + + if let Some(permission_request_handler) = pending.permission_request_handler { + webview_builder = webview_builder.with_permission_handler(move |kind| { + let kind = webview_permissions::from_wry_permission_kind(kind); + let response = permission_request_handler(kind); + webview_permissions::to_wry_permission_response(response) + }); + } + + let webview_bounds = if let Some(bounds) = webview_attributes.bounds { + let bounds: RectWrapper = bounds.into(); + let bounds = bounds.0; + + let scale_factor = window.scale_factor(); + let position = bounds.position.to_logical::(scale_factor); + let size = bounds.size.to_logical::(scale_factor); + + webview_builder = webview_builder.with_bounds(bounds); + + let window_size = window.inner_size().to_logical::(scale_factor); + + if webview_attributes.auto_resize { + Some(WebviewBounds { + x_rate: position.x / window_size.width, + y_rate: position.y / window_size.height, + width_rate: size.width / window_size.width, + height_rate: size.height / window_size.height, + }) + } else { + None + } + } else { + #[cfg(feature = "unstable")] + { + webview_builder = webview_builder.with_bounds(wry::Rect { + position: LogicalPosition::new(0, 0).into(), + size: window.inner_size().into(), + }); + Some(WebviewBounds { + x_rate: 0., + y_rate: 0., + width_rate: 1., + height_rate: 1., + }) + } + #[cfg(not(feature = "unstable"))] + None + }; + + if let Some(download_handler) = pending.download_handler { + let download_handler_ = download_handler.clone(); + webview_builder = webview_builder.with_download_started_handler(move |url, path| { + if let Ok(url) = url.parse() { + download_handler_(DownloadEvent::Requested { + url, + destination: path, + }) + } else { + false + } + }); + webview_builder = webview_builder.with_download_completed_handler(move |url, path, success| { + if let Ok(url) = url.parse() { + download_handler(DownloadEvent::Finished { url, path, success }); + } + }); + } + + if let Some(page_load_handler) = pending.on_page_load_handler { + webview_builder = webview_builder.with_on_page_load_handler(move |event, url| { + if let Ok(url) = url.parse() { + page_load_handler( + url, + match event { + wry::PageLoadEvent::Started => tauri_runtime::webview::PageLoadEvent::Started, + wry::PageLoadEvent::Finished => tauri_runtime::webview::PageLoadEvent::Finished, + }, + ) + }; + }); + } + + if let Some(user_agent) = webview_attributes.user_agent { + webview_builder = webview_builder.with_user_agent(&user_agent); + } + + if let Some(proxy_url) = webview_attributes.proxy_url { + let config = parse_proxy_url(&proxy_url)?; + + webview_builder = webview_builder.with_proxy_config(config); + } + + #[cfg(windows)] + { + if let Some(additional_browser_args) = webview_attributes.additional_browser_args { + webview_builder = webview_builder.with_additional_browser_args(&additional_browser_args); + } + + if let Some(environment) = webview_attributes.environment { + webview_builder = webview_builder.with_environment(environment); + } + + webview_builder = webview_builder.with_theme(match window.theme() { + TaoTheme::Dark => wry::Theme::Dark, + TaoTheme::Light => wry::Theme::Light, + _ => wry::Theme::Light, + }); + + webview_builder = + webview_builder.with_scroll_bar_style(match webview_attributes.scroll_bar_style { + ScrollBarStyle::Default => WryScrollBarStyle::Default, + ScrollBarStyle::FluentOverlay => WryScrollBarStyle::FluentOverlay, + _ => unreachable!(), + }); + } + + #[cfg(windows)] + { + webview_builder = webview_builder + .with_browser_extensions_enabled(webview_attributes.browser_extensions_enabled); + } + + #[cfg(any( + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + { + if let Some(path) = &webview_attributes.extensions_path { + webview_builder = webview_builder.with_extensions_path(path); + } + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + { + if let Some(related_view) = webview_attributes.related_view { + webview_builder = webview_builder.with_related_view(related_view); + } + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + { + if let Some(data_store_identifier) = &webview_attributes.data_store_identifier { + webview_builder = webview_builder.with_data_store_identifier(*data_store_identifier); + } + + webview_builder = + webview_builder.with_allow_link_preview(webview_attributes.allow_link_preview); + + if let Some(on_web_content_process_terminate_handler) = + pending.on_web_content_process_terminate_handler + { + webview_builder = webview_builder + .with_on_web_content_process_terminate_handler(on_web_content_process_terminate_handler); + } else { + log::debug!("web content process terminated"); + let context_ = context.clone(); + let window_id_ = window_id.clone(); + webview_builder = webview_builder.with_on_web_content_process_terminate_handler(move || { + if let Ok(windows) = &context_.main_thread.windows.0.try_borrow() { + if let Some(window) = windows.get(&*window_id_.lock().unwrap()) { + if let Some(webview) = window.webviews.iter().find(|w| w.id == id) { + match webview.reload() { + Ok(_) => log::debug!("webview reloaded"), + Err(e) => log::error!("failed to reload webview: {e}"), + } + } else { + log::error!("failed to find webview") + } + } else { + log::error!("failed to get window") + } + } else { + log::error!("failed to borrow windows") + } + }); + } + } + + #[cfg(target_os = "ios")] + { + webview_builder = webview_builder.with_limit_navigations_to_app_bound_domains( + webview_attributes.limit_navigations_to_app_bound_domains, + ); + + if let Some(input_accessory_view_builder) = webview_attributes.input_accessory_view_builder { + webview_builder = webview_builder + .with_input_accessory_view_builder(move |webview| input_accessory_view_builder.0(webview)); + } + } + + #[cfg(target_os = "macos")] + { + if let Some(position) = &webview_attributes.traffic_light_position { + webview_builder = webview_builder.with_traffic_light_inset(*position); + } + } + + #[cfg(windows)] + let window_id_for_ipc = window_id.clone(); + #[cfg(not(windows))] + let window_id_for_ipc = window_id; + webview_builder = webview_builder.with_ipc_handler(create_ipc_handler( + window_id_for_ipc, + id, + context.clone(), + label.clone(), + ipc_handler, + )); + + for script in webview_attributes.initialization_scripts { + webview_builder = webview_builder + .with_initialization_script_for_main_only(script.script, script.for_main_frame_only); + } + + for (scheme, protocol) in uri_scheme_protocols { + // on Linux the custom protocols are associated with the web context + // and you cannot register a scheme more than once + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + { + if web_context.registered_custom_protocols.contains(&scheme) { + continue; + } + + web_context + .registered_custom_protocols + .insert(scheme.clone()); + } + + webview_builder = webview_builder.with_asynchronous_custom_protocol( + scheme, + move |webview_id, request, responder| { + protocol( + webview_id, + request, + Box::new(move |response| responder.respond(response)), + ) + }, + ); + } + + #[cfg(any(debug_assertions, feature = "devtools"))] + { + webview_builder = webview_builder.with_devtools(webview_attributes.devtools.unwrap_or(true)); + } + + #[cfg(target_os = "android")] + { + if let Some(on_webview_created) = pending.on_webview_created { + webview_builder = webview_builder.on_webview_created(move |ctx| { + on_webview_created(tauri_runtime::webview::CreationContext { + env: ctx.env, + activity: ctx.activity, + webview: ctx.webview, + }) + }); + } + } + + let webview = match kind { + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + WebviewKind::WindowChild => { + // only way to account for menu bar height, and also works for multiwebviews :) + let vbox = window.default_vbox().unwrap(); + webview_builder.build_gtk(vbox) + } + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + WebviewKind::WindowChild => webview_builder.build_as_child(&window), + WebviewKind::WindowContent => { + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let builder = webview_builder.build(&window); + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let builder = { + let vbox = window.default_vbox().unwrap(); + webview_builder.build_gtk(vbox) + }; + builder + } + } + .map_err(|e| Error::CreateWebview(Box::new(e)))?; + + if kind == WebviewKind::WindowContent { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + undecorated_resizing::attach_resize_handler(&webview); + #[cfg(windows)] + if window.is_resizable() && !window.is_decorated() { + undecorated_resizing::attach_resize_handler(window.hwnd(), window.has_undecorated_shadow()); + } + } + + #[cfg(windows)] + { + let controller = webview.controller(); + let mut token = 0; + + add_focus_change_listeners( + window_id.clone(), + id, + context.proxy.clone(), + focused_webview, + label.clone(), + &controller, + &mut token, + ); + + if let Ok(webview) = unsafe { controller.CoreWebView2() } { + let proxy_clone = context.proxy.clone(); + unsafe { + let _ = webview.add_ContainsFullScreenElementChanged( + &ContainsFullScreenElementChangedEventHandler::create(Box::new(move |sender, _| { + let mut contains_fullscreen_element = windows::core::BOOL::default(); + sender + .ok_or_else(windows::core::Error::empty)? + .ContainsFullScreenElement(&mut contains_fullscreen_element)?; + let _ = proxy_clone.send_event(Message::Window( + *window_id.lock().unwrap(), + WindowMessage::SetFullscreen(contains_fullscreen_element.as_bool()), + )); + Ok(()) + })), + &mut token, + ); + } + } + } + + Ok(WebviewWrapper { + label, + id, + inner: Rc::new(webview), + context_store: context.main_thread.web_context.clone(), + webview_event_listeners: Default::default(), + context_key: if automation_enabled { + None + } else { + web_context_key + }, + bounds: Arc::new(Mutex::new(webview_bounds)), + }) +} + +/// Create a wry ipc handler from a tauri ipc handler. +fn create_ipc_handler( + window_id: Arc>, + webview_id: WebviewId, + context: Context, + label: String, + ipc_handler: Option>>, +) -> Box { + Box::new(move |request| { + if let Some(handler) = &ipc_handler { + handler( + DetachedWebview { + label: label.clone(), + dispatcher: WryWebviewDispatcher { + window_id: window_id.clone(), + webview_id, + context: context.clone(), + }, + }, + request, + ); + } + }) +} + +#[cfg(target_os = "macos")] +fn inner_size( + window: &Window, + webviews: &[WebviewWrapper], + has_children: bool, +) -> PhysicalSize { + if !has_children && !webviews.is_empty() { + use wry::WebViewExtMacOS; + let webview = webviews.first().unwrap(); + let view = unsafe { Retained::cast_unchecked::(webview.webview()) }; + let view_frame = view.frame(); + let logical: LogicalSize = (view_frame.size.width, view_frame.size.height).into(); + return logical.to_physical(window.scale_factor()); + } + + window.inner_size() +} + +#[cfg(not(target_os = "macos"))] +#[allow(unused_variables)] +fn inner_size( + window: &Window, + webviews: &[WebviewWrapper], + has_children: bool, +) -> PhysicalSize { + window.inner_size() +} + +fn to_tao_theme(theme: Option) -> Option { + match theme { + Some(Theme::Light) => Some(TaoTheme::Light), + Some(Theme::Dark) => Some(TaoTheme::Dark), + _ => None, + } +} + +/// Used to prevent duplicated [`WindowEvent::Focused`] events, +/// and to track last focused webview in multi-webview mode for us to restore webview focuses +#[cfg(windows)] +fn add_focus_change_listeners( + window_id: Arc>, + id: u32, + proxy: TaoEventLoopProxy>, + focused_webview: Arc>, + label: String, + controller: &ICoreWebView2Controller, + token: &mut i64, +) { + let label_ = label.clone(); + let window_id_ = window_id.clone(); + let proxy_clone = proxy.clone(); + let focused_webview_ = focused_webview.clone(); + if let Err(error) = unsafe { + controller.add_GotFocus( + &FocusChangedEventHandler::create(Box::new(move |_, _| { + let mut focused_webview = focused_webview_.lock().unwrap(); + // when using multiwebview mode, we should check if the focus change is actually a "webview focus change" + // instead of a window focus change (here we're patching window events, so we only care about the actual window changing focus) + let already_focused = matches!( + *focused_webview, + FocusState::WindowFocused | FocusState::WebviewFocused { .. } + ); + *focused_webview = FocusState::WebviewFocused { + webview_label: label_.clone(), + }; + + if !already_focused { + let _ = proxy_clone.send_event(Message::Webview( + *window_id_.lock().unwrap(), + id, + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)), + )); + } + Ok(()) + })), + token, + ) + } { + log::error!( + "Failed to attach WebView2 `add_GotFocus` handler, `WindowEvent::Focused` will not be sent: {error}" + ); + return; + } + + if let Err(error) = unsafe { + controller.add_LostFocus( + &FocusChangedEventHandler::create(Box::new(move |_, _| { + let mut focused_webview = focused_webview.lock().unwrap(); + // when using multiwebview mode, we should handle webview focus changes + // so we check is the currently focused webview matches this webview's + // (in this case, it means we lost the window focus) + // + // on multiwebview mode if we change focus to a different webview + // we get the gotFocus event of the other webview before the lostFocus + // so this check makes sense + if let FocusState::WebviewFocused { ref webview_label } = *focused_webview { + let lost_window_focus = webview_label == &label; + if lost_window_focus { + // only reset when we lost window focus - otherwise some other webview is focused + *focused_webview = FocusState::Blured { + last_focused_webview_label: Some(label.clone()), + }; + let _ = proxy.send_event(Message::Webview( + *window_id.lock().unwrap(), + id, + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), + )); + } + } + + Ok(()) + })), + token, + ) + } { + log::error!( + "Failed to attach WebView2 `add_LostFocus` handler, `WindowEvent::Focused` will not be sent: {error}" + ); + } +} diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs new file mode 100644 index 0000000..3590c1a --- /dev/null +++ b/crates/tauri-utils/src/config.rs @@ -0,0 +1,5071 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +//! The Tauri configuration used at runtime. +//! +//! It is pulled from a `tauri.conf.json` file and the [`Config`] struct is generated at compile time. +//! +//! # Stability +//! +//! This is a core functionality that is not considered part of the stable API. +//! If you use it, note that it may include breaking changes in the future. +//! +//! These items are intended to be non-breaking from a de/serialization standpoint only. +//! Using and modifying existing config values will try to avoid breaking changes, but they are +//! free to add fields in the future - causing breaking changes for creating and full destructuring. +//! +//! To avoid this, [ignore unknown fields when destructuring] with the `{my, config, ..}` pattern. +//! If you need to create the Rust config directly without deserializing, then create the struct +//! the [Struct Update Syntax] with `..Default::default()`, which may need a +//! `#[allow(clippy::needless_update)]` attribute if you are declaring all fields. +//! +//! [ignore unknown fields when destructuring]: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#ignoring-remaining-parts-of-a-value-with- +//! [Struct Update Syntax]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax + +use http::response::Builder; +#[cfg(feature = "schema")] +use schemars::JsonSchema; +#[cfg(feature = "schema")] +use schemars::schema::Schema; +use semver::Version; +use serde::{ + Deserialize, Serialize, Serializer, + de::{Deserializer, Error as DeError, Visitor}, +}; +use serde_json::Value as JsonValue; +use serde_untagged::UntaggedEnumVisitor; +use serde_with::skip_serializing_none; +use url::Url; + +use std::{ + collections::{BTreeMap, HashMap, HashSet}, + fmt::{self, Display}, + fs::read_to_string, + path::PathBuf, + str::FromStr, +}; + +#[cfg(feature = "schema")] +fn add_description(schema: Schema, description: impl Into) -> Schema { + let value = description.into(); + if value.is_empty() { + schema + } else { + let mut schema_obj = schema.into_object(); + schema_obj.metadata().description = value.into(); + Schema::Object(schema_obj) + } +} + +/// Items to help with parsing content into a [`Config`]. +pub mod parse; + +use crate::{TitleBarStyle, WindowEffect, WindowEffectState, acl::capability::Capability}; + +pub use self::parse::parse; + +fn default_true() -> bool { + true +} + +/// An URL to open on a Tauri webview window. +#[derive(PartialEq, Eq, Debug, Clone, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(untagged)] +#[non_exhaustive] +pub enum WebviewUrl { + /// An external URL. Must use either the `http` or `https` schemes. + External(Url), + /// The path portion of an app URL. + /// For instance, to load `tauri://localhost/users/john`, + /// you can simply provide `users/john` in this configuration. + App(PathBuf), + /// A custom protocol url, for example, `doom://index.html` + CustomProtocol(Url), +} + +impl<'de> Deserialize<'de> for WebviewUrl { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(untagged)] + enum WebviewUrlDeserializer { + Url(Url), + Path(PathBuf), + } + + match WebviewUrlDeserializer::deserialize(deserializer)? { + WebviewUrlDeserializer::Url(u) => { + if u.scheme() == "https" || u.scheme() == "http" { + Ok(Self::External(u)) + } else { + Ok(Self::CustomProtocol(u)) + } + } + WebviewUrlDeserializer::Path(p) => Ok(Self::App(p)), + } + } +} + +impl fmt::Display for WebviewUrl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::External(url) | Self::CustomProtocol(url) => write!(f, "{url}"), + Self::App(path) => write!(f, "{}", path.display()), + } + } +} + +impl Default for WebviewUrl { + fn default() -> Self { + Self::App("index.html".into()) + } +} + +/// A bundle referenced by tauri-bundler. +#[derive(Debug, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[cfg_attr(feature = "schema", schemars(rename_all = "lowercase"))] +pub enum BundleType { + /// The debian bundle (.deb). + Deb, + /// The RPM bundle (.rpm). + Rpm, + /// The AppImage bundle (.appimage). + AppImage, + /// The Microsoft Installer bundle (.msi). + Msi, + /// The NSIS bundle (.exe). + Nsis, + /// The macOS application bundle (.app). + App, + /// The Apple Disk Image bundle (.dmg). + Dmg, +} + +impl BundleType { + /// All bundle types. + fn all() -> &'static [Self] { + &[ + BundleType::Deb, + BundleType::Rpm, + BundleType::AppImage, + BundleType::Msi, + BundleType::Nsis, + BundleType::App, + BundleType::Dmg, + ] + } +} + +impl Display for BundleType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Deb => "deb", + Self::Rpm => "rpm", + Self::AppImage => "appimage", + Self::Msi => "msi", + Self::Nsis => "nsis", + Self::App => "app", + Self::Dmg => "dmg", + } + ) + } +} + +impl Serialize for BundleType { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + serializer.serialize_str(self.to_string().as_ref()) + } +} + +impl<'de> Deserialize<'de> for BundleType { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match s.to_lowercase().as_str() { + "deb" => Ok(Self::Deb), + "rpm" => Ok(Self::Rpm), + "appimage" => Ok(Self::AppImage), + "msi" => Ok(Self::Msi), + "nsis" => Ok(Self::Nsis), + "app" => Ok(Self::App), + "dmg" => Ok(Self::Dmg), + _ => Err(DeError::custom(format!("unknown bundle target '{s}'"))), + } + } +} + +/// Targets to bundle. Each value is case insensitive. +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub enum BundleTarget { + /// Bundle all targets. + #[default] + All, + /// A list of bundle targets. + List(Vec), + /// A single bundle target. + One(BundleType), +} + +#[cfg(feature = "schema")] +impl schemars::JsonSchema for BundleTarget { + fn schema_name() -> std::string::String { + "BundleTarget".to_owned() + } + + fn json_schema(generator: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema { + let any_of = vec![ + schemars::schema::SchemaObject { + const_value: Some("all".into()), + metadata: Some(Box::new(schemars::schema::Metadata { + description: Some("Bundle all targets.".to_owned()), + ..Default::default() + })), + ..Default::default() + } + .into(), + add_description( + generator.subschema_for::>(), + "A list of bundle targets.", + ), + add_description( + generator.subschema_for::(), + "A single bundle target.", + ), + ]; + + schemars::schema::SchemaObject { + subschemas: Some(Box::new(schemars::schema::SubschemaValidation { + any_of: Some(any_of), + ..Default::default() + })), + metadata: Some(Box::new(schemars::schema::Metadata { + description: Some("Targets to bundle. Each value is case insensitive.".to_owned()), + ..Default::default() + })), + ..Default::default() + } + .into() + } +} + +impl Serialize for BundleTarget { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + match self { + Self::All => serializer.serialize_str("all"), + Self::List(l) => l.serialize(serializer), + Self::One(t) => serializer.serialize_str(t.to_string().as_ref()), + } + } +} + +impl<'de> Deserialize<'de> for BundleTarget { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize, Serialize)] + #[serde(untagged)] + pub enum BundleTargetInner { + List(Vec), + One(BundleType), + All(String), + } + + match BundleTargetInner::deserialize(deserializer)? { + BundleTargetInner::All(s) if s.to_lowercase() == "all" => Ok(Self::All), + BundleTargetInner::All(t) => Err(DeError::custom(format!( + "invalid bundle type {t}, expected one of `all`, {}", + BundleType::all() + .iter() + .map(|b| format!("`{b}`")) + .collect::>() + .join(", ") + ))), + BundleTargetInner::List(l) => Ok(Self::List(l)), + BundleTargetInner::One(t) => Ok(Self::One(t)), + } + } +} + +impl BundleTarget { + /// Gets the bundle targets as a [`Vec`]. The vector is empty when set to [`BundleTarget::All`]. + #[allow(dead_code)] + pub fn to_vec(&self) -> Vec { + match self { + Self::All => BundleType::all().to_vec(), + Self::List(list) => list.clone(), + Self::One(i) => vec![i.clone()], + } + } +} + +/// Configuration for AppImage bundles. +/// +/// See more: +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct AppImageConfig { + /// Include additional gstreamer dependencies needed for audio and video playback. + /// This increases the bundle size by ~15-35MB depending on your build system. + #[serde(default, alias = "bundle-media-framework")] + pub bundle_media_framework: bool, + /// The files to include in the Appimage Binary. + #[serde(default)] + pub files: HashMap, +} + +/// Configuration for Debian (.deb) bundles. +/// +/// See more: +#[skip_serializing_none] +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct DebConfig { + /// The list of deb dependencies your application relies on. + pub depends: Option>, + /// The list of deb dependencies your application recommends. + pub recommends: Option>, + /// The list of dependencies the package provides. + pub provides: Option>, + /// The list of package conflicts. + pub conflicts: Option>, + /// The list of package replaces. + pub replaces: Option>, + /// The files to include on the package. + #[serde(default)] + pub files: HashMap, + /// Define the section in Debian Control file. See : https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections + pub section: Option, + /// Change the priority of the Debian Package. By default, it is set to `optional`. + /// Recognized Priorities as of now are : `required`, `important`, `standard`, `optional`, `extra` + pub priority: Option, + /// Path of the uncompressed Changelog file, to be stored at /usr/share/doc/package-name/changelog.gz. See + /// + pub changelog: Option, + /// Path to a custom desktop file Handlebars template. + /// + /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`. + #[serde(alias = "desktop-template")] + pub desktop_template: Option, + /// Path to script that will be executed before the package is unpacked. See + /// + #[serde(alias = "pre-install-script")] + pub pre_install_script: Option, + /// Path to script that will be executed after the package is unpacked. See + /// + #[serde(alias = "post-install-script")] + pub post_install_script: Option, + /// Path to script that will be executed before the package is removed. See + /// + #[serde(alias = "pre-remove-script")] + pub pre_remove_script: Option, + /// Path to script that will be executed after the package is removed. See + /// + #[serde(alias = "post-remove-script")] + pub post_remove_script: Option, +} + +/// Configuration for Linux bundles. +/// +/// See more: +#[skip_serializing_none] +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct LinuxConfig { + /// Configuration for the AppImage bundle. + #[serde(default)] + pub appimage: AppImageConfig, + /// Configuration for the Debian bundle. + #[serde(default)] + pub deb: DebConfig, + /// Configuration for the RPM bundle. + #[serde(default)] + pub rpm: RpmConfig, +} + +/// Compression algorithms used when bundling RPM packages. +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields, tag = "type")] +#[non_exhaustive] +pub enum RpmCompression { + /// Gzip compression + Gzip { + /// Gzip compression level + level: u32, + }, + /// Zstd compression + Zstd { + /// Zstd compression level + level: i32, + }, + /// Xz compression + Xz { + /// Xz compression level + level: u32, + }, + /// Bzip2 compression + Bzip2 { + /// Bzip2 compression level + level: u32, + }, + /// Disable compression + None, +} + +/// Configuration for RPM bundles. +#[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 RpmConfig { + /// The list of RPM dependencies your application relies on. + pub depends: Option>, + /// The list of RPM dependencies your application recommends. + pub recommends: Option>, + /// The list of RPM dependencies your application provides. + pub provides: Option>, + /// The list of RPM dependencies your application conflicts with. They must not be present + /// in order for the package to be installed. + pub conflicts: Option>, + /// The list of RPM dependencies your application supersedes - if this package is installed, + /// packages listed as "obsoletes" will be automatically removed (if they are present). + pub obsoletes: Option>, + /// The RPM release tag. + #[serde(default = "default_release")] + pub release: String, + /// The RPM epoch. + #[serde(default)] + pub epoch: u32, + /// The files to include on the package. + #[serde(default)] + pub files: HashMap, + /// Path to a custom desktop file Handlebars template. + /// + /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`. + #[serde(alias = "desktop-template")] + pub desktop_template: Option, + /// Path to script that will be executed before the package is unpacked. See + /// + #[serde(alias = "pre-install-script")] + pub pre_install_script: Option, + /// Path to script that will be executed after the package is unpacked. See + /// + #[serde(alias = "post-install-script")] + pub post_install_script: Option, + /// Path to script that will be executed before the package is removed. See + /// + #[serde(alias = "pre-remove-script")] + pub pre_remove_script: Option, + /// Path to script that will be executed after the package is removed. See + /// + #[serde(alias = "post-remove-script")] + pub post_remove_script: Option, + /// Compression algorithm and level. Defaults to `Gzip` with level 6. + pub compression: Option, +} + +impl Default for RpmConfig { + fn default() -> Self { + Self { + depends: None, + recommends: None, + provides: None, + conflicts: None, + obsoletes: None, + release: default_release(), + epoch: 0, + files: Default::default(), + desktop_template: None, + pre_install_script: None, + post_install_script: None, + pre_remove_script: None, + post_remove_script: None, + compression: None, + } + } +} + +fn default_release() -> String { + "1".into() +} + +/// 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, +} + +/// Position coordinates struct. +#[derive(Default, Debug, PartialEq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct LogicalPosition { + /// X coordinate. + pub x: f64, + /// Y coordinate. + pub y: f64, +} + +/// 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: +#[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_macos_minimum_system_version<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let version = Option::::deserialize(deserializer)?; + match version { + Some(v) if v.is_empty() => Ok(macos_minimum_system_version()), + e => Ok(e), + } +} + +/// Configuration for the macOS bundles. +/// +/// See more: +#[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 MacConfig { + /// A list of strings indicating any macOS X frameworks that need to be bundled with the application. + /// + /// If a name is used, ".framework" must be omitted and it will look for standard install locations. You may also use a path to a specific framework. + pub frameworks: Option>, + /// The files to include in the application relative to the Contents directory. + #[serde(default)] + pub files: HashMap, + /// The version of the build that identifies an iteration of the bundle. + /// + /// Translates to the bundle's CFBundleVersion property. + #[serde(alias = "bundle-version")] + pub bundle_version: Option, + /// The name of the builder that built the bundle. + /// + /// Translates to the bundle's CFBundleName property. + /// + /// If not set, defaults to the package's product name. + #[serde(alias = "bundle-name")] + pub bundle_name: Option, + /// A version string indicating the minimum macOS X version that the bundled application supports. Defaults to `10.13`. + /// + /// Setting it to `null` completely removes the `LSMinimumSystemVersion` field on the bundle's `Info.plist` + /// and the `MACOSX_DEPLOYMENT_TARGET` environment variable. + /// + /// Ignored in `tauri dev`. + /// + /// An empty string is considered an invalid value so the default value is used. + #[serde( + deserialize_with = "de_macos_minimum_system_version", + default = "macos_minimum_system_version", + alias = "minimum-system-version" + )] + pub minimum_system_version: Option, + /// Allows your application to communicate with the outside world. + /// It should be a lowercase, without port and protocol domain name. + #[serde(alias = "exception-domain")] + pub exception_domain: Option, + /// Identity to use for code signing. + #[serde(alias = "signing-identity")] + pub signing_identity: Option, + /// Whether the codesign should enable [hardened runtime](https://developer.apple.com/documentation/security/hardened_runtime) (for executables) or not. + #[serde(alias = "hardened-runtime", default = "default_true")] + pub hardened_runtime: bool, + /// Provider short name for notarization. + #[serde(alias = "provider-short-name")] + pub provider_short_name: Option, + /// Path to the entitlements file. + pub entitlements: Option, + /// Path to a Info.plist file to merge with the default Info.plist. + /// + /// Note that Tauri also looks for a `Info.plist` file in the same directory as the Tauri configuration file. + #[serde(alias = "info-plist")] + pub info_plist: Option, + /// DMG-specific settings. + #[serde(default)] + pub dmg: DmgConfig, +} + +impl Default for MacConfig { + fn default() -> Self { + Self { + frameworks: None, + files: HashMap::new(), + bundle_version: None, + bundle_name: None, + minimum_system_version: macos_minimum_system_version(), + exception_domain: None, + signing_identity: None, + hardened_runtime: true, + provider_short_name: None, + entitlements: None, + info_plist: None, + dmg: Default::default(), + } + } +} + +fn macos_minimum_system_version() -> Option { + Some("10.13".into()) +} + +fn ios_minimum_system_version() -> String { + "15.0".into() +} + +/// Configuration for a target language for the WiX build. +/// +/// See more: +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WixLanguageConfig { + /// The path to a locale (`.wxl`) file. See . + #[serde(alias = "locale-path")] + pub locale_path: Option, +} + +/// The languages to build using WiX. +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(untagged)] +pub enum WixLanguage { + /// A single language to build, without configuration. + One(String), + /// A list of languages to build, without configuration. + List(Vec), + /// A map of languages and its configuration. + Localized(HashMap), +} + +impl Default for WixLanguage { + fn default() -> Self { + Self::One("en-US".into()) + } +} + +/// Configuration for the MSI bundle using WiX. +/// +/// See more: +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WixConfig { + /// MSI installer version in the format `major.minor.patch.build` (build is optional). + /// + /// Because a valid version is required for MSI installer, it will be derived from [`Config::version`] if this field is not set. + /// + /// The first field is the major version and has a maximum value of 255. The second field is the minor version and has a maximum value of 255. + /// The third and fourth fields have a maximum value of 65,535. + /// + /// See for more info. + pub version: Option, + /// A GUID upgrade code for MSI installer. This code **_must stay the same across all of your updates_**, + /// otherwise, Windows will treat your update as a different app and your users will have duplicate versions of your app. + /// + /// By default, tauri generates this code by generating a Uuid v5 using the string `.exe.app.x64` in the DNS namespace. + /// You can use Tauri's CLI to generate and print this code for you, run `tauri inspect wix-upgrade-code`. + /// + /// It is recommended that you set this value in your tauri config file to avoid accidental changes in your upgrade code + /// whenever you want to change your product name. + #[serde(alias = "upgrade-code")] + pub upgrade_code: Option, + /// The installer languages to build. See . + #[serde(default)] + pub language: WixLanguage, + /// A custom .wxs template to use. + pub template: Option, + /// A list of paths to .wxs files with WiX fragments to use. + #[serde(default, alias = "fragment-paths")] + pub fragment_paths: Vec, + /// The ComponentGroup element ids you want to reference from the fragments. + #[serde(default, alias = "component-group-refs")] + pub component_group_refs: Vec, + /// The Component element ids you want to reference from the fragments. + #[serde(default, alias = "component-refs")] + pub component_refs: Vec, + /// The FeatureGroup element ids you want to reference from the fragments. + #[serde(default, alias = "feature-group-refs")] + pub feature_group_refs: Vec, + /// The Feature element ids you want to reference from the fragments. + #[serde(default, alias = "feature-refs")] + pub feature_refs: Vec, + /// The Merge element ids you want to reference from the fragments. + #[serde(default, alias = "merge-refs")] + pub merge_refs: Vec, + /// Create an elevated update task within Windows Task Scheduler. + #[serde(default, alias = "enable-elevated-update-task")] + pub enable_elevated_update_task: bool, + /// Path to a bitmap file to use as the installation user interface banner. + /// This bitmap will appear at the top of all but the first page of the installer. + /// + /// The required dimensions are 493px × 58px. + #[serde(alias = "banner-path")] + pub banner_path: Option, + /// Path to a bitmap file to use on the installation user interface dialogs. + /// It is used on the welcome and completion dialogs. + /// + /// The required dimensions are 493px × 312px. + #[serde(alias = "dialog-image-path")] + pub dialog_image_path: Option, + /// Enables FIPS compliant algorithms. + /// Can also be enabled via the `TAURI_BUNDLER_WIX_FIPS_COMPLIANT` env var. + #[serde(default, alias = "fips-compliant")] + pub fips_compliant: bool, +} + +/// Compression algorithms used in the NSIS installer. +/// +/// See +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub enum NsisCompression { + /// ZLIB uses the deflate algorithm, it is a quick and simple method. With the default compression level it uses about 300 KB of memory. + Zlib, + /// BZIP2 usually gives better compression ratios than ZLIB, but it is a bit slower and uses more memory. With the default compression level it uses about 4 MB of memory. + Bzip2, + /// LZMA (default) is a new compression method that gives very good compression ratios. The decompression speed is high (10-20 MB/s on a 2 GHz CPU), the compression speed is lower. The memory size that will be used for decompression is the dictionary size plus a few KBs, the default is 8 MB. + #[default] + Lzma, + /// Disable compression + None, +} + +/// Install Modes for the NSIS installer. +#[derive(Default, Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +pub enum NSISInstallerMode { + /// Default mode for the installer. + /// + /// Install the app by default in a directory that doesn't require Administrator access. + /// + /// Installer metadata will be saved under the `HKCU` registry path. + #[default] + CurrentUser, + /// Install the app by default in the `Program Files` folder directory requires Administrator + /// access for the installation. + /// + /// Installer metadata will be saved under the `HKLM` registry path. + PerMachine, + /// Combines both modes and allows the user to choose at install time + /// whether to install for the current user or per machine. Note that this mode + /// will require Administrator access even if the user wants to install it for the current user only. + /// + /// Installer metadata will be saved under the `HKLM` or `HKCU` registry path based on the user's choice. + Both, +} + +/// Configuration for the Installer bundle using NSIS. +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct NsisConfig { + /// A custom .nsi template to use. + pub template: Option, + /// The path to a bitmap file to display on the header of installers pages. + /// + /// The recommended dimensions are 150px x 57px. + #[serde(alias = "header-image")] + pub header_image: Option, + /// The path to a bitmap file for the Welcome page and the Finish page. + /// + /// The recommended dimensions are 164px x 314px. + #[serde(alias = "sidebar-image")] + pub sidebar_image: Option, + // TODO: Change the alias to installer-icon in v3 + /// The path to an icon file used as the installer icon. + #[serde(alias = "install-icon")] + pub installer_icon: Option, + /// The path to an icon file used as the uninstaller icon. + #[serde(alias = "uninstaller-icon")] + pub uninstaller_icon: Option, + /// The path to a bitmap file to display on the header of uninstallers pages. + /// Defaults to [`Self::header_image`]. If this is set but [`Self::header_image`] is not, a default image from NSIS will be applied to `header_image` + /// + /// The recommended dimensions are 150px x 57px. + #[serde(alias = "uninstaller-header-image")] + pub uninstaller_header_image: Option, + /// Whether the installation will be for all users or just the current user. + #[serde(default, alias = "install-mode")] + pub install_mode: NSISInstallerMode, + /// A list of installer languages. Default to `["English"]` if not set. + /// + /// By default the OS language is used. If the OS language is not in the list of languages, the first language will be used. + /// To allow the user to select the language, set `display_language_selector` to `true`. + /// + /// See for the complete list of languages. + pub languages: Option>, + /// A key-value pair where the key is the language and the + /// value is the path to a custom `.nsh` file that holds the translated text for tauri's custom messages. + /// + /// See for an example `.nsh` file. + /// + /// **Note**: the key must be a valid NSIS language and it must be added to the [`Self::languages`] array, + pub custom_language_files: Option>, + /// Whether to display a language selector dialog before the installer and uninstaller windows are rendered or not. + /// By default the OS language is selected, with a fallback to the first language in the `languages` array. + #[serde(default, alias = "display-language-selector")] + pub display_language_selector: bool, + /// Set the compression algorithm used to compress files in the installer. + /// + /// See + #[serde(default)] + pub compression: NsisCompression, + /// Set the folder name for the start menu shortcut. + /// + /// Use this option if you have multiple apps and wish to group their shortcuts under one folder + /// or if you generally prefer to set your shortcut inside a folder. + /// + /// Examples: + /// - `AwesomePublisher`, shortcut will be placed in `%AppData%\Microsoft\Windows\Start Menu\Programs\AwesomePublisher\.lnk` + /// - If unset, shortcut will be placed in `%AppData%\Microsoft\Windows\Start Menu\Programs\.lnk` + #[serde(alias = "start-menu-folder")] + pub start_menu_folder: Option, + /// A path to a `.nsh` file that contains special NSIS macros to be hooked into the + /// main installer.nsi script. + /// + /// Supported hooks are: + /// + /// - `NSIS_HOOK_PREINSTALL`: This hook runs before copying files, setting registry key values and creating shortcuts. + /// - `NSIS_HOOK_POSTINSTALL`: This hook runs after the installer has finished copying all files, setting the registry keys and created shortcuts. + /// - `NSIS_HOOK_PREUNINSTALL`: This hook runs before removing any files, registry keys and shortcuts. + /// - `NSIS_HOOK_POSTUNINSTALL`: This hook runs after files, registry keys and shortcuts have been removed. + /// + /// ### Example + /// + /// ```nsh + /// !macro NSIS_HOOK_PREINSTALL + /// MessageBox MB_OK "PreInstall" + /// !macroend + /// + /// !macro NSIS_HOOK_POSTINSTALL + /// MessageBox MB_OK "PostInstall" + /// !macroend + /// + /// !macro NSIS_HOOK_PREUNINSTALL + /// MessageBox MB_OK "PreUnInstall" + /// !macroend + /// + /// !macro NSIS_HOOK_POSTUNINSTALL + /// MessageBox MB_OK "PostUninstall" + /// !macroend + /// ``` + #[serde(alias = "installer-hooks")] + pub installer_hooks: Option, + /// Deprecated: use [`WindowsConfig::minimum_webview2_version`] (`bundle > windows > minimumWebview2Version`) instead. + /// + /// Try to ensure that the WebView2 version is equal to or newer than this version, + /// if the user's WebView2 is older than this version, + /// the installer will try to trigger a WebView2 update. + #[deprecated( + since = "2.10.0", + note = "Use `WindowsConfig::minimum_webview2_version` instead." + )] + #[serde(alias = "minimum-webview2-version")] + pub minimum_webview2_version: Option, +} + +/// Install modes for the Webview2 runtime. +/// Note that for the updater bundle [`Self::DownloadBootstrapper`] is used. +/// +/// For more information see . +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +pub enum WebviewInstallMode { + /// Do not install the Webview2 as part of the Windows Installer. + Skip, + /// Download the bootstrapper and run it. + /// Requires an internet connection. + /// Results in a smaller installer size, but is not recommended on Windows 7. + DownloadBootstrapper { + /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`. + #[serde(default = "default_true")] + silent: bool, + }, + /// Embed the bootstrapper and run it. + /// Requires an internet connection. + /// Increases the installer size by around 1.8MB, but offers better support on Windows 7. + EmbedBootstrapper { + /// Instructs the installer to run the bootstrapper in silent mode. Defaults to `true`. + #[serde(default = "default_true")] + silent: bool, + }, + /// Embed the offline installer and run it. + /// Does not require an internet connection. + /// Increases the installer size by around 127MB. + OfflineInstaller { + /// Instructs the installer to run the installer in silent mode. Defaults to `true`. + #[serde(default = "default_true")] + silent: bool, + }, + /// Embed a fixed webview2 version and use it at runtime. + /// Increases the installer size by around 180MB. + FixedRuntime { + /// The path to the fixed runtime to use. + /// + /// The fixed version can be downloaded [on the official website](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section). + /// The `.cab` file must be extracted to a folder and this folder path must be defined on this field. + path: PathBuf, + }, +} + +impl Default for WebviewInstallMode { + fn default() -> Self { + Self::DownloadBootstrapper { silent: true } + } +} + +/// Custom Signing Command configuration. +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)] +pub enum CustomSignCommandConfig { + /// A string notation of the script to execute. + /// + /// "%1" will be replaced with the path to the binary to be signed. + /// + /// This is a simpler notation for the command. + /// Tauri will split the string with `' '` and use the first element as the command name and the rest as arguments. + /// + /// If you need to use whitespace in the command or arguments, use the object notation [`Self::CommandWithOptions`]. + Command(String), + /// An object notation of the command. + /// + /// This is more complex notation for the command but + /// this allows you to use whitespace in the command and arguments. + CommandWithOptions { + /// The command to run to sign the binary. + cmd: String, + /// The arguments to pass to the command. + /// + /// "%1" will be replaced with the path to the binary to be signed. + args: Vec, + }, +} + +/// Windows bundler configuration. +/// +/// See more: +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WindowsConfig { + /// Specifies the file digest algorithm to use for creating file signatures. + /// Required for code signing. SHA-256 is recommended. + #[serde(alias = "digest-algorithm")] + pub digest_algorithm: Option, + /// Specifies the SHA1 hash of the signing certificate. + #[serde(alias = "certificate-thumbprint")] + pub certificate_thumbprint: Option, + /// Server to use during timestamping. + #[serde(alias = "timestamp-url")] + pub timestamp_url: Option, + /// Whether to use Time-Stamp Protocol (TSP, a.k.a. RFC 3161) for the timestamp server. Your code signing provider may + /// use a TSP timestamp server, like e.g. SSL.com does. If so, enable TSP by setting to true. + #[serde(default)] + pub tsp: bool, + /// The installation mode for the Webview2 runtime. + #[serde(default, alias = "webview-install-mode")] + pub webview_install_mode: WebviewInstallMode, + /// Validates a second app installation, blocking the user from installing an older version if set to `false`. + /// + /// For instance, if `1.2.1` is installed, the user won't be able to install app version `1.2.0` or `1.1.5`. + /// + /// The default value of this flag is `true`. + #[serde(default = "default_true", alias = "allow-downgrades")] + pub allow_downgrades: bool, + /// Try to ensure that the WebView2 version is equal to or newer than this version, + /// if the user's WebView2 is older than this version, + /// the installer will try to trigger a WebView2 update. + #[serde(alias = "minimum-webview2-version")] + pub minimum_webview2_version: Option, + /// Configuration for the MSI generated with WiX. + pub wix: Option, + /// Configuration for the installer generated with NSIS. + pub nsis: Option, + /// Specify a custom command to sign the binaries. + /// This command needs to have a `%1` in args which is just a placeholder for the binary path, + /// which we will detect and replace before calling the command. + /// + /// By Default we use `signtool.exe` which can be found only on Windows so + /// if you are on another platform and want to cross-compile and sign you will + /// need to use another tool like `osslsigncode`. + #[serde(alias = "sign-command")] + pub sign_command: Option, + /// Whether to bundle the Visual C++ runtime DLLs alongside the application. + /// + /// This can be particularly useful when your application includes sidecars or DLLs that do + /// not statically link the Visual C++ runtime and require the runtime DLLs at runtime, and + /// you do not want to require users to install the Visual C++ Redistributable. This can also + /// be useful when `build > windows > staticVCRuntime` is set to `false`. + #[serde( + default, + rename = "bundleVCRuntime", + alias = "bundle-vc-runtime", + alias = "bundleVcRuntime" + )] + pub bundle_vc_runtime: bool, +} + +impl Default for WindowsConfig { + fn default() -> Self { + Self { + digest_algorithm: None, + certificate_thumbprint: None, + timestamp_url: None, + tsp: false, + webview_install_mode: Default::default(), + allow_downgrades: true, + minimum_webview2_version: None, + wix: None, + nsis: None, + sign_command: None, + bundle_vc_runtime: false, + } + } +} + +/// macOS-only. Corresponds to CFBundleTypeRole +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +pub enum BundleTypeRole { + /// CFBundleTypeRole.Editor. Files can be read and edited. + #[default] + Editor, + /// CFBundleTypeRole.Viewer. Files can be read. + Viewer, + /// CFBundleTypeRole.Shell + Shell, + /// CFBundleTypeRole.QLGenerator + QLGenerator, + /// CFBundleTypeRole.None + None, +} + +impl Display for BundleTypeRole { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Editor => write!(f, "Editor"), + Self::Viewer => write!(f, "Viewer"), + Self::Shell => write!(f, "Shell"), + Self::QLGenerator => write!(f, "QLGenerator"), + Self::None => write!(f, "None"), + } + } +} + +// Issue #13159 - Missing the LSHandlerRank and Apple warns after uploading to App Store Connect. +// https://github.com/tauri-apps/tauri/issues/13159 +/// Corresponds to LSHandlerRank +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +pub enum HandlerRank { + /// LSHandlerRank.Default. This app is an opener of files of this type; this value is also used if no rank is specified. + #[default] + Default, + /// LSHandlerRank.Owner. This app is the primary creator of files of this type. + Owner, + /// LSHandlerRank.Alternate. This app is a secondary viewer of files of this type. + Alternate, + /// LSHandlerRank.None. This app is never selected to open files of this type, but it accepts drops of files of this type. + None, +} + +impl Display for HandlerRank { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Default => write!(f, "Default"), + Self::Owner => write!(f, "Owner"), + Self::Alternate => write!(f, "Alternate"), + Self::None => write!(f, "None"), + } + } +} + +/// An extension for a [`FileAssociation`]. +/// +/// A leading `.` is automatically stripped. +#[derive(Debug, PartialEq, Eq, Clone, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +pub struct AssociationExt(pub String); + +impl fmt::Display for AssociationExt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl<'d> serde::Deserialize<'d> for AssociationExt { + fn deserialize>(deserializer: D) -> Result { + let ext = String::deserialize(deserializer)?; + if let Some(ext) = ext.strip_prefix('.') { + Ok(AssociationExt(ext.into())) + } else { + Ok(AssociationExt(ext)) + } + } +} + +/// File association +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct FileAssociation { + /// File extensions to associate with this app. e.g. 'png' + pub ext: Vec, + /// Declare support to a file with the given content type. Maps to `LSItemContentTypes` on macOS. + /// + /// This allows supporting any file format declared by another application that conforms to this type. + /// Declaration of new types can be done with [`Self::exported_type`] and linking to certain content types are done via [`ExportedFileAssociation::conforms_to`]. + #[serde(alias = "content-types")] + pub content_types: Option>, + /// The name. Maps to `CFBundleTypeName` on macOS. Default to `ext[0]` + pub name: Option, + /// The association description. Windows-only. It is displayed on the `Type` column on Windows Explorer. + pub description: Option, + /// The app's role with respect to the type. Maps to `CFBundleTypeRole` on macOS. + #[serde(default)] + pub role: BundleTypeRole, + /// The mime-type of the association, e.g. `'image/png'` or `'text/plain'`. + /// + /// - **Linux**: written as `MimeType=` in the `.desktop` file. + /// - **macOS / iOS**: added as `public.mime-type` in the `UTTypeTagSpecification` dictionary of + /// the `UTExportedTypeDeclarations` entry in `Info.plist`. + /// - **Android**: used as `android:mimeType` in the `` element of an `` + /// in `AndroidManifest.xml`. + #[serde(alias = "mime-type")] + pub mime_type: Option, + /// The ranking of this app among apps that declare themselves as editors or viewers of the given file type. Maps to `LSHandlerRank` on macOS. + #[serde(default)] + pub rank: HandlerRank, + /// The exported type definition. Maps to a `UTExportedTypeDeclarations` entry on macOS. + /// + /// You should define this if the associated file is a custom file type defined by your application. + pub exported_type: Option, + /// Intent action filters for this file association. + /// + /// By default all filters are used. + #[serde(alias = "android-intent-action-filters")] + pub android_intent_action_filters: Option>, +} + +/// Android intent action. +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Hash)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub enum AndroidIntentAction { + /// ACTION_SEND. + /// + /// + Send, + /// ACTION_SEND_MULTIPLE. + /// + /// + SendMultiple, + /// ACTION_VIEW. + /// + /// + View, +} + +/// The exported type definition. Maps to a `UTExportedTypeDeclarations` entry on macOS. +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ExportedFileAssociation { + /// The unique identifier for the exported type. Maps to `UTTypeIdentifier`. + pub identifier: String, + /// The types that this type conforms to. Maps to `UTTypeConformsTo`. + /// + /// Examples are `public.data`, `public.image`, `public.json` and `public.database`. + #[serde(alias = "conforms-to")] + pub conforms_to: Option>, +} + +impl FileAssociation { + /// Infers UTIs (Uniform Type Identifiers) from file extensions and mime types. + /// This is useful for macOS and iOS to automatically populate `LSItemContentTypes` + /// in the Info.plist for share sheet and file association support. + /// + /// Returns a vector of UTIs that should be included in `LSItemContentTypes`. + /// Explicitly provided content types are included first, followed by inferred types. + pub fn infer_content_types(&self) -> HashSet { + let mut content_types = HashSet::new(); + + // when we have an exported type, we only reference it + if let Some(exported_type) = &self.exported_type { + content_types.insert(exported_type.identifier.clone()); + return content_types; + } + + // Start with explicitly provided content types + if let Some(explicit_types) = &self.content_types { + content_types.extend(explicit_types.iter().cloned()); + } + + // Infer from extensions and add to content_types (avoiding duplicates) + for ext in &self.ext { + if let Some(uti) = extension_to_uti(&ext.0) { + content_types.insert(uti.to_string()); + } + } + + // Also infer from mime type if available (avoiding duplicates) + if let Some(mime_type) = &self.mime_type { + if let Some(uti) = mime_type_to_uti(mime_type) { + content_types.insert(uti.to_string()); + } + } + + content_types + } +} + +/// Generates plist dictionary entries for file associations. +/// This is used by both macOS and iOS bundlers to populate Info.plist. +/// +/// Returns a plist dictionary containing `UTExportedTypeDeclarations` and `CFBundleDocumentTypes` +/// if there are any file associations configured. +pub fn file_associations_plist(associations: &[FileAssociation]) -> Option { + use plist::{Dictionary, Value}; + + if associations.is_empty() { + return None; + } + + let exported_associations = associations + .iter() + .filter_map(|association| { + association.exported_type.as_ref().map(|exported_type| { + let mut dict = Dictionary::new(); + + dict.insert( + "UTTypeIdentifier".into(), + exported_type.identifier.clone().into(), + ); + if let Some(description) = &association.description { + dict.insert("UTTypeDescription".into(), description.clone().into()); + } + if let Some(conforms_to) = &exported_type.conforms_to { + dict.insert( + "UTTypeConformsTo".into(), + Value::Array(conforms_to.iter().map(|s| s.clone().into()).collect()), + ); + } + + let mut specification = Dictionary::new(); + specification.insert( + "public.filename-extension".into(), + Value::Array( + association + .ext + .iter() + .map(|s| s.to_string().into()) + .collect(), + ), + ); + if let Some(mime_type) = &association.mime_type { + specification.insert("public.mime-type".into(), mime_type.clone().into()); + } + + dict.insert("UTTypeTagSpecification".into(), specification.into()); + + Value::Dictionary(dict) + }) + }) + .collect::>(); + + let document_types = associations + .iter() + .map(|association| { + let mut dict = Dictionary::new(); + + if !association.ext.is_empty() { + dict.insert( + "CFBundleTypeExtensions".into(), + Value::Array( + association + .ext + .iter() + .map(|ext| ext.to_string().into()) + .collect(), + ), + ); + } + + // For macOS/iOS share sheet, we need LSItemContentTypes with standard UTIs + let content_types = association.infer_content_types(); + + // Add LSItemContentTypes if we have any content types + if !content_types.is_empty() { + dict.insert( + "LSItemContentTypes".into(), + Value::Array(content_types.iter().map(|s| s.clone().into()).collect()), + ); + } + + let type_name = association + .name + .clone() + .or_else(|| association.ext.first().map(|ext| ext.0.clone())) + .unwrap_or_default(); + dict.insert("CFBundleTypeName".into(), type_name.into()); + dict.insert( + "CFBundleTypeRole".into(), + association.role.to_string().into(), + ); + dict.insert("LSHandlerRank".into(), association.rank.to_string().into()); + + Value::Dictionary(dict) + }) + .collect::>(); + + if exported_associations.is_empty() && document_types.is_empty() { + return None; + } + + let mut plist = Dictionary::new(); + if !exported_associations.is_empty() { + plist.insert( + "UTExportedTypeDeclarations".into(), + Value::Array(exported_associations), + ); + } + if !document_types.is_empty() { + plist.insert("CFBundleDocumentTypes".into(), Value::Array(document_types)); + } + + Some(Value::Dictionary(plist)) +} + +/// Maps file extensions to their standard UTIs for macOS/iOS share sheet support +fn extension_to_uti(ext: &str) -> Option<&'static str> { + match ext.to_lowercase().as_str() { + // Images + "png" => Some("public.png"), + "jpg" | "jpeg" => Some("public.jpeg"), + "gif" => Some("com.compuserve.gif"), + "bmp" => Some("com.microsoft.bmp"), + "tiff" | "tif" => Some("public.tiff"), + "ico" => Some("com.microsoft.ico"), + "heic" | "heif" => Some("public.heif-standard-image"), + "webp" => Some("org.webmproject.webp"), + "svg" => Some("public.svg-image"), + // Videos + "mp4" => Some("public.mpeg-4"), + "mov" => Some("com.apple.quicktime-movie"), + "avi" => Some("public.avi"), + "mkv" => Some("public.mpeg-4"), + // Audio + "mp3" => Some("public.mp3"), + "wav" => Some("com.microsoft.waveform-audio"), + "aac" => Some("public.aac-audio"), + "m4a" => Some("public.mpeg-4-audio"), + // Documents + "pdf" => Some("com.adobe.pdf"), + "txt" => Some("public.plain-text"), + "rtf" => Some("public.rtf"), + "html" | "htm" => Some("public.html"), + "json" => Some("public.json"), + "xml" => Some("public.xml"), + _ => None, + } +} + +/// Infers UTIs from mime type +fn mime_type_to_uti(mime_type: &str) -> Option<&'static str> { + match mime_type { + "image/png" => Some("public.png"), + "image/jpeg" | "image/jpg" => Some("public.jpeg"), + "image/gif" => Some("com.compuserve.gif"), + "image/bmp" => Some("com.microsoft.bmp"), + "image/tiff" => Some("public.tiff"), + "image/heic" | "image/heif" => Some("public.heif-standard-image"), + "image/webp" => Some("org.webmproject.webp"), + "image/svg+xml" => Some("public.svg-image"), + mime if mime.starts_with("image/") => Some("public.image"), + "video/mp4" => Some("public.mpeg-4"), + "video/quicktime" => Some("com.apple.quicktime-movie"), + "video/x-msvideo" => Some("public.avi"), + mime if mime.starts_with("video/") => Some("public.movie"), + "audio/mpeg" | "audio/mp3" => Some("public.mp3"), + "audio/wav" | "audio/wave" => Some("com.microsoft.waveform-audio"), + "audio/aac" => Some("public.aac-audio"), + "audio/mp4" => Some("public.mpeg-4-audio"), + mime if mime.starts_with("audio/") => Some("public.audio"), + "application/pdf" => Some("com.adobe.pdf"), + "text/plain" => Some("public.plain-text"), + "text/rtf" => Some("public.rtf"), + "text/html" => Some("public.html"), + "application/json" => Some("public.json"), + "application/xml" | "text/xml" => Some("public.xml"), + _ => None, + } +} + +/// Deep link protocol configuration. +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct DeepLinkProtocol { + /// URL schemes to associate with this app without `://`. For example `my-app` + #[serde(default)] + pub schemes: Vec, + /// Domains to associate with this app. For example `example.com`. + /// Currently only supported on macOS, translating to an [universal app link]. + /// + /// Note that universal app links require signed apps with a provisioning profile to work. + /// You can accomplish that by including the `embedded.provisionprofile` file in the `macOS > files` option. + /// + /// [universal app link]: https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app + #[serde(default)] + pub domains: Vec, + /// The protocol name. **macOS-only** and maps to `CFBundleTypeName`. Defaults to `.` + pub name: Option, + /// The app's role for these schemes. **macOS-only** and maps to `CFBundleTypeRole`. + #[serde(default)] + pub role: BundleTypeRole, +} + +/// Definition for bundle resources. +/// Can be either a list of paths to include or a map of source to target paths. +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)] +pub enum BundleResources { + /// A list of paths to include. + List(Vec), + /// A map of source to target paths. + Map(HashMap), +} + +impl BundleResources { + /// Adds a path to the resource collection. + pub fn push(&mut self, path: impl Into) { + match self { + Self::List(l) => l.push(path.into()), + Self::Map(l) => { + let path = path.into(); + l.insert(path.clone(), path); + } + } + } +} + +/// Updater type +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields, untagged)] +pub enum Updater { + /// Generates legacy zipped v1 compatible updaters + String(V1Compatible), + /// Produce updaters and their signatures or not + // Can't use untagged on enum field here: https://github.com/GREsau/schemars/issues/222 + Bool(bool), +} + +impl Default for Updater { + fn default() -> Self { + Self::Bool(false) + } +} + +/// Generates legacy zipped v1 compatible updaters +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub enum V1Compatible { + /// Generates legacy zipped v1 compatible updaters + V1Compatible, +} + +/// Configuration for tauri-bundler. +/// +/// See more: +#[skip_serializing_none] +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct BundleConfig { + /// Whether Tauri should bundle your application or just output the executable. + #[serde(default)] + pub active: bool, + /// The bundle targets, currently supports ["deb", "rpm", "appimage", "nsis", "msi", "app", "dmg"] or "all". + #[serde(default)] + pub targets: BundleTarget, + #[serde(default)] + /// Produce updaters and their signatures or not + pub create_updater_artifacts: Updater, + /// The application's publisher. Defaults to the second element in the identifier string. + /// + /// Currently maps to the Manufacturer property of the Windows Installer + /// and the Maintainer field of debian packages if the Cargo.toml does not have the authors field. + pub publisher: Option, + /// A url to the home page of your application. If unset, will + /// fallback to `homepage` defined in `Cargo.toml`. + /// + /// Supported bundle targets: `deb`, `rpm`, `nsis` and `msi`. + pub homepage: Option, + /// The app's icons + #[serde(default)] + pub icon: Vec, + /// App resources to bundle. + /// Each resource is a path to a file or directory. + /// Glob patterns are supported. + /// + /// ## Examples + /// + /// To include a list of files: + /// + /// ```json + /// { + /// "bundle": { + /// "resources": [ + /// "./path/to/some-file.txt", + /// "/absolute/path/to/textfile.txt", + /// "../relative/path/to/jsonfile.json", + /// "some-folder/", + /// "resources/**/*.md" + /// ] + /// } + /// } + /// ``` + /// + /// The bundled files will be in `$RESOURCES/` with the original directory structure preserved, + /// for example: `./path/to/some-file.txt` -> `$RESOURCE/path/to/some-file.txt` + /// + /// To fine control where the files will get copied to, use a map instead + /// + /// ```json + /// { + /// "bundle": { + /// "resources": { + /// "/absolute/path/to/textfile.txt": "resources/textfile.txt", + /// "relative/path/to/jsonfile.json": "resources/jsonfile.json", + /// "resources/": "", + /// "docs/**/*md": "website-docs/" + /// } + /// } + /// } + /// ``` + /// + /// Note that when using glob pattern in this case, the original directory structure is not preserved, + /// everything gets copied to the target directory directly + /// + /// See more: + pub resources: Option, + /// A copyright string associated with your application. + pub copyright: Option, + /// The package's license identifier to be included in the appropriate bundles. + /// If not set, defaults to the license from the Cargo.toml file. + pub license: Option, + /// The path to the license file to be included in the appropriate bundles. + #[serde(alias = "license-file")] + pub license_file: Option, + /// The application kind. + /// + /// Should be one of the following: + /// Business, DeveloperTool, Education, Entertainment, Finance, Game, ActionGame, AdventureGame, ArcadeGame, BoardGame, CardGame, CasinoGame, DiceGame, EducationalGame, FamilyGame, KidsGame, MusicGame, PuzzleGame, RacingGame, RolePlayingGame, SimulationGame, SportsGame, StrategyGame, TriviaGame, WordGame, GraphicsAndDesign, HealthcareAndFitness, Lifestyle, Medical, Music, News, Photography, Productivity, Reference, SocialNetworking, Sports, Travel, Utility, Video, Weather. + pub category: Option, + /// File types to associate with the application. + pub file_associations: Option>, + /// A short description of your application. + #[serde(alias = "short-description")] + pub short_description: Option, + /// A longer, multi-line description of the application. + #[serde(alias = "long-description")] + pub long_description: Option, + /// Whether to use the project's `target` directory, for caching build tools (e.g., Wix and NSIS) when building this application. Defaults to `false`. + /// + /// If true, tools will be cached in `target/.tauri/`. + /// If false, tools will be cached in the current user's platform-specific cache directory. + /// + /// An example where it can be appropriate to set this to `true` is when building this application as a Windows System user (e.g., AWS EC2 workloads), + /// because the Window system's app data directory is restricted. + #[serde(default, alias = "use-local-tools-dir")] + pub use_local_tools_dir: bool, + /// A list of—either absolute or relative—paths to binaries to embed with your application. + /// + /// Note that Tauri will look for system-specific binaries following the pattern "binary-name{-target-triple}{.system-extension}". + /// + /// E.g. for the external binary "my-binary", Tauri looks for: + /// + /// - "my-binary-x86_64-pc-windows-msvc.exe" for Windows + /// - "my-binary-x86_64-apple-darwin" for macOS + /// - "my-binary-x86_64-unknown-linux-gnu" for Linux + /// + /// so don't forget to provide binaries for all targeted platforms. + #[serde(alias = "external-bin")] + pub external_bin: Option>, + /// Configuration for the Windows bundles. + #[serde(default)] + pub windows: WindowsConfig, + /// Configuration for the Linux bundles. + #[serde(default)] + pub linux: LinuxConfig, + /// Configuration for the macOS bundles. + #[serde(rename = "macOS", alias = "macos", default)] + pub macos: MacConfig, + /// iOS configuration. + #[serde(rename = "iOS", alias = "ios", default)] + pub ios: IosConfig, + /// Android configuration. + #[serde(default)] + pub android: AndroidConfig, +} + +/// A tuple struct of RGBA colors. Each value has minimum of 0 and maximum of 255. +#[derive(Debug, PartialEq, Eq, Serialize, Default, Clone, Copy)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Color(pub u8, pub u8, pub u8, pub u8); + +impl From for (u8, u8, u8, u8) { + fn from(value: Color) -> Self { + (value.0, value.1, value.2, value.3) + } +} + +impl From for (u8, u8, u8) { + fn from(value: Color) -> Self { + (value.0, value.1, value.2) + } +} + +impl From<(u8, u8, u8, u8)> for Color { + fn from(value: (u8, u8, u8, u8)) -> Self { + Color(value.0, value.1, value.2, value.3) + } +} + +impl From<(u8, u8, u8)> for Color { + fn from(value: (u8, u8, u8)) -> Self { + Color(value.0, value.1, value.2, 255) + } +} + +impl From for [u8; 4] { + fn from(value: Color) -> Self { + [value.0, value.1, value.2, value.3] + } +} + +impl From for [u8; 3] { + fn from(value: Color) -> Self { + [value.0, value.1, value.2] + } +} + +impl From<[u8; 4]> for Color { + fn from(value: [u8; 4]) -> Self { + Color(value[0], value[1], value[2], value[3]) + } +} + +impl From<[u8; 3]> for Color { + fn from(value: [u8; 3]) -> Self { + Color(value[0], value[1], value[2], 255) + } +} + +impl FromStr for Color { + type Err = String; + fn from_str(mut color: &str) -> Result { + color = color.trim().strip_prefix('#').unwrap_or(color); + let color = match color.len() { + 3 => color.chars() + .flat_map(|c| std::iter::repeat_n(c, 2)) + .chain(std::iter::repeat_n('f', 2)) + .collect(), + 6 => format!("{color}FF"), + 8 => color.to_string(), + _ => return Err("Invalid hex color length, must be either 3, 6 or 8, for example: #fff, #ffffff, or #ffffffff".into()), + }; + + let r = u8::from_str_radix(&color[0..2], 16).map_err(|e| e.to_string())?; + let g = u8::from_str_radix(&color[2..4], 16).map_err(|e| e.to_string())?; + let b = u8::from_str_radix(&color[4..6], 16).map_err(|e| e.to_string())?; + let a = u8::from_str_radix(&color[6..8], 16).map_err(|e| e.to_string())?; + + Ok(Color(r, g, b, a)) + } +} + +fn default_alpha() -> u8 { + 255 +} + +#[derive(Deserialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(untagged)] +enum InnerColor { + /// Color hex string, for example: #fff, #ffffff, or #ffffffff. + String(String), + /// Array of RGB colors. Each value has minimum of 0 and maximum of 255. + Rgb((u8, u8, u8)), + /// Array of RGBA colors. Each value has minimum of 0 and maximum of 255. + Rgba((u8, u8, u8, u8)), + /// Object of red, green, blue, alpha color values. Each value has minimum of 0 and maximum of 255. + RgbaObject { + red: u8, + green: u8, + blue: u8, + #[serde(default = "default_alpha")] + alpha: u8, + }, +} + +impl<'de> Deserialize<'de> for Color { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let color = InnerColor::deserialize(deserializer)?; + let color = match color { + InnerColor::String(string) => string.parse().map_err(serde::de::Error::custom)?, + InnerColor::Rgb(rgb) => Color(rgb.0, rgb.1, rgb.2, 255), + InnerColor::Rgba(rgb) => rgb.into(), + InnerColor::RgbaObject { + red, + green, + blue, + alpha, + } => Color(red, green, blue, alpha), + }; + + Ok(color) + } +} + +#[cfg(feature = "schema")] +impl schemars::JsonSchema for Color { + fn schema_name() -> String { + "Color".to_string() + } + + fn json_schema(_generator: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema { + let mut schema = schemars::schema_for!(InnerColor).schema; + schema.metadata = None; // Remove `title: InnerColor` from schema + + // add hex color pattern validation + let any_of = schema.subschemas().any_of.as_mut().unwrap(); + let schemars::schema::Schema::Object(str_schema) = any_of.first_mut().unwrap() else { + unreachable!() + }; + str_schema.string().pattern = Some("^#?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$".into()); + + schema.into() + } +} + +/// Background throttling policy. +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub enum BackgroundThrottlingPolicy { + /// A policy where background throttling is disabled + Disabled, + /// A policy where a web view that's not in a window fully suspends tasks. This is usually the default behavior in case no policy is set. + Suspend, + /// A policy where a web view that's not in a window limits processing, but does not fully suspend tasks. + Throttle, +} + +/// The window effects configuration object +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WindowEffectsConfig { + /// List of Window effects to apply to the Window. + /// + /// Generally, conflicting effects will apply the first one and ignore the rest but + /// on macOS you can specify one Liquid Glass style and one Visual Effect material at the same time + /// to make Tauri fallback to the latter on macOS 15 and below. + pub effects: Vec, + /// Window effect state **macOS Only**. Ignored for Liquid Glass Effects. + pub state: Option, + /// Window effect corner radius **macOS Only** + pub radius: Option, + /// Window effect color. + /// + /// ## Platform-specific + /// + /// - **Windows**: Affects [`WindowEffect::Blur`] and [`WindowEffect::Acrylic`] only + /// on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11. + /// - **macOS**: Only affects Liquid Glass effects. + pub color: Option, + /// Enables interactive glass behavior, which adds a visual response to user interactions. + /// + /// **macOS 27.0+**. Only affects Liquid Glass effects. + #[serde(default)] + pub interactive: bool, +} + +/// Enable prevent overflow with a margin +/// so that the window's size + this margin won't overflow the workarea +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct PreventOverflowMargin { + /// Horizontal margin in physical pixels + pub width: u32, + /// Vertical margin in physical pixels + pub height: u32, +} + +/// Prevent overflow with a margin +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(untagged)] +pub enum PreventOverflowConfig { + /// Enable prevent overflow or not + Enable(bool), + /// Enable prevent overflow with a margin + /// so that the window's size + this margin won't overflow the workarea + Margin(PreventOverflowMargin), +} + +/// The scrollbar style to use in the webview. +/// +/// ## Platform-specific +/// +/// - **Windows**: This option must be given the same value for all webviews that target the same data directory. +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +#[non_exhaustive] +pub enum ScrollBarStyle { + #[default] + /// The platform's native scrollbar, as rendered by the webview by default. + /// + /// This is the only supported value outside of Windows. + Default, + + /// Fluent UI style overlay scrollbars. **Windows Only** + /// + /// Requires WebView2 Runtime version 125.0.2535.41 or higher, does nothing on older versions, + /// see + FluentOverlay, +} + +/// The window configuration object. +/// +/// See more: +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WindowConfig { + /// The window identifier. It must be alphanumeric. + #[serde(default = "default_window_label")] + pub label: String, + /// Whether Tauri should create this window at app startup or not. + /// + /// When this is set to `false` you must manually grab the config object via `app.config().app.windows` + /// and create it with [`WebviewWindowBuilder::from_config`](https://docs.rs/tauri/2/tauri/webview/struct.WebviewWindowBuilder.html#method.from_config). + /// + /// ## Example: + /// + /// ```rust + /// tauri::Builder::default() + /// .setup(|app| { + /// tauri::WebviewWindowBuilder::from_config(app.handle(), &app.config().app.windows[0])?.build()?; + /// Ok(()) + /// }); + /// ``` + #[serde(default = "default_true")] + pub create: bool, + /// The window webview URL. + #[serde(default)] + pub url: WebviewUrl, + /// The user agent for the webview + #[serde(alias = "user-agent")] + pub user_agent: Option, + /// Whether the drag and drop handlers used internally to generate [`DragDropEvent`]s are enabled on the webview. By default it is enabled. + /// + /// Disabling it is required to use HTML5 drag and drop on the frontend on Windows since we replace the drag drop handler of WebView2. + /// + /// Note: this setting maps to [`WebviewBuilder::disable_drag_drop_handler`], not [`WindowBuilder::drag_and_drop`]. + /// + /// [`DragDropEvent`]: https://docs.rs/tauri/latest/tauri/enum.DragDropEvent.html + /// [`WebviewBuilder::disable_drag_drop_handler`]: https://docs.rs/tauri/latest/tauri/webview/struct.WebviewBuilder.html#method.disable_drag_drop_handler + /// [`WindowBuilder::drag_and_drop`]: https://docs.rs/tauri/latest/x86_64-pc-windows-msvc/tauri/window/struct.WindowBuilder.html#method.drag_and_drop + #[serde(default = "default_true", alias = "drag-drop-enabled")] + pub drag_drop_enabled: bool, + /// Whether or not the window starts centered or not. + #[serde(default)] + pub center: bool, + /// The horizontal position of the window's top left corner in logical pixels + pub x: Option, + /// The vertical position of the window's top left corner in logical pixels + pub y: Option, + /// The window width in logical pixels. + #[serde(default = "default_width")] + pub width: f64, + /// The window height in logical pixels. + #[serde(default = "default_height")] + pub height: f64, + /// The min window width in logical pixels. + #[serde(alias = "min-width")] + pub min_width: Option, + /// The min window height in logical pixels. + #[serde(alias = "min-height")] + pub min_height: Option, + /// The max window width in logical pixels. + #[serde(alias = "max-width")] + pub max_width: Option, + /// The max window height in logical pixels. + #[serde(alias = "max-height")] + pub max_height: Option, + /// Whether or not to prevent the window from overflowing the workarea + /// + /// ## Platform-specific + /// + /// - **iOS / Android:** Unsupported. + #[serde(alias = "prevent-overflow")] + pub prevent_overflow: Option, + /// Whether the window is resizable or not. When resizable is set to false, native window's maximize button is automatically disabled. + #[serde(default = "default_true")] + pub resizable: bool, + /// Whether the window's native maximize button is enabled or not. + /// If resizable is set to false, this setting is ignored. + /// + /// ## Platform-specific + /// + /// - **macOS:** Disables the "zoom" button in the window titlebar, which is also used to enter fullscreen mode. + /// - **Linux / iOS / Android:** Unsupported. + #[serde(default = "default_true")] + pub maximizable: bool, + /// Whether the window's native minimize button is enabled or not. + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + #[serde(default = "default_true")] + pub minimizable: bool, + /// Whether the window's native close button is enabled or not. + /// + /// ## Platform-specific + /// + /// - **Linux:** "GTK+ will do its best to convince the window manager not to show a close button. + /// Depending on the system, this function may not have any effect when called on a window that is already visible" + /// - **iOS / Android:** Unsupported. + #[serde(default = "default_true")] + pub closable: bool, + /// The window title. + #[serde(default = "default_title")] + pub title: String, + /// Whether the window starts as fullscreen or not. + #[serde(default)] + pub fullscreen: bool, + /// Whether the window will be initially focused or not. + #[serde(default = "default_true")] + pub focus: bool, + /// Whether the window will be focusable or not. + #[serde(default = "default_true")] + pub focusable: bool, + /// Whether the window is transparent or not. + /// + /// ## Platform-specific + /// + /// - **macOS**: Requires the `macos-private-api` Cargo feature, which is enabled by setting + /// `app > macOSPrivateApi` to `true` in the configuration file. + /// **WARNING:** Using private APIs on macOS prevents your application from being accepted to the App Store. + /// If you only need a translucent background, use `windowEffects` instead, which relies on public APIs. + /// - **Windows**: Using `noRedirectionBitmap` can help avoid a white flash when creating a transparent window. + #[serde(default)] + pub transparent: bool, + /// Whether the window is maximized or not. + #[serde(default)] + pub maximized: bool, + /// Whether the window is visible or not. + #[serde(default = "default_true")] + pub visible: bool, + /// Whether the window should have borders and bars. + #[serde(default = "default_true")] + pub decorations: bool, + /// Whether the window should always be below other windows. + #[serde(default, alias = "always-on-bottom")] + pub always_on_bottom: bool, + /// Whether the window should always be on top of other windows. + #[serde(default, alias = "always-on-top")] + pub always_on_top: bool, + /// Whether the window should be visible on all workspaces or virtual desktops. + /// + /// ## Platform-specific + /// + /// - **Windows / iOS / Android:** Unsupported. + #[serde(default, alias = "visible-on-all-workspaces")] + pub visible_on_all_workspaces: bool, + /// Prevents the window contents from being captured by other apps. + #[serde(default, alias = "content-protected")] + pub content_protected: bool, + /// If `true`, hides the window icon from the taskbar on Windows and Linux. + #[serde(default, alias = "skip-taskbar")] + pub skip_taskbar: bool, + /// The name of the window class created on Windows to create the window. **Windows only**. + pub window_classname: Option, + /// This sets `WS_EX_NOREDIRECTIONBITMAP`. + /// + /// This can avoid the white flash that may appear before the webview content is rendered + /// when using a transparent window. **Windows only**. + #[serde(default, alias = "no-redirection-bitmap")] + pub no_redirection_bitmap: bool, + /// The initial window theme. Defaults to the system theme. Only implemented on Windows and macOS 10.14+. + pub theme: Option, + /// The style of the macOS title bar. + #[serde(default, alias = "title-bar-style")] + pub title_bar_style: TitleBarStyle, + /// The position of the window controls on macOS. + /// + /// Requires titleBarStyle: Overlay and decorations: true. + #[serde(default, alias = "traffic-light-position")] + pub traffic_light_position: Option, + /// If `true`, sets the window title to be hidden on macOS. + #[serde(default, alias = "hidden-title")] + pub hidden_title: bool, + /// Whether clicking an inactive window also clicks through to the webview on macOS. + #[serde(default, alias = "accept-first-mouse")] + pub accept_first_mouse: bool, + /// Defines the window [tabbing identifier] for macOS. + /// + /// Windows with matching tabbing identifiers will be grouped together. + /// If the tabbing identifier is not set, automatic tabbing will be disabled. + /// + /// [tabbing identifier]: + #[serde(default, alias = "tabbing-identifier")] + pub tabbing_identifier: Option, + /// Defines additional browser arguments on Windows. + /// + /// ## Warning + /// + /// Webview instances with different browser arguments must also have different [data directories](Self::data_directory). + /// + /// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection` + /// so if you set this, you also need to disable these components by yourself if you want. + #[serde(default, alias = "additional-browser-args")] + pub additional_browser_args: Option, + /// Whether or not the window has shadow. + /// + /// ## Platform-specific + /// + /// - **Windows:** + /// - `false` has no effect on decorated window, shadow are always ON. + /// - `true` will make undecorated window have a 1px white border, + /// and on Windows 11, it will have a rounded corners. + /// - **Linux:** Unsupported. + #[serde(default = "default_true")] + pub shadow: bool, + /// Window effects. + /// + /// Requires the window to be transparent. + /// + /// ## Platform-specific: + /// + /// - **Windows**: If using decorations or shadows, you may want to try this workaround + /// - **Linux**: Unsupported + #[serde(default, alias = "window-effects")] + pub window_effects: Option, + /// Whether or not the webview should be launched in incognito mode. + /// + /// ## Platform-specific: + /// + /// - **Android**: Unsupported. + #[serde(default)] + pub incognito: bool, + /// Sets the window associated with this label to be the parent of the window to be created. + /// + /// ## Platform-specific + /// + /// - **Windows**: This sets the passed parent as an owner window to the window to be created. + /// From [MSDN owned windows docs](https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows): + /// - An owned window is always above its owner in the z-order. + /// - The system automatically destroys an owned window when its owner is destroyed. + /// - An owned window is hidden when its owner is minimized. + /// - **Linux**: This makes the new window transient for parent, see + /// - **macOS**: This adds the window as a child of parent, see + pub parent: Option, + /// The proxy URL for the WebView for all network requests. + /// + /// Must be either a `http://` or a `socks5://` URL. + /// + /// ## Platform-specific + /// + /// - **macOS**: Requires the `macos-proxy` feature flag and only compiles for macOS 14+. + #[serde(alias = "proxy-url")] + pub proxy_url: Option, + /// Whether page zooming by hotkeys is enabled + /// + /// ## Platform-specific: + /// + /// - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting. + /// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`, + /// 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission + /// + /// - **Android / iOS**: Unsupported. + #[serde(default, alias = "zoom-hotkeys-enabled")] + pub zoom_hotkeys_enabled: bool, + /// Whether browser extensions can be installed for the webview process + /// + /// ## Platform-specific: + /// + /// - **Windows**: Enables the WebView2 environment's [`AreBrowserExtensionsEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2environmentoptions?view=webview2-winrt-1.0.2739.15#arebrowserextensionsenabled) + /// - **MacOS / Linux / iOS / Android** - Unsupported. + #[serde(default, alias = "browser-extensions-enabled")] + pub browser_extensions_enabled: bool, + + /// Sets whether the custom protocols should use `https://.localhost` instead of the default `http://.localhost` on Windows and Android. Defaults to `false`. + /// + /// ## Note + /// + /// Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `://localhost` protocols used on macOS and Linux. + /// + /// ## Warning + /// + /// Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data. + #[serde(default, alias = "use-https-scheme")] + pub use_https_scheme: bool, + /// Enable web inspector which is usually called browser devtools. Enabled by default. + /// + /// This API works in **debug** builds, but requires `devtools` feature flag to enable it in **release** builds. + /// + /// ## Platform-specific + /// + /// - macOS: This will call private functions on **macOS**. + /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android. + /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window. + pub devtools: Option, + + /// Set the window and webview background color. + /// + /// ## Platform-specific: + /// + /// - **Windows**: alpha channel is ignored for the window layer. + /// - **Windows**: On Windows 7, alpha channel is ignored for the webview layer. + /// - **Windows**: On Windows 8 and newer, if alpha channel is not `0`, it will be ignored for the webview layer. + #[serde(alias = "background-color")] + pub background_color: Option, + + /// Change the default background throttling behaviour. + /// + /// By default, browsers use a suspend policy that will throttle timers and even unload + /// the whole tab (view) to free resources after roughly 5 minutes when a view became + /// minimized or hidden. This will pause all tasks until the documents visibility state + /// changes back from hidden to visible by bringing the view back to the foreground. + /// + /// ## Platform-specific + /// + /// - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice. + /// - **iOS**: Supported since version 17.0+. + /// - **macOS**: Supported since version 14.0+. + /// + /// see + #[serde(default, alias = "background-throttling")] + pub background_throttling: Option, + /// Whether we should disable JavaScript code execution on the webview or not. + #[serde(default, alias = "javascript-disabled")] + pub javascript_disabled: bool, + /// on macOS and iOS there is a link preview on long pressing links, this is enabled by default. + /// see https://docs.rs/objc2-web-kit/latest/objc2_web_kit/struct.WKWebView.html#method.allowsLinkPreview + #[serde(default = "default_true", alias = "allow-link-preview")] + pub allow_link_preview: bool, + /// Allows disabling the input accessory view on iOS. + /// + /// The accessory view is the view that appears above the keyboard when a text input element is focused. + /// It usually displays a view with "Done", "Next" buttons. + #[serde( + default, + alias = "disable-input-accessory-view", + alias = "disable_input_accessory_view" + )] + pub disable_input_accessory_view: bool, + /// Set a custom path for the webview's data directory (localStorage, cache, etc.), + /// **relative to the app data directory (`appDataDir()`), followed by the window label**. + /// + /// To set absolute paths, use [`WebviewWindowBuilder::data_directory`](https://docs.rs/tauri/2/tauri/webview/struct.WebviewWindowBuilder.html#method.data_directory) + /// + /// #### Platform-specific: + /// + /// - **Windows**: WebViews with different values for settings like `additionalBrowserArgs`, `browserExtensionsEnabled` or `scrollBarStyle` must have different data directories. + /// - **macOS / iOS**: Unsupported, use `dataStoreIdentifier` instead. + /// - **Android**: Unsupported. + #[serde(default, alias = "data-directory")] + pub data_directory: Option, + /// Initialize the WebView with a custom data store identifier. This can be seen as a replacement for `dataDirectory` which is unavailable in WKWebView. + /// + /// See + /// + /// The array must contain 16 u8 numbers. + /// + /// #### Platform-specific: + /// + /// - **iOS**: Supported since version 17.0+. + /// - **macOS**: Supported since version 14.0+. + /// - **Windows / Linux / Android**: Unsupported. + #[serde(default, alias = "data-store-identifier")] + pub data_store_identifier: Option<[u8; 16]>, + + /// Specifies the native scrollbar style to use with the webview. + /// CSS styles that modify the scrollbar are applied on top of the native appearance configured here. + /// + /// Defaults to `default`, which is the browser default. + /// + /// ## Platform-specific + /// + /// - **Windows**: + /// - `fluentOverlay` requires WebView2 Runtime version 125.0.2535.41 or higher, + /// and does nothing on older versions. + /// - This option must be given the same value for all webviews that target the same data directory. + /// - **Linux / Android / iOS / macOS**: Unsupported. Only supports `Default` and performs no operation. + #[serde(default, alias = "scroll-bar-style")] + pub scroll_bar_style: ScrollBarStyle, + + /// Whether to limit navigations to App-Bound Domains. + /// + /// This is required to enable Service Workers in WKWebView, which are otherwise + /// unavailable. Defaults to `false`. + /// + /// When this is set to `true`, the webview can only navigate to the domains listed in the + /// `WKAppBoundDomains` array of `src-tauri/Info.ios.plist`. Add `localhost` and every + /// [registrable domain](https://developer.mozilla.org/en-US/docs/Glossary/Registrable_domain) + /// this webview loads to that array: + /// + /// ```xml + /// + /// + /// WKAppBoundDomains + /// + /// localhost + /// aregistrabledomain.example + /// + /// + /// + /// ``` + /// + /// `localhost` must be listed if any webview with this option enabled opens a local webpage, + /// makes any localhost call, or uses the isolation pattern, because Tauri serves the + /// application webpage, the IPC protocol and the isolation pattern iframe from the + /// `localhost` domain. + /// + /// Requests served through custom URI schemes are allowed as long as they use a registrable + /// domain listed in the `WKAppBoundDomains` array, including requests to the `localhost` + /// domain. + /// + /// An entire URI scheme can be listed by adding the protocol name followed by a colon, for + /// example `stream:` for a custom `stream` scheme (see the + /// [streaming example](https://github.com/tauri-apps/tauri/blob/dev/examples/streaming/main.rs)). + /// This is not covered by Apple's + /// [App-Bound Domains announcement](https://webkit.org/blog/10882/app-bound-domains/), + /// so it may not be accepted during App Store review. + /// + /// See and + /// + /// for the official documentation on App-Bound Domains. + /// + /// ## Platform-specific + /// + /// - **iOS**: Supported since version 14.0+. + /// - **Linux / Windows / Android / macOS:** Unsupported. + #[serde(default, alias = "limit-navigations-to-app-bound-domains")] + pub limit_navigations_to_app_bound_domains: bool, + /// The name of the Android activity to create for this window. + #[serde(default, alias = "activity-name")] + pub activity_name: Option, + /// The name of the Android activity that is creating this webview window. + /// + /// This is important to determine which stack the activity will belong to. + #[serde(default, alias = "created-by-activity-name")] + pub created_by_activity_name: Option, + + /// Sets the identifier of the scene that is requesting the new scene, + /// establishing a relationship between the two scenes. + /// + /// By default the system uses the foreground scene. + #[serde(default, alias = "requested-by-scene-identifier")] + pub requested_by_scene_identifier: Option, + /// Controls the WebView's browser-level general autofill behavior. + /// + /// **This option does not disable password or credit card autofill.** + /// + /// When set to `false`, the WebView will not automatically populate + /// general form fields using previously stored data such as addresses + /// or contact information. + /// + /// If not specified, this is `true` by default. + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported. WebView2's autofill feature (called + /// "Suggestions") may not honor `autocomplete="off"` on input + /// elements in some cases. + /// - **Linux / Android / iOS / macOS**: Unsupported and performs no + /// operation. + #[serde(default = "default_true", alias = "general-autofill-enabled")] + pub general_autofill_enabled: bool, +} + +impl Default for WindowConfig { + fn default() -> Self { + Self { + label: default_window_label(), + url: WebviewUrl::default(), + create: true, + user_agent: None, + drag_drop_enabled: true, + center: false, + x: None, + y: None, + width: default_width(), + height: default_height(), + min_width: None, + min_height: None, + max_width: None, + max_height: None, + prevent_overflow: None, + resizable: true, + maximizable: true, + minimizable: true, + closable: true, + title: default_title(), + fullscreen: false, + focus: true, + focusable: true, + transparent: false, + maximized: false, + visible: true, + decorations: true, + always_on_bottom: false, + always_on_top: false, + visible_on_all_workspaces: false, + content_protected: false, + skip_taskbar: false, + window_classname: None, + no_redirection_bitmap: false, + theme: None, + title_bar_style: Default::default(), + traffic_light_position: None, + hidden_title: false, + accept_first_mouse: false, + tabbing_identifier: None, + additional_browser_args: None, + shadow: true, + window_effects: None, + incognito: false, + parent: None, + proxy_url: None, + zoom_hotkeys_enabled: false, + browser_extensions_enabled: false, + use_https_scheme: false, + devtools: None, + background_color: None, + background_throttling: None, + javascript_disabled: false, + allow_link_preview: true, + disable_input_accessory_view: false, + data_directory: None, + data_store_identifier: None, + scroll_bar_style: ScrollBarStyle::Default, + limit_navigations_to_app_bound_domains: false, + activity_name: None, + created_by_activity_name: None, + requested_by_scene_identifier: None, + general_autofill_enabled: true, + } + } +} + +fn default_window_label() -> String { + "main".to_string() +} + +fn default_width() -> f64 { + 800. +} + +fn default_height() -> f64 { + 600. +} + +fn default_title() -> String { + "Tauri App".to_string() +} + +/// A Content-Security-Policy directive source list. +/// See . +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", untagged)] +pub enum CspDirectiveSources { + /// An inline list of CSP sources. Same as [`Self::List`], but concatenated with a space separator. + Inline(String), + /// A list of CSP sources. The collection will be concatenated with a space separator for the CSP string. + List(Vec), +} + +impl Default for CspDirectiveSources { + fn default() -> Self { + Self::List(Vec::new()) + } +} + +impl From for Vec { + fn from(sources: CspDirectiveSources) -> Self { + match sources { + CspDirectiveSources::Inline(source) => source.split(' ').map(|s| s.to_string()).collect(), + CspDirectiveSources::List(l) => l, + } + } +} + +impl CspDirectiveSources { + /// Whether the given source is configured on this directive or not. + pub fn contains(&self, source: &str) -> bool { + match self { + Self::Inline(s) => s.contains(&format!("{source} ")) || s.contains(&format!(" {source}")), + Self::List(l) => l.contains(&source.into()), + } + } + + /// Appends the given source to this directive. + pub fn push>(&mut self, source: S) { + match self { + Self::Inline(s) => { + s.push(' '); + s.push_str(source.as_ref()); + } + Self::List(l) => { + l.push(source.as_ref().to_string()); + } + } + } + + /// Extends this CSP directive source list with the given array of sources. + pub fn extend(&mut self, sources: Vec) { + for s in sources { + self.push(s); + } + } +} + +/// A Content-Security-Policy definition. +/// See . +#[derive(Debug, PartialEq, Eq, Clone, Deserialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", untagged)] +pub enum Csp { + /// The entire CSP policy in a single text string. + Policy(String), + /// An object mapping a directive with its sources values as a list of strings. + DirectiveMap(HashMap), +} + +impl Serialize for Csp { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + match self { + Self::Policy(policy) => serializer.serialize_str(policy), + Self::DirectiveMap(map) => { + // Serialize through `BTreeMap` so the output is deterministic + // see: https://github.com/tauri-apps/tauri/issues/14978 + // TODO: Remove this in v3, use a BTreeMap instead of a HashMap + let btree_map: BTreeMap<_, _> = map.iter().collect(); + btree_map.serialize(serializer) + } + } + } +} + +impl From> for Csp { + fn from(map: HashMap) -> Self { + Self::DirectiveMap(map) + } +} + +impl From for HashMap { + fn from(csp: Csp) -> Self { + match csp { + Csp::Policy(policy) => { + let mut map = HashMap::new(); + for directive in policy.split(';') { + let mut tokens = directive.trim().split(' '); + if let Some(directive) = tokens.next() { + let sources = tokens.map(|s| s.to_string()).collect::>(); + map.insert(directive.to_string(), CspDirectiveSources::List(sources)); + } + } + map + } + Csp::DirectiveMap(m) => m, + } + } +} + +impl Display for Csp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Policy(s) => write!(f, "{s}"), + Self::DirectiveMap(m) => { + let len = m.len(); + let mut i = 0; + for (directive, sources) in m { + let sources: Vec = sources.clone().into(); + write!(f, "{} {}", directive, sources.join(" "))?; + i += 1; + if i != len { + write!(f, "; ")?; + } + } + Ok(()) + } + } + } +} + +/// The possible values for the `dangerous_disable_asset_csp_modification` config option. +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(untagged)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +pub enum DisabledCspModificationKind { + /// If `true`, disables all CSP modification. + /// `false` is the default value and it configures Tauri to control the CSP. + Flag(bool), + /// Disables the given list of CSP directives modifications. + List(Vec), +} + +impl DisabledCspModificationKind { + /// Determines whether the given CSP directive can be modified or not. + pub fn can_modify(&self, directive: &str) -> bool { + match self { + Self::Flag(f) => !f, + Self::List(l) => !l.contains(&directive.into()), + } + } +} + +impl Default for DisabledCspModificationKind { + fn default() -> Self { + Self::Flag(false) + } +} + +/// Protocol scope definition. +/// It is a list of glob patterns that restrict the API access from the webview. +/// +/// Each pattern can start with a variable that resolves to a system base directory. +/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, +/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, +/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$TEMP`, +/// `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`. +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(untagged)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +pub enum FsScope { + /// A list of paths that are allowed by this scope. + AllowedPaths(Vec), + /// A complete scope configuration. + #[serde(rename_all = "camelCase")] + Scope { + /// A list of paths that are allowed by this scope. + #[serde(default)] + allow: Vec, + /// A list of paths that are not allowed by this scope. + /// This gets precedence over the [`Self::Scope::allow`] list. + #[serde(default)] + deny: Vec, + /// Whether or not paths that contain components that start with a `.` + /// will require that `.` appears literally in the pattern; `*`, `?`, `**`, + /// or `[...]` will not match. This is useful because such files are + /// conventionally considered hidden on Unix systems and it might be + /// desirable to skip them when listing files. + /// + /// Defaults to `true` on Unix systems and `false` on Windows + // dotfiles are not supposed to be exposed by default on unix + #[serde(alias = "require-literal-leading-dot")] + require_literal_leading_dot: Option, + }, +} + +impl Default for FsScope { + fn default() -> Self { + Self::AllowedPaths(Vec::new()) + } +} + +impl FsScope { + /// The list of allowed paths. + pub fn allowed_paths(&self) -> &Vec { + match self { + Self::AllowedPaths(p) => p, + Self::Scope { allow, .. } => allow, + } + } + + /// The list of forbidden paths. + pub fn forbidden_paths(&self) -> Option<&Vec> { + match self { + Self::AllowedPaths(_) => None, + Self::Scope { deny, .. } => Some(deny), + } + } +} + +/// Config for the asset custom protocol. +/// +/// See more: +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct AssetProtocolConfig { + /// The access scope for the asset protocol. + #[serde(default)] + pub scope: FsScope, + /// Enables the asset protocol. + #[serde(default)] + pub enable: bool, +} + +/// definition of a header source +/// +/// The header value to a header name +#[derive(Debug, PartialEq, Eq, Clone, Deserialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", untagged)] +pub enum HeaderSource { + /// string version of the header Value + Inline(String), + /// list version of the header value. Item are joined by "," for the real header value + List(Vec), + /// (Rust struct | Json | JavaScript Object) equivalent of the header value. Items are composed from: key + space + value. Item are then joined by ";" for the real header value + Map(HashMap), +} + +impl Serialize for HeaderSource { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + match self { + Self::Inline(s) => serializer.serialize_str(s), + Self::List(l) => l.serialize(serializer), + Self::Map(m) => { + // Serialize through `BTreeMap` so the output is deterministic + // see: https://github.com/tauri-apps/tauri/issues/14978 + // TODO: Remove this in v3, use a BTreeMap instead of a HashMap + let btree_map: BTreeMap<_, _> = m.iter().collect(); + btree_map.serialize(serializer) + } + } + } +} + +impl Display for HeaderSource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Inline(s) => write!(f, "{s}"), + Self::List(l) => write!(f, "{}", l.join(", ")), + Self::Map(m) => { + // Format through `BTreeMap` so the resulting header value is deterministic + // see: https://github.com/tauri-apps/tauri/issues/14978 + // TODO: Remove this in v3, use a BTreeMap instead of a HashMap + let map: BTreeMap<_, _> = m.iter().collect(); + let len = map.len(); + for (i, (key, value)) in map.into_iter().enumerate() { + write!(f, "{key} {value}")?; + if i + 1 != len { + write!(f, "; ")?; + } + } + Ok(()) + } + } + } +} + +/// A trait which implements on the [`Builder`] of the http create +/// +/// Must add headers defined in the tauri configuration file to http responses +pub trait HeaderAddition { + /// adds all headers defined on the config file, given the current HeaderConfig + fn add_configured_headers(self, headers: Option<&HeaderConfig>) -> http::response::Builder; +} + +impl HeaderAddition for Builder { + /// Add the headers defined in the tauri configuration file to http responses + /// + /// this is a utility function, which is used in the same way as the `.header(..)` of the rust http library + fn add_configured_headers(mut self, headers: Option<&HeaderConfig>) -> http::response::Builder { + if let Some(headers) = headers { + // Add the header Access-Control-Allow-Credentials, if we find a value for it + if let Some(value) = &headers.access_control_allow_credentials { + self = self.header("Access-Control-Allow-Credentials", value.to_string()); + }; + + // Add the header Access-Control-Allow-Headers, if we find a value for it + if let Some(value) = &headers.access_control_allow_headers { + self = self.header("Access-Control-Allow-Headers", value.to_string()); + }; + + // Add the header Access-Control-Allow-Methods, if we find a value for it + if let Some(value) = &headers.access_control_allow_methods { + self = self.header("Access-Control-Allow-Methods", value.to_string()); + }; + + // Add the header Access-Control-Expose-Headers, if we find a value for it + if let Some(value) = &headers.access_control_expose_headers { + self = self.header("Access-Control-Expose-Headers", value.to_string()); + }; + + // Add the header Access-Control-Max-Age, if we find a value for it + if let Some(value) = &headers.access_control_max_age { + self = self.header("Access-Control-Max-Age", value.to_string()); + }; + + // Add the header Cross-Origin-Embedder-Policy, if we find a value for it + if let Some(value) = &headers.cross_origin_embedder_policy { + self = self.header("Cross-Origin-Embedder-Policy", value.to_string()); + }; + + // Add the header Cross-Origin-Opener-Policy, if we find a value for it + if let Some(value) = &headers.cross_origin_opener_policy { + self = self.header("Cross-Origin-Opener-Policy", value.to_string()); + }; + + // Add the header Cross-Origin-Resource-Policy, if we find a value for it + if let Some(value) = &headers.cross_origin_resource_policy { + self = self.header("Cross-Origin-Resource-Policy", value.to_string()); + }; + + // Add the header Permissions-Policy, if we find a value for it + if let Some(value) = &headers.permissions_policy { + self = self.header("Permissions-Policy", value.to_string()); + }; + + if let Some(value) = &headers.service_worker_allowed { + self = self.header("Service-Worker-Allowed", value.to_string()); + } + + // Add the header Timing-Allow-Origin, if we find a value for it + if let Some(value) = &headers.timing_allow_origin { + self = self.header("Timing-Allow-Origin", value.to_string()); + }; + + // Add the header X-Content-Type-Options, if we find a value for it + if let Some(value) = &headers.x_content_type_options { + self = self.header("X-Content-Type-Options", value.to_string()); + }; + + // Add the header Tauri-Custom-Header, if we find a value for it + if let Some(value) = &headers.tauri_custom_header { + // Keep in mind to correctly set the Access-Control-Expose-Headers + self = self.header("Tauri-Custom-Header", value.to_string()); + }; + } + self + } +} + +/// A struct, where the keys are some specific http header names. +/// +/// If the values to those keys are defined, then they will be send as part of a response message. +/// This does not include error messages and ipc messages +/// +/// ## Example configuration +/// ```javascript +/// { +/// //.. +/// app:{ +/// //.. +/// security: { +/// headers: { +/// "Cross-Origin-Opener-Policy": "same-origin", +/// "Cross-Origin-Embedder-Policy": "require-corp", +/// "Timing-Allow-Origin": [ +/// "https://developer.mozilla.org", +/// "https://example.com", +/// ], +/// "Access-Control-Expose-Headers": "Tauri-Custom-Header", +/// "Tauri-Custom-Header": { +/// "key1": "'value1' 'value2'", +/// "key2": "'value3'" +/// } +/// }, +/// csp: "default-src 'self'; connect-src ipc: http://ipc.localhost", +/// } +/// //.. +/// } +/// //.. +/// } +/// ``` +/// In this example `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` are set to allow for the use of [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer). +/// The result is, that those headers are then set on every response sent via the `get_response` function in crates/tauri/src/protocol/tauri.rs. +/// The Content-Security-Policy header is defined separately, because it is also handled separately. +/// +/// For the helloworld example, this config translates into those response headers: +/// ```http +/// access-control-allow-origin: http://tauri.localhost +/// access-control-expose-headers: Tauri-Custom-Header +/// content-security-policy: default-src 'self'; connect-src ipc: http://ipc.localhost; script-src 'self' 'sha256-Wjjrs6qinmnr+tOry8x8PPwI77eGpUFR3EEGZktjJNs=' +/// content-type: text/html +/// cross-origin-embedder-policy: require-corp +/// cross-origin-opener-policy: same-origin +/// tauri-custom-header: key1 'value1' 'value2'; key2 'value3' +/// timing-allow-origin: https://developer.mozilla.org, https://example.com +/// ``` +/// Since the resulting header values are always 'string-like'. So depending on the what data type the HeaderSource is, they need to be converted. +/// - `String`(JS/Rust): stay the same for the resulting header value +/// - `Array`(JS)/`Vec\`(Rust): Item are joined by ", " for the resulting header value +/// - `Object`(JS)/ `Hashmap\`(Rust): Items are composed from: key + space + value. Item are then joined by "; " for the resulting header value +#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(deny_unknown_fields)] +pub struct HeaderConfig { + /// The Access-Control-Allow-Credentials response header tells browsers whether the + /// server allows cross-origin HTTP requests to include credentials. + /// + /// See + #[serde(rename = "Access-Control-Allow-Credentials")] + pub access_control_allow_credentials: Option, + /// The Access-Control-Allow-Headers response header is used in response + /// to a preflight request which includes the Access-Control-Request-Headers + /// to indicate which HTTP headers can be used during the actual request. + /// + /// This header is required if the request has an Access-Control-Request-Headers header. + /// + /// See + #[serde(rename = "Access-Control-Allow-Headers")] + pub access_control_allow_headers: Option, + /// The Access-Control-Allow-Methods response header specifies one or more methods + /// allowed when accessing a resource in response to a preflight request. + /// + /// See + #[serde(rename = "Access-Control-Allow-Methods")] + pub access_control_allow_methods: Option, + /// The Access-Control-Expose-Headers response header allows a server to indicate + /// which response headers should be made available to scripts running in the browser, + /// in response to a cross-origin request. + /// + /// See + #[serde(rename = "Access-Control-Expose-Headers")] + pub access_control_expose_headers: Option, + /// The Access-Control-Max-Age response header indicates how long the results of a + /// preflight request (that is the information contained in the + /// Access-Control-Allow-Methods and Access-Control-Allow-Headers headers) can + /// be cached. + /// + /// See + #[serde(rename = "Access-Control-Max-Age")] + pub access_control_max_age: Option, + /// The HTTP Cross-Origin-Embedder-Policy (COEP) response header configures embedding + /// cross-origin resources into the document. + /// + /// See + #[serde(rename = "Cross-Origin-Embedder-Policy")] + pub cross_origin_embedder_policy: Option, + /// The HTTP Cross-Origin-Opener-Policy (COOP) response header allows you to ensure a + /// top-level document does not share a browsing context group with cross-origin documents. + /// COOP will process-isolate your document and potential attackers can't access your global + /// object if they were to open it in a popup, preventing a set of cross-origin attacks dubbed XS-Leaks. + /// + /// See + #[serde(rename = "Cross-Origin-Opener-Policy")] + pub cross_origin_opener_policy: Option, + /// The HTTP Cross-Origin-Resource-Policy response header conveys a desire that the + /// browser blocks no-cors cross-origin/cross-site requests to the given resource. + /// + /// See + #[serde(rename = "Cross-Origin-Resource-Policy")] + pub cross_origin_resource_policy: Option, + /// The HTTP Permissions-Policy header provides a mechanism to allow and deny the + /// use of browser features in a document or within any \ elements in the document. + /// + /// See + #[serde(rename = "Permissions-Policy")] + pub permissions_policy: Option, + /// The HTTP Service-Worker-Allowed response header is used to broaden the path restriction for a + /// service worker's default scope. + /// + /// By default, the scope for a service worker registration is the directory where the service + /// worker script is located. For example, if the script `sw.js` is located in `/js/sw.js`, + /// it can only control URLs under `/js/` by default. Servers can use the `Service-Worker-Allowed` + /// header to allow a service worker to control URLs outside of its own directory. + /// + /// See + #[serde(rename = "Service-Worker-Allowed")] + pub service_worker_allowed: Option, + /// The Timing-Allow-Origin response header specifies origins that are allowed to see values + /// of attributes retrieved via features of the Resource Timing API, which would otherwise be + /// reported as zero due to cross-origin restrictions. + /// + /// See + #[serde(rename = "Timing-Allow-Origin")] + pub timing_allow_origin: Option, + /// The X-Content-Type-Options response HTTP header is a marker used by the server to indicate + /// that the MIME types advertised in the Content-Type headers should be followed and not be + /// changed. The header allows you to avoid MIME type sniffing by saying that the MIME types + /// are deliberately configured. + /// + /// See + #[serde(rename = "X-Content-Type-Options")] + pub x_content_type_options: Option, + /// A custom header field Tauri-Custom-Header, don't use it. + /// Remember to set Access-Control-Expose-Headers accordingly + /// + /// **NOT INTENDED FOR PRODUCTION USE** + #[serde(rename = "Tauri-Custom-Header")] + pub tauri_custom_header: Option, +} + +impl HeaderConfig { + /// creates a new header config + pub fn new() -> Self { + HeaderConfig { + access_control_allow_credentials: None, + access_control_allow_methods: None, + access_control_allow_headers: None, + access_control_expose_headers: None, + access_control_max_age: None, + cross_origin_embedder_policy: None, + cross_origin_opener_policy: None, + cross_origin_resource_policy: None, + permissions_policy: None, + service_worker_allowed: None, + timing_allow_origin: None, + x_content_type_options: None, + tauri_custom_header: None, + } + } +} + +/// Security configuration. +/// +/// See more: +#[skip_serializing_none] +#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct SecurityConfig { + /// The Content Security Policy that will be injected on all HTML files on the built application. + /// If `devCsp` is not specified, this value is also injected on dev. + /// + /// This is a really important part of the configuration since it helps you ensure your WebView is secured. + /// See . + pub csp: Option, + /// The Content Security Policy that will be injected on all HTML files on development. + /// + /// This is a really important part of the configuration since it helps you ensure your WebView is secured. + /// See . + #[serde(alias = "dev-csp")] + pub dev_csp: Option, + /// Whether `Object.freeze(Object.prototype)` is run as an initialization script on every webview. + /// + /// This hardens the frontend against prototype pollution: once the prototype is frozen, + /// a script cannot add or replace properties on `Object.prototype` and thus cannot tamper + /// with objects it does not own, including the ones used by the Tauri API. + /// + /// The script runs before any of your frontend code, on every webview, regardless of whether + /// the content is served by the custom protocol or by a development server. + /// + /// Defaults to `false`. Note that frontend libraries that extend built-in prototypes + /// (polyfills, some older frameworks) stop working when this is enabled, so test your + /// application with it on before shipping. + #[serde(default, alias = "freeze-prototype")] + pub freeze_prototype: bool, + /// Disables the Tauri-injected CSP sources. + /// + /// At compile time, Tauri parses all the frontend assets and changes the Content-Security-Policy + /// to only allow loading of your own scripts and styles by injecting nonce and hash sources. + /// This stricts your CSP, which may introduce issues when using along with other flexing sources. + /// + /// This configuration option allows both a boolean and a list of strings as value. + /// A boolean instructs Tauri to disable the injection for all CSP injections, + /// and a list of strings indicates the CSP directives that Tauri cannot inject. + /// + /// **WARNING:** Only disable this if you know what you are doing and have properly configured the CSP. + /// Your application might be vulnerable to XSS attacks without this Tauri protection. + #[serde(default, alias = "dangerous-disable-asset-csp-modification")] + pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind, + /// Custom protocol config. + #[serde(default, alias = "asset-protocol")] + pub asset_protocol: AssetProtocolConfig, + /// The application pattern, which defines how the frontend communicates with the Rust core. + /// + /// - `brownfield` (default): the frontend talks to the core directly. Use it unless you need + /// the extra isolation layer. + /// - `isolation`: every IPC message is routed through a secure JavaScript application you own, + /// hosted in a sandboxed `