Compare commits

..

1 Commits

Author SHA1 Message Date
FabianLars 14381190e7 chore: Apply clippy suggestions 2025-07-23 12:44:36 +02:00
293 changed files with 2410 additions and 5767 deletions
@@ -1,6 +0,0 @@
---
"dialog-js": minor:feat
"dialog": minor:feat
---
Add `xdg-portal` as an optional feature for `rfd`
@@ -1,6 +0,0 @@
---
"log": "minor"
"log-js": "minor"
---
Allow specifying a log formatter per target using the `format` method on `Target`.
-10
View File
@@ -78,7 +78,6 @@
"notification", "notification",
"os", "os",
"process", "process",
"secure-storage",
"shell", "shell",
"store", "store",
"updater", "updater",
@@ -105,7 +104,6 @@
"notification-js", "notification-js",
"os-js", "os-js",
"process-js", "process-js",
"secure-storage-js",
"shell-js", "shell-js",
"store-js", "store-js",
"updater-js" "updater-js"
@@ -284,14 +282,6 @@
"path": "./plugins/process", "path": "./plugins/process",
"manager": "javascript" "manager": "javascript"
}, },
"secure-storage": {
"path": "./plugins/secure-storage",
"manager": "rust"
},
"secure-storage-js": {
"path": "./plugins/secure-storage",
"manager": "javascript"
},
"shell": { "shell": {
"path": "./plugins/shell", "path": "./plugins/shell",
"manager": "rust" "manager": "rust"
-6
View File
@@ -1,6 +0,0 @@
---
stronghold: patch
stronghold-js: patch
---
The `stronghold` plugin is now deprecated and will be removed in v3. The `secure-storage` plugin should be a replacement for most use-cases.
-6
View File
@@ -1,6 +0,0 @@
---
"dialog": minor
"dialog-js": minor
---
Add `pickerMode` option to file picker (currently only used on iOS)
@@ -1,5 +0,0 @@
---
"localhost": patch
---
Disable caching on responses.
@@ -1,6 +0,0 @@
---
"barcode-scanner": patch
"barcode-scanner-js": patch
---
Fix the `cameraView` is not removed after scanning in iOS.
-6
View File
@@ -1,6 +0,0 @@
---
"log": patch
"log-js": patch
---
Fix log file rotation when exceeding `max_file_size`.
-6
View File
@@ -1,6 +0,0 @@
---
"nfc": "patch"
"nfc-js": "patch"
---
Update return value of `isAvailable` to match TypeScript function signature
-6
View File
@@ -1,6 +0,0 @@
---
"updater": minor
"updater-js": minor
---
Updater plugin now supports all bundle types: Deb, Rpm and AppImage for Linux; NSiS, MSI for Windows. This was added in https://github.com/tauri-apps/plugins-workspace/pull/2624
-6
View File
@@ -1,6 +0,0 @@
---
"upload": minor
"upload-js": minor
---
Upload plugin now supports specifying an HTTP method i.e. POST, PUT etc.
@@ -101,11 +101,6 @@ jobs:
- pnpm-lock.yaml - pnpm-lock.yaml
- plugins/process/guest-js/** - plugins/process/guest-js/**
- plugins/process/src/api-iife.js - plugins/process/src/api-iife.js
secure-storage:
- .github/workflows/check-generated-files.yml
- pnpm-lock.yaml
- plugins/secure-storage/guest-js/**
- plugins/secure-storage/src/api-iife.js
shell: shell:
- .github/workflows/check-generated-files.yml - .github/workflows/check-generated-files.yml
- pnpm-lock.yaml - pnpm-lock.yaml
@@ -11,7 +11,7 @@ on:
- v2 - v2
permissions: permissions:
# required for oidc token # required for npm provenance
id-token: write id-token: write
# required to create the GitHub Release # required to create the GitHub Release
contents: write contents: write
@@ -62,6 +62,7 @@ jobs:
id: covector id: covector
env: env:
CARGO_TARGET_DIR: /mnt/target CARGO_TARGET_DIR: /mnt/target
NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }}
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
command: 'version-or-publish' command: 'version-or-publish'
-4
View File
@@ -98,9 +98,6 @@ jobs:
tauri-plugin-process: tauri-plugin-process:
- .github/workflows/lint-rust.yml - .github/workflows/lint-rust.yml
- plugins/process/** - plugins/process/**
tauri-plugin-secure-storage:
- .github/workflows/lint-rust.yml
- plugins/secure-storage/**
tauri-plugin-shell: tauri-plugin-shell:
- .github/workflows/lint-rust.yml - .github/workflows/lint-rust.yml
- plugins/shell/** - plugins/shell/**
@@ -157,5 +154,4 @@ jobs:
run: cargo clippy --package ${{ matrix.package }} --all-targets -- -D warnings run: cargo clippy --package ${{ matrix.package }} --all-targets -- -D warnings
- name: clippy ${{ matrix.package }} --all-features - name: clippy ${{ matrix.package }} --all-features
if: matrix.package != 'tauri-plugin-dialog'
run: cargo clippy --package ${{ matrix.package }} --all-targets --all-features -- -D warnings run: cargo clippy --package ${{ matrix.package }} --all-targets --all-features -- -D warnings
+4 -9
View File
@@ -137,11 +137,6 @@ jobs:
- Cargo.toml - Cargo.toml
- Cargo.lock - Cargo.lock
- plugins/process/** - plugins/process/**
tauri-plugin-secure-storage:
- .github/workflows/test-rust.yml
- Cargo.toml
- Cargo.lock
- plugins/secure-storage/**
tauri-plugin-shell: tauri-plugin-shell:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
@@ -224,7 +219,7 @@ jobs:
target: aarch64-linux-android, target: aarch64-linux-android,
os: ubuntu-latest, os: ubuntu-latest,
runner: 'cross', runner: 'cross',
command: 'build --verbose' command: 'build'
} }
runs-on: ${{ matrix.platform.os }} runs-on: ${{ matrix.platform.os }}
@@ -238,7 +233,7 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev
- uses: dtolnay/rust-toolchain@1.85 - uses: dtolnay/rust-toolchain@1.77.2
with: with:
targets: ${{ matrix.platform.target }} targets: ${{ matrix.platform.target }}
@@ -251,9 +246,9 @@ jobs:
run: cargo +stable install cross --git https://github.com/cross-rs/cross run: cargo +stable install cross --git https://github.com/cross-rs/cross
- name: test ${{ matrix.package }} - name: test ${{ matrix.package }}
if: matrix.package != 'tauri-plugin-http' && matrix.package != 'tauri-plugin-secure-storage' && matrix.package != 'tauri-plugin-dialog' if: matrix.package != 'tauri-plugin-http'
run: ${{ matrix.platform.runner }} ${{ matrix.platform.command }} --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets --all-features run: ${{ matrix.platform.runner }} ${{ matrix.platform.command }} --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets --all-features
- name: test ${{ matrix.package }} - name: test ${{ matrix.package }}
if: ${{ matrix.package == 'tauri-plugin-http' || matrix.package == 'tauri-plugin-dialog' }} if: matrix.package == 'tauri-plugin-http'
run: ${{ matrix.platform.runner }} ${{ matrix.platform.command }} --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets run: ${{ matrix.platform.runner }} ${{ matrix.platform.command }} --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets
+1 -1
View File
@@ -58,4 +58,4 @@ pids
.idea .idea
debug.log debug.log
TODO.md TODO.md
.aider.* .aider*
-1
View File
@@ -1,3 +1,2 @@
plugins/*/permissions/autogenerated/ plugins/*/permissions/autogenerated/
plugins/*/android/.tauri/tauri-api/build/ plugins/*/android/.tauri/tauri-api/build/
plugins/*/android/build/intermediates/
Generated
+125 -368
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -12,10 +12,10 @@ resolver = "2"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
tracing = "0.1" tracing = "0.1"
log = "0.4" log = "0.4"
tauri = { version = "2.8.2", default-features = false } tauri = { version = "2.6", default-features = false }
tauri-build = "2.4" tauri-build = "2.3"
tauri-plugin = "2.4" tauri-plugin = "2.3"
tauri-utils = "2.7" tauri-utils = "2.5"
serde_json = "1" serde_json = "1"
thiserror = "2" thiserror = "2"
url = "2" url = "2"
+1 -2
View File
@@ -3,12 +3,11 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import eslint from '@eslint/js' import eslint from '@eslint/js'
import { defineConfig } from 'eslint/config'
import eslintConfigPrettier from 'eslint-config-prettier' import eslintConfigPrettier from 'eslint-config-prettier'
import eslintPluginSecurity from 'eslint-plugin-security' import eslintPluginSecurity from 'eslint-plugin-security'
import tseslint from 'typescript-eslint' import tseslint from 'typescript-eslint'
export default defineConfig( export default tseslint.config(
{ {
ignores: [ ignores: [
'**/target', '**/target',
-73
View File
@@ -1,78 +1,5 @@
# Changelog # Changelog
## \[2.0.34]
### Dependencies
- Upgraded to `barcode-scanner-js@2.4.2`
- Upgraded to `biometric-js@2.3.2`
- Upgraded to `cli-js@2.4.1`
- Upgraded to `clipboard-manager-js@2.3.2`
- Upgraded to `dialog-js@2.4.2`
- Upgraded to `fs-js@2.4.4`
- Upgraded to `global-shortcut-js@2.3.1`
- Upgraded to `http-js@2.5.4`
- Upgraded to `log-js@2.7.1`
- Upgraded to `nfc-js@2.3.3`
- Upgraded to `notification-js@2.3.3`
- Upgraded to `opener-js@2.5.2`
- Upgraded to `os-js@2.3.2`
- Upgraded to `process-js@2.3.1`
- Upgraded to `shell-js@2.3.3`
- Upgraded to `store-js@2.4.1`
## \[2.0.33]
### Dependencies
- Upgraded to `barcode-scanner-js@2.4.1`
- Upgraded to `biometric-js@2.3.1`
- Upgraded to `clipboard-manager-js@2.3.1`
- Upgraded to `dialog-js@2.4.1`
- Upgraded to `fs-js@2.4.3`
- Upgraded to `nfc-js@2.3.2`
- Upgraded to `notification-js@2.3.2`
- Upgraded to `opener-js@2.5.1`
- Upgraded to `shell-js@2.3.2`
- Upgraded to `http-js@2.5.3`
## \[2.0.32]
### Dependencies
- Upgraded to `dialog-js@2.4.0`
- Upgraded to `log-js@2.7.0`
## \[2.0.31]
### Dependencies
- Upgraded to `shell-js@2.3.1`
## \[2.0.30]
### Dependencies
- Upgraded to `notification-js@2.3.1`
## \[2.0.29]
### Dependencies
- Upgraded to `fs-js@2.4.2`
- Upgraded to `nfc-js@2.3.1`
- Upgraded to `opener-js@2.5.0`
- Upgraded to `os-js@2.3.1`
- Upgraded to `store-js@2.4.0`
- Upgraded to `dialog-js@2.3.3`
- Upgraded to `http-js@2.5.2`
## \[2.0.28]
### Dependencies
- Upgraded to `dialog-js@2.3.2`
## \[2.0.27] ## \[2.0.27]
### Dependencies ### Dependencies
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta <meta
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=0" content="width=device-width, initial-scale=1.0, viewport-fit=cover"
/> />
<title>Svelte + Vite App</title> <title>Svelte + Vite App</title>
</head> </head>
+19 -20
View File
@@ -1,7 +1,7 @@
{ {
"name": "api", "name": "api",
"private": true, "private": true,
"version": "2.0.34", "version": "2.0.27",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --clearScreen false", "dev": "vite --clearScreen false",
@@ -10,25 +10,24 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "2.9.0", "@tauri-apps/api": "2.7.0",
"@tauri-apps/plugin-barcode-scanner": "^2.4.2", "@tauri-apps/plugin-barcode-scanner": "^2.4.0",
"@tauri-apps/plugin-biometric": "^2.3.2", "@tauri-apps/plugin-biometric": "^2.3.0",
"@tauri-apps/plugin-cli": "^2.4.1", "@tauri-apps/plugin-cli": "^2.4.0",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2", "@tauri-apps/plugin-clipboard-manager": "^2.3.0",
"@tauri-apps/plugin-dialog": "^2.4.2", "@tauri-apps/plugin-dialog": "^2.3.1",
"@tauri-apps/plugin-fs": "^2.4.4", "@tauri-apps/plugin-fs": "^2.4.1",
"@tauri-apps/plugin-geolocation": "^2.2.0", "@tauri-apps/plugin-geolocation": "^2.2.0",
"@tauri-apps/plugin-global-shortcut": "^2.3.1", "@tauri-apps/plugin-global-shortcut": "^2.3.0",
"@tauri-apps/plugin-haptics": "^2.2.0", "@tauri-apps/plugin-haptics": "^2.2.0",
"@tauri-apps/plugin-http": "^2.5.4", "@tauri-apps/plugin-http": "^2.5.1",
"@tauri-apps/plugin-nfc": "^2.3.3", "@tauri-apps/plugin-nfc": "^2.3.0",
"@tauri-apps/plugin-notification": "^2.3.3", "@tauri-apps/plugin-notification": "^2.3.0",
"@tauri-apps/plugin-opener": "^2.5.2", "@tauri-apps/plugin-opener": "^2.4.0",
"@tauri-apps/plugin-os": "^2.3.2", "@tauri-apps/plugin-os": "^2.3.0",
"@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-process": "^2.3.0",
"@tauri-apps/plugin-secure-storage": "file:../../plugins/secure-storage", "@tauri-apps/plugin-shell": "^2.3.0",
"@tauri-apps/plugin-shell": "^2.3.3", "@tauri-apps/plugin-store": "^2.3.0",
"@tauri-apps/plugin-store": "^2.4.1",
"@tauri-apps/plugin-updater": "^2.9.0", "@tauri-apps/plugin-updater": "^2.9.0",
"@tauri-apps/plugin-upload": "^2.3.0", "@tauri-apps/plugin-upload": "^2.3.0",
"@zerodevx/svelte-json-view": "1.0.11" "@zerodevx/svelte-json-view": "1.0.11"
@@ -37,10 +36,10 @@
"@iconify-json/codicon": "^1.2.12", "@iconify-json/codicon": "^1.2.12",
"@iconify-json/ph": "^1.2.2", "@iconify-json/ph": "^1.2.2",
"@sveltejs/vite-plugin-svelte": "^6.0.0", "@sveltejs/vite-plugin-svelte": "^6.0.0",
"@tauri-apps/cli": "2.9.4", "@tauri-apps/cli": "2.7.1",
"@unocss/extractor-svelte": "^66.3.3", "@unocss/extractor-svelte": "^66.3.3",
"svelte": "^5.20.4", "svelte": "^5.20.4",
"unocss": "^66.3.3", "unocss": "^66.3.3",
"vite": "^7.0.7" "vite": "^7.0.4"
} }
} }
-77
View File
@@ -1,82 +1,5 @@
# Changelog # Changelog
## \[2.0.38]
### Dependencies
- Upgraded to `barcode-scanner@2.4.2`
- Upgraded to `biometric@2.3.2`
- Upgraded to `cli@2.4.1`
- Upgraded to `clipboard-manager@2.3.2`
- Upgraded to `dialog@2.4.2`
- Upgraded to `fs@2.4.4`
- Upgraded to `geolocation@2.3.2`
- Upgraded to `global-shortcut@2.3.1`
- Upgraded to `haptics@2.3.2`
- Upgraded to `http@2.5.4`
- Upgraded to `log@2.7.1`
- Upgraded to `nfc@2.3.3`
- Upgraded to `notification@2.3.3`
- Upgraded to `opener@2.5.2`
- Upgraded to `os@2.3.2`
- Upgraded to `process@2.3.1`
- Upgraded to `shell@2.3.3`
- Upgraded to `store@2.4.1`
## \[2.0.37]
### Dependencies
- Upgraded to `barcode-scanner@2.4.1`
- Upgraded to `biometric@2.3.1`
- Upgraded to `clipboard-manager@2.3.1`
- Upgraded to `dialog@2.4.1`
- Upgraded to `fs@2.4.3`
- Upgraded to `geolocation@2.3.1`
- Upgraded to `haptics@2.3.1`
- Upgraded to `nfc@2.3.2`
- Upgraded to `notification@2.3.2`
- Upgraded to `opener@2.5.1`
- Upgraded to `shell@2.3.2`
- Upgraded to `http@2.5.3`
## \[2.0.36]
### Dependencies
- Upgraded to `dialog@2.4.0`
- Upgraded to `log@2.7.0`
## \[2.0.35]
### Dependencies
- Upgraded to `shell@2.3.1`
## \[2.0.34]
### Dependencies
- Upgraded to `notification@2.3.1`
## \[2.0.33]
### Dependencies
- Upgraded to `fs@2.4.2`
- Upgraded to `nfc@2.3.1`
- Upgraded to `opener@2.5.0`
- Upgraded to `os@2.3.1`
- Upgraded to `store@2.4.0`
- Upgraded to `dialog@2.3.3`
- Upgraded to `http@2.5.2`
## \[2.0.32]
### Dependencies
- Upgraded to `dialog@2.3.2`
## \[2.0.31] ## \[2.0.31]
### Dependencies ### Dependencies
+19 -20
View File
@@ -1,7 +1,7 @@
[package] [package]
name = "api" name = "api"
publish = false publish = false
version = "2.0.38" version = "2.0.31"
description = "An example Tauri Application showcasing the api" description = "An example Tauri Application showcasing the api"
edition = "2021" edition = "2021"
rust-version = { workspace = true } rust-version = { workspace = true }
@@ -20,25 +20,24 @@ serde = { workspace = true }
tiny_http = "0.12" tiny_http = "0.12"
time = "0.3" time = "0.3"
log = { workspace = true } log = { workspace = true }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.7.1" } tauri-plugin-log = { path = "../../../plugins/log", version = "2.6.0" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.4.4", features = [ tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.4.1", features = [
"watch", "watch",
] } ] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.2" } tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.0" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.4.2" } tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.3.1" }
tauri-plugin-http = { path = "../../../plugins/http", features = [ tauri-plugin-http = { path = "../../../plugins/http", features = [
"multipart", "multipart",
"cookies", "cookies",
], version = "2.5.4" } ], version = "2.5.1" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.3", features = [ tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.0", features = [
"windows7-compat", "windows7-compat",
] } ] }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.2" } tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.0" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.3.1" } tauri-plugin-process = { path = "../../../plugins/process", version = "2.3.0" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.5.2" } tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.4.0" }
tauri-plugin-secure-storage = { path = "../../../plugins/secure-storage" } tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.0" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.3" } tauri-plugin-store = { path = "../../../plugins/store", version = "2.3.0" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.4.1" }
tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" } tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" }
[dependencies.tauri] [dependencies.tauri]
@@ -56,17 +55,17 @@ features = [
] ]
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.4.1" } tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.4.0" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.3.1" } tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.3.0" }
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.9.0" } tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.9.0" }
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" } tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" }
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies] [target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.4.2" } tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.4.0" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.3" } tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.0" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.2" } tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.0" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.2" } tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.0" }
tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.3.2" } tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.3.0" }
[features] [features]
prod = ["tauri/custom-protocol"] prod = ["tauri/custom-protocol"]
+1 -11
View File
@@ -68,9 +68,6 @@
"fs:allow-rename", "fs:allow-rename",
"fs:allow-mkdir", "fs:allow-mkdir",
"fs:allow-remove", "fs:allow-remove",
"fs:allow-stat",
"fs:allow-fstat",
"fs:allow-lstat",
"fs:allow-write-text-file", "fs:allow-write-text-file",
"fs:read-meta", "fs:read-meta",
"fs:scope-download-recursive", "fs:scope-download-recursive",
@@ -78,9 +75,6 @@
{ {
"identifier": "fs:scope-appdata-recursive", "identifier": "fs:scope-appdata-recursive",
"allow": [ "allow": [
{
"path": "$APPDATA/db/"
},
{ {
"path": "$APPDATA/db/**" "path": "$APPDATA/db/**"
} }
@@ -102,10 +96,6 @@
"identifier": "opener:allow-open-path", "identifier": "opener:allow-open-path",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }] "allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
}, },
"upload:default", "upload:default"
"secure-storage:allow-get-string",
"secure-storage:allow-set-string",
"secure-storage:allow-get-binary",
"secure-storage:allow-set-binary"
] ]
} }
@@ -14,13 +14,13 @@ val tauriProperties = Properties().apply {
} }
android { android {
compileSdk = 36 compileSdk = 34
namespace = "com.tauri.api" namespace = "com.tauri.api"
defaultConfig { defaultConfig {
manifestPlaceholders["usesCleartextTraffic"] = "false" manifestPlaceholders["usesCleartextTraffic"] = "false"
applicationId = "com.tauri.api" applicationId = "com.tauri.api"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 34
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
} }
@@ -4,7 +4,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:8.11.0") classpath("com.android.tools.build:gradle:8.5.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25")
} }
} }
@@ -18,6 +18,6 @@ repositories {
dependencies { dependencies {
compileOnly(gradleApi()) compileOnly(gradleApi())
implementation("com.android.tools.build:gradle:8.11.0") implementation("com.android.tools.build:gradle:8.5.1")
} }
+1 -2
View File
@@ -38,7 +38,6 @@ pub fn run() {
.plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_secure_storage::init())
.plugin(tauri_plugin_store::Builder::default().build()) .plugin(tauri_plugin_store::Builder::default().build())
.plugin(tauri_plugin_upload::init()) .plugin(tauri_plugin_upload::init())
.setup(move |app| { .setup(move |app| {
@@ -96,7 +95,7 @@ pub fn run() {
let server = match tiny_http::Server::http("localhost:3003") { let server = match tiny_http::Server::http("localhost:3003") {
Ok(s) => s, Ok(s) => s,
Err(e) => { Err(e) => {
eprintln!("{}", e); eprintln!("{e}");
std::process::exit(1); std::process::exit(1);
} }
}; };
+6 -12
View File
@@ -23,7 +23,6 @@
import Biometric from './views/Biometric.svelte' import Biometric from './views/Biometric.svelte'
import Geolocation from './views/Geolocation.svelte' import Geolocation from './views/Geolocation.svelte'
import Haptics from './views/Haptics.svelte' import Haptics from './views/Haptics.svelte'
import SecureStorage from './views/SecureStorage.svelte'
import { onMount, tick } from 'svelte' import { onMount, tick } from 'svelte'
import { ask } from '@tauri-apps/plugin-dialog' import { ask } from '@tauri-apps/plugin-dialog'
@@ -104,11 +103,6 @@
component: Store, component: Store,
icon: 'i-codicon-file-code' icon: 'i-codicon-file-code'
}, },
{
label: 'SecureStorage',
component: SecureStorage,
icon: 'i-codicon-file-code'
},
!isMobile && { !isMobile && {
label: 'Updater', label: 'Updater',
component: Updater, component: Updater,
@@ -219,9 +213,9 @@
...r, ...r,
{ {
html: html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` `<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
+ (typeof value === 'string' ? value : JSON.stringify(value, null, 1)) (typeof value === 'string' ? value : JSON.stringify(value, null, 1)) +
+ '</pre>' '</pre>'
} }
]) ])
await tick() await tick()
@@ -235,9 +229,9 @@
...r, ...r,
{ {
html: html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` `<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
+ html html +
+ '</pre>' '</pre>'
} }
]) ])
await tick() await tick()
+8 -32
View File
@@ -8,7 +8,6 @@
let filter = null; let filter = null;
let multiple = false; let multiple = false;
let directory = false; let directory = false;
let pickerMode = "";
function arrayBufferToBase64(buffer, callback) { function arrayBufferToBase64(buffer, callback) {
var blob = new Blob([buffer], { var blob = new Blob([buffer], {
@@ -45,13 +44,6 @@
await message("Tauri is awesome!"); await message("Tauri is awesome!");
} }
async function msgCustom(result) {
const buttons = { yes: "awesome", no: "amazing", cancel: "stunning" };
await message(`Tauri is: `, { buttons })
.then((res) => onMessage(`Tauri is ${res}`))
.catch(onMessage);
}
function openDialog() { function openDialog() {
open({ open({
title: "My wonderful open dialog", title: "My wonderful open dialog",
@@ -66,7 +58,6 @@
: [], : [],
multiple, multiple,
directory, directory,
pickerMode: pickerMode === "" ? undefined : pickerMode,
}) })
.then(function (res) { .then(function (res) {
if (Array.isArray(res)) { if (Array.isArray(res)) {
@@ -96,7 +87,7 @@
onMessage(res); onMessage(res);
} }
}) })
.catch(onMessage); .catch(onMessage(res));
} }
}) })
.catch(onMessage); .catch(onMessage);
@@ -144,28 +135,13 @@
<input type="checkbox" id="dialog-directory" bind:checked={directory} /> <input type="checkbox" id="dialog-directory" bind:checked={directory} />
<label for="dialog-directory">Directory</label> <label for="dialog-directory">Directory</label>
</div> </div>
<div>
<label for="dialog-picker-mode">Picker Mode:</label>
<select id="dialog-picker-mode" bind:value={pickerMode}>
<option value="">None</option>
<option value="media">Media</option>
<option value="image">Image</option>
<option value="video">Video</option>
<option value="document">Document</option>
</select>
</div>
<br /> <br />
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
<div class="flex flex-wrap flex-col md:flex-row gap-2 children:flex-shrink-0"> <button class="btn" id="save-dialog" on:click={saveDialog}
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
<button class="btn" id="save-dialog" on:click={saveDialog}
>Open save dialog</button >Open save dialog</button
> >
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button> <button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom} <button class="btn" id="custom-prompt-dialog" on:click={promptCustom}
>Prompt (custom)</button >Prompt (custom)</button
> >
<button class="btn" id="message-dialog" on:click={msg}>Message</button> <button class="btn" id="message-dialog" on:click={msg}>Message</button>
<button class="btn" id="message-dialog" on:click={msgCustom}>Message (custom)</button>
</div>
+93 -114
View File
@@ -1,214 +1,194 @@
<script> <script>
import * as fs from '@tauri-apps/plugin-fs' import * as fs from "@tauri-apps/plugin-fs";
import * as os from '@tauri-apps/plugin-os' import { convertFileSrc } from "@tauri-apps/api/core";
import { convertFileSrc } from '@tauri-apps/api/core' import { arrayBufferToBase64 } from "../lib/utils";
import { arrayBufferToBase64 } from '../lib/utils' import { onDestroy } from "svelte";
import { onDestroy, onMount } from 'svelte'
const { onMessage, insecureRenderHtml } = $props() export let onMessage;
export let insecureRenderHtml;
let path = $state('') let path = "";
let img let img;
/** @type {fs.FileHandle} */ /** @type {fs.FileHandle} */
let file = $state() let file;
let renameTo = $state() let renameTo;
let watchPath = $state('') let watchPath = "";
let watchDebounceDelay = $state(0) let watchDebounceDelay = "0";
let watchRecursive = $state(false) let watchRecursive = false;
/** @type {fs.BaseDirectory | undefined} */ let unwatchFn;
let baseDir = $state() let unwatchPath = "";
let unwatchFn
let unwatchPath = ''
let isMobile = $state(false)
onMount(() => { function getDir() {
let platform = os.platform() const dirSelect = document.getElementById("dir");
isMobile = platform === 'android' || platform === 'ios' return dirSelect.value ? parseInt(dir.value) : null;
}) }
const dirOptions = Object.keys(fs.BaseDirectory).filter((key) => const DirOptions = Object.keys(fs.BaseDirectory)
isNaN(parseInt(key)) .filter((key) => isNaN(parseInt(key)))
) .map((dir) => [dir, fs.BaseDirectory[dir]]);
function open() { function open() {
fs.open(path, { fs.open(path, {
baseDir, baseDir: getDir(),
read: true, read: true,
write: true, write: true,
create: true create: true,
}) })
.then((f) => { .then((f) => {
file = f file = f;
onMessage(`Opened ${path}`) onMessage(`Opened ${path}`);
}) })
.catch(onMessage) .catch(onMessage);
} }
function mkdir() { function mkdir() {
fs.mkdir(path, { baseDir, recursive: true }) fs.mkdir(path, { baseDir: getDir() })
.then(() => { .then(() => {
onMessage(`Created dir ${path}`) onMessage(`Created dir ${path}`);
}) })
.catch(onMessage) .catch(onMessage);
} }
function remove() { function remove() {
fs.remove(path, { baseDir }) fs.remove(path, { baseDir: getDir() })
.then(() => { .then(() => {
onMessage(`Removed ${path}`) onMessage(`Removed ${path}`);
}) })
.catch(onMessage) .catch(onMessage);
} }
function rename() { function rename() {
fs.rename(path, renameTo, { fs.rename(path, renameTo, {
oldPathBaseDir, oldPathBaseDir: getDir(),
newPathBaseDir newPathBaseDir: getDir(),
}) })
.then(() => { .then(() => {
onMessage(`Renamed ${path} to ${renameTo}`) onMessage(`Renamed ${path} to ${renameTo}`);
}) })
.catch(onMessage) .catch(onMessage);
} }
function truncate() { function truncate() {
file file
.truncate(0) .truncate(0)
.then(() => { .then(() => {
onMessage(`Truncated file`) onMessage(`Truncated file`);
}) })
.catch(onMessage) .catch(onMessage);
}
function write() {
const encoder = new TextEncoder()
file
.write(encoder.encode('Hello from Tauri :)'))
.then(() => {
onMessage(`wrote to file`)
})
.catch(onMessage)
} }
function stat() { function stat() {
file file
.stat() .stat()
.then((stat) => { .then((stat) => {
onMessage(`File stat ${JSON.stringify(stat)}`) onMessage(`File stat ${JSON.stringify(stat)}`);
}) })
.catch(onMessage) .catch(onMessage);
} }
function read() { function read() {
const opts = { const opts = {
baseDir baseDir: getDir(),
} };
fs.stat(path, opts) fs.stat(path, opts)
.then((stat) => { .then((stat) => {
const isFile = stat.isFile const isFile = stat.isFile;
const promise = isFile const promise = isFile
? fs.readFile(path, opts) ? fs.readFile(path, opts)
: fs.readDir(path, opts) : fs.readDir(path, opts);
promise promise
.then(function (response) { .then(function (response) {
if (isFile) { if (isFile) {
if (path.includes('.png') || path.includes('.jpg')) { if (path.includes(".png") || path.includes(".jpg")) {
arrayBufferToBase64( arrayBufferToBase64(
new Uint8Array(response), new Uint8Array(response),
function (base64) { function (base64) {
const src = 'data:image/png;base64,' + base64 const src = "data:image/png;base64," + base64;
insecureRenderHtml('<img src="' + src + '"></img>') insecureRenderHtml('<img src="' + src + '"></img>');
} }
) );
} else { } else {
const value = String.fromCharCode.apply(null, response) const value = String.fromCharCode.apply(null, response);
insecureRenderHtml( insecureRenderHtml(
'<textarea id="file-response"></textarea><button id="file-save">Save</button>' '<textarea id="file-response"></textarea><button id="file-save">Save</button>'
) );
setTimeout(() => { setTimeout(() => {
const fileInput = document.getElementById('file-response') const fileInput = document.getElementById("file-response");
fileInput.value = value fileInput.value = value;
document document
.getElementById('file-save') .getElementById("file-save")
.addEventListener('click', function () { .addEventListener("click", function () {
fs.writeTextFile(path, fileInput.value, { fs.writeTextFile(path, fileInput.value, {
baseDir baseDir: getDir(),
}).catch(onMessage) }).catch(onMessage);
}) });
}) });
} }
} else { } else {
onMessage(response) onMessage(response);
} }
}) })
.catch(onMessage) .catch(onMessage);
}) })
.catch(onMessage) .catch(onMessage);
} }
function setSrc() { function setSrc() {
img.src = convertFileSrc(path) img.src = convertFileSrc(path);
} }
function watch() { function watch() {
unwatch() unwatch();
if (watchPath) { if (watchPath) {
onMessage(`Watching ${watchPath} for changes`) onMessage(`Watching ${watchPath} for changes`);
let options = { let options = {
recursive: watchRecursive, recursive: watchRecursive,
delayMs: watchDebounceDelay delayMs: parseInt(watchDebounceDelay),
} };
if (options.delayMs === 0) { if (options.delayMs === 0) {
fs.watchImmediate(watchPath, onMessage, options) fs.watchImmediate(watchPath, onMessage, options)
.then((fn) => { .then((fn) => {
unwatchFn = fn unwatchFn = fn;
unwatchPath = watchPath unwatchPath = watchPath;
}) })
.catch(onMessage) .catch(onMessage);
} else { } else {
fs.watch(watchPath, onMessage, options) fs.watch(watchPath, onMessage, options)
.then((fn) => { .then((fn) => {
unwatchFn = fn unwatchFn = fn;
unwatchPath = watchPath unwatchPath = watchPath;
}) })
.catch(onMessage) .catch(onMessage);
} }
} }
} }
function unwatch() { function unwatch() {
if (unwatchFn) { if (unwatchFn) {
onMessage(`Stopped watching ${unwatchPath} for changes`) onMessage(`Stopped watching ${unwatchPath} for changes`);
unwatchFn() unwatchFn();
} }
unwatchFn = undefined unwatchFn = undefined;
unwatchPath = undefined unwatchPath = undefined;
} }
onDestroy(() => { onDestroy(() => {
if (file) { if (file) {
file.close() file.close();
} }
if (unwatchFn) { if (unwatchFn) {
unwatchFn() unwatchFn();
} }
}) })
</script> </script>
<div class="flex flex-col"> <div class="flex flex-col">
{#if isMobile}
<div>
On mobile, paths outside of App* paths require the use of dialogs
regardless of Tauri's scope mechanism.
</div>
<br />
{/if}
<div class="flex gap-1"> <div class="flex gap-1">
<select class="input" bind:value={baseDir}> <select class="input" id="dir">
<option value={undefined} selected>None</option> <option value="">None</option>
{#each dirOptions as dir} {#each DirOptions as dir}
<option value={fs.BaseDirectory[dir]}>{dir}</option> <option value={dir[1]}>{dir[0]}</option>
{/each} {/each}
</select> </select>
<input <input
@@ -219,21 +199,20 @@
</div> </div>
<br /> <br />
<div> <div>
<button class="btn" onclick={open}>Open</button> <button class="btn" on:click={open}>Open</button>
<button class="btn" onclick={read}>Read</button> <button class="btn" on:click={read}>Read</button>
<button class="btn" onclick={mkdir}>Mkdir</button> <button class="btn" on:click={mkdir}>Mkdir</button>
<button class="btn" onclick={remove}>Remove</button> <button class="btn" on:click={remove}>Remove</button>
<div class="flex flex-row"> <div class="flex flex-row">
<button class="btn" onclick={rename}>Rename</button> <button class="btn" on:click={rename}>Rename</button>
<input class="input" bind:value={renameTo} placeholder="To" /> <input class="input" bind:value={renameTo} placeholder="To" />
</div> </div>
<button class="btn" type="button" onclick={setSrc}>Use as img src</button> <button class="btn" type="button" on:click={setSrc}>Use as img src</button>
</div> </div>
{#if file} {#if file}
<div> <div>
<button class="btn" onclick={write}>Write</button> <button class="btn" on:click={truncate}>Truncate</button>
<button class="btn" onclick={truncate}>Truncate</button> <button class="btn" on:click={stat}>Stat</button>
<button class="btn" onclick={stat}>Stat</button>
</div> </div>
{/if} {/if}
@@ -262,8 +241,8 @@
</div> </div>
<br /> <br />
<div> <div>
<button class="btn" onclick={watch}>Watch</button> <button class="btn" on:click={watch}>Watch</button>
<button class="btn" onclick={unwatch}>Unwatch</button> <button class="btn" on:click={unwatch}>Unwatch</button>
</div> </div>
</div> </div>
+4 -14
View File
@@ -1,21 +1,16 @@
<script> <script>
import { sendNotification } from '@tauri-apps/plugin-notification'
export let onMessage export let onMessage
let sound = ''
// send the notification directly // send the notification directly
// the backend is responsible for checking the permission // the backend is responsible for checking the permission
function _sendNotification() { function _sendNotification() {
sendNotification({ new Notification('Notification title', {
title: 'Notification title', body: 'This is the notification body'
body: 'This is the notification body',
sound: sound || null
}) })
} }
// alternatively, check the permission ourselves // alternatively, check the permission ourselves
function triggerNotification() { function sendNotification() {
if (Notification.permission === 'default') { if (Notification.permission === 'default') {
Notification.requestPermission() Notification.requestPermission()
.then(function (response) { .then(function (response) {
@@ -34,11 +29,6 @@
} }
</script> </script>
<input <button class="btn" id="notification" on:click={sendNotification}>
class="input grow"
placeholder="Notification sound..."
bind:value={sound}
/>
<button class="btn" id="notification" on:click={triggerNotification}>
Send test notification Send test notification
</button> </button>
@@ -1,54 +0,0 @@
<script>
import { onMount } from 'svelte'
import { getString, setString } from '@tauri-apps/plugin-secure-storage'
export let onMessage
let key
let value
let output
async function read(key) {
try {
output = await getString(key)
} catch (error) {
onMessage(error)
}
}
async function write(key, value) {
try {
await setString(key, value)
} catch (error) {
onMessage(error)
}
}
function reset() {
output = ''
}
</script>
<div class="flex flex-col childre:grow gap-1">
<div class="flex flex-col flex-row-md gap-4">
<div class="flex items-center gap-1">
Key:
<input class="grow input" bind:value={key} />
</div>
<div class="flex items-center gap-1">
Value:
<input class="grow input" bind:value />
</div>
<div>
<button class="btn" on:click={() => write(key, value)}>Write</button>
<button class="btn" on:click={() => read(key)}>Read</button>
<button class="btn" on:click={() => reset()}>Reset Output</button>
</div>
</div>
<div>
Output: {output}
</div>
</div>
+32 -49
View File
@@ -1,85 +1,71 @@
<script> <script>
import { appDataDir, resolve } from '@tauri-apps/api/path' import { LazyStore } from "@tauri-apps/plugin-store";
import { LazyStore } from '@tauri-apps/plugin-store' import { onMount } from "svelte";
import { onMount } from 'svelte'
let { onMessage } = $props() export let onMessage;
let key = $state() let key;
let value = $state() let value;
const storeName = 'cache.json' let store = new LazyStore("cache.json");
let cache = {};
let store = new LazyStore(storeName)
let path = $state('')
let cache = $state({})
async function refreshEntries() { async function refreshEntries() {
try { try {
const values = await store.entries() const values = await store.entries();
cache = {} cache = {};
for (const [key, value] of values) { for (const [key, value] of values) {
cache[key] = value cache[key] = value;
} }
} catch (error) { } catch (error) {
onMessage(error) onMessage(error);
} }
} }
onMount(async () => { onMount(async () => {
path = await resolve(await appDataDir(), storeName) await refreshEntries();
await refreshEntries() });
})
async function write(key, value) { async function write(key, value) {
try { try {
if (value) { if (value) {
await store.set(key, value) await store.set(key, value);
} else { } else {
await store.delete(key) await store.delete(key);
} }
const v = await store.get(key) const v = await store.get(key);
if (v === undefined) { if (v === undefined) {
delete cache[key] delete cache[key];
cache = cache cache = cache;
} else { } else {
cache[key] = v cache[key] = v;
} }
} catch (error) { } catch (error) {
onMessage(error) onMessage(error);
} }
} }
async function reset() { async function reset() {
try { try {
await store.reset() await store.reset();
} catch (error) { } catch (error) {
onMessage(error) onMessage(error);
} }
await refreshEntries() await refreshEntries();
}
async function reload() {
try {
await store.reload({ overrideDefaults: true })
} catch (error) {
onMessage(error)
}
await refreshEntries()
} }
async function close() { async function close() {
try { try {
await store.close() await store.close();
onMessage('Store is now closed, any new operations will error out') onMessage("Store is now closed, any new operations will error out");
} catch (error) { } catch (error) {
onMessage(error) onMessage(error);
} }
} }
function reopen() { function reopen() {
store = new LazyStore(storeName) store = new LazyStore("cache.json");
onMessage('We made a new `LazyStore` instance, operations will now work') onMessage("We made a new `LazyStore` instance, operations will now work");
} }
</script> </script>
@@ -96,17 +82,14 @@
</div> </div>
<div> <div>
<button class="btn" onclick={() => write(key, value)}>Write</button> <button class="btn" on:click={() => write(key, value)}>Write</button>
<button class="btn" onclick={() => reset()}>Reset</button> <button class="btn" on:click={() => reset()}>Reset</button>
<button class="btn" onclick={() => reload()}>Reload</button> <button class="btn" on:click={() => close()}>Close</button>
<button class="btn" onclick={() => close()}>Close</button> <button class="btn" on:click={() => reopen()}>Re-open</button>
<button class="btn" onclick={() => reopen()}>Re-open</button>
</div> </div>
<div>Store at <code>{path}</code> on disk</div>
</div> </div>
<div> <div>
<h2>Store Values</h2>
{#each Object.entries(cache) as [k, v]} {#each Object.entries(cache) as [k, v]}
<div>{k} = {v}</div> <div>{k} = {v}</div>
{/each} {/each}
+3 -44
View File
@@ -1,5 +1,5 @@
<script> <script>
import { download, upload, HttpMethod } from '@tauri-apps/plugin-upload' import { download, upload } from '@tauri-apps/plugin-upload'
import { open } from '@tauri-apps/plugin-dialog' import { open } from '@tauri-apps/plugin-dialog'
import { JsonView } from '@zerodevx/svelte-json-view' import { JsonView } from '@zerodevx/svelte-json-view'
import { appDataDir } from '@tauri-apps/api/path' import { appDataDir } from '@tauri-apps/api/path'
@@ -16,22 +16,6 @@
let uploadUrl = 'https://httpbin.org/post' let uploadUrl = 'https://httpbin.org/post'
let uploadFilePath = '' let uploadFilePath = ''
let uploadMethod = HttpMethod.Post
// Update URL when method changes
$: {
switch (uploadMethod) {
case HttpMethod.Post:
uploadUrl = 'https://httpbin.org/post'
break
case HttpMethod.Put:
uploadUrl = 'https://httpbin.org/put'
break
case HttpMethod.Patch:
uploadUrl = 'https://httpbin.org/patch'
break
}
}
let uploadProgress = null let uploadProgress = null
let uploadResult = null let uploadResult = null
let isUploading = false let isUploading = false
@@ -213,8 +197,7 @@
}, },
new Map([ new Map([
['User-Agent', 'Tauri Upload Plugin Demo'] ['User-Agent', 'Tauri Upload Plugin Demo']
]), ])
uploadMethod
) )
uploadResult = { uploadResult = {
@@ -357,36 +340,12 @@
</div> </div>
</div> </div>
<div>
<label for="upload-method" class="block text-sm font-medium text-gray-700 mb-1">HTTP Method:</label>
<select
id="upload-method"
bind:value={uploadMethod}
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
disabled={isUploading}
>
<option value={HttpMethod.Post}>POST</option>
<option value={HttpMethod.Put}>PUT</option>
<option value={HttpMethod.Patch}>PATCH</option>
</select>
<p class="text-xs text-gray-500 mt-1">Choose the HTTP method for the upload request</p>
</div>
<div class="bg-blue-50 border border-blue-200 p-3 rounded-md">
<div class="text-sm text-blue-800">
<strong>Upload Configuration:</strong>
<div class="font-mono text-xs mt-1">
Method: {uploadMethod} | URL: {uploadUrl || 'Not set'}
</div>
</div>
</div>
<button <button
on:click={startUpload} on:click={startUpload}
class="w-full px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 disabled:opacity-50 disabled:cursor-not-allowed" class="w-full px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 disabled:opacity-50 disabled:cursor-not-allowed"
disabled={isUploading || !uploadUrl || !uploadFilePath} disabled={isUploading || !uploadUrl || !uploadFilePath}
> >
{isUploading ? `Uploading (${uploadMethod})...` : `Upload File (${uploadMethod})`} {isUploading ? 'Uploading...' : 'Upload File'}
</button> </button>
{#if uploadProgress} {#if uploadProgress}
+8 -9
View File
@@ -11,21 +11,20 @@
"example:api:dev": "pnpm run --filter \"api\" tauri dev" "example:api:dev": "pnpm run --filter \"api\" tauri dev"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "9.39.1", "@eslint/js": "9.31.0",
"@rollup/plugin-node-resolve": "16.0.3", "@rollup/plugin-node-resolve": "16.0.1",
"@rollup/plugin-terser": "0.4.4", "@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "12.3.0", "@rollup/plugin-typescript": "12.1.4",
"covector": "^0.12.4", "covector": "^0.12.4",
"eslint": "9.39.1", "eslint": "9.31.0",
"eslint-config-prettier": "10.1.8", "eslint-config-prettier": "10.1.8",
"eslint-plugin-security": "3.0.1", "eslint-plugin-security": "3.0.1",
"prettier": "3.6.2", "prettier": "3.6.2",
"rollup": "4.53.2", "rollup": "4.45.1",
"tslib": "2.8.1", "tslib": "2.8.1",
"typescript": "5.9.3", "typescript": "5.8.3",
"typescript-eslint": "8.47.0" "typescript-eslint": "8.38.0"
}, },
"minimumReleaseAge": 4320,
"pnpm": { "pnpm": {
"overrides": { "overrides": {
"esbuild@<0.25.0": ">=0.25.0" "esbuild@<0.25.0": ">=0.25.0"
@@ -35,6 +34,6 @@
] ]
}, },
"engines": { "engines": {
"pnpm": "^10.16.0" "pnpm": "^10.0.0"
} }
} }
-4
View File
@@ -1,9 +1,5 @@
# Changelog # Changelog
## \[2.5.1]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.5.0] ## \[2.5.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+5 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-autostart" name = "tauri-plugin-autostart"
version = "2.5.1" version = "2.5.0"
description = "Automatically launch your application at startup." description = "Automatically launch your application at startup."
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
@@ -9,6 +9,10 @@ rust-version = { workspace = true }
repository = { workspace = true } repository = { workspace = true }
links = "tauri-plugin-autostart" links = "tauri-plugin-autostart"
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.platforms.support] [package.metadata.platforms.support]
windows = { level = "full", notes = "" } windows = { level = "full", notes = "" }
linux = { level = "full", notes = "" } linux = { level = "full", notes = "" }
+9
View File
@@ -33,12 +33,21 @@ tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspac
You can install the JavaScript Guest bindings using your preferred JavaScript package manager: You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh ```sh
pnpm add @tauri-apps/plugin-autostart pnpm add @tauri-apps/plugin-autostart
# or # or
npm add @tauri-apps/plugin-autostart npm add @tauri-apps/plugin-autostart
# or # or
yarn add @tauri-apps/plugin-autostart yarn add @tauri-apps/plugin-autostart
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-autostart#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-autostart#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-autostart#v2
``` ```
## Usage ## Usage
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-autostart", "name": "@tauri-apps/plugin-autostart",
"version": "2.5.1", "version": "2.5.0",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.8.0" "@tauri-apps/api": "^2.6.0"
} }
} }
@@ -331,10 +331,10 @@
"markdownDescription": "Denies the is_enabled command without any pre-configured scope." "markdownDescription": "Denies the is_enabled command without any pre-configured scope."
}, },
{ {
"description": "This permission set configures if your\r\napplication can enable or disable auto\r\nstarting the application on boot.\r\n\r\n#### Granted Permissions\r\n\r\nIt allows all to check, enable and\r\ndisable the automatic start on boot.\r\n\r\n\n#### This default permission set includes:\n\n- `allow-enable`\n- `allow-disable`\n- `allow-is-enabled`", "description": "This permission set configures if your\napplication can enable or disable auto\nstarting the application on boot.\n\n#### Granted Permissions\n\nIt allows all to check, enable and\ndisable the automatic start on boot.\n\n\n#### This default permission set includes:\n\n- `allow-enable`\n- `allow-disable`\n- `allow-is-enabled`",
"type": "string", "type": "string",
"const": "default", "const": "default",
"markdownDescription": "This permission set configures if your\r\napplication can enable or disable auto\r\nstarting the application on boot.\r\n\r\n#### Granted Permissions\r\n\r\nIt allows all to check, enable and\r\ndisable the automatic start on boot.\r\n\r\n\n#### This default permission set includes:\n\n- `allow-enable`\n- `allow-disable`\n- `allow-is-enabled`" "markdownDescription": "This permission set configures if your\napplication can enable or disable auto\nstarting the application on boot.\n\n#### Granted Permissions\n\nIt allows all to check, enable and\ndisable the automatic start on boot.\n\n\n#### This default permission set includes:\n\n- `allow-enable`\n- `allow-disable`\n- `allow-is-enabled`"
} }
] ]
} }
-9
View File
@@ -1,14 +1,5 @@
# Changelog # Changelog
## \[2.4.2]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.4.1]
- [`654bf489`](https://github.com/tauri-apps/plugins-workspace/commit/654bf4891a35769f7e82971641d3ad99974b2dfe) ([#3038](https://github.com/tauri-apps/plugins-workspace/pull/3038) by [@daniel-mader](https://github.com/tauri-apps/plugins-workspace/../../daniel-mader)) Update `androidx.camera` from `1.1.0` to `1.5.1` to support 16 KB memory page sizes.
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
## \[2.4.0] ## \[2.4.0]
- [`aa9140e1`](https://github.com/tauri-apps/plugins-workspace/commit/aa9140e1ac239ab9f015f92b2ed52bbf0eda7c12) ([#2437](https://github.com/tauri-apps/plugins-workspace/pull/2437) by [@enkhjile](https://github.com/tauri-apps/plugins-workspace/../../enkhjile)) Added support for GS1 DataBar on iOS 15.4+ - [`aa9140e1`](https://github.com/tauri-apps/plugins-workspace/commit/aa9140e1ac239ab9f015f92b2ed52bbf0eda7c12) ([#2437](https://github.com/tauri-apps/plugins-workspace/pull/2437) by [@enkhjile](https://github.com/tauri-apps/plugins-workspace/../../enkhjile)) Added support for GS1 DataBar on iOS 15.4+
+3 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-barcode-scanner" name = "tauri-plugin-barcode-scanner"
version = "2.4.2" version = "2.4.0"
description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS" description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -10,6 +10,8 @@ repository = { workspace = true }
links = "tauri-plugin-barcode-scanner" links = "tauri-plugin-barcode-scanner"
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-linux-android"] targets = ["x86_64-linux-android"]
[package.metadata.platforms.support] [package.metadata.platforms.support]
+9
View File
@@ -33,12 +33,21 @@ tauri-plugin-barcode-scanner = { git = "https://github.com/tauri-apps/plugins-wo
You can install the JavaScript Guest bindings using your preferred JavaScript package manager: You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh ```sh
pnpm add @tauri-apps/plugin-barcode-scanner pnpm add @tauri-apps/plugin-barcode-scanner
# or # or
npm add @tauri-apps/plugin-barcode-scanner npm add @tauri-apps/plugin-barcode-scanner
# or # or
yarn add @tauri-apps/plugin-barcode-scanner yarn add @tauri-apps/plugin-barcode-scanner
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
``` ```
## Usage ## Usage
@@ -5,7 +5,7 @@ plugins {
android { android {
namespace = "app.tauri.barcodescanner" namespace = "app.tauri.barcodescanner"
compileSdk = 36 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24
@@ -33,14 +33,16 @@ android {
} }
dependencies { dependencies {
val camerax_version = "1.5.1"
implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.0") implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0") implementation("com.google.android.material:material:1.7.0")
implementation("androidx.camera:camera-core:${camerax_version}") implementation("androidx.camera:camera-core:1.1.0")
implementation("androidx.camera:camera-camera2:${camerax_version}") implementation("androidx.camera:camera-view:1.1.0")
implementation("androidx.camera:camera-lifecycle:${camerax_version}") implementation("androidx.camera:camera-lifecycle:1.1.0")
implementation("androidx.camera:camera-view:${camerax_version}") implementation("androidx.camera:camera-camera2:1.1.0")
implementation("androidx.camera:camera-lifecycle:1.1.0")
implementation("androidx.camera:camera-view:1.1.0")
implementation("com.google.android.gms:play-services-mlkit-barcode-scanning:18.1.0") implementation("com.google.android.gms:play-services-mlkit-barcode-scanning:18.1.0")
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.ext:junit:1.1.5")
@@ -191,7 +191,6 @@ class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
if self.captureSession != nil { if self.captureSession != nil {
self.captureSession!.stopRunning() self.captureSession!.stopRunning()
self.cameraView.removePreviewLayer() self.cameraView.removePreviewLayer()
self.cameraView.removeFromSuperview()
self.captureVideoPreviewLayer = nil self.captureVideoPreviewLayer = nil
self.metaOutput = nil self.metaOutput = nil
self.captureSession = nil self.captureSession = nil
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-barcode-scanner", "name": "@tauri-apps/plugin-barcode-scanner",
"version": "2.4.2", "version": "2.4.0",
"description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS", "description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
@@ -25,6 +25,6 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.8.0" "@tauri-apps/api": "^2.6.0"
} }
} }
-8
View File
@@ -1,13 +1,5 @@
# Changelog # Changelog
## \[2.3.2]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.3.1]
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
## \[2.3.0] ## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+3 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-biometric" name = "tauri-plugin-biometric"
version = "2.3.2" version = "2.3.0"
description = "Prompt the user for biometric authentication on Android and iOS." description = "Prompt the user for biometric authentication on Android and iOS."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -9,6 +9,8 @@ repository = { workspace = true }
links = "tauri-plugin-biometric" links = "tauri-plugin-biometric"
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-linux-android"] targets = ["x86_64-linux-android"]
[package.metadata.platforms.support] [package.metadata.platforms.support]
+9
View File
@@ -33,6 +33,8 @@ tauri-plugin-biometric = { git = "https://github.com/tauri-apps/plugins-workspac
You can install the JavaScript Guest bindings using your preferred JavaScript package manager: You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
<!-- Add the branch for installations using git! --> <!-- Add the branch for installations using git! -->
```sh ```sh
@@ -41,6 +43,13 @@ pnpm add @tauri-apps/plugin-biometric
npm add @tauri-apps/plugin-biometric npm add @tauri-apps/plugin-biometric
# or # or
yarn add @tauri-apps/plugin-biometric yarn add @tauri-apps/plugin-biometric
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-biometric#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-biometric#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-biometric#v2
``` ```
## Usage ## Usage
+1 -1
View File
@@ -5,7 +5,7 @@ plugins {
android { android {
namespace = "app.tauri.biometric" namespace = "app.tauri.biometric"
compileSdk = 36 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-biometric", "name": "@tauri-apps/plugin-biometric",
"version": "2.3.2", "version": "2.3.0",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"
@@ -25,6 +25,6 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.8.0" "@tauri-apps/api": "^2.6.0"
} }
} }
@@ -319,10 +319,10 @@
"markdownDescription": "Denies the status command without any pre-configured scope." "markdownDescription": "Denies the status command without any pre-configured scope."
}, },
{ {
"description": "This permission set configures which\r\nbiometric features are by default exposed.\r\n\r\n#### Granted Permissions\r\n\r\nIt allows acccess to all biometric commands.\r\n\r\n\n#### This default permission set includes:\n\n- `allow-authenticate`\n- `allow-status`", "description": "This permission set configures which\nbiometric features are by default exposed.\n\n#### Granted Permissions\n\nIt allows acccess to all biometric commands.\n\n\n#### This default permission set includes:\n\n- `allow-authenticate`\n- `allow-status`",
"type": "string", "type": "string",
"const": "default", "const": "default",
"markdownDescription": "This permission set configures which\r\nbiometric features are by default exposed.\r\n\r\n#### Granted Permissions\r\n\r\nIt allows acccess to all biometric commands.\r\n\r\n\n#### This default permission set includes:\n\n- `allow-authenticate`\n- `allow-status`" "markdownDescription": "This permission set configures which\nbiometric features are by default exposed.\n\n#### Granted Permissions\n\nIt allows acccess to all biometric commands.\n\n\n#### This default permission set includes:\n\n- `allow-authenticate`\n- `allow-status`"
} }
] ]
} }
-4
View File
@@ -1,9 +1,5 @@
# Changelog # Changelog
## \[2.4.1]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.4.0] ## \[2.4.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+5 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-cli" name = "tauri-plugin-cli"
version = "2.4.1" version = "2.4.0"
description = "Parse arguments from your Tauri application's command line interface." description = "Parse arguments from your Tauri application's command line interface."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -9,6 +9,10 @@ rust-version = { workspace = true }
repository = { workspace = true } repository = { workspace = true }
links = "tauri-plugin-cli" links = "tauri-plugin-cli"
[package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.platforms.support] [package.metadata.platforms.support]
windows = { level = "full", notes = "" } windows = { level = "full", notes = "" }
linux = { level = "full", notes = "" } linux = { level = "full", notes = "" }
+9
View File
@@ -34,12 +34,21 @@ tauri-plugin-cli = { git = "https://github.com/tauri-apps/plugins-workspace", br
You can install the JavaScript Guest bindings using your preferred JavaScript package manager: You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh ```sh
pnpm add @tauri-apps/plugin-cli pnpm add @tauri-apps/plugin-cli
# or # or
npm add @tauri-apps/plugin-cli npm add @tauri-apps/plugin-cli
# or # or
yarn add @tauri-apps/plugin-cli yarn add @tauri-apps/plugin-cli
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-cli#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-cli#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-cli#v2
``` ```
## Usage ## Usage
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-cli", "name": "@tauri-apps/plugin-cli",
"version": "2.4.1", "version": "2.4.0",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.8.0" "@tauri-apps/api": "^2.6.0"
} }
} }
-8
View File
@@ -1,13 +1,5 @@
# Changelog # Changelog
## \[2.3.2]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.3.1]
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
## \[2.3.0] ## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6 - [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+3 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-clipboard-manager" name = "tauri-plugin-clipboard-manager"
version = "2.3.2" version = "2.3.0"
description = "Read and write to the system clipboard." description = "Read and write to the system clipboard."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -10,7 +10,8 @@ repository = { workspace = true }
links = "tauri-plugin-clipboard-manager" links = "tauri-plugin-clipboard-manager"
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-unknown-linux-gnu", "x86_64-linux-android"] targets = ["x86_64-unknown-linux-gnu", "x86_64-linux-android"]
[package.metadata.platforms.support] [package.metadata.platforms.support]
+9
View File
@@ -33,12 +33,21 @@ tauri-plugin-clipboard-manager = { git = "https://github.com/tauri-apps/plugins-
You can install the JavaScript Guest bindings using your preferred JavaScript package manager: You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh ```sh
pnpm add @tauri-apps/plugin-clipboard-manager pnpm add @tauri-apps/plugin-clipboard-manager
# or # or
npm add @tauri-apps/plugin-clipboard-manager npm add @tauri-apps/plugin-clipboard-manager
# or # or
yarn add @tauri-apps/plugin-clipboard-manager yarn add @tauri-apps/plugin-clipboard-manager
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-clipboard-manager#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-clipboard-manager#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-clipboard-manager#v2
``` ```
## Usage ## Usage
@@ -5,7 +5,7 @@ plugins {
android { android {
namespace = "app.tauri.clipboard" namespace = "app.tauri.clipboard"
compileSdk = 36 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-clipboard-manager", "name": "@tauri-apps/plugin-clipboard-manager",
"version": "2.3.2", "version": "2.3.0",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.8.0" "@tauri-apps/api": "^2.6.0"
} }
} }
@@ -367,10 +367,10 @@
"markdownDescription": "Denies the write_text command without any pre-configured scope." "markdownDescription": "Denies the write_text command without any pre-configured scope."
}, },
{ {
"description": "No features are enabled by default, as we believe\r\nthe clipboard can be inherently dangerous and it is\r\napplication specific if read and/or write access is needed.\r\n\r\nClipboard interaction needs to be explicitly enabled.\r\n", "description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n",
"type": "string", "type": "string",
"const": "default", "const": "default",
"markdownDescription": "No features are enabled by default, as we believe\r\nthe clipboard can be inherently dangerous and it is\r\napplication specific if read and/or write access is needed.\r\n\r\nClipboard interaction needs to be explicitly enabled.\r\n" "markdownDescription": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n"
} }
] ]
} }
-17
View File
@@ -1,22 +1,5 @@
# Changelog # Changelog
## \[2.4.5]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
## \[2.4.4]
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
- [`e25a9339`](https://github.com/tauri-apps/plugins-workspace/commit/e25a9339f46268f70dc9afd0c5ab3decbf79b330) ([#3019](https://github.com/tauri-apps/plugins-workspace/pull/3019) by [@kevinschoonover](https://github.com/tauri-apps/plugins-workspace/../../kevinschoonover)) Fix Exec= field in desktop handler if executable path changes
## \[2.4.3]
- [`2522b71f`](https://github.com/tauri-apps/plugins-workspace/commit/2522b71f6bcae65c03b24415eb9295c9e7c84ffc) ([#2970](https://github.com/tauri-apps/plugins-workspace/pull/2970) by [@WSH032](https://github.com/tauri-apps/plugins-workspace/../../WSH032)) Revert the breaking change introduced by [#2928](https://github.com/tauri-apps/plugins-workspace/pull/2928).
## \[2.4.2]
- [`21d721a0`](https://github.com/tauri-apps/plugins-workspace/commit/21d721a0c2731fc201872f5b99ea8bbdc61b0b60) ([#2928](https://github.com/tauri-apps/plugins-workspace/pull/2928) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Linux, improved error messages when OS commands fail.
## \[2.4.1] ## \[2.4.1]
- [`d4f8299b`](https://github.com/tauri-apps/plugins-workspace/commit/d4f8299b12f107718c70692840a63768d65baaf9) ([#2844](https://github.com/tauri-apps/plugins-workspace/pull/2844) by [@yobson1](https://github.com/tauri-apps/plugins-workspace/../../yobson1)) Fix deep link protocol handler not set as default on linux - [`d4f8299b`](https://github.com/tauri-apps/plugins-workspace/commit/d4f8299b12f107718c70692840a63768d65baaf9) ([#2844](https://github.com/tauri-apps/plugins-workspace/pull/2844) by [@yobson1](https://github.com/tauri-apps/plugins-workspace/../../yobson1)) Fix deep link protocol handler not set as default on linux
+3 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-deep-link" name = "tauri-plugin-deep-link"
version = "2.4.5" version = "2.4.1"
description = "Set your Tauri application as the default handler for an URL" description = "Set your Tauri application as the default handler for an URL"
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
@@ -10,6 +10,8 @@ repository = { workspace = true }
links = "tauri-plugin-deep-link" links = "tauri-plugin-deep-link"
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-linux-android"] targets = ["x86_64-linux-android"]
[package.metadata.platforms.support] [package.metadata.platforms.support]
@@ -25,9 +27,6 @@ serde_json = { workspace = true }
tauri-utils = { workspace = true } tauri-utils = { workspace = true }
tauri-plugin = { workspace = true, features = ["build"] } tauri-plugin = { workspace = true, features = ["build"] }
[target."cfg(target_os = \"macos\")".build-dependencies]
plist = "1"
[dependencies] [dependencies]
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
+9
View File
@@ -33,12 +33,21 @@ tauri-plugin-deep-link = { git = "https://github.com/tauri-apps/plugins-workspac
You can install the JavaScript Guest bindings using your preferred JavaScript package manager: You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh ```sh
pnpm add @tauri-apps/plugin-deep-link pnpm add @tauri-apps/plugin-deep-link
# or # or
npm add @tauri-apps/plugin-deep-link npm add @tauri-apps/plugin-deep-link
# or # or
yarn add @tauri-apps/plugin-deep-link yarn add @tauri-apps/plugin-deep-link
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-deep-link#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-deep-link#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-deep-link#v2
``` ```
## Setting up ## Setting up
+1 -1
View File
@@ -5,7 +5,7 @@ plugins {
android { android {
namespace = "app.tauri.deep_link" namespace = "app.tauri.deep_link"
compileSdk = 36 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24
+16 -109
View File
@@ -10,64 +10,50 @@ const COMMANDS: &[&str] = &["get_current", "register", "unregister", "is_registe
// TODO: Consider using activity-alias in case users may have multiple activities in their app. // TODO: Consider using activity-alias in case users may have multiple activities in their app.
fn intent_filter(domain: &AssociatedDomain) -> String { fn intent_filter(domain: &AssociatedDomain) -> String {
let host = domain
.host
.as_ref()
.map(|h| format!(r#"<data android:host="{h}" />"#))
.unwrap_or_default();
let auto_verify = if domain.is_app_link() {
r#"android:autoVerify="true" "#.to_string()
} else {
String::new()
};
format!( format!(
r#"<intent-filter {auto_verify}> r#"<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
{schemes} {}
{host} <data android:host="{}" />
{domains} {}
{path_patterns} {}
{path_prefixes} {}
{path_suffixes} {}
</intent-filter>"#, </intent-filter>"#,
schemes = domain domain
.scheme .scheme
.iter() .iter()
.map(|scheme| format!(r#"<data android:scheme="{scheme}" />"#)) .map(|scheme| format!(r#"<data android:scheme="{scheme}" />"#))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n "), .join("\n "),
host = host, domain.host,
domains = domain domain
.path .path
.iter() .iter()
.map(|path| format!(r#"<data android:path="{path}" />"#)) .map(|path| format!(r#"<data android:path="{path}" />"#))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n "), .join("\n "),
path_patterns = domain domain
.path_pattern .path_pattern
.iter() .iter()
.map(|pattern| format!(r#"<data android:pathPattern="{pattern}" />"#)) .map(|pattern| format!(r#"<data android:pathPattern="{pattern}" />"#))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n "), .join("\n "),
path_prefixes = domain domain
.path_prefix .path_prefix
.iter() .iter()
.map(|prefix| format!(r#"<data android:pathPrefix="{prefix}" />"#)) .map(|prefix| format!(r#"<data android:pathPrefix="{prefix}" />"#))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n "), .join("\n "),
path_suffixes = domain domain
.path_suffix .path_suffix
.iter() .iter()
.map(|suffix| format!(r#"<data android:pathSuffix="{suffix}" />"#)) .map(|suffix| format!(r#"<data android:pathSuffix="{suffix}" />"#))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n "), .join("\n "),
) )
.trim()
.to_string()
} }
fn main() { fn main() {
@@ -82,16 +68,6 @@ fn main() {
} }
if let Some(config) = tauri_plugin::plugin_config::<Config>("deep-link") { if let Some(config) = tauri_plugin::plugin_config::<Config>("deep-link") {
let errors: Vec<String> = config
.mobile
.iter()
.filter_map(|d| d.validate().err())
.collect();
if !errors.is_empty() {
panic!("Deep link config validation failed:\n{}", errors.join("\n"));
}
tauri_plugin::mobile::update_android_manifest( tauri_plugin::mobile::update_android_manifest(
"DEEP LINK PLUGIN", "DEEP LINK PLUGIN",
"activity", "activity",
@@ -104,89 +80,20 @@ fn main() {
) )
.expect("failed to rewrite AndroidManifest.xml"); .expect("failed to rewrite AndroidManifest.xml");
#[cfg(any(target_os = "macos", target_os = "ios"))] #[cfg(target_os = "macos")]
{ {
// we need to ensure that the entitlements are only
// generated for explicit app links and not
// other deep links because then they
// are just going to complain and not be built or signed
let has_app_links = config.mobile.iter().any(|d| d.is_app_link());
if !has_app_links {
tauri_plugin::mobile::update_entitlements(|entitlements| {
entitlements.remove("com.apple.developer.associated-domains");
})
.expect("failed to update entitlements");
} else {
tauri_plugin::mobile::update_entitlements(|entitlements| { tauri_plugin::mobile::update_entitlements(|entitlements| {
entitlements.insert( entitlements.insert(
"com.apple.developer.associated-domains".into(), "com.apple.developer.associated-domains".into(),
config config
.mobile .mobile
.iter() .into_iter()
.filter(|d| d.is_app_link()) .map(|d| format!("applinks:{}", d.host).into())
.filter_map(|d| d.host.as_ref())
.map(|host| format!("applinks:{}", host).into())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into(), .into(),
); );
}) })
.expect("failed to update entitlements"); .expect("failed to update entitlements");
} }
let deep_link_domains = config
.mobile
.iter()
.filter_map(|domain| {
if domain.is_app_link() {
return None;
}
Some(domain)
})
.collect::<Vec<_>>();
if deep_link_domains.is_empty() {
tauri_plugin::mobile::update_info_plist(|info_plist| {
info_plist.remove("CFBundleURLTypes");
})
.expect("failed to update Info.plist");
} else {
tauri_plugin::mobile::update_info_plist(|info_plist| {
info_plist.insert(
"CFBundleURLTypes".into(),
deep_link_domains
.iter()
.map(|domain| {
let schemes = domain
.scheme
.iter()
.filter(|scheme| {
scheme.as_str() != "https" && scheme.as_str() != "http"
})
.collect::<Vec<_>>();
let mut dict = plist::Dictionary::new();
dict.insert(
"CFBundleURLSchemes".into(),
schemes
.iter()
.map(|s| s.to_string().into())
.collect::<Vec<_>>()
.into(),
);
dict.insert(
"CFBundleURLName".into(),
format!("{}", domain.scheme[0]).into(),
);
plist::Value::Dictionary(dict)
})
.collect::<Vec<_>>()
.into(),
);
})
.expect("failed to update Info.plist");
}
}
} }
} }
@@ -1,29 +1,5 @@
# Changelog # Changelog
## \[2.2.8]
### Dependencies
- Upgraded to `deep-link-js@2.4.5`
## \[2.2.7]
### Dependencies
- Upgraded to `deep-link-js@2.4.4`
## \[2.2.6]
### Dependencies
- Upgraded to `deep-link-js@2.4.3`
## \[2.2.5]
### Dependencies
- Upgraded to `deep-link-js@2.4.2`
## \[2.2.4] ## \[2.2.4]
### Dependencies ### Dependencies
+5 -5
View File
@@ -1,7 +1,7 @@
{ {
"name": "deep-link-example", "name": "deep-link-example",
"private": true, "private": true,
"version": "2.2.8", "version": "2.2.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -10,12 +10,12 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "2.9.0", "@tauri-apps/api": "2.7.0",
"@tauri-apps/plugin-deep-link": "2.4.5" "@tauri-apps/plugin-deep-link": "2.4.1"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.9.4", "@tauri-apps/cli": "2.7.1",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"vite": "^7.0.7" "vite": "^7.0.4"
} }
} }
@@ -14,13 +14,13 @@ val tauriProperties = Properties().apply {
} }
android { android {
compileSdk = 36 compileSdk = 34
namespace = "com.tauri.deep_link_example" namespace = "com.tauri.deep_link_example"
defaultConfig { defaultConfig {
manifestPlaceholders["usesCleartextTraffic"] = "false" manifestPlaceholders["usesCleartextTraffic"] = "false"
applicationId = "com.tauri.deep_link_example" applicationId = "com.tauri.deep_link_example"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 34
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
} }
@@ -58,10 +58,9 @@ rust {
} }
dependencies { dependencies {
implementation("androidx.webkit:webkit:1.14.0") implementation("androidx.webkit:webkit:1.6.1")
implementation("androidx.appcompat:appcompat:1.7.1") implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.activity:activity-ktx:1.10.1") implementation("com.google.android.material:material:1.8.0")
implementation("com.google.android.material:material:1.12.0")
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.4") androidTestImplementation("androidx.test.ext:junit:1.1.4")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
@@ -23,40 +23,23 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter> </intent-filter>
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. --> <!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
<intent-filter android:autoVerify="true" > <intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:scheme="http" /> <data android:scheme="http" />
<data android:scheme="https" />
<data android:host="fabianlars.de" /> <data android:host="fabianlars.de" />
<data android:pathPrefix="/intent" /> <data android:pathPrefix="/intent" />
</intent-filter> </intent-filter>
<intent-filter android:autoVerify="true" > <intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:scheme="http" /> <data android:scheme="http" />
<data android:scheme="https" />
<data android:host="tauri.app" /> <data android:host="tauri.app" />
</intent-filter>
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="taurideeplink" />
</intent-filter> </intent-filter>
<!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. --> <!-- DEEP LINK PLUGIN. AUTO-GENERATED. DO NOT REMOVE. -->
</activity> </activity>
@@ -1,11 +1,7 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package com.tauri.deep_link_example package com.tauri.deep_link_example
import android.os.Bundle class MainActivity : TauriActivity()
import androidx.activity.enableEdgeToEdge
class MainActivity : TauriActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
}
}
@@ -4,7 +4,7 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath("com.android.tools.build:gradle:8.11.0") classpath("com.android.tools.build:gradle:8.5.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25")
} }
} }
@@ -18,6 +18,6 @@ repositories {
dependencies { dependencies {
compileOnly(gradleApi()) compileOnly(gradleApi())
implementation("com.android.tools.build:gradle:8.11.0") implementation("com.android.tools.build:gradle:8.5.1")
} }
@@ -1,6 +1,6 @@
#Tue May 10 19:22:52 CST 2022 #Tue May 10 19:22:52 CST 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
@@ -1,116 +1,116 @@
{ {
"images" : [ "images": [
{ {
"size" : "20x20", "size": "20x20",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "AppIcon-20x20@2x.png", "filename": "AppIcon-20x20@2x.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "20x20", "size": "20x20",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "AppIcon-20x20@3x.png", "filename": "AppIcon-20x20@3x.png",
"scale" : "3x" "scale": "3x"
}, },
{ {
"size" : "29x29", "size": "29x29",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "AppIcon-29x29@2x-1.png", "filename": "AppIcon-29x29@2x-1.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "29x29", "size": "29x29",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "AppIcon-29x29@3x.png", "filename": "AppIcon-29x29@3x.png",
"scale" : "3x" "scale": "3x"
}, },
{ {
"size" : "40x40", "size": "40x40",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "AppIcon-40x40@2x.png", "filename": "AppIcon-40x40@2x.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "40x40", "size": "40x40",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "AppIcon-40x40@3x.png", "filename": "AppIcon-40x40@3x.png",
"scale" : "3x" "scale": "3x"
}, },
{ {
"size" : "60x60", "size": "60x60",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "AppIcon-60x60@2x.png", "filename": "AppIcon-60x60@2x.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "60x60", "size": "60x60",
"idiom" : "iphone", "idiom": "iphone",
"filename" : "AppIcon-60x60@3x.png", "filename": "AppIcon-60x60@3x.png",
"scale" : "3x" "scale": "3x"
}, },
{ {
"size" : "20x20", "size": "20x20",
"idiom" : "ipad", "idiom": "ipad",
"filename" : "AppIcon-20x20@1x.png", "filename": "AppIcon-20x20@1x.png",
"scale" : "1x" "scale": "1x"
}, },
{ {
"size" : "20x20", "size": "20x20",
"idiom" : "ipad", "idiom": "ipad",
"filename" : "AppIcon-20x20@2x-1.png", "filename": "AppIcon-20x20@2x-1.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "29x29", "size": "29x29",
"idiom" : "ipad", "idiom": "ipad",
"filename" : "AppIcon-29x29@1x.png", "filename": "AppIcon-29x29@1x.png",
"scale" : "1x" "scale": "1x"
}, },
{ {
"size" : "29x29", "size": "29x29",
"idiom" : "ipad", "idiom": "ipad",
"filename" : "AppIcon-29x29@2x.png", "filename": "AppIcon-29x29@2x.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "40x40", "size": "40x40",
"idiom" : "ipad", "idiom": "ipad",
"filename" : "AppIcon-40x40@1x.png", "filename": "AppIcon-40x40@1x.png",
"scale" : "1x" "scale": "1x"
}, },
{ {
"size" : "40x40", "size": "40x40",
"idiom" : "ipad", "idiom": "ipad",
"filename" : "AppIcon-40x40@2x-1.png", "filename": "AppIcon-40x40@2x-1.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "76x76", "size": "76x76",
"idiom" : "ipad", "idiom": "ipad",
"filename" : "AppIcon-76x76@1x.png", "filename": "AppIcon-76x76@1x.png",
"scale" : "1x" "scale": "1x"
}, },
{ {
"size" : "76x76", "size": "76x76",
"idiom" : "ipad", "idiom": "ipad",
"filename" : "AppIcon-76x76@2x.png", "filename": "AppIcon-76x76@2x.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "83.5x83.5", "size": "83.5x83.5",
"idiom" : "ipad", "idiom": "ipad",
"filename" : "AppIcon-83.5x83.5@2x.png", "filename": "AppIcon-83.5x83.5@2x.png",
"scale" : "2x" "scale": "2x"
}, },
{ {
"size" : "1024x1024", "size": "1024x1024",
"idiom" : "ios-marketing", "idiom": "ios-marketing",
"filename" : "AppIcon-512@2x.png", "filename": "AppIcon-512@2x.png",
"scale" : "1x" "scale": "1x"
} }
], ],
"info" : { "info": {
"version" : 1, "version": 1,
"author" : "xcode" "author": "xcode"
} }
} }
@@ -1,6 +1,6 @@
{ {
"info" : { "info": {
"version" : 1, "version": 1,
"author" : "xcode" "author": "xcode"
} }
} }
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17150" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17150" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17122"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17122"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
@@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 63; objectVersion = 56;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@@ -167,8 +167,6 @@
dependencies = ( dependencies = (
); );
name = "deep-link-example_iOS"; name = "deep-link-example_iOS";
packageProductDependencies = (
);
productName = "deep-link-example_iOS"; productName = "deep-link-example_iOS";
productReference = 1CAAFA750FD735A285DC1238 /* deep-link-example.app */; productReference = 1CAAFA750FD735A285DC1238 /* deep-link-example.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@@ -191,7 +189,6 @@
en, en,
); );
mainGroup = 1DC58B1705AA3ECC6B887FE7; mainGroup = 1DC58B1705AA3ECC6B887FE7;
minimizedProjectReferenceProxies = 1;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
targets = ( targets = (
@@ -230,6 +227,7 @@
outputPaths = ( outputPaths = (
"$(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a", "$(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a",
"$(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a", "$(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a",
"$(SRCROOT)/Externals/arm64-sim/${CONFIGURATION}/libapp.a",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
@@ -316,13 +314,18 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ARCHS = arm64; ARCHS = (
arm64,
"arm64-sim",
);
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "deep-link-example_iOS/deep-link-example_iOS.entitlements"; CODE_SIGN_ENTITLEMENTS = "deep-link-example_iOS/deep-link-example_iOS.entitlements";
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
DEVELOPMENT_TEAM = "Q93MBH6S2F"; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = Q93MBH6S2F;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphoneos*]" = x86_64; "EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\".\"", "\".\"",
@@ -332,6 +335,13 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
"LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = (
"$(inherited)",
"$(PROJECT_DIR)/Externals/arm64-sim/$(CONFIGURATION)",
"$(SDKROOT)/usr/lib/swift",
"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)",
"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)",
);
"LIBRARY_SEARCH_PATHS[arch=arm64]" = ( "LIBRARY_SEARCH_PATHS[arch=arm64]" = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION)", "$(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION)",
@@ -350,7 +360,7 @@
PRODUCT_NAME = "deep-link-example"; PRODUCT_NAME = "deep-link-example";
SDKROOT = iphoneos; SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = arm64; VALID_ARCHS = "arm64 arm64-sim";
}; };
name = debug; name = debug;
}; };
@@ -414,13 +424,18 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ARCHS = arm64; ARCHS = (
arm64,
"arm64-sim",
);
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "deep-link-example_iOS/deep-link-example_iOS.entitlements"; CODE_SIGN_ENTITLEMENTS = "deep-link-example_iOS/deep-link-example_iOS.entitlements";
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
DEVELOPMENT_TEAM = "Q93MBH6S2F"; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = Q93MBH6S2F;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphoneos*]" = x86_64; "EXCLUDED_ARCHS[sdk=iphoneos*]" = "arm64-sim x86_64";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\".\"", "\".\"",
@@ -430,6 +445,13 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
"LIBRARY_SEARCH_PATHS[arch=arm64-sim]" = (
"$(inherited)",
"$(PROJECT_DIR)/Externals/arm64-sim/$(CONFIGURATION)",
"$(SDKROOT)/usr/lib/swift",
"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)",
"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)",
);
"LIBRARY_SEARCH_PATHS[arch=arm64]" = ( "LIBRARY_SEARCH_PATHS[arch=arm64]" = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION)", "$(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION)",
@@ -448,7 +470,7 @@
PRODUCT_NAME = "deep-link-example"; PRODUCT_NAME = "deep-link-example";
SDKROOT = iphoneos; SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = arm64; VALID_ARCHS = "arm64 arm64-sim";
}; };
name = release; name = release;
}; };
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.1.0</string> <string>0.0.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>0.1.0</string> <string>0.1.0</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
@@ -40,16 +40,5 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>taurideeplink</string>
</array>
<key>CFBundleURLName</key>
<string>taurideeplink</string>
</dict>
</array>
</dict> </dict>
</plist> </plist>
@@ -1,3 +1,7 @@
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
name: deep-link-example name: deep-link-example
options: options:
bundleIdPrefix: com.tauri.deep-link-example bundleIdPrefix: com.tauri.deep-link-example
@@ -12,6 +16,7 @@ settingGroups:
base: base:
PRODUCT_NAME: deep-link-example PRODUCT_NAME: deep-link-example
PRODUCT_BUNDLE_IDENTIFIER: com.tauri.deep-link-example PRODUCT_BUNDLE_IDENTIFIER: com.tauri.deep-link-example
DEVELOPMENT_TEAM: Q93MBH6S2F
targetTemplates: targetTemplates:
app: app:
type: application type: application
@@ -51,8 +56,8 @@ targets:
- UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationLandscapeRight
CFBundleShortVersionString: 0.1.0 CFBundleShortVersionString: 0.0.0
CFBundleVersion: '0.1.0' CFBundleVersion: 0.0.0
entitlements: entitlements:
path: deep-link-example_iOS/deep-link-example_iOS.entitlements path: deep-link-example_iOS/deep-link-example_iOS.entitlements
scheme: scheme:
@@ -62,12 +67,14 @@ targets:
settings: settings:
base: base:
ENABLE_BITCODE: false ENABLE_BITCODE: false
ARCHS: [arm64] ARCHS: [arm64, arm64-sim]
VALID_ARCHS: arm64 VALID_ARCHS: arm64 arm64-sim
LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
LIBRARY_SEARCH_PATHS[arch=arm64-sim]: $(inherited) $(PROJECT_DIR)/Externals/arm64-sim/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true
EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64 EXCLUDED_ARCHS[sdk=iphonesimulator*]: arm64
EXCLUDED_ARCHS[sdk=iphoneos*]: arm64-sim x86_64
groups: [app] groups: [app]
dependencies: dependencies:
- framework: libapp.a - framework: libapp.a
@@ -86,3 +93,4 @@ targets:
outputFiles: outputFiles:
- $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a - $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a
- $(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a - $(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a
- $(SRCROOT)/Externals/arm64-sim/${CONFIGURATION}/libapp.a
@@ -35,9 +35,6 @@
}, },
{ {
"host": "tauri.app" "host": "tauri.app"
},
{
"scheme": ["taurideeplink"]
} }
], ],
"desktop": { "desktop": {
+2 -5
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-deep-link", "name": "@tauri-apps/plugin-deep-link",
"version": "2.4.5", "version": "2.4.1",
"description": "Set your Tauri application as the default handler for an URL", "description": "Set your Tauri application as the default handler for an URL",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
@@ -25,9 +25,6 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.8.0" "@tauri-apps/api": "^2.6.0"
},
"devDependencies": {
"@tauri-apps/cli": "2.9.4"
} }
} }
+11 -45
View File
@@ -11,8 +11,10 @@ use tauri_utils::config::DeepLinkProtocol;
pub struct AssociatedDomain { pub struct AssociatedDomain {
#[serde(default = "default_schemes")] #[serde(default = "default_schemes")]
pub scheme: Vec<String>, pub scheme: Vec<String>,
#[serde(default, deserialize_with = "deserialize_associated_host")]
pub host: Option<String>, // Optional custom uri schemes dont NEED a host (may have one still), but required for https/http schemes #[serde(deserialize_with = "deserialize_associated_host")]
pub host: String,
#[serde(default)] #[serde(default)]
pub path: Vec<String>, pub path: Vec<String>,
#[serde(default, alias = "path-pattern", rename = "pathPattern")] #[serde(default, alias = "path-pattern", rename = "pathPattern")]
@@ -21,41 +23,6 @@ pub struct AssociatedDomain {
pub path_prefix: Vec<String>, pub path_prefix: Vec<String>,
#[serde(default, alias = "path-suffix", rename = "pathSuffix")] #[serde(default, alias = "path-suffix", rename = "pathSuffix")]
pub path_suffix: Vec<String>, pub path_suffix: Vec<String>,
#[serde(default, alias = "app-link", rename = "appLink")]
pub app_link: Option<bool>,
}
impl AssociatedDomain {
/// Returns true if the domain uses http or https scheme.
pub fn is_web_link(&self) -> bool {
self.scheme.iter().any(|s| s == "https" || s == "http")
}
/// Returns true if the domain uses http or https scheme and has proper host configuration.
pub fn is_app_link(&self) -> bool {
self.app_link
.unwrap_or_else(|| self.is_web_link() && self.host.is_some())
}
pub fn validate(&self) -> Result<(), String> {
// Rule 1: All web links require a host.
if self.is_web_link() && self.host.is_none() {
return Err("Web link requires a host".into());
}
// Rule 2: If it's an App Link, ensure http(s) and host.
if self.is_app_link() {
if !self.is_web_link() {
return Err("AppLink must be a valid web link (https/http + host)".into());
}
if self.scheme.iter().any(|s| s == "http") && !self.scheme.iter().any(|s| s == "https")
{
eprintln!("Warning: AppLink uses only 'http' — allowed on Android but not secure for production.");
}
}
Ok(())
}
} }
// TODO: Consider removing this in v3 // TODO: Consider removing this in v3
@@ -63,19 +30,18 @@ fn default_schemes() -> Vec<String> {
vec!["https".to_string(), "http".to_string()] vec!["https".to_string(), "http".to_string()]
} }
fn deserialize_associated_host<'de, D>(deserializer: D) -> Result<Option<String>, D::Error> fn deserialize_associated_host<'de, D>(deserializer: D) -> Result<String, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let opt = Option::<String>::deserialize(deserializer)?; let host = String::deserialize(deserializer)?;
if let Some(ref host) = opt { if let Some((scheme, _host)) = host.split_once("://") {
if let Some((scheme, _)) = host.split_once("://") { Err(serde::de::Error::custom(format!(
return Err(serde::de::Error::custom(format!(
"host `{host}` cannot start with a scheme, please remove the `{scheme}://` prefix" "host `{host}` cannot start with a scheme, please remove the `{scheme}://` prefix"
))); )))
} else {
Ok(host)
} }
}
Ok(opt)
} }
#[derive(Deserialize, Clone)] #[derive(Deserialize, Clone)]
-10
View File
@@ -28,16 +28,6 @@ pub enum Error {
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError), PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
} }
// TODO(v3): change this into an error in v3,
// see <https://github.com/tauri-apps/plugins-workspace/pull/2970#issuecomment-3244660138>.
#[inline]
#[cfg(target_os = "linux")]
pub(crate) fn inspect_command_error<'a>(command: &'a str) -> impl Fn(&std::io::Error) + 'a {
move |e| {
tracing::error!("Failed to run OS command `{command}`: {e}");
}
}
impl Serialize for Error { impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where where
+6 -30
View File
@@ -254,7 +254,6 @@ mod imp {
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **Linux**: Needs the `xdg-mime` and `update-desktop-database` commands available on the system.
/// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`). /// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
pub fn register<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> { pub fn register<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> {
#[cfg(windows)] #[cfg(windows)]
@@ -293,7 +292,6 @@ mod imp {
.unwrap_or_else(|| bin.into_os_string()) .unwrap_or_else(|| bin.into_os_string())
.to_string_lossy() .to_string_lossy()
.to_string(); .to_string();
let qualified_exec = format!("{} %u", exec);
let target = self.app.path().data_dir()?.join("applications"); let target = self.app.path().data_dir()?.join("applications");
@@ -305,28 +303,12 @@ mod imp {
if let Ok(mut desktop_file) = ini::Ini::load_from_file(&target_file) { if let Ok(mut desktop_file) = ini::Ini::load_from_file(&target_file) {
if let Some(section) = desktop_file.section_mut(Some("Desktop Entry")) { if let Some(section) = desktop_file.section_mut(Some("Desktop Entry")) {
// it's ok to remove it - we only write to the file if it's missing
// and in that case we include old_mimes
let old_mimes = section.remove("MimeType").unwrap_or_default(); let old_mimes = section.remove("MimeType").unwrap_or_default();
let mut change = false;
// if the mime type is not present, append it to the list
if !old_mimes.split(';').any(|mime| mime == mime_type) { if !old_mimes.split(';').any(|mime| mime == mime_type) {
section.append("MimeType", format!("{mime_type};{old_mimes}")); section.append("MimeType", format!("{mime_type};{old_mimes}"));
change = true;
} else {
section.insert("MimeType".to_string(), old_mimes);
}
// if the exec command doesnt match, update to the new one
let old_exec = section.remove("Exec").unwrap_or_default();
if old_exec != qualified_exec {
section.append("Exec", qualified_exec);
change = true;
} else {
section.insert("Exec".to_string(), old_exec.to_string());
}
// if any property has changed, rewrite the .desktop file
if change {
desktop_file.write_to_file(&target_file)?; desktop_file.write_to_file(&target_file)?;
} }
} }
@@ -341,7 +323,7 @@ mod imp {
.product_name .product_name
.clone() .clone()
.unwrap_or_else(|| file_name.clone()), .unwrap_or_else(|| file_name.clone()),
qualified_exec = qualified_exec, exec = exec,
mime_type = mime_type mime_type = mime_type
) )
.as_bytes(), .as_bytes(),
@@ -350,15 +332,11 @@ mod imp {
Command::new("update-desktop-database") Command::new("update-desktop-database")
.arg(target) .arg(target)
.status() .status()?;
.inspect_err(crate::error::inspect_command_error(
"update-desktop-database",
))?;
Command::new("xdg-mime") Command::new("xdg-mime")
.args(["default", &file_name, mime_type.as_str()]) .args(["default", &file_name, mime_type.as_str()])
.status() .status()?;
.inspect_err(crate::error::inspect_command_error("xdg-mime"))?;
Ok(()) Ok(())
} }
@@ -427,7 +405,6 @@ mod imp {
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **Linux**: Needs the `xdg-mime` command available on the system.
/// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`). /// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
pub fn is_registered<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<bool> { pub fn is_registered<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<bool> {
#[cfg(windows)] #[cfg(windows)]
@@ -462,8 +439,7 @@ mod imp {
"default", "default",
&format!("x-scheme-handler/{}", _protocol.as_ref()), &format!("x-scheme-handler/{}", _protocol.as_ref()),
]) ])
.output() .output()?;
.inspect_err(crate::error::inspect_command_error("xdg-mime"))?;
Ok(String::from_utf8_lossy(&output.stdout).contains(&file_name)) Ok(String::from_utf8_lossy(&output.stdout).contains(&file_name))
} }
+1 -1
View File
@@ -1,7 +1,7 @@
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
Name={name} Name={name}
Exec={qualified_exec} Exec={exec} %u
Terminal=false Terminal=false
MimeType={mime_type} MimeType={mime_type}
NoDisplay=true NoDisplay=true
-30
View File
@@ -1,35 +1,5 @@
# Changelog # Changelog
## \[2.4.2]
- [`93426f85`](https://github.com/tauri-apps/plugins-workspace/commit/93426f85120f49beb9f40222bff45185a32d54a9) Fixed an issue that caused docs.rs builds to fail. No user facing changes.
### Dependencies
- Upgraded to `fs-js@2.4.4`
## \[2.4.1]
- [`6c9b61fb`](https://github.com/tauri-apps/plugins-workspace/commit/6c9b61fb658145d13893626112fc489f7458aa17) ([#3039](https://github.com/tauri-apps/plugins-workspace/pull/3039) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) On Android, updated compileSdk to 36.
### Dependencies
- Upgraded to `fs-js@2.4.3`
## \[2.4.0]
- [`509eba8d`](https://github.com/tauri-apps/plugins-workspace/commit/509eba8d441c4f6ecf0af77b572cb2afd69a752d) ([#2641](https://github.com/tauri-apps/plugins-workspace/pull/2641) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Add support for showing a message dialog with 3 buttons.
## \[2.3.3]
### Dependencies
- Upgraded to `fs-js@2.4.2`
## \[2.3.2]
- [`af08c66f`](https://github.com/tauri-apps/plugins-workspace/commit/af08c66faafe0dffc4b0a80aef030cd3f0f89a9c) ([#2871](https://github.com/tauri-apps/plugins-workspace/pull/2871) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused the file picker not to open on Android when extension filters were set.
## \[2.3.1] ## \[2.3.1]
### Dependencies ### Dependencies
+5 -8
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-dialog" name = "tauri-plugin-dialog"
version = "2.4.2" version = "2.3.1"
description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application." description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@@ -9,12 +9,9 @@ rust-version = { workspace = true }
repository = { workspace = true } repository = { workspace = true }
links = "tauri-plugin-dialog" links = "tauri-plugin-dialog"
[features]
default = ["gtk3"]
xdg-portal = ["rfd/xdg-portal"]
gtk3 = ["rfd/gtk3"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
targets = ["x86_64-unknown-linux-gnu", "x86_64-linux-android"] targets = ["x86_64-unknown-linux-gnu", "x86_64-linux-android"]
[package.metadata.platforms.support] [package.metadata.platforms.support]
@@ -37,7 +34,7 @@ tauri = { workspace = true }
log = { workspace = true } log = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
url = { workspace = true } url = { workspace = true }
tauri-plugin-fs = { path = "../fs", version = "2.4.4" } tauri-plugin-fs = { path = "../fs", version = "2.4.1" }
[target.'cfg(target_os = "ios")'.dependencies] [target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] } tauri = { workspace = true, features = ["wry"] }
@@ -45,7 +42,7 @@ tauri = { workspace = true, features = ["wry"] }
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
rfd = { version = "0.15", default-features = false, features = [ rfd = { version = "0.15", default-features = false, features = [
"tokio", "tokio",
"gtk3",
"common-controls-v6", "common-controls-v6",
] } ] }
raw-window-handle = "0.6" raw-window-handle = "0.6"
+9 -18
View File
@@ -31,32 +31,23 @@ tauri-plugin-dialog = "2.0.0"
tauri-plugin-dialog = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } tauri-plugin-dialog = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
``` ```
### Linux XDG Desktop Portal Support
By default, this plugin uses gtk to show dialogs, however since `v2.5.0` you can switch to using [XDG Desktop Portal](https://flatpak.github.io/xdg-desktop-portal/) by adding the following to your `Cargo.toml` file:
```toml
[dependencies]
tauri-plugin-dialog = { version = "2.5.0", default-features = false, features = ["xdg-portal"] }
# alternatively with Git:
tauri-plugin-dialog = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2", default-features = false, features = ["xdg-portal"] }
```
Do note if you use the `xdg-portal` feature, you need to ensure that [`zenity`](https://gitlab.gnome.org/GNOME/zenity) and an [XDG Desktop Portal backend](https://flatpak.github.io/xdg-desktop-portal#using-portals) is installed with your program.
For more information, see [XDG Desktop Portal documentation](https://flatpak.github.io/xdg-desktop-portal/) and [`rfd` documentation](https://docs.rs/rfd/latest/rfd#xdg-desktop-portal-backend).
### JavaScript
You can install the JavaScript Guest bindings using your preferred JavaScript package manager: You can install the JavaScript Guest bindings using your preferred JavaScript package manager:
> Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use.
```sh ```sh
pnpm add @tauri-apps/plugin-dialog pnpm add @tauri-apps/plugin-dialog
# or # or
npm add @tauri-apps/plugin-dialog npm add @tauri-apps/plugin-dialog
# or # or
yarn add @tauri-apps/plugin-dialog yarn add @tauri-apps/plugin-dialog
# alternatively with Git:
pnpm add https://github.com/tauri-apps/tauri-plugin-dialog#v2
# or
npm add https://github.com/tauri-apps/tauri-plugin-dialog#v2
# or
yarn add https://github.com/tauri-apps/tauri-plugin-dialog#v2
``` ```
## Usage ## Usage
+1 -1
View File
@@ -5,7 +5,7 @@ plugins {
android { android {
namespace = "app.tauri.dialog" namespace = "app.tauri.dialog"
compileSdk = 36 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24
@@ -31,7 +31,6 @@ class Filter {
class FilePickerOptions { class FilePickerOptions {
lateinit var filters: Array<Filter> lateinit var filters: Array<Filter>
var multiple: Boolean? = null var multiple: Boolean? = null
var pickerMode: String? = null
} }
@InvokeArg @InvokeArg
@@ -39,7 +38,6 @@ class MessageOptions {
var title: String? = null var title: String? = null
lateinit var message: String lateinit var message: String
var okButtonLabel: String? = null var okButtonLabel: String? = null
var noButtonLabel: String? = null
var cancelButtonLabel: String? = null var cancelButtonLabel: String? = null
} }
@@ -59,22 +57,15 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
val args = invoke.parseArgs(FilePickerOptions::class.java) val args = invoke.parseArgs(FilePickerOptions::class.java)
val parsedTypes = parseFiltersOption(args.filters) val parsedTypes = parseFiltersOption(args.filters)
// TODO: ACTION_OPEN_DOCUMENT ?? val intent = if (parsedTypes.isNotEmpty()) {
val intent = Intent(Intent.ACTION_PICK)
setIntentMimeTypes(intent, parsedTypes)
intent
} else {
val intent = Intent(Intent.ACTION_GET_CONTENT) val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
if (args.pickerMode == "image") {
intent.type = "image/*"
} else if (args.pickerMode == "video") {
intent.type = "video/*"
} else if (args.pickerMode == "media") {
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("video/*", "image/*"))
} else if (parsedTypes.isNotEmpty()) {
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, parsedTypes)
} else {
intent.type = "*/*" intent.type = "*/*"
intent
} }
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, args.multiple ?: false) intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, args.multiple ?: false)
@@ -141,6 +132,34 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
return mimeTypes.toTypedArray() return mimeTypes.toTypedArray()
} }
private fun setIntentMimeTypes(intent: Intent, mimeTypes: Array<String>) {
if (mimeTypes.isNotEmpty()) {
var uniqueMimeKind = true
var mimeKind: String? = null
for (mime in mimeTypes) {
val kind = mime.split("/")[0]
if (mimeKind == null) {
mimeKind = kind
} else if (mimeKind != kind) {
uniqueMimeKind = false
}
}
if (uniqueMimeKind) {
if (mimeTypes.size > 1) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
intent.type = Intent.normalizeMimeType("$mimeKind/*")
} else {
intent.type = mimeTypes[0]
}
} else {
intent.type = "*/*"
}
} else {
intent.type = "*/*"
}
}
@Command @Command
fun showMessageDialog(invoke: Invoke) { fun showMessageDialog(invoke: Invoke) {
val args = invoke.parseArgs(MessageOptions::class.java) val args = invoke.parseArgs(MessageOptions::class.java)
@@ -150,8 +169,9 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
return return
} }
val handler = { value: String -> val handler = { cancelled: Boolean, value: Boolean ->
val ret = JSObject() val ret = JSObject()
ret.put("cancelled", cancelled)
ret.put("value", value) ret.put("value", value)
invoke.resolve(ret) invoke.resolve(ret)
} }
@@ -163,34 +183,24 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
if (args.title != null) { if (args.title != null) {
builder.setTitle(args.title) builder.setTitle(args.title)
} }
val okButtonLabel = args.okButtonLabel ?: "Ok"
builder builder
.setMessage(args.message) .setMessage(args.message)
.setPositiveButton(okButtonLabel) { dialog, _ -> .setPositiveButton(
args.okButtonLabel ?: "OK"
) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
handler(okButtonLabel) handler(false, true)
} }
.setOnCancelListener { dialog -> .setOnCancelListener { dialog ->
dialog.dismiss() dialog.dismiss()
handler(args.cancelButtonLabel ?: "Cancel") handler(true, false)
} }
if (args.noButtonLabel != null) {
builder.setNeutralButton(args.noButtonLabel) { dialog, _ ->
dialog.dismiss()
handler(args.noButtonLabel!!)
}
}
if (args.cancelButtonLabel != null) { if (args.cancelButtonLabel != null) {
builder.setNegativeButton( args.cancelButtonLabel) { dialog, _ -> builder.setNegativeButton( args.cancelButtonLabel) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
handler(args.cancelButtonLabel!!) handler(false, false)
} }
} }
val dialog = builder.create() val dialog = builder.create()
dialog.show() dialog.show()
} }
@@ -203,14 +213,10 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
val parsedTypes = parseFiltersOption(args.filters) val parsedTypes = parseFiltersOption(args.filters)
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
setIntentMimeTypes(intent, parsedTypes)
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.putExtra(Intent.EXTRA_TITLE, args.fileName ?: "") intent.putExtra(Intent.EXTRA_TITLE, args.fileName ?: "")
intent.type = "*/*"
if (parsedTypes.isNotEmpty()) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, parsedTypes)
}
startActivityForResult(invoke, intent, "saveFileDialogResult") startActivityForResult(invoke, intent, "saveFileDialogResult")
} catch (ex: Exception) { } catch (ex: Exception) {
val message = ex.message ?: "Failed to pick save file" val message = ex.message ?: "Failed to pick save file"
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_DIALOG__=function(t){"use strict";async function n(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}function e(t){if(void 0!==t)return"string"==typeof t?t:"ok"in t&&"cancel"in t?{OkCancelCustom:[t.ok,t.cancel]}:"yes"in t&&"no"in t&&"cancel"in t?{YesNoCancelCustom:[t.yes,t.no,t.cancel]}:"ok"in t?{OkCustom:t.ok}:void 0}return"function"==typeof SuppressedError&&SuppressedError,t.ask=async function(t,e){const o="string"==typeof e?{title:e}:e;return await n("plugin:dialog|ask",{message:t.toString(),title:o?.title?.toString(),kind:o?.kind,yesButtonLabel:o?.okLabel?.toString(),noButtonLabel:o?.cancelLabel?.toString()})},t.confirm=async function(t,e){const o="string"==typeof e?{title:e}:e;return await n("plugin:dialog|confirm",{message:t.toString(),title:o?.title?.toString(),kind:o?.kind,okButtonLabel:o?.okLabel?.toString(),cancelButtonLabel:o?.cancelLabel?.toString()})},t.message=async function(t,o){const i="string"==typeof o?{title:o}:o;return n("plugin:dialog|message",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString(),buttons:e(i?.buttons)})},t.open=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|open",{options:t})},t.save=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|save",{options:t})},t}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_PLUGIN_DIALOG__})} if("__TAURI__"in window){var __TAURI_PLUGIN_DIALOG__=function(t){"use strict";async function n(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}return"function"==typeof SuppressedError&&SuppressedError,t.ask=async function(t,e){const i="string"==typeof e?{title:e}:e;return await n("plugin:dialog|ask",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,yesButtonLabel:i?.okLabel?.toString(),noButtonLabel:i?.cancelLabel?.toString()})},t.confirm=async function(t,e){const i="string"==typeof e?{title:e}:e;return await n("plugin:dialog|confirm",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString(),cancelButtonLabel:i?.cancelLabel?.toString()})},t.message=async function(t,e){const i="string"==typeof e?{title:e}:e;await n("plugin:dialog|message",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString()})},t.open=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|open",{options:t})},t.save=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|save",{options:t})},t}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_PLUGIN_DIALOG__})}
+5 -175
View File
@@ -14,16 +14,6 @@ interface DialogFilter {
name: string name: string
/** /**
* Extensions to filter, without a `.` prefix. * Extensions to filter, without a `.` prefix.
*
* **Note:** Mobile platforms have different APIs for filtering that may not support extensions.
* iOS: Extensions are supported in the document picker, but not in the media picker.
* Android: Extensions are not supported.
*
* For these platforms, MIME types are the primary way to filter files, as opposed to extensions.
* This means the string values here labeled as `extensions` may also be a MIME type.
* This property name of `extensions` is being kept for backwards compatibility, but this may be revisited to
* specify the difference between extension or MIME type filtering.
*
* @example * @example
* ```typescript * ```typescript
* extensions: ['svg', 'png'] * extensions: ['svg', 'png']
@@ -40,14 +30,7 @@ interface DialogFilter {
interface OpenDialogOptions { interface OpenDialogOptions {
/** The title of the dialog window (desktop only). */ /** The title of the dialog window (desktop only). */
title?: string title?: string
/** /** The filters of the dialog. */
* The filters of the dialog.
* On mobile platforms, if either:
* A) the {@linkcode pickerMode} is set to `media`, `image`, or `video`
* -- or --
* B) the filters include **only** either image or video mime types, the media picker will be displayed.
* Otherwise, the document picker will be displayed.
*/
filters?: DialogFilter[] filters?: DialogFilter[]
/** /**
* Initial directory or file path. * Initial directory or file path.
@@ -69,13 +52,6 @@ interface OpenDialogOptions {
recursive?: boolean recursive?: boolean
/** Whether to allow creating directories in the dialog. Enabled by default. **macOS Only** */ /** Whether to allow creating directories in the dialog. Enabled by default. **macOS Only** */
canCreateDirectories?: boolean canCreateDirectories?: boolean
/**
* The preferred mode of the dialog.
* This is meant for mobile platforms (iOS and Android) which have distinct file and media pickers.
* If not provided, the dialog will automatically choose the best mode based on the MIME types or extensions of the {@linkcode filters}.
* On desktop, this option is ignored.
*/
pickerMode?: PickerMode
} }
/** /**
@@ -101,90 +77,6 @@ interface SaveDialogOptions {
canCreateDirectories?: boolean canCreateDirectories?: boolean
} }
/**
* The preferred mode of the dialog.
* This is meant for mobile platforms (iOS and Android) which have distinct file and media pickers.
* On desktop, this option is ignored.
* If not provided, the dialog will automatically choose the best mode based on the MIME types or extensions of the {@linkcode filters}.
*
* **Note:** This option is only supported on iOS 14 and above. This parameter is ignored on iOS 13 and below.
*/
export type PickerMode = 'document' | 'media' | 'image' | 'video'
/**
* Default buttons for a message dialog.
*
* @since 2.4.0
*/
export type MessageDialogDefaultButtons =
| 'Ok'
| 'OkCancel'
| 'YesNo'
| 'YesNoCancel'
/** All possible button keys. */
type ButtonKey = 'ok' | 'cancel' | 'yes' | 'no'
/** Ban everything except a set of keys. */
type BanExcept<Allowed extends ButtonKey> = Partial<
Record<Exclude<ButtonKey, Allowed>, never>
>
/**
* The Yes, No and Cancel buttons of a message dialog.
*
* @since 2.4.0
*/
export type MessageDialogButtonsYesNoCancel = {
/** The Yes button. */
yes: string
/** The No button. */
no: string
/** The Cancel button. */
cancel: string
} & BanExcept<'yes' | 'no' | 'cancel'>
/**
* The Ok and Cancel buttons of a message dialog.
*
* @since 2.4.0
*/
export type MessageDialogButtonsOkCancel = {
/** The Ok button. */
ok: string
/** The Cancel button. */
cancel: string
} & BanExcept<'ok' | 'cancel'>
/**
* The Ok button of a message dialog.
*
* @since 2.4.0
*/
export type MessageDialogButtonsOk = {
/** The Ok button. */
ok: string
} & BanExcept<'ok'>
/**
* Custom buttons for a message dialog.
*
* @since 2.4.0
*/
export type MessageDialogCustomButtons =
| MessageDialogButtonsYesNoCancel
| MessageDialogButtonsOkCancel
| MessageDialogButtonsOk
/**
* The buttons of a message dialog.
*
* @since 2.4.0
*/
export type MessageDialogButtons =
| MessageDialogDefaultButtons
| MessageDialogCustomButtons
/** /**
* @since 2.0.0 * @since 2.0.0
*/ */
@@ -193,58 +85,8 @@ interface MessageDialogOptions {
title?: string title?: string
/** The kind of the dialog. Defaults to `info`. */ /** The kind of the dialog. Defaults to `info`. */
kind?: 'info' | 'warning' | 'error' kind?: 'info' | 'warning' | 'error'
/** /** The label of the confirm button. */
* The label of the Ok button.
*
* @deprecated Use {@linkcode MessageDialogOptions.buttons} instead.
*/
okLabel?: string okLabel?: string
/**
* The buttons of the dialog.
*
* @example
*
* ```ts
* // Use system default buttons texts
* await message('Hello World!', { buttons: 'Ok' })
* await message('Hello World!', { buttons: 'OkCancel' })
*
* // Or with custom button texts
* await message('Hello World!', { buttons: { ok: 'Yes!' } })
* await message('Take on the task?', {
* buttons: { ok: 'Accept', cancel: 'Cancel' }
* })
* await message('Show the file content?', {
* buttons: { yes: 'Show content', no: 'Show in folder', cancel: 'Cancel' }
* })
* ```
*
* @since 2.4.0
*/
buttons?: MessageDialogButtons
}
/**
* Internal function to convert the buttons to the Rust type.
*/
function buttonsToRust(buttons: MessageDialogButtons | undefined) {
if (buttons === undefined) {
return undefined
}
if (typeof buttons === 'string') {
return buttons
} else if ('ok' in buttons && 'cancel' in buttons) {
return { OkCancelCustom: [buttons.ok, buttons.cancel] }
} else if ('yes' in buttons && 'no' in buttons && 'cancel' in buttons) {
return {
YesNoCancelCustom: [buttons.yes, buttons.no, buttons.cancel]
}
} else if ('ok' in buttons) {
return { OkCustom: buttons.ok }
}
return undefined
} }
interface ConfirmDialogOptions { interface ConfirmDialogOptions {
@@ -360,16 +202,6 @@ async function save(options: SaveDialogOptions = {}): Promise<string | null> {
return await invoke('plugin:dialog|save', { options }) return await invoke('plugin:dialog|save', { options })
} }
/**
* The result of a message dialog.
*
* The result is a string if the dialog has custom buttons,
* otherwise it is one of the default buttons.
*
* @since 2.4.0
*/
export type MessageDialogResult = 'Yes' | 'No' | 'Ok' | 'Cancel' | (string & {})
/** /**
* Shows a message dialog with an `Ok` button. * Shows a message dialog with an `Ok` button.
* @example * @example
@@ -390,15 +222,13 @@ export type MessageDialogResult = 'Yes' | 'No' | 'Ok' | 'Cancel' | (string & {})
async function message( async function message(
message: string, message: string,
options?: string | MessageDialogOptions options?: string | MessageDialogOptions
): Promise<MessageDialogResult> { ): Promise<void> {
const opts = typeof options === 'string' ? { title: options } : options const opts = typeof options === 'string' ? { title: options } : options
await invoke('plugin:dialog|message', {
return invoke<MessageDialogResult>('plugin:dialog|message', {
message: message.toString(), message: message.toString(),
title: opts?.title?.toString(), title: opts?.title?.toString(),
kind: opts?.kind, kind: opts?.kind,
okButtonLabel: opts?.okLabel?.toString(), okButtonLabel: opts?.okLabel?.toString()
buttons: buttonsToRust(opts?.buttons)
}) })
} }
+63 -122
View File
@@ -8,7 +8,6 @@ import PhotosUI
import SwiftRs import SwiftRs
import Tauri import Tauri
import UIKit import UIKit
import UniformTypeIdentifiers
import WebKit import WebKit
enum FilePickerEvent { enum FilePickerEvent {
@@ -21,7 +20,6 @@ struct MessageDialogOptions: Decodable {
var title: String? var title: String?
let message: String let message: String
var okButtonLabel: String? var okButtonLabel: String?
var noButtonLabel: String?
var cancelButtonLabel: String? var cancelButtonLabel: String?
} }
@@ -33,7 +31,6 @@ struct FilePickerOptions: Decodable {
var multiple: Bool? var multiple: Bool?
var filters: [Filter]? var filters: [Filter]?
var defaultPath: String? var defaultPath: String?
var pickerMode: PickerMode?
} }
struct SaveFileDialogOptions: Decodable { struct SaveFileDialogOptions: Decodable {
@@ -41,13 +38,6 @@ struct SaveFileDialogOptions: Decodable {
var defaultPath: String? var defaultPath: String?
} }
enum PickerMode: String, Decodable {
case document
case media
case image
case video
}
class DialogPlugin: Plugin { class DialogPlugin: Plugin {
var filePickerController: FilePickerController! var filePickerController: FilePickerController!
@@ -61,6 +51,26 @@ class DialogPlugin: Plugin {
@objc public func showFilePicker(_ invoke: Invoke) throws { @objc public func showFilePicker(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(FilePickerOptions.self) let args = try invoke.parseArgs(FilePickerOptions.self)
let parsedTypes = parseFiltersOption(args.filters ?? [])
var isMedia = !parsedTypes.isEmpty
var uniqueMimeType: Bool? = nil
var mimeKind: String? = nil
if !parsedTypes.isEmpty {
uniqueMimeType = true
for mime in parsedTypes {
let kind = mime.components(separatedBy: "/")[0]
if kind != "image" && kind != "video" {
isMedia = false
}
if mimeKind == nil {
mimeKind = kind
} else if mimeKind != kind {
uniqueMimeType = false
}
}
}
onFilePickerResult = { (event: FilePickerEvent) -> Void in onFilePickerResult = { (event: FilePickerEvent) -> Void in
switch event { switch event {
case .selected(let urls): case .selected(let urls):
@@ -72,55 +82,49 @@ class DialogPlugin: Plugin {
} }
} }
if #available(iOS 14, *) { if uniqueMimeType == true || isMedia {
let parsedTypes = parseFiltersOption(args.filters ?? [])
let mimeKinds = Set(parsedTypes.compactMap { $0.preferredMIMEType?.components(separatedBy: "/")[0] })
let filtersIncludeImage = mimeKinds.contains("image")
let filtersIncludeVideo = mimeKinds.contains("video")
let filtersIncludeNonMedia = mimeKinds.contains(where: { $0 != "image" && $0 != "video" })
// If the picker mode is media, images, or videos, we always want to show the media picker regardless of what's in the filters.
// Otherwise, if the filters A) do not include non-media types and B) include either image or video, we want to show the media picker.
if args.pickerMode == .media
|| args.pickerMode == .image
|| args.pickerMode == .video
|| (!filtersIncludeNonMedia && (filtersIncludeImage || filtersIncludeVideo)) {
DispatchQueue.main.async { DispatchQueue.main.async {
if #available(iOS 14, *) {
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared()) var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.selectionLimit = (args.multiple ?? false) ? 0 : 1 configuration.selectionLimit = (args.multiple ?? false) ? 0 : 1
// If the filters include image or video, use the appropriate filter. if uniqueMimeType == true {
// If both are true, don't define a filter, which means we will display all media. if mimeKind == "image" {
if args.pickerMode == .image || (filtersIncludeImage && !filtersIncludeVideo) {
configuration.filter = .images configuration.filter = .images
} else if args.pickerMode == .video || (filtersIncludeVideo && !filtersIncludeImage) { } else if mimeKind == "video" {
configuration.filter = .videos configuration.filter = .videos
} }
}
let picker = PHPickerViewController(configuration: configuration) let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self.filePickerController picker.delegate = self.filePickerController
picker.modalPresentationStyle = .fullScreen picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker) self.presentViewController(picker)
}
} else { } else {
DispatchQueue.main.async { let picker = UIImagePickerController()
// The UTType.item is the catch-all, allowing for any file type to be selected. picker.delegate = self.filePickerController
let contentTypes = parsedTypes.isEmpty ? [UTType.item] : parsedTypes
let picker: UIDocumentPickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: contentTypes, asCopy: true)
if let defaultPath = args.defaultPath { if uniqueMimeType == true && mimeKind == "image" {
picker.directoryURL = URL(string: defaultPath) picker.sourceType = .photoLibrary
} }
picker.delegate = self.filePickerController picker.sourceType = .photoLibrary
picker.allowsMultipleSelection = args.multiple ?? false
picker.modalPresentationStyle = .fullScreen picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker) self.presentViewController(picker)
} }
} }
} else { } else {
showFilePickerLegacy(args: args) let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
DispatchQueue.main.async {
let picker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .import)
if let defaultPath = args.defaultPath {
picker.directoryURL = URL(string: defaultPath)
}
picker.delegate = self.filePickerController
picker.allowsMultipleSelection = args.multiple ?? false
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
}
} }
} }
@@ -168,80 +172,19 @@ class DialogPlugin: Plugin {
self.manager.viewController?.present(viewControllerToPresent, animated: true, completion: nil) self.manager.viewController?.present(viewControllerToPresent, animated: true, completion: nil)
} }
@available(iOS 14, *) private func parseFiltersOption(_ filters: [Filter]) -> [String] {
private func parseFiltersOption(_ filters: [Filter]) -> [UTType] {
var parsedTypes: [UTType] = []
for filter in filters {
for ext in filter.extensions ?? [] {
// We need to support extensions as well as MIME types.
if let utType = UTType(mimeType: ext) {
parsedTypes.append(utType)
} else if let utType = UTType(filenameExtension: ext) {
parsedTypes.append(utType)
}
}
}
return parsedTypes
}
/// This function is only used for iOS < 14, and should be removed if/when the deployment target is raised to 14.
private func showFilePickerLegacy(args: FilePickerOptions) {
let parsedTypes = parseFiltersOptionLegacy(args.filters ?? [])
var filtersIncludeImage: Bool = false
var filtersIncludeVideo: Bool = false
var filtersIncludeNonMedia: Bool = false
if !parsedTypes.isEmpty {
let mimeKinds = Set(parsedTypes.map { $0.components(separatedBy: "/")[0] })
filtersIncludeImage = mimeKinds.contains("image")
filtersIncludeVideo = mimeKinds.contains("video")
filtersIncludeNonMedia = mimeKinds.contains(where: { $0 != "image" && $0 != "video" })
}
if !filtersIncludeNonMedia && (filtersIncludeImage || filtersIncludeVideo) {
DispatchQueue.main.async {
let picker = UIImagePickerController()
picker.delegate = self.filePickerController
if filtersIncludeImage && !filtersIncludeVideo {
picker.sourceType = .photoLibrary
}
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
}
} else {
let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
DispatchQueue.main.async {
let picker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .import)
if let defaultPath = args.defaultPath {
picker.directoryURL = URL(string: defaultPath)
}
picker.delegate = self.filePickerController
picker.allowsMultipleSelection = args.multiple ?? false
picker.modalPresentationStyle = .fullScreen
self.presentViewController(picker)
}
}
}
/// This function is only used for iOS < 14, and should be removed if/when the deployment target is raised to 14.
private func parseFiltersOptionLegacy(_ filters: [Filter]) -> [String] {
var parsedTypes: [String] = [] var parsedTypes: [String] = []
for filter in filters { for filter in filters {
for ext in filter.extensions ?? [] { for ext in filter.extensions ?? [] {
guard guard
let utType: String = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, ext as CFString, nil)?.takeRetainedValue() as String? let utType: String = UTTypeCreatePreferredIdentifierForTag(
kUTTagClassMIMEType, ext as CFString, nil)?.takeRetainedValue() as String?
else { else {
continue continue
} }
parsedTypes.append(utType) parsedTypes.append(utType)
} }
} }
return parsedTypes return parsedTypes
} }
@@ -257,37 +200,35 @@ class DialogPlugin: Plugin {
let alert = UIAlertController( let alert = UIAlertController(
title: args.title, message: args.message, preferredStyle: UIAlertController.Style.alert) title: args.title, message: args.message, preferredStyle: UIAlertController.Style.alert)
if let cancelButtonLabel = args.cancelButtonLabel { let cancelButtonLabel = args.cancelButtonLabel ?? ""
if !cancelButtonLabel.isEmpty {
alert.addAction( alert.addAction(
UIAlertAction( UIAlertAction(
title: cancelButtonLabel, style: UIAlertAction.Style.default, title: cancelButtonLabel, style: UIAlertAction.Style.default,
handler: { (_) -> Void in handler: { (_) -> Void in
invoke.resolve(["value": cancelButtonLabel]) Logger.error("cancel")
}
) invoke.resolve([
) "value": false,
"cancelled": false,
])
}))
} }
if let noButtonLabel = args.noButtonLabel { let okButtonLabel = args.okButtonLabel ?? (cancelButtonLabel.isEmpty ? "OK" : "")
alert.addAction( if !okButtonLabel.isEmpty {
UIAlertAction(
title: noButtonLabel, style: UIAlertAction.Style.default,
handler: { (_) -> Void in
invoke.resolve(["value": noButtonLabel])
}
)
)
}
let okButtonLabel = args.okButtonLabel ?? "Ok"
alert.addAction( alert.addAction(
UIAlertAction( UIAlertAction(
title: okButtonLabel, style: UIAlertAction.Style.default, title: okButtonLabel, style: UIAlertAction.Style.default,
handler: { (_) -> Void in handler: { (_) -> Void in
invoke.resolve(["value": okButtonLabel]) Logger.error("ok")
invoke.resolve([
"value": true,
"cancelled": false,
])
}))
} }
)
)
manager.viewController?.present(alert, animated: true, completion: nil) manager.viewController?.present(alert, animated: true, completion: nil)
} }
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-dialog", "name": "@tauri-apps/plugin-dialog",
"version": "2.4.2", "version": "2.3.1",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"
@@ -24,6 +24,6 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2.8.0" "@tauri-apps/api": "^2.6.0"
} }
} }

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