diff --git a/.changes/add-command.md b/.changes/add-command.md index 9d8713834..7c8638185 100644 --- a/.changes/add-command.md +++ b/.changes/add-command.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": 'patch:feat' +"@tauri-apps/cli": 'patch:feat' --- Added `tauri plugin add` command to add a plugin to the Tauri project. diff --git a/.changes/add-mobile-to-plugin.md b/.changes/add-mobile-to-plugin.md index 5ee7a0f8f..fcb3d854d 100644 --- a/.changes/add-mobile-to-plugin.md +++ b/.changes/add-mobile-to-plugin.md @@ -1,6 +1,6 @@ --- -"cli.rs": minor -"cli.js": minor +"tauri-cli": minor +"@tauri-apps/cli": minor --- Added `plugin android add` and `plugin ios add` commands to add mobile plugin functionality to existing projects. diff --git a/.changes/add-tauri-get-version.md b/.changes/add-tauri-get-version.md new file mode 100644 index 000000000..03e8cd04d --- /dev/null +++ b/.changes/add-tauri-get-version.md @@ -0,0 +1,5 @@ +--- +'tauri': minor:feat +--- + +Added `tauri::VERSION` const to get Tauri's version from Rust. diff --git a/.changes/add-webview-version.md b/.changes/add-webview-version.md new file mode 100644 index 000000000..0ec104eaf --- /dev/null +++ b/.changes/add-webview-version.md @@ -0,0 +1,5 @@ +--- +'tauri': minor:feat +--- + +Added `tauri::webview_version` , to get webview version. diff --git a/.changes/android-buildsrc-gitignore.md b/.changes/android-buildsrc-gitignore.md index f4d1f07bb..93b811aa2 100644 --- a/.changes/android-buildsrc-gitignore.md +++ b/.changes/android-buildsrc-gitignore.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Do not gitignore the Android project's `buildSrc` folder by default since we removed absolute paths from it. diff --git a/.changes/api-js-os-locale.md b/.changes/api-js-os-locale.md new file mode 100644 index 000000000..a75d0579e --- /dev/null +++ b/.changes/api-js-os-locale.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/api': 'patch' +--- + +Add `locale` function in the `os` module to get the system locale. diff --git a/.changes/api-rs-os-locale.md b/.changes/api-rs-os-locale.md new file mode 100644 index 000000000..0677de797 --- /dev/null +++ b/.changes/api-rs-os-locale.md @@ -0,0 +1,5 @@ +--- +"tauri": "patch" +--- + +Add `tauri::api::os::locale` function to get the system locale. diff --git a/.changes/appimage-follow-symlinks.md b/.changes/appimage-follow-symlinks.md new file mode 100644 index 000000000..891390347 --- /dev/null +++ b/.changes/appimage-follow-symlinks.md @@ -0,0 +1,6 @@ +--- +'tauri-bundler': patch:bug +--- + +- Updated the AppImage bundler to follow symlinks for `/usr/lib*`. +- Fixes AppImage bundling for Void Linux, which was failing to bundle webkit2gtk because the `/usr/lib64` is a symlink to `/usr/lib`. diff --git a/.changes/cli-android-build.md b/.changes/cli-android-build.md index b1eb65a83..535a98e6c 100644 --- a/.changes/cli-android-build.md +++ b/.changes/cli-android-build.md @@ -1,6 +1,6 @@ --- -"cli.rs": minor -"cli.js": minor +"tauri-cli": minor +"@tauri-apps/cli": minor --- Added `android build` command. diff --git a/.changes/cli-android-dev-release.md b/.changes/cli-android-dev-release.md index 1c540f81f..e45ca93d4 100644 --- a/.changes/cli-android-dev-release.md +++ b/.changes/cli-android-dev-release.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -'cli.js': 'patch' +'tauri-cli': 'patch' +'@tauri-apps/cli': 'patch' --- 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 diff --git a/.changes/cli-android-specified-targets-only.md b/.changes/cli-android-specified-targets-only.md index 6ca2c2c90..5d83b54fa 100644 --- a/.changes/cli-android-specified-targets-only.md +++ b/.changes/cli-android-specified-targets-only.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -'cli.js': 'patch' +'tauri-cli': 'patch' +'@tauri-apps/cli': 'patch' --- Build only specified rust targets for `tauri android build` instead of all. diff --git a/.changes/cli-android-split-per-abit-target.md b/.changes/cli-android-split-per-abit-target.md index 61439fdcc..00330291d 100644 --- a/.changes/cli-android-split-per-abit-target.md +++ b/.changes/cli-android-split-per-abit-target.md @@ -1,5 +1,5 @@ --- -'cli.rs': 'patch' +'tauri-cli': 'patch:bug' --- Fix `--split-per-abi` not building any targets unless specified by `--target` flag. diff --git a/.changes/cli-autoreload-mime-type.md b/.changes/cli-autoreload-mime-type.md new file mode 100644 index 000000000..b341d6e73 --- /dev/null +++ b/.changes/cli-autoreload-mime-type.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': patch:bug +'@tauri-apps/cli': patch:bug +--- + +Fix built-in devserver adding hot-reload code to non-html files. diff --git a/.changes/cli-built-in-dev-server-mobile.md b/.changes/cli-built-in-dev-server-mobile.md index 7077e95b2..41aab33a6 100644 --- a/.changes/cli-built-in-dev-server-mobile.md +++ b/.changes/cli-built-in-dev-server-mobile.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -'cli.js': 'patch' +'tauri-cli': 'patch' +'@tauri-apps/cli': 'patch' --- Use local ip address for built-in dev server on mobile. diff --git a/.changes/cli-ios-build.md b/.changes/cli-ios-build.md index 5a611ecb7..5fcf50409 100644 --- a/.changes/cli-ios-build.md +++ b/.changes/cli-ios-build.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Added `ios build` command. diff --git a/.changes/cli-key-properties.md b/.changes/cli-key-properties.md index 09be0ab33..8a792e3d8 100644 --- a/.changes/cli-key-properties.md +++ b/.changes/cli-key-properties.md @@ -1,6 +1,6 @@ ---- -"cli.rs": patch -"cli.js": patch ---- - -Add `key.properties` file to android's `.gitignore`. \ No newline at end of file +--- +"tauri-cli": patch +"@tauri-apps/cli": patch +--- + +Add `key.properties` file to android's `.gitignore`. diff --git a/.changes/cli-libname-dashes.md b/.changes/cli-libname-dashes.md index fbd5e7799..de87ca32d 100644 --- a/.changes/cli-libname-dashes.md +++ b/.changes/cli-libname-dashes.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -'cli.js': 'patch' +'tauri-cli': 'patch' +'@tauri-apps/cli': 'patch' --- On mobile, fix regression introduced in `tauri-cli` version `2.0.0-alpha.3` where library not found error was thrown. diff --git a/.changes/cli-library-compilation.md b/.changes/cli-library-compilation.md index 8b7fbc80d..c26ac0db1 100644 --- a/.changes/cli-library-compilation.md +++ b/.changes/cli-library-compilation.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": 'patch:enhance' +"@tauri-apps/cli": 'patch:enhance' --- Don't build library files when building desktop targets. diff --git a/.changes/cli-mobile-auto-ip.md b/.changes/cli-mobile-auto-ip.md index 022f5f100..e4aa1a05f 100644 --- a/.changes/cli-mobile-auto-ip.md +++ b/.changes/cli-mobile-auto-ip.md @@ -1,5 +1,5 @@ --- -'cli.rs': 'patch' +'tauri-cli': 'patch' --- Auto select an external IP for mobile development and fallback to prompting the user. Use `--force-ip-prompt` to force prompting. diff --git a/.changes/cli-mobile-cwd-config.md b/.changes/cli-mobile-cwd-config.md index b216ea4e3..dc6ab98e5 100644 --- a/.changes/cli-mobile-cwd-config.md +++ b/.changes/cli-mobile-cwd-config.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -'cli.js': 'patch' +'tauri-cli': 'patch:bug' +'@tauri-apps/cli': 'patch:bug' --- Set current directory to tauri directory before reading config file. diff --git a/.changes/cli-mobile-dev.md b/.changes/cli-mobile-dev.md index bd0df7aa9..6e52ff08f 100644 --- a/.changes/cli-mobile-dev.md +++ b/.changes/cli-mobile-dev.md @@ -1,6 +1,6 @@ --- -"cli.rs": minor -"cli.js": minor +"tauri-cli": minor +"@tauri-apps/cli": minor --- Added `android dev` and `ios dev` commands. diff --git a/.changes/cli-mobile-plugin.md b/.changes/cli-mobile-plugin.md index 3529c1238..481b166bd 100644 --- a/.changes/cli-mobile-plugin.md +++ b/.changes/cli-mobile-plugin.md @@ -1,6 +1,6 @@ --- -"cli.rs": minor -"cli.js": minor +"tauri-cli": minor +"@tauri-apps/cli": minor --- Add commands to add native Android and iOS functionality to plugins. diff --git a/.changes/cli-nodejs-detection.md b/.changes/cli-nodejs-detection.md index 0bc88902e..c61f85c32 100644 --- a/.changes/cli-nodejs-detection.md +++ b/.changes/cli-nodejs-detection.md @@ -1,5 +1,5 @@ --- -'cli.rs': 'patch' +'tauri-cli': 'patch' --- In mobile commands, correctly detect when nodejs binary has the version in its name, for example `node-18` diff --git a/.changes/cli-pnpm.md b/.changes/cli-pnpm.md index 6aba346f1..1e9609120 100644 --- a/.changes/cli-pnpm.md +++ b/.changes/cli-pnpm.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -'cli.js': 'patch' +'tauri-cli': 'patch' +'@tauri-apps/cli': 'patch' --- Fix android project build crashing when using `pnpm` caused by extra `--`. diff --git a/.changes/cli-profile.md b/.changes/cli-profile.md new file mode 100644 index 000000000..a9f5f48aa --- /dev/null +++ b/.changes/cli-profile.md @@ -0,0 +1,5 @@ +--- +'tauri-cli': patch:bug +--- + +Fix building with a custom cargo profile diff --git a/.changes/cli-refactor-ipc-mobile.md b/.changes/cli-refactor-ipc-mobile.md index dd3ede2c8..ac2005096 100644 --- a/.changes/cli-refactor-ipc-mobile.md +++ b/.changes/cli-refactor-ipc-mobile.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Use temp file instead of environment variable to pass CLI IPC websocket address to the IDE. diff --git a/.changes/cli-skip-targets-install.md b/.changes/cli-skip-targets-install.md index 0e84a5ba0..e5825cb0c 100644 --- a/.changes/cli-skip-targets-install.md +++ b/.changes/cli-skip-targets-install.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -'cli.js': 'patch' +'tauri-cli': 'patch:enhance' +'@tauri-apps/cli': 'patch:enhance' --- Skip Rust target installation if they are already installed. diff --git a/.changes/cli-wry-0-28.md b/.changes/cli-wry-0-28.md index 489763bbc..798f5aa79 100644 --- a/.changes/cli-wry-0-28.md +++ b/.changes/cli-wry-0-28.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -'cli.js': 'patch' +'tauri-cli': 'patch' +'@tauri-apps/cli': 'patch' --- Update mobile template to `wry@0.28` diff --git a/.changes/clijs-node-version-20.md b/.changes/clijs-node-version-20.md new file mode 100644 index 000000000..f47ab315d --- /dev/null +++ b/.changes/clijs-node-version-20.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/cli': patch:bug +--- + +Fix nodejs binary regex when `0` is in the version name, for example `node-20` diff --git a/.changes/config-require-literal_leading_dot.md b/.changes/config-require-literal_leading_dot.md new file mode 100644 index 000000000..68a22cc73 --- /dev/null +++ b/.changes/config-require-literal_leading_dot.md @@ -0,0 +1,5 @@ +--- +'tauri-utils': 'minor' +--- + +Add option to configure `require_literal_leading_dot` on `fs` and `asset` protcol scopes. diff --git a/.changes/config-scope-url.md b/.changes/config-scope-url.md new file mode 100644 index 000000000..d9ca875ef --- /dev/null +++ b/.changes/config-scope-url.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:bug' +--- + +Fix parsing `allowlist > http > scope` urls that added a trailing slash which broke matching the incoming requests url. diff --git a/.changes/config.json b/.changes/config.json index 47dddc32b..33ce3a189 100644 --- a/.changes/config.json +++ b/.changes/config.json @@ -1,6 +1,15 @@ { "gitSiteUrl": "https://www.github.com/tauri-apps/tauri/", "timeout": 3600000, + "changeTags": { + "feat": "New Features", + "enhance": "Enhancements", + "bug": "Bug Fixes", + "pref": "Performance Improvements", + "changes": "What's Changed", + "deps": "Dependencies" + }, + "defaultChangeTag": "changes", "pkgManagers": { "rust": { "version": true, @@ -121,13 +130,13 @@ } }, "packages": { - "api": { + "@tauri-apps/api": { "path": "./tooling/api", "manager": "javascript", "assets": [ { "path": "./tooling/api/dist/tauri-apps-api-${ pkgFile.version }.tgz", - "name": "api-${ pkgFile.version }.tgz" + "name": "tauri-apps-api-${ pkgFile.version }.tgz" } ], "prepublish": [ @@ -230,11 +239,11 @@ ], "postversion": "node ../../.scripts/covector/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }" }, - "cli.js": { + "@tauri-apps/cli": { "path": "./tooling/cli/node", "manager": "javascript", "getPublishedVersion": "node ../../../.scripts/covector/package-latest-version.js npm ${ pkgFile.pkg.name } ${ pkgFile.pkg.version }", - "dependencies": ["cli.rs"], + "dependencies": ["tauri-cli"], "postversion": [ "node ../../../.scripts/covector/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }", "cargo build --manifest-path ../../../core/tauri-config-schema/Cargo.toml" @@ -243,7 +252,7 @@ "publish": [], "postpublish": [] }, - "cli.rs": { + "tauri-cli": { "path": "./tooling/cli", "manager": "rust", "dependencies": ["tauri-bundler", "tauri-utils"], diff --git a/.changes/core-asset-protocol-streaming-crash.md b/.changes/core-asset-protocol-streaming-crash.md new file mode 100644 index 000000000..44bcd09a3 --- /dev/null +++ b/.changes/core-asset-protocol-streaming-crash.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:enhance' +--- + +Enhance the `asset` protocol to support streaming of large files. diff --git a/.changes/core-ipc-failed-navigation.md b/.changes/core-ipc-failed-navigation.md new file mode 100644 index 000000000..c8609b792 --- /dev/null +++ b/.changes/core-ipc-failed-navigation.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:bug' +--- + +Fix IPC failing after a failed navigation to an external URL. diff --git a/.changes/core-navigation-handler.md b/.changes/core-navigation-handler.md new file mode 100644 index 000000000..64a6c0857 --- /dev/null +++ b/.changes/core-navigation-handler.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:bug' +--- + +Fix `WindowBuilder::on_navigation` handler not registered properly. diff --git a/.changes/core-updater-204-js-event.md b/.changes/core-updater-204-js-event.md new file mode 100644 index 000000000..ce025b14c --- /dev/null +++ b/.changes/core-updater-204-js-event.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:bug' +--- + +Emit `UPTODATE` update status to javascript when the updater server returns status code `204` diff --git a/.changes/core-window-config.md b/.changes/core-window-config.md new file mode 100644 index 000000000..0ebdf3673 --- /dev/null +++ b/.changes/core-window-config.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch' +--- + +Fix some configurations not applied when creating the window through Javascript. diff --git a/.changes/core-windows-notification-sound.md b/.changes/core-windows-notification-sound.md new file mode 100644 index 000000000..9057e5c49 --- /dev/null +++ b/.changes/core-windows-notification-sound.md @@ -0,0 +1,5 @@ +--- +"tauri": "patch" +--- + +Play a sound when showing a notification on Windows. diff --git a/.changes/deb-custom-desktop-file-config.md b/.changes/deb-custom-desktop-file-config.md new file mode 100644 index 000000000..96f0d81b9 --- /dev/null +++ b/.changes/deb-custom-desktop-file-config.md @@ -0,0 +1,7 @@ +--- +"tauri-utils": patch +"tauri-cli": patch +"@tauri-apps/cli": patch +--- + +Added the `desktop_template` option on `tauri.conf.json > tauri > bundle > deb`. diff --git a/.changes/deb-custom-desktop-file.md b/.changes/deb-custom-desktop-file.md new file mode 100644 index 000000000..ba66f4a20 --- /dev/null +++ b/.changes/deb-custom-desktop-file.md @@ -0,0 +1,5 @@ +--- +"tauri-bundler": "minor:feat" +--- + +Added `desktop_template` option on `DebianSettings`. diff --git a/.changes/disable-window-controls-api-options.md b/.changes/disable-window-controls-api-options.md new file mode 100644 index 000000000..4a129f004 --- /dev/null +++ b/.changes/disable-window-controls-api-options.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/api': 'minor:feat' +--- + +Added the `maximizable`, `minimizable` and `closable` fields on `WindowOptions`. diff --git a/.changes/disable-window-controls-api.md b/.changes/disable-window-controls-api.md new file mode 100644 index 000000000..7485a1cfc --- /dev/null +++ b/.changes/disable-window-controls-api.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/api': 'minor:feat' +--- + +Added the `setMaximizable`, `setMinimizable`, `setClosable`, `isMaximizable`, `isMinimizable` and `isClosable` methods. diff --git a/.changes/disable-window-controls-config.md b/.changes/disable-window-controls-config.md new file mode 100644 index 000000000..11827a66d --- /dev/null +++ b/.changes/disable-window-controls-config.md @@ -0,0 +1,5 @@ +--- +'tauri-utils': 'minor:feat' +--- + +Added the `maximizable`, `minimizable` and `closable` options to the window configuration. diff --git a/.changes/disable-window-controls-runtime-builder.md b/.changes/disable-window-controls-runtime-builder.md new file mode 100644 index 000000000..9112b4b04 --- /dev/null +++ b/.changes/disable-window-controls-runtime-builder.md @@ -0,0 +1,6 @@ +--- +'tauri-runtime-wry': 'minor:feat' +'tauri-runtime': 'minor:feat' +--- + +Added the `maximizable`, `minimizable` and `closable` methods to `WindowBuilder`. diff --git a/.changes/disable-window-controls-runtime-window.md b/.changes/disable-window-controls-runtime-window.md new file mode 100644 index 000000000..f4da492df --- /dev/null +++ b/.changes/disable-window-controls-runtime-window.md @@ -0,0 +1,6 @@ +--- +'tauri-runtime-wry': 'minor:feat' +'tauri-runtime': 'minor:feat' +--- + +Added `set_maximizable`, `set_minimizable`, `set_closable`, `is_maximizable`, `is_minimizable` and `is_closable` methods to the `Dispatch` trait. diff --git a/.changes/disable-window-controls-window-builder.md b/.changes/disable-window-controls-window-builder.md new file mode 100644 index 000000000..d568786f9 --- /dev/null +++ b/.changes/disable-window-controls-window-builder.md @@ -0,0 +1,5 @@ +--- +'tauri': 'minor:feat' +--- + +Added the `maximizable`, `minimizable` and `closable` options to the window builder. diff --git a/.changes/disable-window-controls-window.md b/.changes/disable-window-controls-window.md new file mode 100644 index 000000000..5390d81e8 --- /dev/null +++ b/.changes/disable-window-controls-window.md @@ -0,0 +1,5 @@ +--- +'tauri': 'minor:feat' +--- + +Added the `set_maximizable`, `set_minimizable`, `set_closable`, `is_maximizable`, `is_minimizable` and `is_closable` methods on `Window`. diff --git a/.changes/downgrade-min-sdk-version.md b/.changes/downgrade-min-sdk-version.md index 963249645..137260ea7 100644 --- a/.changes/downgrade-min-sdk-version.md +++ b/.changes/downgrade-min-sdk-version.md @@ -1,5 +1,5 @@ --- -"cli.rs": patch +"tauri-cli": patch "tauri": patch --- diff --git a/.changes/early-panic-for-png-not-rgba.md b/.changes/early-panic-for-png-not-rgba.md new file mode 100644 index 000000000..ace15393d --- /dev/null +++ b/.changes/early-panic-for-png-not-rgba.md @@ -0,0 +1,5 @@ +--- +"tauri-codegen": 'patch:enhance' +--- + +Early panic if the PNG icon is not RGBA. diff --git a/.changes/enable-minify.md b/.changes/enable-minify.md index 271799d65..e6d5fdb45 100644 --- a/.changes/enable-minify.md +++ b/.changes/enable-minify.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Change the Android template to enable minification on release and pull ProGuard rules from proguard-tauri.pro. diff --git a/.changes/error-on-identifier-change.md b/.changes/error-on-identifier-change.md index 9dc26537b..e09d787a4 100644 --- a/.changes/error-on-identifier-change.md +++ b/.changes/error-on-identifier-change.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Print an error if the Android project was generated with an older bundle identifier or package name. diff --git a/.changes/feat-shell-completions.md b/.changes/feat-shell-completions.md new file mode 100644 index 000000000..b94758d67 --- /dev/null +++ b/.changes/feat-shell-completions.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': 'minor:feat' +'@tauri-apps/cli': 'minor:feat' +--- + +Added `tauri completions` to generate shell completions scripts. diff --git a/.changes/fix-build-script-mobile-runner-npm.md b/.changes/fix-build-script-mobile-runner-npm.md index e076d7dcb..255b84445 100644 --- a/.changes/fix-build-script-mobile-runner-npm.md +++ b/.changes/fix-build-script-mobile-runner-npm.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Fixes the generated mobile build script when using an NPM runner. diff --git a/.changes/fix-cursor-icon-zoomin.md b/.changes/fix-cursor-icon-zoomin.md new file mode 100644 index 000000000..f604d3b27 --- /dev/null +++ b/.changes/fix-cursor-icon-zoomin.md @@ -0,0 +1,5 @@ +--- +'tauri-runtime': 'patch:bug' +--- + +Fixes typo in `CursorIcon` deserialization of the `ZoomIn` variant. diff --git a/.changes/fix-empty-identifier.md b/.changes/fix-empty-identifier.md index f9399c40a..86326c6a5 100644 --- a/.changes/fix-empty-identifier.md +++ b/.changes/fix-empty-identifier.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch "tauri-macros": patch --- diff --git a/.changes/fix-feature-removal.md b/.changes/fix-feature-removal.md new file mode 100644 index 000000000..8cc8be0a3 --- /dev/null +++ b/.changes/fix-feature-removal.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': 'patch:bug' +'@tauri-apps/cli': 'patch:bug' +--- + +Fixes Cargo.toml feature rewriting. diff --git a/.changes/fix-ios-run-xcode14.md b/.changes/fix-ios-run-xcode14.md index 592244de5..cb2f8ab32 100644 --- a/.changes/fix-ios-run-xcode14.md +++ b/.changes/fix-ios-run-xcode14.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Fixes running on device using Xcode 14. diff --git a/.changes/fix-ios-template.md b/.changes/fix-ios-template.md index f37cfba39..924210e23 100644 --- a/.changes/fix-ios-template.md +++ b/.changes/fix-ios-template.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Fixes the iOS project script to build the Rust library. diff --git a/.changes/fix-mobile-env-vars.md b/.changes/fix-mobile-env-vars.md index 8c1bd2c8a..365d34afd 100644 --- a/.changes/fix-mobile-env-vars.md +++ b/.changes/fix-mobile-env-vars.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Fixes `TAURI_*` environment variables for hook scripts on mobile commands. diff --git a/.changes/fix-nodejs-android-cmds.md b/.changes/fix-nodejs-android-cmds.md index 41e98f4e5..b1cda1788 100644 --- a/.changes/fix-nodejs-android-cmds.md +++ b/.changes/fix-nodejs-android-cmds.md @@ -1,5 +1,5 @@ --- -"cli.js": patch +"@tauri-apps/cli": patch --- Update tauri-mobile to fix running ADB scripts. diff --git a/.changes/fix-orientation-crash.md b/.changes/fix-orientation-crash.md index e89f70000..98b1c0170 100644 --- a/.changes/fix-orientation-crash.md +++ b/.changes/fix-orientation-crash.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Update Android project template with fix to crash on orientation change. diff --git a/.changes/fix-plugin-removal.md b/.changes/fix-plugin-removal.md index 473ee7158..fe1f6b254 100644 --- a/.changes/fix-plugin-removal.md +++ b/.changes/fix-plugin-removal.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Clear Android plugin JSON file before building Rust library to ensure removed plugins are propagated to the Android project. diff --git a/.changes/fix-plugin-template-cargotoml.md b/.changes/fix-plugin-template-cargotoml.md index 56fb27e7a..30d97f891 100644 --- a/.changes/fix-plugin-template-cargotoml.md +++ b/.changes/fix-plugin-template-cargotoml.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Readd the Cargo.toml file to the plugin template. diff --git a/.changes/fix-proguard-injection.md b/.changes/fix-proguard-injection.md index a6544b273..eedcabbc0 100644 --- a/.changes/fix-proguard-injection.md +++ b/.changes/fix-proguard-injection.md @@ -1,5 +1,5 @@ --- -"tauri-build": patch +"tauri-build": 'patch:bug' --- Fixes injection of the proguard rules on the Android project. diff --git a/.changes/fix-tauri-binary-windows.md b/.changes/fix-tauri-binary-windows.md index 0fcb32dcf..6ba7d7ba1 100644 --- a/.changes/fix-tauri-binary-windows.md +++ b/.changes/fix-tauri-binary-windows.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Fixes the Android build gradle plugin implementation on Windows. diff --git a/.changes/fix-wix-escape-resources.md b/.changes/fix-wix-escape-resources.md deleted file mode 100644 index 21757b72d..000000000 --- a/.changes/fix-wix-escape-resources.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tauri-bundler': 'patch' ---- - -Correctly escape XML for resource files in WiX bundler. diff --git a/.changes/fix-xcodescript-lib-path.md b/.changes/fix-xcodescript-lib-path.md index 5c435cbcc..d9d8dc6d1 100644 --- a/.changes/fix-xcodescript-lib-path.md +++ b/.changes/fix-xcodescript-lib-path.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Fixes iOS build script using the wrong path for the app library file. diff --git a/.changes/force-colored-logs.md b/.changes/force-colored-logs.md index 406ff82b1..a2bf87b05 100644 --- a/.changes/force-colored-logs.md +++ b/.changes/force-colored-logs.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Force colored logs on mobile commands. diff --git a/.changes/gradle-8.md b/.changes/gradle-8.md index b97aa28f5..e7eab5ab1 100644 --- a/.changes/gradle-8.md +++ b/.changes/gradle-8.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -'cli.js': 'patch' +'tauri-cli': 'patch' +'@tauri-apps/cli': 'patch' --- Update android template to gradle 8.0 diff --git a/.changes/improve-async-cmd-error-message.md b/.changes/improve-async-cmd-error-message.md new file mode 100644 index 000000000..48bbbb02c --- /dev/null +++ b/.changes/improve-async-cmd-error-message.md @@ -0,0 +1,5 @@ +--- +"tauri-macros": 'patch:enhance' +--- + +Improve compiler error message when generating an async command that has a reference input and don't return a Result. diff --git a/.changes/improve-local-ip-detection.md b/.changes/improve-local-ip-detection.md index 4d3d3d01e..b755cc9bf 100644 --- a/.changes/improve-local-ip-detection.md +++ b/.changes/improve-local-ip-detection.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Improve local IP address detection with user selection. diff --git a/.changes/inject-config.md b/.changes/inject-config.md index 7d905dd72..6f93ac59b 100644 --- a/.changes/inject-config.md +++ b/.changes/inject-config.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Inject Tauri configuration in the Android assets. diff --git a/.changes/ios-icon-color.md b/.changes/ios-icon-color.md index ff0436560..396e234c1 100644 --- a/.changes/ios-icon-color.md +++ b/.changes/ios-icon-color.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Added `--ios-color` option to the `tauri icon` command. diff --git a/.changes/ios-keep-alive.md b/.changes/ios-keep-alive.md index 2313abdd8..9687fed89 100644 --- a/.changes/ios-keep-alive.md +++ b/.changes/ios-keep-alive.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Keep the process alive even when the iOS application is closed. diff --git a/.changes/ios-logs.md b/.changes/ios-logs.md index 52493a06f..000786309 100644 --- a/.changes/ios-logs.md +++ b/.changes/ios-logs.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch "tauri": patch --- diff --git a/.changes/is_focused-api.md b/.changes/is_focused-api.md new file mode 100644 index 000000000..fee5a93a5 --- /dev/null +++ b/.changes/is_focused-api.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/api': 'minor:feat' +--- + +Add `WebviewWindow.is_focused` and `WebviewWindow.getFocusedWindow` getters. diff --git a/.changes/is_focused-runtime.md b/.changes/is_focused-runtime.md new file mode 100644 index 000000000..3991e4af6 --- /dev/null +++ b/.changes/is_focused-runtime.md @@ -0,0 +1,6 @@ +--- +'tauri-runtime': 'minor:feat' +'tauri-runtime-wry': 'minor:feat' +--- + +Add `Window::is_focused` getter. diff --git a/.changes/is_focused-tauri.md b/.changes/is_focused-tauri.md new file mode 100644 index 000000000..928f34f90 --- /dev/null +++ b/.changes/is_focused-tauri.md @@ -0,0 +1,5 @@ +--- +'tauri': 'minor:feat' +--- + +Add `Window::is_focused` and `Manager::get_focused_window` getters. diff --git a/.changes/local-dev-path-mobile.md b/.changes/local-dev-path-mobile.md index a4b21ca60..ae5542542 100644 --- a/.changes/local-dev-path-mobile.md +++ b/.changes/local-dev-path-mobile.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Fixes HMR on mobile when devPath is configured to load a filesystem path. diff --git a/.changes/logcat-all-tags.md b/.changes/logcat-all-tags.md index bc10a6200..64e4427e0 100644 --- a/.changes/logcat-all-tags.md +++ b/.changes/logcat-all-tags.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Print log output for all tags on Android development. diff --git a/.changes/migrate-cmd.md b/.changes/migrate-cmd.md index 3cd4dafed..4fae458f3 100644 --- a/.changes/migrate-cmd.md +++ b/.changes/migrate-cmd.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": 'patch:feat' +"@tauri-apps/cli": 'patch:feat' --- Added `migrate` command. diff --git a/.changes/mime-type.md b/.changes/mime-type.md new file mode 100644 index 000000000..0b43e79e4 --- /dev/null +++ b/.changes/mime-type.md @@ -0,0 +1,5 @@ +--- +'tauri-utils': 'patch' +--- + +Add `MimeType::parse_with_fallback` and `MimeType::parse_from_uri_with_fallback` diff --git a/.changes/mobile-dev-watcher-ignore-gen.md b/.changes/mobile-dev-watcher-ignore-gen.md index b0dc17e7c..4c78e0890 100644 --- a/.changes/mobile-dev-watcher-ignore-gen.md +++ b/.changes/mobile-dev-watcher-ignore-gen.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Ignore the `gen` folder on the dev watcher. diff --git a/.changes/mobile-init.md b/.changes/mobile-init.md index 97c17f266..a2996b3cb 100644 --- a/.changes/mobile-init.md +++ b/.changes/mobile-init.md @@ -1,6 +1,6 @@ --- -"cli.rs": minor -"cli.js": minor +"tauri-cli": minor +"@tauri-apps/cli": minor --- Added `android init` and `ios init` commands. diff --git a/.changes/mobile-lib-name.md b/.changes/mobile-lib-name.md index be976b9e4..c6c51e2b8 100644 --- a/.changes/mobile-lib-name.md +++ b/.changes/mobile-lib-name.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Add support to custom and kebab case library names for mobile apps. diff --git a/.changes/mobile-open.md b/.changes/mobile-open.md index d504ac24a..5e515a0ad 100644 --- a/.changes/mobile-open.md +++ b/.changes/mobile-open.md @@ -1,6 +1,6 @@ --- -"cli.rs": minor -"cli.js": minor +"tauri-cli": minor +"@tauri-apps/cli": minor --- Added `android open` and `ios open` commands. diff --git a/.changes/mobile.md b/.changes/mobile.md index caf32195e..2a8dfbe96 100644 --- a/.changes/mobile.md +++ b/.changes/mobile.md @@ -6,8 +6,8 @@ "tauri-macros": major "tauri-build": major "tauri": major -"cli.rs": major -"cli.js": major +"tauri-cli": major +"@tauri-apps/cli": major --- First mobile alpha release! diff --git a/.changes/move-updater-config.md b/.changes/move-updater-config.md index 499551461..5428d95f2 100644 --- a/.changes/move-updater-config.md +++ b/.changes/move-updater-config.md @@ -1,8 +1,8 @@ --- "tauri": patch "tauri-utils": patch -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Moved the updater configuration to the `BundleConfig`. diff --git a/.changes/msrv-1.64.md b/.changes/msrv-1.64.md index b61ad5297..7ee098be2 100644 --- a/.changes/msrv-1.64.md +++ b/.changes/msrv-1.64.md @@ -1,5 +1,5 @@ --- -"cli.rs": minor +"tauri-cli": minor "tauri-bundler": minor "tauri": minor "tauri-build": minor diff --git a/.changes/msrv-1.65.md b/.changes/msrv-1.65.md index 0aa5b991b..fcf612dd8 100644 --- a/.changes/msrv-1.65.md +++ b/.changes/msrv-1.65.md @@ -1,5 +1,5 @@ --- -"cli.rs": minor +"tauri-cli": minor "tauri-bundler": minor "tauri": minor "tauri-build": minor diff --git a/.changes/napi-rs.md b/.changes/napi-rs.md index cb47e3862..5b59d3026 100644 --- a/.changes/napi-rs.md +++ b/.changes/napi-rs.md @@ -1,5 +1,5 @@ --- -'cli.js': 'patch' +'@tauri-apps/cli': 'patch' --- Update `napi-rs` dependencies to latest to fix CLI hanging up forever. diff --git a/.changes/npm-pass-args.md b/.changes/npm-pass-args.md index adfd1d980..9718cdb10 100644 --- a/.changes/npm-pass-args.md +++ b/.changes/npm-pass-args.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch' -"cli.js": patch +'tauri-cli': 'patch' +"@tauri-apps/cli": patch --- Correctly pass arguments from `npm run` to `tauri`. diff --git a/.changes/nsis-accurate-app-size.md b/.changes/nsis-accurate-app-size.md new file mode 100644 index 000000000..913e6602a --- /dev/null +++ b/.changes/nsis-accurate-app-size.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:bug' +--- + +Fix incorrect estimated app size for NSIS bundler when installed to a non-empty directory. diff --git a/.changes/nsis-branding.md b/.changes/nsis-branding.md new file mode 100644 index 000000000..0c0c80276 --- /dev/null +++ b/.changes/nsis-branding.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:enhance' +--- + +Added Copyright field as BrandingText to the NSIS bundler. diff --git a/.changes/nsis-custom-language-files.md b/.changes/nsis-custom-language-files.md new file mode 100644 index 000000000..325147db5 --- /dev/null +++ b/.changes/nsis-custom-language-files.md @@ -0,0 +1,7 @@ +--- +'tauri-bundler': 'minor:feat' +'tauri-utils': 'minor' +'tauri-cli': 'minor:feat' +--- + +Allow specifying custom language files of Tauri's custom messages for the NSIS installer diff --git a/.changes/nsis-custom-template.md b/.changes/nsis-custom-template.md new file mode 100644 index 000000000..67a0b10bc --- /dev/null +++ b/.changes/nsis-custom-template.md @@ -0,0 +1,8 @@ +--- +'tauri-utils': 'minor' +'tauri-bundler': 'minor:feat' +'tauri-cli': 'minor:feat' +'@tauri-apps/cli': 'minor:feat' +--- + +Add `nsis > template` option to specify custom NSIS installer template. diff --git a/.changes/nsis-downgrades.md b/.changes/nsis-downgrades.md new file mode 100644 index 000000000..b0a297758 --- /dev/null +++ b/.changes/nsis-downgrades.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch' +--- + +Fix NSIS installer disabling `do not uninstall` button and silent installer aborting, if `allowDowngrades` was disabled even when we are not downgrading. diff --git a/.changes/nsis-dutch.md b/.changes/nsis-dutch.md new file mode 100644 index 000000000..d576dd612 --- /dev/null +++ b/.changes/nsis-dutch.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:enhance' +--- + +Added Dutch language support to the NSIS bundler. diff --git a/.changes/nsis-encoding.md b/.changes/nsis-encoding.md new file mode 100644 index 000000000..5e936e96e --- /dev/null +++ b/.changes/nsis-encoding.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:bug' +--- + +Fix NSIS bundler failing to build when `productName` contained chinsese characters. diff --git a/.changes/nsis-install-mode-args.md b/.changes/nsis-install-mode-args.md new file mode 100644 index 000000000..71927fd9d --- /dev/null +++ b/.changes/nsis-install-mode-args.md @@ -0,0 +1,5 @@ +--- +'tauri-utils': 'patch' +--- + +Add `WindowsUpdateInstallMode::nsis_args` diff --git a/.changes/nsis-japanese.md b/.changes/nsis-japanese.md new file mode 100644 index 000000000..e3f2739fc --- /dev/null +++ b/.changes/nsis-japanese.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:enhance' +--- + +Added Japanese language support to the NSIS bundler. diff --git a/.changes/nsis-korean.md b/.changes/nsis-korean.md new file mode 100644 index 000000000..320d0f2d7 --- /dev/null +++ b/.changes/nsis-korean.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:enhance' +--- + +Added Korean language support to the NSIS bundler. diff --git a/.changes/nsis-passive-mode.md b/.changes/nsis-passive-mode.md new file mode 100644 index 000000000..8f90fd5ac --- /dev/null +++ b/.changes/nsis-passive-mode.md @@ -0,0 +1,5 @@ +--- +'tauri': 'minor' +--- + +Support `passive` mode for NSIS updater. diff --git a/.changes/nsis-persian.md b/.changes/nsis-persian.md new file mode 100644 index 000000000..0b0008227 --- /dev/null +++ b/.changes/nsis-persian.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:enhance' +--- + +Added Persian language support to the NSIS bundler. diff --git a/.changes/nsis-restart-flag.md b/.changes/nsis-restart-flag.md new file mode 100644 index 000000000..889aa4fdb --- /dev/null +++ b/.changes/nsis-restart-flag.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'minor' +--- + +For NSIS, Add support for `/P` to install or uninstall in passive mode, `/R` to (re)start the app and `/NS` to disable creating shortcuts in `silent` and `passive` modes. diff --git a/.changes/nsis-restore-installation-path.md b/.changes/nsis-restore-installation-path.md new file mode 100644 index 000000000..f5e83dc05 --- /dev/null +++ b/.changes/nsis-restore-installation-path.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:bug' +--- + +Fix NSIS installer not using the old installation path as a default when using `perMachine` or `currentUser` install modes. Also fixes NSIS not respecting the `/D` flag which used to set the installation directory from command line. diff --git a/.changes/nsis-silent-kill.md b/.changes/nsis-silent-kill.md new file mode 100644 index 000000000..ee1e30ef4 --- /dev/null +++ b/.changes/nsis-silent-kill.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'minor' +--- + +NSIS `silent` and `passive` installer/updater will auto-kill the app if its running. diff --git a/.changes/nsis-silent-shortcuts.md b/.changes/nsis-silent-shortcuts.md new file mode 100644 index 000000000..b9cf4ec53 --- /dev/null +++ b/.changes/nsis-silent-shortcuts.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch' +--- + +Fix NSIS silent installer not creating Desktop and StartMenu shortcuts. Pass `/NS` to disable creating them. diff --git a/.changes/nsis-spanish.md b/.changes/nsis-spanish.md deleted file mode 100644 index 98bdec9df..000000000 --- a/.changes/nsis-spanish.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'tauri-bundler': 'patch' ---- - -Added the following languages to the NSIS bundler: - -- `Spanish` -- `SpanishInternational` diff --git a/.changes/nsis-swedish.md b/.changes/nsis-swedish.md new file mode 100644 index 000000000..18e2e7835 --- /dev/null +++ b/.changes/nsis-swedish.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:enhance' +--- + +Added Swedish language support to the NSIS bundler. diff --git a/.changes/nsis-turkish.md b/.changes/nsis-turkish.md new file mode 100644 index 000000000..0527faa5a --- /dev/null +++ b/.changes/nsis-turkish.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:enhance' +--- + +Added Turkish language support to the NSIS bundler. diff --git a/.changes/nsis-uninstall-wix.md b/.changes/nsis-uninstall-wix.md new file mode 100644 index 000000000..a76841744 --- /dev/null +++ b/.changes/nsis-uninstall-wix.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'minor' +--- + +NSIS installer will now check if a previous WiX `.msi` installation exist and will prompt users to uninstall it. diff --git a/.changes/nsis-updater-restart.md b/.changes/nsis-updater-restart.md new file mode 100644 index 000000000..f07075713 --- /dev/null +++ b/.changes/nsis-updater-restart.md @@ -0,0 +1,5 @@ +--- +'tauri': 'minor:enhance' +--- + +Restart the app after the NSIS updater is finished. diff --git a/.changes/nsis-webview-installmodes.md b/.changes/nsis-webview-installmodes.md deleted file mode 100644 index a9de60a63..000000000 --- a/.changes/nsis-webview-installmodes.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tauri-bundler': 'patch' ---- - -Correctly escape arguments in NSIS script to fix bundling apps that use non-default WebView2 install modes. diff --git a/.changes/plugin-android-project-refactor.md b/.changes/plugin-android-project-refactor.md index 84965802d..0ba0a844a 100644 --- a/.changes/plugin-android-project-refactor.md +++ b/.changes/plugin-android-project-refactor.md @@ -1,5 +1,5 @@ --- -"cli.rs": patch +"tauri-cli": patch "tauri-build": patch --- diff --git a/.changes/plugin-init-refactor.md b/.changes/plugin-init-refactor.md index 25e0ab7e5..567bd7b09 100644 --- a/.changes/plugin-init-refactor.md +++ b/.changes/plugin-init-refactor.md @@ -1,6 +1,6 @@ --- -"cli.rs": minor -"cli.js": minor +"tauri-cli": minor +"@tauri-apps/cli": minor --- Changed the `--api` flag on `plugin init` to `--no-api`. diff --git a/.changes/plugin-template-examples-manifest.md b/.changes/plugin-template-examples-manifest.md index c8f0de216..f3f2a7d93 100644 --- a/.changes/plugin-template-examples-manifest.md +++ b/.changes/plugin-template-examples-manifest.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Add Cargo manifest files for the plugin example templates. diff --git a/.changes/pnpm-android.md b/.changes/pnpm-android.md index 827fc0608..98302b1ef 100644 --- a/.changes/pnpm-android.md +++ b/.changes/pnpm-android.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Fix `tauri android build/dev` crashing when used with standalone `pnpm` executable on Windows. diff --git a/.changes/readme.md b/.changes/readme.md index 8151b11df..73ee22d97 100644 --- a/.changes/readme.md +++ b/.changes/readme.md @@ -1,31 +1,44 @@ -# Changes - -##### via https://github.com/jbolda/covector - -As you create PRs and make changes that require a version bump, please add a new markdown file in this folder. You do not note the version _number_, but rather the type of bump that you expect: major, minor, or patch. The filename is not important, as long as it is a `.md`, but we recommend that it represents the overall change for organizational purposes. - -When you select the version bump required, you do _not_ need to consider dependencies. Only note the package with the actual change, and any packages that depend on that package will be bumped automatically in the process. - -Use the following format: - -```md ---- -"package-a": patch -"package-b": patch ---- - -Change summary goes here - -``` - -Summaries do not have a specific character limit, but are text only. These summaries are used within the (future implementation of) changelogs. They will give context to the change and also point back to the original PR if more details and context are needed. - -Changes will be designated as a `major`, `minor` or `patch` as further described in [semver](https://semver.org/). - -Given a version number MAJOR.MINOR.PATCH, increment the: - -- MAJOR version when you make incompatible API changes, -- MINOR version when you add functionality in a backwards compatible manner, and -- PATCH version when you make backwards compatible bug fixes. - -Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format, but will be discussed prior to usage (as extra steps will be necessary in consideration of merging and publishing). \ No newline at end of file +# Changes + +##### via https://github.com/jbolda/covector + +As you create PRs and make changes that require a version bump, please add a new markdown file in this folder. You do not note the version _number_, but rather the type of bump that you expect: major, minor, or patch. The filename is not important, as long as it is a `.md`, but we recommend that it represents the overall change for organizational purposes. + +When you select the version bump required, you do _not_ need to consider dependencies. Only note the package with the actual change, and any packages that depend on that package will be bumped automatically in the process. + +Use the following format: + +```md +--- +'package-a': patch +'package-b': patch +--- + +Change summary goes here +``` + +Summaries do not have a specific character limit, but are text only. These summaries are used within the (future implementation of) changelogs. They will give context to the change and also point back to the original PR if more details and context are needed. + +Changes will be designated as a `major`, `minor` or `patch` as further described in [semver](https://semver.org/). + +Given a version number MAJOR.MINOR.PATCH, increment the: + +- MAJOR version when you make incompatible API changes, +- MINOR version when you add functionality in a backwards compatible manner, and +- PATCH version when you make backwards compatible bug fixes. + +Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format, but will be discussed prior to usage (as extra steps will be necessary in consideration of merging and publishing). + +Additionally you could specify a tag for the change file to group it with other changes by prefixing the bump with `:`, for example: + +```md +--- +'package-a': patch:bug +--- + +Change summary goes here +``` + +which will group this change file with other changes that specify the `bug` tag. + +For list of available tags, see the `changeTags` key in [./config.json](./config.json) diff --git a/.changes/remove-allowlist.md b/.changes/remove-allowlist.md index 64e4333d3..c0e007213 100644 --- a/.changes/remove-allowlist.md +++ b/.changes/remove-allowlist.md @@ -1,8 +1,8 @@ --- "tauri": patch "tauri-utils": patch -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Removed the allowlist configuration. diff --git a/.changes/rustls-default.md b/.changes/rustls-default.md new file mode 100644 index 000000000..56d4c54b2 --- /dev/null +++ b/.changes/rustls-default.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': patch +'@tauri-apps/cli': patch +--- + +Add `rustls` as default Cargo feature. diff --git a/.changes/shadow-api.md b/.changes/shadow-api.md index ce9337b22..01b7797aa 100644 --- a/.changes/shadow-api.md +++ b/.changes/shadow-api.md @@ -1,5 +1,5 @@ --- -'api': minor +'@tauri-apps/api': 'minor:feat' --- Added the `shadow` option when creating a window and `setShadow` function. diff --git a/.changes/skip-target-install-arg.md b/.changes/skip-target-install-arg.md index cb3709d88..98e96702c 100644 --- a/.changes/skip-target-install-arg.md +++ b/.changes/skip-target-install-arg.md @@ -1,6 +1,6 @@ --- -'cli.rs': 'patch:enhance' -'cli.js': 'patch:enhance' +'tauri-cli': 'patch:enhance' +'@tauri-apps/cli': 'patch:enhance' --- Add `--skip-targets-install` flag for `tauri android init` and `tauri ios init` to skip installing needed rust targets vie rustup. diff --git a/.changes/target-dir-detection.md b/.changes/target-dir-detection.md index eab17d395..d1bd99f8c 100644 --- a/.changes/target-dir-detection.md +++ b/.changes/target-dir-detection.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Fix target directory detection when compiling for Android. diff --git a/.changes/tauri-build-shortdesc-copyright.md b/.changes/tauri-build-shortdesc-copyright.md new file mode 100644 index 000000000..227e2b1c3 --- /dev/null +++ b/.changes/tauri-build-shortdesc-copyright.md @@ -0,0 +1,5 @@ +--- +'tauri-build': 'patch:enhance' +--- + +On Windows, set `LegalCopyright` and `FileDescription` file properties on the executable from `tauri.bundle.copyright` and `tauri.bundle.shortDescription`, diff --git a/.changes/tauri-info-msvc-detection.md b/.changes/tauri-info-msvc-detection.md new file mode 100644 index 000000000..d1e5071b3 --- /dev/null +++ b/.changes/tauri-info-msvc-detection.md @@ -0,0 +1,5 @@ +--- +'tauri-cli': 'patch:enhance' +--- + +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`. diff --git a/.changes/tls-features-automatically-enabled.md b/.changes/tls-features-automatically-enabled.md index 3afa2ec3c..ce2cf0002 100644 --- a/.changes/tls-features-automatically-enabled.md +++ b/.changes/tls-features-automatically-enabled.md @@ -1,6 +1,6 @@ --- -"cli.rs": patch -"cli.js": patch +"tauri-cli": patch +"@tauri-apps/cli": patch --- Automatically enable the `rustls-tls` tauri feature on mobile and `native-tls` on desktop if `rustls-tls` is not enabled. diff --git a/.changes/tray_get_item.md b/.changes/tray_get_item.md new file mode 100644 index 000000000..c4f02dc8c --- /dev/null +++ b/.changes/tray_get_item.md @@ -0,0 +1,5 @@ +--- +'tauri': minor:feat +--- + +Add `MenuHandle::try_get_item` and `SystemTrayHandle::try_get_item` which returns a `Option` instead of panicking. diff --git a/.changes/ubuntu-20.04-cli.js.md b/.changes/ubuntu-20.04-cli.js.md index 4dfe3ac4d..18f06f6cc 100644 --- a/.changes/ubuntu-20.04-cli.js.md +++ b/.changes/ubuntu-20.04-cli.js.md @@ -1,5 +1,5 @@ --- -"cli.js": patch +"@tauri-apps/cli": patch --- Use Ubuntu 20.04 to compile the CLI, increasing the minimum libc version required. diff --git a/.changes/ubuntu-20.04-cli.rs.md b/.changes/ubuntu-20.04-cli.rs.md index 4d5abfcec..f17d1df01 100644 --- a/.changes/ubuntu-20.04-cli.rs.md +++ b/.changes/ubuntu-20.04-cli.rs.md @@ -1,5 +1,5 @@ --- -"cli.rs": patch +"tauri-cli": patch --- - Use Ubuntu 20.04 to compile the CLI for cargo-binstall, increasing the minimum libc required. diff --git a/.changes/unpin-deps.md b/.changes/unpin-deps.md new file mode 100644 index 000000000..29a88af13 --- /dev/null +++ b/.changes/unpin-deps.md @@ -0,0 +1,9 @@ +--- +'tauri': patch:enhance +'tauri-build': patch:enhance +'tauri-codegen': patch:enhance +'tauri-runtime': patch:enhance +'tauri-runtime-wry': patch:enhance +--- + +Unpin `time`, `ignore`, `winnow`, and `ignore` crate versions. Developers now have to pin crates if needed themselves. A list of crates that need pinning to adhere to Tauri's MSRV will be visible in Tauri's GitHub workflow: https://github.com/tauri-apps/tauri/blob/dev/.github/workflows/test-core.yml#L85. diff --git a/.changes/webview-attributes-from-window-config-impl.md b/.changes/webview-attributes-from-window-config-impl.md new file mode 100644 index 000000000..3adaa2e92 --- /dev/null +++ b/.changes/webview-attributes-from-window-config-impl.md @@ -0,0 +1,5 @@ +--- +'tauri-runtime': 'patch' +--- + +impl `From<&WindowConfig>` for `WebviewAttributes`. diff --git a/.changes/window-effects-api.md b/.changes/window-effects-api.md index 39845271e..44e25bb01 100644 --- a/.changes/window-effects-api.md +++ b/.changes/window-effects-api.md @@ -1,5 +1,5 @@ --- -'api': minor +'@tauri-apps/api': 'minor:feat' --- Added the `windowEffects` option when creating a window and `setWindowEffects` method to change it at runtime. diff --git a/.changes/window-effects-config.md b/.changes/window-effects-config.md index efd8fc14d..941771324 100644 --- a/.changes/window-effects-config.md +++ b/.changes/window-effects-config.md @@ -1,5 +1,5 @@ --- -'tauri-utils': minor +'tauri-utils': 'minor:feat' --- Added the `window_effects` option to the window configuration. diff --git a/.changes/window-effects.md b/.changes/window-effects.md index d7447873e..f5f767807 100644 --- a/.changes/window-effects.md +++ b/.changes/window-effects.md @@ -1,7 +1,7 @@ --- -'tauri': minor -'tauri-runtime-wry': minor -'tauri-runtime': minor +'tauri': 'minor:feat' +'tauri-runtime-wry': 'minor:feat' +'tauri-runtime': 'minor:feat' --- Added the `window_effects` option when creating a window and `Window::set_effects` to change it at runtime. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 0e9d4cda1..cdf2d5c61 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -54,20 +54,20 @@ Some Tauri packages will be automatically built when running one of the examples ### Packages Overview -- The JS API (`/tooling/api`) contains JS bindings to the builtin Rust functions in the Rust API. -- cli.rs (`/tooling/cli`) is the primary CLI for creating and developing Tauri apps. -- cli.js (`/tooling/cli/node`) is a Node.js CLI wrapper for `cli.rs`. -- Tauri Bundler (`/tooling/bundler`) is used by the Rust CLI to package executables into installers. - Tauri Core (`/core/tauri`) is the heart of Tauri. It contains the code that starts the app, configures communication between Rust and the Webview, and ties all the other packages together. - The Macros (`/core/tauri-macros`) are used by Tauri Core for various functions. - -### Developing The Node.js CLI (cli.js) - -`cli.js` is a wrapper to `cli.rs` so most changes should be written on the Rust CLI. The `[Tauri repo root]/tooling/cli/node` folder contains only packaging scripts to properly publish the Rust CLI binaries to NPM. +- Tauri Bundler (`/tooling/bundler`) is used by the Rust CLI to package executables into installers. +- The Rust CLI aka `tauri-cli` (`/tooling/cli`) is the primary CLI for creating and developing Tauri apps. +- The JS CLI aka `@tauri-apps/cli` (`/tooling/cli/node`) is a Node.js CLI wrapper for `tauri-cli`. +- The JS API aka `@tauri-apps/api` (`/tooling/api`) contains JS bindings to the builtin Rust functions in the Rust API. ### Developing Tauri Bundler and Rust CLI -The code for the bundler is located in `[Tauri repo root]/tooling/bundler`, and the code for the Rust CLI is located in `[Tauri repo root]/tooling/cli`. If you are using your local copy of cli.js (see above), any changes you make to the bundler and CLI will be automatically built and applied when running the build or dev command. Otherwise, running `cargo install --path .` in the Rust CLI directory will allow you to run `cargo tauri build` and `cargo tauri dev` anywhere, using the updated copy of the bundler and cli. You will have to run this command each time you make a change in either package. +The code for the bundler is located in `[Tauri repo root]/tooling/bundler`, and the code for the Rust CLI is located in `[Tauri repo root]/tooling/cli`. If you are using your local copy of `@tauri-apps/cli` (see above), any changes you make to the bundler and CLI will be automatically built and applied when running the build or dev command. Otherwise, running `cargo install --path .` in the Rust CLI directory will allow you to run `cargo tauri build` and `cargo tauri dev` anywhere, using the updated copy of the bundler and cli. You will have to run this command each time you make a change in either package. + +### Developing The Node.js CLI (`@tauri-apps/cli`) + +`@tauri-apps/cli` is a wrapper to `tauri-cli` so most changes should be written on the Rust CLI. The `[Tauri repo root]/tooling/cli/node` folder contains only packaging scripts to properly publish the Rust CLI binaries to NPM. ### Developing Tauri Core and Related Components (Rust API, Macros, Codegen, and Utils) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 7f895a391..6738a4d56 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: benches +name: bench on: push: diff --git a/.github/workflows/check-generated-files.yml b/.github/workflows/check-generated-files.yml index 099ba0129..2fc6b7fea 100644 --- a/.github/workflows/check-generated-files.yml +++ b/.github/workflows/check-generated-files.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: Check generated files +name: check generated files on: pull_request: @@ -49,7 +49,7 @@ jobs: - 'tooling/cli/schema.json' - 'core/tauri-config-schema/schema.json' - check-api: + api: runs-on: ubuntu-latest needs: changes if: needs.changes.outputs.api == 'true' @@ -64,7 +64,7 @@ jobs: git restore tooling/api/docs/js-api.json ./.scripts/ci/has-diff.sh - check-schema: + schema: runs-on: ubuntu-latest needs: changes if: needs.changes.outputs.schema == 'true' diff --git a/.github/workflows/check-license-header.yml b/.github/workflows/check-license-header.yml index c0d6da66b..61a83af07 100644 --- a/.github/workflows/check-license-header.yml +++ b/.github/workflows/check-license-header.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: check license header +name: check license headers on: pull_request: diff --git a/.github/workflows/change-status-on-PR.yml b/.github/workflows/covector-status.yml similarity index 100% rename from .github/workflows/change-status-on-PR.yml rename to .github/workflows/covector-status.yml diff --git a/.github/workflows/covector-version-or-publish.yml b/.github/workflows/covector-version-or-publish.yml index a59c8835f..27b8e57b6 100644 --- a/.github/workflows/covector-version-or-publish.yml +++ b/.github/workflows/covector-version-or-publish.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: version or publish +name: covector version or publish on: push: @@ -115,21 +115,30 @@ jobs: repository: tauri-apps/tauri-docs event-type: update-docs - - name: Trigger cli.js publishing workflow + - name: Get `@tauri-apps/cli` release id + id: cliReleaseId if: | steps.covector.outputs.successfulPublish == 'true' && - contains(steps.covector.outputs.packagesPublished, 'cli.rs') + contains(steps.covector.outputs.packagesPublished, '@tauri-apps/cli') + run: | + id=$(jq '.-tauri-apps-cli-releaseId' <<< "${{ steps.covector.outputs }}") + echo "cliReleaseId=$id" >> "$GITHUB_OUTPUT" + + - name: Trigger `@tauri-apps/cli` publishing workflow + if: | + steps.covector.outputs.successfulPublish == 'true' && + contains(steps.covector.outputs.packagesPublished, '@tauri-apps/cli') uses: peter-evans/repository-dispatch@v1 with: token: ${{ secrets.ORG_TAURI_BOT_PAT }} repository: tauri-apps/tauri - event-type: publish-clijs - client-payload: '{"releaseId": "${{ steps.covector.outputs.cli.js-releaseId }}" }' + event-type: publish-js-cli + client-payload: '{"releaseId": "${{ steps.cliReleaseId.outputs.cliReleaseId }}" }' - - name: Trigger cli.rs publishing workflow + - name: Trigger `tauri-cli` publishing workflow if: | steps.covector.outputs.successfulPublish == 'true' && - contains(steps.covector.outputs.packagesPublished, 'cli.rs') + contains(steps.covector.outputs.packagesPublished, 'tauri-cli') uses: peter-evans/repository-dispatch@v1 with: token: ${{ secrets.ORG_TAURI_BOT_PAT }} diff --git a/.github/workflows/lint-fmt-cli.yml b/.github/workflows/lint-cli.yml similarity index 93% rename from .github/workflows/lint-fmt-cli.yml rename to .github/workflows/lint-cli.yml index 200e1daa5..297330b13 100644 --- a/.github/workflows/lint-fmt-cli.yml +++ b/.github/workflows/lint-cli.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: cli clippy and fmt check +name: lint cli on: push: @@ -11,7 +11,7 @@ on: - next pull_request: paths: - - '.github/workflows/lint-fmt-cli.yml' + - '.github/workflows/lint-cli.yml' - 'tooling/cli/**' env: @@ -23,7 +23,7 @@ concurrency: cancel-in-progress: true jobs: - fmt_check: + fmt: runs-on: ubuntu-latest steps: @@ -41,7 +41,7 @@ jobs: command: fmt args: --manifest-path ./tooling/cli/Cargo.toml --all -- --check - cli_clippy_check: + clippy: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/lint-fmt-core.yml b/.github/workflows/lint-core.yml similarity index 94% rename from .github/workflows/lint-fmt-core.yml rename to .github/workflows/lint-core.yml index b977b77c4..68b01ea6b 100644 --- a/.github/workflows/lint-fmt-core.yml +++ b/.github/workflows/lint-core.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: core clippy and fmt check +name: lint core on: push: @@ -11,7 +11,7 @@ on: - next pull_request: paths: - - '.github/workflows/lint-fmt-core.yml' + - '.github/workflows/lint-core.yml' - 'core/**' - '!core/tauri/scripts/**' - 'examples/**' @@ -25,7 +25,7 @@ concurrency: cancel-in-progress: true jobs: - fmt_check: + fmt: runs-on: ubuntu-latest steps: @@ -43,7 +43,7 @@ jobs: command: fmt args: --all -- --check - core_clippy_check: + clippy: runs-on: ubuntu-latest strategy: matrix: diff --git a/.github/workflows/lint-js.yml b/.github/workflows/lint-js.yml index 7165c07d0..7cf54f022 100644 --- a/.github/workflows/lint-js.yml +++ b/.github/workflows/lint-js.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: api and cli.js lint check +name: lint js on: pull_request: @@ -16,7 +16,7 @@ concurrency: cancel-in-progress: true jobs: - eslint-check: + eslint-cli: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -24,24 +24,34 @@ jobs: with: node-version: '14' cache: yarn - cache-dependency-path: tooling/*/yarn.lock - - name: install cli.js deps via yarn + cache-dependency-path: tooling/cli/node/yarn.lock + - name: install deps via yarn working-directory: ./tooling/cli/node/ run: yarn # nothing to lint - #- name: run cli.js lint + #- name: run lint # working-directory: ./tooling/cli/node/ # run: yarn lint - - name: run cli.js format + - name: run format working-directory: ./tooling/cli/node/ run: yarn format:check - - name: install api deps via yarn + eslint-api: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '14' + cache: yarn + cache-dependency-path: tooling/api/yarn.lock + + - name: install deps via yarn working-directory: ./tooling/api/ run: yarn - - name: run api lint + - name: run lint working-directory: ./tooling/api/ run: yarn lint - - name: run api format + - name: run format working-directory: ./tooling/api/ run: yarn format:check diff --git a/.github/workflows/publish-cli-js.yml b/.github/workflows/publish-cli-js.yml index 77a0480fc..c493659e4 100644 --- a/.github/workflows/publish-cli-js.yml +++ b/.github/workflows/publish-cli-js.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: publish cli.js +name: publish `@tauri-apps/cli` env: DEBUG: napi:* APP_NAME: cli @@ -11,10 +11,10 @@ on: workflow_dispatch: inputs: releaseId: - description: 'ID of the cli.js release' + description: 'ID of the `@tauri-apps/cli` release' required: true repository_dispatch: - types: [publish-clijs] + types: [publish-js-cli] defaults: run: @@ -30,14 +30,14 @@ jobs: target: x86_64-apple-darwin architecture: x64 build: | - yarn build:release --features openssl-vendored + yarn build:release strip -x *.node - host: windows-latest - build: yarn build:release --features openssl-vendored + build: yarn build:release target: x86_64-pc-windows-msvc architecture: x64 - host: windows-latest - build: yarn build:release --features openssl-vendored --target i686-pc-windows-msvc + build: yarn build:release --target i686-pc-windows-msvc target: i686-pc-windows-msvc architecture: x64 - host: ubuntu-20.04 @@ -45,26 +45,26 @@ jobs: docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian build: | cd tooling/cli/node - yarn build:release --features openssl-vendored --target x86_64-unknown-linux-gnu + yarn build:release --target x86_64-unknown-linux-gnu strip *.node - host: ubuntu-20.04 target: x86_64-unknown-linux-musl docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine build: | cd tooling/cli/node - yarn build:release --features openssl-vendored + yarn build:release strip *.node - host: macos-latest target: aarch64-apple-darwin build: | - yarn build:release --features openssl-vendored --target=aarch64-apple-darwin + yarn build:release --target=aarch64-apple-darwin strip -x *.node - host: ubuntu-20.04 target: aarch64-unknown-linux-gnu docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 build: | cd tooling/cli/node - yarn build:release --features openssl-vendored --target aarch64-unknown-linux-gnu + yarn build:release --target aarch64-unknown-linux-gnu aarch64-unknown-linux-gnu-strip *.node - host: ubuntu-20.04 architecture: x64 @@ -73,7 +73,7 @@ jobs: sudo apt-get update sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y build: | - yarn build:release --features openssl-vendored --target=armv7-unknown-linux-gnueabihf + yarn build:release --target=armv7-unknown-linux-gnueabihf arm-linux-gnueabihf-strip *.node - host: ubuntu-20.04 architecture: x64 @@ -82,12 +82,12 @@ jobs: build: | cd tooling/cli/node rustup target add aarch64-unknown-linux-musl - yarn build:release --features openssl-vendored --target aarch64-unknown-linux-musl + yarn build:release --target aarch64-unknown-linux-musl /aarch64-linux-musl-cross/bin/aarch64-linux-musl-strip *.node #- host: windows-latest # architecture: x64 # target: aarch64-pc-windows-msvc - # build: yarn build:release --features openssl-vendored --target aarch64-pc-windows-msvc + # build: yarn build:release --target aarch64-pc-windows-msvc name: stable - ${{ matrix.settings.target }} - node@16 runs-on: ${{ matrix.settings.host }} steps: @@ -179,7 +179,7 @@ jobs: # freebsd-version # cd ./tooling/cli/node/ # yarn install --ignore-scripts --frozen-lockfile --registry https://registry.npmjs.org --network-timeout 300000 - # yarn build:release --features openssl-vendored + # yarn build:release # strip -x *.node # rm -rf node_modules # rm -rf ../target diff --git a/.github/workflows/publish-cli-rs.yml b/.github/workflows/publish-cli-rs.yml index 60cabf620..2fb4b9d05 100644 --- a/.github/workflows/publish-cli-rs.yml +++ b/.github/workflows/publish-cli-rs.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: publish cli.rs +name: publish `tauri-cli` env: MACOSX_DEPLOYMENT_TARGET: '10.13' on: @@ -79,12 +79,12 @@ jobs: run: ./.scripts/ci/pack-cli.sh - name: Get CLI version - run: echo "CLI_VERSION=$(cat tooling/cli/metadata.json | jq '."cli.js".version' -r)" >> $GITHUB_ENV + run: echo "CLI_VERSION=$(cat tooling/cli/metadata.json | jq '."@tauri-apps/cli".version' -r)" >> $GITHUB_ENV - name: Publish release uses: softprops/action-gh-release@50195ba7f6f93d1ac97ba8332a178e008ad176aa with: - tag_name: cli.rs-v${{ env.CLI_VERSION }} + tag_name: tauri-cli-v${{ env.CLI_VERSION }} files: | outputs/cargo-tauri-*.zip outputs/cargo-tauri-*.tgz diff --git a/.github/workflows/test-cli-js.yml b/.github/workflows/test-cli-js.yml index 4818c370e..aaeea54f6 100644 --- a/.github/workflows/test-cli-js.yml +++ b/.github/workflows/test-cli-js.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: test cli.js +name: test `@tauri-apps/cli` on: push: @@ -12,7 +12,7 @@ on: pull_request: paths: - '.github/workflows/test-cli-js.yml' - # currently cli.js only tests the template + # currently` @tauri-apps/cli` only tests the template - 'tooling/cli/templates/app/**' env: @@ -24,7 +24,7 @@ concurrency: cancel-in-progress: true jobs: - test-tauri-js-cli: + test: runs-on: ${{ matrix.platform }} strategy: diff --git a/.github/workflows/test-cli-rs.yml b/.github/workflows/test-cli-rs.yml index 6dfbd246e..20485b551 100644 --- a/.github/workflows/test-cli-rs.yml +++ b/.github/workflows/test-cli-rs.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: test cli.rs +name: test `tauri-cli` on: push: @@ -23,7 +23,7 @@ concurrency: cancel-in-progress: true jobs: - test-tauri-cli: + test: runs-on: ${{ matrix.platform }} strategy: diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index a3ef29d2d..39d5c16d0 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -24,7 +24,7 @@ concurrency: cancel-in-progress: true jobs: - test-tauri-core: + test: runs-on: ${{ matrix.platform.os }} strategy: diff --git a/.github/workflows/test-bundler.yml b/.github/workflows/test-lint-bundler.yml similarity index 96% rename from .github/workflows/test-bundler.yml rename to .github/workflows/test-lint-bundler.yml index 4467a3574..f53a15117 100644 --- a/.github/workflows/test-bundler.yml +++ b/.github/workflows/test-lint-bundler.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT -name: test bundler +name: test and lint bundler on: push: @@ -23,7 +23,7 @@ concurrency: cancel-in-progress: true jobs: - build-tauri-bundler: + test: runs-on: ${{ matrix.platform }} strategy: @@ -48,7 +48,7 @@ jobs: cd ./tooling/bundler cargo test - fmt_check: + fmt: runs-on: ubuntu-latest steps: @@ -66,7 +66,7 @@ jobs: command: fmt args: --manifest-path ./tooling/bundler/Cargo.toml --all -- --check - clippy-check: + clippy: runs-on: ubuntu-latest steps: diff --git a/.husky/pre-commit b/.husky/pre-commit index dcd6fda84..3050d5d43 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -16,7 +16,7 @@ else fi if [ -z "$(git diff --name-only tooling/cli/node)" ]; then - echo "skipping cli.js - no changes detected" + echo "skipping \`@tauri-apps/cli\` - no changes detected" else cd tooling/cli/node yarn format diff --git a/.scripts/covector/sync-cli-metadata.js b/.scripts/covector/sync-cli-metadata.js index d8ac19d23..707837ddd 100644 --- a/.scripts/covector/sync-cli-metadata.js +++ b/.scripts/covector/sync-cli-metadata.js @@ -6,7 +6,7 @@ /* This script is solely intended to be run as part of the `covector version` step to keep the `../tooling/cli/metadata.json` up to date with other version bumps. Long term -we should look to find a more "rusty way" to import / "pin" a version value in our cli.rs +we should look to find a more "rusty way" to import / "pin" a version value in our tauri-cli rust binaries. */ @@ -15,7 +15,7 @@ const { resolve } = require('path') const packageNickname = process.argv[2] const filePath = - packageNickname === 'cli.js' + packageNickname === '@tauri-apps/cli' ? `../../../tooling/cli/metadata.json` : `../../tooling/cli/metadata.json` const bump = process.argv[3] @@ -60,9 +60,9 @@ const metadata = JSON.parse(readFileSync(filePath, 'utf-8')) // set field version let version -if (packageNickname === 'cli.js') { - version = inc(metadata[packageNickname].version) - metadata[packageNickname].version = version +if (packageNickname === '@tauri-apps/cli') { + version = inc(metadata['cli.js'].version) + metadata['cli.js'].version = version } else { version = inc(metadata[packageNickname]) metadata[packageNickname] = version diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 5f40656bf..bebaed43e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -39,16 +39,16 @@ This is common code that is reused in many places and offers useful utilities li ### Tauri Tooling -#### [api](https://github.com/tauri-apps/tauri/tree/dev/tooling/api) [TS -> JS] +#### [@tauri-apps/api](https://github.com/tauri-apps/tauri/tree/dev/tooling/api) [TS -> JS] A TypeScript library that creates `cjs` and `esm` JavaScript endpoints for you to import into your Frontend framework so that the Webview can call and listen to backend activity. We also ship the pure TypeScript, because for some frameworks this is more optimal. It uses the message passing of webviews to their hosts. #### [bundler](https://github.com/tauri-apps/tauri/tree/dev/tooling/bundler) [RUST / SHELL] The bundler is a library that builds a Tauri App for the platform triple it detects / is told. At the moment it currently supports macOS, Windows and Linux - but in the near future will support mobile platforms as well. May be used outside of Tauri projects. -#### [cli.js](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli/node) [JS] -It is a wrapper around [cli.rs](https://github.com/tauri-apps/tauri/blob/dev/tooling/cli) using [napi-rs](https://github.com/napi-rs/napi-rs) to produce NPM packages for each platform. +#### [@tauri-apps/cli](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli/node) [JS] +It is a wrapper around [tauri-cli](https://github.com/tauri-apps/tauri/blob/dev/tooling/cli) using [napi-rs](https://github.com/napi-rs/napi-rs) to produce NPM packages for each platform. -#### [cli.rs](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli) [RUST] +#### [tauri-cli](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli) [RUST] This rust executable provides the full interface to all of the required activities for which the CLI is required. It will run on macOS, Windows, and Linux. #### [create-tauri-app](https://github.com/tauri-apps/create-tauri-app) [JS] @@ -67,9 +67,6 @@ Tauri uses WRY as the abstract layer responsible to determine which webview is u # Additional tooling -## [binary-releases](https://github.com/tauri-apps/binary-releases) -This is the delivery mechanism for tauri prebuilt binaries: currently the cli.rs (used by cli.js) and rustup binaries (used by the deps install command of cli.js). These artifacts are automatically created on release. - ## [tauri-action](https://github.com/tauri-apps/tauri-action) This is a github workflow that builds tauri binaries for all platforms. It is not the fastest out there, but it gets the job done and is highly configurable. Even allowing you to create a (very basic) tauri app even if tauri is not setup. @@ -119,7 +116,7 @@ This will do several things: 1. start the JS Framework devserver 2. begin the long process of downloading and compiling the rust libraries 3. open an application window with devtools enabled -4. keep a long-lived console alive +4. keep a long-lived console alive If you change your HTML/CSS/TS/JS, your framework devserver should give you its best shot at instant hot module reloading and you will see the changes instantly. @@ -149,7 +146,7 @@ After some time, the process will end and you can see the results in the `./src- End users will be provided with binaries in ways that are appropriate for their systems. Whether macOS, Linux, or Windows, direct download or store installations - they will be able to follow procedures for installing and removing that they are used to. ## What does the Updating flow look like? -When a new version is ready, the developer publishes the new signed artifacts to a server (that they have configured within `tauri.conf.json`). +When a new version is ready, the developer publishes the new signed artifacts to a server (that they have configured within `tauri.conf.json`). The application can poll this server to see if there is a new release. When there is a new release, the user is prompted to update. The application update is downloaded, verified (checksum & signature), updated, closed, and restarted. diff --git a/README.md b/README.md index 8a8f56f86..18903b7b5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![status](https://img.shields.io/badge/status-stable-blue.svg)](https://github.com/tauri-apps/tauri/tree/dev) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri) -[![test library](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library)](https://github.com/tauri-apps/tauri/actions?query=workflow%3A%22test+library%22) +[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield) [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S) [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app) @@ -25,12 +25,12 @@ ### Tooling -| Component | Description | Version | Lin | Win | Mac | -| --------------------------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------ | --- | --- | --- | -| [**bundler**](https://github.com/tauri-apps/tauri/tree/dev/tooling/bundler) | manufacture the final binaries | [![](https://img.shields.io/crates/v/tauri-bundler.svg)](https://crates.io/crates/tauri-bundler) | ✅ | ✅ | ✅ | -| [**api.js**](https://github.com/tauri-apps/tauri/tree/dev/tooling/api) | JS API for interaction with Rust backend | [![](https://img.shields.io/npm/v/@tauri-apps/api.svg)](https://www.npmjs.com/package/@tauri-apps/api) | ✅ | ✅ | ✅ | -| [**cli.rs**](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli) | create, develop and build apps | [![](https://img.shields.io/crates/v/tauri-cli.svg)](https://crates.io/crates/tauri-cli) | ✅ | ✅ | ✅ | -| [**cli.js**](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli/node) | Node.js CLI wrapper for cli.rs | [![](https://img.shields.io/npm/v/@tauri-apps/cli.svg)](https://www.npmjs.com/package/@tauri-apps/cli) | ✅ | ✅ | ✅ | +| Component | Description | Version | Lin | Win | Mac | +| ------------------------------------------------------------------------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------ | --- | --- | --- | +| [**bundler**](https://github.com/tauri-apps/tauri/tree/dev/tooling/bundler) | manufacture the final binaries | [![](https://img.shields.io/crates/v/tauri-bundler.svg)](https://crates.io/crates/tauri-bundler) | ✅ | ✅ | ✅ | +| [**tauri-cli**](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli) | create, develop and build apps | [![](https://img.shields.io/crates/v/tauri-cli.svg)](https://crates.io/crates/tauri-cli) | ✅ | ✅ | ✅ | +| [**@tauri-apps/cli**](https://github.com/tauri-apps/tauri/tree/dev/tooling/cli/node) | Node.js CLI wrapper for `tauri-cli` | [![](https://img.shields.io/npm/v/@tauri-apps/cli.svg)](https://www.npmjs.com/package/@tauri-apps/cli) | ✅ | ✅ | ✅ | +| [**@tauri-apps/api**](https://github.com/tauri-apps/tauri/tree/dev/tooling/api) | JS API for interaction with Rust backend | [![](https://img.shields.io/npm/v/@tauri-apps/api.svg)](https://www.npmjs.com/package/@tauri-apps/api) | ✅ | ✅ | ✅ | ### Utilities and Plugins @@ -75,6 +75,8 @@ For **running** Tauri apps we support the below configurations (these are automa - `webkit2gtk`, `gtk3`, `libayatana-appindicator`1 - Fedora (latest 2 versions) with the following packages installed: - `webkit2gtk3`, `gtk3`, `libappindicator-gtk3`1 +- Void with the following packages installed: + - `webkit2gtk`, `gtk+3`, `libappindicator`1 1 `appindicator` is only required if system trays are used diff --git a/core/tauri-build/README.md b/core/tauri-build/README.md index 4d24c8ea2..835e9b932 100644 --- a/core/tauri-build/README.md +++ b/core/tauri-build/README.md @@ -4,7 +4,7 @@ [![status](https://img.shields.io/badge/status-stable-blue.svg)](https://github.com/tauri-apps/tauri/tree/dev) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri) -[![test library](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library)](https://github.com/tauri-apps/tauri/actions?query=workflow%3A%22test+library%22) +[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield) [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S) [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app) diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 31f94be6e..47881f63b 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -111,10 +111,28 @@ pub struct WindowsAttributes { /// A string containing an [application manifest] to be included with the application on Windows. /// /// Defaults to: - /// ```ignore + /// ```text #[doc = include_str!("window-app-manifest.xml")] /// ``` /// + /// ## Warning + /// + /// if you are using tauri's dialog APIs, you need to specify a dependency on Common Control v6 by adding the following to your custom manifest: + /// ```text + /// + /// + /// + /// + /// + /// ``` + /// /// [application manifest]: https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests app_manifest: Option, } @@ -135,39 +153,57 @@ impl WindowsAttributes { self } - /// Sets the Windows app [manifest]. + /// Sets the [application manifest] to be included with the application on Windows. + /// + /// Defaults to: + /// ```text + #[doc = include_str!("window-app-manifest.xml")] + /// ``` + /// + /// ## Warning + /// + /// if you are using tauri's dialog APIs, you need to specify a dependency on Common Control v6 by adding the following to your custom manifest: + /// ```text + /// + /// + /// + /// + /// + /// ``` /// /// # Example /// /// The following manifest will brand the exe as requesting administrator privileges. /// Thus, everytime it is executed, a Windows UAC dialog will appear. /// - /// Note that you can move the manifest contents to a separate file and use `include_str!("manifest.xml")` - /// instead of the inline string. - /// /// ```rust,no_run /// let mut windows = tauri_build::WindowsAttributes::new(); /// windows = windows.app_manifest(r#" /// - /// - /// - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// + /// + /// /// /// "#); - /// tauri_build::try_build( - /// tauri_build::Attributes::new().windows_attributes(windows) - /// ).expect("failed to run build script"); + /// let attrs = tauri_build::Attributes::new().windows_attributes(windows); + /// tauri_build::try_build(attrs).expect("failed to run build script"); /// ``` /// - /// Defaults to: - /// ```ignore - #[doc = include_str!("window-app-manifest.xml")] + /// Note that you can move the manifest contents to a separate file and use `include_str!("manifest.xml")` + /// instead of the inline string. + /// /// [manifest]: https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests - /// ``` #[must_use] pub fn app_manifest>(mut self, manifest: S) -> Self { self.app_manifest = Some(manifest.as_ref().to_string()); @@ -380,26 +416,32 @@ pub fn try_build(attributes: Attributes) -> Result<()> { res.set_manifest(include_str!("window-app-manifest.xml")); } - if let Some(version) = &config.package.version { - if let Ok(v) = Version::parse(version) { + if let Some(version_str) = &config.package.version { + if let Ok(v) = Version::parse(version_str) { let version = v.major << 48 | v.minor << 32 | v.patch << 16; res.set_version_info(VersionInfo::FILEVERSION, version); res.set_version_info(VersionInfo::PRODUCTVERSION, version); } - res.set("FileVersion", version); - res.set("ProductVersion", version); + res.set("FileVersion", version_str); + res.set("ProductVersion", version_str); + + if let Some(product_name) = &config.package.product_name { + res.set("ProductName", product_name); + } + if let Some(short_description) = &config.tauri.bundle.short_description { + res.set("FileDescription", short_description); + } + if let Some(copyright) = &config.tauri.bundle.copyright { + res.set("LegalCopyright", copyright); + } + res.set_icon_with_id(&window_icon_path.display().to_string(), "32512"); + res.compile().with_context(|| { + format!( + "failed to compile `{}` into a Windows Resource file during tauri-build", + window_icon_path.display() + ) + })?; } - if let Some(product_name) = &config.package.product_name { - res.set("ProductName", product_name); - res.set("FileDescription", product_name); - } - res.set_icon_with_id(&window_icon_path.display().to_string(), "32512"); - res.compile().with_context(|| { - format!( - "failed to compile `{}` into a Windows Resource file during tauri-build", - window_icon_path.display() - ) - })?; } else { return Err(anyhow!(format!( "`{}` not found; required for generating a Windows Resource file during tauri-build", diff --git a/core/tauri-codegen/README.md b/core/tauri-codegen/README.md index 15a1c7b8a..3f3e15990 100644 --- a/core/tauri-codegen/README.md +++ b/core/tauri-codegen/README.md @@ -6,7 +6,7 @@ [![Chat Server](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/SpmNs4S) [![devto](https://img.shields.io/badge/blog-dev.to-black.svg)](https://dev.to/tauri) -![](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library) +[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml) [![devto](https://img.shields.io/badge/documentation-site-purple.svg)](https://tauri.app) [![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation) diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index 4861d4728..bce0e3a3d 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -502,6 +502,13 @@ fn png_icon>( let mut reader = decoder .read_info() .unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e)); + + let (color_type, _) = reader.output_color_type(); + + if color_type != png::ColorType::Rgba { + panic!("icon {} is not RGBA", path.display()); + } + let mut buffer: Vec = Vec::new(); while let Ok(Some(row)) = reader.next_row() { buffer.extend(row.data()); diff --git a/core/tauri-config-schema/Cargo.toml b/core/tauri-config-schema/Cargo.toml index 0f3f670ba..64188842f 100644 --- a/core/tauri-config-schema/Cargo.toml +++ b/core/tauri-config-schema/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tauri-tauri-config-schema" +name = "tauri-config-schema" version = "0.0.0" edition = "2021" publish = false diff --git a/core/tauri-config-schema/schema.json b/core/tauri-config-schema/schema.json index 24e4c6283..2e87ffe2e 100644 --- a/core/tauri-config-schema/schema.json +++ b/core/tauri-config-schema/schema.json @@ -125,7 +125,7 @@ "pattern": "^[^/\\:*?\"<>|]+$" }, "version": { - "description": "App version. It is a semver version number or a path to a `package.json` file containing the `version` field.", + "description": "App version. It is a semver version number or a path to a `package.json` file containing the `version` field. If removed the version number from `Cargo.toml` is used.", "default": null, "type": [ "string", @@ -386,7 +386,22 @@ "format": "double" }, "resizable": { - "description": "Whether the window is resizable or not.", + "description": "Whether the window is resizable or not. When resizable is set to false, native window's maximize button is automatically disabled.", + "default": true, + "type": "boolean" + }, + "maximizable": { + "description": "Whether the window's native maximize button is enabled or not. If resizable is set to false, this setting is ignored.\n\n## Platform-specific\n\n- **macOS:** Disables the \"zoom\" button in the window titlebar, which is also used to enter fullscreen mode. - **Linux / iOS / Android:** Unsupported.", + "default": true, + "type": "boolean" + }, + "minimizable": { + "description": "Whether the window's native minimize button is enabled or not.\n\n## Platform-specific\n\n- **Linux / iOS / Android:** Unsupported.", + "default": true, + "type": "boolean" + }, + "closable": { + "description": "Whether the window's native close button is enabled or not.\n\n## Platform-specific\n\n- **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.", "default": true, "type": "boolean" }, @@ -1121,6 +1136,13 @@ "additionalProperties": { "type": "string" } + }, + "desktopTemplate": { + "description": "Path to a custom desktop file Handlebars template.\n\nAvailable variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.", + "type": [ + "string", + "null" + ] } }, "additionalProperties": false @@ -1512,6 +1534,13 @@ "description": "Configuration for the Installer bundle using NSIS.", "type": "object", "properties": { + "template": { + "description": "A custom .nsi template to use.", + "type": [ + "string", + "null" + ] + }, "license": { "description": "The path to the license file to render on the installer.", "type": [ @@ -1559,6 +1588,16 @@ "type": "string" } }, + "customLanguageFiles": { + "description": "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.\n\nSee for an example `.nsh` file.\n\n**Note**: the key must be a valid NSIS language and it must be added to [`NsisConfig`] languages array,", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "string" + } + }, "displayLanguageSelector": { "description": "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.", "default": false, @@ -1676,7 +1715,7 @@ ] }, { - "description": "The quiet mode means there's no user interaction required. Requires admin privileges if the installer does (WiX).", + "description": "The quiet mode means there's no user interaction required. Requires admin privileges if the installer does.", "type": "string", "enum": [ "quiet" @@ -1889,6 +1928,13 @@ "items": { "type": "string" } + }, + "requireLiteralLeadingDot": { + "description": "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.\n\nDefaults to `false` on Unix systems and `true` on Windows", + "type": [ + "boolean", + "null" + ] } } } diff --git a/core/tauri-macros/README.md b/core/tauri-macros/README.md index ca27b3b05..64eb56123 100644 --- a/core/tauri-macros/README.md +++ b/core/tauri-macros/README.md @@ -4,7 +4,7 @@ [![status](https://img.shields.io/badge/status-stable-blue.svg)](https://github.com/tauri-apps/tauri/tree/dev) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri) -[![test library](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library)](https://github.com/tauri-apps/tauri/actions?query=workflow%3A%22test+library%22) +[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield) [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S) [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app) diff --git a/core/tauri-macros/src/command/wrapper.rs b/core/tauri-macros/src/command/wrapper.rs index d9ab44b6c..19dbb2c37 100644 --- a/core/tauri-macros/src/command/wrapper.rs +++ b/core/tauri-macros/src/command/wrapper.rs @@ -5,7 +5,7 @@ use heck::{ToLowerCamelCase, ToSnakeCase}; use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, quote_spanned}; use syn::{ ext::IdentExt, parse::{Parse, ParseStream}, @@ -116,6 +116,63 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { resolver: format_ident!("__tauri_resolver__"), }; + // Tauri currently doesn't support async commands that take a reference as input and don't return + // a result. See: https://github.com/tauri-apps/tauri/issues/2533 + // + // For now, we provide an informative error message to the user in that case. Once #2533 is + // resolved, this check can be removed. + let mut async_command_check = TokenStream2::new(); + if function.sig.asyncness.is_some() { + // This check won't catch all possible problems but it should catch the most common ones. + let mut ref_argument_span = None; + + for arg in &function.sig.inputs { + if let syn::FnArg::Typed(pat) = arg { + match &*pat.ty { + syn::Type::Reference(_) => { + ref_argument_span = Some(pat.span()); + } + syn::Type::Path(path) => { + // Check if the type contains a lifetime argument + let last = path.path.segments.last().unwrap(); + if let syn::PathArguments::AngleBracketed(args) = &last.arguments { + if args + .args + .iter() + .any(|arg| matches!(arg, syn::GenericArgument::Lifetime(_))) + { + ref_argument_span = Some(pat.span()); + } + } + } + _ => {} + } + + if let Some(span) = ref_argument_span { + if let syn::ReturnType::Type(_, return_type) = &function.sig.output { + // To check if the return type is `Result` we require it to check a trait that is + // only implemented by `Result`. That way we don't exclude renamed result types + // which we wouldn't otherwise be able to detect purely from the token stream. + // The "error message" displayed to the user is simply the trait name. + async_command_check = quote_spanned! {return_type.span() => + #[allow(unreachable_code, clippy::diverging_sub_expression)] + const _: () = if false { + trait AsyncCommandMustReturnResult {} + impl AsyncCommandMustReturnResult for ::std::result::Result {} + let _check: #return_type = unreachable!(); + let _: &dyn AsyncCommandMustReturnResult = &_check; + }; + }; + } else { + return quote_spanned! { + span => compile_error!("async commands that contain references as inputs must return a `Result`"); + }.into(); + } + } + } + } + } + // body to the command wrapper or a `compile_error!` of an error occurred while parsing it. let (body, attributes) = syn::parse::(attributes) .map(|mut attrs| { @@ -141,6 +198,8 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { // Rely on rust 2018 edition to allow importing a macro from a path. quote!( + #async_command_check + #function #maybe_macro_export diff --git a/core/tauri-runtime-wry/README.md b/core/tauri-runtime-wry/README.md index a136160c7..0b1c2c5c0 100644 --- a/core/tauri-runtime-wry/README.md +++ b/core/tauri-runtime-wry/README.md @@ -6,8 +6,7 @@ [![Chat Server](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/SpmNs4S) [![devto](https://img.shields.io/badge/blog-dev.to-black.svg)](https://dev.to/tauri) -![](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library -) +[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml) [![devto](https://img.shields.io/badge/documentation-site-purple.svg)](https://tauri.app) [![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation) diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index d9b4ce519..a1c3b4bca 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -69,6 +69,8 @@ use wry::{ pub use wry; pub use wry::application::window::{Window, WindowBuilder as WryWindowBuilder, WindowId}; +pub use wry::webview::webview_version; + #[cfg(windows)] use wry::webview::WebviewExtWindows; #[cfg(target_os = "android")] @@ -204,7 +206,6 @@ impl Context { impl Context { fn create_webview(&self, pending: PendingWindow>) -> Result>> { let label = pending.label.clone(); - let current_url = pending.current_url.clone(); let menu_ids = pending.menu_ids.clone(); let context = self.clone(); let window_id = rand::random(); @@ -225,7 +226,6 @@ impl Context { }; Ok(DetachedWindow { label, - current_url, dispatcher, menu_ids, }) @@ -806,6 +806,21 @@ impl WindowBuilder for WindowBuilderWrapper { 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 @@ -1056,8 +1071,12 @@ pub enum WindowMessage { IsFullscreen(Sender), IsMinimized(Sender), IsMaximized(Sender), + IsFocused(Sender), IsDecorated(Sender), IsResizable(Sender), + IsMaximizable(Sender), + IsMinimizable(Sender), + IsClosable(Sender), IsVisible(Sender), Title(Sender), IsMenuVisible(Sender), @@ -1078,6 +1097,9 @@ pub enum WindowMessage { Center, RequestUserAttention(Option), SetResizable(bool), + SetMaximizable(bool), + SetMinimizable(bool), + SetClosable(bool), SetTitle(String), Maximize, Unmaximize, @@ -1279,6 +1301,10 @@ impl Dispatch for WryDispatcher { 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) @@ -1289,6 +1315,21 @@ impl Dispatch for WryDispatcher { 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) } @@ -1380,6 +1421,27 @@ impl Dispatch for WryDispatcher { ) } + fn set_maximizable(&self, maximizable: bool) -> Result<()> { + send_user_message( + &self.context, + Message::Window(self.window_id, WindowMessage::SetMaximizable(maximizable)), + ) + } + + fn set_minimizable(&self, minimizable: bool) -> Result<()> { + send_user_message( + &self.context, + Message::Window(self.window_id, WindowMessage::SetMinimizable(minimizable)), + ) + } + + fn set_closable(&self, closable: bool) -> Result<()> { + send_user_message( + &self.context, + Message::Window(self.window_id, WindowMessage::SetClosable(closable)), + ) + } + fn set_title>(&self, title: S) -> Result<()> { send_user_message( &self.context, @@ -1940,7 +2002,6 @@ impl Runtime for Wry { fn create_window(&self, pending: PendingWindow) -> Result> { let label = pending.label.clone(); - let current_url = pending.current_url.clone(); let menu_ids = pending.menu_ids.clone(); let window_id = rand::random(); @@ -1966,7 +2027,6 @@ impl Runtime for Wry { Ok(DetachedWindow { label, - current_url, dispatcher, menu_ids, }) @@ -2320,8 +2380,12 @@ fn handle_user_message( 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(), + WindowMessage::IsFocused(tx) => tx.send(window.is_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::IsMenuVisible(tx) => tx.send(window.is_menu_visible()).unwrap(), @@ -2354,6 +2418,9 @@ fn handle_user_message( window.request_user_attention(request_type.map(|r| r.0)); } WindowMessage::SetResizable(resizable) => window.set_resizable(resizable), + 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), @@ -2936,7 +3003,7 @@ fn create_webview( mut window_builder, ipc_handler, label, - current_url, + url, menu_ids, #[cfg(target_os = "android")] on_webview_created, @@ -2986,7 +3053,7 @@ fn create_webview( } let mut webview_builder = WebViewBuilder::new(window) .map_err(|e| Error::CreateWebview(Box::new(e)))? - .with_url(current_url.lock().unwrap().as_str()) + .with_url(&url) .unwrap() // safe to unwrap because we validate the URL beforehand .with_transparent(is_window_transparent) .with_accept_first_mouse(webview_attributes.accept_first_mouse); @@ -3021,7 +3088,6 @@ fn create_webview( webview_builder = webview_builder.with_ipc_handler(create_ipc_handler( context, label.clone(), - current_url, menu_ids, handler, )); @@ -3144,7 +3210,6 @@ fn create_webview( fn create_ipc_handler( context: Context, label: String, - current_url: Arc>, menu_ids: Arc>>, handler: WebviewIpcHandler>, ) -> Box { @@ -3152,7 +3217,6 @@ fn create_ipc_handler( let window_id = context.webview_id_map.get(&window.id()).unwrap(); handler( DetachedWindow { - current_url: current_url.clone(), dispatcher: WryDispatcher { window_id, context: context.clone(), diff --git a/core/tauri-runtime/README.md b/core/tauri-runtime/README.md index 8f49ee2fe..4bb9b97f5 100644 --- a/core/tauri-runtime/README.md +++ b/core/tauri-runtime/README.md @@ -6,7 +6,7 @@ [![Chat Server](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/SpmNs4S) [![devto](https://img.shields.io/badge/blog-dev.to-black.svg)](https://dev.to/tauri) -![](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library) +[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml) [![devto](https://img.shields.io/badge/documentation-site-purple.svg)](https://tauri.app) [![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation) diff --git a/core/tauri-runtime/src/lib.rs b/core/tauri-runtime/src/lib.rs index 8631ea70b..5cd05cac3 100644 --- a/core/tauri-runtime/src/lib.rs +++ b/core/tauri-runtime/src/lib.rs @@ -555,12 +555,36 @@ pub trait Dispatch: Debug + Clone + Send + Sync + Sized + 'static /// Gets the window's current maximized state. fn is_maximized(&self) -> Result; + /// Gets the window's current focus state. + fn is_focused(&self) -> Result; + /// Gets the window’s current decoration state. fn is_decorated(&self) -> Result; /// Gets the window’s current resizable state. fn is_resizable(&self) -> Result; + /// Gets the window's native maximize button state. + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + fn is_maximizable(&self) -> Result; + + /// Gets the window's native minize button state. + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + fn is_minimizable(&self) -> Result; + + /// Gets the window's native close button state. + /// + /// ## Platform-specific + /// + /// - **iOS / Android:** Unsupported. + fn is_closable(&self) -> Result; + /// Gets the window's current visibility state. fn is_visible(&self) -> Result; /// Gets the window's current title. @@ -619,6 +643,30 @@ pub trait Dispatch: Debug + Clone + Send + Sync + Sized + 'static /// Updates the window resizable flag. fn set_resizable(&self, resizable: bool) -> Result<()>; + /// Updates the window's native maximize button state. + /// + /// ## Platform-specific + /// + /// - **macOS:** Disables the "zoom" button in the window titlebar, which is also used to enter fullscreen mode. + /// - **Linux / iOS / Android:** Unsupported. + fn set_maximizable(&self, maximizable: bool) -> Result<()>; + + /// Updates the window's native minimize button state. + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + fn set_minimizable(&self, minimizable: bool) -> Result<()>; + + /// Updates the window's native close button state. + /// + /// ## 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. + fn set_closable(&self, closable: bool) -> Result<()>; + /// Updates the window title. fn set_title>(&self, title: S) -> Result<()>; diff --git a/core/tauri-runtime/src/webview.rs b/core/tauri-runtime/src/webview.rs index f3193e062..5103b28e9 100644 --- a/core/tauri-runtime/src/webview.rs +++ b/core/tauri-runtime/src/webview.rs @@ -32,6 +32,26 @@ pub struct WebviewAttributes { pub window_effects: Option, } +impl From<&WindowConfig> for WebviewAttributes { + fn from(config: &WindowConfig) -> Self { + let mut builder = Self::new(config.url.clone()); + builder = builder.accept_first_mouse(config.accept_first_mouse); + if !config.file_drop_enabled { + builder = builder.disable_file_drop_handler(); + } + if let Some(user_agent) = &config.user_agent { + builder = builder.user_agent(user_agent); + } + if let Some(additional_browser_args) = &config.additional_browser_args { + builder = builder.additional_browser_args(additional_browser_args); + } + if let Some(effects) = &config.window_effects { + builder = builder.window_effects(effects.clone()); + } + builder + } +} + impl WebviewAttributes { /// Initializes the default attributes for a webview. pub fn new(url: WindowUrl) -> Self { @@ -149,9 +169,38 @@ pub trait WindowBuilder: WindowBuilderBase { fn max_inner_size(self, max_width: f64, max_height: f64) -> Self; /// Whether the window is resizable or not. + /// When resizable is set to false, native window's maximize button is automatically disabled. #[must_use] fn resizable(self, resizable: bool) -> Self; + /// 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. + #[must_use] + fn maximizable(self, maximizable: bool) -> Self; + + /// Whether the window's native minimize button is enabled or not. + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + #[must_use] + fn minimizable(self, minimizable: bool) -> Self; + + /// 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. + #[must_use] + fn closable(self, closable: bool) -> Self; + /// The title of the window in the title bar. #[must_use] fn title>(self, title: S) -> Self; diff --git a/core/tauri-runtime/src/window.rs b/core/tauri-runtime/src/window.rs index fee411ef5..fbb7899f9 100644 --- a/core/tauri-runtime/src/window.rs +++ b/core/tauri-runtime/src/window.rs @@ -24,6 +24,8 @@ use std::{ type UriSchemeProtocol = dyn Fn(&HttpRequest) -> Result> + Send + Sync + 'static; +type WebResourceRequestHandler = dyn Fn(&HttpRequest, &mut HttpResponse) + Send + Sync; + /// UI scaling utilities. pub mod dpi; @@ -183,7 +185,7 @@ impl<'de> Deserialize<'de> for CursorIcon { "grab" => CursorIcon::Grab, "grabbing" => CursorIcon::Grabbing, "allscroll" => CursorIcon::AllScroll, - "zoomun" => CursorIcon::ZoomIn, + "zoomin" => CursorIcon::ZoomIn, "zoomout" => CursorIcon::ZoomOut, "eresize" => CursorIcon::EResize, "nresize" => CursorIcon::NResize, @@ -233,13 +235,15 @@ pub struct PendingWindow> { /// A handler to decide if incoming url is allowed to navigate. pub navigation_handler: Option bool + Send>>, - /// The current webview URL. - pub current_url: Arc>, + /// The resolved URL to load on the webview. + pub url: String, #[cfg(target_os = "android")] #[allow(clippy::type_complexity)] pub on_webview_created: Option) -> Result<(), jni::errors::Error> + Send>>, + + pub web_resource_request_handler: Option>, } pub fn is_label_valid(label: &str) -> bool { @@ -278,9 +282,10 @@ impl> PendingWindow { ipc_handler: None, menu_ids: Arc::new(Mutex::new(menu_ids)), navigation_handler: Default::default(), - current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())), + url: "tauri://localhost".to_string(), #[cfg(target_os = "android")] on_webview_created: None, + web_resource_request_handler: Default::default(), }) } } @@ -309,9 +314,10 @@ impl> PendingWindow { ipc_handler: None, menu_ids: Arc::new(Mutex::new(menu_ids)), navigation_handler: Default::default(), - current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())), + url: "tauri://localhost".to_string(), #[cfg(target_os = "android")] on_webview_created: None, + web_resource_request_handler: Default::default(), }) } } @@ -354,9 +360,6 @@ impl> PendingWindow { /// A webview window that is not yet managed by Tauri. #[derive(Debug)] pub struct DetachedWindow> { - /// The current webview URL. - pub current_url: Arc>, - /// Name of the window pub label: String, @@ -370,7 +373,6 @@ pub struct DetachedWindow> { impl> Clone for DetachedWindow { fn clone(&self) -> Self { Self { - current_url: self.current_url.clone(), label: self.label.clone(), dispatcher: self.dispatcher.clone(), menu_ids: self.menu_ids.clone(), diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml index 919c04bb0..dda6ad6dc 100644 --- a/core/tauri-utils/Cargo.toml +++ b/core/tauri-utils/Cargo.toml @@ -26,7 +26,7 @@ html5ever = "0.25" proc-macro2 = { version = "1", optional = true } quote = { version = "1", optional = true } schemars = { version = "0.8", features = [ "url" ], optional = true } -serde_with = "2" +serde_with = "3" aes-gcm = { version = "0.10", optional = true } getrandom = { version = "0.2", optional = true, features = [ "std" ] } serialize-to-javascript = { version = "=0.1.1", optional = true } diff --git a/core/tauri-utils/README.md b/core/tauri-utils/README.md index 4ba44258f..fcc3d5956 100644 --- a/core/tauri-utils/README.md +++ b/core/tauri-utils/README.md @@ -4,7 +4,7 @@ [![status](https://img.shields.io/badge/status-stable-blue.svg)](https://github.com/tauri-apps/tauri/tree/dev) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri) -[![test library](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library)](https://github.com/tauri-apps/tauri/actions?query=workflow%3A%22test+library%22) +[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield) [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S) [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app) diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 6a73961af..03cdd4207 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -38,6 +38,10 @@ use crate::{TitleBarStyle, WindowEffect, WindowEffectState}; pub use self::parse::parse; +fn default_true() -> bool { + true +} + /// An URL to open on a Tauri webview window. #[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)] #[cfg_attr(feature = "schema", derive(JsonSchema))] @@ -272,6 +276,10 @@ pub struct DebConfig { /// 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`. + pub desktop_template: Option, } fn de_minimum_system_version<'de, D>(deserializer: D) -> Result, D::Error> @@ -435,6 +443,8 @@ pub struct WixConfig { #[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 the license file to render on the installer. pub license: Option, /// The path to a bitmap file to display on the header of installers pages. @@ -459,6 +469,13 @@ pub struct NsisConfig { /// /// 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 [`NsisConfig`] 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")] @@ -542,9 +559,7 @@ pub enum WebviewInstallMode { impl Default for WebviewInstallMode { fn default() -> Self { - Self::DownloadBootstrapper { - silent: default_true(), - } + Self::DownloadBootstrapper { silent: true } } } @@ -602,7 +617,7 @@ impl Default for WindowsConfig { tsp: false, webview_install_mode: Default::default(), webview_fixed_runtime_path: None, - allow_downgrades: default_true(), + allow_downgrades: true, wix: None, nsis: None, } @@ -818,9 +833,34 @@ pub struct WindowConfig { /// The max window height. #[serde(alias = "max-height")] pub max_height: Option, - /// Whether the window is resizable or not. + /// 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, @@ -906,7 +946,7 @@ impl Default for WindowConfig { label: default_window_label(), url: WindowUrl::default(), user_agent: None, - file_drop_enabled: default_true(), + file_drop_enabled: true, center: false, x: None, y: None, @@ -916,14 +956,17 @@ impl Default for WindowConfig { min_height: None, max_width: None, max_height: None, - resizable: default_true(), + resizable: true, + maximizable: true, + minimizable: true, + closable: true, title: default_title(), fullscreen: false, focus: false, transparent: false, maximized: false, - visible: default_true(), - decorations: default_true(), + visible: true, + decorations: true, always_on_top: false, content_protected: false, skip_taskbar: false, @@ -951,10 +994,6 @@ fn default_height() -> f64 { 600f64 } -fn default_true() -> bool { - true -} - fn default_title() -> String { "Tauri App".to_string() } @@ -1127,12 +1166,13 @@ pub struct RemoteDomainAccessScope { /// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, /// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`. #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[cfg_attr(feature = "schema", derive(JsonSchema))] #[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)] @@ -1141,6 +1181,16 @@ pub enum FsScope { /// 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 `false` on Unix systems and `true` on Windows + // dotfiles are not supposed to be exposed by default on unix + #[serde(alias = "require-literal-leading-dot")] + require_literal_leading_dot: Option, }, } @@ -1329,7 +1379,7 @@ pub enum WindowsUpdateInstallMode { /// Specifies there's a basic UI during the installation process, including a final dialog box at the end. BasicUi, /// The quiet mode means there's no user interaction required. - /// Requires admin privileges if the installer does (WiX). + /// Requires admin privileges if the installer does. Quiet, /// Specifies unattended mode, which means the installation only shows a progress bar. Passive, @@ -1346,6 +1396,15 @@ impl WindowsUpdateInstallMode { Self::Passive => &["/passive"], } } + + /// Returns the associated nsis arguments. + pub fn nsis_args(&self) -> &'static [&'static str] { + match self { + Self::Passive => &["/P", "/R"], + Self::Quiet => &["/S", "/R"], + _ => &[], + } + } } impl Display for WindowsUpdateInstallMode { @@ -1666,7 +1725,7 @@ pub struct PackageConfig { #[serde(alias = "product-name")] #[cfg_attr(feature = "schema", validate(regex(pattern = "^[^/\\:*?\"<>|]+$")))] pub product_name: Option, - /// App version. It is a semver version number or a path to a `package.json` file containing the `version` field. + /// App version. It is a semver version number or a path to a `package.json` file containing the `version` field. If removed the version number from `Cargo.toml` is used. #[serde(deserialize_with = "version_deserializer", default)] pub version: Option, } @@ -2081,6 +2140,9 @@ mod build { let max_width = opt_lit(self.max_width.as_ref()); let max_height = opt_lit(self.max_height.as_ref()); let resizable = self.resizable; + let maximizable = self.maximizable; + let minimizable = self.minimizable; + let closable = self.closable; let title = str_lit(&self.title); let fullscreen = self.fullscreen; let focus = self.focus; @@ -2117,6 +2179,9 @@ mod build { max_width, max_height, resizable, + maximizable, + minimizable, + closable, title, fullscreen, focus, @@ -2435,10 +2500,11 @@ mod build { let allowed_paths = vec_lit(allow, path_buf_lit); quote! { #prefix::AllowedPaths(#allowed_paths) } } - Self::Scope { allow, deny } => { + Self::Scope { allow, deny , require_literal_leading_dot} => { let allow = vec_lit(allow, path_buf_lit); let deny = vec_lit(deny, path_buf_lit); - quote! { #prefix::Scope { allow: #allow, deny: #deny } } + let require_literal_leading_dot = opt_lit(require_literal_leading_dot.as_ref()); + quote! { #prefix::Scope { allow: #allow, deny: #deny, require_literal_leading_dot: #require_literal_leading_dot } } } }); } diff --git a/core/tauri-utils/src/mime_type.rs b/core/tauri-utils/src/mime_type.rs index d3ae5f6d9..ba8a2571b 100644 --- a/core/tauri-utils/src/mime_type.rs +++ b/core/tauri-utils/src/mime_type.rs @@ -46,6 +46,11 @@ impl std::fmt::Display for MimeType { impl MimeType { /// parse a URI suffix to convert text/plain mimeType to their actual web compatible mimeType. pub fn parse_from_uri(uri: &str) -> MimeType { + Self::parse_from_uri_with_fallback(uri, Self::Html) + } + + /// parse a URI suffix to convert text/plain mimeType to their actual web compatible mimeType with specified fallback for unknown file extensions. + pub fn parse_from_uri_with_fallback(uri: &str, fallback: MimeType) -> MimeType { let suffix = uri.split('.').last(); match suffix { Some("bin") => Self::OctetStream, @@ -61,15 +66,19 @@ impl MimeType { Some("svg") => Self::Svg, Some("mp4") => Self::Mp4, // Assume HTML when a TLD is found for eg. `wry:://tauri.app` | `wry://hello.com` - Some(_) => Self::Html, - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types + Some(_) => fallback, // using octet stream according to this: + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types None => Self::OctetStream, } } /// infer mimetype from content (or) URI if needed. pub fn parse(content: &[u8], uri: &str) -> String { + Self::parse_with_fallback(content, uri, Self::Html) + } + /// infer mimetype from content (or) URI if needed with specified fallback for unknown file extensions. + pub fn parse_with_fallback(content: &[u8], uri: &str, fallback: MimeType) -> String { let mime = if uri.ends_with(".svg") { // when reading svg, we can't use `infer` None @@ -78,8 +87,10 @@ impl MimeType { }; match mime { - Some(mime) if mime == MIMETYPE_PLAIN => Self::parse_from_uri(uri).to_string(), - None => Self::parse_from_uri(uri).to_string(), + Some(mime) if mime == MIMETYPE_PLAIN => { + Self::parse_from_uri_with_fallback(uri, fallback).to_string() + } + None => Self::parse_from_uri_with_fallback(uri, fallback).to_string(), Some(mime) => mime.to_string(), } } diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 69cc56220..3145ce8fa 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -114,7 +114,6 @@ serde_json = "1.0" tauri = { path = ".", default-features = false, features = [ "wry" ] } tokio = { version = "1", features = [ "full" ] } cargo_toml = "0.15" -winnow = "=0.4.1" [features] default = [ "wry", "compression", "objc-exception" ] diff --git a/core/tauri/README.md b/core/tauri/README.md index e3876c10f..f0cf6e5a8 100644 --- a/core/tauri/README.md +++ b/core/tauri/README.md @@ -4,7 +4,7 @@ [![status](https://img.shields.io/badge/status-stable-blue.svg)](https://github.com/tauri-apps/tauri/tree/dev) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri) -[![test library](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library)](https://github.com/tauri-apps/tauri/actions?query=workflow%3A%22test+library%22) +[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield) [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S) [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app) diff --git a/core/tauri/src/api/os.rs b/core/tauri/src/api/os.rs new file mode 100644 index 000000000..c5ea565a8 --- /dev/null +++ b/core/tauri/src/api/os.rs @@ -0,0 +1,10 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +//! Types and functions related to operating system operations. + +/// Returns `Some(String)` with a `BCP-47` language tag inside. If the locale couldn’t be obtained, `None` is returned instead. +pub fn locale() -> Option { + sys_locale::get_locale() +} diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 791f2b147..a277246c3 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -1287,25 +1287,8 @@ impl Builder { // set up all the windows defined in the config for config in manager.config().tauri.windows.clone() { - let url = config.url.clone(); let label = config.label.clone(); - let window_effects = config.window_effects.clone(); - - let mut webview_attributes = - WebviewAttributes::new(url).accept_first_mouse(config.accept_first_mouse); - if let Some(ua) = &config.user_agent { - webview_attributes = webview_attributes.user_agent(ua); - } - if let Some(args) = &config.additional_browser_args { - webview_attributes = webview_attributes.additional_browser_args(args); - } - if !config.file_drop_enabled { - webview_attributes = webview_attributes.disable_file_drop_handler(); - } - if let Some(effects) = window_effects { - webview_attributes = webview_attributes.window_effects(effects); - } - + let webview_attributes = WebviewAttributes::from(&config); self.pending_windows.push(PendingWindow::with_config( config, webview_attributes, @@ -1430,10 +1413,9 @@ fn setup(app: &mut App) -> crate::Result<()> { .collect::>(); for pending in pending_windows { - let pending = - app - .manager - .prepare_window(app.handle.clone(), pending, &window_labels, None)?; + let pending = app + .manager + .prepare_window(app.handle.clone(), pending, &window_labels)?; let window_effects = pending.webview_attributes.window_effects.clone(); let detached = if let RuntimeOrDispatch::RuntimeHandle(runtime) = app.handle().runtime() { runtime.create_window(pending)? diff --git a/core/tauri/src/app/tray.rs b/core/tauri/src/app/tray.rs index 605c3181e..b88b932c7 100644 --- a/core/tauri/src/app/tray.rs +++ b/core/tauri/src/app/tray.rs @@ -610,6 +610,19 @@ impl SystemTrayHandle { panic!("item id not found") } + /// Attempts to get a handle to the menu item that has the specified `id`, return an error if `id` is not found. + pub fn try_get_item(&self, id: MenuIdRef<'_>) -> Option> { + self + .ids + .lock() + .unwrap() + .iter() + .find(|i| i.1 == id) + .map(|i| SystemTrayMenuItemHandle { + id: *i.0, + tray_handler: self.inner.clone(), + }) + } /// Updates the tray icon. pub fn set_icon(&self, icon: Icon) -> crate::Result<()> { self.inner.set_icon(icon.try_into()?).map_err(Into::into) diff --git a/core/tauri/src/asset_protocol.rs b/core/tauri/src/asset_protocol.rs new file mode 100644 index 000000000..ec961a9a1 --- /dev/null +++ b/core/tauri/src/asset_protocol.rs @@ -0,0 +1,226 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use crate::path::SafePathBuf; +use crate::scope::FsScope; +use rand::RngCore; +use std::io::SeekFrom; +use tauri_runtime::http::HttpRange; +use tauri_runtime::http::{ + header::*, status::StatusCode, MimeType, Request, Response, ResponseBuilder, +}; +use tauri_utils::debug_eprintln; +use tokio::fs::File; +use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; +use url::Position; +use url::Url; + +pub fn asset_protocol_handler( + request: &Request, + scope: FsScope, + window_origin: String, +) -> Result> { + let parsed_path = Url::parse(request.uri())?; + let filtered_path = &parsed_path[..Position::AfterPath]; + let path = filtered_path + .strip_prefix("asset://localhost/") + // the `strip_prefix` only returns None when a request is made to `https://tauri.$P` on Windows + // where `$P` is not `localhost/*` + .unwrap_or(""); + let path = percent_encoding::percent_decode(path.as_bytes()) + .decode_utf8_lossy() + .to_string(); + + if let Err(e) = SafePathBuf::new(path.clone().into()) { + debug_eprintln!("asset protocol path \"{}\" is not valid: {}", path, e); + return ResponseBuilder::new().status(403).body(Vec::new()); + } + + if !scope.is_allowed(&path) { + debug_eprintln!("asset protocol not configured to allow the path: {}", path); + return ResponseBuilder::new().status(403).body(Vec::new()); + } + + let mut resp = ResponseBuilder::new().header("Access-Control-Allow-Origin", &window_origin); + + let (mut file, len, mime_type, read_bytes) = crate::async_runtime::safe_block_on(async move { + let mut file = File::open(&path).await?; + + // get file length + let len = { + let old_pos = file.stream_position().await?; + let len = file.seek(SeekFrom::End(0)).await?; + file.seek(SeekFrom::Start(old_pos)).await?; + len + }; + + // get file mime type + let (mime_type, read_bytes) = { + let nbytes = len.min(8192); + let mut magic_buf = Vec::with_capacity(nbytes as usize); + let old_pos = file.stream_position().await?; + (&mut file).take(nbytes).read_to_end(&mut magic_buf).await?; + file.seek(SeekFrom::Start(old_pos)).await?; + ( + MimeType::parse(&magic_buf, &path), + // return the `magic_bytes` if we read the whole file + // to avoid reading it again later if this is not a range request + if len < 8192 { Some(magic_buf) } else { None }, + ) + }; + + Ok::<(File, u64, String, Option>), anyhow::Error>((file, len, mime_type, read_bytes)) + })?; + + resp = resp.header(CONTENT_TYPE, &mime_type); + + // handle 206 (partial range) http requests + let response = if let Some(range_header) = request + .headers() + .get("range") + .and_then(|r| r.to_str().map(|r| r.to_string()).ok()) + { + resp = resp.header(ACCEPT_RANGES, "bytes"); + + let not_satisfiable = || { + ResponseBuilder::new() + .status(StatusCode::RANGE_NOT_SATISFIABLE) + .header(CONTENT_RANGE, format!("bytes */{len}")) + .body(vec![]) + }; + + // parse range header + let ranges = if let Ok(ranges) = HttpRange::parse(&range_header, len) { + ranges + .iter() + // map the output to spec range , example: 0-499 + .map(|r| (r.start, r.start + r.length - 1)) + .collect::>() + } else { + return not_satisfiable(); + }; + + /// The Maximum bytes we send in one range + const MAX_LEN: u64 = 1000 * 1024; + + // single-part range header + if ranges.len() == 1 { + let &(start, mut end) = ranges.first().unwrap(); + + // check if a range is not satisfiable + // + // this should be already taken care of by the range parsing library + // but checking here again for extra assurance + if start >= len || end >= len || end < start { + return not_satisfiable(); + } + + // adjust end byte for MAX_LEN + end = start + (end - start).min(len - start).min(MAX_LEN - 1); + + // calculate number of bytes needed to be read + let nbytes = end + 1 - start; + + let buf = crate::async_runtime::safe_block_on(async move { + let mut buf = Vec::with_capacity(nbytes as usize); + file.seek(SeekFrom::Start(start)).await?; + file.take(nbytes).read_to_end(&mut buf).await?; + Ok::, anyhow::Error>(buf) + })?; + + resp = resp.header(CONTENT_RANGE, format!("bytes {start}-{end}/{len}")); + resp = resp.header(CONTENT_LENGTH, end + 1 - start); + resp = resp.status(StatusCode::PARTIAL_CONTENT); + resp.body(buf) + } else { + let ranges = ranges + .iter() + .filter_map(|&(start, mut end)| { + // filter out unsatisfiable ranges + // + // this should be already taken care of by the range parsing library + // but checking here again for extra assurance + if start >= len || end >= len || end < start { + None + } else { + // adjust end byte for MAX_LEN + end = start + (end - start).min(len - start).min(MAX_LEN - 1); + Some((start, end)) + } + }) + .collect::>(); + + let boundary = random_boundary(); + let boundary_sep = format!("\r\n--{boundary}\r\n"); + let boundary_closer = format!("\r\n--{boundary}\r\n"); + + resp = resp.header( + CONTENT_TYPE, + format!("multipart/byteranges; boundary={boundary}"), + ); + + let buf = crate::async_runtime::safe_block_on(async move { + // multi-part range header + let mut buf = Vec::new(); + + for (end, start) in ranges { + // a new range is being written, write the range boundary + buf.write_all(boundary_sep.as_bytes()).await?; + + // write the needed headers `Content-Type` and `Content-Range` + buf + .write_all(format!("{CONTENT_TYPE}: {mime_type}\r\n").as_bytes()) + .await?; + buf + .write_all(format!("{CONTENT_RANGE}: bytes {start}-{end}/{len}\r\n").as_bytes()) + .await?; + + // write the separator to indicate the start of the range body + buf.write_all("\r\n".as_bytes()).await?; + + // calculate number of bytes needed to be read + let nbytes = end + 1 - start; + + let mut local_buf = Vec::with_capacity(nbytes as usize); + file.seek(SeekFrom::Start(start)).await?; + (&mut file).take(nbytes).read_to_end(&mut local_buf).await?; + buf.extend_from_slice(&local_buf); + } + // all ranges have been written, write the closing boundary + buf.write_all(boundary_closer.as_bytes()).await?; + + Ok::, anyhow::Error>(buf) + })?; + resp.body(buf) + } + } else { + // avoid reading the file if we already read it + // as part of mime type detection + let buf = if let Some(b) = read_bytes { + b + } else { + crate::async_runtime::safe_block_on(async move { + let mut local_buf = Vec::with_capacity(len as usize); + file.read_to_end(&mut local_buf).await?; + Ok::, anyhow::Error>(local_buf) + })? + }; + resp = resp.header(CONTENT_LENGTH, len); + resp.body(buf) + }; + + response +} + +fn random_boundary() -> String { + let mut x = [0_u8; 30]; + rand::thread_rng().fill_bytes(&mut x); + (x[..]) + .iter() + .map(|&x| format!("{x:x}")) + .fold(String::new(), |mut a, x| { + a.push_str(x.as_str()); + a + }) +} diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index bb59a2279..a68f2bcd4 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -68,6 +68,8 @@ pub use tauri_macros::{command, generate_handler}; pub mod api; pub(crate) mod app; +#[cfg(feature = "protocol-asset")] +pub(crate) mod asset_protocol; pub mod async_runtime; pub mod command; mod error; @@ -144,6 +146,10 @@ use std::{collections::HashMap, fmt, sync::Arc}; // Export types likely to be used by the application. pub use runtime::http; +#[cfg(feature = "wry")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))] +pub use tauri_runtime_wry::webview_version; + #[cfg(target_os = "macos")] #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))] pub use runtime::{menu::NativeImage, ActivationPolicy}; @@ -563,6 +569,10 @@ pub trait Manager: sealed::ManagerBase { fn get_window(&self, label: &str) -> Option> { self.manager().get_window(label) } + /// Fetch the focused window. Returns `None` if there is not any focused window. + fn get_focused_window(&self) -> Option> { + self.manager().get_focused_window() + } /// Fetch all managed windows. fn windows(&self) -> HashMap> { diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 7d73244ac..b7409b5c9 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -25,6 +25,7 @@ use tauri_utils::{ html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN}, }; +use crate::app::{GlobalMenuEventListener, WindowMenuEvent}; use crate::hooks::IpcJavascript; #[cfg(feature = "isolation")] use crate::hooks::IsolationJavascript; @@ -50,10 +51,6 @@ use crate::{ Context, EventLoopMessage, Icon, Invoke, Manager, Pattern, Runtime, Scopes, StateManager, Window, WindowEvent, }; -use crate::{ - app::{GlobalMenuEventListener, WindowMenuEvent}, - window::WebResourceRequestHandler, -}; #[cfg(any(target_os = "linux", target_os = "windows"))] use crate::path::BaseDirectory; @@ -408,7 +405,6 @@ impl WindowManager { label: &str, window_labels: &[String], app_handle: AppHandle, - web_resource_request_handler: Option>, ) -> crate::Result> { let is_init_global = self.inner.config.build.with_global_tauri; let plugin_init = self @@ -480,7 +476,7 @@ impl WindowManager { }); } - let window_url = pending.current_url.lock().unwrap().clone(); + let window_url = Url::parse(&pending.url).unwrap(); let window_origin = if cfg!(windows) && window_url.scheme() != "http" && window_url.scheme() != "https" { format!("https://{}.localhost", window_url.scheme()) @@ -498,6 +494,7 @@ impl WindowManager { }; if !registered_scheme_protocols.contains(&"tauri".into()) { + let web_resource_request_handler = pending.web_resource_request_handler.take(); pending.register_uri_scheme_protocol( "tauri", self.prepare_uri_scheme_protocol(&window_origin, web_resource_request_handler), @@ -507,203 +504,13 @@ impl WindowManager { #[cfg(feature = "protocol-asset")] if !registered_scheme_protocols.contains(&"asset".into()) { - use crate::path::SafePathBuf; - use tokio::io::{AsyncReadExt, AsyncSeekExt}; - use url::Position; let asset_scope = self.state().get::().asset_protocol.clone(); pending.register_uri_scheme_protocol("asset", move |request| { - let parsed_path = Url::parse(request.uri())?; - let filtered_path = &parsed_path[..Position::AfterPath]; - let path = filtered_path - .strip_prefix("asset://localhost/") - // the `strip_prefix` only returns None when a request is made to `https://tauri.$P` on Windows - // where `$P` is not `localhost/*` - .unwrap_or(""); - let path = percent_encoding::percent_decode(path.as_bytes()) - .decode_utf8_lossy() - .to_string(); - - if let Err(e) = SafePathBuf::new(path.clone().into()) { - debug_eprintln!("asset protocol path \"{}\" is not valid: {}", path, e); - return HttpResponseBuilder::new().status(403).body(Vec::new()); - } - - if !asset_scope.is_allowed(&path) { - debug_eprintln!("asset protocol not configured to allow the path: {}", path); - return HttpResponseBuilder::new().status(403).body(Vec::new()); - } - - let path_ = path.clone(); - - let mut response = - HttpResponseBuilder::new().header("Access-Control-Allow-Origin", &window_origin); - - // handle 206 (partial range) http request - if let Some(range) = request - .headers() - .get("range") - .and_then(|r| r.to_str().map(|r| r.to_string()).ok()) - { - #[derive(Default)] - struct RangeMetadata { - file: Option, - range: Option, - metadata: Option, - headers: HashMap<&'static str, String>, - status_code: u16, - body: Vec, - } - - let mut range_metadata = crate::async_runtime::safe_block_on(async move { - let mut data = RangeMetadata::default(); - // open the file - let mut file = match tokio::fs::File::open(path_.clone()).await { - Ok(file) => file, - Err(e) => { - debug_eprintln!("Failed to open asset: {}", e); - data.status_code = 404; - return data; - } - }; - // Get the file size - let file_size = match file.metadata().await { - Ok(metadata) => { - let len = metadata.len(); - data.metadata.replace(metadata); - len - } - Err(e) => { - debug_eprintln!("Failed to read asset metadata: {}", e); - data.file.replace(file); - data.status_code = 404; - return data; - } - }; - // parse the range - let range = match crate::runtime::http::HttpRange::parse( - &if range.ends_with("-*") { - range.chars().take(range.len() - 1).collect::() - } else { - range.clone() - }, - file_size, - ) { - Ok(r) => r, - Err(e) => { - debug_eprintln!("Failed to parse range {}: {:?}", range, e); - data.file.replace(file); - data.status_code = 400; - return data; - } - }; - - // FIXME: Support multiple ranges - // let support only 1 range for now - if let Some(range) = range.first() { - data.range.replace(*range); - let mut real_length = range.length; - // prevent max_length; - // specially on webview2 - if range.length > file_size / 3 { - // max size sent (400ko / request) - // as it's local file system we can afford to read more often - real_length = std::cmp::min(file_size - range.start, 1024 * 400); - } - - // last byte we are reading, the length of the range include the last byte - // who should be skipped on the header - let last_byte = range.start + real_length - 1; - - data.headers.insert("Connection", "Keep-Alive".into()); - data.headers.insert("Accept-Ranges", "bytes".into()); - data - .headers - .insert("Content-Length", real_length.to_string()); - data.headers.insert( - "Content-Range", - format!("bytes {}-{last_byte}/{file_size}", range.start), - ); - - if let Err(e) = file.seek(std::io::SeekFrom::Start(range.start)).await { - debug_eprintln!("Failed to seek file to {}: {}", range.start, e); - data.file.replace(file); - data.status_code = 422; - return data; - } - - let mut f = file.take(real_length); - let r = f.read_to_end(&mut data.body).await; - file = f.into_inner(); - data.file.replace(file); - - if let Err(e) = r { - debug_eprintln!("Failed read file: {}", e); - data.status_code = 422; - return data; - } - // partial content - data.status_code = 206; - } else { - data.status_code = 200; - } - - data - }); - - for (k, v) in range_metadata.headers { - response = response.header(k, v); - } - - let mime_type = if let (Some(mut file), Some(metadata), Some(range)) = ( - range_metadata.file, - range_metadata.metadata, - range_metadata.range, - ) { - // if we're already reading the beginning of the file, we do not need to re-read it - if range.start == 0 { - MimeType::parse(&range_metadata.body, &path) - } else { - let (status, bytes) = crate::async_runtime::safe_block_on(async move { - let mut status = None; - if let Err(e) = file.rewind().await { - debug_eprintln!("Failed to rewind file: {}", e); - status.replace(422); - (status, Vec::with_capacity(0)) - } else { - // taken from https://docs.rs/infer/0.9.0/src/infer/lib.rs.html#240-251 - let limit = std::cmp::min(metadata.len(), 8192) as usize + 1; - let mut bytes = Vec::with_capacity(limit); - if let Err(e) = file.take(8192).read_to_end(&mut bytes).await { - debug_eprintln!("Failed read file: {}", e); - status.replace(422); - } - (status, bytes) - } - }); - if let Some(s) = status { - range_metadata.status_code = s; - } - MimeType::parse(&bytes, &path) - } - } else { - MimeType::parse(&range_metadata.body, &path) - }; - response - .mimetype(&mime_type) - .status(range_metadata.status_code) - .body(range_metadata.body) - } else { - match crate::async_runtime::safe_block_on(async move { tokio::fs::read(path_).await }) { - Ok(data) => { - let mime_type = MimeType::parse(&data, &path); - response.mimetype(&mime_type).body(data) - } - Err(e) => { - debug_eprintln!("Failed to read file: {}", e); - response.status(404).body(Vec::new()) - } - } - } + crate::asset_protocol::asset_protocol_handler( + request, + asset_scope.clone(), + window_origin.clone(), + ) }); } @@ -1167,7 +974,6 @@ impl WindowManager { app_handle: AppHandle, mut pending: PendingWindow, window_labels: &[String], - web_resource_request_handler: Option>, ) -> crate::Result> { if self.windows_lock().contains_key(&pending.label) { return Err(crate::Error::WindowLabelAlreadyExists(pending.label)); @@ -1227,7 +1033,7 @@ impl WindowManager { } } - *pending.current_url.lock().unwrap() = url; + pending.url = url.to_string(); if !pending.window_builder.has_icon() { if let Some(default_window_icon) = self.inner.default_window_icon.clone() { @@ -1275,7 +1081,6 @@ impl WindowManager { window_labels, #[allow(clippy::redundant_clone)] app_handle.clone(), - web_resource_request_handler, )?; pending.ipc_handler = Some(self.prepare_ipc_handler()); @@ -1301,7 +1106,6 @@ impl WindowManager { #[cfg(feature = "isolation")] let pattern = self.pattern().clone(); - let current_url_ = pending.current_url.clone(); let navigation_handler = pending.navigation_handler.take(); pending.navigation_handler = Some(Box::new(move |url| { // always allow navigation events for the isolation iframe and do not emit them for consumers @@ -1313,7 +1117,6 @@ impl WindowManager { return true; } } - *current_url_.lock().unwrap() = url.clone(); if let Some(handler) = &navigation_handler { handler(url) } else { @@ -1472,6 +1275,14 @@ impl WindowManager { self.windows_lock().get(label).cloned() } + pub fn get_focused_window(&self) -> Option> { + self + .windows_lock() + .iter() + .find(|w| w.1.is_focused().unwrap_or(false)) + .map(|w| w.1.clone()) + } + pub fn windows(&self) -> HashMap> { self.windows_lock().clone() } diff --git a/core/tauri/src/scope/fs.rs b/core/tauri/src/scope/fs.rs index 3e415d89b..754720e7d 100644 --- a/core/tauri/src/scope/fs.rs +++ b/core/tauri/src/scope/fs.rs @@ -10,6 +10,7 @@ use std::{ }; pub use glob::Pattern; +use tauri_utils::config::FsScope; use uuid::Uuid; /// Scope change event. @@ -29,6 +30,7 @@ pub struct Scope { allowed_patterns: Arc>>, forbidden_patterns: Arc>>, event_listeners: Arc>>, + match_options: glob::MatchOptions, } impl fmt::Debug for Scope { @@ -99,10 +101,29 @@ impl Scope { } } + let require_literal_leading_dot = match scope { + FsScope::Scope { + require_literal_leading_dot: Some(require), + .. + } => *require, + // dotfiles are not supposed to be exposed by default on unix + #[cfg(unix)] + _ => false, + #[cfg(windows)] + _ => true, + }; + Ok(Self { allowed_patterns: Arc::new(Mutex::new(allowed_patterns)), forbidden_patterns: Arc::new(Mutex::new(forbidden_patterns)), event_listeners: Default::default(), + match_options: glob::MatchOptions { + // this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt` + // see: https://github.com/tauri-apps/tauri/security/advisories/GHSA-6mv3-wm7j-h4w5 + require_literal_separator: true, + require_literal_leading_dot, + ..Default::default() + }, }) } @@ -209,22 +230,12 @@ impl Scope { if let Ok(path) = path { let path: PathBuf = path.components().collect(); - let options = glob::MatchOptions { - // this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt` - // see: https://github.com/tauri-apps/tauri/security/advisories/GHSA-6mv3-wm7j-h4w5 - require_literal_separator: true, - // dotfiles are not supposed to be exposed by default - #[cfg(unix)] - require_literal_leading_dot: true, - ..Default::default() - }; - let forbidden = self .forbidden_patterns .lock() .unwrap() .iter() - .any(|p| p.matches_path_with(&path, options)); + .any(|p| p.matches_path_with(&path, self.match_options)); if forbidden { false @@ -234,7 +245,7 @@ impl Scope { .lock() .unwrap() .iter() - .any(|p| p.matches_path_with(&path, options)); + .any(|p| p.matches_path_with(&path, self.match_options)); allowed } } else { @@ -264,6 +275,17 @@ mod tests { allowed_patterns: Default::default(), forbidden_patterns: Default::default(), event_listeners: Default::default(), + match_options: glob::MatchOptions { + // this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt` + // see: https://github.com/tauri-apps/tauri/security/advisories/GHSA-6mv3-wm7j-h4w5 + require_literal_separator: true, + // dotfiles are not supposed to be exposed by default on unix + #[cfg(unix)] + require_literal_leading_dot: false, + #[cfg(windows)] + require_literal_leading_dot: true, + ..Default::default() + }, } } diff --git a/core/tauri/src/scope/ipc.rs b/core/tauri/src/scope/ipc.rs index 728eb4ec6..b9540a81e 100644 --- a/core/tauri/src/scope/ipc.rs +++ b/core/tauri/src/scope/ipc.rs @@ -254,7 +254,7 @@ mod tests { #[test] fn scope_not_defined() { - let (_app, window) = test_context(vec![RemoteDomainAccessScope::new("app.tauri.app") + let (_app, mut window) = test_context(vec![RemoteDomainAccessScope::new("app.tauri.app") .add_window("other") .add_plugin("path")]); @@ -271,7 +271,7 @@ mod tests { #[test] fn scope_not_defined_for_window() { - let (_app, window) = test_context(vec![RemoteDomainAccessScope::new("tauri.app") + let (_app, mut window) = test_context(vec![RemoteDomainAccessScope::new("tauri.app") .add_window("second") .add_plugin("path")]); @@ -285,7 +285,7 @@ mod tests { #[test] fn scope_not_defined_for_url() { - let (_app, window) = test_context(vec![RemoteDomainAccessScope::new("github.com") + let (_app, mut window) = test_context(vec![RemoteDomainAccessScope::new("github.com") .add_window("main") .add_plugin("path")]); @@ -339,7 +339,7 @@ mod tests { #[test] fn subpath_is_allowed() { - let (_app, window) = test_context(vec![RemoteDomainAccessScope::new("tauri.app") + let (_app, mut window) = test_context(vec![RemoteDomainAccessScope::new("tauri.app") .add_window("main") .add_plugin("path")]); @@ -349,7 +349,7 @@ mod tests { #[test] fn tauri_api_not_allowed() { - let (_app, window) = test_context(vec![ + let (_app, mut window) = test_context(vec![ RemoteDomainAccessScope::new("tauri.app").add_window("main") ]); @@ -363,7 +363,7 @@ mod tests { #[test] fn plugin_allowed() { - let (_app, window) = test_context(vec![RemoteDomainAccessScope::new("tauri.app") + let (_app, mut window) = test_context(vec![RemoteDomainAccessScope::new("tauri.app") .add_window("main") .add_plugin(PLUGIN_NAME)]); @@ -377,7 +377,7 @@ mod tests { #[test] fn plugin_not_allowed() { - let (_app, window) = test_context(vec![ + let (_app, mut window) = test_context(vec![ RemoteDomainAccessScope::new("tauri.app").add_window("main") ]); diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index 4c95107b1..9073435eb 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -66,7 +66,6 @@ impl RuntimeHandle for MockRuntimeHandle { ) -> Result> { Ok(DetachedWindow { label: pending.label, - current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())), dispatcher: MockDispatcher { context: self.context.clone(), last_evaluated_script: Default::default(), @@ -180,6 +179,18 @@ impl WindowBuilder for MockWindowBuilder { self } + fn maximizable(self, resizable: bool) -> Self { + self + } + + fn minimizable(self, resizable: bool) -> Self { + self + } + + fn closable(self, resizable: bool) -> Self { + self + } + fn title>(self, title: S) -> Self { self } @@ -350,6 +361,10 @@ impl Dispatch for MockDispatcher { Ok(false) } + fn is_focused(&self) -> Result { + Ok(false) + } + fn is_decorated(&self) -> Result { Ok(false) } @@ -358,6 +373,18 @@ impl Dispatch for MockDispatcher { Ok(false) } + fn is_maximizable(&self) -> Result { + Ok(true) + } + + fn is_minimizable(&self) -> Result { + Ok(true) + } + + fn is_closable(&self) -> Result { + Ok(true) + } + fn is_visible(&self) -> Result { Ok(true) } @@ -424,6 +451,18 @@ impl Dispatch for MockDispatcher { Ok(()) } + fn set_maximizable(&self, maximizable: bool) -> Result<()> { + Ok(()) + } + + fn set_minimizable(&self, minimizable: bool) -> Result<()> { + Ok(()) + } + + fn set_closable(&self, closable: bool) -> Result<()> { + Ok(()) + } + fn set_title>(&self, title: S) -> Result<()> { Ok(()) } @@ -646,7 +685,6 @@ impl Runtime for MockRuntime { fn create_window(&self, pending: PendingWindow) -> Result> { Ok(DetachedWindow { label: pending.label, - current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())), dispatcher: MockDispatcher { context: self.context.clone(), last_evaluated_script: Default::default(), diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index 2933a8044..6e21abf07 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -223,31 +223,19 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { /// /// [the Webview2 issue]: https://github.com/tauri-apps/wry/issues/583 pub fn from_config>(manager: &'a M, config: WindowConfig) -> Self { - let runtime = manager.runtime(); - let app_handle = manager.app_handle(); - let url = config.url.clone(); - let file_drop_enabled = config.file_drop_enabled; - let mut webview_attributes = WebviewAttributes::new(url); - if let Some(effects) = config.window_effects.clone() { - webview_attributes = webview_attributes.window_effects(effects); - } - let mut builder = Self { + let builder = Self { manager: manager.manager().clone(), - runtime, - app_handle, + runtime: manager.runtime(), + app_handle: manager.app_handle(), label: config.label.clone(), + webview_attributes: WebviewAttributes::from(&config), window_builder: >::WindowBuilder::with_config( config, ), - webview_attributes, web_resource_request_handler: None, navigation_handler: None, }; - if !file_drop_enabled { - builder = builder.disable_file_drop_handler(); - } - builder } @@ -325,19 +313,18 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { /// Creates a new webview window. pub fn build(mut self) -> crate::Result> { - let web_resource_request_handler = self.web_resource_request_handler.take(); - let pending = PendingWindow::new( + let mut pending = PendingWindow::new( self.window_builder.clone(), self.webview_attributes.clone(), self.label.clone(), )?; + pending.navigation_handler = self.navigation_handler.take(); + pending.web_resource_request_handler = self.web_resource_request_handler.take(); + let labels = self.manager.labels().into_iter().collect::>(); - let pending = self.manager.prepare_window( - self.app_handle.clone(), - pending, - &labels, - web_resource_request_handler, - )?; + let pending = self + .manager + .prepare_window(self.app_handle.clone(), pending, &labels)?; let window_effects = pending.webview_attributes.window_effects.clone(); let window = match &mut self.runtime { RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending), @@ -413,12 +400,50 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { } /// Whether the window is resizable or not. + /// When resizable is set to false, native window's maximize button is automatically disabled. #[must_use] pub fn resizable(mut self, resizable: bool) -> Self { self.window_builder = self.window_builder.resizable(resizable); self } + /// 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. + #[must_use] + pub fn maximizable(mut self, maximizable: bool) -> Self { + self.window_builder = self.window_builder.maximizable(maximizable); + self + } + + /// Whether the window's native minimize button is enabled or not. + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + #[must_use] + pub fn minimizable(mut self, minimizable: bool) -> Self { + self.window_builder = self.window_builder.minimizable(minimizable); + self + } + + /// 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. + #[must_use] + pub fn closable(mut self, closable: bool) -> Self { + self.window_builder = self.window_builder.closable(closable); + self + } + /// The title of the window in the title bar. #[must_use] pub fn title>(mut self, title: S) -> Self { @@ -503,7 +528,7 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> { self } - /// Whether the window should always be on top of other windows. + /// Prevents the window contents from being captured by other apps. #[must_use] pub fn content_protected(mut self, protected: bool) -> Self { self.window_builder = self.window_builder.content_protected(protected); @@ -741,6 +766,9 @@ pub struct Window { manager: WindowManager, pub(crate) app_handle: AppHandle, js_event_listeners: Arc>>>, + + #[cfg(test)] + pub(crate) current_url: url::Url, } unsafe impl raw_window_handle::HasRawWindowHandle for Window { @@ -756,6 +784,8 @@ impl Clone for Window { manager: self.manager.clone(), app_handle: self.app_handle.clone(), js_event_listeners: self.js_event_listeners.clone(), + #[cfg(test)] + current_url: self.current_url.clone(), } } } @@ -908,6 +938,8 @@ impl Window { manager, app_handle, js_event_listeners: Default::default(), + #[cfg(test)] + current_url: "http://tauri.app".parse().unwrap(), } } @@ -1086,6 +1118,11 @@ impl Window { self.window.dispatcher.is_maximized().map_err(Into::into) } + /// Gets the window's current focus state. + pub fn is_focused(&self) -> crate::Result { + self.window.dispatcher.is_focused().map_err(Into::into) + } + /// Gets the window’s current decoration state. pub fn is_decorated(&self) -> crate::Result { self.window.dispatcher.is_decorated().map_err(Into::into) @@ -1096,6 +1133,33 @@ impl Window { self.window.dispatcher.is_resizable().map_err(Into::into) } + /// Gets the window’s native maximize button state + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + pub fn is_maximizable(&self) -> crate::Result { + self.window.dispatcher.is_maximizable().map_err(Into::into) + } + + /// Gets the window’s native minimize button state + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + pub fn is_minimizable(&self) -> crate::Result { + self.window.dispatcher.is_minimizable().map_err(Into::into) + } + + /// Gets the window’s native close button state + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + pub fn is_closable(&self) -> crate::Result { + self.window.dispatcher.is_closable().map_err(Into::into) + } + /// Gets the window's current visibility state. pub fn is_visible(&self) -> crate::Result { self.window.dispatcher.is_visible().map_err(Into::into) @@ -1236,6 +1300,7 @@ impl Window { } /// Determines if this window should be resizable. + /// When resizable is set to false, native window's maximize button is automatically disabled. pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> { self .window @@ -1244,6 +1309,49 @@ impl Window { .map_err(Into::into) } + /// Determines if this window's native maximize button should be enabled. + /// 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. + pub fn set_maximizable(&self, maximizable: bool) -> crate::Result<()> { + self + .window + .dispatcher + .set_maximizable(maximizable) + .map_err(Into::into) + } + + /// Determines if this window's native minize button should be enabled. + /// + /// ## Platform-specific + /// + /// - **Linux / iOS / Android:** Unsupported. + pub fn set_minimizable(&self, minimizable: bool) -> crate::Result<()> { + self + .window + .dispatcher + .set_minimizable(minimizable) + .map_err(Into::into) + } + + /// Determines if this window's native close button should be enabled. + /// + /// ## 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. + pub fn set_closable(&self, closable: bool) -> crate::Result<()> { + self + .window + .dispatcher + .set_closable(closable) + .map_err(Into::into) + } + /// Set this window's title. pub fn set_title(&self, title: &str) -> crate::Result<()> { self @@ -1518,13 +1626,20 @@ impl Window { /// Webview APIs. impl Window { /// Returns the current url of the webview. + // TODO: in v2, change this type to Result + #[cfg(not(test))] pub fn url(&self) -> Url { - self.window.current_url.lock().unwrap().clone() + self.window.dispatcher.url().unwrap() } #[cfg(test)] - pub(crate) fn navigate(&self, url: Url) { - *self.window.current_url.lock().unwrap() = url; + pub fn url(&self) -> Url { + self.current_url.clone() + } + + #[cfg(test)] + pub(crate) fn navigate(&mut self, url: Url) { + self.current_url = url; } /// Handles this window receiving an [`InvokeMessage`]. diff --git a/core/tauri/src/window/menu.rs b/core/tauri/src/window/menu.rs index ab7360dd5..688882284 100644 --- a/core/tauri/src/window/menu.rs +++ b/core/tauri/src/window/menu.rs @@ -80,6 +80,20 @@ impl MenuHandle { panic!("item id not found") } + /// Attempts to get a handle to the menu item that has the specified `id`, return an error if `id` is not found. + pub fn try_get_item(&self, id: MenuIdRef<'_>) -> Option> { + self + .ids + .lock() + .unwrap() + .iter() + .find(|i| i.1 == id) + .map(|i| MenuItemHandle { + id: *i.0, + dispatcher: self.dispatcher.clone(), + }) + } + /// Shows the menu. pub fn show(&self) -> crate::Result<()> { self.dispatcher.show_menu().map_err(Into::into) diff --git a/core/tests/restart/LICENSE.spdx b/core/tests/restart/LICENSE.spdx index cc563bd76..e3b1b8037 120000 --- a/core/tests/restart/LICENSE.spdx +++ b/core/tests/restart/LICENSE.spdx @@ -1 +1 @@ -../../../../LICENSE.spdx \ No newline at end of file +../../../LICENSE.spdx \ No newline at end of file diff --git a/core/tests/restart/LICENSE_APACHE-2.0 b/core/tests/restart/LICENSE_APACHE-2.0 index 1f7a8da6e..5ee573597 120000 --- a/core/tests/restart/LICENSE_APACHE-2.0 +++ b/core/tests/restart/LICENSE_APACHE-2.0 @@ -1 +1 @@ -../../../../LICENSE_APACHE-2.0 \ No newline at end of file +../../../LICENSE_APACHE-2.0 \ No newline at end of file diff --git a/core/tests/restart/LICENSE_MIT b/core/tests/restart/LICENSE_MIT index dd01abfbb..71b666341 120000 --- a/core/tests/restart/LICENSE_MIT +++ b/core/tests/restart/LICENSE_MIT @@ -1 +1 @@ -../../../../LICENSE_MIT \ No newline at end of file +../../../LICENSE_MIT \ No newline at end of file diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 480f87dbe..339b4b7bf 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -324,7 +324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f83bc2e401ed041b7057345ebc488c005efa0341d5541ce7004d30458d0090b" dependencies = [ "serde", - "toml", + "toml 0.7.3", ] [[package]] @@ -3865,6 +3865,15 @@ dependencies = [ "windows-targets 0.48.0", ] +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-bindgen" version = "0.44.0" diff --git a/examples/streaming/main.rs b/examples/streaming/main.rs index cee84c6b1..087aa4566 100644 --- a/examples/streaming/main.rs +++ b/examples/streaming/main.rs @@ -4,14 +4,15 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use std::sync::{Arc, Mutex}; + fn main() { use std::{ - cmp::min, - io::{Read, Seek, SeekFrom}, + io::{Read, Seek, SeekFrom, Write}, path::PathBuf, process::{Command, Stdio}, }; - use tauri::http::{HttpRange, ResponseBuilder}; + use tauri::http::{header::*, status::StatusCode, HttpRange, ResponseBuilder}; let video_file = PathBuf::from("test_video.mp4"); let video_url = @@ -35,77 +36,149 @@ fn main() { assert!(video_file.exists()); } + // NOTE: for production use `rand` crate to generate a random boundary + let boundary_id = Arc::new(Mutex::new(0)); + tauri::Builder::default() .invoke_handler(tauri::generate_handler![video_uri]) .register_uri_scheme_protocol("stream", move |_app, request| { - // prepare our response - let mut response = ResponseBuilder::new(); // get the file path let path = request.uri().strip_prefix("stream://localhost/").unwrap(); let path = percent_encoding::percent_decode(path.as_bytes()) .decode_utf8_lossy() .to_string(); - if path != "example/test_video.mp4" { - // return error 404 if it's not out video - return response.mimetype("text/plain").status(404).body(Vec::new()); + if path != "test_video.mp4" { + // return error 404 if it's not our video + return ResponseBuilder::new().status(404).body(Vec::new()); } - // read our file - let mut content = std::fs::File::open(&video_file)?; - let mut buf = Vec::new(); + let mut file = std::fs::File::open(&path)?; - // default status code - let mut status_code = 200; + // get file length + let len = { + let old_pos = file.stream_position()?; + let len = file.seek(SeekFrom::End(0))?; + file.seek(SeekFrom::Start(old_pos))?; + len + }; + + let mut resp = ResponseBuilder::new().header(CONTENT_TYPE, "video/mp4"); // if the webview sent a range header, we need to send a 206 in return // Actually only macOS and Windows are supported. Linux will ALWAYS return empty headers. - if let Some(range) = request.headers().get("range") { - // Get the file size - let file_size = content.metadata().unwrap().len(); + let response = if let Some(range_header) = request.headers().get("range") { + let not_satisfiable = || { + ResponseBuilder::new() + .status(StatusCode::RANGE_NOT_SATISFIABLE) + .header(CONTENT_RANGE, format!("bytes */{len}")) + .body(vec![]) + }; - // we parse the range header with tauri helper - let range = HttpRange::parse(range.to_str().unwrap(), file_size).unwrap(); - // let support only 1 range for now - let first_range = range.first(); - if let Some(range) = first_range { - let mut real_length = range.length; + // parse range header + let ranges = if let Ok(ranges) = HttpRange::parse(range_header.to_str()?, len) { + ranges + .iter() + // map the output back to spec range , example: 0-499 + .map(|r| (r.start, r.start + r.length - 1)) + .collect::>() + } else { + return not_satisfiable(); + }; - // prevent max_length; - // specially on webview2 - if range.length > file_size / 3 { - // max size sent (400ko / request) - // as it's local file system we can afford to read more often - real_length = min(file_size - range.start, 1024 * 400); + /// The Maximum bytes we send in one range + const MAX_LEN: u64 = 1000 * 1024; + + if ranges.len() == 1 { + let &(start, mut end) = ranges.first().unwrap(); + + // check if a range is not satisfiable + // + // this should be already taken care of by HttpRange::parse + // but checking here again for extra assurance + if start >= len || end >= len || end < start { + return not_satisfiable(); } - // last byte we are reading, the length of the range include the last byte - // who should be skipped on the header - let last_byte = range.start + real_length - 1; - // partial content - status_code = 206; + // adjust end byte for MAX_LEN + end = start + (end - start).min(len - start).min(MAX_LEN - 1); - // Only macOS and Windows are supported, if you set headers in linux they are ignored - response = response - .header("Connection", "Keep-Alive") - .header("Accept-Ranges", "bytes") - .header("Content-Length", real_length) - .header( - "Content-Range", - format!("bytes {}-{}/{}", range.start, last_byte, file_size), - ); + // calculate number of bytes needed to be read + let bytes_to_read = end + 1 - start; - // FIXME: Add ETag support (caching on the webview) + // allocate a buf with a suitable capacity + let mut buf = Vec::with_capacity(bytes_to_read as usize); + // seek the file to the starting byte + file.seek(SeekFrom::Start(start))?; + // read the needed bytes + file.take(bytes_to_read).read_to_end(&mut buf)?; - // seek our file bytes - content.seek(SeekFrom::Start(range.start))?; - content.take(real_length).read_to_end(&mut buf)?; + resp = resp.header(CONTENT_RANGE, format!("bytes {start}-{end}/{len}")); + resp = resp.header(CONTENT_LENGTH, end + 1 - start); + resp = resp.status(StatusCode::PARTIAL_CONTENT); + resp.body(buf) } else { - content.read_to_end(&mut buf)?; - } - } + let mut buf = Vec::new(); + let ranges = ranges + .iter() + .filter_map(|&(start, mut end)| { + // filter out unsatisfiable ranges + // + // this should be already taken care of by HttpRange::parse + // but checking here again for extra assurance + if start >= len || end >= len || end < start { + None + } else { + // adjust end byte for MAX_LEN + end = start + (end - start).min(len - start).min(MAX_LEN - 1); + Some((start, end)) + } + }) + .collect::>(); - response.mimetype("video/mp4").status(status_code).body(buf) + let mut id = boundary_id.lock().unwrap(); + *id += 1; + let boundary = format!("sadasq2e{id}"); + let boundary_sep = format!("\r\n--{boundary}\r\n"); + let boundary_closer = format!("\r\n--{boundary}\r\n"); + + resp = resp.header( + CONTENT_TYPE, + format!("multipart/byteranges; boundary={boundary}"), + ); + + for (end, start) in ranges { + // a new range is being written, write the range boundary + buf.write_all(boundary_sep.as_bytes())?; + + // write the needed headers `Content-Type` and `Content-Range` + buf.write_all(format!("{CONTENT_TYPE}: video/mp4\r\n").as_bytes())?; + buf.write_all(format!("{CONTENT_RANGE}: bytes {start}-{end}/{len}\r\n").as_bytes())?; + + // write the separator to indicate the start of the range body + buf.write_all("\r\n".as_bytes())?; + + // calculate number of bytes needed to be read + let bytes_to_read = end + 1 - start; + + let mut local_buf = vec![0_u8; bytes_to_read as usize]; + file.seek(SeekFrom::Start(start))?; + file.read_exact(&mut local_buf)?; + buf.extend_from_slice(&local_buf); + } + // all ranges have been written, write the closing boundary + buf.write_all(boundary_closer.as_bytes())?; + + resp.body(buf) + } + } else { + resp = resp.header(CONTENT_LENGTH, len); + let mut buf = Vec::with_capacity(len as usize); + file.read_to_end(&mut buf)?; + resp.body(buf) + }; + + response }) .run(tauri::generate_context!( "../../examples/streaming/tauri.conf.json" @@ -125,5 +198,5 @@ fn video_uri() -> (&'static str, std::path::PathBuf) { } #[cfg(not(feature = "protocol-asset"))] - ("stream", "example/test_video.mp4".into()) + ("stream", "test_video.mp4".into()) } diff --git a/package.json b/package.json index 9b24e6286..b81d2bb43 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "postinstall": "husky install" }, "devDependencies": { - "covector": "^0.7.3", + "covector": "^0.9.0", "husky": "^6.0.0", "prettier": "^2.5.1" }, diff --git a/tooling/api/README.md b/tooling/api/README.md index 6205f01e2..71c1486b5 100644 --- a/tooling/api/README.md +++ b/tooling/api/README.md @@ -4,7 +4,7 @@ [![status](https://img.shields.io/badge/status-stable-blue.svg)](https://github.com/tauri-apps/tauri/tree/dev) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri) -[![test library](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library)](https://github.com/tauri-apps/tauri/actions?query=workflow%3A%22test+library%22) +[![lint js](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/lint-js.yml?label=lint%20js&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/lint-js.yml) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield) [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S) [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app) diff --git a/tooling/api/docs/js-api.json b/tooling/api/docs/js-api.json index 9715719eb..6a81e9ae8 100644 --- a/tooling/api/docs/js-api.json +++ b/tooling/api/docs/js-api.json @@ -1 +1 @@ -{"id":0,"name":"@tauri-apps/api","kind":1,"flags":{},"originalName":"","children":[{"id":1,"name":"event","kind":2,"kindString":"Module","flags":{},"comment":{"summary":[{"kind":"text","text":"The event system allows you to emit events to the backend and listen to events from it.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.event`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":"."}]},"children":[{"id":3,"name":"TauriEvent","kind":8,"kindString":"Enumeration","flags":{},"comment":{"summary":[],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.1.0"}]}]},"children":[{"id":16,"name":"MENU","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":33,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L33"}],"type":{"type":"literal","value":"tauri://menu"}},{"id":10,"name":"WINDOW_BLUR","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":27,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L27"}],"type":{"type":"literal","value":"tauri://blur"}},{"id":6,"name":"WINDOW_CLOSE_REQUESTED","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":23,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L23"}],"type":{"type":"literal","value":"tauri://close-requested"}},{"id":7,"name":"WINDOW_CREATED","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":24,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L24"}],"type":{"type":"literal","value":"tauri://window-created"}},{"id":8,"name":"WINDOW_DESTROYED","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":25,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L25"}],"type":{"type":"literal","value":"tauri://destroyed"}},{"id":13,"name":"WINDOW_FILE_DROP","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":30,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L30"}],"type":{"type":"literal","value":"tauri://file-drop"}},{"id":15,"name":"WINDOW_FILE_DROP_CANCELLED","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":32,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L32"}],"type":{"type":"literal","value":"tauri://file-drop-cancelled"}},{"id":14,"name":"WINDOW_FILE_DROP_HOVER","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":31,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L31"}],"type":{"type":"literal","value":"tauri://file-drop-hover"}},{"id":9,"name":"WINDOW_FOCUS","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":26,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L26"}],"type":{"type":"literal","value":"tauri://focus"}},{"id":5,"name":"WINDOW_MOVED","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":22,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L22"}],"type":{"type":"literal","value":"tauri://move"}},{"id":4,"name":"WINDOW_RESIZED","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":21,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L21"}],"type":{"type":"literal","value":"tauri://resize"}},{"id":11,"name":"WINDOW_SCALE_FACTOR_CHANGED","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":28,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L28"}],"type":{"type":"literal","value":"tauri://scale-change"}},{"id":12,"name":"WINDOW_THEME_CHANGED","kind":16,"kindString":"Enumeration Member","flags":{},"sources":[{"fileName":"event.ts","line":29,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L29"}],"type":{"type":"literal","value":"tauri://theme-changed"}}],"groups":[{"title":"Enumeration Members","children":[16,10,6,7,8,13,15,14,9,5,4,11,12]}],"sources":[{"fileName":"event.ts","line":20,"character":12,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L20"}]},{"id":106,"name":"Event","kind":256,"kindString":"Interface","flags":{},"children":[{"id":107,"name":"event","kind":1024,"kindString":"Property","flags":{},"comment":{"summary":[{"kind":"text","text":"Event name"}]},"sources":[{"fileName":"helpers/event.ts","line":10,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/helpers/event.ts#L10"}],"type":{"type":"reference","id":2,"name":"EventName"}},{"id":109,"name":"id","kind":1024,"kindString":"Property","flags":{},"comment":{"summary":[{"kind":"text","text":"Event identifier used to unlisten"}]},"sources":[{"fileName":"helpers/event.ts","line":14,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/helpers/event.ts#L14"}],"type":{"type":"intrinsic","name":"number"}},{"id":110,"name":"payload","kind":1024,"kindString":"Property","flags":{},"comment":{"summary":[{"kind":"text","text":"Event payload"}]},"sources":[{"fileName":"helpers/event.ts","line":16,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/helpers/event.ts#L16"}],"type":{"type":"reference","id":111,"name":"T"}},{"id":108,"name":"windowLabel","kind":1024,"kindString":"Property","flags":{},"comment":{"summary":[{"kind":"text","text":"The label of the window that emitted this event."}]},"sources":[{"fileName":"helpers/event.ts","line":12,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/helpers/event.ts#L12"}],"type":{"type":"intrinsic","name":"string"}}],"groups":[{"title":"Properties","children":[107,109,110,108]}],"sources":[{"fileName":"helpers/event.ts","line":8,"character":17,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/helpers/event.ts#L8"}],"typeParameters":[{"id":111,"name":"T","kind":131072,"kindString":"Type parameter","flags":{}}]},{"id":112,"name":"EventCallback","kind":4194304,"kindString":"Type alias","flags":{},"sources":[{"fileName":"helpers/event.ts","line":19,"character":12,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/helpers/event.ts#L19"}],"typeParameters":[{"id":116,"name":"T","kind":131072,"kindString":"Type parameter","flags":{}}],"type":{"type":"reflection","declaration":{"id":113,"name":"__type","kind":65536,"kindString":"Type literal","flags":{},"sources":[{"fileName":"helpers/event.ts","line":19,"character":31,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/helpers/event.ts#L19"}],"signatures":[{"id":114,"name":"__type","kind":4096,"kindString":"Call signature","flags":{},"parameters":[{"id":115,"name":"event","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reference","id":106,"typeArguments":[{"type":"reference","id":116,"name":"T"}],"name":"Event"}}],"type":{"type":"intrinsic","name":"void"}}]}}},{"id":2,"name":"EventName","kind":4194304,"kindString":"Type alias","flags":{},"sources":[{"fileName":"event.ts","line":15,"character":12,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L15"}],"type":{"type":"union","types":[{"type":"template-literal","head":"","tail":[[{"type":"reference","id":3,"name":"TauriEvent"},""]]},{"type":"intersection","types":[{"type":"intrinsic","name":"string"},{"type":"reference","typeArguments":[{"type":"intrinsic","name":"never"},{"type":"intrinsic","name":"never"}],"name":"Record","qualifiedName":"Record","package":"typescript"}]}]}},{"id":117,"name":"UnlistenFn","kind":4194304,"kindString":"Type alias","flags":{},"sources":[{"fileName":"helpers/event.ts","line":21,"character":12,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/helpers/event.ts#L21"}],"type":{"type":"reflection","declaration":{"id":118,"name":"__type","kind":65536,"kindString":"Type literal","flags":{},"sources":[{"fileName":"helpers/event.ts","line":21,"character":25,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/helpers/event.ts#L21"}],"signatures":[{"id":119,"name":"__type","kind":4096,"kindString":"Call signature","flags":{},"type":{"type":"intrinsic","name":"void"}}]}}},{"id":27,"name":"emit","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"event.ts","line":107,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L107"}],"signatures":[{"id":28,"name":"emit","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Emits an event to the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { emit } from '@tauri-apps/api/event';\nawait emit('frontend-loaded', { loggedIn: true, token: 'authToken' });\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"parameters":[{"id":29,"name":"event","kind":32768,"kindString":"Parameter","flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"intrinsic","name":"string"}},{"id":30,"name":"payload","kind":32768,"kindString":"Parameter","flags":{"isOptional":true},"type":{"type":"intrinsic","name":"unknown"}}],"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise","qualifiedName":"Promise","package":"typescript"}}]},{"id":17,"name":"listen","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"event.ts","line":57,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L57"}],"signatures":[{"id":18,"name":"listen","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Listen to an event from the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { listen } from '@tauri-apps/api/event';\nconst unlisten = await listen('error', (event) => {\n console.log(`Got error in window ${event.windowLabel}, payload: ${event.payload}`);\n});\n\n// you need to call unlisten if your handler goes out of scope e.g. the component is unmounted\nunlisten();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving to a function to unlisten to the event.\nNote that removing the listener is required if your listener goes out of scope e.g. the component is unmounted."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"typeParameter":[{"id":19,"name":"T","kind":131072,"kindString":"Type parameter","flags":{}}],"parameters":[{"id":20,"name":"event","kind":32768,"kindString":"Parameter","flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"reference","id":2,"name":"EventName"}},{"id":21,"name":"handler","kind":32768,"kindString":"Parameter","flags":{},"comment":{"summary":[{"kind":"text","text":"Event handler callback."}]},"type":{"type":"reference","id":112,"typeArguments":[{"type":"reference","id":19,"name":"T"}],"name":"EventCallback"}}],"type":{"type":"reference","typeArguments":[{"type":"reference","id":117,"name":"UnlistenFn"}],"name":"Promise","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise","qualifiedName":"Promise","package":"typescript"}}]},{"id":22,"name":"once","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"event.ts","line":88,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L88"}],"signatures":[{"id":23,"name":"once","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Listen to an one-off event from the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { once } from '@tauri-apps/api/event';\ninterface LoadedPayload {\n loggedIn: boolean,\n token: string\n}\nconst unlisten = await once('loaded', (event) => {\n console.log(`App is loaded, loggedIn: ${event.payload.loggedIn}, token: ${event.payload.token}`);\n});\n\n// you need to call unlisten if your handler goes out of scope e.g. the component is unmounted\nunlisten();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving to a function to unlisten to the event.\nNote that removing the listener is required if your listener goes out of scope e.g. the component is unmounted."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"typeParameter":[{"id":24,"name":"T","kind":131072,"kindString":"Type parameter","flags":{}}],"parameters":[{"id":25,"name":"event","kind":32768,"kindString":"Parameter","flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"reference","id":2,"name":"EventName"}},{"id":26,"name":"handler","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reference","id":112,"typeArguments":[{"type":"reference","id":24,"name":"T"}],"name":"EventCallback"}}],"type":{"type":"reference","typeArguments":[{"type":"reference","id":117,"name":"UnlistenFn"}],"name":"Promise","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise","qualifiedName":"Promise","package":"typescript"}}]}],"groups":[{"title":"Enumerations","children":[3]},{"title":"Interfaces","children":[106]},{"title":"Type Aliases","children":[112,2,117]},{"title":"Functions","children":[27,17,22]}],"sources":[{"fileName":"event.ts","line":12,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/event.ts#L12"}]},{"id":31,"name":"mocks","kind":2,"kindString":"Module","flags":{},"children":[{"id":43,"name":"clearMocks","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"mocks.ts","line":186,"character":16,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/mocks.ts#L186"}],"signatures":[{"id":44,"name":"clearMocks","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Clears mocked functions/data injected by the other functions in this module.\nWhen using a test runner that doesn't provide a fresh window object for each test, calling this function will reset tauri specific properties.\n\n# Example\n\n"},{"kind":"code","text":"```js\nimport { mockWindows, clearMocks } from \"@tauri-apps/api/mocks\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked windows\", () => {\n mockWindows(\"main\", \"second\", \"third\");\n\n expect(window).toHaveProperty(\"__TAURI_METADATA__\")\n})\n\ntest(\"no mocked windows\", () => {\n expect(window).not.toHaveProperty(\"__TAURI_METADATA__\")\n})\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"type":{"type":"intrinsic","name":"void"}}]},{"id":32,"name":"mockIPC","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"mocks.ts","line":80,"character":16,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/mocks.ts#L80"}],"signatures":[{"id":33,"name":"mockIPC","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Intercepts all IPC requests with the given mock handler.\n\nThis function can be used when testing tauri frontend applications or when running the frontend in a Node.js context during static site generation.\n\n# Examples\n\nTesting setup using vitest:\n"},{"kind":"code","text":"```js\nimport { mockIPC, clearMocks } from \"@tauri-apps/api/mocks\"\nimport { invoke } from \"@tauri-apps/api/tauri\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked command\", () => {\n mockIPC((cmd, args) => {\n switch (cmd) {\n case \"add\":\n return (args.a as number) + (args.b as number);\n default:\n break;\n }\n });\n\n expect(invoke('add', { a: 12, b: 15 })).resolves.toBe(27);\n})\n```"},{"kind":"text","text":"\n\nThe callback function can also return a Promise:\n"},{"kind":"code","text":"```js\nimport { mockIPC, clearMocks } from \"@tauri-apps/api/mocks\"\nimport { invoke } from \"@tauri-apps/api/tauri\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked command\", () => {\n mockIPC((cmd, args) => {\n if(cmd === \"get_data\") {\n return fetch(\"https://example.com/data.json\")\n .then((response) => response.json())\n }\n });\n\n expect(invoke('get_data')).resolves.toBe({ foo: 'bar' });\n})\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"parameters":[{"id":34,"name":"cb","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reflection","declaration":{"id":35,"name":"__type","kind":65536,"kindString":"Type literal","flags":{},"sources":[{"fileName":"mocks.ts","line":81,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/mocks.ts#L81"}],"signatures":[{"id":36,"name":"__type","kind":4096,"kindString":"Call signature","flags":{},"parameters":[{"id":37,"name":"cmd","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":38,"name":"args","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"string"},{"type":"intrinsic","name":"unknown"}],"name":"Record","qualifiedName":"Record","package":"typescript"}}],"type":{"type":"intrinsic","name":"any"}}]}}}],"type":{"type":"intrinsic","name":"void"}}]},{"id":39,"name":"mockWindows","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"mocks.ts","line":150,"character":16,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/mocks.ts#L150"}],"signatures":[{"id":40,"name":"mockWindows","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Mocks one or many window labels.\nIn non-tauri context it is required to call this function *before* using the "},{"kind":"code","text":"`@tauri-apps/api/window`"},{"kind":"text","text":" module.\n\nThis function only mocks the *presence* of windows,\nwindow properties (e.g. width and height) can be mocked like regular IPC calls using the "},{"kind":"code","text":"`mockIPC`"},{"kind":"text","text":" function.\n\n# Examples\n\n"},{"kind":"code","text":"```js\nimport { mockWindows } from \"@tauri-apps/api/mocks\";\nimport { getCurrent } from \"@tauri-apps/api/window\";\n\nmockWindows(\"main\", \"second\", \"third\");\n\nconst win = getCurrent();\n\nwin.label // \"main\"\n```"},{"kind":"text","text":"\n\n"},{"kind":"code","text":"```js\nimport { mockWindows } from \"@tauri-apps/api/mocks\";\n\nmockWindows(\"main\", \"second\", \"third\");\n\nmockIPC((cmd, args) => {\n if (cmd === \"tauri\") {\n if (\n args?.__tauriModule === \"Window\" &&\n args?.message?.cmd === \"manage\" &&\n args?.message?.data?.cmd?.type === \"close\"\n ) {\n console.log('closing window!');\n }\n }\n});\n\nconst { getCurrent } = await import(\"@tauri-apps/api/window\");\n\nconst win = getCurrent();\nawait win.close(); // this will cause the mocked IPC handler to log to the console.\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"parameters":[{"id":41,"name":"current","kind":32768,"kindString":"Parameter","flags":{},"comment":{"summary":[{"kind":"text","text":"Label of window this JavaScript context is running in."}]},"type":{"type":"intrinsic","name":"string"}},{"id":42,"name":"additionalWindows","kind":32768,"kindString":"Parameter","flags":{"isRest":true},"comment":{"summary":[{"kind":"text","text":"Label of additional windows the app has."}]},"type":{"type":"array","elementType":{"type":"intrinsic","name":"string"}}}],"type":{"type":"intrinsic","name":"void"}}]}],"groups":[{"title":"Functions","children":[43,32,39]}],"sources":[{"fileName":"mocks.ts","line":6,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/mocks.ts#L6"}]},{"id":45,"name":"tauri","kind":2,"kindString":"Module","flags":{},"comment":{"summary":[{"kind":"text","text":"Invoke your custom commands.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.tauri`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":"."}]},"children":[{"id":54,"name":"Channel","kind":128,"kindString":"Class","flags":{},"children":[{"id":55,"name":"constructor","kind":512,"kindString":"Constructor","flags":{},"sources":[{"fileName":"tauri.ts","line":66,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L66"}],"signatures":[{"id":56,"name":"new Channel","kind":16384,"kindString":"Constructor signature","flags":{},"typeParameter":[{"id":57,"name":"T","kind":131072,"kindString":"Type parameter","flags":{},"default":{"type":"intrinsic","name":"unknown"}}],"type":{"type":"reference","id":54,"typeArguments":[{"type":"reference","id":57,"name":"T"}],"name":"Channel"}}]},{"id":60,"name":"#onmessage","kind":1024,"kindString":"Property","flags":{"isPrivate":true},"sources":[{"fileName":"tauri.ts","line":62,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L62"}],"type":{"type":"reflection","declaration":{"id":61,"name":"__type","kind":65536,"kindString":"Type literal","flags":{},"sources":[{"fileName":"tauri.ts","line":62,"character":14,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L62"}],"signatures":[{"id":62,"name":"__type","kind":4096,"kindString":"Call signature","flags":{},"parameters":[{"id":63,"name":"response","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reference","id":57,"name":"T"}}],"type":{"type":"intrinsic","name":"void"}}]}},"defaultValue":"..."},{"id":59,"name":"__TAURI_CHANNEL_MARKER__","kind":1024,"kindString":"Property","flags":{"isPrivate":true,"isReadonly":true},"sources":[{"fileName":"tauri.ts","line":61,"character":19,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L61"}],"type":{"type":"literal","value":true},"defaultValue":"true"},{"id":58,"name":"id","kind":1024,"kindString":"Property","flags":{},"sources":[{"fileName":"tauri.ts","line":59,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L59"}],"type":{"type":"intrinsic","name":"number"}},{"id":64,"name":"onmessage","kind":262144,"kindString":"Accessor","flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L72"},{"fileName":"tauri.ts","line":76,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L76"}],"getSignature":{"id":65,"name":"onmessage","kind":524288,"kindString":"Get signature","flags":{},"type":{"type":"reflection","declaration":{"id":66,"name":"__type","kind":65536,"kindString":"Type literal","flags":{},"sources":[{"fileName":"tauri.ts","line":76,"character":19,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L76"}],"signatures":[{"id":67,"name":"__type","kind":4096,"kindString":"Call signature","flags":{},"parameters":[{"id":68,"name":"response","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reference","id":57,"name":"T"}}],"type":{"type":"intrinsic","name":"void"}}]}}},"setSignature":{"id":69,"name":"onmessage","kind":1048576,"kindString":"Set signature","flags":{},"parameters":[{"id":70,"name":"handler","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reflection","declaration":{"id":71,"name":"__type","kind":65536,"kindString":"Type literal","flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":25,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L72"}],"signatures":[{"id":72,"name":"__type","kind":4096,"kindString":"Call signature","flags":{},"parameters":[{"id":73,"name":"response","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reference","id":57,"name":"T"}}],"type":{"type":"intrinsic","name":"void"}}]}}}],"type":{"type":"intrinsic","name":"void"}}},{"id":74,"name":"toJSON","kind":2048,"kindString":"Method","flags":{},"sources":[{"fileName":"tauri.ts","line":80,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L80"}],"signatures":[{"id":75,"name":"toJSON","kind":4096,"kindString":"Call signature","flags":{},"type":{"type":"intrinsic","name":"string"}}]}],"groups":[{"title":"Constructors","children":[55]},{"title":"Properties","children":[60,59,58]},{"title":"Accessors","children":[64]},{"title":"Methods","children":[74]}],"sources":[{"fileName":"tauri.ts","line":58,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L58"}],"typeParameters":[{"id":76,"name":"T","kind":131072,"kindString":"Type parameter","flags":{},"default":{"type":"intrinsic","name":"unknown"}}]},{"id":77,"name":"PluginListener","kind":128,"kindString":"Class","flags":{},"children":[{"id":78,"name":"constructor","kind":512,"kindString":"Constructor","flags":{},"sources":[{"fileName":"tauri.ts","line":90,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L90"}],"signatures":[{"id":79,"name":"new PluginListener","kind":16384,"kindString":"Constructor signature","flags":{},"parameters":[{"id":80,"name":"plugin","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":81,"name":"event","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":82,"name":"channelId","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"intrinsic","name":"number"}}],"type":{"type":"reference","id":77,"name":"PluginListener"}}]},{"id":85,"name":"channelId","kind":1024,"kindString":"Property","flags":{},"sources":[{"fileName":"tauri.ts","line":88,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L88"}],"type":{"type":"intrinsic","name":"number"}},{"id":84,"name":"event","kind":1024,"kindString":"Property","flags":{},"sources":[{"fileName":"tauri.ts","line":87,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L87"}],"type":{"type":"intrinsic","name":"string"}},{"id":83,"name":"plugin","kind":1024,"kindString":"Property","flags":{},"sources":[{"fileName":"tauri.ts","line":86,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L86"}],"type":{"type":"intrinsic","name":"string"}},{"id":86,"name":"unregister","kind":2048,"kindString":"Method","flags":{},"sources":[{"fileName":"tauri.ts","line":96,"character":8,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L96"}],"signatures":[{"id":87,"name":"unregister","kind":4096,"kindString":"Call signature","flags":{},"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise","qualifiedName":"Promise","package":"typescript"}}]}],"groups":[{"title":"Constructors","children":[78]},{"title":"Properties","children":[85,84,83]},{"title":"Methods","children":[86]}],"sources":[{"fileName":"tauri.ts","line":85,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L85"}]},{"id":46,"name":"InvokeArgs","kind":4194304,"kindString":"Type alias","flags":{},"comment":{"summary":[{"kind":"text","text":"Command arguments."}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":128,"character":5,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L128"}],"type":{"type":"reference","typeArguments":[{"type":"intrinsic","name":"string"},{"type":"intrinsic","name":"unknown"}],"name":"Record","qualifiedName":"Record","package":"typescript"}},{"id":88,"name":"addPluginListener","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"tauri.ts","line":111,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L111"}],"signatures":[{"id":89,"name":"addPluginListener","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Adds a listener to a plugin event."}],"blockTags":[{"tag":"@returns","content":[{"kind":"text","text":"The listener object to stop listening to the events."}]},{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"typeParameter":[{"id":90,"name":"T","kind":131072,"kindString":"Type parameter","flags":{}}],"parameters":[{"id":91,"name":"plugin","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":92,"name":"event","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":93,"name":"cb","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reflection","declaration":{"id":94,"name":"__type","kind":65536,"kindString":"Type literal","flags":{},"sources":[{"fileName":"tauri.ts","line":114,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L114"}],"signatures":[{"id":95,"name":"__type","kind":4096,"kindString":"Call signature","flags":{},"parameters":[{"id":96,"name":"payload","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"reference","id":90,"name":"T"}}],"type":{"type":"intrinsic","name":"void"}}]}}}],"type":{"type":"reference","typeArguments":[{"type":"reference","id":77,"name":"PluginListener"}],"name":"Promise","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise","qualifiedName":"Promise","package":"typescript"}}]},{"id":102,"name":"convertFileSrc","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"tauri.ts","line":194,"character":9,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L194"}],"signatures":[{"id":103,"name":"convertFileSrc","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Convert a device file path to an URL that can be loaded by the webview.\nNote that "},{"kind":"code","text":"`asset:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`https://asset.localhost`"},{"kind":"text","text":" must be added to ["},{"kind":"code","text":"`tauri.security.csp`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#securityconfig.csp) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":".\nExample CSP value: "},{"kind":"code","text":"`\"csp\": \"default-src 'self'; img-src 'self' asset: https://asset.localhost\"`"},{"kind":"text","text":" to use the asset protocol on image sources.\n\nAdditionally, "},{"kind":"code","text":"`asset`"},{"kind":"text","text":" must be added to ["},{"kind":"code","text":"`tauri.allowlist.protocol`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#allowlistconfig.protocol)\nin "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" and its access scope must be defined on the "},{"kind":"code","text":"`assetScope`"},{"kind":"text","text":" array on the same "},{"kind":"code","text":"`protocol`"},{"kind":"text","text":" object."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appDataDir, join } from '@tauri-apps/api/path';\nimport { convertFileSrc } from '@tauri-apps/api/tauri';\nconst appDataDirPath = await appDataDir();\nconst filePath = await join(appDataDirPath, 'assets/video.mp4');\nconst assetUrl = convertFileSrc(filePath);\n\nconst video = document.getElementById('my-video');\nconst source = document.createElement('source');\nsource.type = 'video/mp4';\nsource.src = assetUrl;\nvideo.appendChild(source);\nvideo.load();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"the URL that can be used as source on the webview."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"parameters":[{"id":104,"name":"filePath","kind":32768,"kindString":"Parameter","flags":{},"comment":{"summary":[{"kind":"text","text":"The file path."}]},"type":{"type":"intrinsic","name":"string"}},{"id":105,"name":"protocol","kind":32768,"kindString":"Parameter","flags":{},"comment":{"summary":[{"kind":"text","text":"The protocol to use. Defaults to "},{"kind":"code","text":"`asset`"},{"kind":"text","text":". You only need to set this when using a custom protocol."}]},"type":{"type":"intrinsic","name":"string"},"defaultValue":"'asset'"}],"type":{"type":"intrinsic","name":"string"}}]},{"id":97,"name":"invoke","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"tauri.ts","line":144,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L144"}],"signatures":[{"id":98,"name":"invoke","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Sends a message to the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { invoke } from '@tauri-apps/api/tauri';\nawait invoke('login', { user: 'tauri', password: 'poiwe3h4r5ip3yrhtew9ty' });\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving or rejecting to the backend response."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"typeParameter":[{"id":99,"name":"T","kind":131072,"kindString":"Type parameter","flags":{}}],"parameters":[{"id":100,"name":"cmd","kind":32768,"kindString":"Parameter","flags":{},"comment":{"summary":[{"kind":"text","text":"The command name."}]},"type":{"type":"intrinsic","name":"string"}},{"id":101,"name":"args","kind":32768,"kindString":"Parameter","flags":{},"comment":{"summary":[{"kind":"text","text":"The optional arguments to pass to the command."}]},"type":{"type":"reference","id":46,"name":"InvokeArgs"},"defaultValue":"{}"}],"type":{"type":"reference","typeArguments":[{"type":"reference","id":99,"name":"T"}],"name":"Promise","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise","qualifiedName":"Promise","package":"typescript"}}]},{"id":47,"name":"transformCallback","kind":64,"kindString":"Function","flags":{},"sources":[{"fileName":"tauri.ts","line":36,"character":9,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L36"}],"signatures":[{"id":48,"name":"transformCallback","kind":4096,"kindString":"Call signature","flags":{},"comment":{"summary":[{"kind":"text","text":"Transforms a callback function to a string identifier that can be passed to the backend.\nThe backend uses the identifier to "},{"kind":"code","text":"`eval()`"},{"kind":"text","text":" the callback."}],"blockTags":[{"tag":"@returns","content":[{"kind":"text","text":"A unique identifier associated with the callback function."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"parameters":[{"id":49,"name":"callback","kind":32768,"kindString":"Parameter","flags":{"isOptional":true},"type":{"type":"reflection","declaration":{"id":50,"name":"__type","kind":65536,"kindString":"Type literal","flags":{},"sources":[{"fileName":"tauri.ts","line":37,"character":13,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L37"}],"signatures":[{"id":51,"name":"__type","kind":4096,"kindString":"Call signature","flags":{},"parameters":[{"id":52,"name":"response","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"intrinsic","name":"any"}}],"type":{"type":"intrinsic","name":"void"}}]}}},{"id":53,"name":"once","kind":32768,"kindString":"Parameter","flags":{},"type":{"type":"intrinsic","name":"boolean"},"defaultValue":"false"}],"type":{"type":"intrinsic","name":"number"}}]}],"groups":[{"title":"Classes","children":[54,77]},{"title":"Type Aliases","children":[46]},{"title":"Functions","children":[88,102,97,47]}],"sources":[{"fileName":"tauri.ts","line":13,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/eb4a00240/tooling/api/src/tauri.ts#L13"}]}],"groups":[{"title":"Modules","children":[1,31,45]}]} \ No newline at end of file +{"id":0,"name":"@tauri-apps/api","variant":"project","kind":1,"flags":{},"children":[{"id":1,"name":"event","variant":"declaration","kind":2,"flags":{},"comment":{"summary":[{"kind":"text","text":"The event system allows you to emit events to the backend and listen to events from it.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.event`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":"."}]},"children":[{"id":3,"name":"TauriEvent","variant":"declaration","kind":8,"flags":{},"comment":{"summary":[],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.1.0"}]}]},"children":[{"id":16,"name":"MENU","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":33,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L33"}],"type":{"type":"literal","value":"tauri://menu"}},{"id":10,"name":"WINDOW_BLUR","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":27,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L27"}],"type":{"type":"literal","value":"tauri://blur"}},{"id":6,"name":"WINDOW_CLOSE_REQUESTED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":23,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L23"}],"type":{"type":"literal","value":"tauri://close-requested"}},{"id":7,"name":"WINDOW_CREATED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":24,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L24"}],"type":{"type":"literal","value":"tauri://window-created"}},{"id":8,"name":"WINDOW_DESTROYED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":25,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L25"}],"type":{"type":"literal","value":"tauri://destroyed"}},{"id":13,"name":"WINDOW_FILE_DROP","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":30,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L30"}],"type":{"type":"literal","value":"tauri://file-drop"}},{"id":15,"name":"WINDOW_FILE_DROP_CANCELLED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":32,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L32"}],"type":{"type":"literal","value":"tauri://file-drop-cancelled"}},{"id":14,"name":"WINDOW_FILE_DROP_HOVER","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":31,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L31"}],"type":{"type":"literal","value":"tauri://file-drop-hover"}},{"id":9,"name":"WINDOW_FOCUS","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":26,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L26"}],"type":{"type":"literal","value":"tauri://focus"}},{"id":5,"name":"WINDOW_MOVED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":22,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L22"}],"type":{"type":"literal","value":"tauri://move"}},{"id":4,"name":"WINDOW_RESIZED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":21,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L21"}],"type":{"type":"literal","value":"tauri://resize"}},{"id":11,"name":"WINDOW_SCALE_FACTOR_CHANGED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":28,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L28"}],"type":{"type":"literal","value":"tauri://scale-change"}},{"id":12,"name":"WINDOW_THEME_CHANGED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":29,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L29"}],"type":{"type":"literal","value":"tauri://theme-changed"}}],"groups":[{"title":"Enumeration Members","children":[16,10,6,7,8,13,15,14,9,5,4,11,12]}],"sources":[{"fileName":"event.ts","line":20,"character":12,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L20"}]},{"id":106,"name":"Event","variant":"declaration","kind":256,"flags":{},"children":[{"id":107,"name":"event","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name"}]},"sources":[{"fileName":"helpers/event.ts","line":10,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/helpers/event.ts#L10"}],"type":{"type":"reference","target":2,"name":"EventName","package":"@tauri-apps/api"}},{"id":109,"name":"id","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event identifier used to unlisten"}]},"sources":[{"fileName":"helpers/event.ts","line":14,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/helpers/event.ts#L14"}],"type":{"type":"intrinsic","name":"number"}},{"id":110,"name":"payload","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event payload"}]},"sources":[{"fileName":"helpers/event.ts","line":16,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/helpers/event.ts#L16"}],"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}},{"id":108,"name":"windowLabel","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"The label of the window that emitted this event."}]},"sources":[{"fileName":"helpers/event.ts","line":12,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/helpers/event.ts#L12"}],"type":{"type":"intrinsic","name":"string"}}],"groups":[{"title":"Properties","children":[107,109,110,108]}],"sources":[{"fileName":"helpers/event.ts","line":8,"character":17,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/helpers/event.ts#L8"}],"typeParameters":[{"id":111,"name":"T","variant":"typeParam","kind":131072,"flags":{}}]},{"id":112,"name":"EventCallback","variant":"declaration","kind":4194304,"flags":{},"sources":[{"fileName":"helpers/event.ts","line":19,"character":12,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/helpers/event.ts#L19"}],"typeParameters":[{"id":116,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"type":{"type":"reflection","declaration":{"id":113,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"helpers/event.ts","line":19,"character":31,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/helpers/event.ts#L19"}],"signatures":[{"id":114,"name":"__type","variant":"signature","kind":4096,"flags":{},"parameters":[{"id":115,"name":"event","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":106,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"Event","package":"@tauri-apps/api"}}],"type":{"type":"intrinsic","name":"void"}}]}}},{"id":2,"name":"EventName","variant":"declaration","kind":4194304,"flags":{},"sources":[{"fileName":"event.ts","line":15,"character":12,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L15"}],"type":{"type":"union","types":[{"type":"templateLiteral","head":"","tail":[[{"type":"reference","target":3,"name":"TauriEvent","package":"@tauri-apps/api"},""]]},{"type":"intersection","types":[{"type":"intrinsic","name":"string"},{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"never"},{"type":"intrinsic","name":"never"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"}]}]}},{"id":117,"name":"UnlistenFn","variant":"declaration","kind":4194304,"flags":{},"sources":[{"fileName":"helpers/event.ts","line":21,"character":12,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/helpers/event.ts#L21"}],"type":{"type":"reflection","declaration":{"id":118,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"helpers/event.ts","line":21,"character":25,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/helpers/event.ts#L21"}],"signatures":[{"id":119,"name":"__type","variant":"signature","kind":4096,"flags":{},"type":{"type":"intrinsic","name":"void"}}]}}},{"id":27,"name":"emit","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"event.ts","line":107,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L107"}],"signatures":[{"id":28,"name":"emit","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Emits an event to the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { emit } from '@tauri-apps/api/event';\nawait emit('frontend-loaded', { loggedIn: true, token: 'authToken' });\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"event.ts","line":107,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L107"}],"parameters":[{"id":29,"name":"event","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"intrinsic","name":"string"}},{"id":30,"name":"payload","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"intrinsic","name":"unknown"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":17,"name":"listen","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"event.ts","line":57,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L57"}],"signatures":[{"id":18,"name":"listen","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Listen to an event from the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { listen } from '@tauri-apps/api/event';\nconst unlisten = await listen('error', (event) => {\n console.log(`Got error in window ${event.windowLabel}, payload: ${event.payload}`);\n});\n\n// you need to call unlisten if your handler goes out of scope e.g. the component is unmounted\nunlisten();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving to a function to unlisten to the event.\nNote that removing the listener is required if your listener goes out of scope e.g. the component is unmounted."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"event.ts","line":57,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L57"}],"typeParameter":[{"id":19,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":20,"name":"event","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"reference","target":2,"name":"EventName","package":"@tauri-apps/api"}},{"id":21,"name":"handler","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event handler callback."}]},"type":{"type":"reference","target":112,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"EventCallback","package":"@tauri-apps/api"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":117,"name":"UnlistenFn","package":"@tauri-apps/api"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":22,"name":"once","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"event.ts","line":88,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L88"}],"signatures":[{"id":23,"name":"once","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Listen to an one-off event from the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { once } from '@tauri-apps/api/event';\ninterface LoadedPayload {\n loggedIn: boolean,\n token: string\n}\nconst unlisten = await once('loaded', (event) => {\n console.log(`App is loaded, loggedIn: ${event.payload.loggedIn}, token: ${event.payload.token}`);\n});\n\n// you need to call unlisten if your handler goes out of scope e.g. the component is unmounted\nunlisten();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving to a function to unlisten to the event.\nNote that removing the listener is required if your listener goes out of scope e.g. the component is unmounted."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"event.ts","line":88,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L88"}],"typeParameter":[{"id":24,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":25,"name":"event","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"reference","target":2,"name":"EventName","package":"@tauri-apps/api"}},{"id":26,"name":"handler","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":112,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"EventCallback","package":"@tauri-apps/api"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":117,"name":"UnlistenFn","package":"@tauri-apps/api"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]}],"groups":[{"title":"Enumerations","children":[3]},{"title":"Interfaces","children":[106]},{"title":"Type Aliases","children":[112,2,117]},{"title":"Functions","children":[27,17,22]}],"sources":[{"fileName":"event.ts","line":1,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/event.ts#L1"}]},{"id":31,"name":"mocks","variant":"declaration","kind":2,"flags":{},"children":[{"id":43,"name":"clearMocks","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"mocks.ts","line":186,"character":16,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/mocks.ts#L186"}],"signatures":[{"id":44,"name":"clearMocks","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Clears mocked functions/data injected by the other functions in this module.\nWhen using a test runner that doesn't provide a fresh window object for each test, calling this function will reset tauri specific properties.\n\n# Example\n\n"},{"kind":"code","text":"```js\nimport { mockWindows, clearMocks } from \"@tauri-apps/api/mocks\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked windows\", () => {\n mockWindows(\"main\", \"second\", \"third\");\n\n expect(window).toHaveProperty(\"__TAURI_METADATA__\")\n})\n\ntest(\"no mocked windows\", () => {\n expect(window).not.toHaveProperty(\"__TAURI_METADATA__\")\n})\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"mocks.ts","line":186,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/mocks.ts#L186"}],"type":{"type":"intrinsic","name":"void"}}]},{"id":32,"name":"mockIPC","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"mocks.ts","line":80,"character":16,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/mocks.ts#L80"}],"signatures":[{"id":33,"name":"mockIPC","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Intercepts all IPC requests with the given mock handler.\n\nThis function can be used when testing tauri frontend applications or when running the frontend in a Node.js context during static site generation.\n\n# Examples\n\nTesting setup using vitest:\n"},{"kind":"code","text":"```js\nimport { mockIPC, clearMocks } from \"@tauri-apps/api/mocks\"\nimport { invoke } from \"@tauri-apps/api/tauri\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked command\", () => {\n mockIPC((cmd, args) => {\n switch (cmd) {\n case \"add\":\n return (args.a as number) + (args.b as number);\n default:\n break;\n }\n });\n\n expect(invoke('add', { a: 12, b: 15 })).resolves.toBe(27);\n})\n```"},{"kind":"text","text":"\n\nThe callback function can also return a Promise:\n"},{"kind":"code","text":"```js\nimport { mockIPC, clearMocks } from \"@tauri-apps/api/mocks\"\nimport { invoke } from \"@tauri-apps/api/tauri\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked command\", () => {\n mockIPC((cmd, args) => {\n if(cmd === \"get_data\") {\n return fetch(\"https://example.com/data.json\")\n .then((response) => response.json())\n }\n });\n\n expect(invoke('get_data')).resolves.toBe({ foo: 'bar' });\n})\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"mocks.ts","line":80,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/mocks.ts#L80"}],"parameters":[{"id":34,"name":"cb","variant":"param","kind":32768,"flags":{},"type":{"type":"reflection","declaration":{"id":35,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"mocks.ts","line":81,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/mocks.ts#L81"}],"signatures":[{"id":36,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"mocks.ts","line":81,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/mocks.ts#L81"}],"parameters":[{"id":37,"name":"cmd","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":38,"name":"args","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"string"},{"type":"intrinsic","name":"unknown"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"}}],"type":{"type":"intrinsic","name":"any"}}]}}}],"type":{"type":"intrinsic","name":"void"}}]},{"id":39,"name":"mockWindows","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"mocks.ts","line":150,"character":16,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/mocks.ts#L150"}],"signatures":[{"id":40,"name":"mockWindows","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Mocks one or many window labels.\nIn non-tauri context it is required to call this function *before* using the "},{"kind":"code","text":"`@tauri-apps/api/window`"},{"kind":"text","text":" module.\n\nThis function only mocks the *presence* of windows,\nwindow properties (e.g. width and height) can be mocked like regular IPC calls using the "},{"kind":"code","text":"`mockIPC`"},{"kind":"text","text":" function.\n\n# Examples\n\n"},{"kind":"code","text":"```js\nimport { mockWindows } from \"@tauri-apps/api/mocks\";\nimport { getCurrent } from \"@tauri-apps/api/window\";\n\nmockWindows(\"main\", \"second\", \"third\");\n\nconst win = getCurrent();\n\nwin.label // \"main\"\n```"},{"kind":"text","text":"\n\n"},{"kind":"code","text":"```js\nimport { mockWindows } from \"@tauri-apps/api/mocks\";\n\nmockWindows(\"main\", \"second\", \"third\");\n\nmockIPC((cmd, args) => {\n if (cmd === \"tauri\") {\n if (\n args?.__tauriModule === \"Window\" &&\n args?.message?.cmd === \"manage\" &&\n args?.message?.data?.cmd?.type === \"close\"\n ) {\n console.log('closing window!');\n }\n }\n});\n\nconst { getCurrent } = await import(\"@tauri-apps/api/window\");\n\nconst win = getCurrent();\nawait win.close(); // this will cause the mocked IPC handler to log to the console.\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"mocks.ts","line":150,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/mocks.ts#L150"}],"parameters":[{"id":41,"name":"current","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Label of window this JavaScript context is running in."}]},"type":{"type":"intrinsic","name":"string"}},{"id":42,"name":"additionalWindows","variant":"param","kind":32768,"flags":{"isRest":true},"comment":{"summary":[{"kind":"text","text":"Label of additional windows the app has."}]},"type":{"type":"array","elementType":{"type":"intrinsic","name":"string"}}}],"type":{"type":"intrinsic","name":"void"}}]}],"groups":[{"title":"Functions","children":[43,32,39]}],"sources":[{"fileName":"mocks.ts","line":1,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/mocks.ts#L1"}]},{"id":45,"name":"tauri","variant":"declaration","kind":2,"flags":{},"comment":{"summary":[{"kind":"text","text":"Invoke your custom commands.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.tauri`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":"."}]},"children":[{"id":54,"name":"Channel","variant":"declaration","kind":128,"flags":{},"children":[{"id":55,"name":"constructor","variant":"declaration","kind":512,"flags":{},"sources":[{"fileName":"tauri.ts","line":66,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L66"}],"signatures":[{"id":56,"name":"new Channel","variant":"signature","kind":16384,"flags":{},"sources":[{"fileName":"tauri.ts","line":66,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L66"}],"typeParameter":[{"id":57,"name":"T","variant":"typeParam","kind":131072,"flags":{},"default":{"type":"intrinsic","name":"unknown"}}],"type":{"type":"reference","target":54,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"Channel","package":"@tauri-apps/api"}}]},{"id":60,"name":"#onmessage","variant":"declaration","kind":1024,"flags":{"isPrivate":true},"sources":[{"fileName":"tauri.ts","line":62,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L62"}],"type":{"type":"reflection","declaration":{"id":61,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":62,"character":14,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L62"}],"signatures":[{"id":62,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":62,"character":14,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L62"}],"parameters":[{"id":63,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}},"defaultValue":"..."},{"id":59,"name":"__TAURI_CHANNEL_MARKER__","variant":"declaration","kind":1024,"flags":{"isPrivate":true,"isReadonly":true},"sources":[{"fileName":"tauri.ts","line":61,"character":19,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L61"}],"type":{"type":"literal","value":true},"defaultValue":"true"},{"id":58,"name":"id","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":59,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L59"}],"type":{"type":"intrinsic","name":"number"}},{"id":64,"name":"onmessage","variant":"declaration","kind":262144,"flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L72"},{"fileName":"tauri.ts","line":76,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L76"}],"getSignature":{"id":65,"name":"onmessage","variant":"signature","kind":524288,"flags":{},"sources":[{"fileName":"tauri.ts","line":76,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L76"}],"type":{"type":"reflection","declaration":{"id":66,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":76,"character":19,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L76"}],"signatures":[{"id":67,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":76,"character":19,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L76"}],"parameters":[{"id":68,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}}},"setSignature":{"id":69,"name":"onmessage","variant":"signature","kind":1048576,"flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L72"}],"parameters":[{"id":70,"name":"handler","variant":"param","kind":32768,"flags":{},"type":{"type":"reflection","declaration":{"id":71,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":25,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L72"}],"signatures":[{"id":72,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":25,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L72"}],"parameters":[{"id":73,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}}}],"type":{"type":"intrinsic","name":"void"}}},{"id":74,"name":"toJSON","variant":"declaration","kind":2048,"flags":{},"sources":[{"fileName":"tauri.ts","line":80,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L80"}],"signatures":[{"id":75,"name":"toJSON","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":80,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L80"}],"type":{"type":"intrinsic","name":"string"}}]}],"groups":[{"title":"Constructors","children":[55]},{"title":"Properties","children":[60,59,58]},{"title":"Accessors","children":[64]},{"title":"Methods","children":[74]}],"sources":[{"fileName":"tauri.ts","line":58,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L58"}],"typeParameters":[{"id":76,"name":"T","variant":"typeParam","kind":131072,"flags":{},"default":{"type":"intrinsic","name":"unknown"}}]},{"id":77,"name":"PluginListener","variant":"declaration","kind":128,"flags":{},"children":[{"id":78,"name":"constructor","variant":"declaration","kind":512,"flags":{},"sources":[{"fileName":"tauri.ts","line":90,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L90"}],"signatures":[{"id":79,"name":"new PluginListener","variant":"signature","kind":16384,"flags":{},"sources":[{"fileName":"tauri.ts","line":90,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L90"}],"parameters":[{"id":80,"name":"plugin","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":81,"name":"event","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":82,"name":"channelId","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"number"}}],"type":{"type":"reference","target":77,"name":"PluginListener","package":"@tauri-apps/api"}}]},{"id":85,"name":"channelId","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":88,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L88"}],"type":{"type":"intrinsic","name":"number"}},{"id":84,"name":"event","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":87,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L87"}],"type":{"type":"intrinsic","name":"string"}},{"id":83,"name":"plugin","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":86,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L86"}],"type":{"type":"intrinsic","name":"string"}},{"id":86,"name":"unregister","variant":"declaration","kind":2048,"flags":{},"sources":[{"fileName":"tauri.ts","line":96,"character":8,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L96"}],"signatures":[{"id":87,"name":"unregister","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":96,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L96"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]}],"groups":[{"title":"Constructors","children":[78]},{"title":"Properties","children":[85,84,83]},{"title":"Methods","children":[86]}],"sources":[{"fileName":"tauri.ts","line":85,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L85"}]},{"id":46,"name":"InvokeArgs","variant":"declaration","kind":4194304,"flags":{},"comment":{"summary":[{"kind":"text","text":"Command arguments."}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":128,"character":5,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L128"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"string"},{"type":"intrinsic","name":"unknown"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"}},{"id":88,"name":"addPluginListener","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":111,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L111"}],"signatures":[{"id":89,"name":"addPluginListener","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Adds a listener to a plugin event."}],"blockTags":[{"tag":"@returns","content":[{"kind":"text","text":"The listener object to stop listening to the events."}]},{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":111,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L111"}],"typeParameter":[{"id":90,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":91,"name":"plugin","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":92,"name":"event","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":93,"name":"cb","variant":"param","kind":32768,"flags":{},"type":{"type":"reflection","declaration":{"id":94,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":114,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L114"}],"signatures":[{"id":95,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":114,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L114"}],"parameters":[{"id":96,"name":"payload","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":77,"name":"PluginListener","package":"@tauri-apps/api"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":102,"name":"convertFileSrc","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":194,"character":9,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L194"}],"signatures":[{"id":103,"name":"convertFileSrc","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Convert a device file path to an URL that can be loaded by the webview.\nNote that "},{"kind":"code","text":"`asset:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`https://asset.localhost`"},{"kind":"text","text":" must be added to ["},{"kind":"code","text":"`tauri.security.csp`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#securityconfig.csp) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":".\nExample CSP value: "},{"kind":"code","text":"`\"csp\": \"default-src 'self'; img-src 'self' asset: https://asset.localhost\"`"},{"kind":"text","text":" to use the asset protocol on image sources.\n\nAdditionally, "},{"kind":"code","text":"`asset`"},{"kind":"text","text":" must be added to ["},{"kind":"code","text":"`tauri.allowlist.protocol`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#allowlistconfig.protocol)\nin "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" and its access scope must be defined on the "},{"kind":"code","text":"`assetScope`"},{"kind":"text","text":" array on the same "},{"kind":"code","text":"`protocol`"},{"kind":"text","text":" object."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appDataDir, join } from '@tauri-apps/api/path';\nimport { convertFileSrc } from '@tauri-apps/api/tauri';\nconst appDataDirPath = await appDataDir();\nconst filePath = await join(appDataDirPath, 'assets/video.mp4');\nconst assetUrl = convertFileSrc(filePath);\n\nconst video = document.getElementById('my-video');\nconst source = document.createElement('source');\nsource.type = 'video/mp4';\nsource.src = assetUrl;\nvideo.appendChild(source);\nvideo.load();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"the URL that can be used as source on the webview."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":194,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L194"}],"parameters":[{"id":104,"name":"filePath","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The file path."}]},"type":{"type":"intrinsic","name":"string"}},{"id":105,"name":"protocol","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The protocol to use. Defaults to "},{"kind":"code","text":"`asset`"},{"kind":"text","text":". You only need to set this when using a custom protocol."}]},"type":{"type":"intrinsic","name":"string"},"defaultValue":"'asset'"}],"type":{"type":"intrinsic","name":"string"}}]},{"id":97,"name":"invoke","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":144,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L144"}],"signatures":[{"id":98,"name":"invoke","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Sends a message to the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { invoke } from '@tauri-apps/api/tauri';\nawait invoke('login', { user: 'tauri', password: 'poiwe3h4r5ip3yrhtew9ty' });\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving or rejecting to the backend response."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":144,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L144"}],"typeParameter":[{"id":99,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":100,"name":"cmd","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The command name."}]},"type":{"type":"intrinsic","name":"string"}},{"id":101,"name":"args","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The optional arguments to pass to the command."}]},"type":{"type":"reference","target":46,"name":"InvokeArgs","package":"@tauri-apps/api"},"defaultValue":"{}"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":47,"name":"transformCallback","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":36,"character":9,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L36"}],"signatures":[{"id":48,"name":"transformCallback","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Transforms a callback function to a string identifier that can be passed to the backend.\nThe backend uses the identifier to "},{"kind":"code","text":"`eval()`"},{"kind":"text","text":" the callback."}],"blockTags":[{"tag":"@returns","content":[{"kind":"text","text":"A unique identifier associated with the callback function."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":36,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L36"}],"parameters":[{"id":49,"name":"callback","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"reflection","declaration":{"id":50,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":37,"character":13,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L37"}],"signatures":[{"id":51,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":37,"character":13,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L37"}],"parameters":[{"id":52,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"any"}}],"type":{"type":"intrinsic","name":"void"}}]}}},{"id":53,"name":"once","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"boolean"},"defaultValue":"false"}],"type":{"type":"intrinsic","name":"number"}}]}],"groups":[{"title":"Classes","children":[54,77]},{"title":"Type Aliases","children":[46]},{"title":"Functions","children":[88,102,97,47]}],"sources":[{"fileName":"tauri.ts","line":1,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/1ed2600da/tooling/api/src/tauri.ts#L1"}]}],"groups":[{"title":"Modules","children":[1,31,45]}],"packageName":"@tauri-apps/api","symbolIdMap":{"1":{"sourceFileName":"src/event.ts","qualifiedName":""},"2":{"sourceFileName":"src/event.ts","qualifiedName":"EventName"},"3":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent"},"4":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_RESIZED"},"5":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_MOVED"},"6":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_CLOSE_REQUESTED"},"7":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_CREATED"},"8":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_DESTROYED"},"9":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FOCUS"},"10":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_BLUR"},"11":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_SCALE_FACTOR_CHANGED"},"12":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_THEME_CHANGED"},"13":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FILE_DROP"},"14":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FILE_DROP_HOVER"},"15":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FILE_DROP_CANCELLED"},"16":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.MENU"},"17":{"sourceFileName":"src/event.ts","qualifiedName":"listen"},"18":{"sourceFileName":"src/event.ts","qualifiedName":"listen"},"19":{"sourceFileName":"src/event.ts","qualifiedName":"T"},"20":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"21":{"sourceFileName":"src/event.ts","qualifiedName":"handler"},"22":{"sourceFileName":"src/event.ts","qualifiedName":"once"},"23":{"sourceFileName":"src/event.ts","qualifiedName":"once"},"24":{"sourceFileName":"src/event.ts","qualifiedName":"T"},"25":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"26":{"sourceFileName":"src/event.ts","qualifiedName":"handler"},"27":{"sourceFileName":"src/event.ts","qualifiedName":"emit"},"28":{"sourceFileName":"src/event.ts","qualifiedName":"emit"},"29":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"30":{"sourceFileName":"src/event.ts","qualifiedName":"payload"},"31":{"sourceFileName":"src/mocks.ts","qualifiedName":""},"32":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockIPC"},"33":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockIPC"},"34":{"sourceFileName":"src/mocks.ts","qualifiedName":"cb"},"35":{"sourceFileName":"src/mocks.ts","qualifiedName":"__type"},"36":{"sourceFileName":"src/mocks.ts","qualifiedName":"__type"},"37":{"sourceFileName":"src/mocks.ts","qualifiedName":"cmd"},"38":{"sourceFileName":"src/mocks.ts","qualifiedName":"args"},"39":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockWindows"},"40":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockWindows"},"41":{"sourceFileName":"src/mocks.ts","qualifiedName":"current"},"42":{"sourceFileName":"src/mocks.ts","qualifiedName":"additionalWindows"},"43":{"sourceFileName":"src/mocks.ts","qualifiedName":"clearMocks"},"44":{"sourceFileName":"src/mocks.ts","qualifiedName":"clearMocks"},"45":{"sourceFileName":"src/tauri.ts","qualifiedName":""},"46":{"sourceFileName":"src/tauri.ts","qualifiedName":"InvokeArgs"},"47":{"sourceFileName":"src/tauri.ts","qualifiedName":"transformCallback"},"48":{"sourceFileName":"src/tauri.ts","qualifiedName":"transformCallback"},"49":{"sourceFileName":"src/tauri.ts","qualifiedName":"callback"},"50":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"51":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"52":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"53":{"sourceFileName":"src/tauri.ts","qualifiedName":"once"},"54":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel"},"55":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.__constructor"},"56":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel"},"57":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.T"},"58":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.id"},"59":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.__TAURI_CHANNEL_MARKER__"},"60":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.#onmessage"},"61":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"62":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"63":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"64":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.onmessage"},"65":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.onmessage"},"66":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"67":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"68":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"69":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.onmessage"},"70":{"sourceFileName":"src/tauri.ts","qualifiedName":"handler"},"71":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"72":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"73":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"74":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.toJSON"},"75":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.toJSON"},"76":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.T"},"77":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener"},"78":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.__constructor"},"79":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener"},"80":{"sourceFileName":"src/tauri.ts","qualifiedName":"plugin"},"81":{"sourceFileName":"src/tauri.ts","qualifiedName":"event"},"82":{"sourceFileName":"src/tauri.ts","qualifiedName":"channelId"},"83":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.plugin"},"84":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.event"},"85":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.channelId"},"86":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.unregister"},"87":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.unregister"},"88":{"sourceFileName":"src/tauri.ts","qualifiedName":"addPluginListener"},"89":{"sourceFileName":"src/tauri.ts","qualifiedName":"addPluginListener"},"90":{"sourceFileName":"src/tauri.ts","qualifiedName":"T"},"91":{"sourceFileName":"src/tauri.ts","qualifiedName":"plugin"},"92":{"sourceFileName":"src/tauri.ts","qualifiedName":"event"},"93":{"sourceFileName":"src/tauri.ts","qualifiedName":"cb"},"94":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"95":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"96":{"sourceFileName":"src/tauri.ts","qualifiedName":"payload"},"97":{"sourceFileName":"src/tauri.ts","qualifiedName":"invoke"},"98":{"sourceFileName":"src/tauri.ts","qualifiedName":"invoke"},"99":{"sourceFileName":"src/tauri.ts","qualifiedName":"T"},"100":{"sourceFileName":"src/tauri.ts","qualifiedName":"cmd"},"101":{"sourceFileName":"src/tauri.ts","qualifiedName":"args"},"102":{"sourceFileName":"src/tauri.ts","qualifiedName":"convertFileSrc"},"103":{"sourceFileName":"src/tauri.ts","qualifiedName":"convertFileSrc"},"104":{"sourceFileName":"src/tauri.ts","qualifiedName":"filePath"},"105":{"sourceFileName":"src/tauri.ts","qualifiedName":"protocol"},"106":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"Event"},"107":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"Event.event"},"108":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"Event.windowLabel"},"109":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"Event.id"},"110":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"Event.payload"},"111":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"Event.T"},"112":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"EventCallback"},"113":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"__type"},"114":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"__type"},"115":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"event"},"116":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"T"},"117":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"UnlistenFn"},"118":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"__type"},"119":{"sourceFileName":"src/helpers/event.ts","qualifiedName":"__type"}}} \ No newline at end of file diff --git a/tooling/api/package.json b/tooling/api/package.json index 9ea3cf396..e91dc4fb8 100644 --- a/tooling/api/package.json +++ b/tooling/api/package.json @@ -18,7 +18,7 @@ "lint-fix": "eslint --fix --ext ts \"./src/**/*.ts\"", "format": "prettier --write --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path ../../.prettierignore", "format:check": "prettier --check --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path ../../.prettierignore", - "generate-docs": "typedoc" + "generate-docs": "typedoc --plugin typedoc-plugin-markdown --plugin typedoc-plugin-mdn-links" }, "repository": { "type": "git", @@ -41,9 +41,9 @@ "yarn": ">= 1.19.1" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "5.59.0", - "@typescript-eslint/parser": "5.59.0", - "eslint": "8.38.0", + "@typescript-eslint/eslint-plugin": "5.59.7", + "@typescript-eslint/parser": "5.59.7", + "eslint": "8.41.0", "eslint-config-prettier": "8.8.0", "eslint-config-standard-with-typescript": "34.0.1", "eslint-plugin-import": "2.27.5", @@ -51,11 +51,11 @@ "eslint-plugin-node": "11.1.0", "eslint-plugin-promise": "6.1.1", "eslint-plugin-security": "1.7.1", - "prettier": "2.8.7", + "prettier": "2.8.8", "tsup": "6.7.0", - "typedoc": "0.23.28", - "typedoc-plugin-markdown": "3.14.0", - "typedoc-plugin-mdn-links": "2.0.2", - "typescript": "4.9.5" + "typedoc": "0.24.7", + "typedoc-plugin-markdown": "3.15.3", + "typedoc-plugin-mdn-links": "3.0.3", + "typescript": "5.0.4" } } diff --git a/tooling/api/yarn.lock b/tooling/api/yarn.lock index 7ce536354..2218923be 100644 --- a/tooling/api/yarn.lock +++ b/tooling/api/yarn.lock @@ -124,14 +124,14 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== -"@eslint/eslintrc@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" - integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== +"@eslint/eslintrc@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331" + integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.1" + espree "^9.5.2" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -139,10 +139,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.38.0": - version "8.38.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892" - integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g== +"@eslint/js@8.41.0": + version "8.41.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.41.0.tgz#080321c3b68253522f7646b55b577dd99d2950b3" + integrity sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA== "@humanwhocodes/config-array@^0.11.8": version "0.11.8" @@ -199,15 +199,15 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.12.tgz#920447fdd78d76b19de0438b7f60df3c4a80bf1c" integrity sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A== -"@typescript-eslint/eslint-plugin@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz#c0e10eeb936debe5d1c3433cf36206a95befefd0" - integrity sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw== +"@typescript-eslint/eslint-plugin@5.59.7": + version "5.59.7" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.7.tgz#e470af414f05ecfdc05a23e9ce6ec8f91db56fe2" + integrity sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/type-utils" "5.59.0" - "@typescript-eslint/utils" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.7" + "@typescript-eslint/type-utils" "5.59.7" + "@typescript-eslint/utils" "5.59.7" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" @@ -215,14 +215,14 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/parser@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.0.tgz#0ad7cd019346cc5d150363f64869eca10ca9977c" - integrity sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w== +"@typescript-eslint/parser@5.59.7": + version "5.59.7" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.7.tgz#02682554d7c1028b89aa44a48bf598db33048caa" + integrity sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ== dependencies: - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.7" + "@typescript-eslint/types" "5.59.7" + "@typescript-eslint/typescript-estree" "5.59.7" debug "^4.3.4" "@typescript-eslint/parser@^5.43.0": @@ -243,21 +243,21 @@ "@typescript-eslint/types" "5.57.1" "@typescript-eslint/visitor-keys" "5.57.1" -"@typescript-eslint/scope-manager@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.0.tgz#86501d7a17885710b6716a23be2e93fc54a4fe8c" - integrity sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ== +"@typescript-eslint/scope-manager@5.59.7": + version "5.59.7" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz#0243f41f9066f3339d2f06d7f72d6c16a16769e2" + integrity sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ== dependencies: - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/visitor-keys" "5.59.0" + "@typescript-eslint/types" "5.59.7" + "@typescript-eslint/visitor-keys" "5.59.7" -"@typescript-eslint/type-utils@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz#8e8d1420fc2265989fa3a0d897bde37f3851e8c9" - integrity sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA== +"@typescript-eslint/type-utils@5.59.7": + version "5.59.7" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.7.tgz#89c97291371b59eb18a68039857c829776f1426d" + integrity sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ== dependencies: - "@typescript-eslint/typescript-estree" "5.59.0" - "@typescript-eslint/utils" "5.59.0" + "@typescript-eslint/typescript-estree" "5.59.7" + "@typescript-eslint/utils" "5.59.7" debug "^4.3.4" tsutils "^3.21.0" @@ -266,10 +266,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.57.1.tgz#d9989c7a9025897ea6f0550b7036027f69e8a603" integrity sha512-bSs4LOgyV3bJ08F5RDqO2KXqg3WAdwHCu06zOqcQ6vqbTJizyBhuh1o1ImC69X4bV2g1OJxbH71PJqiO7Y1RuA== -"@typescript-eslint/types@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32" - integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA== +"@typescript-eslint/types@5.59.7": + version "5.59.7" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.7.tgz#6f4857203fceee91d0034ccc30512d2939000742" + integrity sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A== "@typescript-eslint/typescript-estree@5.57.1": version "5.57.1" @@ -284,30 +284,30 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz#8869156ee1dcfc5a95be3ed0e2809969ea28e965" - integrity sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg== +"@typescript-eslint/typescript-estree@5.59.7": + version "5.59.7" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz#b887acbd4b58e654829c94860dbff4ac55c5cff8" + integrity sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ== dependencies: - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/visitor-keys" "5.59.0" + "@typescript-eslint/types" "5.59.7" + "@typescript-eslint/visitor-keys" "5.59.7" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.0.tgz#063d066b3bc4850c18872649ed0da9ee72d833d5" - integrity sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA== +"@typescript-eslint/utils@5.59.7": + version "5.59.7" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.7.tgz#7adf068b136deae54abd9a66ba5a8780d2d0f898" + integrity sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.7" + "@typescript-eslint/types" "5.59.7" + "@typescript-eslint/typescript-estree" "5.59.7" eslint-scope "^5.1.1" semver "^7.3.7" @@ -319,12 +319,12 @@ "@typescript-eslint/types" "5.57.1" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz#a59913f2bf0baeb61b5cfcb6135d3926c3854365" - integrity sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA== +"@typescript-eslint/visitor-keys@5.59.7": + version "5.59.7" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz#09c36eaf268086b4fbb5eb9dc5199391b6485fc5" + integrity sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ== dependencies: - "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/types" "5.59.7" eslint-visitor-keys "^3.3.0" acorn-jsx@^5.3.2: @@ -832,10 +832,10 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -869,20 +869,20 @@ eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint-visitor-keys@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" - integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== +eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== -eslint@8.38.0: - version "8.38.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" - integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg== +eslint@8.41.0: + version "8.41.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.41.0.tgz#3062ca73363b4714b16dbc1e60f035e6134b6f1c" + integrity sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.2" - "@eslint/js" "8.38.0" + "@eslint/eslintrc" "^2.0.3" + "@eslint/js" "8.41.0" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -892,9 +892,9 @@ eslint@8.38.0: debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-visitor-keys "^3.4.0" - espree "^9.5.1" + eslint-scope "^7.2.0" + eslint-visitor-keys "^3.4.1" + espree "^9.5.2" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -902,13 +902,12 @@ eslint@8.38.0: find-up "^5.0.0" glob-parent "^6.0.2" globals "^13.19.0" - grapheme-splitter "^1.0.4" + graphemer "^1.4.0" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" - js-sdsl "^4.1.4" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" @@ -920,14 +919,14 @@ eslint@8.38.0: strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.5.1: - version "9.5.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" - integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== +espree@^9.5.2: + version "9.5.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b" + integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.0" + eslint-visitor-keys "^3.4.1" esquery@^1.4.2: version "1.5.0" @@ -1185,6 +1184,11 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" @@ -1464,11 +1468,6 @@ joycon@^3.0.1: resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== -js-sdsl@^4.1.4: - version "4.1.5" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.5.tgz#1ff1645e6b4d1b028cd3f862db88c9d887f26e2a" - integrity sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q== - js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -1550,7 +1549,7 @@ lunr@^2.3.9: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== -marked@^4.2.12: +marked@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== @@ -1585,10 +1584,10 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^7.1.3: - version "7.4.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" - integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== +minimatch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.0.tgz#bfc8e88a1c40ffd40c172ddac3decb8451503b56" + integrity sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w== dependencies: brace-expansion "^2.0.1" @@ -1792,10 +1791,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@2.8.7: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== +prettier@2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== punycode@^2.1.0: version "2.1.1" @@ -2163,32 +2162,32 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typedoc-plugin-markdown@3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.14.0.tgz#17b99ee3ab0d21046d253f185f7669e80d0d7891" - integrity sha512-UyQLkLRkfTFhLdhSf3RRpA3nNInGn+k6sll2vRXjflaMNwQAAiB61SYbisNZTg16t4K1dt1bPQMMGLrxS0GZ0Q== +typedoc-plugin-markdown@3.15.3: + version "3.15.3" + resolved "https://registry.yarnpkg.com/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.15.3.tgz#f5419a32b93efbdc0fcba60ca4de37727aeb8ba9" + integrity sha512-idntFYu3vfaY3eaD+w9DeRd0PmNGqGuNLKihPU9poxFGnATJYGn9dPtEhn2QrTdishFMg7jPXAhos+2T6YCWRQ== dependencies: handlebars "^4.7.7" -typedoc-plugin-mdn-links@2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-2.0.2.tgz#5a6aa268950c41a1437b57e2c72ed77f8ba323d2" - integrity sha512-Fzjvfsj3rxvmZNqWRvq9JTGBkOkrPp0kBtvJCJ4U5Jm14OF1KoRErtmwgVQcPLA5Xs8h5I/W4uZBaL8SDHsgxQ== +typedoc-plugin-mdn-links@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-3.0.3.tgz#da8d1a9750d57333e6c21717b38bfc13d4058de2" + integrity sha512-NXhIpwQnsg7BcyMCHVqj3tUK+DL4g3Bt96JbFl4APzTGFkA+iM6GfZ/fn3TAqJ8O0CXG5R9BfWxolw1m1omNuQ== -typedoc@0.23.28: - version "0.23.28" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.28.tgz#3ce9c36ef1c273fa849d2dea18651855100d3ccd" - integrity sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w== +typedoc@0.24.7: + version "0.24.7" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.24.7.tgz#7eeb272a1894b3789acc1a94b3f2ae8e7330ee39" + integrity sha512-zzfKDFIZADA+XRIp2rMzLe9xZ6pt12yQOhCr7cD7/PBTjhPmMyMvGrkZ2lPNJitg3Hj1SeiYFNzCsSDrlpxpKw== dependencies: lunr "^2.3.9" - marked "^4.2.12" - minimatch "^7.1.3" + marked "^4.3.0" + minimatch "^9.0.0" shiki "^0.14.1" -typescript@4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== uglify-js@^3.1.4: version "3.15.5" diff --git a/tooling/bench/README.md b/tooling/bench/README.md index aef8d1550..bdb8074e2 100644 --- a/tooling/bench/README.md +++ b/tooling/bench/README.md @@ -4,7 +4,7 @@ [![status](https://img.shields.io/badge/Status-beta-green.svg)](https://github.com/tauri-apps/tauri) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri) -[![test library](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library)](https://github.com/tauri-apps/tauri/actions?query=workflow%3A%22test+library%22) +[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield) [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S) [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app) diff --git a/tooling/bundler/CHANGELOG.md b/tooling/bundler/CHANGELOG.md index eccdd13f9..7a8cb1b15 100644 --- a/tooling/bundler/CHANGELOG.md +++ b/tooling/bundler/CHANGELOG.md @@ -38,6 +38,22 @@ - 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.2.1] + +- Correctly escape XML for resource files in WiX 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` + +- [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. + - [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.2.0] - Add dylib support to `tauri.bundle.macOS.frameworks`. diff --git a/tooling/bundler/Cargo.toml b/tooling/bundler/Cargo.toml index 9775694ea..c25df5678 100644 --- a/tooling/bundler/Cargo.toml +++ b/tooling/bundler/Cargo.toml @@ -19,7 +19,7 @@ exclude = [ "CHANGELOG.md", "/target", "rustfmt.toml" ] [dependencies] tauri-utils = { version = "2.0.0-alpha.5", path = "../../core/tauri-utils", features = [ "resources" ] } image = "0.24.6" -libflate = "1.3" +libflate = "1.4" anyhow = "1.0" thiserror = "1.0" serde_json = "1.0" @@ -31,7 +31,6 @@ handlebars = "4.3" tempfile = "3.5.0" log = { version = "0.4.17", features = [ "kv_unstable" ] } dirs-next = "2.0" -encoding_rs = "0.8" os_pipe = "1" ureq = "2.5" hex = "0.4" @@ -39,6 +38,7 @@ semver = "1" sha1 = "0.10" sha2 = "0.10" zip = "0.6" +dunce = "1" [target."cfg(target_os = \"windows\")".dependencies] uuid = { version = "1", features = [ "v4", "v5" ] } diff --git a/tooling/bundler/src/bundle/linux/appimage.rs b/tooling/bundler/src/bundle/linux/appimage.rs index a7f0b5ea2..90f6665ab 100644 --- a/tooling/bundler/src/bundle/linux/appimage.rs +++ b/tooling/bundler/src/bundle/linux/appimage.rs @@ -81,7 +81,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result> { // initialize shell script template. let mut handlebars = Handlebars::new(); - handlebars.register_escape_fn(|s| s.into()); + handlebars.register_escape_fn(handlebars::no_escape); handlebars .register_template_string("appimage", include_str!("templates/appimage")) .expect("Failed to register template for handlebars"); diff --git a/tooling/bundler/src/bundle/linux/debian.rs b/tooling/bundler/src/bundle/linux/debian.rs index 87d73663f..38b9d87cb 100644 --- a/tooling/bundler/src/bundle/linux/debian.rs +++ b/tooling/bundler/src/bundle/linux/debian.rs @@ -26,16 +26,18 @@ use super::super::common; use crate::Settings; use anyhow::Context; +use handlebars::Handlebars; use heck::AsKebabCase; use image::{self, codecs::png::PngDecoder, ImageDecoder}; use libflate::gzip; use log::info; +use serde::Serialize; use walkdir::WalkDir; use std::{ collections::BTreeSet, ffi::OsStr, - fs::{self, File}, + fs::{self, read_to_string, File}, io::{self, Write}, path::{Path, PathBuf}, }; @@ -141,23 +143,51 @@ fn generate_desktop_file(settings: &Settings, data_dir: &Path) -> crate::Result< let desktop_file_path = data_dir .join("usr/share/applications") .join(desktop_file_name); - let file = &mut common::create_file(&desktop_file_path)?; + // For more information about the format of this file, see // https://developer.gnome.org/integration-guide/stable/desktop-files.html.en - writeln!(file, "[Desktop Entry]")?; - if let Some(category) = settings.app_category() { - writeln!(file, "Categories={}", category.gnome_desktop_categories())?; + let file = &mut common::create_file(&desktop_file_path)?; + + let mut handlebars = Handlebars::new(); + handlebars.register_escape_fn(handlebars::no_escape); + if let Some(template) = &settings.deb().desktop_template { + handlebars + .register_template_string("main.desktop", read_to_string(template)?) + .with_context(|| "Failed to setup custom handlebar template")?; } else { - writeln!(file, "Categories=")?; + handlebars + .register_template_string("main.desktop", include_str!("./templates/main.desktop")) + .with_context(|| "Failed to setup custom handlebar template")?; } - if !settings.short_description().is_empty() { - writeln!(file, "Comment={}", settings.short_description())?; + + #[derive(Serialize)] + struct DesktopTemplateParams<'a> { + categories: &'a str, + comment: Option<&'a str>, + exec: &'a str, + icon: &'a str, + name: &'a str, } - writeln!(file, "Exec={bin_name}")?; - writeln!(file, "Icon={bin_name}")?; - writeln!(file, "Name={}", settings.product_name())?; - writeln!(file, "Terminal=false")?; - writeln!(file, "Type=Application")?; + + handlebars.render_to_write( + "main.desktop", + &DesktopTemplateParams { + categories: settings + .app_category() + .map(|app_category| app_category.gnome_desktop_categories()) + .unwrap_or(""), + comment: if !settings.short_description().is_empty() { + Some(settings.short_description()) + } else { + None + }, + exec: bin_name, + icon: bin_name, + name: settings.product_name(), + }, + file, + )?; + Ok(()) } diff --git a/tooling/bundler/src/bundle/linux/templates/appimage b/tooling/bundler/src/bundle/linux/templates/appimage index fa35add60..9d1308a02 100644 --- a/tooling/bundler/src/bundle/linux/templates/appimage +++ b/tooling/bundler/src/bundle/linux/templates/appimage @@ -42,10 +42,10 @@ if [[ "$TRAY_LIBRARY_PATH" != "0" ]]; then fi fi -# Copy WebKit files. -find /usr/lib* -name WebKitNetworkProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true -find /usr/lib* -name WebKitWebProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true -find /usr/lib* -name libwebkit2gtkinjectedbundle.so -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true +# Copy WebKit files. Follow symlinks in case `/usr/lib64` is a symlink to `/usr/lib` +find -L /usr/lib* -name WebKitNetworkProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true +find -L /usr/lib* -name WebKitWebProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true +find -L /usr/lib* -name libwebkit2gtkinjectedbundle.so -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true ( cd "{{tauri_tools_path}}" && ( wget -q -4 -N https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-${ARCH} || wget -q -4 -N https://github.com/AppImage/AppImageKit/releases/download/12/AppRun-${ARCH} ) ) chmod +x "{{tauri_tools_path}}/AppRun-${ARCH}" diff --git a/tooling/bundler/src/bundle/linux/templates/main.desktop b/tooling/bundler/src/bundle/linux/templates/main.desktop new file mode 100644 index 000000000..574cd598e --- /dev/null +++ b/tooling/bundler/src/bundle/linux/templates/main.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Categories={{categories}} +{{#if comment}} +Comment={{comment}} +{{/if}} +Exec={{exec}} +Icon={{icon}} +Name={{name}} +Terminal=false +Type=Application diff --git a/tooling/bundler/src/bundle/settings.rs b/tooling/bundler/src/bundle/settings.rs index 03e028e58..83e4e547f 100644 --- a/tooling/bundler/src/bundle/settings.rs +++ b/tooling/bundler/src/bundle/settings.rs @@ -152,6 +152,15 @@ pub struct DebianSettings { /// List of custom files to add to the deb package. /// Maps the path on the debian package to the path of the file to include (relative to the current working directory). pub files: HashMap, + /// Path to a custom desktop file Handlebars template. + /// + /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`. + /// + /// Default file contents: + /// ```text + #[doc = include_str!("./linux/templates/main.desktop")] + /// ``` + pub desktop_template: Option, } /// The macOS bundle settings. @@ -248,6 +257,8 @@ pub struct WixSettings { /// Settings specific to the NSIS implementation. #[derive(Clone, Debug, Default)] pub struct NsisSettings { + /// A custom .nsi template to use. + pub template: Option, /// The path to the license file to render on the installer. pub license: Option, /// The path to a bitmap file to display on the header of installers pages. @@ -268,6 +279,13 @@ pub struct NsisSettings { /// /// See for the complete list of languages. pub languages: Option>, + /// An key-value pair where the key is the language and the + /// value is the path to a custom `.nsi` file that holds the translated text for tauri's custom messages. + /// + /// See for an example `.nsi` file. + /// + /// **Note**: the key must be a valid NSIS language and it must be added to [`NsisConfig`]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. pub display_language_selector: bool, diff --git a/tooling/bundler/src/bundle/windows/msi/wix.rs b/tooling/bundler/src/bundle/windows/msi/wix.rs index 6d9ba5d3d..d1945de11 100644 --- a/tooling/bundler/src/bundle/windows/msi/wix.rs +++ b/tooling/bundler/src/bundle/windows/msi/wix.rs @@ -19,7 +19,7 @@ use log::info; use regex::Regex; use serde::{Deserialize, Serialize}; use std::{ - collections::{BTreeMap, HashMap}, + collections::{BTreeMap, HashMap, HashSet}, fs::{create_dir_all, read_to_string, remove_dir_all, rename, write, File}, io::Write, path::{Path, PathBuf}, @@ -356,14 +356,7 @@ fn run_light( ) -> crate::Result<()> { let light_exe = wix_toolset_path.join("light.exe"); - let mut args: Vec = vec![ - "-ext".to_string(), - "WixUIExtension".to_string(), - "-ext".to_string(), - "WixUtilExtension".to_string(), - "-o".to_string(), - display_path(output_path), - ]; + let mut args: Vec = vec!["-o".to_string(), display_path(output_path)]; args.extend(arguments); @@ -614,8 +607,8 @@ pub fn build_wix_app_installer( let mut fragment_paths = Vec::new(); let mut handlebars = Handlebars::new(); - handlebars.register_escape_fn(|s| s.into()); - let mut has_custom_template = false; + handlebars.register_escape_fn(handlebars::no_escape); + let mut custom_template_path = None; let mut enable_elevated_update_task = false; if let Some(wix) = &settings.windows().wix { @@ -626,15 +619,7 @@ pub fn build_wix_app_installer( data.insert("merge_refs", to_json(&wix.merge_refs)); fragment_paths = wix.fragment_paths.clone(); enable_elevated_update_task = wix.enable_elevated_update_task; - - if let Some(temp_path) = &wix.template { - let template = read_to_string(temp_path)?; - handlebars - .register_template_string("main.wxs", &template) - .map_err(|e| e.to_string()) - .expect("Failed to setup custom handlebar template"); - has_custom_template = true; - } + custom_template_path = wix.template.clone(); if let Some(banner_path) = &wix.banner_path { let filename = banner_path @@ -661,7 +646,12 @@ pub fn build_wix_app_installer( } } - if !has_custom_template { + if let Some(path) = custom_template_path { + handlebars + .register_template_string("main.wxs", read_to_string(path)?) + .map_err(|e| e.to_string()) + .expect("Failed to setup custom handlebar template"); + } else { handlebars .register_template_string("main.wxs", include_str!("../templates/main.wxs")) .map_err(|e| e.to_string()) @@ -693,7 +683,7 @@ pub fn build_wix_app_installer( // Create the Powershell script to install the task let mut skip_uac_task_installer = Handlebars::new(); - skip_uac_task_installer.register_escape_fn(|s| s.into()); + skip_uac_task_installer.register_escape_fn(handlebars::no_escape); let xml = include_str!("../templates/install-task.ps1"); skip_uac_task_installer .register_template_string("install-task.ps1", xml) @@ -705,7 +695,7 @@ pub fn build_wix_app_installer( // Create the Powershell script to uninstall the task let mut skip_uac_task_uninstaller = Handlebars::new(); - skip_uac_task_uninstaller.register_escape_fn(|s| s.into()); + skip_uac_task_uninstaller.register_escape_fn(handlebars::no_escape); let xml = include_str!("../templates/uninstall-task.ps1"); skip_uac_task_uninstaller .register_template_string("uninstall-task.ps1", xml) @@ -735,9 +725,15 @@ pub fn build_wix_app_installer( candle_inputs.push((fragment_path, extensions)); } - let mut fragment_extensions = Vec::new(); + let mut fragment_extensions = HashSet::new(); + //Default extensions + fragment_extensions.insert(wix_toolset_path.join("WixUIExtension.dll")); + fragment_extensions.insert(wix_toolset_path.join("WixUtilExtension.dll")); + for (path, extensions) in candle_inputs { - fragment_extensions.extend(extensions.clone()); + for ext in &extensions { + fragment_extensions.insert(ext.clone()); + } run_candle(settings, wix_toolset_path, &output_path, path, extensions)?; } @@ -816,7 +812,7 @@ pub fn build_wix_app_installer( wix_toolset_path, &output_path, arguments, - &fragment_extensions, + &(fragment_extensions.clone().into_iter().collect()), &msi_output_path, )?; rename(&msi_output_path, &msi_path)?; diff --git a/tooling/bundler/src/bundle/windows/nsis.rs b/tooling/bundler/src/bundle/windows/nsis.rs index c6171f538..57960c879 100644 --- a/tooling/bundler/src/bundle/windows/nsis.rs +++ b/tooling/bundler/src/bundle/windows/nsis.rs @@ -8,9 +8,9 @@ use crate::{ bundle::{ common::CommandExt, windows::util::{ - download, download_and_verify, extract_zip, remove_unc_lossy, HashAlgorithm, - NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME, WEBVIEW2_BOOTSTRAPPER_URL, - WEBVIEW2_X64_INSTALLER_GUID, WEBVIEW2_X86_INSTALLER_GUID, + download, download_and_verify, extract_zip, HashAlgorithm, NSIS_OUTPUT_FOLDER_NAME, + NSIS_UPDATER_OUTPUT_FOLDER_NAME, WEBVIEW2_BOOTSTRAPPER_URL, WEBVIEW2_X64_INSTALLER_GUID, + WEBVIEW2_X86_INSTALLER_GUID, }, }, Settings, @@ -26,7 +26,7 @@ use tauri_utils::{ }; use std::{ - collections::BTreeMap, + collections::{BTreeMap, HashMap}, fs::{copy, create_dir_all, remove_dir_all, rename, write}, path::{Path, PathBuf}, process::Command, @@ -121,6 +121,25 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c Ok(()) } +fn add_build_number_if_needed(version_str: &str) -> anyhow::Result { + let version = semver::Version::parse(version_str).context("invalid app version")?; + if !version.build.is_empty() { + let build = version.build.parse::(); + if build.is_ok() { + return Ok(format!( + "{}.{}.{}.{}", + version.major, version.minor, version.patch, version.build + )); + } else { + anyhow::bail!("optional build metadata in app version must be numeric-only"); + } + } + + Ok(format!( + "{}.{}.{}.0", + version.major, version.minor, version.patch, + )) +} fn build_nsis_app_installer( settings: &Settings, _nsis_toolset_path: &Path, @@ -164,7 +183,9 @@ fn build_nsis_app_installer( let mut data = BTreeMap::new(); let bundle_id = settings.bundle_identifier(); - let manufacturer = bundle_id.split('.').nth(1).unwrap_or(bundle_id); + let manufacturer = settings + .publisher() + .unwrap_or_else(|| bundle_id.split('.').nth(1).unwrap_or(bundle_id)); #[cfg(not(target_os = "windows"))] { @@ -177,9 +198,15 @@ fn build_nsis_app_installer( data.insert("bundle_id", to_json(bundle_id)); data.insert("manufacturer", to_json(manufacturer)); data.insert("product_name", to_json(settings.product_name())); + data.insert("short_description", to_json(settings.short_description())); + data.insert("copyright", to_json(settings.copyright_string())); let version = settings.version_string(); data.insert("version", to_json(version)); + data.insert( + "version_with_build", + to_json(add_build_number_if_needed(version)?), + ); data.insert( "allow_downgrades", @@ -188,34 +215,32 @@ fn build_nsis_app_installer( let mut install_mode = NSISInstallerMode::CurrentUser; let mut languages = vec!["English".into()]; + let mut custom_template_path = None; + let mut custom_language_files = None; if let Some(nsis) = &settings.windows().nsis { + custom_template_path = nsis.template.clone(); + custom_language_files = nsis.custom_language_files.clone(); install_mode = nsis.install_mode; if let Some(langs) = &nsis.languages { languages.clear(); languages.extend_from_slice(langs); } if let Some(license) = &nsis.license { - data.insert( - "license", - to_json(remove_unc_lossy(license.canonicalize()?)), - ); + data.insert("license", to_json(dunce::canonicalize(license)?)); } if let Some(installer_icon) = &nsis.installer_icon { data.insert( "installer_icon", - to_json(remove_unc_lossy(installer_icon.canonicalize()?)), + to_json(dunce::canonicalize(installer_icon)?), ); } if let Some(header_image) = &nsis.header_image { - data.insert( - "header_image", - to_json(remove_unc_lossy(header_image.canonicalize()?)), - ); + data.insert("header_image", to_json(dunce::canonicalize(header_image)?)); } if let Some(sidebar_image) = &nsis.sidebar_image { data.insert( "sidebar_image", - to_json(remove_unc_lossy(sidebar_image.canonicalize()?)), + to_json(dunce::canonicalize(sidebar_image)?), ); } @@ -232,7 +257,26 @@ fn build_nsis_app_installer( NSISInstallerMode::Both => "both", }), ); + + let mut languages_data = Vec::new(); + for lang in &languages { + if let Some(data) = get_lang_data(lang, custom_language_files.as_ref())? { + languages_data.push(data); + } else { + log::warn!("Custom tauri messages for {lang} are not translated.\nIf it is a valid language listed on , please open a Tauri feature request\n or you can provide a custom language file for it in `tauri.conf.json > tauri > bundle > windows > nsis > custom_language_files`"); + } + } + data.insert("languages", to_json(languages.clone())); + data.insert( + "language_files", + to_json( + languages_data + .iter() + .map(|d| d.0.clone()) + .collect::>(), + ), + ); let main_binary = settings .binaries() @@ -359,29 +403,26 @@ fn build_nsis_app_installer( } output }); - handlebars - .register_template_string("installer.nsi", include_str!("./templates/installer.nsi")) - .map_err(|e| e.to_string()) - .expect("Failed to setup handlebar template"); + if let Some(path) = custom_template_path { + handlebars + .register_template_string("installer.nsi", std::fs::read_to_string(path)?) + .map_err(|e| e.to_string()) + .expect("Failed to setup custom handlebar template"); + } else { + handlebars + .register_template_string("installer.nsi", include_str!("./templates/installer.nsi")) + .map_err(|e| e.to_string()) + .expect("Failed to setup handlebar template"); + } let installer_nsi_path = output_path.join("installer.nsi"); - write( + write_ut16_le_with_bom( &installer_nsi_path, - encoding_rs::UTF_8 - .encode(handlebars.render("installer.nsi", &data)?.as_str()) - .0, + handlebars.render("installer.nsi", &data)?.as_str(), )?; - for lang in languages { - if let Some((data, encoding)) = get_lang_data(&lang) { - write( - output_path.join(lang).with_extension("nsh"), - encoding.encode(data).0, - )?; - } else { - return Err( - anyhow::anyhow!("Language {lang} not implemented. If it is a valid language listed on , please open a Tauri feature request") - .into() - ); + for (lang, data) in languages_data.iter() { + if let Some(content) = data { + write_ut16_le_with_bom(output_path.join(lang).with_extension("nsh"), content)?; } } @@ -442,7 +483,7 @@ fn generate_resource_data(settings: &Settings) -> crate::Result { for src in settings.resource_files() { let src = src?; - let resource_path = remove_unc_lossy(cwd.join(&src).canonicalize()?); + let resource_path = dunce::canonicalize(cwd.join(&src))?; // In some glob resource paths like `assets/**/*` a file might appear twice // because the `tauri_utils::resources::ResourcePaths` iterator also reads a directory @@ -476,7 +517,7 @@ fn generate_binaries_data(settings: &Settings) -> crate::Result { for src in settings.external_binaries() { let src = src?; - let binary_path = remove_unc_lossy(cwd.join(&src).canonicalize()?); + let binary_path = dunce::canonicalize(cwd.join(&src))?; let dest_filename = src .file_name() .expect("failed to extract external binary filename") @@ -502,38 +543,47 @@ fn generate_binaries_data(settings: &Settings) -> crate::Result { Ok(binaries) } -fn get_lang_data(lang: &str) -> Option<(&'static str, &'static encoding_rs::Encoding)> { - use encoding_rs::*; - match lang.to_lowercase().as_str() { - "arabic" => Some(( - include_str!("./templates/nsis-languages/Arabic.nsh"), - UTF_16LE, - )), - "english" => Some(( - include_str!("./templates/nsis-languages/English.nsh"), - UTF_8, - )), - "portuguesebr" => Some(( - include_str!("./templates/nsis-languages/PortugueseBR.nsh"), - UTF_8, - )), - "tradchinese" => Some(( - include_str!("./templates/nsis-languages/TradChinese.nsh"), - UTF_8, - )), - "simpchinese" => Some(( - include_str!("./templates/nsis-languages/SimpChinese.nsh"), - UTF_8, - )), - "french" => Some((include_str!("./templates/nsis-languages/French.nsh"), UTF_8)), - "spanish" => Some(( - include_str!("./templates/nsis-languages/Spanish.nsh"), - UTF_8, - )), - "spanishinternational" => Some(( - include_str!("./templates/nsis-languages/SpanishInternational.nsh"), - UTF_8, - )), - _ => None, +fn get_lang_data( + lang: &str, + custom_lang_files: Option<&HashMap>, +) -> crate::Result)>> { + if let Some(path) = custom_lang_files.and_then(|h| h.get(lang)) { + return Ok(Some((dunce::canonicalize(path)?, None))); } + + let lang_path = PathBuf::from(format!("{lang}.nsh")); + let lang_content = match lang.to_lowercase().as_str() { + "arabic" => Some(include_str!("./templates/nsis-languages/Arabic.nsh")), + "dutch" => Some(include_str!("./templates/nsis-languages/Dutch.nsh")), + "english" => Some(include_str!("./templates/nsis-languages/English.nsh")), + "japanese" => Some(include_str!("./templates/nsis-languages/Japanese.nsh")), + "korean" => Some(include_str!("./templates/nsis-languages/Korean.nsh")), + "portuguesebr" => Some(include_str!("./templates/nsis-languages/PortugueseBR.nsh")), + "tradchinese" => Some(include_str!("./templates/nsis-languages/TradChinese.nsh")), + "simpchinese" => Some(include_str!("./templates/nsis-languages/SimpChinese.nsh")), + "french" => Some(include_str!("./templates/nsis-languages/French.nsh")), + "spanish" => Some(include_str!("./templates/nsis-languages/Spanish.nsh")), + "spanishinternational" => Some(include_str!( + "./templates/nsis-languages/SpanishInternational.nsh" + )), + "persian" => Some(include_str!("./templates/nsis-languages/Persian.nsh")), + "turkish" => Some(include_str!("./templates/nsis-languages/Turkish.nsh")), + "swedish" => Some(include_str!("./templates/nsis-languages/Swedish.nsh")), + _ => return Ok(None), + }; + + Ok(Some((lang_path, lang_content))) +} + +fn write_ut16_le_with_bom>(path: P, content: &str) -> crate::Result<()> { + use std::fs::File; + use std::io::{BufWriter, Write}; + + let file = File::create(path)?; + let mut output = BufWriter::new(file); + output.write_all(&[0xFF, 0xFE])?; // the BOM part + for utf16 in content.encode_utf16() { + output.write_all(&utf16.to_le_bytes())?; + } + Ok(()) } diff --git a/tooling/bundler/src/bundle/windows/templates/installer.nsi b/tooling/bundler/src/bundle/windows/templates/installer.nsi index cdf71b85e..c8f5e2602 100644 --- a/tooling/bundler/src/bundle/windows/templates/installer.nsi +++ b/tooling/bundler/src/bundle/windows/templates/installer.nsi @@ -1,6 +1,3 @@ -Var AppStartMenuFolder -Var ReinstallPageCheck - !include MUI2.nsh !include FileFunc.nsh !include x64.nsh @@ -9,6 +6,8 @@ Var ReinstallPageCheck !define MANUFACTURER "{{manufacturer}}" !define PRODUCTNAME "{{product_name}}" !define VERSION "{{version}}" +!define VERSIONWITHBUILD "{{version_with_build}}" +!define SHORTDESCRIPTION "{{short_description}}" !define INSTALLMODE "{{install_mode}}" !define LICENSE "{{license}}" !define INSTALLERICON "{{installer_icon}}" @@ -30,23 +29,34 @@ Var ReinstallPageCheck !define MANUPRODUCTKEY "Software\${MANUFACTURER}\${PRODUCTNAME}" Name "${PRODUCTNAME}" +BrandingText "{{copyright}}" OutFile "${OUTFILE}" Unicode true SetCompressor /SOLID lzma +VIProductVersion "${VERSIONWITHBUILD}" +VIAddVersionKey "ProductName" "${PRODUCTNAME}" +VIAddVersionKey "FileDescription" "${SHORTDESCRIPTION}" +VIAddVersionKey "LegalCopyright" "${COPYRIGHT}" +VIAddVersionKey "FileVersion" "${VERSION}" +VIAddVersionKey "ProductVersion" "${VERSION}" + +; Plugins path, currently exists for linux only !if "${PLUGINSPATH}" != "" !addplugindir "${PLUGINSPATH}" !endif -RequestExecutionLevel user - +; Handle install mode, `perUser`, `perMachine` or `both` !if "${INSTALLMODE}" == "perMachine" RequestExecutionLevel highest !endif +!if "${INSTALLMODE}" == "currentUser" + RequestExecutionLevel user +!endif + !if "${INSTALLMODE}" == "both" !define MULTIUSER_MUI - !define MULTIUSER_EXECUTIONLEVEL Highest !define MULTIUSER_INSTALLMODE_INSTDIR "${PRODUCTNAME}" !define MULTIUSER_INSTALLMODE_COMMANDLINE !if "${ARCH}" == "x64" @@ -58,22 +68,21 @@ RequestExecutionLevel user !define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME "CurrentUser" !define MULTIUSER_INSTALLMODEPAGE_SHOWUSERNAME !define MULTIUSER_INSTALLMODE_FUNCTION RestorePreviousInstallLocation - Function RestorePreviousInstallLocation - ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" "" - StrCmp $4 "" +2 0 - StrCpy $INSTDIR $4 - FunctionEnd + !define MULTIUSER_EXECUTIONLEVEL Highest !include MultiUser.nsh !endif +; installer icon !if "${INSTALLERICON}" != "" !define MUI_ICON "${INSTALLERICON}" !endif +; installer sidebar image !if "${SIDEBARIMAGE}" != "" !define MUI_WELCOMEFINISHPAGE_BITMAP "${SIDEBARIMAGE}" !endif +; installer header image !if "${HEADERIMAGE}" != "" !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "${HEADERIMAGE}" @@ -85,25 +94,66 @@ RequestExecutionLevel user !define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" ; Installer pages, must be ordered as they appear +; 1. Welcome Page +!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive !insertmacro MUI_PAGE_WELCOME + +; 2. License Page (if defined) !if "${LICENSE}" != "" + !define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive !insertmacro MUI_PAGE_LICENSE "${LICENSE}" !endif + +; 3. Install mode (if it is set to `both`) !if "${INSTALLMODE}" == "both" + !define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive !insertmacro MULTIUSER_PAGE_INSTALLMODE !endif -; Custom page to ask user if he wants to reinstall/uninstall + +; 4. Custom page to ask user if he wants to reinstall/uninstall +; only if a previous installtion was detected +Var ReinstallPageCheck Page custom PageReinstall PageLeaveReinstall Function PageReinstall + ; Uninstall previous WiX installation if exists. + ; + ; A WiX installer stores the isntallation info in registry + ; using a UUID and so we have to loop through all keys under + ; `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall` + ; and check if `DisplayName` and `Publisher` keys match ${PRODUCTNAME} and ${MANUFACTURER} + ; + ; This has a potentional issue that there maybe another installation that matches + ; our ${PRODUCTNAME} and ${MANUFACTURER} but wasn't installed by our WiX installer, + ; however, this should be fine since the user will have to confirm the uninstallation + ; and they can chose to abort it if doesn't make sense. + StrCpy $0 0 + wix_loop: + EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" $0 + StrCmp $1 "" wix_done ; Exit loop if there is no more keys to loop on + IntOp $0 $0 + 1 + ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "DisplayName" + ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "Publisher" + StrCmp "$R0$R1" "${PRODUCTNAME}${MANUFACTURER}" 0 wix_loop + StrCpy $R5 "wix" + StrCpy $R6 "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" + Goto compare_version + wix_done: + ; Check if there is an existing installation, if not, abort the reinstall page ReadRegStr $R0 SHCTX "${UNINSTKEY}" "" ReadRegStr $R1 SHCTX "${UNINSTKEY}" "UninstallString" ${IfThen} "$R0$R1" == "" ${|} Abort ${|} - ; Compare this installar version with the existing installation and modify the messages presented to the user accordingly + ; Compare this installar version with the existing installation + ; and modify the messages presented to the user accordingly + compare_version: StrCpy $R4 "$(older)" - ReadRegStr $R0 SHCTX "${UNINSTKEY}" "DisplayVersion" + ${If} $R5 == "wix" + ReadRegStr $R0 HKLM "$R6" "DisplayVersion" + ${Else} + ReadRegStr $R0 SHCTX "${UNINSTKEY}" "DisplayVersion" + ${EndIf} ${IfThen} $R0 == "" ${|} StrCpy $R4 "$(unknown)" ${|} nsis_tauri_utils::SemverCompare "${VERSION}" $R0 @@ -114,14 +164,14 @@ Function PageReinstall StrCpy $R2 "$(addOrReinstall)" StrCpy $R3 "$(uninstallApp)" !insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(chooseMaintenanceOption)" - StrCpy $R0 "2" + StrCpy $R5 "2" ; Upgrading ${ElseIf} $R0 == 1 StrCpy $R1 "$(olderOrUnknownVersionInstalled)" StrCpy $R2 "$(uninstallBeforeInstalling)" StrCpy $R3 "$(dontUninstall)" !insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(choowHowToInstall)" - StrCpy $R0 "1" + StrCpy $R5 "1" ; Downgrading ${ElseIf} $R0 == -1 StrCpy $R1 "$(newerVersionInstalled)" @@ -132,16 +182,16 @@ Function PageReinstall StrCpy $R3 "$(dontUninstallDowngrade)" !endif !insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(choowHowToInstall)" - StrCpy $R0 "1" + StrCpy $R5 "1" ${Else} Abort ${EndIf} + Call SkipIfPassive + nsDialogs::Create 1018 Pop $R4 - ${If} $(^RTL) == 1 - nsDialogs::SetRTL $(^RTL) - ${EndIf} + ${IfThen} $(^RTL) == 1 ${|} nsDialogs::SetRTL $(^RTL) ${|} ${NSD_CreateLabel} 0 0 100% 24u $R1 Pop $R1 @@ -152,12 +202,15 @@ Function PageReinstall ${NSD_CreateRadioButton} 30u 70u -30u 8u $R3 Pop $R3 - ; disable this radio button if downgrades are not allowed + ; disable this radio button if downgrading and downgrades are disabled !if "${ALLOWDOWNGRADES}" == "false" - EnableWindow $R3 0 + ${IfThen} $R0 == -1 ${|} EnableWindow $R3 0 ${|} !endif ${NSD_OnClick} $R3 PageReinstallUpdateSelection + ; Check the first radio button if this the first time + ; we enter this page or if the second button wasn't + ; selected the last time we were on this page ${If} $ReinstallPageCheck != 2 SendMessage $R2 ${BM_SETCHECK} ${BST_CHECKED} 0 ${Else} @@ -165,41 +218,41 @@ Function PageReinstall ${EndIf} ${NSD_SetFocus} $R2 - nsDialogs::Show FunctionEnd Function PageReinstallUpdateSelection - Pop $R1 - ${NSD_GetState} $R2 $R1 - ${If} $R1 == ${BST_CHECKED} StrCpy $ReinstallPageCheck 1 ${Else} StrCpy $ReinstallPageCheck 2 ${EndIf} - FunctionEnd Function PageLeaveReinstall ${NSD_GetState} $R2 $R1 - ; $R0 holds whether we are reinstalling the same version or not - ; $R0 == "1" -> different versions - ; $R0 == "2" -> same version + ; $R5 holds whether we are reinstalling the same version or not + ; $R5 == "1" -> different versions + ; $R5 == "2" -> same version ; ; $R1 holds the radio buttons state. its meaning is dependant on the context - StrCmp $R0 "1" 0 +2 ; Existing install is not the same version? - StrCmp $R1 "1" reinst_uninstall reinst_done - StrCmp $R1 "1" reinst_done ; Same version, skip to add/reinstall components? + StrCmp $R5 "1" 0 +2 ; Existing install is not the same version? + StrCmp $R1 "1" reinst_uninstall reinst_done ; $R1 == "1", then user chose to uninstall existing version, otherwise skip uninstalling + StrCmp $R1 "1" reinst_done ; Same version? skip uninstalling reinst_uninstall: - ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" "" - ReadRegStr $R1 SHCTX "${UNINSTKEY}" "UninstallString" - HideWindow - ClearErrors - ExecWait '$R1 _?=$4' $0 + ExecWait '$R1 /P _?=$4' $0 + + ${If} $R5 == "wix" + ReadRegStr $R1 HKLM "$R6" "UninstallString" + ExecWait '$R1' $0 + ${Else} + ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" "" + ReadRegStr $R1 SHCTX "${UNINSTKEY}" "UninstallString" + ExecWait '$R1 _?=$4' $0 + ${EndIf} BringToFront @@ -208,7 +261,7 @@ Function PageLeaveReinstall ${If} $0 <> 0 ${OrIf} ${FileExists} "$INSTDIR\${MAINBINARYNAME}.exe" ${If} $0 = 1 ; User aborted uninstaller? - StrCmp $R0 "2" 0 +2 ; Is the existing install the same version? + StrCmp $R5 "2" 0 +2 ; Is the existing install the same version? Quit ; ...yes, already installed, we are done Abort ${EndIf} @@ -220,30 +273,37 @@ Function PageLeaveReinstall Delete $R1 RMDir $INSTDIR ${EndIf} - reinst_done: FunctionEnd +; 5. Choose install directoy page +!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive !insertmacro MUI_PAGE_DIRECTORY + +; 6. Start menu shortcut page +!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive +Var AppStartMenuFolder !insertmacro MUI_PAGE_STARTMENU Application $AppStartMenuFolder + +; 7. Installation page !insertmacro MUI_PAGE_INSTFILES +; 8. Finish page +; ; Don't auto jump to finish page after installation page, ; because the installation page has useful info that can be used debug any issues with the installer. !define MUI_FINISHPAGE_NOAUTOCLOSE -; Use show readme button in the finish page to create a desktop shortcut +; Use show readme button in the finish page as a button create a desktop shortcut !define MUI_FINISHPAGE_SHOWREADME !define MUI_FINISHPAGE_SHOWREADME_TEXT "$(createDesktop)" !define MUI_FINISHPAGE_SHOWREADME_FUNCTION CreateDesktopShortcut -Function CreateDesktopShortcut - CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe" - ApplicationID::Set "$DESKTOP\${MAINBINARYNAME}.lnk" "${BUNDLEID}" -FunctionEnd ; Show run app after installation. !define MUI_FINISHPAGE_RUN "$INSTDIR\${MAINBINARYNAME}.exe" +!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive !insertmacro MUI_PAGE_FINISH ; Uninstaller Pages +; 1. Confirm uninstall page Var DeleteAppDataCheckbox Var DeleteAppDataCheckboxState !define /ifndef WS_EX_LAYOUTRTL 0x00400000 @@ -265,6 +325,7 @@ Function un.ConfirmLeave FunctionEnd !insertmacro MUI_UNPAGE_CONFIRM +; 2. Uninstalling Page !insertmacro MUI_UNPAGE_INSTFILES ;Languages @@ -272,11 +333,16 @@ FunctionEnd !insertmacro MUI_LANGUAGE "{{this}}" {{/each}} !insertmacro MUI_RESERVEFILE_LANGDLL -{{#each languages}} - !include "{{this}}.nsh" +{{#each language_files}} + !include "{{this}}" {{/each}} +Var PassiveMode Function .onInit + ${GetOptions} $CMDLINE "/P" $PassiveMode + IfErrors +2 0 + StrCpy $PassiveMode 1 + !if "${DISPLAYLANGUAGESELECTOR}" == "true" !insertmacro MUI_LANGDLL_DISPLAY !endif @@ -287,44 +353,64 @@ Function .onInit SetShellVarContext all !endif - !if "${INSTALLMODE}" == "perMachine" + ${If} ${RunningX64} + !if "${ARCH}" == "x64" + SetRegView 64 + !else if "${ARCH}" == "arm64" + SetRegView 64 + !else + SetRegView 32 + !endif + ${EndIf} + + ${If} $INSTDIR == "" ; Set default install location - ${If} ${RunningX64} - !if "${ARCH}" == "x64" - StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCTNAME}" - !else if "${ARCH}" == "arm64" - StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCTNAME}" - !else + !if "${INSTALLMODE}" == "perMachine" + ${If} ${RunningX64} + !if "${ARCH}" == "x64" + StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCTNAME}" + !else if "${ARCH}" == "arm64" + StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCTNAME}" + !else + StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCTNAME}" + !endif + ${Else} StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCTNAME}" - !endif - ${Else} - StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCTNAME}" - ${EndIf} - !else if "${INSTALLMODE}" == "currentUser" - StrCpy $INSTDIR "$LOCALAPPDATA\${PRODUCTNAME}" - !endif + ${EndIf} + !else if "${INSTALLMODE}" == "currentUser" + StrCpy $INSTDIR "$LOCALAPPDATA\${PRODUCTNAME}" + !endif + + Call RestorePreviousInstallLocation + ${EndIf} + !if "${INSTALLMODE}" == "both" !insertmacro MULTIUSER_INIT !endif FunctionEnd + Section EarlyChecks ; Abort silent installer if downgrades is disabled !if "${ALLOWDOWNGRADES}" == "false" - IfSilent 0 done - System::Call 'kernel32::AttachConsole(i -1)i.r0' - ${If} $0 != 0 - System::Call 'kernel32::GetStdHandle(i -11)i.r0' - System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color - FileWrite $0 "$(silentDowngrades)" + IfSilent 0 silent_downgrades_done + ; If downgrading + ${If} $R0 == -1 + System::Call 'kernel32::AttachConsole(i -1)i.r0' + ${If} $0 != 0 + System::Call 'kernel32::GetStdHandle(i -11)i.r0' + System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color + FileWrite $0 "$(silentDowngrades)" + ${EndIf} + Abort ${EndIf} - Abort - done: + silent_downgrades_done: !endif + SectionEnd -Section Webview2 +Section WebView2 ; Check if Webview2 is already installed and skip this section ${If} ${RunningX64} ReadRegStr $4 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" @@ -333,12 +419,10 @@ Section Webview2 ${EndIf} ReadRegStr $5 HKCU "SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" - StrCmp $4 "" 0 done - StrCmp $5 "" 0 done + StrCmp $4 "" 0 webview2_done + StrCmp $5 "" 0 webview2_done - ;-------------------------------- ; Webview2 install modes - !if "${INSTALLWEBVIEW2MODE}" == "downloadBootstrapper" Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe" DetailPrint "$(webview2Downloading)" @@ -370,7 +454,7 @@ Section Webview2 Goto install_webview2 !endif - Goto done + Goto webview2_done install_webview2: DetailPrint "$(installingWebview2)" @@ -382,57 +466,65 @@ Section Webview2 DetailPrint "$(webview2InstallError)" Abort "$(webview2AbortError)" ${EndIf} - - done: + webview2_done: SectionEnd !macro CheckIfAppIsRunning nsis_tauri_utils::FindProcess "${MAINBINARYNAME}.exe" Pop $R0 ${If} $R0 = 0 - IfSilent silent ui - silent: - System::Call 'kernel32::AttachConsole(i -1)i.r0' - ${If} $0 != 0 - System::Call 'kernel32::GetStdHandle(i -11)i.r0' - System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color - FileWrite $0 "$(appRunning)$\n" - ${EndIf} - Abort - ui: - MessageBox MB_OKCANCEL "$(appRunningOkKill)" IDOK ok IDCANCEL cancel - ok: + IfSilent kill 0 + ${IfThen} $PassiveMode != 1 ${|} MessageBox MB_OKCANCEL "$(appRunningOkKill)" IDOK kill IDCANCEL cancel ${|} + kill: nsis_tauri_utils::KillProcess "${MAINBINARYNAME}.exe" Pop $R0 Sleep 500 ${If} $R0 = 0 - Goto done + Goto app_check_done ${Else} - Abort "$(failedToKillApp)" + IfSilent silent ui + silent: + System::Call 'kernel32::AttachConsole(i -1)i.r0' + ${If} $0 != 0 + System::Call 'kernel32::GetStdHandle(i -11)i.r0' + System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color + FileWrite $0 "$(appRunning)$\n" + ${EndIf} + Abort + ui: + Abort "$(failedToKillApp)" ${EndIf} cancel: Abort "$(appRunning)" ${EndIf} - done: + app_check_done: !macroend +Var AppSize Section Install SetOutPath $INSTDIR + StrCpy $AppSize 0 !insertmacro CheckIfAppIsRunning ; Copy main executable File "${MAINBINARYSRCPATH}" + ${GetSize} "$INSTDIR" "/M=${MAINBINARYNAME}.exe /S=0B" $0 $1 $2 + IntOp $AppSize $AppSize + $0 ; Copy resources {{#each resources}} CreateDirectory "$INSTDIR\\{{this.[0]}}" File /a "/oname={{this.[1]}}" "{{@key}}" + ${GetSize} "$INSTDIR" "/M={{this.[1]}} /S=0B" $0 $1 $2 + IntOp $AppSize $AppSize + $0 {{/each}} ; Copy external binaries {{#each binaries}} File /a "/oname={{this}}" "{{@key}}" + ${GetSize} "$INSTDIR" "/M={{this}} /S=0B" $0 $1 $2 + IntOp $AppSize $AppSize + $0 {{/each}} ; Create uninstaller @@ -443,14 +535,8 @@ Section Install !if "${INSTALLMODE}" == "both" ; Save install mode to be selected by default for the next installation such as updating + ; or when uninstalling WriteRegStr SHCTX "${UNINSTKEY}" $MultiUser.InstallMode 1 - - ; Save install mode to be read by the uninstaller in order to remove the correct - ; registry key - FileOpen $4 "$INSTDIR\installmode" w - FileWrite $4 $MultiUser.InstallMode - FileClose $4 - SetFileAttributes "$INSTDIR\installmode" HIDDEN|READONLY !endif ; Registry information for add/remove programs @@ -462,20 +548,56 @@ Section Install WriteRegStr SHCTX "${UNINSTKEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" WriteRegDWORD SHCTX "${UNINSTKEY}" "NoModify" "1" WriteRegDWORD SHCTX "${UNINSTKEY}" "NoRepair" "1" - ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 - IntFmt $0 "0x%08X" $0 - WriteRegDWORD SHCTX "${UNINSTKEY}" "EstimatedSize" "$0" + IntOp $AppSize $AppSize / 1000 + IntFmt $AppSize "0x%08X" $AppSize + WriteRegDWORD SHCTX "${UNINSTKEY}" "EstimatedSize" "$AppSize" - ; Create start menu shortcut + ; Create start menu shortcut (GUI) !insertmacro MUI_STARTMENU_WRITE_BEGIN Application - CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder" - CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe" - ApplicationID::Set "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "${BUNDLEID}" + Call CreateStartMenuShortcut !insertmacro MUI_STARTMENU_WRITE_END + ; Create shortcuts for silent and passive installers, which + ; can be disabled by passing `/NS` flag + ; GUI installer has buttons for users to control creating them + IfSilent check_ns_flag 0 + ${IfThen} $PassiveMode == 1 ${|} Goto check_ns_flag ${|} + Goto shortcuts_done + check_ns_flag: + ${GetOptions} $CMDLINE "/NS" $R0 + IfErrors 0 shortcuts_done + Call CreateDesktopShortcut + Call CreateStartMenuShortcut + shortcuts_done: + + ; Auto close this page for passive mode + ${IfThen} $PassiveMode == 1 ${|} SetAutoClose true ${|} SectionEnd +Function .onInstSuccess + ; Check for `/R` flag only in silent and passive installers because + ; GUI installer has a toggle for the user to (re)start the app + IfSilent check_r_flag 0 + ${IfThen} $PassiveMode == 1 ${|} Goto check_r_flag ${|} + Goto run_done + check_r_flag: + ${GetOptions} $CMDLINE "/R" $R0 + IfErrors run_done 0 + Exec '"$INSTDIR\${MAINBINARYNAME}.exe"' + run_done: +FunctionEnd + Function un.onInit + ${If} ${RunningX64} + !if "${ARCH}" == "x64" + SetRegView 64 + !else if "${ARCH}" == "arm64" + SetRegView 64 + !else + SetRegView 32 + !endif + ${EndIf} + !if "${INSTALLMODE}" == "both" !insertmacro MULTIUSER_UNINIT !endif @@ -486,27 +608,6 @@ FunctionEnd Section Uninstall !insertmacro CheckIfAppIsRunning - ; Remove registry information for add/remove programs - !if "${INSTALLMODE}" == "both" - ; Get the saved install mode - FileOpen $4 "$INSTDIR\installmode" r - FileRead $4 $1 - FileClose $4 - Delete "$INSTDIR\installmode" - - ${If} $1 == "AllUsers" - DeleteRegKey HKLM "${UNINSTKEY}" - ${ElseIf} $1 == "CurrentUser" - DeleteRegKey HKCU "${UNINSTKEY}" - ${EndIf} - !else if "${INSTALLMODE}" == "perMachine" - DeleteRegKey HKLM "${UNINSTKEY}" - !else - DeleteRegKey HKCU "${UNINSTKEY}" - !endif - - DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language" - ; Delete the app directory and its content from disk ; Copy main executable Delete "$INSTDIR\${MAINBINARYNAME}.exe" @@ -540,5 +641,40 @@ Section Uninstall RmDir /r "$APPDATA\${BUNDLEID}" RmDir /r "$LOCALAPPDATA\${BUNDLEID}" ${EndIf} + + ; Remove registry information for add/remove programs + !if "${INSTALLMODE}" == "both" + DeleteRegKey SHCTX "${UNINSTKEY}" + !else if "${INSTALLMODE}" == "perMachine" + DeleteRegKey HKLM "${UNINSTKEY}" + !else + DeleteRegKey HKCU "${UNINSTKEY}" + !endif + + DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language" + + ${GetOptions} $CMDLINE "/P" $R0 + IfErrors +2 0 + SetAutoClose true SectionEnd +Function RestorePreviousInstallLocation + ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" "" + StrCmp $4 "" +2 0 + StrCpy $INSTDIR $4 +FunctionEnd + +Function SkipIfPassive + ${IfThen} $PassiveMode == 1 ${|} Abort ${|} +FunctionEnd + +Function CreateDesktopShortcut + CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe" + ApplicationID::Set "$DESKTOP\${MAINBINARYNAME}.lnk" "${BUNDLEID}" +FunctionEnd + +Function CreateStartMenuShortcut + CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder" + CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe" + ApplicationID::Set "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "${BUNDLEID}" +FunctionEnd diff --git a/tooling/bundler/src/bundle/windows/templates/nsis-languages/Dutch.nsh b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Dutch.nsh new file mode 100644 index 000000000..927bbd934 --- /dev/null +++ b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Dutch.nsh @@ -0,0 +1,27 @@ +LangString addOrReinstall ${LANG_DUTCH} "(Her)installeer componenten" +LangString alreadyInstalled ${LANG_DUTCH} "Al geïnstalleerd" +LangString alreadyInstalledLong ${LANG_DUTCH} "${PRODUCTNAME} ${VERSION} is al geïnstalleerd. Kies een van de volgende opties en klik op Volgende om door te gaan." +LangString appRunning ${LANG_DUTCH} "${PRODUCTNAME} is geopend! Sluit het programma eerst en probeer het dan opnieuw." +LangString appRunningOkKill ${LANG_DUTCH} "${PRODUCTNAME} is geopend!$\nKlik op OK om het te stoppen." +LangString chooseMaintenanceOption ${LANG_DUTCH} "Kies de onderhoudsoptie die u wilt uitvoeren." +LangString choowHowToInstall ${LANG_DUTCH} "Kies hoe u ${PRODUCTNAME} wilt installeren." +LangString createDesktop ${LANG_DUTCH} "Maak een snelkoppeling aan op het bureaublad" +LangString dontUninstall ${LANG_DUTCH} "Deïnstalleer niet" +LangString dontUninstallDowngrade ${LANG_DUTCH} "Deïnstalleer niet (Downgraden zonder deïnstalleren is uitgeschakeld voor deze installer)" +LangString failedToKillApp ${LANG_DUTCH} "Het is niet gelukt ${PRODUCTNAME} te stoppen. Sluit het eerst zelf en probeer het dan nog een keer" +LangString installingWebview2 ${LANG_DUTCH} "WebView2 wordt geïnstalleerd..." +LangString newerVersionInstalled ${LANG_DUTCH} "Een nieuwere versie van ${PRODUCTNAME} is al geïnstalleerd! Het word niet aangeraden om een oudere versie te installeren. Als u echt deze oudere versie wilt installeren, kunt u beter de huidige versie eerst deïnstalleren. Kies een van de volgende opties en klik op Volgende om door te gaan." +LangString older ${LANG_DUTCH} "oudere" +LangString olderOrUnknownVersionInstalled ${LANG_DUTCH} "Een $R4 versie van ${PRODUCTNAME} is al geïnstalleerd. Het word aangeraden om de huidige versie eerst te deïnstalleren. Kies een van de volgende opties en klik op Volgende om door te gaan." +LangString silentDowngrades ${LANG_DUTCH} "Downgrades zijn uitgeschakeld voor deze installer, de stille installatie kan niet worden voltooid, gebruik a.u.b. de grafische installatie methode.$\n" +LangString unableToUninstall ${LANG_DUTCH} "De installatie kan niet ongedaan worden gemaakt." +LangString uninstallApp ${LANG_DUTCH} "Deïnstalleer ${PRODUCTNAME}" +LangString uninstallBeforeInstalling ${LANG_DUTCH} "Deïnstalleer voor installatie" +LangString unknown ${LANG_DUTCH} "onbekende" +LangString webview2AbortError ${LANG_DUTCH} "De installatie van WebView2 is mislukt! De software kan niet draaien zonder. Probeer de installatie opnieuw te starten." +LangString webview2DownloadError ${LANG_DUTCH} "Error: Het downloaden van WebView2 is mislukt - $0" +LangString webview2DownloadSuccess ${LANG_DUTCH} "De download van WebView2 bootstrapper is gelukt" +LangString webview2Downloading ${LANG_DUTCH} "WebView2 bootstrapper aan het downloaden..." +LangString webview2InstallError ${LANG_DUTCH} "Error: Het installeren van WebView2 is mislukt met exit-code $1" +LangString webview2InstallSuccess ${LANG_DUTCH} "De installatie van WebView2 is gelukt" +LangString deleteAppData ${LANG_DUTCH} "Verwijder de data van de applicatie" diff --git a/tooling/bundler/src/bundle/windows/templates/nsis-languages/Japanese.nsh b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Japanese.nsh new file mode 100644 index 000000000..4cfac8034 --- /dev/null +++ b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Japanese.nsh @@ -0,0 +1,27 @@ +LangString addOrReinstall ${LANG_JAPANESE} "コンポーネントの追加・再インストール" +LangString alreadyInstalled ${LANG_JAPANESE} "既にインストールされています" +LangString alreadyInstalledLong ${LANG_JAPANESE} "${PRODUCTNAME} ${VERSION} は既にインストールされています。実行したい操作を選択し、「次へ」をクリックして続行します。" +LangString appRunning ${LANG_JAPANESE} "${PRODUCTNAME} は動作中です。動作中のプログラムを終了し、もう一度やり直してください。" +LangString appRunningOkKill ${LANG_JAPANESE} "${PRODUCTNAME} は動作中です。$\n「OK」を押すと動作中のプログラムを終了します。" +LangString chooseMaintenanceOption ${LANG_JAPANESE} "メンテナンスオプションを選択して実行します。" +LangString choowHowToInstall ${LANG_JAPANESE} "${PRODUCTNAME} のインストール方法を選択してください。" +LangString createDesktop ${LANG_JAPANESE} "デスクトップショートカットを作成する" +LangString dontUninstall ${LANG_JAPANESE} "アンインストールしない" +LangString dontUninstallDowngrade ${LANG_JAPANESE} "アンインストールしない (このインストーラーでは、アンインストールをせずにダウングレードすることはできません)" +LangString failedToKillApp ${LANG_JAPANESE} "${PRODUCTNAME} の終了に失敗しました。動作中のプログラムを終了し、もう一度やり直してください。" +LangString installingWebview2 ${LANG_JAPANESE} "WebView2 をインストール中です..." +LangString newerVersionInstalled ${LANG_JAPANESE} "既に新しいバージョンの ${PRODUCTNAME} がインストールされています。古いバージョンをインストールすることは推奨されません。どうしてもこの旧バージョンをインストールしたい場合は、先に現行バージョンをアンインストールしておく方がよいでしょう。実行したい操作を選択し、「次へ」をクリックして続行します。" +LangString older ${LANG_JAPANESE} "旧" +LangString olderOrUnknownVersionInstalled ${LANG_JAPANESE} "お使いのシステムには、 ${PRODUCTNAME} のバージョン $R4 がインストールされています。インストールする前に、現在のバージョンをアンインストールすることをお勧めします。実行したい操作を選択し、「次へ」をクリックして続行します。" +LangString silentDowngrades ${LANG_JAPANESE} "このインストーラーではダウングレードはできません。サイレントインストーラーを続行できないので、代わりにグラフィカルインターフェースインストーラーを使用してください。$\n" +LangString unableToUninstall ${LANG_JAPANESE} "アンインストールできません。" +LangString uninstallApp ${LANG_JAPANESE} "${PRODUCTNAME} をアンインストールする" +LangString uninstallBeforeInstalling ${LANG_JAPANESE} "インストールする前にアンインストールする" +LangString unknown ${LANG_JAPANESE} "不明" +LangString webview2AbortError ${LANG_JAPANESE} WebView2 のインストールに失敗しました。 WebView2 がないとアプリは実行できません。インストーラーを再起動してください。" +LangString webview2DownloadError ${LANG_JAPANESE} "エラー: WebView2 のダウンロードに失敗しました - $0" +LangString webview2DownloadSuccess ${LANG_JAPANESE} "WebView2 ブートストラップ が正常にダウンロードされました" +LangString webview2Downloading ${LANG_JAPANESE} "WebView2 ブートストラップ をダウンロード中です..." +LangString webview2InstallError ${LANG_JAPANESE} "エラー: WebView2 のインストールは終了コード $1 で失敗しました。" +LangString webview2InstallSuccess ${LANG_JAPANESE} "WebView2 が正常にインストールされました" +LangString deleteAppData ${LANG_JAPANESE} "アプリケーションデータを削除する" diff --git a/tooling/bundler/src/bundle/windows/templates/nsis-languages/Korean.nsh b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Korean.nsh new file mode 100644 index 000000000..401e0891b --- /dev/null +++ b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Korean.nsh @@ -0,0 +1,27 @@ +LangString addOrReinstall ${LANG_KOREAN} "컴포넌트 추가 및 재설치" +LangString alreadyInstalled ${LANG_KOREAN} "이미 설치되어 있습니다" +LangString alreadyInstalledLong ${LANG_KOREAN} "${PRODUCTNAME} ${VERSION}이(가) 이미 설치되어 있습니다. 수행하고자 하는 작업을 선택하고 '다음'을 클릭하여 계속합니다." +LangString appRunning ${LANG_KOREAN} "${PRODUCTNAME}이(가) 실행 중입니다! 먼저 닫은 후 다시 시도하세요." +LangString appRunningOkKill ${LANG_KOREAN} "${PRODUCTNAME}이(가) 실행 중입니다!$\n'OK'를 누르면 실행 중인 프로그램을 종료합니다." +LangString chooseMaintenanceOption ${LANG_KOREAN} "수행하려는 관리 옵션을 선택합니다." +LangString choowHowToInstall ${LANG_KOREAN} "${PRODUCTNAME}의 설치 방법을 선택하세요.." +LangString createDesktop ${LANG_KOREAN} "바탕화면 바로가기 만들기" +LangString dontUninstall ${LANG_KOREAN} "제거하지 않기" +LangString dontUninstallDowngrade ${LANG_KOREAN} "제거하지 않기 (이 설치 프로그램에서는 제거하지 않고 다운그레이드할 수 없습니다.)" +LangString failedToKillApp ${LANG_KOREAN} "${PRODUCTNAME}을(를) 종료하지 못했습니다. 먼저 닫은 후 다시 시도하세요." +LangString installingWebview2 ${LANG_KOREAN} "WebView2를 설치하는 중입니다..." +LangString newerVersionInstalled ${LANG_KOREAN} "${PRODUCTNAME}의 최신 버전이 이미 설치되어 있습니다! 이전 버전을 설치하지 않는 것이 좋습니다. 이 이전 버전을 꼭 설치하려면 먼저 현재 버전을 제거하는 것이 좋습니다. 수행하려는 작업을 선택하고 '다음'을 클릭하여 계속합니다." +LangString older ${LANG_KOREAN} "구" +LangString olderOrUnknownVersionInstalled ${LANG_KOREAN} "시스템에 ${PRODUCTNAME}의 $R4 버전이 설치되어 있습니다. 설치하기 전에 현재 버전을 제거하는 것이 좋습니다. 수행하려는 작업을 선택하고 다음을 클릭하여 계속합니다." +LangString silentDowngrades ${LANG_KOREAN} "이 설치 프로그램에서는 다운그레이드가 비활성화되어 자동 설치 프로그램을 진행할 수 없습니다. 대신 그래픽 인터페이스 설치 프로그램을 사용하세요.$\n" +LangString unableToUninstall ${LANG_KOREAN} "제거할 수 없습니다!" +LangString uninstallApp ${LANG_KOREAN} "${PRODUCTNAME} 제거하기" +LangString uninstallBeforeInstalling ${LANG_KOREAN} "설치하기 전에 제거하기" +LangString unknown ${LANG_KOREAN} "알 수 없음" +LangString webview2AbortError ${LANG_KOREAN} "WebView2를 설치하지 못했습니다! WebView2가 없으면 앱을 실행할 수 없습니다. 인스톨러를 다시 시작해보세요." +LangString webview2DownloadError ${LANG_KOREAN} "오류: WebView2 다운로드를 실패하였습니다. - $0" +LangString webview2DownloadSuccess ${LANG_KOREAN} "WebView2 부트스트래퍼가 성공적으로 다운로드되었습니다." +LangString webview2Downloading ${LANG_KOREAN} "WebView2 부트스트래퍼 다운로드 중..." +LangString webview2InstallError ${LANG_KOREAN} "오류: 종료 코드 $1로 WebView2를 설치하지 못했습니다." +LangString webview2InstallSuccess ${LANG_KOREAN} "WebView2가 성공적으로 설치되었습니다." +LangString deleteAppData ${LANG_KOREAN} "애플리케이션 데이터 삭제하기" diff --git a/tooling/bundler/src/bundle/windows/templates/nsis-languages/Persian.nsh b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Persian.nsh new file mode 100644 index 000000000..5e509f901 --- /dev/null +++ b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Persian.nsh @@ -0,0 +1,27 @@ +LangString addOrReinstall ${LANG_PERSIAN} "اضافه کردن/نصب مجدد کامپونتت" +LangString alreadyInstalled ${LANG_PERSIAN} "قبلا نصب شده است" +LangString alreadyInstalledLong ${LANG_PERSIAN} "${PRODUCTNAME} ${VERSION} قبلا نصب شده است. عملیات مدنظر را انتخاب کنید و بروی بعدی کلیک کنید." +LangString appRunning ${LANG_PERSIAN} "${PRODUCTNAME} در حال اجر می باشد ! لطفا اول الان را ببندید و دوباره تلاش کنید" +LangString appRunningOkKill ${LANG_PERSIAN} "${PRODUCTNAME} در حال اجرا می باشد!$\nبرای از بین بردن اوکی را انتخاب کنید" +LangString chooseMaintenanceOption ${LANG_PERSIAN} "عملیات نگهداری مدنظر را برای اجرا انتخاب کنید" +LangString choowHowToInstall ${LANG_PERSIAN} "نحوه نصب ${PRODUCTNAME} را انتخاب کنید" +LangString createDesktop ${LANG_PERSIAN} "ایجاد میانبر دسکتاپ" +LangString dontUninstall ${LANG_PERSIAN} "حذف نکنید" +LangString dontUninstallDowngrade ${LANG_PERSIAN} "حذف نکنید (تنزل ورژن بدون حذف برای نصب کننده غیرفعال است)" +LangString failedToKillApp ${LANG_PERSIAN} "${PRODUCTNAME} قابل کشته شدن نیست. اول آن را ببندید و دوباره تلاش کنید" +LangString installingWebview2 ${LANG_PERSIAN} "در حال نصب WebView2 ..." +LangString newerVersionInstalled ${LANG_PERSIAN} "ورژن جدید ${PRODUCTNAME} قبلا نصب شده است! نصب ورژن قدیمی تر به هیچ عنوان پیشنهاد نمی شود. اگر از این بابت اطمینان دارید , بهتر است ورژن فعلی را حذف کنید. عملیات مدنظر را انتخاب کنید و بروی بعدی کلیک کنید." +LangString older ${LANG_PERSIAN} "قدیمی تر" +LangString olderOrUnknownVersionInstalled ${LANG_PERSIAN} "ورژن $R4 ${PRODUCTNAME} قبلا بروی سیستم شما نصب شده است. ر. عملیات مدنظر را انتخاب کنید و بروی بعدی کلیک کنید." +LangString silentDowngrades ${LANG_PERSIAN} "تنزل ورژن بدون حذف غیرفعال می باشد, عملیات نصب خاموش غیرقابل انجام است , از رابط گرافیکی برای نصب استفاده کنید.$\n" +LangString unableToUninstall ${LANG_PERSIAN} "قابل نصب نیست!" +LangString uninstallApp ${LANG_PERSIAN} "حذف ${PRODUCTNAME}" +LangString uninstallBeforeInstalling ${LANG_PERSIAN} "قبل از نصب , حذف کنید" +LangString unknown ${LANG_PERSIAN} "ناشناس" +LangString webview2AbortError ${LANG_PERSIAN} "نصب WebView2 شکست خورد! اپ بدون ان کار نمی کند. نصب کننده را دوباره نصب کنید" +LangString webview2DownloadError ${LANG_PERSIAN} "ارور: دانلود WebView2 شکست خورد - $0" +LangString webview2DownloadSuccess ${LANG_PERSIAN} "WebView2 بوت استرپر با موفقیت نصب شد" +LangString webview2Downloading ${LANG_PERSIAN} "دانلود بوت استرپر WebView2..." +LangString webview2InstallError ${LANG_PERSIAN} "ارور: نصب WebView2 با کد $1 شکست خورد" +LangString webview2InstallSuccess ${LANG_PERSIAN} "WebView2 با موفقیت نصب شد" +LangString deleteAppData ${LANG_PERSIAN} "حذف دیتا های اپلیکیشن" diff --git a/tooling/bundler/src/bundle/windows/templates/nsis-languages/SimpChinese.nsh b/tooling/bundler/src/bundle/windows/templates/nsis-languages/SimpChinese.nsh index 71c80ad7a..e4308b41a 100644 --- a/tooling/bundler/src/bundle/windows/templates/nsis-languages/SimpChinese.nsh +++ b/tooling/bundler/src/bundle/windows/templates/nsis-languages/SimpChinese.nsh @@ -24,4 +24,4 @@ LangString webview2DownloadSuccess ${LANG_SIMPCHINESE} "WebView2 安装程序下 LangString webview2Downloading ${LANG_SIMPCHINESE} "正在下载 WebView2 安装程序..." LangString webview2InstallError ${LANG_SIMPCHINESE} "错误:安装 WebView2 时失败,错误代码:$1" LangString webview2InstallSuccess ${LANG_SIMPCHINESE} "成功安装 WebView2" -LangString deleteAppData ${LANG_SIMPCHINESE} "Delete the application data" +LangString deleteAppData ${LANG_SIMPCHINESE} "删除应用程序数据" diff --git a/tooling/bundler/src/bundle/windows/templates/nsis-languages/Swedish.nsh b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Swedish.nsh new file mode 100644 index 000000000..f1c4ecec7 --- /dev/null +++ b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Swedish.nsh @@ -0,0 +1,27 @@ +LangString addOrReinstall ${LANG_SWEDISH} "Lägg till/Installera om komponenter" +LangString alreadyInstalled ${LANG_SWEDISH}} "Redan installerad" +LangString alreadyInstalledLong ${LANG_SWEDISH}} "${PRODUCTNAME} ${VERSION} är redan installerad. Välj åtgärd och klicka på Nästa för att fortsätta." +LangString appRunning ${LANG_SWEDISH} "${PRODUCTNAME} körs! Stäng det först och försök igen." +LangString appRunningOkKill ${LANG_SWEDISH} "${PRODUCTNAME} körs!$\nKlicka på OK för att avsluta det." +LangString chooseMaintenanceOption ${LANG_SWEDISH} "Välj underhållsåtgärd." +LangString choowHowToInstall ${LANG_SWEDISH} "Välj hur du vill installera ${PRODUCTNAME}." +LangString createDesktop ${LANG_SWEDISH} "Skapa genväg på skrivbordet" +LangString dontUninstall ${LANG_SWEDISH} "Avinstallera inte" +LangString dontUninstallDowngrade ${LANG_SWEDISH} "Avinstallera inte (nedgradering utan avinstallation är inaktiverad för den här installationsprogrammet)" +LangString failedToKillApp ${LANG_SWEDISH} "Kunde inte avsluta ${PRODUCTNAME}. Stäng det först och försök igen." +LangString installingWebview2 ${LANG_SWEDISH} "Installerar WebView2..." +LangString newerVersionInstalled ${LANG_SWEDISH} "En nyare version av ${PRODUCTNAME} är redan installerad! Det rekommenderas inte att installera en äldre version. Om du verkligen vill installera denna äldre version är det bättre att avinstallera den nuvarande versionen först. Välj åtgärd och klicka på Nästa för att fortsätta." +LangString older ${LANG_SWEDISH} "äldre" +LangString olderOrUnknownVersionInstalled ${LANG_SWEDISH} "En $R4-version av ${PRODUCTNAME} är installerad på ditt system. Det rekommenderas att du avinstallerar den nuvarande versionen innan du installerar. Välj åtgärd och klicka på Nästa för att fortsätta." +LangString silentDowngrades ${LANG_SWEDISH} "Nedgraderingar är inaktiverade för den här installationsprogrammet. Kan inte fortsätta med installationsprogrammet. Använd det grafiska installationsprogrammet istället.$\n" +LangString unableToUninstall ${LANG_SWEDISH} "Kan inte avinstallera!" +LangString uninstallApp ${LANG_SWEDISH} "Avinstallera ${PRODUCTNAME}" +LangString uninstallBeforeInstalling ${LANG_SWEDISH} "Avinstallera innan installation" +LangString unknown ${LANG_SWEDISH} "okänd" +LangString webview2AbortError ${LANG_SWEDISH} "Misslyckades med att installera WebView2! Appen kan inte köras utan det. Försök starta om installationsprogrammet." +LangString webview2DownloadError ${LANG_SWEDISH} "Fel: Nedladdning av WebView2 misslyckades - $0" +LangString webview2DownloadSuccess ${LANG_SWEDISH} "WebView2 bootstrapper nedladdad framgångsrikt" +LangString webview2Downloading ${LANG_SWEDISH} "Laddar ner WebView2 bootstrapper..." +LangString webview2InstallError ${LANG_SWEDISH} "Fel: Installation av WebView2 misslyckades med felkod $1" +LangString webview2InstallSuccess ${LANG_SWEDISH} "WebView2 installerades framgångsrikt" +LangString deleteAppData ${LANG_SWEDISH} "Ta bort applikationsdata" diff --git a/tooling/bundler/src/bundle/windows/templates/nsis-languages/TradChinese.nsh b/tooling/bundler/src/bundle/windows/templates/nsis-languages/TradChinese.nsh index 450b8c96f..c80c3cd30 100644 --- a/tooling/bundler/src/bundle/windows/templates/nsis-languages/TradChinese.nsh +++ b/tooling/bundler/src/bundle/windows/templates/nsis-languages/TradChinese.nsh @@ -24,4 +24,4 @@ LangString webview2DownloadSuccess ${LANG_TRADCHINESE} "WebView2 啟動載入器 LangString webview2Downloading ${LANG_TRADCHINESE} "正在下載 WebView2 啟動載入器..." LangString webview2InstallError ${LANG_TRADCHINESE} "錯誤:WebView2 安裝失敗,錯誤碼 $1" LangString webview2InstallSuccess ${LANG_TRADCHINESE} "WebView2 安裝成功" -LangString deleteAppData ${LANG_TRADCHINESE} "Delete the application data" +LangString deleteAppData ${LANG_TRADCHINESE} "刪除應用程式數據" diff --git a/tooling/bundler/src/bundle/windows/templates/nsis-languages/Turkish.nsh b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Turkish.nsh new file mode 100644 index 000000000..c47665de5 --- /dev/null +++ b/tooling/bundler/src/bundle/windows/templates/nsis-languages/Turkish.nsh @@ -0,0 +1,27 @@ +LangString addOrReinstall ${LANG_TURKISH} "Bileşen Ekle/Yeniden Yükle" +LangString alreadyInstalled ${LANG_TURKISH} "Daha Önceden Yüklenmiş" +LangString alreadyInstalledLong ${LANG_TURKISH} "${PRODUCTNAME} ${VERSION} daha önceden yüklenmiş. Gerçekleştirmek istediğiniz işlemi seçin ve devam etmek için İleri'ye tıklayın." +LangString appRunning ${LANG_TURKISH} "${PRODUCTNAME} çalışır durumda! Lütfen önce uygulamayı kapatın ve sonra tekrar deneyin." +LangString appRunningOkKill ${LANG_TURKISH} "${PRODUCTNAME} çalışır durumda!$\nUygulamayı sonlandırmak için Tamam'a tıklayın." +LangString chooseMaintenanceOption ${LANG_TURKISH} "Gerçekleştirmek istediğiniz bakım seçeneğini belirleyin." +LangString choowHowToInstall ${LANG_TURKISH} "${PRODUCTNAME} uygulamasını nasıl yüklemek istediğinizi seçin." +LangString createDesktop ${LANG_TURKISH} "Masaüstü kısayolu oluştur" +LangString dontUninstall ${LANG_TURKISH} "Kaldırma işlemini gerçekleştirme" +LangString dontUninstallDowngrade ${LANG_TURKISH} "Kaldırma işlemini gerçekleştirme (Kaldırma işlemi yapmadan sürüm düşürme bu yükleyici için devre dışı bırakılmıştır)" +LangString failedToKillApp ${LANG_TURKISH} "${PRODUCTNAME} sonlandırılamadı. Lütfen önce kapatın sonra tekrar deneyin." +LangString installingWebview2 ${LANG_TURKISH} "WebView2 yükleniyor..." +LangString newerVersionInstalled ${LANG_TURKISH} "${PRODUCTNAME} uygulamasının daha yeni bir sürümü zaten yüklü! Daha eski bir sürümü yüklemeniz önerilmez. Bu eski sürümü gerçekten yüklemek istiyorsanız, önce mevcut sürümü kaldırmanız daha uygundur. Gerçekleştirmek istediğiniz işlemi seçin ve devam etmek için İleri'ye tıklayın." +LangString older ${LANG_TURKISH} "daha eski" +LangString olderOrUnknownVersionInstalled ${LANG_TURKISH} "Sisteminizde ${PRODUCTNAME} uygulamasının $R4 sürümü yüklü. Yükleme işleminden önce mevcut sürümü kaldırmanız önerilir. Gerçekleştirmek istediğiniz işlemi seçin ve devam etmek için İleri'ye tıklayın." +LangString silentDowngrades ${LANG_TURKISH} "Bu yükleyici için sürüm düşürme işlemleri devre dışı bırakıldı, sessiz yükleyici ile devam edilemiyor, lütfen bunun yerine grafik arayüz yükleyicisini kullanın.$\n" +LangString unableToUninstall ${LANG_TURKISH} "Kaldırma işlemi gerçekleştirilemiyor!" +LangString uninstallApp ${LANG_TURKISH} "${PRODUCTNAME}'i kaldır" +LangString uninstallBeforeInstalling ${LANG_TURKISH} "Yükleme yapmadan önce kaldırın" +LangString unknown ${LANG_TURKISH} "bilinmeyen" +LangString webview2AbortError ${LANG_TURKISH} "WebView2 yüklenemedi! Uygulama bu bileşen olmadan çalışamaz. Yükleyiciyi yeniden başlatmayı deneyin." +LangString webview2DownloadError ${LANG_TURKISH} "Hata: WebView2 İndirmesi Başarısız - $0" +LangString webview2DownloadSuccess ${LANG_TURKISH} "WebView2 önyükleyicisi başarıyla indirildi" +LangString webview2Downloading ${LANG_TURKISH} "WebView2 önyükleyicisi indiriliyor..." +LangString webview2InstallError ${LANG_TURKISH} "Hata: WebView2 yüklemesi $1 hata koduyla başarısız oldu." +LangString webview2InstallSuccess ${LANG_TURKISH} "WebView2 başarıyla yüklendi" +LangString deleteAppData ${LANG_TURKISH} "Uygulama verilerini sil" diff --git a/tooling/bundler/src/bundle/windows/util.rs b/tooling/bundler/src/bundle/windows/util.rs index 1e73284d8..2672302a0 100644 --- a/tooling/bundler/src/bundle/windows/util.rs +++ b/tooling/bundler/src/bundle/windows/util.rs @@ -5,7 +5,7 @@ use std::{ fs::{create_dir_all, File}, io::{Cursor, Read, Write}, - path::{Path, PathBuf}, + path::Path, }; use log::info; @@ -76,7 +76,7 @@ fn verify(data: &Vec, hash: &str, mut hasher: impl Digest) -> crate::Result< } #[cfg(target_os = "windows")] -pub fn try_sign(file_path: &PathBuf, settings: &Settings) -> crate::Result<()> { +pub fn try_sign(file_path: &std::path::PathBuf, settings: &Settings) -> crate::Result<()> { use tauri_utils::display_path; if let Some(certificate_thumbprint) = settings.windows().certificate_thumbprint.as_ref() { @@ -136,7 +136,3 @@ pub fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> { Ok(()) } - -pub fn remove_unc_lossy>(p: P) -> PathBuf { - PathBuf::from(p.as_ref().to_string_lossy().replacen(r"\\?\", "", 1)) -} diff --git a/tooling/cli/CHANGELOG.md b/tooling/cli/CHANGELOG.md index 1be528432..e9415d6ac 100644 --- a/tooling/cli/CHANGELOG.md +++ b/tooling/cli/CHANGELOG.md @@ -131,6 +131,26 @@ - 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.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. diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 4d355808b..824e2e9a8 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -24,18 +24,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "opaque-debug", -] - [[package]] name = "aes" version = "0.8.2" @@ -43,19 +31,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" dependencies = [ "cfg-if", - "cipher 0.4.4", + "cipher", "cpufeatures", ] [[package]] name = "aes-gcm" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" dependencies = [ "aead", - "aes 0.8.2", - "cipher 0.4.4", + "aes", + "cipher", "ctr", "ghash", "subtle", @@ -191,7 +179,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -269,9 +257,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -317,9 +305,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813" +checksum = "6776fc96284a0bb647b615056fc496d1fe1644a7ab01829818a6d91cae888b84" [[package]] name = "bitness" @@ -359,9 +347,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5" dependencies = [ "memchr", "serde", @@ -369,9 +357,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytecount" @@ -457,15 +445,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - [[package]] name = "cipher" version = "0.4.4" @@ -478,9 +457,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.7" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" dependencies = [ "clap_builder", "clap_derive", @@ -489,9 +468,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.7" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" dependencies = [ "anstream", "anstyle", @@ -501,22 +480,31 @@ dependencies = [ ] [[package]] -name = "clap_derive" -version = "4.2.0" +name = "clap_complete" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "a04ddfaacc3bc9e6ea67d024575fafc2a813027cf374b8f24f7bc233c6b6be12" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_derive" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "cocoa" @@ -533,16 +521,6 @@ dependencies = [ "objc", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -595,15 +573,15 @@ checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" [[package]] name = "console" -version = "0.15.5" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -789,7 +767,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd4056f63fce3b82d852c3da92b08ea59959890813a7f4ce9c0ff85b10cf301b" dependencies = [ "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -798,61 +776,17 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] name = "ctrlc" -version = "3.2.5" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639" +checksum = "7394a21d012ce5c850497fb774b167d81b99f060025fbf06ee92b9848bd97eb2" dependencies = [ "nix", - "windows-sys 0.45.0", -] - -[[package]] -name = "cxx" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.15", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", + "windows-sys 0.48.0", ] [[package]] @@ -876,7 +810,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -887,7 +821,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -932,9 +866,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common", @@ -1337,7 +1271,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -1460,9 +1394,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ "bytes", "fnv", @@ -1502,9 +1436,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.6" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" dependencies = [ "log", "pest", @@ -1565,7 +1499,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -1677,12 +1611,11 @@ dependencies = [ [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -1816,9 +1749,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", @@ -1904,24 +1837,13 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json-patch" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" -dependencies = [ - "serde", - "serde_json", - "treediff 3.0.2", -] - [[package]] name = "json-patch" version = "1.0.0" @@ -1931,7 +1853,7 @@ dependencies = [ "serde", "serde_json", "thiserror", - "treediff 4.0.2", + "treediff", ] [[package]] @@ -2138,15 +2060,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.143" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libflate" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97822bf791bd4d5b403713886a5fbe8bf49520fe78e323b0dc480ca1a03e50b0" +checksum = "5ff4ae71b685bbad2f2f391fe74f6b7659a34871c08b210fdc039e43bee07d18" dependencies = [ "adler32", "crc32fast", @@ -2181,20 +2103,11 @@ dependencies = [ "safemem", ] -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "local-ip-address" @@ -2355,11 +2268,11 @@ dependencies = [ [[package]] name = "napi" -version = "2.12.6" +version = "2.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ac8112fe5998579b22e29903c7b277fc7f91c7860c0236f35792caf8156e18" +checksum = "78e1f965758ed9f6b31b07c38e18ec9b6b9fcae56dd88a0812650bb6c8f35590" dependencies = [ - "bitflags 2.2.1", + "bitflags 2.3.1", "ctor 0.2.0", "napi-derive", "napi-sys", @@ -2456,9 +2369,9 @@ dependencies = [ [[package]] name = "notify" -version = "5.1.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" +checksum = "4d9ba6c734de18ca27c8cef5cd7058aa4ac9f63596131e4c7e41e579319032a2" dependencies = [ "bitflags 1.3.2", "crossbeam-channel", @@ -2469,14 +2382,14 @@ dependencies = [ "libc", "mio", "walkdir", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "notify-debouncer-mini" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e23e9fa24f094b143c1eb61f90ac6457de87be6987bc70746e0179f7dbc9007b" +checksum = "e55ee272914f4563a2f8b8553eb6811f3c0caea81c756346bad15b7e3ef969f0" dependencies = [ "crossbeam-channel", "notify", @@ -2637,7 +2550,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -2744,7 +2657,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "hmac", "password-hash", "sha2", @@ -2756,7 +2669,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "hmac", ] @@ -2796,7 +2709,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -2910,22 +2823,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.17", ] [[package]] @@ -2952,7 +2865,7 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "indexmap", "line-wrap", "quick-xml", @@ -3005,9 +2918,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] @@ -3038,9 +2951,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -3179,9 +3092,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" dependencies = [ "aho-corasick 1.0.1", "memchr", @@ -3190,17 +3103,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "reqwest" -version = "0.11.17" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", @@ -3329,7 +3242,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] @@ -3372,12 +3285,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" - [[package]] name = "scrypt" version = "0.11.0" @@ -3427,9 +3334,9 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.162" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] @@ -3446,13 +3353,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.162" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -3501,7 +3408,23 @@ dependencies = [ "indexmap", "serde", "serde_json", - "serde_with_macros", + "serde_with_macros 2.3.3", + "time", +] + +[[package]] +name = "serde_with" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" +dependencies = [ + "base64 0.21.2", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros 3.0.0", "time", ] @@ -3514,7 +3437,19 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", +] + +[[package]] +name = "serde_with_macros" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.17", ] [[package]] @@ -3570,7 +3505,7 @@ checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3581,7 +3516,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3598,7 +3533,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3753,9 +3688,9 @@ checksum = "fa7986063f7c0ab374407e586d7048a3d5aac94f103f751088bf398e07cd5400" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "sval" @@ -3776,9 +3711,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "45b6ddbb36c5b969c182aec3c4a0bce7df3fbad4b77114706a49aacc80567388" dependencies = [ "proc-macro2", "quote", @@ -3823,9 +3758,9 @@ dependencies = [ "ar", "bitness", "dirs-next", - "encoding_rs", + "dunce", "glob", - "handlebars 4.3.6", + "handlebars 4.3.7", "heck", "hex", "image", @@ -3860,8 +3795,10 @@ version = "2.0.0-alpha.9" dependencies = [ "anyhow", "axum", - "base64 0.21.0", + "base64 0.21.2", + "cc", "clap", + "clap_complete", "colored 2.0.0", "common-path", "css-color", @@ -3869,14 +3806,14 @@ dependencies = [ "dialoguer", "duct", "env_logger", - "handlebars 4.3.6", + "handlebars 4.3.7", "heck", "html5ever", "ignore", "image", "include_dir", "itertools", - "json-patch 0.2.7", + "json-patch", "jsonrpsee", "jsonrpsee-client-transport", "jsonrpsee-core", @@ -3989,7 +3926,7 @@ dependencies = [ "heck", "html5ever", "infer", - "json-patch 1.0.0", + "json-patch", "json5", "kuchiki", "memchr", @@ -3998,7 +3935,7 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_with", + "serde_with 2.3.3", "serialize-to-javascript", "thiserror", "toml", @@ -4017,7 +3954,7 @@ dependencies = [ "heck", "html5ever", "infer", - "json-patch 1.0.0", + "json-patch", "json5", "kuchiki", "memchr", @@ -4026,7 +3963,7 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_with", + "serde_with 3.0.0", "serialize-to-javascript", "thiserror", "toml", @@ -4111,7 +4048,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -4179,9 +4116,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.0" +version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ "autocfg", "bytes", @@ -4202,7 +4139,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] @@ -4332,27 +4269,18 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.17", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] -[[package]] -name = "treediff" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" -dependencies = [ - "serde_json", -] - [[package]] name = "treediff" version = "4.0.2" @@ -4416,9 +4344,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-normalization" @@ -4443,9 +4371,9 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "universal-hash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle", @@ -4499,9 +4427,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" +checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" dependencies = [ "getrandom 0.2.9", "sha1_smol", @@ -4584,9 +4512,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4594,24 +4522,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.17", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" dependencies = [ "cfg-if", "js-sys", @@ -4621,9 +4549,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4631,28 +4559,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.17", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5010,11 +4938,11 @@ checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" [[package]] name = "zip" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e92305c174683d78035cbf1b70e18db6329cc0f1b9cae0a52ca90bf5bfe7125" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ - "aes 0.7.5", + "aes", "byteorder", "bzip2", "constant_time_eq", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index 27f3f5110..c62060e1d 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -24,7 +24,7 @@ include = [ ] [package.metadata.binstall] -pkg-url = "{ repo }/releases/download/cli.rs-v{ version }/cargo-tauri-{ target }.{ archive-format }" +pkg-url = "{ repo }/releases/download/tauri-cli-v{ version }/cargo-tauri-{ target }.{ archive-format }" bin-dir = "{ bin }{ binary-ext }" pkg-fmt = "tgz" @@ -47,6 +47,7 @@ jsonrpsee-client-transport = { version = "0.16", features = [ "ws" ] } jsonrpsee-ws-client = { version = "0.16", default-features = false } thiserror = "1" sublime_fuzzy = "0.7" +clap_complete = "4" clap = { version = "4.0", features = [ "derive" ] } anyhow = "1.0" tauri-bundler = { version = "2.0.0-alpha.5", path = "../bundler" } @@ -54,12 +55,12 @@ colored = "2.0" once_cell = "1" serde = { version = "1.0", features = [ "derive" ] } serde_json = "1.0" -notify = "5.0" -notify-debouncer-mini = "0.2" +notify = "6.0" +notify-debouncer-mini = "0.3" shared_child = "1.0" duct = "0.13" toml_edit = "0.14" -json-patch = "0.2" +json-patch = "1.0" tauri-utils = { version = "2.0.0-alpha.5", path = "../../core/tauri-utils", features = [ "isolation", "schema", "config-json5", "config-toml" ] } tauri-utils-v1 = { version = "1", package = "tauri-utils", features = [ "isolation", "schema", "config-json5", "config-toml" ] } toml = "0.5" @@ -96,6 +97,7 @@ css-color = "0.2" [target."cfg(windows)".dependencies] winapi = { version = "0.3", features = [ "handleapi", "processenv", "winbase", "wincon", "winnt" ] } +cc = "1" [target."cfg(unix)".dependencies] libc = "0.2" diff --git a/tooling/cli/ENVIRONMENT_VARIABLES.md b/tooling/cli/ENVIRONMENT_VARIABLES.md new file mode 100644 index 000000000..8416b62b9 --- /dev/null +++ b/tooling/cli/ENVIRONMENT_VARIABLES.md @@ -0,0 +1,46 @@ + + +### Tauri's Environment Variables + +This is a documentation of all environment variables used by tauri core crates and tauri CLI. + +### Tauri CLI + +These environment variables are inputs to the CLI which may have an equivalent CLI flag. + +> if both environment variable and CLI flag are used, the CLI flag will have priority. + +- `TAURI_PATH_DEPTH` — Number of levels to traverse and find tauri configuration file. +- `TAURI_DEV_SERVER_PORT` — Port to use for the CLI built-in dev server. +- `TAURI_DEV_WATCHER_IGNORE_FILE` — A `.gitignore`-style file to control which files should be watched by the CLI in `dev` command. +- `TAURI_SKIP_DEVSERVER_CHECK` — Skip waiting for the frontend dev server to start before building the tauri application. +- `TAURI_SKIP_UPDATE_CHECK` — Skip checking for a newer CLI version before exiting the CLI process. +- `TAURI_TRAY` — Set this var to `ayatana` to use `libayatana-appindicator` for system tray on Linux or set it `appindicator` to use `libappindicator`. + - For `deb` bundle target, the CLI will add the appropriate package as a dependency, if unset, the CLI will default to `ayatana`. + - For `appimage` bundle target, the CLI will copy the appropriate package to the appimage. if unset, the CLI will make a guess based on what package is installed on the developer system. +- `TAURI_FIPS_COMPLIANT` — Specify WiX `FipsCompliant` option +- `TAURI_PRIVATE_KEY` — Private key used to sign your app bundles +- `TAURI_KEY_PASSWORD` — The private key password, see `TAURI_PRIVATE_KEY` +- `APPLE_CERTIFICATE` — Base64 encoded of the `.p12` certificate for code signing. To get this value, run `openssl base64 -in MyCertificate.p12 -out MyCertificate-base64.txt`. +- `APPLE_CERTIFICATE_PASSWORD` — The password you used to export the certificate. +- `APPLE_ID` — The Apple ID used to notarize the application. If this environment variable is provided, `APPLE_PASSWORD` must also be set. Alternatively, `APPLE_API_KEY` and `APPLE_API_ISSUER` can be used to authenticate. +- `APPLE_PASSWORD` — The Apple password used to authenticate for application notarization. Required if `APPLE_ID` is specified. An app-specific password can be used. Alternatively to entering the password in plaintext, it may also be specified using a '@keychain:' or '@env:' prefix followed by a keychain password item name or environment variable name. +- `APPLE_API_KEY` — Alternative to `APPLE_ID` and `APPLE_PASSWORD` for notarization authentication using JWT. + - This option will search the following directories in sequence for a private key file with the name of 'AuthKey_.p8': './private_keys', '~/private_keys', '~/.private_keys', and '~/.appstoreconnect/private_keys'. Additionally, you can set environment variable $API_PRIVATE_KEYS_DIR or a user default API_PRIVATE_KEYS_DIR to specify the directory where your AuthKey file is located. + - See [creating API keys](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) for more information. +- `APPLE_API_ISSUER` — Issuer ID. Required if `APPLE_API_KEY` is specified. +- `APPLE_SIGNING_IDENTITY` — The identity used to code sign. Overwrites `tauri.conf.json > tauri > bundle > macOS > signingIdentity`. +- `APPLE_PROVIDER_SHORT_NAME` — If your Apple ID is connected to multiple teams, you have to specify the provider short name of the team you want to use to notarize your app. Overwrites `tauri.conf.json > tauri > bundle > macOS > providerShortName`. +- `CI` — If set, the CLI will run in CI mode and won't require any user interaction. + +### Tauri CLI Hook Commands + +These environment variables are set for each hook command (`beforeDevCommand`, `beforeBuildCommand`, ...etc) which could be useful to conditionally build your frontend or execute a specific action. + +- `TAURI_ARCH` — Target arch, `x86_64`, `aarch64`...etc. +- `TAURI_PLATFORM` — Target platform, `windows`, `macos`, `linux`...etc. +- `TAURI_FAMILY` — Target platform family `unix` or `windows`. +- `TAURI_PLATFORM_TYPE` — Target platform type `Linux`, `Windows_NT` or `Darwin` +- `TAURI_PLATFORM_VERSION` — Build platform version +- `TAURI_DEBUG` — `true` for `dev` command, `false` for `build` command. +- `TAURI_TARGET_TRIPLE` — Target triple the CLI is building. diff --git a/tooling/cli/README.md b/tooling/cli/README.md index 0e40f5475..1f9fabbdb 100644 --- a/tooling/cli/README.md +++ b/tooling/cli/README.md @@ -4,7 +4,7 @@ [![status](https://img.shields.io/badge/status-stable-blue.svg)](https://github.com/tauri-apps/tauri/tree/dev) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri) -[![test library](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library)](https://github.com/tauri-apps/tauri/actions?query=workflow%3A%22test+library%22) +[![test cli](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-cli-rs.yml?label=test%20cli&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-cli-rs.yml) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield) [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S) [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app) diff --git a/tooling/cli/node/CHANGELOG.md b/tooling/cli/node/CHANGELOG.md index b40bda9e5..6b8033e34 100644 --- a/tooling/cli/node/CHANGELOG.md +++ b/tooling/cli/node/CHANGELOG.md @@ -122,6 +122,29 @@ - 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.3.1] + +- Correctly escape XML for resource files in WiX bundler. + - Bumped due to a bump in tauri-bundler. + - Bumped due to a bump in cli.rs. + - [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. + - Bumped due to a bump in cli.rs. + +- [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. + - Bumped due to a bump in cli.rs. + - [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] - 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. diff --git a/tooling/cli/node/README.md b/tooling/cli/node/README.md index 1911835b1..bf961aa46 100644 --- a/tooling/cli/node/README.md +++ b/tooling/cli/node/README.md @@ -3,7 +3,7 @@ [![status](https://img.shields.io/badge/status-stable-blue.svg)](https://github.com/tauri-apps/tauri/tree/dev) [![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri) -[![test library](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library)](https://github.com/tauri-apps/tauri/actions?query=workflow%3A%22test+library%22) +[![test cli](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-cli-js.yml?label=test%20cli&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-cli-js.yml) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield) [![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S) [![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app) @@ -19,7 +19,7 @@ Tauri is a polyglot and generic system that is very composable and allows engine Tauri apps can have custom menus and have tray-type interfaces. They can be updated, and are managed by the user's operating system as expected. They are very small, because they use the system's webview. They do not ship a runtime, since the final binary is compiled from rust. This makes the reversing of Tauri apps not a trivial task. ## This module -Written in Typescript and packaged such that it can be used with `npm`, `pnpm`, and `yarn`, this library provides a node.js runner for common tasks when using Tauri, like `yarn tauri dev`. For the most part it is a wrapper around [cli.rs](https://github.com/tauri-apps/tauri/blob/dev/tooling/cli). +Written in Typescript and packaged such that it can be used with `npm`, `pnpm`, and `yarn`, this library provides a node.js runner for common tasks when using Tauri, like `yarn tauri dev`. For the most part it is a wrapper around [tauri-cli](https://github.com/tauri-apps/tauri/blob/dev/tooling/cli). To learn more about the details of how all of these pieces fit together, please consult this [ARCHITECTURE.md](https://github.com/tauri-apps/tauri/blob/dev/ARCHITECTURE.md) document. diff --git a/tooling/cli/node/package.json b/tooling/cli/node/package.json index 234773f3c..89fb4b38b 100644 --- a/tooling/cli/node/package.json +++ b/tooling/cli/node/package.json @@ -43,7 +43,7 @@ "fs-extra": "11.1.1", "jest": "29.5.0", "jest-transform-toml": "1.0.0", - "prettier": "2.8.7" + "prettier": "2.8.8" }, "resolutions": { "json5": "2.2.3" @@ -56,7 +56,7 @@ }, "scripts": { "artifacts": "napi artifacts", - "build:release": "cross-env TARGET=node napi build --platform --release", + "build:release": "cross-env TARGET=node napi build --platform --release --features openssl-vendored", "build": "cross-env TARGET=node napi build --platform", "prepublishOnly": "napi prepublish -t npm --gh-release-id $RELEASE_ID", "prepack": "cp ../schema.json .", diff --git a/tooling/cli/node/tauri.js b/tooling/cli/node/tauri.js index 3bd320171..1516306df 100755 --- a/tooling/cli/node/tauri.js +++ b/tooling/cli/node/tauri.js @@ -20,7 +20,7 @@ if (bin === '@tauri-apps/cli') { } // Even if started by a package manager, the binary will be NodeJS. // Some distribution still use "nodejs" as the binary name. -else if (binStem.match(/(nodejs|node)\-?([1-9]*)*$/g)) { +else if (binStem.match(/(nodejs|node)\-?([0-9]*)*$/g)) { const managerStem = process.env.npm_execpath ? path.parse(process.env.npm_execpath).name.toLowerCase() : null diff --git a/tooling/cli/node/test/jest/__tests__/template.spec.js b/tooling/cli/node/test/jest/__tests__/template.spec.js index d598a0347..61d1ca681 100644 --- a/tooling/cli/node/test/jest/__tests__/template.spec.js +++ b/tooling/cli/node/test/jest/__tests__/template.spec.js @@ -10,7 +10,7 @@ const cli = require('~/main.js') const currentDirName = __dirname -describe('[CLI] cli.js template', () => { +describe('[CLI] @tauri-apps/cli template', () => { it('init a project and builds it', async () => { const cwd = process.cwd() const fixturePath = resolve(currentDirName, '../fixtures/empty') diff --git a/tooling/cli/node/yarn.lock b/tooling/cli/node/yarn.lock index 964086069..e164bbce6 100644 --- a/tooling/cli/node/yarn.lock +++ b/tooling/cli/node/yarn.lock @@ -2033,10 +2033,10 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -prettier@2.8.7: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== +prettier@2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-format@^29.5.0: version "29.5.0" diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index 24e4c6283..2e87ffe2e 100644 --- a/tooling/cli/schema.json +++ b/tooling/cli/schema.json @@ -125,7 +125,7 @@ "pattern": "^[^/\\:*?\"<>|]+$" }, "version": { - "description": "App version. It is a semver version number or a path to a `package.json` file containing the `version` field.", + "description": "App version. It is a semver version number or a path to a `package.json` file containing the `version` field. If removed the version number from `Cargo.toml` is used.", "default": null, "type": [ "string", @@ -386,7 +386,22 @@ "format": "double" }, "resizable": { - "description": "Whether the window is resizable or not.", + "description": "Whether the window is resizable or not. When resizable is set to false, native window's maximize button is automatically disabled.", + "default": true, + "type": "boolean" + }, + "maximizable": { + "description": "Whether the window's native maximize button is enabled or not. If resizable is set to false, this setting is ignored.\n\n## Platform-specific\n\n- **macOS:** Disables the \"zoom\" button in the window titlebar, which is also used to enter fullscreen mode. - **Linux / iOS / Android:** Unsupported.", + "default": true, + "type": "boolean" + }, + "minimizable": { + "description": "Whether the window's native minimize button is enabled or not.\n\n## Platform-specific\n\n- **Linux / iOS / Android:** Unsupported.", + "default": true, + "type": "boolean" + }, + "closable": { + "description": "Whether the window's native close button is enabled or not.\n\n## Platform-specific\n\n- **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.", "default": true, "type": "boolean" }, @@ -1121,6 +1136,13 @@ "additionalProperties": { "type": "string" } + }, + "desktopTemplate": { + "description": "Path to a custom desktop file Handlebars template.\n\nAvailable variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.", + "type": [ + "string", + "null" + ] } }, "additionalProperties": false @@ -1512,6 +1534,13 @@ "description": "Configuration for the Installer bundle using NSIS.", "type": "object", "properties": { + "template": { + "description": "A custom .nsi template to use.", + "type": [ + "string", + "null" + ] + }, "license": { "description": "The path to the license file to render on the installer.", "type": [ @@ -1559,6 +1588,16 @@ "type": "string" } }, + "customLanguageFiles": { + "description": "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.\n\nSee for an example `.nsh` file.\n\n**Note**: the key must be a valid NSIS language and it must be added to [`NsisConfig`] languages array,", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "string" + } + }, "displayLanguageSelector": { "description": "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.", "default": false, @@ -1676,7 +1715,7 @@ ] }, { - "description": "The quiet mode means there's no user interaction required. Requires admin privileges if the installer does (WiX).", + "description": "The quiet mode means there's no user interaction required. Requires admin privileges if the installer does.", "type": "string", "enum": [ "quiet" @@ -1889,6 +1928,13 @@ "items": { "type": "string" } + }, + "requireLiteralLeadingDot": { + "description": "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.\n\nDefaults to `false` on Unix systems and `true` on Windows", + "type": [ + "boolean", + "null" + ] } } } diff --git a/tooling/cli/scripts/vswhere.exe b/tooling/cli/scripts/vswhere.exe index b48c6da15..64f58cc93 100644 Binary files a/tooling/cli/scripts/vswhere.exe and b/tooling/cli/scripts/vswhere.exe differ diff --git a/tooling/cli/src/completions.rs b/tooling/cli/src/completions.rs new file mode 100644 index 000000000..874e5d2a0 --- /dev/null +++ b/tooling/cli/src/completions.rs @@ -0,0 +1,97 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use crate::Result; +use anyhow::Context; +use clap::{Command, Parser}; +use clap_complete::{generate, Shell}; +use log::info; + +use std::{fs::write, path::PathBuf}; + +const PKG_MANAGERS: &[&str] = &["cargo", "pnpm", "npm", "yarn"]; + +#[derive(Debug, Clone, Parser)] +#[clap(about = "Shell completions")] +pub struct Options { + /// Shell to generate a completion script for. + #[clap(short, long, verbatim_doc_comment)] + shell: Shell, + /// Output file for the shell completions. By default the completions are printed to stdout. + #[clap(short, long)] + output: Option, +} + +fn completions_for(shell: Shell, manager: &'static str, cmd: Command) -> Vec { + let tauri = cmd.name("tauri"); + let mut command = if manager == "npm" { + Command::new(manager) + .bin_name(manager) + .subcommand(Command::new("run").subcommand(tauri)) + } else { + Command::new(manager).bin_name(manager).subcommand(tauri) + }; + + let mut buf = Vec::new(); + generate(shell, &mut command, manager, &mut buf); + buf +} + +fn get_completions(shell: Shell, cmd: Command) -> Result { + let completions = if shell == Shell::Bash { + let mut completions = + String::from_utf8_lossy(&completions_for(shell, "cargo", cmd)).into_owned(); + for manager in PKG_MANAGERS { + completions.push_str(&format!( + "complete -F _cargo -o bashdefault -o default {} tauri\n", + if manager == &"npm" { + "npm run" + } else { + manager + } + )); + } + completions + } else { + let mut buffer = String::new(); + + for (i, manager) in PKG_MANAGERS.iter().enumerate() { + let buf = String::from_utf8_lossy(&completions_for(shell, manager, cmd.clone())).into_owned(); + + let completions = match shell { + Shell::PowerShell => { + if i != 0 { + // namespaces have already been imported + buf + .replace("using namespace System.Management.Automation.Language", "") + .replace("using namespace System.Management.Automation", "") + } else { + buf + } + } + _ => buf, + }; + + buffer.push_str(&completions); + buffer.push('\n'); + } + + buffer + }; + + Ok(completions) +} + +pub fn command(options: Options, cmd: Command) -> Result<()> { + info!("Generating completion file for {}...", options.shell); + + let completions = get_completions(options.shell, cmd)?; + if let Some(output) = options.output { + write(output, completions).context("failed to write to output path")?; + } else { + print!("{completions}"); + } + + Ok(()) +} diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index 91de59dd4..ef51fdd52 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -360,11 +360,15 @@ pub fn setup(options: &mut Options, mobile: bool) -> Result { }; let mut i = 0; let sleep_interval = std::time::Duration::from_secs(2); + let timeout_duration = std::time::Duration::from_secs(1); let max_attempts = 90; - loop { - if std::net::TcpStream::connect(addrs).is_ok() { - break; + 'waiting: loop { + for addr in addrs.iter() { + if std::net::TcpStream::connect_timeout(addr, timeout_duration).is_ok() { + break 'waiting; + } } + if i % 3 == 1 { warn!( "Waiting for your frontend dev server to start on {}...", diff --git a/tooling/cli/src/helpers/config.rs b/tooling/cli/src/helpers/config.rs index 5be0e1635..035cf3554 100644 --- a/tooling/cli/src/helpers/config.rs +++ b/tooling/cli/src/helpers/config.rs @@ -99,12 +99,14 @@ pub fn wix_settings(config: WixConfig) -> tauri_bundler::WixSettings { pub fn nsis_settings(config: NsisConfig) -> tauri_bundler::NsisSettings { tauri_bundler::NsisSettings { + template: config.template, license: config.license, header_image: config.header_image, sidebar_image: config.sidebar_image, installer_icon: config.installer_icon, install_mode: config.install_mode, languages: config.languages, + custom_language_files: config.custom_language_files, display_language_selector: config.display_language_selector, } } diff --git a/tooling/cli/src/helpers/web_dev_server.rs b/tooling/cli/src/helpers/web_dev_server.rs index d1b5c458b..446f9291f 100644 --- a/tooling/cli/src/helpers/web_dev_server.rs +++ b/tooling/cli/src/helpers/web_dev_server.rs @@ -154,7 +154,7 @@ async fn handler(req: Request, state: Arc) -> impl IntoResponse { file .map(|mut f| { - let mime_type = MimeType::parse(&f, uri); + let mime_type = MimeType::parse_with_fallback(&f, uri, MimeType::OctetStream); if mime_type == MimeType::Html.to_string() { let mut document = kuchiki::parse_html().one(String::from_utf8_lossy(&f).into_owned()); fn with_html_head(document: &mut NodeRef, f: F) { diff --git a/tooling/cli/src/info/env_rust.rs b/tooling/cli/src/info/env_rust.rs index dfee30198..40f03ebd5 100644 --- a/tooling/cli/src/info/env_rust.rs +++ b/tooling/cli/src/info/env_rust.rs @@ -31,7 +31,7 @@ pub fn items() -> Vec { .unwrap_or_else(|| { ( format!( - "rustc: {}\nmaybe you don't have rust installed! Visist {}", + "rustc: {}\nMaybe you don't have rust installed! Visit {}", "not installed!".red(), "https://rustup.rs/".cyan() ), @@ -65,7 +65,7 @@ pub fn items() -> Vec { .unwrap_or_else(|| { ( format!( - "Cargo: {}\nmaybe you don't have rust installed! Visit {}", + "Cargo: {}\nMaybe you don't have rust installed! Visit {}", "not installed!".red(), "https://rustup.rs/".cyan() ), @@ -131,7 +131,7 @@ pub fn items() -> Vec { .unwrap_or_else(|| { ( format!( - "Rust toolchain: couldn't be deteceted!\nmaybe you don't have rustup installed? if so, Visit {}", "https://rustup.rs/".cyan() + "Rust toolchain: couldn't be detected!\nMaybe you don't have rustup installed? if so, Visit {}", "https://rustup.rs/".cyan() ), Status::Warning, ) diff --git a/tooling/cli/src/info/env_system.rs b/tooling/cli/src/info/env_system.rs index 543268930..654bbfcd3 100644 --- a/tooling/cli/src/info/env_system.rs +++ b/tooling/cli/src/info/env_system.rs @@ -20,7 +20,7 @@ struct VsInstanceInfo { const VSWHERE: &[u8] = include_bytes!("../../scripts/vswhere.exe"); #[cfg(windows)] -fn build_tools_version() -> crate::Result>> { +fn build_tools_version() -> crate::Result> { let mut vswhere = std::env::temp_dir(); vswhere.push("vswhere.exe"); @@ -30,32 +30,60 @@ fn build_tools_version() -> crate::Result>> { let _ = file.write_all(VSWHERE); } } - let output = Command::new(vswhere) + + // Check if there are Visual Studio installations that have the "MSVC - C++ Buildtools" and "Windows SDK" components. + // Both the Windows 10 and Windows 11 SDKs work so we need to query it twice. + let output_sdk10 = Command::new(&vswhere) .args([ "-prerelease", "-products", "*", - "-requiresAny", "-requires", - "Microsoft.VisualStudio.Workload.NativeDesktop", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-requires", - "Microsoft.VisualStudio.Workload.VCTools", + "Microsoft.VisualStudio.Component.Windows10SDK.*", "-format", "json", ]) .output()?; - Ok(if output.status.success() { - let stdout = String::from_utf8_lossy(&output.stdout); - let instances: Vec = serde_json::from_str(&stdout)?; - Some( - instances - .iter() - .map(|i| i.display_name.clone()) - .collect::>(), - ) - } else { - None - }) + + let output_sdk11 = Command::new(vswhere) + .args([ + "-prerelease", + "-products", + "*", + "-requires", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-requires", + "Microsoft.VisualStudio.Component.Windows11SDK.*", + "-format", + "json", + ]) + .output()?; + + let mut instances: Vec = Vec::new(); + + if output_sdk10.status.success() { + let stdout = String::from_utf8_lossy(&output_sdk10.stdout); + let found: Vec = serde_json::from_str(&stdout)?; + instances.extend(found); + } + + if output_sdk11.status.success() { + let stdout = String::from_utf8_lossy(&output_sdk11.stdout); + let found: Vec = serde_json::from_str(&stdout)?; + instances.extend(found); + } + + let mut instances: Vec = instances + .iter() + .map(|i| i.display_name.clone()) + .collect::>(); + + instances.sort_unstable(); + instances.dedup(); + + Ok(instances) } #[cfg(windows)] @@ -190,13 +218,11 @@ pub fn items() -> Vec { #[cfg(windows)] SectionItem::new( || { - let build_tools = build_tools_version() - .unwrap_or_default() - .unwrap_or_default(); + let build_tools = build_tools_version().unwrap_or_default(); if build_tools.is_empty() { Some(( format!( - "Couldn't detect Visual Studio or Visual Studio Build Tools. Download from {}", + "Couldn't detect any Visual Studio or VS Build Tools instance with MSVC and SDK components. Download from {}", "https://aka.ms/vs/17/release/vs_BuildTools.exe".cyan() ), Status::Error, diff --git a/tooling/cli/src/init.rs b/tooling/cli/src/init.rs index 1ea407951..a2dcaceec 100644 --- a/tooling/cli/src/init.rs +++ b/tooling/cli/src/init.rs @@ -184,7 +184,7 @@ pub fn command(mut options: Options) -> Result<()> { let _ = remove_dir_all(&template_target_path); let mut handlebars = Handlebars::new(); - handlebars.register_escape_fn(|s| s.into()); + handlebars.register_escape_fn(handlebars::no_escape); let mut data = BTreeMap::new(); data.insert("tauri_dep", to_json(tauri_dep)); diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 05ffd5e3b..1350374ea 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -713,7 +713,7 @@ impl AppSettings for RustAppSettings { .expect("Cargo manifest must have the `package.name` field"); let out_dir = self - .out_dir(options.target.clone(), options.debug) + .out_dir(options.target.clone(), get_profile(options)) .with_context(|| "failed to get project out directory")?; let binary_extension: String = if self.target_triple.contains("windows") { @@ -974,12 +974,12 @@ impl RustAppSettings { &self.cargo_package_settings } - pub fn out_dir(&self, target: Option, debug: bool) -> crate::Result { + pub fn out_dir(&self, target: Option, profile: String) -> crate::Result { get_target_dir( target .as_deref() .or_else(|| self.cargo_config.build().target()), - !debug, + profile, ) } } @@ -1006,9 +1006,9 @@ fn get_cargo_metadata() -> crate::Result { Ok(serde_json::from_slice(&output.stdout)?) } -/// This function determines the 'target' directory and suffixes it with 'release' or 'debug' +/// This function determines the 'target' directory and suffixes it with the profile /// to determine where the compiled binary will be located. -fn get_target_dir(target: Option<&str>, is_release: bool) -> crate::Result { +fn get_target_dir(target: Option<&str>, profile: String) -> crate::Result { let mut path = get_cargo_metadata() .with_context(|| "failed to get cargo metadata")? .target_directory; @@ -1017,7 +1017,7 @@ fn get_target_dir(target: Option<&str>, is_release: bool) -> crate::Result crate::Result { ) } +pub fn get_profile(options: &Options) -> String { + options + .args + .iter() + .position(|a| a == "--profile") + .map(|i| options.args[i + 1].clone()) + .unwrap_or_else(|| if options.debug { "debug" } else { "release" }.into()) +} + #[allow(unused_variables)] fn tauri_config_to_bundle_settings( manifest: &Manifest, @@ -1131,6 +1140,7 @@ fn tauri_config_to_bundle_settings( Some(depends) }, files: config.deb.files, + desktop_template: config.deb.desktop_template, }, macos: MacOsSettings { frameworks: config.macos.frameworks, diff --git a/tooling/cli/src/interface/rust/desktop.rs b/tooling/cli/src/interface/rust/desktop.rs index 71e3b3a8b..aedcfba71 100644 --- a/tooling/cli/src/interface/rust/desktop.rs +++ b/tooling/cli/src/interface/rust/desktop.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use super::{AppSettings, DevProcess, ExitReason, Options, RustAppSettings, Target}; +use super::{get_profile, AppSettings, DevProcess, ExitReason, Options, RustAppSettings, Target}; use crate::CommandExt; use tauri_utils::display_path; @@ -167,7 +167,7 @@ pub fn build( options.target.replace(triple.into()); let triple_out_dir = app_settings - .out_dir(Some(triple.into()), options.debug) + .out_dir(Some(triple.into()), get_profile(&options)) .with_context(|| format!("failed to get {triple} out dir"))?; build_production_app(options, available_targets, config_features.clone()) diff --git a/tooling/cli/src/interface/rust/manifest.rs b/tooling/cli/src/interface/rust/manifest.rs index 6b3a00e39..7a8865531 100644 --- a/tooling/cli/src/interface/rust/manifest.rs +++ b/tooling/cli/src/interface/rust/manifest.rs @@ -146,14 +146,15 @@ fn write_features( } // remove features that shouldn't be in the manifest anymore - let mut i = 0; - while i < features_array.len() { - if let Some(f) = features_array.get(i).and_then(|f| f.as_str()) { + let mut i = features_array.len(); + while i != 0 { + let index = i - 1; + if let Some(f) = features_array.get(index).and_then(|f| f.as_str()) { if !features.contains(f) { - features_array.remove(i); + features_array.remove(index); } } - i += 1; + i -= 1; } } else { *manifest_features = Item::Value(Value::Array(toml_array(features))); diff --git a/tooling/cli/src/lib.rs b/tooling/cli/src/lib.rs index 81af93e5a..8145887d2 100644 --- a/tooling/cli/src/lib.rs +++ b/tooling/cli/src/lib.rs @@ -5,6 +5,7 @@ pub use anyhow::Result; mod build; +mod completions; mod dev; mod helpers; mod icon; @@ -77,7 +78,7 @@ pub struct PackageJson { propagate_version(true), no_binary_name(true) )] -struct Cli { +pub(crate) struct Cli { /// Enables verbose logging #[clap(short, long, global = true, action = ArgAction::Count)] verbose: u8, @@ -94,6 +95,7 @@ enum Commands { Init(init::Options), Plugin(plugin::Cli), Signer(signer::Cli), + Completions(completions::Options), Android(mobile::android::Cli), #[cfg(target_os = "macos")] Ios(mobile::ios::Cli), @@ -137,11 +139,12 @@ where I: IntoIterator, A: Into + Clone, { - let matches = match bin_name { + let cli = match bin_name { Some(bin_name) => Cli::command().bin_name(bin_name), None => Cli::command(), - } - .get_matches_from(args); + }; + let cli_ = cli.clone(); + let matches = cli.get_matches_from(args); let res = Cli::from_arg_matches(&matches).map_err(format_error::); let cli = match res { @@ -198,6 +201,7 @@ where Commands::Init(options) => init::command(options)?, Commands::Plugin(cli) => plugin::command(cli)?, Commands::Signer(cli) => signer::command(cli)?, + Commands::Completions(options) => completions::command(options, cli_)?, Commands::Android(c) => mobile::android::command(c, cli.verbose)?, #[cfg(target_os = "macos")] Commands::Ios(c) => mobile::ios::command(c, cli.verbose)?, diff --git a/tooling/cli/src/plugin/init.rs b/tooling/cli/src/plugin/init.rs index 06b35fddd..8fc572e5a 100644 --- a/tooling/cli/src/plugin/init.rs +++ b/tooling/cli/src/plugin/init.rs @@ -98,7 +98,7 @@ pub fn command(mut options: Options) -> Result<()> { let _ = remove_dir_all(&template_target_path); let mut handlebars = Handlebars::new(); - handlebars.register_escape_fn(|s| s.into()); + handlebars.register_escape_fn(handlebars::no_escape); let mut data = BTreeMap::new(); plugin_name_data(&mut data, &options.plugin_name); diff --git a/tooling/cli/src/signer/sign.rs b/tooling/cli/src/signer/sign.rs index b29d6e031..eaefa40af 100644 --- a/tooling/cli/src/signer/sign.rs +++ b/tooling/cli/src/signer/sign.rs @@ -17,10 +17,10 @@ use tauri_utils::display_path; #[clap(about = "Sign a file")] pub struct Options { /// Load the private key from a file - #[clap(short = 'k', long, conflicts_with("private-key-path"))] + #[clap(short = 'k', long, conflicts_with("private_key_path"))] private_key: Option, /// Load the private key from a string - #[clap(short = 'f', long, conflicts_with("private-key"))] + #[clap(short = 'f', long, conflicts_with("private_key"))] private_key_path: Option, /// Set private key password when signing #[clap(short, long)]