Compare commits

...

29 Commits

Author SHA1 Message Date
github-actions[bot] 27ddcd0abe publish new versions (#2869)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-22 23:32:16 +02:00
Fabian-Lars d7fb5623d6 docs(deep-link): update platform support wording
ref https://github.com/tauri-apps/tauri/issues/13877
2025-07-22 23:27:41 +02:00
yobson1 d4f8299b12 fix(deep-link): handler not set as default on linux (#2844)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-07-22 11:54:12 +02:00
Fabian-Lars 341919ed57 docs(shell): Remove left over tauri.conf.json > scope mentino 2025-07-22 09:58:00 +02:00
renovate[bot] 124f2191aa chore(deps): update dependency @tauri-apps/cli to v2.7.1 (#2867)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-22 14:50:50 +08:00
renovate[bot] 0970b94949 chore(deps): update dependency typescript-eslint to v8.38.0 (#2864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-22 14:40:05 +08:00
renovate[bot] 7340242d4e chore(deps): update tauri monorepo to v2.7.0 (#2863)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 11:35:10 +02:00
github-actions[bot] d66aa6ff78 publish new versions (#2822)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-20 23:39:53 +02:00
Nick Müller 6f345870df fix(single-instance): disable dbus name replacement (#2860) 2025-07-20 23:05:09 +02:00
renovate[bot] 708fa4e2b7 chore(deps): update dependency eslint-config-prettier to v10.1.8 (#2858)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-19 17:41:02 +08:00
Matthew Richardson b729203059 fix(upload): fix download() locks main thread on Android (#2838)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-07-18 20:37:39 +02:00
renovate[bot] 2f9c71aae7 chore(deps): update dependency rollup to v4.45.1 (#2850)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-18 16:54:16 +08:00
renovate[bot] 80d4d8e128 chore(deps): update eslint monorepo to v9.31.0 (v2) (#2839)
* chore(deps): update eslint monorepo to v9.31.0

* Deduplicate

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-07-15 10:13:33 +08:00
renovate[bot] e7a98b0d2e chore(deps): update dependency typescript-eslint to v8.37.0 (#2848)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-15 09:51:41 +08:00
Tony 44a1f65912 fix(fs): writeFile create file by default (#2846) 2025-07-15 09:46:43 +08:00
renovate[bot] 6210cd31df chore(deps): update dependency rollup to v4.45.0 (#2841)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-12 15:00:21 +08:00
renovate[bot] 467f07b7de chore(deps): update dependency vite to v7 (v2) (#2800)
* chore(deps): update dependency vite to v7

* Bump unocss and svelte plugin

* Align @unocss/extractor-svelte

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2025-07-11 13:24:31 +08:00
renovate[bot] 7ba6e08a86 chore(deps): update dependency typescript-eslint to v8.36.0 (#2832)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-08 08:33:16 +08:00
renovate[bot] 989470f0d7 chore(deps): update dependency rollup to v4.44.2 (#2827)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 00:10:20 +08:00
renovate[bot] ca3c3aa28a chore(deps): update eslint monorepo to v9.30.1 (#2824)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 11:36:56 +08:00
Enkhjil Enkhbaatar aa9140e1ac feat(barcode-scanner): Add support for GS1 DataBar on iOS 15.4+ (#2437)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-07-01 16:40:22 +02:00
Tony 6a8f255878 feat(window-state): make flags optional in js side (#2619) 2025-07-01 21:21:58 +08:00
renovate[bot] 8ac494da7c chore(deps): update dependency @rollup/plugin-typescript to v12.1.4 (#2818)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-01 14:53:13 +02:00
renovate[bot] 1635282868 chore(deps): update dependency typescript-eslint to v8.35.1 (#2820)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-01 14:34:07 +08:00
renovate[bot] 4587e4a2b3 chore(deps): update eslint monorepo to v9.30.0 (#2816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-29 10:49:14 +08:00
renovate[bot] 1135dc7ed3 chore(deps): update dependency @tauri-apps/cli to v2.6.2 (#2819)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-29 10:48:47 +08:00
renovate[bot] fe01894e7f chore(deps): update dependency prettier to v3.6.2 (#2814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-27 14:06:34 +08:00
renovate[bot] 36400b5678 chore(deps): update dependency @tauri-apps/cli to v2.6.1 (#2813)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-27 08:46:11 +08:00
renovate[bot] ead3c268e1 chore(deps): update dependency rollup to v4.44.1 (#2811)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 13:11:34 +08:00
56 changed files with 1547 additions and 896 deletions
Generated
+22 -46
View File
@@ -207,7 +207,7 @@ checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "api"
version = "2.0.30"
version = "2.0.31"
dependencies = [
"log",
"serde",
@@ -233,6 +233,7 @@ dependencies = [
"tauri-plugin-shell",
"tauri-plugin-store",
"tauri-plugin-updater",
"tauri-plugin-upload",
"tauri-plugin-window-state",
"time",
"tiny_http",
@@ -403,17 +404,6 @@ dependencies = [
"slab",
]
[[package]]
name = "async-fs"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a"
dependencies = [
"async-lock",
"blocking",
"futures-lite",
]
[[package]]
name = "async-io"
version = "2.4.0"
@@ -3701,9 +3691,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.29.0"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags 2.9.0",
"cfg-if",
@@ -6495,7 +6485,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-barcode-scanner"
version = "2.3.0"
version = "2.4.0"
dependencies = [
"log",
"serde",
@@ -6546,7 +6536,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-deep-link"
version = "2.4.0"
version = "2.4.1"
dependencies = [
"dunce",
"rust-ini",
@@ -6564,7 +6554,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-dialog"
version = "2.3.0"
version = "2.3.1"
dependencies = [
"log",
"raw-window-handle",
@@ -6580,7 +6570,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-fs"
version = "2.4.0"
version = "2.4.1"
dependencies = [
"anyhow",
"dunce",
@@ -6641,7 +6631,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-http"
version = "2.5.0"
version = "2.5.1"
dependencies = [
"bytes",
"cookie_store",
@@ -6769,7 +6759,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-persisted-scope"
version = "2.3.0"
version = "2.3.1"
dependencies = [
"aho-corasick",
"bincode",
@@ -6823,7 +6813,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-single-instance"
version = "2.3.0"
version = "2.3.2"
dependencies = [
"semver",
"serde",
@@ -6920,7 +6910,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-upload"
version = "2.3.0"
version = "2.3.1"
dependencies = [
"futures-util",
"log",
@@ -6955,7 +6945,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-window-state"
version = "2.3.0"
version = "2.4.0"
dependencies = [
"bitflags 2.9.0",
"log",
@@ -8843,16 +8833,6 @@ dependencies = [
"rustix 1.0.5",
]
[[package]]
name = "xdg-home"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "xkeysym"
version = "0.2.1"
@@ -8885,13 +8865,12 @@ dependencies = [
[[package]]
name = "zbus"
version = "5.5.0"
version = "5.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236"
checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad"
dependencies = [
"async-broadcast",
"async-executor",
"async-fs",
"async-io",
"async-lock",
"async-process",
@@ -8904,17 +8883,15 @@ dependencies = [
"futures-core",
"futures-lite",
"hex",
"nix 0.29.0",
"nix 0.30.1",
"ordered-stream",
"serde",
"serde_repr",
"static_assertions",
"tokio",
"tracing",
"uds_windows",
"windows-sys 0.59.0",
"winnow 0.7.6",
"xdg-home",
"zbus_macros",
"zbus_names",
"zvariant",
@@ -8922,9 +8899,9 @@ dependencies = [
[[package]]
name = "zbus_macros"
version = "5.5.0"
version = "5.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0"
checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659"
dependencies = [
"proc-macro-crate 3.3.0",
"proc-macro2",
@@ -9122,14 +9099,13 @@ dependencies = [
[[package]]
name = "zvariant"
version = "5.4.0"
version = "5.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac"
checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f"
dependencies = [
"endi",
"enumflags2",
"serde",
"static_assertions",
"url",
"winnow 0.7.6",
"zvariant_derive",
@@ -9138,9 +9114,9 @@ dependencies = [
[[package]]
name = "zvariant_derive"
version = "5.4.0"
version = "5.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f"
checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208"
dependencies = [
"proc-macro-crate 3.3.0",
"proc-macro2",
+1 -1
View File
@@ -23,7 +23,7 @@ schemars = "0.8"
dunce = "1"
specta = "^2.0.0-rc.16"
glob = "0.3"
zbus = "5"
zbus = "5.9"
[workspace.package]
edition = "2021"
+1 -1
View File
@@ -33,7 +33,7 @@ This repo and all plugins require a Rust version of at least **1.77.2**
| [store](plugins/store) | Persistent key value storage. | ✅ | ✅ | ✅ | ✅ | ✅ |
| [stronghold](plugins/stronghold) | Encrypted, secure database. | ✅ | ✅ | ✅ | ? | ? |
| [updater](plugins/updater) | In-app updates for Tauri applications. | ✅ | ✅ | ✅ | ❌ | ❌ |
| [upload](plugins/upload) | Tauri plugin for file uploads through HTTP. | ✅ | ✅ | ✅ | ? | ? |
| [upload](plugins/upload) | Tauri plugin for file uploads through HTTP. | ✅ | ✅ | ✅ | | |
| [websocket](plugins/websocket) | Open a WebSocket connection using a Rust client in JS. | ✅ | ✅ | ✅ | ? | ? |
| [window-state](plugins/window-state) | Persist window sizes and positions. | ✅ | ✅ | ✅ | ❌ | ❌ |
+9
View File
@@ -1,5 +1,14 @@
# Changelog
## \[2.0.27]
### Dependencies
- Upgraded to `barcode-scanner-js@2.4.0`
- Upgraded to `fs-js@2.4.1`
- Upgraded to `dialog-js@2.3.1`
- Upgraded to `http-js@2.5.1`
## \[2.0.26]
### Dependencies
+12 -11
View File
@@ -1,7 +1,7 @@
{
"name": "api",
"private": true,
"version": "2.0.26",
"version": "2.0.27",
"type": "module",
"scripts": {
"dev": "vite --clearScreen false",
@@ -10,17 +10,17 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.6.0",
"@tauri-apps/plugin-barcode-scanner": "^2.3.0",
"@tauri-apps/api": "2.7.0",
"@tauri-apps/plugin-barcode-scanner": "^2.4.0",
"@tauri-apps/plugin-biometric": "^2.3.0",
"@tauri-apps/plugin-cli": "^2.4.0",
"@tauri-apps/plugin-clipboard-manager": "^2.3.0",
"@tauri-apps/plugin-dialog": "^2.3.0",
"@tauri-apps/plugin-fs": "^2.4.0",
"@tauri-apps/plugin-dialog": "^2.3.1",
"@tauri-apps/plugin-fs": "^2.4.1",
"@tauri-apps/plugin-geolocation": "^2.2.0",
"@tauri-apps/plugin-global-shortcut": "^2.3.0",
"@tauri-apps/plugin-haptics": "^2.2.0",
"@tauri-apps/plugin-http": "^2.5.0",
"@tauri-apps/plugin-http": "^2.5.1",
"@tauri-apps/plugin-nfc": "^2.3.0",
"@tauri-apps/plugin-notification": "^2.3.0",
"@tauri-apps/plugin-opener": "^2.4.0",
@@ -29,16 +29,17 @@
"@tauri-apps/plugin-shell": "^2.3.0",
"@tauri-apps/plugin-store": "^2.3.0",
"@tauri-apps/plugin-updater": "^2.9.0",
"@tauri-apps/plugin-upload": "^2.3.0",
"@zerodevx/svelte-json-view": "1.0.11"
},
"devDependencies": {
"@iconify-json/codicon": "^1.2.12",
"@iconify-json/ph": "^1.2.2",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tauri-apps/cli": "2.6.0",
"@unocss/extractor-svelte": "^66.0.0",
"@sveltejs/vite-plugin-svelte": "^6.0.0",
"@tauri-apps/cli": "2.7.1",
"@unocss/extractor-svelte": "^66.3.3",
"svelte": "^5.20.4",
"unocss": "^66.0.0",
"vite": "^6.2.6"
"unocss": "^66.3.3",
"vite": "^7.0.4"
}
}
+9
View File
@@ -1,5 +1,14 @@
# Changelog
## \[2.0.31]
### Dependencies
- Upgraded to `barcode-scanner@2.4.0`
- Upgraded to `fs@2.4.1`
- Upgraded to `dialog@2.3.1`
- Upgraded to `http@2.5.1`
## \[2.0.30]
### Dependencies
+6 -5
View File
@@ -1,7 +1,7 @@
[package]
name = "api"
publish = false
version = "2.0.30"
version = "2.0.31"
description = "An example Tauri Application showcasing the api"
edition = "2021"
rust-version = { workspace = true }
@@ -21,15 +21,15 @@ tiny_http = "0.12"
time = "0.3"
log = { workspace = true }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.6.0" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.4.0", features = [
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.4.1", features = [
"watch",
] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.0" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.3.0" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.3.1" }
tauri-plugin-http = { path = "../../../plugins/http", features = [
"multipart",
"cookies",
], version = "2.5.0" }
], version = "2.5.1" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.0", features = [
"windows7-compat",
] }
@@ -38,6 +38,7 @@ tauri-plugin-process = { path = "../../../plugins/process", version = "2.3.0" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.4.0" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.0" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.3.0" }
tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" }
[dependencies.tauri]
workspace = true
@@ -60,7 +61,7 @@ tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.9.0" }
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" }
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.3.0" }
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.4.0" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.0" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.0" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.0" }
@@ -95,6 +95,7 @@
{
"identifier": "opener:allow-open-path",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
}
},
"upload:default"
]
}
+1
View File
@@ -39,6 +39,7 @@ pub fn run() {
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_store::Builder::default().build())
.plugin(tauri_plugin_upload::init())
.setup(move |app| {
#[cfg(desktop)]
{
+6
View File
@@ -16,6 +16,7 @@
import Opener from './views/Opener.svelte'
import Store from './views/Store.svelte'
import Updater from './views/Updater.svelte'
import Upload from './views/Upload.svelte'
import Clipboard from './views/Clipboard.svelte'
import WebRTC from './views/WebRTC.svelte'
import Scanner from './views/Scanner.svelte'
@@ -107,6 +108,11 @@
component: Updater,
icon: 'i-codicon-cloud-download'
},
{
label: 'Upload',
component: Upload,
icon: 'i-codicon-cloud-upload'
},
{
label: 'Clipboard',
component: Clipboard,
+376
View File
@@ -0,0 +1,376 @@
<script>
import { download, upload } from '@tauri-apps/plugin-upload'
import { open } from '@tauri-apps/plugin-dialog'
import { JsonView } from '@zerodevx/svelte-json-view'
import { appDataDir } from '@tauri-apps/api/path'
import { onMount } from 'svelte'
export let onMessage
let downloadUrl = 'https://httpbin.org/json'
let downloadFolder = ''
let downloadPath = ''
let downloadProgress = null
let downloadResult = null
let isDownloading = false
let uploadUrl = 'https://httpbin.org/post'
let uploadFilePath = ''
let uploadProgress = null
let uploadResult = null
let isUploading = false
onMount(async () => {
try {
const defaultDir = await appDataDir()
if (!downloadFolder) {
downloadFolder = defaultDir
updateDownloadPath()
}
} catch (error) {
onMessage({ error: `Failed to get default directory: ${error.toString()}` })
}
})
async function selectDownloadFolder() {
try {
const selected = await open({
directory: true,
multiple: false,
defaultPath: downloadFolder || undefined
})
if (selected) {
downloadFolder = selected
updateDownloadPath()
}
} catch (error) {
onMessage({ error: error.toString() })
}
}
function getFilenameFromUrl(url) {
try {
const urlObj = new URL(url)
let pathname = urlObj.pathname
// Remove leading slash
if (pathname.startsWith('/')) {
pathname = pathname.substring(1)
}
// If pathname is empty or ends with slash, use a default name
if (!pathname || pathname.endsWith('/')) {
return 'downloaded-file.json'
}
// Extract filename from pathname
const segments = pathname.split('/')
let filename = segments[segments.length - 1]
// If no extension, try to infer from URL or use default
if (!filename.includes('.')) {
// Check if URL suggests a file type
if (url.includes('json') || urlObj.searchParams.has('format') && urlObj.searchParams.get('format') === 'json') {
filename += '.json'
} else if (url.includes('xml')) {
filename += '.xml'
} else if (url.includes('csv')) {
filename += '.csv'
} else {
filename += '.txt'
}
}
return filename
} catch (error) {
return 'downloaded-file.json'
}
}
function updateDownloadPath() {
if (downloadFolder && downloadUrl) {
const filename = getFilenameFromUrl(downloadUrl)
downloadPath = `${downloadFolder}/${filename}`
} else {
downloadPath = ''
}
}
// Update download path when URL changes
$: if (downloadUrl) {
updateDownloadPath()
}
async function selectUploadFile() {
try {
const selected = await open({
directory: false,
multiple: false
})
if (selected) {
uploadFilePath = selected
}
} catch (error) {
onMessage({ error: error.toString() })
}
}
async function startDownload() {
if (!downloadUrl || !downloadFolder) {
onMessage({ error: 'Please provide both URL and download folder' })
return
}
// Ensure download path is updated
updateDownloadPath()
if (!downloadPath) {
onMessage({ error: 'Could not generate download path' })
return
}
isDownloading = true
downloadProgress = null
downloadResult = null
try {
await download(
downloadUrl,
downloadPath,
(progress) => {
downloadProgress = {
progress: progress.progress,
progressTotal: progress.progressTotal,
total: progress.total,
transferSpeed: progress.transferSpeed,
percentage: progress.total > 0 ? Math.round((progress.progressTotal / progress.total) * 100) : 0
}
},
new Map([
['User-Agent', 'Tauri Upload Plugin Demo']
])
)
downloadResult = {
success: true,
message: `File downloaded successfully to: ${downloadPath}`,
finalProgress: downloadProgress
}
onMessage({
type: 'download',
result: downloadResult
})
} catch (error) {
downloadResult = {
success: false,
error: error.toString()
}
onMessage({ error: error.toString() })
} finally {
isDownloading = false
}
}
async function startUpload() {
if (!uploadUrl || !uploadFilePath) {
onMessage({ error: 'Please provide both URL and file path' })
return
}
isUploading = true
uploadProgress = null
uploadResult = null
try {
const response = await upload(
uploadUrl,
uploadFilePath,
(progress) => {
uploadProgress = {
progress: progress.progress,
progressTotal: progress.progressTotal,
total: progress.total,
transferSpeed: progress.transferSpeed,
percentage: progress.total > 0 ? Math.round((progress.progressTotal / progress.total) * 100) : 0
}
},
new Map([
['User-Agent', 'Tauri Upload Plugin Demo']
])
)
uploadResult = {
success: true,
response: response,
finalProgress: uploadProgress
}
onMessage({
type: 'upload',
result: uploadResult
})
} catch (error) {
uploadResult = {
success: false,
error: error.toString()
}
onMessage({ error: error.toString() })
} finally {
isUploading = false
}
}
</script>
<div class="space-y-6">
<div class="bg-gray-50 p-4 rounded-lg">
<h3 class="text-lg font-semibold mb-4 text-gray-800">File Download</h3>
<div class="space-y-3">
<div>
<label for="download-url" class="block text-sm font-medium text-gray-700 mb-1">Download URL:</label>
<input
id="download-url"
bind:value={downloadUrl}
type="url"
placeholder="https://example.com/file.json"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={isDownloading}
/>
</div>
<div>
<label for="download-folder" class="block text-sm font-medium text-gray-700 mb-1">Download folder:</label>
<div class="flex gap-2">
<input
id="download-folder"
bind:value={downloadFolder}
type="text"
placeholder="Select download folder..."
class="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={isDownloading}
/>
<button
on:click={selectDownloadFolder}
class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 disabled:opacity-50"
disabled={isDownloading}
>
Browse
</button>
</div>
</div>
{#if downloadPath}
<div class="bg-blue-50 border border-blue-200 p-3 rounded-md">
<div class="text-sm text-blue-800">
<strong>File will be saved as:</strong>
<div class="font-mono text-xs mt-1 break-all">{downloadPath}</div>
</div>
</div>
{/if}
<button
on:click={startDownload}
class="w-full px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed"
disabled={isDownloading || !downloadUrl || !downloadFolder}
>
{isDownloading ? 'Downloading...' : 'Download File'}
</button>
{#if downloadProgress}
<div class="bg-white p-3 rounded border">
<div class="flex justify-between text-sm text-gray-600 mb-1">
<span>Progress: {downloadProgress.percentage}%</span>
<span>Speed: {Math.round(downloadProgress.transferSpeed / 1024)} KB/s</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2">
<div
class="bg-blue-500 h-2 rounded-full transition-all duration-300"
style="width: {downloadProgress.percentage}%"
></div>
</div>
<div class="text-xs text-gray-500 mt-1">
{Math.round(downloadProgress.progressTotal / 1024)} KB / {Math.round(downloadProgress.total / 1024)} KB
</div>
</div>
{/if}
{#if downloadResult}
<div class="bg-white p-3 rounded border">
<JsonView json={downloadResult} />
</div>
{/if}
</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<h3 class="text-lg font-semibold mb-4 text-gray-800">File Upload</h3>
<div class="space-y-3">
<div>
<label for="upload-url" class="block text-sm font-medium text-gray-700 mb-1">Upload URL:</label>
<input
id="upload-url"
bind:value={uploadUrl}
type="url"
placeholder="https://httpbin.org/post"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
disabled={isUploading}
/>
</div>
<div>
<label for="upload-file" class="block text-sm font-medium text-gray-700 mb-1">File to upload:</label>
<div class="flex gap-2">
<input
id="upload-file"
bind:value={uploadFilePath}
type="text"
placeholder="Select file to upload..."
class="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
disabled={isUploading}
/>
<button
on:click={selectUploadFile}
class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 disabled:opacity-50"
disabled={isUploading}
>
Browse
</button>
</div>
</div>
<button
on:click={startUpload}
class="w-full px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 disabled:opacity-50 disabled:cursor-not-allowed"
disabled={isUploading || !uploadUrl || !uploadFilePath}
>
{isUploading ? 'Uploading...' : 'Upload File'}
</button>
{#if uploadProgress}
<div class="bg-white p-3 rounded border">
<div class="flex justify-between text-sm text-gray-600 mb-1">
<span>Progress: {uploadProgress.percentage}%</span>
<span>Speed: {Math.round(uploadProgress.transferSpeed / 1024)} KB/s</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2">
<div
class="bg-green-500 h-2 rounded-full transition-all duration-300"
style="width: {uploadProgress.percentage}%"
></div>
</div>
<div class="text-xs text-gray-500 mt-1">
{Math.round(uploadProgress.progressTotal / 1024)} KB / {Math.round(uploadProgress.total / 1024)} KB
</div>
</div>
{/if}
{#if uploadResult}
<div class="bg-white p-3 rounded border">
<JsonView json={uploadResult} />
</div>
{/if}
</div>
</div>
</div>
+7 -7
View File
@@ -11,19 +11,19 @@
"example:api:dev": "pnpm run --filter \"api\" tauri dev"
},
"devDependencies": {
"@eslint/js": "9.29.0",
"@eslint/js": "9.31.0",
"@rollup/plugin-node-resolve": "16.0.1",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "12.1.3",
"@rollup/plugin-typescript": "12.1.4",
"covector": "^0.12.4",
"eslint": "9.29.0",
"eslint-config-prettier": "10.1.5",
"eslint": "9.31.0",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-security": "3.0.1",
"prettier": "3.6.1",
"rollup": "4.44.0",
"prettier": "3.6.2",
"rollup": "4.45.1",
"tslib": "2.8.1",
"typescript": "5.8.3",
"typescript-eslint": "8.35.0"
"typescript-eslint": "8.38.0"
},
"pnpm": {
"overrides": {
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.4.0]
- [`aa9140e1`](https://github.com/tauri-apps/plugins-workspace/commit/aa9140e1ac239ab9f015f92b2ed52bbf0eda7c12) ([#2437](https://github.com/tauri-apps/plugins-workspace/pull/2437) by [@enkhjile](https://github.com/tauri-apps/plugins-workspace/../../enkhjile)) Added support for GS1 DataBar on iOS 15.4+
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-barcode-scanner"
version = "2.3.0"
version = "2.4.0"
description = "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS"
edition = { workspace = true }
authors = { workspace = true }
+1 -1
View File
@@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_BARCODE_SCANNER__=function(n){"use strict";async function e(n,e={},r){return window.__TAURI_INTERNALS__.invoke(n,e,r)}var r;return"function"==typeof SuppressedError&&SuppressedError,n.Format=void 0,(r=n.Format||(n.Format={})).QRCode="QR_CODE",r.UPC_A="UPC_A",r.UPC_E="UPC_E",r.EAN8="EAN_8",r.EAN13="EAN_13",r.Code39="CODE_39",r.Code93="CODE_93",r.Code128="CODE_128",r.Codabar="CODABAR",r.ITF="ITF",r.Aztec="AZTEC",r.DataMatrix="DATA_MATRIX",r.PDF417="PDF_417",n.cancel=async function(){await e("plugin:barcode-scanner|cancel")},n.checkPermissions=async function(){return await async function(n){return e(`plugin:${n}|check_permissions`)}("barcode-scanner").then((n=>n.camera))},n.openAppSettings=async function(){await e("plugin:barcode-scanner|open_app_settings")},n.requestPermissions=async function(){return await async function(n){return e(`plugin:${n}|request_permissions`)}("barcode-scanner").then((n=>n.camera))},n.scan=async function(n){return await e("plugin:barcode-scanner|scan",{...n})},n}({});Object.defineProperty(window.__TAURI__,"barcodeScanner",{value:__TAURI_PLUGIN_BARCODE_SCANNER__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_BARCODE_SCANNER__=function(n){"use strict";async function a(n,a={},e){return window.__TAURI_INTERNALS__.invoke(n,a,e)}var e;return"function"==typeof SuppressedError&&SuppressedError,n.Format=void 0,(e=n.Format||(n.Format={})).QRCode="QR_CODE",e.UPC_A="UPC_A",e.UPC_E="UPC_E",e.EAN8="EAN_8",e.EAN13="EAN_13",e.Code39="CODE_39",e.Code93="CODE_93",e.Code128="CODE_128",e.Codabar="CODABAR",e.ITF="ITF",e.Aztec="AZTEC",e.DataMatrix="DATA_MATRIX",e.PDF417="PDF_417",e.GS1DataBar="GS1_DATA_BAR",e.GS1DataBarLimited="GS1_DATA_BAR_LIMITED",e.GS1DataBarExpanded="GS1_DATA_BAR_EXPANDED",n.cancel=async function(){await a("plugin:barcode-scanner|cancel")},n.checkPermissions=async function(){return await async function(n){return a(`plugin:${n}|check_permissions`)}("barcode-scanner").then((n=>n.camera))},n.openAppSettings=async function(){await a("plugin:barcode-scanner|open_app_settings")},n.requestPermissions=async function(){return await async function(n){return a(`plugin:${n}|request_permissions`)}("barcode-scanner").then((n=>n.camera))},n.scan=async function(n){return await a("plugin:barcode-scanner|scan",{...n})},n}({});Object.defineProperty(window.__TAURI__,"barcodeScanner",{value:__TAURI_PLUGIN_BARCODE_SCANNER__})}
+19 -1
View File
@@ -12,6 +12,9 @@ export type { PermissionState } from '@tauri-apps/api/core'
export enum Format {
QRCode = 'QR_CODE',
/**
* Not supported on iOS.
*/
UPC_A = 'UPC_A',
UPC_E = 'UPC_E',
EAN8 = 'EAN_8',
@@ -19,11 +22,26 @@ export enum Format {
Code39 = 'CODE_39',
Code93 = 'CODE_93',
Code128 = 'CODE_128',
/**
* Not supported on iOS.
*/
Codabar = 'CODABAR',
ITF = 'ITF',
Aztec = 'AZTEC',
DataMatrix = 'DATA_MATRIX',
PDF417 = 'PDF_417'
PDF417 = 'PDF_417',
/**
* Not supported on Android. Requires iOS 15.4+
*/
GS1DataBar = 'GS1_DATA_BAR',
/**
* Not supported on Android. Requires iOS 15.4+
*/
GS1DataBarLimited = 'GS1_DATA_BAR_LIMITED',
/**
* Not supported on Android. Requires iOS 15.4+
*/
GS1DataBarExpanded = 'GS1_DATA_BAR_EXPANDED'
}
export interface ScanOptions {
@@ -27,8 +27,11 @@ enum SupportedFormat: String, CaseIterable, Decodable {
case DATA_MATRIX
case PDF_417
case QR_CODE
case GS1_DATA_BAR
case GS1_DATA_BAR_LIMITED
case GS1_DATA_BAR_EXPANDED
var value: AVMetadataObject.ObjectType {
var value: AVMetadataObject.ObjectType? {
switch self {
case .UPC_E: return AVMetadataObject.ObjectType.upce
case .EAN_8: return AVMetadataObject.ObjectType.ean8
@@ -41,6 +44,24 @@ enum SupportedFormat: String, CaseIterable, Decodable {
case .DATA_MATRIX: return AVMetadataObject.ObjectType.dataMatrix
case .PDF_417: return AVMetadataObject.ObjectType.pdf417
case .QR_CODE: return AVMetadataObject.ObjectType.qr
case .GS1_DATA_BAR:
if #available(iOS 15.4, *) {
return AVMetadataObject.ObjectType.gs1DataBar
} else {
return nil
}
case .GS1_DATA_BAR_LIMITED:
if #available(iOS 15.4, *) {
return AVMetadataObject.ObjectType.gs1DataBarLimited
} else {
return nil
}
case .GS1_DATA_BAR_EXPANDED:
if #available(iOS 15.4, *) {
return AVMetadataObject.ObjectType.gs1DataBarExpanded
} else {
return nil
}
}
}
}
@@ -242,13 +263,20 @@ class BarcodeScannerPlugin: Plugin, AVCaptureMetadataOutputObjectsDelegate {
scanFormats = [AVMetadataObject.ObjectType]()
(args.formats ?? []).forEach { format in
scanFormats.append(format.value)
if let formatValue = format.value {
scanFormats.append(formatValue)
} else {
invoke.reject("Unsupported barcode format on this iOS version: \(format)")
return
}
}
if scanFormats.count == 0 {
for supportedFormat in SupportedFormat.allCases {
scanFormats.append(supportedFormat.value)
}
if scanFormats.isEmpty {
for supportedFormat in SupportedFormat.allCases {
if let formatValue = supportedFormat.value {
scanFormats.append(formatValue)
}
}
}
self.metaOutput!.metadataObjectTypes = self.scanFormats
@@ -52,6 +52,15 @@ func discoverCaptureDevices() -> [AVCaptureDevice] {
}
func formatStringFromMetadata(_ type: AVMetadataObject.ObjectType) -> String {
if #available(iOS 15.4, *) {
if type == .gs1DataBar {
return "GS1_DATA_BAR"
} else if type == .gs1DataBarLimited {
return "GS1_DATA_BAR_LIMITED"
} else if type == .gs1DataBarExpanded {
return "GS1_DATA_BAR_EXPANDED"
}
}
switch type {
case AVMetadataObject.ObjectType.upce:
return "UPC_E"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-barcode-scanner",
"version": "2.3.0",
"version": "2.4.0",
"description": "Scan QR codes, EAN-13 and other kinds of barcodes on Android and iOS",
"license": "MIT OR Apache-2.0",
"authors": [
+5
View File
@@ -1,5 +1,10 @@
# Changelog
## \[2.4.1]
- [`d4f8299b`](https://github.com/tauri-apps/plugins-workspace/commit/d4f8299b12f107718c70692840a63768d65baaf9) ([#2844](https://github.com/tauri-apps/plugins-workspace/pull/2844) by [@yobson1](https://github.com/tauri-apps/plugins-workspace/../../yobson1)) Fix deep link protocol handler not set as default on linux
Fix duplicate protocols added to MimeType section in .desktop files on linux
## \[2.4.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+4 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-deep-link"
version = "2.4.0"
version = "2.4.1"
description = "Set your Tauri application as the default handler for an URL"
authors = { workspace = true }
license = { workspace = true }
@@ -17,9 +17,9 @@ targets = ["x86_64-linux-android"]
[package.metadata.platforms.support]
windows = { level = "full", notes = "" }
linux = { level = "full", notes = "" }
macos = { level = "partial", notes = "Runtime deep link registration is not supported" }
android = { level = "partial", notes = "Runtime deep link registration is not supported" }
ios = { level = "partial", notes = "Runtime deep link registration is not supported" }
macos = { level = "partial", notes = "Deep links must be registered in config. Dynamic registration at runtime is not supported." }
android = { level = "partial", notes = "Deep links must be registered in config. Dynamic registration at runtime is not supported." }
ios = { level = "partial", notes = "Deep links must be registered in config. Dynamic registration at runtime is not supported." }
[build-dependencies]
serde = { workspace = true }
@@ -1,5 +1,11 @@
# Changelog
## \[2.2.4]
### Dependencies
- Upgraded to `deep-link-js@2.4.1`
## \[2.2.3]
### Dependencies
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "deep-link-example",
"private": true,
"version": "2.2.3",
"version": "2.2.4",
"type": "module",
"scripts": {
"dev": "vite",
@@ -10,12 +10,12 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.6.0",
"@tauri-apps/plugin-deep-link": "2.4.0"
"@tauri-apps/api": "2.7.0",
"@tauri-apps/plugin-deep-link": "2.4.1"
},
"devDependencies": {
"@tauri-apps/cli": "2.6.0",
"@tauri-apps/cli": "2.7.1",
"typescript": "^5.7.3",
"vite": "^6.2.6"
"vite": "^7.0.4"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-deep-link",
"version": "2.4.0",
"version": "2.4.1",
"description": "Set your Tauri application as the default handler for an URL",
"license": "MIT OR Apache-2.0",
"authors": [
+9 -7
View File
@@ -303,12 +303,14 @@ mod imp {
if let Ok(mut desktop_file) = ini::Ini::load_from_file(&target_file) {
if let Some(section) = desktop_file.section_mut(Some("Desktop Entry")) {
let old_mimes = section.remove("MimeType");
section.append(
"MimeType",
format!("{mime_type};{}", old_mimes.unwrap_or_default()),
);
desktop_file.write_to_file(&target_file)?;
// it's ok to remove it - we only write to the file if it's missing
// and in that case we include old_mimes
let old_mimes = section.remove("MimeType").unwrap_or_default();
if !old_mimes.split(';').any(|mime| mime == mime_type) {
section.append("MimeType", format!("{mime_type};{old_mimes}"));
desktop_file.write_to_file(&target_file)?;
}
}
} else {
let mut file = File::create(target_file)?;
@@ -333,7 +335,7 @@ mod imp {
.status()?;
Command::new("xdg-mime")
.args(["default", &file_name, _protocol.as_ref()])
.args(["default", &file_name, mime_type.as_str()])
.status()?;
Ok(())
+6
View File
@@ -1,5 +1,11 @@
# Changelog
## \[2.3.1]
### Dependencies
- Upgraded to `fs-js@2.4.1`
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-dialog"
version = "2.3.0"
version = "2.3.1"
description = "Native system dialogs for opening and saving files along with message dialogs on your Tauri application."
edition = { workspace = true }
authors = { workspace = true }
@@ -34,7 +34,7 @@ tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
url = { workspace = true }
tauri-plugin-fs = { path = "../fs", version = "2.4.0" }
tauri-plugin-fs = { path = "../fs", version = "2.4.1" }
[target.'cfg(target_os = "ios")'.dependencies]
tauri = { workspace = true, features = ["wry"] }
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-dialog",
"version": "2.3.0",
"version": "2.3.1",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.4.1]
- [`44a1f659`](https://github.com/tauri-apps/plugins-workspace/commit/44a1f659125a341191420e650608b0b6ff316a0e) ([#2846](https://github.com/tauri-apps/plugins-workspace/pull/2846) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Fix `writeFile` doesn't create a new file by default when the data is a `ReadableStream`
## \[2.4.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-fs"
version = "2.4.0"
version = "2.4.1"
description = "Access the file system."
authors = { workspace = true }
license = { workspace = true }
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1074,7 +1074,7 @@ async function writeFile(
}
if (data instanceof ReadableStream) {
const file = await open(path, options)
const file = await open(path, { create: true, ...options })
const reader = data.getReader()
try {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-fs",
"version": "2.4.0",
"version": "2.4.1",
"description": "Access the file system.",
"license": "MIT OR Apache-2.0",
"authors": [
+6
View File
@@ -1,5 +1,11 @@
# Changelog
## \[2.5.1]
### Dependencies
- Upgraded to `fs-js@2.4.1`
## \[2.5.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-http"
version = "2.5.0"
version = "2.5.1"
description = "Access an HTTP client written in Rust."
edition = { workspace = true }
authors = { workspace = true }
@@ -34,7 +34,7 @@ serde_json = { workspace = true }
tauri = { workspace = true }
thiserror = { workspace = true }
tokio = { version = "1", features = ["sync", "macros"] }
tauri-plugin-fs = { path = "../fs", version = "2.4.0" }
tauri-plugin-fs = { path = "../fs", version = "2.4.1" }
urlpattern = "0.3"
regex = "1"
http = "1"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-http",
"version": "2.5.0",
"version": "2.5.1",
"license": "MIT OR Apache-2.0",
"authors": [
"Tauri Programme within The Commons Conservancy"
+6
View File
@@ -1,5 +1,11 @@
# Changelog
## \[2.3.1]
### Dependencies
- Upgraded to `fs@2.4.1`
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-persisted-scope"
version = "2.3.0"
version = "2.3.1"
description = "Save filesystem and asset scopes and restore them when the app is reopened."
authors = { workspace = true }
license = { workspace = true }
@@ -27,7 +27,7 @@ log = { workspace = true }
thiserror = { workspace = true }
aho-corasick = "1"
bincode = "1"
tauri-plugin-fs = { path = "../fs", version = "2.4.0" }
tauri-plugin-fs = { path = "../fs", version = "2.4.1" }
[features]
protocol-asset = ["tauri/protocol-asset"]
+3 -3
View File
@@ -388,7 +388,7 @@ class Command<O extends IOPayload> extends EventEmitter<CommandEvents> {
* Creates a new `Command` instance.
*
* @param program The program name to execute.
* It must be configured on `tauri.conf.json > plugins > shell > scope`.
* It must be configured in your project's capabilities.
* @param args Program arguments.
* @param options Spawn options.
*/
@@ -425,7 +425,7 @@ class Command<O extends IOPayload> extends EventEmitter<CommandEvents> {
* ```
*
* @param program The program to execute.
* It must be configured on `tauri.conf.json > plugins > shell > scope`.
* It must be configured in your project's capabilities.
*/
static create<O extends IOPayload>(
program: string,
@@ -457,7 +457,7 @@ class Command<O extends IOPayload> extends EventEmitter<CommandEvents> {
* ```
*
* @param program The program to execute.
* It must be configured on `tauri.conf.json > plugins > shell > scope`.
* It must be configured in your project's capabilities.
*/
static sidecar<O extends IOPayload>(
program: string,
+11
View File
@@ -1,5 +1,16 @@
# Changelog
## \[2.3.2]
### Dependencies
- Upgraded to `deep-link@2.4.1`
## \[2.3.1]
- [`6f345870`](https://github.com/tauri-apps/plugins-workspace/commit/6f345870df4e7b187deb869df03b79858e03b4fe) ([#2860](https://github.com/tauri-apps/plugins-workspace/pull/2860) by [@MorpheusXAUT](https://github.com/tauri-apps/plugins-workspace/../../MorpheusXAUT)) Fix D-Bus name replacement logic on Linux to prevent multiple instances from acquiring the same well-known name.\
This patch updates the `zbus` dependency to the latest compatible version (`^5.9`) and explicitly sets `RequestNameFlags` to ensure a second instance fails to acquire the D-Bus name when another one is already running.
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-single-instance"
version = "2.3.0"
version = "2.3.2"
description = "Ensure a single instance of your tauri app is running."
authors = { workspace = true }
license = { workspace = true }
@@ -26,7 +26,7 @@ serde_json = { workspace = true }
tauri = { workspace = true }
tracing = { workspace = true }
thiserror = { workspace = true }
tauri-plugin-deep-link = { path = "../deep-link", version = "2.4.0", optional = true }
tauri-plugin-deep-link = { path = "../deep-link", version = "2.4.1", optional = true }
semver = { version = "1", optional = true }
[target."cfg(target_os = \"windows\")".dependencies.windows-sys]
@@ -9,6 +9,6 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@tauri-apps/cli": "2.6.0"
"@tauri-apps/cli": "2.7.1"
}
}
@@ -61,6 +61,8 @@ pub fn init<R: Runtime>(f: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
.unwrap()
.name(dbus_name.as_str())
.unwrap()
.replace_existing_names(false)
.allow_name_replacements(false)
.serve_at(dbus_path.as_str(), single_instance_dbus)
.unwrap()
.build()
@@ -8,8 +8,8 @@
"tauri": "tauri"
},
"devDependencies": {
"@tauri-apps/cli": "2.6.0",
"@tauri-apps/cli": "2.7.1",
"typescript": "^5.7.3",
"vite": "^6.2.6"
"vite": "^7.0.4"
}
}
+5
View File
@@ -1,5 +1,10 @@
# Changelog
## \[2.3.1]
- [`b7292030`](https://github.com/tauri-apps/plugins-workspace/commit/b7292030594daa04e78979214478031241b6e38e) ([#2838](https://github.com/tauri-apps/plugins-workspace/pull/2838) by [@velocitysystems](https://github.com/tauri-apps/plugins-workspace/../../velocitysystems)) Fix `download` and `upload` locks main thread on Android.
Use Tokio to spawn task when invoking commands.
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-upload"
version = "2.3.0"
version = "2.3.1"
description = "Upload files from disk to a remote server over HTTP."
authors = { workspace = true }
license = { workspace = true }
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-upload",
"version": "2.3.0",
"version": "2.3.1",
"description": "Upload files from disk to a remote server over HTTP.",
"license": "MIT OR Apache-2.0",
"authors": [
+152 -70
View File
@@ -66,99 +66,106 @@ struct ProgressPayload {
#[command]
async fn download(
url: &str,
file_path: &str,
url: String,
file_path: String,
headers: HashMap<String, String>,
body: Option<String>,
on_progress: Channel<ProgressPayload>,
) -> Result<()> {
let client = reqwest::Client::new();
let mut request = if let Some(body) = body {
client.post(url).body(body)
} else {
client.get(url)
};
// Loop trought the headers keys and values
// and add them to the request object.
for (key, value) in headers {
request = request.header(&key, value);
}
tokio::spawn(async move {
let client = reqwest::Client::new();
let mut request = if let Some(body) = body {
client.post(&url).body(body)
} else {
client.get(&url)
};
// Loop trought the headers keys and values
// and add them to the request object.
for (key, value) in headers {
request = request.header(&key, value);
}
let response = request.send().await?;
if !response.status().is_success() {
return Err(Error::HttpErrorCode(
response.status().as_u16(),
response.text().await.unwrap_or_default(),
));
}
let total = response.content_length().unwrap_or(0);
let response = request.send().await?;
if !response.status().is_success() {
return Err(Error::HttpErrorCode(
response.status().as_u16(),
response.text().await.unwrap_or_default(),
));
}
let total = response.content_length().unwrap_or(0);
let mut file = BufWriter::new(File::create(file_path).await?);
let mut stream = response.bytes_stream();
let mut file = BufWriter::new(File::create(&file_path).await?);
let mut stream = response.bytes_stream();
let mut stats = TransferStats::default();
while let Some(chunk) = stream.try_next().await? {
file.write_all(&chunk).await?;
stats.record_chunk_transfer(chunk.len());
let _ = on_progress.send(ProgressPayload {
progress: chunk.len() as u64,
progress_total: stats.total_transferred,
total,
transfer_speed: stats.transfer_speed,
});
}
file.flush().await?;
Ok(())
let mut stats = TransferStats::default();
while let Some(chunk) = stream.try_next().await? {
file.write_all(&chunk).await?;
stats.record_chunk_transfer(chunk.len());
let _ = on_progress.send(ProgressPayload {
progress: chunk.len() as u64,
progress_total: stats.total_transferred,
total,
transfer_speed: stats.transfer_speed,
});
}
file.flush().await?;
Ok(())
})
.await
.map_err(|e| Error::Io(std::io::Error::other(e.to_string())))?
}
#[command]
async fn upload(
url: &str,
file_path: &str,
url: String,
file_path: String,
headers: HashMap<String, String>,
on_progress: Channel<ProgressPayload>,
) -> Result<String> {
// Read the file
let file = File::open(file_path).await?;
let file_len = file.metadata().await.unwrap().len();
tokio::spawn(async move {
// Read the file
let file = File::open(&file_path).await?;
let file_len = file.metadata().await.unwrap().len();
// Create the request and attach the file to the body
let client = reqwest::Client::new();
let mut request = client
.post(url)
.header(reqwest::header::CONTENT_LENGTH, file_len)
.body(file_to_body(on_progress, file));
// Create the request and attach the file to the body
let client = reqwest::Client::new();
let mut request = client
.post(&url)
.header(reqwest::header::CONTENT_LENGTH, file_len)
.body(file_to_body(on_progress, file, file_len));
// Loop through the headers keys and values
// and add them to the request object.
for (key, value) in headers {
request = request.header(&key, value);
}
// Loop through the headers keys and values
// and add them to the request object.
for (key, value) in headers {
request = request.header(&key, value);
}
let response = request.send().await?;
if response.status().is_success() {
response.text().await.map_err(Into::into)
} else {
Err(Error::HttpErrorCode(
response.status().as_u16(),
response.text().await.unwrap_or_default(),
))
}
let response = request.send().await?;
if response.status().is_success() {
response.text().await.map_err(Into::into)
} else {
Err(Error::HttpErrorCode(
response.status().as_u16(),
response.text().await.unwrap_or_default(),
))
}
})
.await
.map_err(|e| Error::Io(std::io::Error::other(e.to_string())))?
}
fn file_to_body(channel: Channel<ProgressPayload>, file: File) -> reqwest::Body {
fn file_to_body(channel: Channel<ProgressPayload>, file: File, file_len: u64) -> reqwest::Body {
let stream = FramedRead::new(file, BytesCodec::new()).map_ok(|r| r.freeze());
let mut stats = TransferStats::default();
reqwest::Body::wrap_stream(ReadProgressStream::new(
stream,
Box::new(move |progress, total| {
Box::new(move |progress, _total| {
stats.record_chunk_transfer(progress as usize);
let _ = channel.send(ProgressPayload {
progress,
progress_total: stats.total_transferred,
total,
total: file_len,
transfer_speed: stats.transfer_speed,
});
}),
@@ -183,9 +190,9 @@ mod tests {
}
#[tokio::test]
async fn should_error_if_status_not_success() {
async fn should_error_on_download_if_status_not_success() {
let mocked_server = spawn_server_mocked(400).await;
let result = download_file(&mocked_server.url).await;
let result = download_file(mocked_server.url).await;
mocked_server.mocked_endpoint.assert();
assert!(result.is_err());
}
@@ -193,7 +200,7 @@ mod tests {
#[tokio::test]
async fn should_download_file_successfully() {
let mocked_server = spawn_server_mocked(200).await;
let result = download_file(&mocked_server.url).await;
let result = download_file(mocked_server.url).await;
mocked_server.mocked_endpoint.assert();
assert!(
result.is_ok(),
@@ -202,8 +209,53 @@ mod tests {
);
}
async fn download_file(url: &str) -> Result<()> {
let file_path = concat!(env!("CARGO_MANIFEST_DIR"), "/test/test.txt");
#[tokio::test]
async fn should_error_on_upload_if_status_not_success() {
let mocked_server = spawn_upload_server_mocked(500).await;
let result = upload_file(mocked_server.url).await;
mocked_server.mocked_endpoint.assert();
assert!(result.is_err());
match result.unwrap_err() {
Error::HttpErrorCode(status, _) => assert_eq!(status, 500),
_ => panic!("Expected HttpErrorCode error"),
}
}
#[tokio::test]
async fn should_error_on_upload_if_file_not_found() {
let mocked_server = spawn_upload_server_mocked(200).await;
let file_path = "/nonexistent/file.txt".to_string();
let headers = HashMap::new();
let sender: Channel<ProgressPayload> =
Channel::new(|msg: InvokeResponseBody| -> tauri::Result<()> {
let _ = msg;
Ok(())
});
let result = upload(mocked_server.url, file_path, headers, sender).await;
assert!(result.is_err());
match result.unwrap_err() {
Error::Io(_) => {}
_ => panic!("Expected IO error for missing file"),
}
}
#[tokio::test]
async fn should_upload_file_successfully() {
let mocked_server = spawn_upload_server_mocked(200).await;
let result = upload_file(mocked_server.url).await;
mocked_server.mocked_endpoint.assert();
assert!(
result.is_ok(),
"failed to upload file: {}",
result.unwrap_err()
);
let response_body = result.unwrap();
assert_eq!(response_body, "upload successful");
}
async fn download_file(url: String) -> Result<()> {
let file_path = concat!(env!("CARGO_MANIFEST_DIR"), "/test/test.txt").to_string();
let headers = HashMap::new();
let sender: Channel<ProgressPayload> =
Channel::new(|msg: InvokeResponseBody| -> tauri::Result<()> {
@@ -213,6 +265,17 @@ mod tests {
download(url, file_path, headers, None, sender).await
}
async fn upload_file(url: String) -> Result<String> {
let file_path = concat!(env!("CARGO_MANIFEST_DIR"), "/test/test.txt").to_string();
let headers = HashMap::new();
let sender: Channel<ProgressPayload> =
Channel::new(|msg: InvokeResponseBody| -> tauri::Result<()> {
let _ = msg;
Ok(())
});
upload(url, file_path, headers, sender).await
}
async fn spawn_server_mocked(return_status: usize) -> MockedServer {
let mut _server = Server::new_async().await;
let path = "/mock_test";
@@ -230,4 +293,23 @@ mod tests {
mocked_endpoint: mock,
}
}
async fn spawn_upload_server_mocked(return_status: usize) -> MockedServer {
let mut _server = Server::new_async().await;
let path = "/upload_test";
let mock = _server
.mock("POST", path)
.with_status(return_status)
.with_body("upload successful")
.match_header("content-length", "20")
.create_async()
.await;
let url = _server.url() + path;
MockedServer {
_server,
url,
mocked_endpoint: mock,
}
}
}
@@ -9,9 +9,9 @@
"preview": "vite preview"
},
"devDependencies": {
"@tauri-apps/cli": "2.6.0",
"@tauri-apps/cli": "2.7.1",
"typescript": "^5.7.3",
"vite": "^6.2.6"
"vite": "^7.0.4"
},
"dependencies": {
"tauri-plugin-websocket-api": "link:..\\.."
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## \[2.4.0]
- [`6a8f2558`](https://github.com/tauri-apps/plugins-workspace/commit/6a8f25587852b958a9e48b8c2e8884cc94a7dc7f) ([#2619](https://github.com/tauri-apps/plugins-workspace/pull/2619) by [@Legend-Master](https://github.com/tauri-apps/plugins-workspace/../../Legend-Master)) Making `flags` optional in the `saveWindowState`, `restoreState`, `restoreStateCurrent` JavaScripts APIs, leaving it empty will make it use plugin's default flags
## \[2.3.0]
- [`f209b2f2`](https://github.com/tauri-apps/plugins-workspace/commit/f209b2f23cb29133c97ad5961fb46ef794dbe063) ([#2804](https://github.com/tauri-apps/plugins-workspace/pull/2804) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated tauri to 2.6
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tauri-plugin-window-state"
version = "2.3.0"
version = "2.4.0"
description = "Save window positions and sizes and restore them when the app is reopened."
authors = { workspace = true }
license = { workspace = true }
+3 -3
View File
@@ -18,7 +18,7 @@ export enum StateFlags {
/**
* Save the state of all open windows to disk.
*/
async function saveWindowState(flags: StateFlags): Promise<void> {
async function saveWindowState(flags?: StateFlags): Promise<void> {
await invoke('plugin:window-state|save_window_state', { flags })
}
@@ -27,7 +27,7 @@ async function saveWindowState(flags: StateFlags): Promise<void> {
*/
async function restoreState(
label: WindowLabel,
flags: StateFlags
flags?: StateFlags
): Promise<void> {
await invoke('plugin:window-state|restore_state', { label, flags })
}
@@ -35,7 +35,7 @@ async function restoreState(
/**
* Restore the state for the current window from disk.
*/
async function restoreStateCurrent(flags: StateFlags): Promise<void> {
async function restoreStateCurrent(flags?: StateFlags): Promise<void> {
await restoreState(getCurrentWindow().label, flags)
}
/**
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tauri-apps/plugin-window-state",
"version": "2.3.0",
"version": "2.4.0",
"description": "Save window positions and sizes and restore them when the app is reopened.",
"license": "MIT OR Apache-2.0",
"authors": [
+18 -7
View File
@@ -5,13 +5,25 @@
use crate::{AppHandleExt, StateFlags, WindowExt};
use tauri::{command, AppHandle, Manager, Runtime};
fn get_state_flags<R: Runtime>(
app: &AppHandle<R>,
flags: Option<u32>,
) -> std::result::Result<StateFlags, String> {
let flags = if let Some(flags) = flags {
StateFlags::from_bits(flags).ok_or_else(|| format!("Invalid state flags bits: {flags}"))?
} else {
let plugin_state = app.state::<crate::PluginState>();
plugin_state.state_flags
};
Ok(flags)
}
#[command]
pub async fn save_window_state<R: Runtime>(
app: AppHandle<R>,
flags: u32,
flags: Option<u32>,
) -> std::result::Result<(), String> {
let flags = StateFlags::from_bits(flags)
.ok_or_else(|| format!("Invalid state flags bits: {}", flags))?;
let flags = get_state_flags(&app, flags)?;
app.save_window_state(flags).map_err(|e| e.to_string())?;
Ok(())
}
@@ -20,12 +32,11 @@ pub async fn save_window_state<R: Runtime>(
pub async fn restore_state<R: Runtime>(
app: AppHandle<R>,
label: String,
flags: u32,
flags: Option<u32>,
) -> std::result::Result<(), String> {
let flags = StateFlags::from_bits(flags)
.ok_or_else(|| format!("Invalid state flags bits: {}", flags))?;
let flags = get_state_flags(&app, flags)?;
app.get_webview_window(&label)
.ok_or_else(|| format!("Couldn't find window with label: {}", label))?
.ok_or_else(|| format!("Couldn't find window with label: {label}"))?
.restore_state(flags)
.map_err(|e| e.to_string())?;
Ok(())
+10 -8
View File
@@ -60,12 +60,14 @@ bitflags! {
}
impl Default for StateFlags {
/// Default to [`all`](Self::all)
fn default() -> Self {
Self::all()
}
}
struct PluginState {
pub(crate) state_flags: StateFlags,
filename: String,
map_label: Option<Box<LabelMapperFn>>,
}
@@ -381,7 +383,7 @@ impl Builder {
}
pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
let flags = self.state_flags;
let state_flags = self.state_flags;
let filename = self.filename.unwrap_or_else(|| DEFAULT_FILENAME.into());
let map_label = self.map_label;
@@ -391,11 +393,12 @@ impl Builder {
cmd::restore_state,
cmd::filename
])
.setup(|app, _api| {
.setup(move |app, _api| {
let cache = load_saved_window_states(app, &filename).unwrap_or_default();
app.manage(WindowStateCache(Arc::new(Mutex::new(cache))));
app.manage(RestoringWindowState(Mutex::new(())));
app.manage(PluginState {
state_flags,
filename,
map_label,
});
@@ -423,14 +426,13 @@ impl Builder {
}
if !self.skip_initial_state.contains(label) {
let _ = window.restore_state(self.state_flags);
let _ = window.restore_state(state_flags);
}
let cache = window.state::<WindowStateCache>();
let cache = cache.0.clone();
let label = label.to_string();
let window_clone = window.clone();
let flags = self.state_flags;
// insert a default state if this window should be tracked and
// the disk cache doesn't have a state for it
@@ -446,11 +448,11 @@ impl Builder {
WindowEvent::CloseRequested { .. } => {
let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) {
let _ = window_clone.update_state(state, flags);
let _ = window_clone.update_state(state, state_flags);
}
}
WindowEvent::Moved(position) if flags.contains(StateFlags::POSITION) => {
WindowEvent::Moved(position) if state_flags.contains(StateFlags::POSITION) => {
if window_clone
.state::<RestoringWindowState>()
.0
@@ -468,7 +470,7 @@ impl Builder {
}
}
}
WindowEvent::Resized(size) if flags.contains(StateFlags::SIZE) => {
WindowEvent::Resized(size) if state_flags.contains(StateFlags::SIZE) => {
if window_clone
.state::<RestoringWindowState>()
.0
@@ -499,7 +501,7 @@ impl Builder {
})
.on_event(move |app, event| {
if let RunEvent::Exit = event {
let _ = app.save_window_state(flags);
let _ = app.save_window_state(state_flags);
}
})
.build()
+743 -683
View File
File diff suppressed because it is too large Load Diff