Merge branch 'v2' into ws-patch

pull/2027/head
Twilight 6 months ago committed by GitHub
commit a585be74e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,6 +0,0 @@
---
"upload": "minor"
"upload-js": "minor"
---
Added a new field `progressTotal` to track the total amount of data transferred during the upload/download process.

@ -14,10 +14,20 @@
"command": "pnpm build", "command": "pnpm build",
"dryRunCommand": "pnpm build" "dryRunCommand": "pnpm build"
}, },
{
"command": "echo '<details>\n<summary><em><h4>PNPM Publish</h4></em></summary>\n\n```'",
"dryRunCommand": true,
"pipe": true
},
{ {
"command": "npm publish --provenance --access public", "command": "npm publish --provenance --access public",
"dryRunCommand": "npm publish --provenance --access public --dry-run", "dryRunCommand": "npm publish --provenance --access public --dry-run",
"pipe": true "pipe": true
},
{
"command": "echo '```\n\n</details>\n'",
"dryRunCommand": true,
"pipe": true
} }
] ]
}, },
@ -56,12 +66,13 @@
"dependencies": [ "dependencies": [
"barcode-scanner", "barcode-scanner",
"biometric", "biometric",
"log-plugin", "log",
"cli", "cli",
"clipboard-manager", "clipboard-manager",
"dialog", "dialog",
"fs", "fs",
"global-shortcut", "global-shortcut",
"opener",
"http", "http",
"nfc", "nfc",
"notification", "notification",
@ -87,6 +98,7 @@
"dialog-js", "dialog-js",
"fs-js", "fs-js",
"global-shortcut-js", "global-shortcut-js",
"opener-js",
"http-js", "http-js",
"nfc-js", "nfc-js",
"notification-js", "notification-js",
@ -168,7 +180,8 @@
}, },
"dialog-js": { "dialog-js": {
"path": "./plugins/dialog", "path": "./plugins/dialog",
"manager": "javascript" "manager": "javascript",
"dependencies": ["fs-js"]
}, },
"geolocation": { "geolocation": {
"path": "./plugins/geolocation", "path": "./plugins/geolocation",
@ -186,6 +199,14 @@
"path": "./plugins/global-shortcut", "path": "./plugins/global-shortcut",
"manager": "javascript" "manager": "javascript"
}, },
"opener": {
"path": "./plugins/opener",
"manager": "rust"
},
"opener-js": {
"path": "./plugins/opener",
"manager": "javascript"
},
"haptics": { "haptics": {
"path": "./plugins/haptics", "path": "./plugins/haptics",
"manager": "rust" "manager": "rust"
@ -201,13 +222,14 @@
}, },
"http-js": { "http-js": {
"path": "./plugins/http", "path": "./plugins/http",
"manager": "javascript" "manager": "javascript",
"dependencies": ["fs-js"]
}, },
"localhost": { "localhost": {
"path": "./plugins/localhost", "path": "./plugins/localhost",
"manager": "rust" "manager": "rust"
}, },
"log-plugin": { "log": {
"path": "./plugins/log", "path": "./plugins/log",
"manager": "rust" "manager": "rust"
}, },

@ -1,6 +0,0 @@
---
deep-link: patch
deep-link-js: patch
---
`onOpenUrl()` will now not call `getCurrent()` anymore, matching the documented behavior.

@ -1,6 +0,0 @@
---
'log-plugin': 'patch'
'log-js': 'patch'
---
Make webview log target more consistent that it always starts with `webview`

@ -1,7 +0,0 @@
---
"fs": "patch"
"fs-js": "patch"
---
Fix `readDir` function failing to read directories that contain broken symlinks.

@ -1,5 +0,0 @@
---
'localhost': 'minor'
---
Add custom host binding to allow external access

@ -6,6 +6,8 @@ As you create PRs and make changes that require a version bump, please add a new
When you select the version bump required, you do _not_ need to consider dependencies. Only note the package with the actual change, and any packages that depend on that package will be bumped automatically in the process. When you select the version bump required, you do _not_ need to consider dependencies. Only note the package with the actual change, and any packages that depend on that package will be bumped automatically in the process.
**Note, that in this repository, even if only the Rust code or only the JavaScript code of a plugin changed, both packages need to be bumped with the same increment!**
Use the following format: Use the following format:
```md ```md

@ -1,5 +0,0 @@
---
"sql": "patch"
---
Allow blocking on async code without creating a nested runtime.

@ -0,0 +1,44 @@
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
name: check change files
on:
pull_request:
paths:
- '.changes/*.md'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: check change files end with .md
run: |
for file in .changes/*
do
if [[ ! "$file" =~ \.(md|json)$ ]]; then
echo ".changes directory should only contain files that end with .md"
echo "found an invalid file in .changes directory:"
echo "$file"
exit 1
fi
done
- uses: dorny/paths-filter@v3
id: filter
with:
list-files: shell
filters: |
changes:
- added|modified: '.changes/*.md'
- name: check
run: node ./.scripts/ci/check-change-files.js ${{ steps.filter.outputs.changes_files }}
if: ${{ steps.filter.outputs.changes == 'true' }}

@ -53,6 +53,10 @@ jobs:
- .github/workflows/check-generated-files.yml - .github/workflows/check-generated-files.yml
- plugins/global-shortcut/guest-js/** - plugins/global-shortcut/guest-js/**
- plugins/global-shortcut/src/api-iife.js - plugins/global-shortcut/src/api-iife.js
opener:
- .github/workflows/check-generated-files.yml
- plugins/opener/guest-js/**
- plugins/opener/src/api-iife.js
haptics: haptics:
- .github/workflows/check-generated-files.yml - .github/workflows/check-generated-files.yml
- plugins/haptics/guest-js/** - plugins/haptics/guest-js/**

@ -66,6 +66,9 @@ jobs:
tauri-plugin-global-shortcut: tauri-plugin-global-shortcut:
- .github/workflows/lint-rust.yml - .github/workflows/lint-rust.yml
- plugins/global-shortcut/** - plugins/global-shortcut/**
tauri-plugin-opener:
- .github/workflows/lint-rust.yml
- plugins/opener/**
tauri-plugin-haptics: tauri-plugin-haptics:
- .github/workflows/lint-rust.yml - .github/workflows/lint-rust.yml
- plugins/haptics/** - plugins/haptics/**

@ -47,109 +47,140 @@ jobs:
tauri-plugin-autostart: tauri-plugin-autostart:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/autostart/** - plugins/autostart/**
tauri-plugin-cli: tauri-plugin-cli:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/cli/** - plugins/cli/**
tauri-plugin-clipboard-manager: tauri-plugin-clipboard-manager:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/clipboard-manager/** - plugins/clipboard-manager/**
tauri-plugin-deep-link: tauri-plugin-deep-link:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/deep-link/** - plugins/deep-link/**
tauri-plugin-dialog: tauri-plugin-dialog:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/dialog/** - plugins/dialog/**
- plugins/fs/** - plugins/fs/**
tauri-plugin-fs: tauri-plugin-fs:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/fs/** - plugins/fs/**
tauri-plugin-geolocation: tauri-plugin-geolocation:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/geolocation/** - plugins/geolocation/**
tauri-plugin-global-shortcut: tauri-plugin-global-shortcut:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/global-shortcut/** - plugins/global-shortcut/**
tauri-plugin-opener:
- .github/workflows/test-rust.yml
- Cargo.toml
- Cargo.lock
- plugins/opener/**
tauri-plugin-haptics: tauri-plugin-haptics:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/haptics/** - plugins/haptics/**
tauri-plugin-http: tauri-plugin-http:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/http/** - plugins/http/**
- plugins/fs/** - plugins/fs/**
tauri-plugin-localhost: tauri-plugin-localhost:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/localhost/** - plugins/localhost/**
tauri-plugin-log: tauri-plugin-log:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/log/** - plugins/log/**
tauri-plugin-notification: tauri-plugin-notification:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/notification/** - plugins/notification/**
tauri-plugin-os: tauri-plugin-os:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/os/** - plugins/os/**
tauri-plugin-persisted-scope: tauri-plugin-persisted-scope:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/persisted-scope/** - plugins/persisted-scope/**
- plugins/fs/** - plugins/fs/**
tauri-plugin-positioner: tauri-plugin-positioner:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/positioner/** - plugins/positioner/**
tauri-plugin-process: tauri-plugin-process:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/process/** - plugins/process/**
tauri-plugin-shell: tauri-plugin-shell:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/shell/** - plugins/shell/**
tauri-plugin-single-instance: tauri-plugin-single-instance:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/single-instance/** - plugins/single-instance/**
tauri-plugin-sql: tauri-plugin-sql:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/sql/** - plugins/sql/**
tauri-plugin-store: tauri-plugin-store:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/store/** - plugins/store/**
tauri-plugin-stronghold: tauri-plugin-stronghold:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/stronghold/** - plugins/stronghold/**
tauri-plugin-updater: tauri-plugin-updater:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/updater/** - plugins/updater/**
tauri-plugin-upload: tauri-plugin-upload:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/upload/** - plugins/upload/**
tauri-plugin-websocket: tauri-plugin-websocket:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/websocket/** - plugins/websocket/**
tauri-plugin-window-state: tauri-plugin-window-state:
- .github/workflows/test-rust.yml - .github/workflows/test-rust.yml
- Cargo.toml - Cargo.toml
- Cargo.lock
- plugins/window-state/** - plugins/window-state/**
test: test:

@ -0,0 +1,86 @@
#!/usr/bin/env node
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
import { readFileSync, readdirSync } from 'fs'
import { join } from 'path'
/* const ignorePackages = [
'api-example',
'api-example-js',
'deep-link-example',
'deep-link-example-js'
] */
const rsOnly = ['localhost', 'persisted-scope', 'single-instance']
function checkChangeFiles(changeFiles) {
let code = 0
for (const file of changeFiles) {
const content = readFileSync(file, 'utf8')
const [frontMatter] = /^---[\s\S.]*---\n/i.exec(content)
const packages = frontMatter
.split('\n')
.filter((l) => !(l === '---' || !l))
.map((l) => l.replace(/('|")/g, '').split(':'))
const rsPackages = Object.fromEntries(
packages
.filter((v) => !v[0].endsWith('-js'))
.map((v) => [v[0], v[1].trim()])
)
const jsPackages = Object.fromEntries(
packages
.filter((v) => v[0].endsWith('-js'))
.map((v) => [v[0].slice(0, -3), v[1].trim()])
)
for (const pkg in rsPackages) {
if (rsOnly.includes(pkg)) continue
if (!jsPackages[pkg]) {
console.error(
`Missing "${rsPackages[pkg]}" bump for JS package "${pkg}-js" in ${file}.`
)
code = 1
} else if (rsPackages[pkg] != jsPackages[pkg]) {
console.error(
`"${pkg}" and "${pkg}-js" have different version bumps in ${file}.`
)
code = 1
}
}
for (const pkg in jsPackages) {
if (!rsPackages[pkg]) {
console.error(
`Missing "${jsPackages[pkg]}" bump for Rust package "${pkg}" in ${file}.`
)
code = 1
} else if (rsPackages[pkg] != jsPackages[pkg]) {
console.error(
`"${pkg}" and "${pkg}-js" have different version bumps in ${file}.`
)
code = 1
}
}
}
process.exit(code)
}
const [_bin, _script, ...files] = process.argv
if (files.length > 0) {
checkChangeFiles(
files.filter((f) => f.toLowerCase() !== '.changes/readme.md')
)
} else {
const changeFiles = readdirSync('.changes')
.filter((f) => f.endsWith('.md') && f.toLowerCase() !== 'readme.md')
.map((p) => join('.changes', p))
checkChangeFiles(changeFiles)
}

@ -0,0 +1 @@
plugins/*/permissions/autogenerated/

1510
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -10,6 +10,7 @@ resolver = "2"
[workspace.dependencies] [workspace.dependencies]
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
tracing = "0.1"
log = "0.4" log = "0.4"
tauri = { version = "2", default-features = false } tauri = { version = "2", default-features = false }
tauri-build = "2" tauri-build = "2"
@ -20,8 +21,9 @@ thiserror = "2"
url = "2" url = "2"
schemars = "0.8" schemars = "0.8"
dunce = "1" dunce = "1"
specta = "=2.0.0-rc.20" specta = "^2.0.0-rc.16"
#tauri-specta = "=2.0.0-rc.11" glob = "0.3"
zbus = "5"
[workspace.package] [workspace.package]
edition = "2021" edition = "2021"

@ -22,13 +22,14 @@ This repo and all plugins require a Rust version of at least **1.77.2**
| [log](plugins/log) | Configurable logging. | ✅ | ✅ | ✅ | ✅ | ✅ | | [log](plugins/log) | Configurable logging. | ✅ | ✅ | ✅ | ✅ | ✅ |
| [nfc](plugins/nfc) | Read and write NFC tags on Android and iOS. | ? | ? | ? | ✅ | ✅ | | [nfc](plugins/nfc) | Read and write NFC tags on Android and iOS. | ? | ? | ? | ✅ | ✅ |
| [notification](plugins/notification) | Send message notifications (brief auto-expiring OS window element) to your user. Can also be used with the Notification Web API. | ✅ | ✅ | ✅ | ✅ | ✅ | | [notification](plugins/notification) | Send message notifications (brief auto-expiring OS window element) to your user. Can also be used with the Notification Web API. | ✅ | ✅ | ✅ | ✅ | ✅ |
| [opener](plugins/opener) | Open files and URLs using their default application. | ✅ | ✅ | ✅ | ? | ? |
| [os](plugins/os) | Read information about the operating system. | ✅ | ✅ | ✅ | ✅ | ✅ | | [os](plugins/os) | Read information about the operating system. | ✅ | ✅ | ✅ | ✅ | ✅ |
| [persisted-scope](plugins/persisted-scope) | Persist runtime scope changes on the filesystem. | ✅ | ✅ | ✅ | ? | ? | | [persisted-scope](plugins/persisted-scope) | Persist runtime scope changes on the filesystem. | ✅ | ✅ | ✅ | ? | ? |
| [positioner](plugins/positioner) | Move windows to common locations. | ✅ | ✅ | ✅ | ❌ | ❌ | | [positioner](plugins/positioner) | Move windows to common locations. | ✅ | ✅ | ✅ | ❌ | ❌ |
| [process](plugins/process) | This plugin provides APIs to access the current process. To spawn child processes, see the [`shell`](https://github.com/tauri-apps/tauri-plugin-shell) plugin. | ✅ | ✅ | ✅ | ? | ? | | [process](plugins/process) | This plugin provides APIs to access the current process. To spawn child processes, see the [`shell`](https://github.com/tauri-apps/tauri-plugin-shell) plugin. | ✅ | ✅ | ✅ | ? | ? |
| [shell](plugins/shell) | Access the system shell. Allows you to spawn child processes and manage files and URLs using their default application. | ✅ | ✅ | ✅ | ? | ? | | [shell](plugins/shell) | Access the system shell. Allows you to spawn child processes and manage files and URLs using their default application. | ✅ | ✅ | ✅ | ? | ? |
| [single-instance](plugins/single-instance) | Ensure a single instance of your tauri app is running. | ✅ | ✅ | ✅ | ❌ | ❌ | | [single-instance](plugins/single-instance) | Ensure a single instance of your tauri app is running. | ✅ | ✅ | ✅ | ❌ | ❌ |
| [sql](plugins/sql) | Interface with SQL databases. | ✅ | ✅ | ✅ | ? | ✅ | | [sql](plugins/sql) | Interface with SQL databases. | ✅ | ✅ | ✅ | | ✅ |
| [store](plugins/store) | Persistent key value storage. | ✅ | ✅ | ✅ | ✅ | ✅ | | [store](plugins/store) | Persistent key value storage. | ✅ | ✅ | ✅ | ✅ | ✅ |
| [stronghold](plugins/stronghold) | Encrypted, secure database. | ✅ | ✅ | ✅ | ? | ? | | [stronghold](plugins/stronghold) | Encrypted, secure database. | ✅ | ✅ | ✅ | ? | ? |
| [updater](plugins/updater) | In-app updates for Tauri applications. | ✅ | ✅ | ✅ | ❌ | ❌ | | [updater](plugins/updater) | In-app updates for Tauri applications. | ✅ | ✅ | ✅ | ❌ | ❌ |

@ -1,5 +1,77 @@
# Changelog # Changelog
## \[2.0.10]
### Dependencies
- Upgraded to `notification-js@2.2.1`
- Upgraded to `opener-js@2.2.4`
## \[2.0.9]
### Dependencies
- Upgraded to `opener-js@2.2.3`
- Upgraded to `updater-js@2.3.1`
## \[2.0.8]
### Dependencies
- Upgraded to `opener-js@2.2.2`
## \[2.0.7]
### Dependencies
- Upgraded to `updater-js@2.3.0`
- Upgraded to `opener-js@2.2.1`
## \[2.0.6]
### Dependencies
- Upgraded to `barcode-scanner-js@2.1.0`
- Upgraded to `biometric-js@2.1.0`
- Upgraded to `cli-js@2.1.0`
- Upgraded to `clipboard-manager-js@2.1.0`
- Upgraded to `dialog-js@2.1.0`
- Upgraded to `fs-js@2.1.0`
- Upgraded to `global-shortcut-js@2.1.0`
- Upgraded to `http-js@2.1.0`
- Upgraded to `log-js@2.1.0`
- Upgraded to `nfc-js@2.1.0`
- Upgraded to `notification-js@2.1.0`
- Upgraded to `opener-js@2.1.0`
- Upgraded to `os-js@2.1.0`
- Upgraded to `process-js@2.1.0`
- Upgraded to `shell-js@2.1.0`
- Upgraded to `store-js@2.2.0`
- Upgraded to `updater-js@2.1.0`
## \[2.0.5]
### Dependencies
- Upgraded to `fs-js@2.0.4`
- Upgraded to `dialog-js@2.0.2`
- Upgraded to `http-js@2.0.2`
## \[2.0.4]
### Dependencies
- Upgraded to `log-js@2.0.2`
## \[2.0.3]
### Dependencies
- Upgraded to `clipboard-manager-js@2.0.1`
- Upgraded to `log-js@2.0.1`
- Upgraded to `fs-js@2.0.3`
- Upgraded to `opener-js@2.0.0`
## \[2.0.2] ## \[2.0.2]
### Dependencies ### Dependencies

@ -1,7 +1,7 @@
{ {
"name": "api", "name": "api",
"private": true, "private": true,
"version": "2.0.2", "version": "2.0.10",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --clearScreen false", "dev": "vite --clearScreen false",
@ -10,34 +10,35 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "2.1.1", "@tauri-apps/api": "2.2.0",
"@tauri-apps/plugin-barcode-scanner": "2.0.0", "@tauri-apps/plugin-barcode-scanner": "^2.2.0",
"@tauri-apps/plugin-biometric": "2.0.0", "@tauri-apps/plugin-biometric": "^2.2.0",
"@tauri-apps/plugin-cli": "2.0.0", "@tauri-apps/plugin-cli": "^2.2.0",
"@tauri-apps/plugin-clipboard-manager": "2.0.0", "@tauri-apps/plugin-clipboard-manager": "^2.2.0",
"@tauri-apps/plugin-dialog": "2.0.1", "@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "2.0.2", "@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-geolocation": "2.0.0", "@tauri-apps/plugin-geolocation": "^2.2.0",
"@tauri-apps/plugin-global-shortcut": "2.0.0", "@tauri-apps/plugin-global-shortcut": "^2.2.0",
"@tauri-apps/plugin-haptics": "2.0.0", "@tauri-apps/plugin-opener": "^2.2.4",
"@tauri-apps/plugin-http": "2.0.1", "@tauri-apps/plugin-haptics": "^2.2.0",
"@tauri-apps/plugin-nfc": "2.0.0", "@tauri-apps/plugin-http": "^2.2.0",
"@tauri-apps/plugin-notification": "2.0.0", "@tauri-apps/plugin-nfc": "^2.2.0",
"@tauri-apps/plugin-os": "2.0.0", "@tauri-apps/plugin-notification": "^2.2.1",
"@tauri-apps/plugin-process": "2.0.0", "@tauri-apps/plugin-os": "^2.2.0",
"@tauri-apps/plugin-shell": "2.0.1", "@tauri-apps/plugin-process": "^2.2.0",
"@tauri-apps/plugin-store": "2.1.0", "@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-updater": "2.0.0", "@tauri-apps/plugin-store": "^2.2.0",
"@tauri-apps/plugin-updater": "^2.2.0",
"@zerodevx/svelte-json-view": "1.0.11" "@zerodevx/svelte-json-view": "1.0.11"
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/codicon": "^1.1.37", "@iconify-json/codicon": "^1.2.6",
"@iconify-json/ph": "^1.1.8", "@iconify-json/ph": "^1.2.1",
"@sveltejs/vite-plugin-svelte": "^4.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.1",
"@tauri-apps/cli": "2.1.0", "@tauri-apps/cli": "2.2.4",
"@unocss/extractor-svelte": "^0.64.0", "@unocss/extractor-svelte": "^65.0.0",
"svelte": "^5.0.0", "svelte": "^5.10.0",
"unocss": "^0.64.0", "unocss": "^65.0.0",
"vite": "^5.4.7" "vite": "^6.0.3"
} }
} }

@ -1,5 +1,90 @@
# Changelog # Changelog
## \[2.0.14]
### Dependencies
- Upgraded to `geolocation@2.2.2`
- Upgraded to `haptics@2.2.2`
- Upgraded to `notification@2.2.1`
- Upgraded to `opener@2.2.4`
## \[2.0.13]
### Dependencies
- Upgraded to `geolocation@2.2.1`
- Upgraded to `haptics@2.2.1`
## \[2.0.12]
### Dependencies
- Upgraded to `opener@2.2.3`
- Upgraded to `updater@2.3.1`
## \[2.0.11]
### Dependencies
- Upgraded to `opener@2.2.2`
## \[2.0.10]
### Dependencies
- Upgraded to `updater@2.3.0`
- Upgraded to `opener@2.2.1`
## \[2.0.9]
### Dependencies
- Upgraded to `barcode-scanner@2.1.0`
- Upgraded to `biometric@2.1.0`
- Upgraded to `cli@2.1.0`
- Upgraded to `clipboard-manager@2.1.0`
- Upgraded to `dialog@2.1.0`
- Upgraded to `fs@2.2.0`
- Upgraded to `geolocation@2.1.0`
- Upgraded to `global-shortcut@2.1.0`
- Upgraded to `haptics@2.1.0`
- Upgraded to `http@2.1.0`
- Upgraded to `log@2.1.0`
- Upgraded to `nfc@2.1.0`
- Upgraded to `notification@2.1.0`
- Upgraded to `opener@2.1.0`
- Upgraded to `os@2.1.0`
- Upgraded to `process@2.1.0`
- Upgraded to `shell@2.1.0`
- Upgraded to `store@2.2.0`
- Upgraded to `updater@2.2.0`
## \[2.0.8]
### Dependencies
- Upgraded to `fs@2.1.1`
- Upgraded to `dialog@2.0.5`
- Upgraded to `http@2.0.5`
## \[2.0.7]
### Dependencies
- Upgraded to `log@2.0.4`
## \[2.0.6]
### Dependencies
- Upgraded to `fs@2.1.0`
- Upgraded to `updater@2.1.0`
- Upgraded to `dialog@2.0.4`
- Upgraded to `log-plugin@2.0.3`
- Upgraded to `http@2.0.4`
- Upgraded to `opener@2.0.0`
## \[2.0.5] ## \[2.0.5]
### Dependencies ### Dependencies

@ -1,7 +1,7 @@
[package] [package]
name = "api" name = "api"
publish = false publish = false
version = "2.0.5" version = "2.0.14"
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 }
@ -19,22 +19,23 @@ serde_json = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
tiny_http = "0.12" tiny_http = "0.12"
log = { workspace = true } log = { workspace = true }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.0.2" } tauri-plugin-log = { path = "../../../plugins/log", version = "2.2.0" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.0.3", features = [ tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.2.0", features = [
"watch", "watch",
] } ] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.0.2" } tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.2.0" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.0.3" } tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.2.0" }
tauri-plugin-http = { path = "../../../plugins/http", features = [ tauri-plugin-http = { path = "../../../plugins/http", features = [
"multipart", "multipart",
], version = "2.0.3" } ], version = "2.2.0" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.0.1", features = [ tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.2.1", features = [
"windows7-compat", "windows7-compat",
] } ] }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.0.1" } tauri-plugin-os = { path = "../../../plugins/os", version = "2.2.0" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.0.1" } tauri-plugin-process = { path = "../../../plugins/process", version = "2.2.0" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.0.2" } tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.2.4" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.1.0" } tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.2.0" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.2.0" }
[dependencies.tauri] [dependencies.tauri]
workspace = true workspace = true
@ -50,17 +51,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.0.1" } tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.2.0" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.0.1" } tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.2.0" }
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.0.2" } tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.3.1" }
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.0.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.0.1" } tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.2.0" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.0.1" } tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.2.0" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.0.1" } tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.2.0" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.0.1" } tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.2.2" }
tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.0.1" } tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.2.2" }
[features] [features]
prod = ["tauri/custom-protocol"] prod = ["tauri/custom-protocol"]

@ -80,6 +80,11 @@
], ],
"deny": ["$APPDATA/db/*.stronghold"] "deny": ["$APPDATA/db/*.stronghold"]
}, },
"store:default" "store:default",
"opener:default",
{
"identifier": "opener:allow-open-path",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
}
] ]
} }

@ -36,6 +36,7 @@ pub fn run() {
.plugin(tauri_plugin_notification::init()) .plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_store::Builder::default().build()) .plugin(tauri_plugin_store::Builder::default().build())
.setup(move |app| { .setup(move |app| {

@ -45,7 +45,7 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
.tooltip("Tauri") .tooltip("Tauri")
.icon(app.default_window_icon().unwrap().clone()) .icon(app.default_window_icon().unwrap().clone())
.menu(&menu1) .menu(&menu1)
.menu_on_left_click(false) .show_menu_on_left_click(false)
.on_menu_event(move |app, event| match event.id.as_ref() { .on_menu_event(move |app, event| match event.id.as_ref() {
"quit" => { "quit" => {
app.exit(0); app.exit(0);

@ -1,6 +1,5 @@
<script> <script>
import { writable } from 'svelte/store' import { writable } from 'svelte/store'
import { open } from '@tauri-apps/plugin-shell'
import { getCurrentWindow } from '@tauri-apps/api/window' import { getCurrentWindow } from '@tauri-apps/api/window'
import { getCurrentWebview } from '@tauri-apps/api/webview' import { getCurrentWebview } from '@tauri-apps/api/webview'
import * as os from '@tauri-apps/plugin-os' import * as os from '@tauri-apps/plugin-os'
@ -14,6 +13,7 @@
import Notifications from './views/Notifications.svelte' import Notifications from './views/Notifications.svelte'
import Shortcuts from './views/Shortcuts.svelte' import Shortcuts from './views/Shortcuts.svelte'
import Shell from './views/Shell.svelte' import Shell from './views/Shell.svelte'
import Opener from './views/Opener.svelte'
import Store from './views/Store.svelte' import Store from './views/Store.svelte'
import Updater from './views/Updater.svelte' import Updater from './views/Updater.svelte'
import Clipboard from './views/Clipboard.svelte' import Clipboard from './views/Clipboard.svelte'
@ -92,6 +92,11 @@
component: Shell, component: Shell,
icon: 'i-codicon-terminal-bash' icon: 'i-codicon-terminal-bash'
}, },
{
label: 'Opener',
component: Opener,
icon: 'i-codicon-link-external'
},
{ {
label: 'Store', label: 'Store',
component: Store, component: Store,

@ -0,0 +1,66 @@
<script>
import * as opener from '@tauri-apps/plugin-opener'
export let onMessage
let url = ''
let urlProgram = ''
function openUrl() {
opener.openUrl(url, urlProgram ? urlProgram : undefined).catch(onMessage)
}
let path = ''
let pathProgram = ''
function openPath() {
opener
.openPath(path, pathProgram ? pathProgram : undefined)
.catch(onMessage)
}
let revealPath = ''
function revealItemInDir() {
opener.revealItemInDir(revealPath).catch(onMessage)
}
</script>
<div class="flex flex-col gap-2">
<form
class="flex flex-row gap-2 items-center"
on:submit|preventDefault={openUrl}
>
<button class="btn" type="submit">Open URL</button>
<input
class="input grow"
placeholder="Type the URL to open..."
bind:value={url}
/>
<span> with </span>
<input class="input" bind:value={urlProgram} />
</form>
<form
class="flex flex-row gap-2 items-center"
on:submit|preventDefault={openPath}
>
<button class="btn" type="submit">Open Path</button>
<input
class="input grow"
placeholder="Type the path to open..."
bind:value={path}
/>
<span> with </span>
<input class="input" bind:value={pathProgram} />
</form>
<form
class="flex flex-row gap-2 items-center"
on:submit|preventDefault={revealItemInDir}
>
<button class="btn" type="submit">Reveal</button>
<input
class="input grow"
placeholder="Type the path to reveal..."
bind:value={revealPath}
/>
</form>
</div>

@ -11,20 +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.14.0", "@eslint/js": "9.18.0",
"@rollup/plugin-node-resolve": "15.3.0", "@rollup/plugin-node-resolve": "16.0.0",
"@rollup/plugin-terser": "0.4.4", "@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "11.1.6", "@rollup/plugin-typescript": "11.1.6",
"@types/eslint__js": "8.42.3", "@types/eslint__js": "8.42.3",
"covector": "^0.12.3", "covector": "^0.12.3",
"eslint": "9.14.0", "eslint": "9.18.0",
"eslint-config-prettier": "9.1.0", "eslint-config-prettier": "10.0.1",
"eslint-plugin-security": "3.0.1", "eslint-plugin-security": "3.0.1",
"prettier": "3.3.3", "prettier": "3.4.2",
"rollup": "4.26.0", "rollup": "4.30.1",
"tslib": "2.8.1", "tslib": "2.8.1",
"typescript": "5.6.3", "typescript": "5.7.3",
"typescript-eslint": "8.14.0" "typescript-eslint": "8.19.1"
}, },
"resolutions": { "resolutions": {
"semver": ">=7.5.2", "semver": ">=7.5.2",

@ -1,5 +1,9 @@
# Changelog # Changelog
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.1] ## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7. - [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@ -88,11 +92,3 @@
## \[2.0.0-alpha.0] ## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release! - [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
te to alpha.11.
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
ae67\`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-autostart" name = "tauri-plugin-autostart"
version = "2.0.1" version = "2.2.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 }
@ -27,6 +27,5 @@ tauri-plugin = { workspace = true, features = ["build"] }
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tauri = { workspace = true } tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
auto-launch = "0.5" auto-launch = "0.5"

@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-autostart#v2
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_autostart::MacosLauncher;

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-autostart", "name": "@tauri-apps/plugin-autostart",
"version": "2.0.0", "version": "2.2.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"

@ -11,8 +11,6 @@
#![cfg(not(any(target_os = "android", target_os = "ios")))] #![cfg(not(any(target_os = "android", target_os = "ios")))]
use auto_launch::{AutoLaunch, AutoLaunchBuilder}; use auto_launch::{AutoLaunch, AutoLaunchBuilder};
#[cfg(target_os = "macos")]
use log::info;
use serde::{ser::Serializer, Serialize}; use serde::{ser::Serializer, Serialize};
use tauri::{ use tauri::{
command, command,
@ -133,7 +131,6 @@ pub fn init<R: Runtime>(
} else { } else {
exe_path exe_path
}; };
info!("auto_start path {}", &app_path);
builder.set_app_path(&app_path); builder.set_app_path(&app_path);
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]

@ -1,5 +1,9 @@
# Changelog # Changelog
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.1] ## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7. - [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-barcode-scanner" name = "tauri-plugin-barcode-scanner"
version = "2.0.1" version = "2.2.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 }

@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-barcode-scanner#v2
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-barcode-scanner", "name": "@tauri-apps/plugin-barcode-scanner",
"version": "2.0.0", "version": "2.2.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": [

@ -1,5 +1,9 @@
# Changelog # Changelog
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.1] ## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7. - [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-biometric" name = "tauri-plugin-biometric"
version = "2.0.1" version = "2.2.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 }

@ -56,7 +56,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-biometric#v2
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-biometric", "name": "@tauri-apps/plugin-biometric",
"version": "2.0.0", "version": "2.2.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"

@ -1,5 +1,9 @@
# Changelog # Changelog
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.1] ## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7. - [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@ -89,10 +93,3 @@
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release! - [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
te to alpha.11. te to alpha.11.
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
ae67\`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
om/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-cli" name = "tauri-plugin-cli"
version = "2.0.1" version = "2.2.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 }

@ -55,7 +55,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-cli#v2
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-cli", "name": "@tauri-apps/plugin-cli",
"version": "2.0.0", "version": "2.2.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"

@ -1,5 +1,13 @@
# Changelog # Changelog
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.1]
- [`3fa0fc09`](https://github.com/tauri-apps/plugins-workspace/commit/3fa0fc09bbee0d619801e5757af9fb3c09883c97) ([#2099](https://github.com/tauri-apps/plugins-workspace/pull/2099) by [@rasteiner](https://github.com/tauri-apps/plugins-workspace/../../rasteiner)) Fix clipboard manager client side api not copying fallback alternative text when calling `writeHtml`.
## \[2.0.2] ## \[2.0.2]
- [`d57df4de`](https://github.com/tauri-apps/plugins-workspace/commit/d57df4debe7c75cfbd6d6558fff1beb07dbee54c) ([#1986](https://github.com/tauri-apps/plugins-workspace/pull/1986) by [@RikaKagurasaka](https://github.com/tauri-apps/plugins-workspace/../../RikaKagurasaka)) Fix that `read_image` wrongly set the image rgba data with binary PNG data. - [`d57df4de`](https://github.com/tauri-apps/plugins-workspace/commit/d57df4debe7c75cfbd6d6558fff1beb07dbee54c) ([#1986](https://github.com/tauri-apps/plugins-workspace/pull/1986) by [@RikaKagurasaka](https://github.com/tauri-apps/plugins-workspace/../../RikaKagurasaka)) Fix that `read_image` wrongly set the image rgba data with binary PNG data.
@ -112,38 +120,3 @@
## \[2.0.0-alpha.0] ## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release! - [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
te to alpha.11.
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
\`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
te to alpha.11.
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
hub.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
\`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
te to alpha.11.
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
te to alpha.11.
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
ps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
om/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-clipboard-manager" name = "tauri-plugin-clipboard-manager"
version = "2.0.2" version = "2.2.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 }

@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-clipboard-manager#v2
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {
@ -72,7 +72,6 @@ import {
writeText, writeText,
readText, readText,
writeHtml, writeHtml,
readHtml,
clear clear
} from '@tauri-apps/plugin-clipboard-manager' } from '@tauri-apps/plugin-clipboard-manager'
await writeText('Tauri is awesome!') await writeText('Tauri is awesome!')

@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var t;async function r(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;class n{get rid(){return function(e,t,r,n){if("a"===r&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}(this,t,"f")}constructor(e){t.set(this,void 0),function(e,t,r,n,a){if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");t.set(e,r)}(this,t,e)}async close(){return r("plugin:resources|close",{rid:this.rid})}}t=new WeakMap;class a extends n{constructor(e){super(e)}static async new(e,t,n){return r("plugin:image|new",{rgba:i(e),width:t,height:n}).then((e=>new a(e)))}static async fromBytes(e){return r("plugin:image|from_bytes",{bytes:i(e)}).then((e=>new a(e)))}static async fromPath(e){return r("plugin:image|from_path",{path:e}).then((e=>new a(e)))}async rgba(){return r("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return r("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await r("plugin:clipboard-manager|clear")},e.readImage=async function(){return await r("plugin:clipboard-manager|read_image").then((e=>new a(e)))},e.readText=async function(){return await r("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,t){await r("plugin:clipboard-manager|write_html",{html:e,altHtml:t})},e.writeImage=async function(e){await r("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,t){await r("plugin:clipboard-manager|write_text",{label:t?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})} if("__TAURI__"in window){var __TAURI_PLUGIN_CLIPBOARD_MANAGER__=function(e){"use strict";var t;async function r(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;class n{get rid(){return function(e,t,r,n){if("a"===r&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}(this,t,"f")}constructor(e){t.set(this,void 0),function(e,t,r){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");t.set(e,r)}(this,t,e)}async close(){return r("plugin:resources|close",{rid:this.rid})}}t=new WeakMap;class a extends n{constructor(e){super(e)}static async new(e,t,n){return r("plugin:image|new",{rgba:i(e),width:t,height:n}).then((e=>new a(e)))}static async fromBytes(e){return r("plugin:image|from_bytes",{bytes:i(e)}).then((e=>new a(e)))}static async fromPath(e){return r("plugin:image|from_path",{path:e}).then((e=>new a(e)))}async rgba(){return r("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return r("plugin:image|size",{rid:this.rid})}}function i(e){return null==e?null:"string"==typeof e?e:e instanceof a?e.rid:e}return e.clear=async function(){await r("plugin:clipboard-manager|clear")},e.readImage=async function(){return await r("plugin:clipboard-manager|read_image").then((e=>new a(e)))},e.readText=async function(){return await r("plugin:clipboard-manager|read_text")},e.writeHtml=async function(e,t){await r("plugin:clipboard-manager|write_html",{html:e,altText:t})},e.writeImage=async function(e){await r("plugin:clipboard-manager|write_image",{image:i(e)})},e.writeText=async function(e,t){await r("plugin:clipboard-manager|write_text",{label:t?.label,text:e})},e}({});Object.defineProperty(window.__TAURI__,"clipboardManager",{value:__TAURI_PLUGIN_CLIPBOARD_MANAGER__})}

@ -65,6 +65,7 @@ async function readText(): Promise<string> {
* 0, 255, 0, 255, * 0, 255, 0, 255,
* ]; * ];
* await writeImage(buffer); * await writeImage(buffer);
* ```
* *
* @returns A promise indicating the success or failure of the operation. * @returns A promise indicating the success or failure of the operation.
* *
@ -90,7 +91,7 @@ async function writeImage(
* import { readImage } from '@tauri-apps/plugin-clipboard-manager'; * import { readImage } from '@tauri-apps/plugin-clipboard-manager';
* *
* const clipboardImage = await readImage(); * const clipboardImage = await readImage();
* const blob = new Blob([clipboardImage.bytes], { type: 'image' }) * const blob = new Blob([await clipboardImage.rbga()], { type: 'image' })
* const url = URL.createObjectURL(blob) * const url = URL.createObjectURL(blob)
* ``` * ```
* @since 2.0.0 * @since 2.0.0
@ -110,9 +111,11 @@ async function readImage(): Promise<Image> {
* *
* @example * @example
* ```typescript * ```typescript
* import { writeHtml, readHtml } from '@tauri-apps/plugin-clipboard-manager'; * import { writeHtml } from '@tauri-apps/plugin-clipboard-manager';
* await writeHtml('<h1>Tauri is awesome!</h1>', 'plaintext'); * await writeHtml('<h1>Tauri is awesome!</h1>', 'plaintext');
* await writeHtml('<h1>Tauri is awesome!</h1>', '<h1>Tauri is awesome</h1>'); // Will write "<h1>Tauri is awesome</h1>" as plain text * // The following will write "<h1>Tauri is awesome</h1>" as plain text
* await writeHtml('<h1>Tauri is awesome!</h1>', '<h1>Tauri is awesome</h1>');
* // we can read html data only as a string so there's just readText(), no readHtml()
* assert(await readText(), '<h1>Tauri is awesome!</h1>'); * assert(await readText(), '<h1>Tauri is awesome!</h1>');
* ``` * ```
* *
@ -120,10 +123,10 @@ async function readImage(): Promise<Image> {
* *
* @since 2.0.0 * @since 2.0.0
*/ */
async function writeHtml(html: string, altHtml?: string): Promise<void> { async function writeHtml(html: string, altText?: string): Promise<void> {
await invoke('plugin:clipboard-manager|write_html', { await invoke('plugin:clipboard-manager|write_html', {
html, html,
altHtml altText
}) })
} }

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-clipboard-manager", "name": "@tauri-apps/plugin-clipboard-manager",
"version": "2.0.0", "version": "2.2.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"

@ -1,5 +1,13 @@
# Changelog # Changelog
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.1]
- [`b2aea045`](https://github.com/tauri-apps/plugins-workspace/commit/b2aea0456799775a7243706fdd7a5abf9a193992) ([#2008](https://github.com/tauri-apps/plugins-workspace/pull/2008) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) `onOpenUrl()` will now not call `getCurrent()` anymore, matching the documented behavior.
## \[2.0.1] ## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7. - [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-deep-link" name = "tauri-plugin-deep-link"
version = "2.0.1" version = "2.2.0"
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 }
@ -32,14 +32,14 @@ serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tauri = { workspace = true } tauri = { workspace = true }
tauri-utils = { workspace = true } tauri-utils = { workspace = true }
log = { workspace = true } tracing = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
url = { workspace = true } url = { workspace = true }
[target."cfg(windows)".dependencies] [target."cfg(windows)".dependencies]
dunce = "1" dunce = "1"
windows-registry = "0.3" windows-registry = "0.4"
windows-result = "0.2" windows-result = "0.3"
[target."cfg(target_os = \"linux\")".dependencies] [target."cfg(target_os = \"linux\")".dependencies]
rust-ini = "0.21" rust-ini = "0.21"

@ -133,7 +133,7 @@ Under `tauri.conf.json > plugins > deep-link`, configure the domains (mobile) an
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {

@ -1,5 +1,17 @@
# Changelog # Changelog
## \[2.2.0]
### Dependencies
- Upgraded to `deep-link-js@2.1.0`
## \[2.0.1]
### Dependencies
- Upgraded to `deep-link-js@2.0.1`
## \[2.0.0] ## \[2.0.0]
- [`e2c4dfb6`](https://github.com/tauri-apps/plugins-workspace/commit/e2c4dfb6af43e5dd8d9ceba232c315f5febd55c1) Update to tauri v2 stable release. - [`e2c4dfb6`](https://github.com/tauri-apps/plugins-workspace/commit/e2c4dfb6af43e5dd8d9ceba232c315f5febd55c1) Update to tauri v2 stable release.

@ -1,7 +1,7 @@
{ {
"name": "deep-link-example", "name": "deep-link-example",
"private": true, "private": true,
"version": "2.0.0", "version": "2.2.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@ -10,12 +10,12 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "2.1.1", "@tauri-apps/api": "2.2.0",
"@tauri-apps/plugin-deep-link": "2.0.0" "@tauri-apps/plugin-deep-link": "2.2.0"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.1.0", "@tauri-apps/cli": "2.2.4",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^5.4.7" "vite": "^6.0.0"
} }
} }

@ -73,7 +73,7 @@ export async function unregister(protocol: string): Promise<null> {
* await isRegistered("my-scheme"); * await isRegistered("my-scheme");
* ``` * ```
* *
* #### - **macOS / Android / iOS**: Unsupported, always returns `true`. * #### - **macOS / Android / iOS**: Unsupported.
* *
* @since 2.0.0 * @since 2.0.0
*/ */
@ -92,7 +92,7 @@ export async function isRegistered(protocol: string): Promise<boolean> {
* await onOpenUrl((urls) => { console.log(urls) }); * await onOpenUrl((urls) => { console.log(urls) });
* ``` * ```
* *
* #### - **Windows / Linux**: Unsupported, the OS will spawn a new app instance passing the URL as a CLI argument. * #### - **Windows / Linux**: Unsupported without the single-instance plugin. The OS will spawn a new app instance passing the URL as a CLI argument.
* *
* @since 2.0.0 * @since 2.0.0
*/ */

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-deep-link", "name": "@tauri-apps/plugin-deep-link",
"version": "2.0.0", "version": "2.2.0",
"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": [

@ -215,7 +215,7 @@ mod imp {
current.replace(vec![url.clone()]); current.replace(vec![url.clone()]);
let _ = self.app.emit("deep-link://new-url", vec![url]); let _ = self.app.emit("deep-link://new-url", vec![url]);
} else if cfg!(debug_assertions) { } else if cfg!(debug_assertions) {
log::warn!("argument {url} does not match any configured deep link scheme; skipping it"); tracing::warn!("argument {url} does not match any configured deep link scheme; skipping it");
} }
} }
} }

@ -1,5 +1,27 @@
# Changelog # Changelog
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
### Dependencies
- Upgraded to `fs-js@2.1.0`
## \[2.0.2]
### Dependencies
- Upgraded to `fs-js@2.0.4`
## \[2.0.4]
- [`76f99ce9`](https://github.com/tauri-apps/plugins-workspace/commit/76f99ce999a2ff9e40235c1675e3eb6570b5e1e2) ([#2108](https://github.com/tauri-apps/plugins-workspace/pull/2108) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) The `Dialog` struct is now correctly exported, primarily to fix the documentation on `docs.rs`.
### Dependencies
- Upgraded to `fs@2.1.0`
## \[2.0.3] ## \[2.0.3]
### Dependencies ### Dependencies
@ -216,88 +238,3 @@
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release! - [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
d6e80b)([#545](https://github.com/tauri-apps/plugins-workspace/pull/545)) Fixes docs.rs build by enabling the `tauri/dox` feature flag. d6e80b)([#545](https://github.com/tauri-apps/plugins-workspace/pull/545)) Fixes docs.rs build by enabling the `tauri/dox` feature flag.
- [`d74fc0a`](https://github.com/tauri-apps/plugins-workspace/commit/d74fc0a097996e90a37be8f57d50b7d1f6ca616f)([#555](https://github.com/tauri-apps/plugins-workspace/pull/555)) Update to alpha.11.
### Dependencies
- Upgraded to `fs@2.0.0-alpha.1`
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
\`
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
ri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
\`
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
hub.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
ri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
\`
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
alpha release!
pull/371)) First v2 alpha release!
ri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
\`
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
kspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
71]\(https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
kspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
7ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
71]\(https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
kspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lpha release!
pull/371)) First v2 alpha release!
lease!
pull/371)) First v2 alpha release!

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-dialog" name = "tauri-plugin-dialog"
version = "2.0.3" version = "2.2.0"
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 }
@ -34,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.0.3" } tauri-plugin-fs = { path = "../fs", version = "2.2.0" }
[target.'cfg(target_os = "ios")'.dependencies] [target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] } tauri = { workspace = true, features = ["wry"] }

@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-dialog#v2
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-dialog", "name": "@tauri-apps/plugin-dialog",
"version": "2.0.1", "version": "2.2.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"

@ -143,7 +143,7 @@ pub(crate) async fn open<R: Runtime>(
for folder in folders { for folder in folders {
if let Ok(path) = folder.clone().into_path() { if let Ok(path) = folder.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_directory(&path, options.recursive); s.allow_directory(&path, options.recursive)?;
} }
tauri_scope.allow_directory(&path, options.directory)?; tauri_scope.allow_directory(&path, options.directory)?;
} }
@ -157,7 +157,7 @@ pub(crate) async fn open<R: Runtime>(
if let Some(folder) = &folder { if let Some(folder) = &folder {
if let Ok(path) = folder.clone().into_path() { if let Ok(path) = folder.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_directory(&path, options.recursive); s.allow_directory(&path, options.recursive)?;
} }
tauri_scope.allow_directory(&path, options.directory)?; tauri_scope.allow_directory(&path, options.directory)?;
} }
@ -175,7 +175,7 @@ pub(crate) async fn open<R: Runtime>(
for file in files { for file in files {
if let Ok(path) = file.clone().into_path() { if let Ok(path) = file.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_file(&path); s.allow_file(&path)?;
} }
tauri_scope.allow_file(&path)?; tauri_scope.allow_file(&path)?;
@ -190,7 +190,7 @@ pub(crate) async fn open<R: Runtime>(
if let Some(file) = &file { if let Some(file) = &file {
if let Ok(path) = file.clone().into_path() { if let Ok(path) = file.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_file(&path); s.allow_file(&path)?;
} }
tauri_scope.allow_file(&path)?; tauri_scope.allow_file(&path)?;
} }
@ -232,7 +232,7 @@ pub(crate) async fn save<R: Runtime>(
if let Some(p) = &path { if let Some(p) = &path {
if let Ok(path) = p.clone().into_path() { if let Ok(path) = p.clone().into_path() {
if let Some(s) = window.try_fs_scope() { if let Some(s) = window.try_fs_scope() {
s.allow_file(&path); s.allow_file(&path)?;
} }
tauri_scope.allow_file(&path)?; tauri_scope.allow_file(&path)?;
} }

@ -39,6 +39,11 @@ use desktop::*;
#[cfg(mobile)] #[cfg(mobile)]
use mobile::*; use mobile::*;
#[cfg(desktop)]
pub use desktop::Dialog;
#[cfg(mobile)]
pub use mobile::Dialog;
pub(crate) const OK: &str = "Ok"; pub(crate) const OK: &str = "Ok";
pub(crate) const CANCEL: &str = "Cancel"; pub(crate) const CANCEL: &str = "Cancel";
pub(crate) const YES: &str = "Yes"; pub(crate) const YES: &str = "Yes";

@ -1,5 +1,19 @@
# Changelog # Changelog
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.4]
- [`77b85507`](https://github.com/tauri-apps/plugins-workspace/commit/77b855074aad612f2b28e6a3b5881fac767a05ae) ([#2171](https://github.com/tauri-apps/plugins-workspace/pull/2171) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed docs.rs build.
## \[2.0.3]
- [`ed981027`](https://github.com/tauri-apps/plugins-workspace/commit/ed981027dd4fba7d0e2f836eb5db34d344388d73) ([#1962](https://github.com/tauri-apps/plugins-workspace/pull/1962) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Improve performance of `readTextFile` and `readTextFileLines` APIs
- [`3e78173d`](https://github.com/tauri-apps/plugins-workspace/commit/3e78173df9ce90aa3b19e1f36d1f8712c5020fb6) ([#2018](https://github.com/tauri-apps/plugins-workspace/pull/2018) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Fix `readDir` function failing to read directories that contain broken symlinks.
- [`5092ea5e`](https://github.com/tauri-apps/plugins-workspace/commit/5092ea5e89817c0550d09b0a4ad17bf1253b23df) ([#1964](https://github.com/tauri-apps/plugins-workspace/pull/1964) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Add support for using `ReadableStream<Unit8Array>` with `writeFile` API.
## \[2.0.2] ## \[2.0.2]
- [`77149dc4`](https://github.com/tauri-apps/plugins-workspace/commit/77149dc4320d26b413e4a6bbe82c654367c51b32) ([#1965](https://github.com/tauri-apps/plugins-workspace/pull/1965) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Fix `writeTextFile` converting UTF-8 characters (for example `äöü`) in the given path into replacement character (`<60>`) - [`77149dc4`](https://github.com/tauri-apps/plugins-workspace/commit/77149dc4320d26b413e4a6bbe82c654367c51b32) ([#1965](https://github.com/tauri-apps/plugins-workspace/pull/1965) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Fix `writeTextFile` converting UTF-8 characters (for example `äöü`) in the given path into replacement character (`<60>`)
@ -161,33 +175,3 @@
## \[2.0.0-alpha.0] ## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release! - [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
/pull/454)) Fix `writeBinaryFile` crashing with `command 'write_binary_file' not found`
- [`d74fc0a`](https://github.com/tauri-apps/plugins-workspace/commit/d74fc0a097996e90a37be8f57d50b7d1f6ca616f)([#555](https://github.com/tauri-apps/plugins-workspace/pull/555)) Update to alpha.11.
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
ae67\`]\(https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
ac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
s/plugins-workspace/pull/371)) First v2 alpha release!
ac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
kspace/pull/371)) First v2 alpha release!
s/plugins-workspace/pull/371)) First v2 alpha release!
ac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
uri-apps/plugins-workspace/pull/371)) First v2 alpha release!
.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
kspace/pull/371)) First v2 alpha release!
s/plugins-workspace/pull/371)) First v2 alpha release!
ac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-fs" name = "tauri-plugin-fs"
version = "2.0.3" version = "2.2.0"
description = "Access the file system." description = "Access the file system."
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
@ -14,7 +14,7 @@ rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.platforms.support] [package.metadata.platforms.support]
windows = { level = "full", notes = "" } windows = { level = "full", notes = "Apps installed via MSI or NSIS in `perMachine` and `both` mode require admin permissions for write acces in `$RESOURCES` folder" }
linux = { level = "full", notes = "No write access to `$RESOURCES` folder" } linux = { level = "full", notes = "No write access to `$RESOURCES` folder" }
macos = { level = "full", notes = "No write access to `$RESOURCES` folder" } macos = { level = "full", notes = "No write access to `$RESOURCES` folder" }
android = { level = "partial", notes = "Access is restricted to Application folder by default" } android = { level = "partial", notes = "Access is restricted to Application folder by default" }
@ -24,6 +24,8 @@ ios = { level = "partial", notes = "Access is restricted to Application folder b
tauri-plugin = { workspace = true, features = ["build"] } tauri-plugin = { workspace = true, features = ["build"] }
schemars = { workspace = true } schemars = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
toml = "0.8"
tauri-utils = { workspace = true, features = ["build"] }
[dependencies] [dependencies]
serde = { workspace = true } serde = { workspace = true }
@ -34,13 +36,13 @@ thiserror = { workspace = true }
url = { workspace = true } url = { workspace = true }
anyhow = "1" anyhow = "1"
uuid = { version = "1", features = ["v4"] } uuid = { version = "1", features = ["v4"] }
glob = "0.3" glob = { workspace = true }
# TODO: Remove `serialization-compat-6` in v3 # TODO: Remove `serialization-compat-6` in v3
notify = { version = "7", optional = true, features = [ notify = { version = "8", optional = true, features = [
"serde", "serde",
"serialization-compat-6", "serialization-compat-6",
] } ] }
notify-debouncer-full = { version = "0.4", optional = true } notify-debouncer-full = { version = "0.5", optional = true }
dunce = { workspace = true } dunce = { workspace = true }
percent-encoding = "2" percent-encoding = "2"

@ -54,7 +54,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-fs#v2
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {

File diff suppressed because one or more lines are too long

@ -7,6 +7,8 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use tauri_utils::acl::manifest::PermissionFile;
#[path = "src/scope.rs"] #[path = "src/scope.rs"]
#[allow(dead_code)] #[allow(dead_code)]
mod scope; mod scope;
@ -16,10 +18,23 @@ mod scope;
#[serde(untagged)] #[serde(untagged)]
#[allow(unused)] #[allow(unused)]
enum FsScopeEntry { enum FsScopeEntry {
/// FS scope path. /// A path that can be accessed by the webview when using the fs APIs.
/// FS scope path pattern.
///
/// The pattern can start with a variable that resolves to a system base directory.
/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
Value(PathBuf), Value(PathBuf),
Object { Object {
/// FS scope path. /// A path that can be accessed by the webview when using the fs APIs.
///
/// The pattern can start with a variable that resolves to a system base directory.
/// The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`,
/// `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`,
/// `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`,
/// `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.
path: PathBuf, path: PathBuf,
}, },
} }
@ -62,31 +77,32 @@ const BASE_DIR_VARS: &[&str] = &[
"APPCACHE", "APPCACHE",
"APPLOG", "APPLOG",
]; ];
const COMMANDS: &[&str] = &[ const COMMANDS: &[(&str, &[&str])] = &[
"mkdir", ("mkdir", &[]),
"create", ("create", &[]),
"copy_file", ("copy_file", &[]),
"remove", ("remove", &[]),
"rename", ("rename", &[]),
"truncate", ("truncate", &[]),
"ftruncate", ("ftruncate", &[]),
"write", ("write", &[]),
"write_file", ("write_file", &["open", "write"]),
"write_text_file", ("write_text_file", &[]),
"read_dir", ("read_dir", &[]),
"read_file", ("read_file", &[]),
"read", ("read", &[]),
"open", ("open", &[]),
"read_text_file", ("read_text_file", &[]),
"read_text_file_lines", ("read_text_file_lines", &["read_text_file_lines_next"]),
"read_text_file_lines_next", ("read_text_file_lines_next", &[]),
"seek", ("seek", &[]),
"stat", ("stat", &[]),
"lstat", ("lstat", &[]),
"fstat", ("fstat", &[]),
"exists", ("exists", &[]),
"watch", ("watch", &[]),
"unwatch", ("unwatch", &[]),
("size", &[]),
]; ];
fn main() { fn main() {
@ -192,9 +208,59 @@ permissions = [
} }
} }
tauri_plugin::Builder::new(COMMANDS) tauri_plugin::Builder::new(
.global_api_script_path("./api-iife.js") &COMMANDS
.global_scope_schema(schemars::schema_for!(FsScopeEntry)) .iter()
.android_path("android") // FIXME: https://docs.rs/crate/tauri-plugin-fs/2.1.0/builds/1571296
.build(); .filter(|c| c.1.is_empty())
.map(|c| c.0)
.collect::<Vec<_>>(),
)
.global_api_script_path("./api-iife.js")
.global_scope_schema(schemars::schema_for!(FsScopeEntry))
.android_path("android")
.build();
// workaround to include nested permissions as `tauri_plugin` doesn't support it
let permissions_dir = autogenerated.join("commands");
for (command, nested_commands) in COMMANDS {
if nested_commands.is_empty() {
continue;
}
let permission_path = permissions_dir.join(format!("{command}.toml"));
let content = std::fs::read_to_string(&permission_path)
.unwrap_or_else(|_| panic!("failed to read {command}.toml"));
let mut permission_file = toml::from_str::<PermissionFile>(&content)
.unwrap_or_else(|_| panic!("failed to deserialize {command}.toml"));
for p in permission_file
.permission
.iter_mut()
.filter(|p| p.identifier.starts_with("allow"))
{
for c in nested_commands.iter().map(|s| s.to_string()) {
if !p.commands.allow.contains(&c) {
p.commands.allow.push(c);
}
}
}
let out = toml::to_string_pretty(&permission_file)
.unwrap_or_else(|_| panic!("failed to serialize {command}.toml"));
let out = format!(
r#"# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
{out}"#
);
if content != out {
std::fs::write(permission_path, out)
.unwrap_or_else(|_| panic!("failed to write {command}.toml"));
}
}
} }

@ -10,7 +10,7 @@
* This module prevents path traversal, not allowing parent directory accessors to be used * This module prevents path traversal, not allowing parent directory accessors to be used
* (i.e. "/usr/path/to/../file" or "../path/to/file" paths are not allowed). * (i.e. "/usr/path/to/../file" or "../path/to/file" paths are not allowed).
* Paths accessed with this API must be either relative to one of the {@link BaseDirectory | base directories} * Paths accessed with this API must be either relative to one of the {@link BaseDirectory | base directories}
* or created with the {@link https://v2.tauri.app/reference/javascript/api/namespacepath | path API}. * or created with the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/ | path API}.
* *
* The API has a scope configuration that forces you to restrict the paths that can be accessed using glob patterns. * The API has a scope configuration that forces you to restrict the paths that can be accessed using glob patterns.
* *
@ -266,6 +266,7 @@ function fromBytes(buffer: FixedSizeArray<number, 8>): number {
const size = bytes.byteLength const size = bytes.byteLength
let x = 0 let x = 0
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
// eslint-disable-next-line security/detect-object-injection
const byte = bytes[i] const byte = bytes[i]
x *= 0x100 x *= 0x100
x += byte x += byte
@ -427,11 +428,11 @@ class FileHandle extends Resource {
} }
/** /**
* Writes `p.byteLength` bytes from `p` to the underlying data stream. It * Writes `data.byteLength` bytes from `data` to the underlying data stream. It
* resolves to the number of bytes written from `p` (`0` <= `n` <= * resolves to the number of bytes written from `data` (`0` <= `n` <=
* `p.byteLength`) or reject with the error encountered that caused the * `data.byteLength`) or reject with the error encountered that caused the
* write to stop early. `write()` must reject with a non-null error if * write to stop early. `write()` must reject with a non-null error if
* would resolve to `n` < `p.byteLength`. `write()` must not modify the * would resolve to `n` < `data.byteLength`. `write()` must not modify the
* slice data, even temporarily. * slice data, even temporarily.
* *
* @example * @example
@ -769,10 +770,14 @@ async function readTextFile(
throw new TypeError('Must be a file URL.') throw new TypeError('Must be a file URL.')
} }
return await invoke<string>('plugin:fs|read_text_file', { const arr = await invoke<ArrayBuffer | number[]>('plugin:fs|read_text_file', {
path: path instanceof URL ? path.toString() : path, path: path instanceof URL ? path.toString() : path,
options options
}) })
const bytes = arr instanceof ArrayBuffer ? arr : Uint8Array.from(arr)
return new TextDecoder().decode(bytes)
} }
/** /**
@ -803,6 +808,7 @@ async function readTextFileLines(
return await Promise.resolve({ return await Promise.resolve({
path: pathStr, path: pathStr,
rid: null as number | null, rid: null as number | null,
async next(): Promise<IteratorResult<string>> { async next(): Promise<IteratorResult<string>> {
if (this.rid === null) { if (this.rid === null) {
this.rid = await invoke<number>('plugin:fs|read_text_file_lines', { this.rid = await invoke<number>('plugin:fs|read_text_file_lines', {
@ -811,19 +817,35 @@ async function readTextFileLines(
}) })
} }
const [line, done] = await invoke<[string | null, boolean]>( const arr = await invoke<ArrayBuffer | number[]>(
'plugin:fs|read_text_file_lines_next', 'plugin:fs|read_text_file_lines_next',
{ rid: this.rid } { rid: this.rid }
) )
// an iteration is over, reset rid for next iteration const bytes =
if (done) this.rid = null arr instanceof ArrayBuffer ? new Uint8Array(arr) : Uint8Array.from(arr)
// Rust side will never return an empty array for this command and
// ensure there is at least one elements there.
//
// This is an optimization to include whether we finished iteration or not (1 or 0)
// at the end of returned array to avoid serialization overhead of separate values.
const done = bytes[bytes.byteLength - 1] === 1
if (done) {
// a full iteration is over, reset rid for next iteration
this.rid = null
return { value: null, done }
}
const line = new TextDecoder().decode(bytes.slice(0, bytes.byteLength))
return { return {
value: done ? '' : line!, value: line,
done done
} }
}, },
[Symbol.asyncIterator](): AsyncIterableIterator<string> { [Symbol.asyncIterator](): AsyncIterableIterator<string> {
return this return this
} }
@ -1044,19 +1066,27 @@ interface WriteFileOptions {
*/ */
async function writeFile( async function writeFile(
path: string | URL, path: string | URL,
data: Uint8Array, data: Uint8Array | ReadableStream<Uint8Array>,
options?: WriteFileOptions options?: WriteFileOptions
): Promise<void> { ): Promise<void> {
if (path instanceof URL && path.protocol !== 'file:') { if (path instanceof URL && path.protocol !== 'file:') {
throw new TypeError('Must be a file URL.') throw new TypeError('Must be a file URL.')
} }
await invoke('plugin:fs|write_file', data, { if (data instanceof ReadableStream) {
headers: { const file = await open(path, options)
path: encodeURIComponent(path instanceof URL ? path.toString() : path), for await (const chunk of data) {
options: JSON.stringify(options) await file.write(chunk)
} }
}) await file.close()
} else {
await invoke('plugin:fs|write_file', data, {
headers: {
path: encodeURIComponent(path instanceof URL ? path.toString() : path),
options: JSON.stringify(options)
}
})
}
} }
/** /**
@ -1292,6 +1322,31 @@ async function watchImmediate(
} }
} }
/**
* Get the size of a file or directory. For files, the `stat` functions can be used as well.
*
* If `path` is a directory, this function will recursively iterate over every file and every directory inside of `path` and therefore will be very time consuming if used on larger directories.
*
* @example
* ```typescript
* import { size, BaseDirectory } from '@tauri-apps/plugin-fs';
* // Get the size of the `$APPDATA/tauri` directory.
* const dirSize = await size('tauri', { baseDir: BaseDirectory.AppData });
* console.log(dirSize); // 1024
* ```
*
* @since 2.1.0
*/
async function size(path: string | URL): Promise<number> {
if (path instanceof URL && path.protocol !== 'file:') {
throw new TypeError('Must be a file URL.')
}
return await invoke('plugin:fs|size', {
path: path instanceof URL ? path.toString() : path
})
}
export type { export type {
CreateOptions, CreateOptions,
OpenOptions, OpenOptions,
@ -1339,5 +1394,6 @@ export {
writeTextFile, writeTextFile,
exists, exists,
watch, watch,
watchImmediate watchImmediate,
size
} }

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-fs", "name": "@tauri-apps/plugin-fs",
"version": "2.0.2", "version": "2.2.0",
"description": "Access the file system.", "description": "Access the file system.",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [

@ -5,9 +5,18 @@
[[permission]] [[permission]]
identifier = "allow-read-text-file-lines" identifier = "allow-read-text-file-lines"
description = "Enables the read_text_file_lines command without any pre-configured scope." description = "Enables the read_text_file_lines command without any pre-configured scope."
commands.allow = ["read_text_file_lines"]
[permission.commands]
allow = [
"read_text_file_lines",
"read_text_file_lines_next",
]
deny = []
[[permission]] [[permission]]
identifier = "deny-read-text-file-lines" identifier = "deny-read-text-file-lines"
description = "Denies the read_text_file_lines command without any pre-configured scope." description = "Denies the read_text_file_lines command without any pre-configured scope."
commands.deny = ["read_text_file_lines"]
[permission.commands]
allow = []
deny = ["read_text_file_lines"]

@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-size"
description = "Enables the size command without any pre-configured scope."
commands.allow = ["size"]
[[permission]]
identifier = "deny-size"
description = "Denies the size command without any pre-configured scope."
commands.deny = ["size"]

@ -5,9 +5,19 @@
[[permission]] [[permission]]
identifier = "allow-write-file" identifier = "allow-write-file"
description = "Enables the write_file command without any pre-configured scope." description = "Enables the write_file command without any pre-configured scope."
commands.allow = ["write_file"]
[permission.commands]
allow = [
"write_file",
"open",
"write",
]
deny = []
[[permission]] [[permission]]
identifier = "deny-write-file" identifier = "deny-write-file"
description = "Denies the write_file command without any pre-configured scope." description = "Denies the write_file command without any pre-configured scope."
commands.deny = ["write_file"]
[permission.commands]
allow = []
deny = ["write_file"]

@ -3410,6 +3410,32 @@ Denies the seek command without any pre-configured scope.
<tr> <tr>
<td> <td>
`fs:allow-size`
</td>
<td>
Enables the size command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`fs:deny-size`
</td>
<td>
Denies the size command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`fs:allow-stat` `fs:allow-stat`
</td> </td>

@ -3,4 +3,4 @@
[[permission]] [[permission]]
identifier = "read-meta" identifier = "read-meta"
description = "This enables all index or metadata related commands without any pre-configured accessible paths." description = "This enables all index or metadata related commands without any pre-configured accessible paths."
commands.allow = ["read_dir", "stat", "lstat", "fstat", "exists"] commands.allow = ["read_dir", "stat", "lstat", "fstat", "exists", "size"]

@ -1589,6 +1589,16 @@
"type": "string", "type": "string",
"const": "deny-seek" "const": "deny-seek"
}, },
{
"description": "Enables the size command without any pre-configured scope.",
"type": "string",
"const": "allow-size"
},
{
"description": "Denies the size command without any pre-configured scope.",
"type": "string",
"const": "deny-size"
},
{ {
"description": "Enables the stat command without any pre-configured scope.", "description": "Enables the stat command without any pre-configured scope.",
"type": "string", "type": "string",

@ -15,14 +15,14 @@ use tauri::{
use std::{ use std::{
borrow::Cow, borrow::Cow,
fs::File, fs::File,
io::{BufReader, Lines, Read, Write}, io::{BufRead, BufReader, Read, Write},
path::PathBuf, path::{Path, PathBuf},
str::FromStr, str::FromStr,
sync::Mutex, sync::Mutex,
time::{SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
}; };
use crate::{scope::Entry, Error, FsExt, SafeFilePath}; use crate::{scope::Entry, Error, SafeFilePath};
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum CommandError { pub enum CommandError {
@ -372,6 +372,7 @@ pub async fn read_file<R: Runtime>(
Ok(tauri::ipc::Response::new(contents)) Ok(tauri::ipc::Response::new(contents))
} }
// TODO, remove in v3, rely on `read_file` command instead
#[tauri::command] #[tauri::command]
pub async fn read_text_file<R: Runtime>( pub async fn read_text_file<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
@ -379,33 +380,8 @@ pub async fn read_text_file<R: Runtime>(
command_scope: CommandScope<Entry>, command_scope: CommandScope<Entry>,
path: SafeFilePath, path: SafeFilePath,
options: Option<BaseOptions>, options: Option<BaseOptions>,
) -> CommandResult<String> { ) -> CommandResult<tauri::ipc::Response> {
let (mut file, path) = resolve_file( read_file(webview, global_scope, command_scope, path, options).await
&webview,
&global_scope,
&command_scope,
path,
OpenOptions {
base: BaseOptions {
base_dir: options.as_ref().and_then(|o| o.base_dir),
},
options: crate::OpenOptions {
read: true,
..Default::default()
},
},
)?;
let mut contents = String::new();
file.read_to_string(&mut contents).map_err(|e| {
format!(
"failed to read file as text at path: {} with error: {e}",
path.display()
)
})?;
Ok(contents)
} }
#[tauri::command] #[tauri::command]
@ -416,8 +392,6 @@ pub fn read_text_file_lines<R: Runtime>(
path: SafeFilePath, path: SafeFilePath,
options: Option<BaseOptions>, options: Option<BaseOptions>,
) -> CommandResult<ResourceId> { ) -> CommandResult<ResourceId> {
use std::io::BufRead;
let resolved_path = resolve_path( let resolved_path = resolve_path(
&webview, &webview,
&global_scope, &global_scope,
@ -433,7 +407,7 @@ pub fn read_text_file_lines<R: Runtime>(
) )
})?; })?;
let lines = BufReader::new(file).lines(); let lines = BufReader::new(file);
let rid = webview.resources_table().add(StdLinesResource::new(lines)); let rid = webview.resources_table().add(StdLinesResource::new(lines));
Ok(rid) Ok(rid)
@ -443,18 +417,28 @@ pub fn read_text_file_lines<R: Runtime>(
pub async fn read_text_file_lines_next<R: Runtime>( pub async fn read_text_file_lines_next<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
rid: ResourceId, rid: ResourceId,
) -> CommandResult<(Option<String>, bool)> { ) -> CommandResult<tauri::ipc::Response> {
let mut resource_table = webview.resources_table(); let mut resource_table = webview.resources_table();
let lines = resource_table.get::<StdLinesResource>(rid)?; let lines = resource_table.get::<StdLinesResource>(rid)?;
let ret = StdLinesResource::with_lock(&lines, |lines| { let ret = StdLinesResource::with_lock(&lines, |lines| -> CommandResult<Vec<u8>> {
lines.next().map(|a| (a.ok(), false)).unwrap_or_else(|| { // This is an optimization to include wether we finished iteration or not (1 or 0)
let _ = resource_table.close(rid); // at the end of returned vector so we can use `tauri::ipc::Response`
(None, true) // and avoid serialization overhead of separate values.
}) match lines.next() {
Some(Ok(mut bytes)) => {
bytes.push(false as u8);
Ok(bytes)
}
Some(Err(_)) => Ok(vec![false as u8]),
None => {
resource_table.close(rid)?;
Ok(vec![true as u8])
}
}
}); });
Ok(ret) ret.map(tauri::ipc::Response::new)
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
@ -805,10 +789,11 @@ fn default_create_value() -> bool {
true true
} }
fn write_file_inner<R: Runtime>( #[tauri::command]
pub async fn write_file<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
global_scope: &GlobalScope<Entry>, global_scope: GlobalScope<Entry>,
command_scope: &CommandScope<Entry>, command_scope: CommandScope<Entry>,
request: tauri::ipc::Request<'_>, request: tauri::ipc::Request<'_>,
) -> CommandResult<()> { ) -> CommandResult<()> {
let data = match request.body() { let data = match request.body() {
@ -839,8 +824,8 @@ fn write_file_inner<R: Runtime>(
let (mut file, path) = resolve_file( let (mut file, path) = resolve_file(
&webview, &webview,
global_scope, &global_scope,
command_scope, &command_scope,
path, path,
if let Some(opts) = options { if let Some(opts) = options {
OpenOptions { OpenOptions {
@ -883,35 +868,43 @@ fn write_file_inner<R: Runtime>(
.map_err(Into::into) .map_err(Into::into)
} }
// TODO, remove in v3, rely on `write_file` command instead
#[tauri::command] #[tauri::command]
pub async fn write_file<R: Runtime>( pub async fn write_text_file<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
global_scope: GlobalScope<Entry>, global_scope: GlobalScope<Entry>,
command_scope: CommandScope<Entry>, command_scope: CommandScope<Entry>,
request: tauri::ipc::Request<'_>, request: tauri::ipc::Request<'_>,
) -> CommandResult<()> { ) -> CommandResult<()> {
write_file_inner(webview, &global_scope, &command_scope, request) write_file(webview, global_scope, command_scope, request).await
} }
// TODO, in v3, remove this command and rely on `write_file` command only
#[tauri::command] #[tauri::command]
pub async fn write_text_file<R: Runtime>( pub fn exists<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
global_scope: GlobalScope<Entry>, global_scope: GlobalScope<Entry>,
command_scope: CommandScope<Entry>, command_scope: CommandScope<Entry>,
request: tauri::ipc::Request<'_>, path: SafeFilePath,
) -> CommandResult<()> { options: Option<BaseOptions>,
write_file_inner(webview, &global_scope, &command_scope, request) ) -> CommandResult<bool> {
let resolved_path = resolve_path(
&webview,
&global_scope,
&command_scope,
path,
options.as_ref().and_then(|o| o.base_dir),
)?;
Ok(resolved_path.exists())
} }
#[tauri::command] #[tauri::command]
pub fn exists<R: Runtime>( pub async fn size<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
global_scope: GlobalScope<Entry>, global_scope: GlobalScope<Entry>,
command_scope: CommandScope<Entry>, command_scope: CommandScope<Entry>,
path: SafeFilePath, path: SafeFilePath,
options: Option<BaseOptions>, options: Option<BaseOptions>,
) -> CommandResult<bool> { ) -> CommandResult<u64> {
let resolved_path = resolve_path( let resolved_path = resolve_path(
&webview, &webview,
&global_scope, &global_scope,
@ -919,7 +912,38 @@ pub fn exists<R: Runtime>(
path, path,
options.as_ref().and_then(|o| o.base_dir), options.as_ref().and_then(|o| o.base_dir),
)?; )?;
Ok(resolved_path.exists())
let metadata = resolved_path.metadata()?;
if metadata.is_file() {
Ok(metadata.len())
} else {
let size = get_dir_size(&resolved_path).map_err(|e| {
format!(
"failed to get size at path: {} with error: {e}",
resolved_path.display()
)
})?;
Ok(size)
}
}
fn get_dir_size(path: &PathBuf) -> CommandResult<u64> {
let mut size = 0;
for entry in std::fs::read_dir(path)? {
let entry = entry?;
let metadata = entry.metadata()?;
if metadata.is_file() {
size += metadata.len();
} else if metadata.is_dir() {
size += get_dir_size(&entry.path())?;
}
}
Ok(size)
} }
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
@ -967,6 +991,8 @@ pub fn resolve_file<R: Runtime>(
path: SafeFilePath, path: SafeFilePath,
open_options: OpenOptions, open_options: OpenOptions,
) -> CommandResult<(File, PathBuf)> { ) -> CommandResult<(File, PathBuf)> {
use crate::FsExt;
match path { match path {
SafeFilePath::Url(url) => { SafeFilePath::Url(url) => {
let path = url.as_str().into(); let path = url.as_str().into();
@ -999,40 +1025,81 @@ pub fn resolve_path<R: Runtime>(
path path
}; };
let fs_scope = webview.state::<crate::Scope>();
let scope = tauri::scope::fs::Scope::new( let scope = tauri::scope::fs::Scope::new(
webview, webview,
&FsScope::Scope { &FsScope::Scope {
allow: webview allow: global_scope
.fs_scope() .allows()
.allowed .iter()
.lock() .filter_map(|e| e.path.clone())
.unwrap()
.clone()
.into_iter()
.chain(global_scope.allows().iter().filter_map(|e| e.path.clone()))
.chain(command_scope.allows().iter().filter_map(|e| e.path.clone())) .chain(command_scope.allows().iter().filter_map(|e| e.path.clone()))
.collect(), .collect(),
deny: webview deny: global_scope
.fs_scope() .denies()
.denied .iter()
.lock() .filter_map(|e| e.path.clone())
.unwrap()
.clone()
.into_iter()
.chain(global_scope.denies().iter().filter_map(|e| e.path.clone()))
.chain(command_scope.denies().iter().filter_map(|e| e.path.clone())) .chain(command_scope.denies().iter().filter_map(|e| e.path.clone()))
.collect(), .collect(),
require_literal_leading_dot: webview.fs_scope().require_literal_leading_dot, require_literal_leading_dot: fs_scope.require_literal_leading_dot,
}, },
)?; )?;
if scope.is_allowed(&path) { let require_literal_leading_dot = fs_scope.require_literal_leading_dot.unwrap_or(cfg!(unix));
if is_forbidden(&fs_scope.scope, &path, require_literal_leading_dot)
|| is_forbidden(&scope, &path, require_literal_leading_dot)
{
return Err(CommandError::Plugin(Error::PathForbidden(path)));
}
if fs_scope.scope.is_allowed(&path) || scope.is_allowed(&path) {
Ok(path) Ok(path)
} else { } else {
Err(CommandError::Plugin(Error::PathForbidden(path))) Err(CommandError::Plugin(Error::PathForbidden(path)))
} }
} }
fn is_forbidden<P: AsRef<Path>>(
scope: &tauri::fs::Scope,
path: P,
require_literal_leading_dot: bool,
) -> bool {
let path = path.as_ref();
let path = if path.is_symlink() {
match std::fs::read_link(path) {
Ok(p) => p,
Err(_) => return false,
}
} else {
path.to_path_buf()
};
let path = if !path.exists() {
crate::Result::Ok(path)
} else {
std::fs::canonicalize(path).map_err(Into::into)
};
if let Ok(path) = path {
let path: PathBuf = path.components().collect();
scope.forbidden_patterns().iter().any(|p| {
p.matches_path_with(
&path,
glob::MatchOptions {
// this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt`
// see: <https://github.com/tauri-apps/tauri/security/advisories/GHSA-6mv3-wm7j-h4w5>
require_literal_separator: true,
require_literal_leading_dot,
..Default::default()
},
)
})
} else {
false
}
}
struct StdFileResource(Mutex<File>); struct StdFileResource(Mutex<File>);
impl StdFileResource { impl StdFileResource {
@ -1048,14 +1115,38 @@ impl StdFileResource {
impl Resource for StdFileResource {} impl Resource for StdFileResource {}
struct StdLinesResource(Mutex<Lines<BufReader<File>>>); /// Same as [std::io::Lines] but with bytes
struct LinesBytes<T: BufRead>(T);
impl<B: BufRead> Iterator for LinesBytes<B> {
type Item = std::io::Result<Vec<u8>>;
fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
let mut buf = Vec::new();
match self.0.read_until(b'\n', &mut buf) {
Ok(0) => None,
Ok(_n) => {
if buf.last() == Some(&b'\n') {
buf.pop();
if buf.last() == Some(&b'\r') {
buf.pop();
}
}
Some(Ok(buf))
}
Err(e) => Some(Err(e)),
}
}
}
struct StdLinesResource(Mutex<LinesBytes<BufReader<File>>>);
impl StdLinesResource { impl StdLinesResource {
fn new(lines: Lines<BufReader<File>>) -> Self { fn new(lines: BufReader<File>) -> Self {
Self(Mutex::new(lines)) Self(Mutex::new(LinesBytes(lines)))
} }
fn with_lock<R, F: FnMut(&mut Lines<BufReader<File>>) -> R>(&self, mut f: F) -> R { fn with_lock<R, F: FnMut(&mut LinesBytes<BufReader<File>>) -> R>(&self, mut f: F) -> R {
let mut lines = self.0.lock().unwrap(); let mut lines = self.0.lock().unwrap();
f(&mut lines) f(&mut lines)
} }
@ -1154,7 +1245,12 @@ fn get_stat(metadata: std::fs::Metadata) -> FileInfo {
} }
} }
#[cfg(test)]
mod test { mod test {
use std::io::{BufRead, BufReader};
use super::LinesBytes;
#[test] #[test]
fn safe_file_path_parse() { fn safe_file_path_parse() {
use super::SafeFilePath; use super::SafeFilePath;
@ -1168,4 +1264,24 @@ mod test {
Ok(SafeFilePath::Url(_)) Ok(SafeFilePath::Url(_))
)); ));
} }
#[test]
fn test_lines_bytes() {
let base = String::from("line 1\nline2\nline 3\nline 4");
let bytes = base.as_bytes();
let string1 = base.lines().collect::<String>();
let string2 = BufReader::new(bytes)
.lines()
.map_while(Result::ok)
.collect::<String>();
let string3 = LinesBytes(BufReader::new(bytes))
.flatten()
.flat_map(String::from_utf8)
.collect::<String>();
assert_eq!(string1, string2);
assert_eq!(string1, string3);
assert_eq!(string2, string3);
}
} }

@ -138,7 +138,7 @@ impl<'de> serde::Deserialize<'de> for FilePath {
{ {
struct FilePathVisitor; struct FilePathVisitor;
impl<'de> serde::de::Visitor<'de> for FilePathVisitor { impl serde::de::Visitor<'_> for FilePathVisitor {
type Value = FilePath; type Value = FilePath;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
@ -169,7 +169,7 @@ impl<'de> serde::Deserialize<'de> for SafeFilePath {
{ {
struct SafeFilePathVisitor; struct SafeFilePathVisitor;
impl<'de> serde::de::Visitor<'de> for SafeFilePathVisitor { impl serde::de::Visitor<'_> for SafeFilePathVisitor {
type Value = SafeFilePath; type Value = SafeFilePath;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {

@ -15,7 +15,7 @@ use serde::Deserialize;
use tauri::{ use tauri::{
ipc::ScopeObject, ipc::ScopeObject,
plugin::{Builder as PluginBuilder, TauriPlugin}, plugin::{Builder as PluginBuilder, TauriPlugin},
utils::acl::Value, utils::{acl::Value, config::FsScope},
AppHandle, DragDropEvent, Manager, RunEvent, Runtime, WindowEvent, AppHandle, DragDropEvent, Manager, RunEvent, Runtime, WindowEvent,
}; };
@ -39,7 +39,6 @@ pub use desktop::Fs;
pub use mobile::Fs; pub use mobile::Fs;
pub use error::Error; pub use error::Error;
pub use scope::{Event as ScopeEvent, Scope};
pub use file_path::FilePath; pub use file_path::FilePath;
pub use file_path::SafeFilePath; pub use file_path::SafeFilePath;
@ -365,21 +364,26 @@ impl ScopeObject for scope::Entry {
} }
} }
pub(crate) struct Scope {
pub(crate) scope: tauri::fs::Scope,
pub(crate) require_literal_leading_dot: Option<bool>,
}
pub trait FsExt<R: Runtime> { pub trait FsExt<R: Runtime> {
fn fs_scope(&self) -> &Scope; fn fs_scope(&self) -> tauri::fs::Scope;
fn try_fs_scope(&self) -> Option<&Scope>; fn try_fs_scope(&self) -> Option<tauri::fs::Scope>;
/// Cross platform file system APIs that also support manipulating Android files. /// Cross platform file system APIs that also support manipulating Android files.
fn fs(&self) -> &Fs<R>; fn fs(&self) -> &Fs<R>;
} }
impl<R: Runtime, T: Manager<R>> FsExt<R> for T { impl<R: Runtime, T: Manager<R>> FsExt<R> for T {
fn fs_scope(&self) -> &Scope { fn fs_scope(&self) -> tauri::fs::Scope {
self.state::<Scope>().inner() self.state::<Scope>().scope.clone()
} }
fn try_fs_scope(&self) -> Option<&Scope> { fn try_fs_scope(&self) -> Option<tauri::fs::Scope> {
self.try_state::<Scope>().map(|s| s.inner()) self.try_state::<Scope>().map(|s| s.scope.clone())
} }
fn fs(&self) -> &Fs<R> { fn fs(&self) -> &Fs<R> {
@ -413,17 +417,20 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
commands::write_file, commands::write_file,
commands::write_text_file, commands::write_text_file,
commands::exists, commands::exists,
commands::size,
#[cfg(feature = "watch")] #[cfg(feature = "watch")]
watcher::watch, watcher::watch,
#[cfg(feature = "watch")] #[cfg(feature = "watch")]
watcher::unwatch watcher::unwatch
]) ])
.setup(|app, api| { .setup(|app, api| {
let mut scope = Scope::default(); let scope = Scope {
scope.require_literal_leading_dot = api require_literal_leading_dot: api
.config() .config()
.as_ref() .as_ref()
.and_then(|c| c.require_literal_leading_dot); .and_then(|c| c.require_literal_leading_dot),
scope: tauri::fs::Scope::new(app, &FsScope::default())?,
};
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
{ {
@ -446,9 +453,9 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<config::Config>> {
let scope = app.fs_scope(); let scope = app.fs_scope();
for path in paths { for path in paths {
if path.is_file() { if path.is_file() {
scope.allow_file(path); let _ = scope.allow_file(path);
} else { } else {
scope.allow_directory(path, true); let _ = scope.allow_directory(path, true);
} }
} }
} }

@ -2,130 +2,18 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::{ use std::path::PathBuf;
collections::HashMap,
path::{Path, PathBuf},
sync::{
atomic::{AtomicU32, Ordering},
Mutex,
},
};
use serde::Deserialize; use serde::Deserialize;
#[derive(Deserialize)]
#[serde(untagged)]
pub(crate) enum EntryRaw {
Value(PathBuf),
Object { path: PathBuf },
}
#[derive(Debug)] #[derive(Debug)]
pub struct Entry { pub struct Entry {
pub path: Option<PathBuf>, pub path: Option<PathBuf>,
} }
pub type EventId = u32; #[derive(Deserialize)]
type EventListener = Box<dyn Fn(&Event) + Send>; #[serde(untagged)]
pub(crate) enum EntryRaw {
/// Scope change event. Value(PathBuf),
#[derive(Debug, Clone)] Object { path: PathBuf },
pub enum Event {
/// A path has been allowed.
PathAllowed(PathBuf),
/// A path has been forbidden.
PathForbidden(PathBuf),
}
#[derive(Default)]
pub struct Scope {
pub(crate) allowed: Mutex<Vec<PathBuf>>,
pub(crate) denied: Mutex<Vec<PathBuf>>,
event_listeners: Mutex<HashMap<EventId, EventListener>>,
next_event_id: AtomicU32,
pub(crate) require_literal_leading_dot: Option<bool>,
}
impl Scope {
/// Extend the allowed patterns with the given directory.
///
/// After this function has been called, the frontend will be able to use the Tauri API to read
/// the directory and all of its files. If `recursive` is `true`, subdirectories will be accessible too.
pub fn allow_directory<P: AsRef<Path>>(&self, path: P, recursive: bool) {
let path = path.as_ref();
{
let mut allowed = self.allowed.lock().unwrap();
allowed.push(path.to_path_buf());
allowed.push(path.join(if recursive { "**" } else { "*" }));
}
self.emit(Event::PathAllowed(path.to_path_buf()));
}
/// Extend the allowed patterns with the given file path.
///
/// After this function has been called, the frontend will be able to use the Tauri API to read the contents of this file.
pub fn allow_file<P: AsRef<Path>>(&self, path: P) {
let path = path.as_ref();
self.allowed.lock().unwrap().push(path.to_path_buf());
self.emit(Event::PathAllowed(path.to_path_buf()));
}
/// Set the given directory path to be forbidden by this scope.
///
/// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
pub fn forbid_directory<P: AsRef<Path>>(&self, path: P, recursive: bool) {
let path = path.as_ref();
{
let mut denied = self.denied.lock().unwrap();
denied.push(path.to_path_buf());
denied.push(path.join(if recursive { "**" } else { "*" }));
}
self.emit(Event::PathForbidden(path.to_path_buf()));
}
/// Set the given file path to be forbidden by this scope.
///
/// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
pub fn forbid_file<P: AsRef<Path>>(&self, path: P) {
let path = path.as_ref();
self.denied.lock().unwrap().push(path.to_path_buf());
self.emit(Event::PathForbidden(path.to_path_buf()));
}
/// List of allowed paths.
pub fn allowed(&self) -> Vec<PathBuf> {
self.allowed.lock().unwrap().clone()
}
/// List of forbidden paths.
pub fn forbidden(&self) -> Vec<PathBuf> {
self.denied.lock().unwrap().clone()
}
fn next_event_id(&self) -> u32 {
self.next_event_id.fetch_add(1, Ordering::Relaxed)
}
fn emit(&self, event: Event) {
let listeners = self.event_listeners.lock().unwrap();
let handlers = listeners.values();
for listener in handlers {
listener(&event);
}
}
/// Listen to an event on this scope.
pub fn listen<F: Fn(&Event) + Send + 'static>(&self, f: F) -> EventId {
let id = self.next_event_id();
self.event_listeners.lock().unwrap().insert(id, Box::new(f));
id
}
} }

@ -1,5 +1,18 @@
# Changelog # Changelog
## \[2.2.2]
- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) **Breaking change:** `specta` integration is now behind a `specta` feature flag like in Tauri.
- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Unlock and widen `specta` version range to match Tauri. No API changes.
## \[2.2.1]
- [`fb67ab2b`](https://github.com/tauri-apps/plugins-workspace/commit/fb67ab2b926502bfc20d6b43fbdd156691ea8526) ([#2281](https://github.com/tauri-apps/plugins-workspace/pull/2281) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Added `specta-util` to fix a "dependency not found" compilation error.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.1] ## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7. - [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.

@ -1,7 +1,7 @@
[package] [package]
name = "tauri-plugin-geolocation" name = "tauri-plugin-geolocation"
description = "Get and track the device's current position" description = "Get and track the device's current position"
version = "2.0.1" version = "2.2.2"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
@ -26,10 +26,13 @@ tauri-plugin = { workspace = true, features = ["build"] }
[dependencies] [dependencies]
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tauri = { workspace = true, features = ["specta"] } tauri = { workspace = true }
log = { workspace = true } log = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
specta = { workspace = true } specta = { workspace = true, optional = true }
[target.'cfg(target_os = "ios")'.dependencies] [target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] } tauri = { workspace = true, features = ["wry"] }
[features]
specta = ["dep:specta", "tauri/specta"]

@ -81,7 +81,7 @@ The Google Play Store uses this property to decide whether it should show the ap
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {
@ -92,6 +92,20 @@ fn main() {
} }
``` ```
Then, for instance, grant the plugin the permission to check or request permissions from the user and to read the device position
`src-tauri/capabilities/default.json`
```json
"permissions": [
"core:default",
"geolocation:allow-check-permissions",
"geolocation:allow-request-permissions",
"geolocation:allow-get-current-position",
"geolocation:allow-watch-position",
]
```
Afterwards all the plugin's APIs are available through the JavaScript guest bindings: Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
```javascript ```javascript

@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_GEOLOCATION__=function(t){"use strict";function e(t,e,n,i){if("a"===n&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?i:"a"===n?i.call(t):i?i.value:e.get(t)}function n(t,e,n,i,o){if("function"==typeof e?t!==e||!o:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,n),n}var i,o,s;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),o.set(this,0),s.set(this,{}),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:r})=>{if(r===e(this,o,"f")){n(this,o,r+1),e(this,i,"f").call(this,t);const a=Object.keys(e(this,s,"f"));if(a.length>0){let t=r+1;for(const n of a.sort()){if(parseInt(n)!==t)break;{const o=e(this,s,"f")[n];delete e(this,s,"f")[n],e(this,i,"f").call(this,o),t+=1}}n(this,o,t)}}else e(this,s,"f")[r.toString()]=t}))}set onmessage(t){n(this,i,t)}get onmessage(){return e(this,i,"f")}[(i=new WeakMap,o=new WeakMap,s=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}async function c(t,e={},n){return window.__TAURI_INTERNALS__.invoke(t,e,n)}return t.checkPermissions=async function(){return await async function(t){return c(`plugin:${t}|check_permissions`)}("geolocation")},t.clearWatch=async function(t){await c("plugin:geolocation|clear_watch",{channelId:t})},t.getCurrentPosition=async function(t){return await c("plugin:geolocation|get_current_position",{options:t})},t.requestPermissions=async function(t){return await c("plugin:geolocation|request_permissions",{permissions:t})},t.watchPosition=async function(t,e){const n=new a;return n.onmessage=t=>{"string"==typeof t?e(null,t):e(t)},await c("plugin:geolocation|watch_position",{options:t,channel:n}),n.id},t}({});Object.defineProperty(window.__TAURI__,"geolocation",{value:__TAURI_PLUGIN_GEOLOCATION__})} if("__TAURI__"in window){var __TAURI_PLUGIN_GEOLOCATION__=function(t){"use strict";function n(t,n,e,i){if("a"===e&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof n?t!==n||!i:!n.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===e?i:"a"===e?i.call(t):i?i.value:n.get(t)}function e(t,n,e,i,s){if("function"==typeof n||!n.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return n.set(t,e),e}var i,s,o;"function"==typeof SuppressedError&&SuppressedError;const r="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),s.set(this,0),o.set(this,[]),this.id=function(t,n=!1){return window.__TAURI_INTERNALS__.transformCallback(t,n)}((({message:t,id:r})=>{if(r==n(this,s,"f"))for(n(this,i,"f").call(this,t),e(this,s,n(this,s,"f")+1);n(this,s,"f")in n(this,o,"f");){const t=n(this,o,"f")[n(this,s,"f")];n(this,i,"f").call(this,t),delete n(this,o,"f")[n(this,s,"f")],e(this,s,n(this,s,"f")+1)}else n(this,o,"f")[r]=t}))}set onmessage(t){e(this,i,t)}get onmessage(){return n(this,i,"f")}[(i=new WeakMap,s=new WeakMap,o=new WeakMap,r)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[r]()}}async function c(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}return t.checkPermissions=async function(){return await async function(t){return c(`plugin:${t}|check_permissions`)}("geolocation")},t.clearWatch=async function(t){await c("plugin:geolocation|clear_watch",{channelId:t})},t.getCurrentPosition=async function(t){return await c("plugin:geolocation|get_current_position",{options:t})},t.requestPermissions=async function(t){return await c("plugin:geolocation|request_permissions",{permissions:t})},t.watchPosition=async function(t,n){const e=new a;return e.onmessage=t=>{"string"==typeof t?n(null,t):n(t)},await c("plugin:geolocation|watch_position",{options:t,channel:e}),e.id},t}({});Object.defineProperty(window.__TAURI__,"geolocation",{value:__TAURI_PLUGIN_GEOLOCATION__})}

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-geolocation", "name": "@tauri-apps/plugin-geolocation",
"version": "2.0.0", "version": "2.2.2",
"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"

@ -7,7 +7,6 @@ use tauri::{command, ipc::Channel, AppHandle, Runtime};
use crate::{GeolocationExt, PermissionStatus, PermissionType, Position, PositionOptions, Result}; use crate::{GeolocationExt, PermissionStatus, PermissionType, Position, PositionOptions, Result};
#[command] #[command]
#[specta::specta]
pub(crate) async fn get_current_position<R: Runtime>( pub(crate) async fn get_current_position<R: Runtime>(
app: AppHandle<R>, app: AppHandle<R>,
options: Option<PositionOptions>, options: Option<PositionOptions>,
@ -16,7 +15,6 @@ pub(crate) async fn get_current_position<R: Runtime>(
} }
#[command] #[command]
#[specta::specta]
pub(crate) async fn watch_position<R: Runtime>( pub(crate) async fn watch_position<R: Runtime>(
app: AppHandle<R>, app: AppHandle<R>,
options: PositionOptions, options: PositionOptions,
@ -26,19 +24,16 @@ pub(crate) async fn watch_position<R: Runtime>(
} }
#[command] #[command]
#[specta::specta]
pub(crate) async fn clear_watch<R: Runtime>(app: AppHandle<R>, channel_id: u32) -> Result<()> { pub(crate) async fn clear_watch<R: Runtime>(app: AppHandle<R>, channel_id: u32) -> Result<()> {
app.geolocation().clear_watch(channel_id) app.geolocation().clear_watch(channel_id)
} }
#[command] #[command]
#[specta::specta]
pub(crate) async fn check_permissions<R: Runtime>(app: AppHandle<R>) -> Result<PermissionStatus> { pub(crate) async fn check_permissions<R: Runtime>(app: AppHandle<R>) -> Result<PermissionStatus> {
app.geolocation().check_permissions() app.geolocation().check_permissions()
} }
#[command] #[command]
#[specta::specta]
pub(crate) async fn request_permissions<R: Runtime>( pub(crate) async fn request_permissions<R: Runtime>(
app: AppHandle<R>, app: AppHandle<R>,
permissions: Option<Vec<PermissionType>>, permissions: Option<Vec<PermissionType>>,

@ -3,13 +3,13 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use serde::{ser::Serializer, Serialize}; use serde::{ser::Serializer, Serialize};
use specta::Type;
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
// TODO: Improve Error handling (different typed errors instead of one (stringified) PluginInvokeError for all mobile errors) // TODO: Improve Error handling (different typed errors instead of one (stringified) PluginInvokeError for all mobile errors)
#[derive(Debug, thiserror::Error, Type)] #[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
pub enum Error { pub enum Error {
#[cfg(mobile)] #[cfg(mobile)]
#[error(transparent)] #[error(transparent)]

@ -7,8 +7,6 @@ use tauri::{
Manager, Runtime, Manager, Runtime,
}; };
//use tauri_specta::*;
pub use models::*; pub use models::*;
#[cfg(desktop)] #[cfg(desktop)]
@ -27,24 +25,6 @@ use desktop::Geolocation;
#[cfg(mobile)] #[cfg(mobile)]
use mobile::Geolocation; use mobile::Geolocation;
/* macro_rules! specta_builder {
() => {
ts::builder()
.commands(collect_commands![
commands::get_current_position,
commands::watch_position,
commands::clear_watch,
commands::check_permissions,
commands::request_permissions
])
.header("// @ts-nocheck")
.config(
specta::ts::ExportConfig::default()
.bigint(specta::ts::BigIntExportBehavior::Number),
)
};
} */
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the geolocation APIs. /// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the geolocation APIs.
pub trait GeolocationExt<R: Runtime> { pub trait GeolocationExt<R: Runtime> {
fn geolocation(&self) -> &Geolocation<R>; fn geolocation(&self) -> &Geolocation<R>;
@ -58,9 +38,6 @@ impl<R: Runtime, T: Manager<R>> crate::GeolocationExt<R> for T {
/// Initializes the plugin. /// Initializes the plugin.
pub fn init<R: Runtime>() -> TauriPlugin<R> { pub fn init<R: Runtime>() -> TauriPlugin<R> {
/* let (invoke_handler, register_events) =
specta_builder!().build_plugin_utils("geolocation").unwrap(); */
Builder::new("geolocation") Builder::new("geolocation")
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
commands::get_current_position, commands::get_current_position,
@ -79,22 +56,3 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
}) })
.build() .build()
} }
/* #[cfg(test)]
mod test {
use super::*;
#[test]
fn export_types() {
specta_builder!()
.path("./guest-js/bindings.ts")
.config(
specta::ts::ExportConfig::default()
.formatter(specta::ts::formatter::prettier)
.bigint(specta::ts::BigIntExportBehavior::Number),
)
.export_for_plugin("geolocation")
.expect("failed to export specta types");
}
}
*/

@ -3,10 +3,10 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specta::Type;
use tauri::plugin::PermissionState; use tauri::plugin::PermissionState;
#[derive(Debug, Clone, Default, Serialize, Deserialize, Type)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct PermissionStatus { pub struct PermissionStatus {
/// Permission state for the location alias. /// Permission state for the location alias.
@ -25,7 +25,8 @@ pub struct PermissionStatus {
pub coarse_location: PermissionState, pub coarse_location: PermissionState,
} }
#[derive(Debug, Clone, Default, Serialize, Deserialize, Type)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct PositionOptions { pub struct PositionOptions {
/// High accuracy mode (such as GPS, if available) /// High accuracy mode (such as GPS, if available)
@ -46,14 +47,16 @@ pub struct PositionOptions {
pub maximum_age: u32, pub maximum_age: u32,
} }
#[derive(Debug, Clone, Serialize, Deserialize, Type)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum PermissionType { pub enum PermissionType {
Location, Location,
CoarseLocation, CoarseLocation,
} }
#[derive(Debug, Clone, Default, Serialize, Deserialize, Type)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Coordinates { pub struct Coordinates {
/// Latitude in decimal degrees. /// Latitude in decimal degrees.
@ -73,7 +76,8 @@ pub struct Coordinates {
pub heading: Option<f64>, pub heading: Option<f64>,
} }
#[derive(Debug, Clone, Default, Serialize, Deserialize, Type)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Position { pub struct Position {
/// Creation time for these coordinates. /// Creation time for these coordinates.
@ -83,7 +87,8 @@ pub struct Position {
pub coords: Coordinates, pub coords: Coordinates,
} }
#[derive(Debug, Clone, Serialize, Deserialize, Type)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "specta", derive(specta::Type))]
#[serde(untagged)] #[serde(untagged)]
pub enum WatchEvent { pub enum WatchEvent {
Position(Position), Position(Position),

@ -1,5 +1,9 @@
# Changelog # Changelog
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.1] ## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7. - [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.
@ -104,22 +108,3 @@
## \[2.0.0-alpha.0] ## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release! - [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
te to alpha.11.
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
\-apps/plugins-workspace/pull/371)) First v2 alpha release!
te to alpha.11.
## \[2.0.0-alpha.0]
- [`717ae67`](https://github.com/tauri-apps/plugins-workspace/commit/717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
]\(https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
om/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
st v2 alpha release!
]\(https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
717ae670978feb4492fac1f295998b93f2b9347f)([#371](https://github.com/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!
om/tauri-apps/plugins-workspace/pull/371)) First v2 alpha release!

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-global-shortcut" name = "tauri-plugin-global-shortcut"
version = "2.0.1" version = "2.2.0"
description = "Register global hotkeys listeners on your Tauri application." description = "Register global hotkeys listeners on your Tauri application."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }

@ -55,7 +55,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-global-shortcut#v2
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {

@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBAL_SHORTCUT__=function(t){"use strict";function e(t,e,r,s){if("a"===r&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function r(t,e,r,s,n){if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,r),r}var s,n,i;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,s.set(this,(()=>{})),n.set(this,0),i.set(this,{}),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:o})=>{if(o===e(this,n,"f")){r(this,n,o+1),e(this,s,"f").call(this,t);const a=Object.keys(e(this,i,"f"));if(a.length>0){let t=o+1;for(const r of a.sort()){if(parseInt(r)!==t)break;{const n=e(this,i,"f")[r];delete e(this,i,"f")[r],e(this,s,"f").call(this,n),t+=1}}r(this,n,t)}}else e(this,i,"f")[o.toString()]=t}))}set onmessage(t){r(this,s,t)}get onmessage(){return e(this,s,"f")}[(s=new WeakMap,n=new WeakMap,i=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function _(t,e={},r){return window.__TAURI_INTERNALS__.invoke(t,e,r)}return t.isRegistered=async function(t){return await _("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const r=new a;return r.onmessage=e,await _("plugin:global-shortcut|register",{shortcuts:Array.isArray(t)?t:[t],handler:r})},t.unregister=async function(t){return await _("plugin:global-shortcut|unregister",{shortcuts:Array.isArray(t)?t:[t]})},t.unregisterAll=async function(){return await _("plugin:global-shortcut|unregister_all",{})},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBAL_SHORTCUT__})} if("__TAURI__"in window){var __TAURI_PLUGIN_GLOBAL_SHORTCUT__=function(t){"use strict";function e(t,e,r,s){if("a"===r&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function r(t,e,r,s,i){if("function"==typeof e||!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,r),r}var s,i,n;"function"==typeof SuppressedError&&SuppressedError;const o="__TAURI_TO_IPC_KEY__";class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,s.set(this,(()=>{})),i.set(this,0),n.set(this,[]),this.id=function(t,e=!1){return window.__TAURI_INTERNALS__.transformCallback(t,e)}((({message:t,id:o})=>{if(o==e(this,i,"f"))for(e(this,s,"f").call(this,t),r(this,i,e(this,i,"f")+1);e(this,i,"f")in e(this,n,"f");){const t=e(this,n,"f")[e(this,i,"f")];e(this,s,"f").call(this,t),delete e(this,n,"f")[e(this,i,"f")],r(this,i,e(this,i,"f")+1)}else e(this,n,"f")[o]=t}))}set onmessage(t){r(this,s,t)}get onmessage(){return e(this,s,"f")}[(s=new WeakMap,i=new WeakMap,n=new WeakMap,o)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[o]()}}async function _(t,e={},r){return window.__TAURI_INTERNALS__.invoke(t,e,r)}return t.isRegistered=async function(t){return await _("plugin:global-shortcut|is_registered",{shortcut:t})},t.register=async function(t,e){const r=new a;return r.onmessage=e,await _("plugin:global-shortcut|register",{shortcuts:Array.isArray(t)?t:[t],handler:r})},t.unregister=async function(t){return await _("plugin:global-shortcut|unregister",{shortcuts:Array.isArray(t)?t:[t]})},t.unregisterAll=async function(){return await _("plugin:global-shortcut|unregister_all",{})},t}({});Object.defineProperty(window.__TAURI__,"globalShortcut",{value:__TAURI_PLUGIN_GLOBAL_SHORTCUT__})}

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-global-shortcut", "name": "@tauri-apps/plugin-global-shortcut",
"version": "2.0.0", "version": "2.2.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"

@ -1,5 +1,18 @@
# Changelog # Changelog
## \[2.2.2]
- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) **Breaking change:** `specta` integration is now behind a `specta` feature flag like in Tauri.
- [`c9c13a0f`](https://github.com/tauri-apps/plugins-workspace/commit/c9c13a0fe7cdaac223843f5ba33176252f8e22f5) ([#2316](https://github.com/tauri-apps/plugins-workspace/pull/2316) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Unlock and widen `specta` version range to match Tauri. No API changes.
## \[2.2.1]
- [`fb67ab2b`](https://github.com/tauri-apps/plugins-workspace/commit/fb67ab2b926502bfc20d6b43fbdd156691ea8526) ([#2281](https://github.com/tauri-apps/plugins-workspace/pull/2281) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Added `specta-util` to fix a "dependency not found" compilation error.
## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.
## \[2.0.1] ## \[2.0.1]
- [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7. - [`a1a82208`](https://github.com/tauri-apps/plugins-workspace/commit/a1a82208ed4ab87f83310be0dc95428aec9ab241) ([#1873](https://github.com/tauri-apps/plugins-workspace/pull/1873) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Downgrade MSRV to 1.77.2 to support Windows 7.

@ -1,7 +1,7 @@
[package] [package]
name = "tauri-plugin-haptics" name = "tauri-plugin-haptics"
description = "Haptic feedback and vibrations on Android and iOS" description = "Haptic feedback and vibrations on Android and iOS"
version = "2.0.1" version = "2.2.2"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
@ -26,10 +26,13 @@ tauri-plugin = { workspace = true, features = ["build"] }
[dependencies] [dependencies]
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tauri = { workspace = true, features = ["specta"] } tauri = { workspace = true }
log = { workspace = true } log = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
specta = { workspace = true } specta = { workspace = true, optional = true }
[target.'cfg(target_os = "ios")'.dependencies] [target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] } tauri = { workspace = true, features = ["wry"] }
[features]
specta = ["dep:specta", "tauri/specta"]

@ -58,7 +58,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-haptics#v2
First you need to register the core plugin with Tauri: First you need to register the core plugin with Tauri:
`src-tauri/src/main.rs` `src-tauri/src/lib.rs`
```rust ```rust
fn main() { fn main() {

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-haptics", "name": "@tauri-apps/plugin-haptics",
"version": "2.0.0", "version": "2.2.2",
"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"

@ -7,13 +7,11 @@ use tauri::{command, AppHandle, Runtime};
use crate::{HapticsExt, ImpactFeedbackStyle, NotificationFeedbackType, Result}; use crate::{HapticsExt, ImpactFeedbackStyle, NotificationFeedbackType, Result};
#[command] #[command]
#[specta::specta]
pub(crate) async fn vibrate<R: Runtime>(app: AppHandle<R>, duration: u32) -> Result<()> { pub(crate) async fn vibrate<R: Runtime>(app: AppHandle<R>, duration: u32) -> Result<()> {
app.haptics().vibrate(duration) app.haptics().vibrate(duration)
} }
#[command] #[command]
#[specta::specta]
pub(crate) async fn impact_feedback<R: Runtime>( pub(crate) async fn impact_feedback<R: Runtime>(
app: AppHandle<R>, app: AppHandle<R>,
style: ImpactFeedbackStyle, style: ImpactFeedbackStyle,
@ -22,7 +20,6 @@ pub(crate) async fn impact_feedback<R: Runtime>(
} }
#[command] #[command]
#[specta::specta]
pub(crate) async fn notification_feedback<R: Runtime>( pub(crate) async fn notification_feedback<R: Runtime>(
app: AppHandle<R>, app: AppHandle<R>,
r#type: NotificationFeedbackType, r#type: NotificationFeedbackType,
@ -31,7 +28,6 @@ pub(crate) async fn notification_feedback<R: Runtime>(
} }
#[command] #[command]
#[specta::specta]
pub(crate) async fn selection_feedback<R: Runtime>(app: AppHandle<R>) -> Result<()> { pub(crate) async fn selection_feedback<R: Runtime>(app: AppHandle<R>) -> Result<()> {
app.haptics().selection_feedback() app.haptics().selection_feedback()
} }

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

Loading…
Cancel
Save