Merge remote-tracking branch 'origin/v2' into plugin/secure-storage

This commit is contained in:
FabianLars
2025-10-14 17:48:38 +02:00
143 changed files with 2632 additions and 1262 deletions
+31
View File
@@ -1,5 +1,36 @@
# Changelog
## \[2.0.32]
### Dependencies
- Upgraded to `dialog-js@2.4.0`
- Upgraded to `log-js@2.7.0`
## \[2.0.31]
### Dependencies
- Upgraded to `shell-js@2.3.1`
## \[2.0.30]
### Dependencies
- Upgraded to `notification-js@2.3.1`
## \[2.0.29]
### Dependencies
- Upgraded to `fs-js@2.4.2`
- Upgraded to `nfc-js@2.3.1`
- Upgraded to `opener-js@2.5.0`
- Upgraded to `os-js@2.3.1`
- Upgraded to `store-js@2.4.0`
- Upgraded to `dialog-js@2.3.3`
- Upgraded to `http-js@2.5.2`
## \[2.0.28]
### Dependencies
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=0"
/>
<title>Svelte + Vite App</title>
</head>
+13 -13
View File
@@ -1,7 +1,7 @@
{
"name": "api",
"private": true,
"version": "2.0.28",
"version": "2.0.32",
"type": "module",
"scripts": {
"dev": "vite --clearScreen false",
@@ -10,25 +10,25 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "2.7.0",
"@tauri-apps/api": "2.8.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.2",
"@tauri-apps/plugin-fs": "^2.4.1",
"@tauri-apps/plugin-dialog": "^2.4.0",
"@tauri-apps/plugin-fs": "^2.4.2",
"@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.1",
"@tauri-apps/plugin-nfc": "^2.3.0",
"@tauri-apps/plugin-notification": "^2.3.0",
"@tauri-apps/plugin-opener": "^2.4.0",
"@tauri-apps/plugin-os": "^2.3.0",
"@tauri-apps/plugin-http": "^2.5.2",
"@tauri-apps/plugin-nfc": "^2.3.1",
"@tauri-apps/plugin-notification": "^2.3.1",
"@tauri-apps/plugin-opener": "^2.5.0",
"@tauri-apps/plugin-os": "^2.3.1",
"@tauri-apps/plugin-process": "^2.3.0",
"@tauri-apps/plugin-shell": "^2.3.0",
"@tauri-apps/plugin-secure-storage": "file:../../plugins/secure-storage",
"@tauri-apps/plugin-store": "^2.3.0",
"@tauri-apps/plugin-shell": "^2.3.1",
"@tauri-apps/plugin-store": "^2.4.0",
"@tauri-apps/plugin-updater": "^2.9.0",
"@tauri-apps/plugin-upload": "^2.3.0",
"@zerodevx/svelte-json-view": "1.0.11"
@@ -37,10 +37,10 @@
"@iconify-json/codicon": "^1.2.12",
"@iconify-json/ph": "^1.2.2",
"@sveltejs/vite-plugin-svelte": "^6.0.0",
"@tauri-apps/cli": "2.7.1",
"@tauri-apps/cli": "2.8.4",
"@unocss/extractor-svelte": "^66.3.3",
"svelte": "^5.20.4",
"unocss": "^66.3.3",
"vite": "^7.0.4"
"vite": "^7.0.7"
}
}
+31
View File
@@ -1,5 +1,36 @@
# Changelog
## \[2.0.36]
### Dependencies
- Upgraded to `dialog@2.4.0`
- Upgraded to `log@2.7.0`
## \[2.0.35]
### Dependencies
- Upgraded to `shell@2.3.1`
## \[2.0.34]
### Dependencies
- Upgraded to `notification@2.3.1`
## \[2.0.33]
### Dependencies
- Upgraded to `fs@2.4.2`
- Upgraded to `nfc@2.3.1`
- Upgraded to `opener@2.5.0`
- Upgraded to `os@2.3.1`
- Upgraded to `store@2.4.0`
- Upgraded to `dialog@2.3.3`
- Upgraded to `http@2.5.2`
## \[2.0.32]
### Dependencies
+11 -11
View File
@@ -1,7 +1,7 @@
[package]
name = "api"
publish = false
version = "2.0.32"
version = "2.0.36"
description = "An example Tauri Application showcasing the api"
edition = "2021"
rust-version = { workspace = true }
@@ -20,25 +20,25 @@ serde = { workspace = true }
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.1", features = [
tauri-plugin-log = { path = "../../../plugins/log", version = "2.7.0" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.4.2", features = [
"watch",
] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.3.0" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.3.2" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.4.0" }
tauri-plugin-http = { path = "../../../plugins/http", features = [
"multipart",
"cookies",
], version = "2.5.1" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.0", features = [
], version = "2.5.2" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.3.1", features = [
"windows7-compat",
] }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.0" }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.3.1" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.3.0" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.4.0" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.5.0" }
tauri-plugin-secure-storage = { path = "../../../plugins/secure-storage" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.0" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.3.0" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.1" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.4.0" }
tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" }
[dependencies.tauri]
@@ -63,7 +63,7 @@ tauri-plugin-window-state = { path = "../../../plugins/window-state", version =
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.4.0" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.0" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.3.1" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.3.0" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.3.0" }
tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.3.0" }
@@ -68,6 +68,9 @@
"fs:allow-rename",
"fs:allow-mkdir",
"fs:allow-remove",
"fs:allow-stat",
"fs:allow-fstat",
"fs:allow-lstat",
"fs:allow-write-text-file",
"fs:read-meta",
"fs:scope-download-recursive",
@@ -75,6 +78,9 @@
{
"identifier": "fs:scope-appdata-recursive",
"allow": [
{
"path": "$APPDATA/db/"
},
{
"path": "$APPDATA/db/**"
}
+21 -9
View File
@@ -44,6 +44,13 @@
await message("Tauri is awesome!");
}
async function msgCustom(result) {
const buttons = { yes: "awesome", no: "amazing", cancel: "stunning" };
await message(`Tauri is: `, { buttons })
.then((res) => onMessage(`Tauri is ${res}`))
.catch(onMessage);
}
function openDialog() {
open({
title: "My wonderful open dialog",
@@ -136,12 +143,17 @@
<label for="dialog-directory">Directory</label>
</div>
<br />
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
<button class="btn" id="save-dialog" on:click={saveDialog}
>Open save dialog</button
>
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom}
>Prompt (custom)</button
>
<button class="btn" id="message-dialog" on:click={msg}>Message</button>
<div class="flex flex-wrap flex-col md:flex-row gap-2 children:flex-shrink-0">
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
<button class="btn" id="save-dialog" on:click={saveDialog}
>Open save dialog</button
>
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom}
>Prompt (custom)</button
>
<button class="btn" id="message-dialog" on:click={msg}>Message</button>
<button class="btn" id="message-dialog" on:click={msgCustom}>Message (custom)</button>
</div>
+114 -93
View File
@@ -1,194 +1,214 @@
<script>
import * as fs from "@tauri-apps/plugin-fs";
import { convertFileSrc } from "@tauri-apps/api/core";
import { arrayBufferToBase64 } from "../lib/utils";
import { onDestroy } from "svelte";
import * as fs from '@tauri-apps/plugin-fs'
import * as os from '@tauri-apps/plugin-os'
import { convertFileSrc } from '@tauri-apps/api/core'
import { arrayBufferToBase64 } from '../lib/utils'
import { onDestroy, onMount } from 'svelte'
export let onMessage;
export let insecureRenderHtml;
const { onMessage, insecureRenderHtml } = $props()
let path = "";
let img;
let path = $state('')
let img
/** @type {fs.FileHandle} */
let file;
let renameTo;
let watchPath = "";
let watchDebounceDelay = "0";
let watchRecursive = false;
let unwatchFn;
let unwatchPath = "";
let file = $state()
let renameTo = $state()
let watchPath = $state('')
let watchDebounceDelay = $state(0)
let watchRecursive = $state(false)
/** @type {fs.BaseDirectory | undefined} */
let baseDir = $state()
let unwatchFn
let unwatchPath = ''
let isMobile = $state(false)
function getDir() {
const dirSelect = document.getElementById("dir");
return dirSelect.value ? parseInt(dir.value) : null;
}
onMount(() => {
let platform = os.platform()
isMobile = platform === 'android' || platform === 'ios'
})
const DirOptions = Object.keys(fs.BaseDirectory)
.filter((key) => isNaN(parseInt(key)))
.map((dir) => [dir, fs.BaseDirectory[dir]]);
const dirOptions = Object.keys(fs.BaseDirectory).filter((key) =>
isNaN(parseInt(key))
)
function open() {
fs.open(path, {
baseDir: getDir(),
baseDir,
read: true,
write: true,
create: true,
create: true
})
.then((f) => {
file = f;
onMessage(`Opened ${path}`);
file = f
onMessage(`Opened ${path}`)
})
.catch(onMessage);
.catch(onMessage)
}
function mkdir() {
fs.mkdir(path, { baseDir: getDir() })
fs.mkdir(path, { baseDir, recursive: true })
.then(() => {
onMessage(`Created dir ${path}`);
onMessage(`Created dir ${path}`)
})
.catch(onMessage);
.catch(onMessage)
}
function remove() {
fs.remove(path, { baseDir: getDir() })
fs.remove(path, { baseDir })
.then(() => {
onMessage(`Removed ${path}`);
onMessage(`Removed ${path}`)
})
.catch(onMessage);
.catch(onMessage)
}
function rename() {
fs.rename(path, renameTo, {
oldPathBaseDir: getDir(),
newPathBaseDir: getDir(),
oldPathBaseDir,
newPathBaseDir
})
.then(() => {
onMessage(`Renamed ${path} to ${renameTo}`);
onMessage(`Renamed ${path} to ${renameTo}`)
})
.catch(onMessage);
.catch(onMessage)
}
function truncate() {
file
.truncate(0)
.then(() => {
onMessage(`Truncated file`);
onMessage(`Truncated file`)
})
.catch(onMessage);
.catch(onMessage)
}
function write() {
const encoder = new TextEncoder()
file
.write(encoder.encode('Hello from Tauri :)'))
.then(() => {
onMessage(`wrote to file`)
})
.catch(onMessage)
}
function stat() {
file
.stat()
.then((stat) => {
onMessage(`File stat ${JSON.stringify(stat)}`);
onMessage(`File stat ${JSON.stringify(stat)}`)
})
.catch(onMessage);
.catch(onMessage)
}
function read() {
const opts = {
baseDir: getDir(),
};
baseDir
}
fs.stat(path, opts)
.then((stat) => {
const isFile = stat.isFile;
const isFile = stat.isFile
const promise = isFile
? fs.readFile(path, opts)
: fs.readDir(path, opts);
: fs.readDir(path, opts)
promise
.then(function (response) {
if (isFile) {
if (path.includes(".png") || path.includes(".jpg")) {
if (path.includes('.png') || path.includes('.jpg')) {
arrayBufferToBase64(
new Uint8Array(response),
function (base64) {
const src = "data:image/png;base64," + base64;
insecureRenderHtml('<img src="' + src + '"></img>');
const src = 'data:image/png;base64,' + base64
insecureRenderHtml('<img src="' + src + '"></img>')
}
);
)
} else {
const value = String.fromCharCode.apply(null, response);
const value = String.fromCharCode.apply(null, response)
insecureRenderHtml(
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
);
)
setTimeout(() => {
const fileInput = document.getElementById("file-response");
fileInput.value = value;
const fileInput = document.getElementById('file-response')
fileInput.value = value
document
.getElementById("file-save")
.addEventListener("click", function () {
.getElementById('file-save')
.addEventListener('click', function () {
fs.writeTextFile(path, fileInput.value, {
baseDir: getDir(),
}).catch(onMessage);
});
});
baseDir
}).catch(onMessage)
})
})
}
} else {
onMessage(response);
onMessage(response)
}
})
.catch(onMessage);
.catch(onMessage)
})
.catch(onMessage);
.catch(onMessage)
}
function setSrc() {
img.src = convertFileSrc(path);
img.src = convertFileSrc(path)
}
function watch() {
unwatch();
unwatch()
if (watchPath) {
onMessage(`Watching ${watchPath} for changes`);
onMessage(`Watching ${watchPath} for changes`)
let options = {
recursive: watchRecursive,
delayMs: parseInt(watchDebounceDelay),
};
delayMs: watchDebounceDelay
}
if (options.delayMs === 0) {
fs.watchImmediate(watchPath, onMessage, options)
.then((fn) => {
unwatchFn = fn;
unwatchPath = watchPath;
unwatchFn = fn
unwatchPath = watchPath
})
.catch(onMessage);
.catch(onMessage)
} else {
fs.watch(watchPath, onMessage, options)
.then((fn) => {
unwatchFn = fn;
unwatchPath = watchPath;
unwatchFn = fn
unwatchPath = watchPath
})
.catch(onMessage);
.catch(onMessage)
}
}
}
function unwatch() {
if (unwatchFn) {
onMessage(`Stopped watching ${unwatchPath} for changes`);
unwatchFn();
onMessage(`Stopped watching ${unwatchPath} for changes`)
unwatchFn()
}
unwatchFn = undefined;
unwatchPath = undefined;
unwatchFn = undefined
unwatchPath = undefined
}
onDestroy(() => {
if (file) {
file.close();
file.close()
}
if (unwatchFn) {
unwatchFn();
unwatchFn()
}
})
</script>
<div class="flex flex-col">
{#if isMobile}
<div>
On mobile, paths outside of App* paths require the use of dialogs
regardless of Tauri's scope mechanism.
</div>
<br />
{/if}
<div class="flex gap-1">
<select class="input" id="dir">
<option value="">None</option>
{#each DirOptions as dir}
<option value={dir[1]}>{dir[0]}</option>
<select class="input" bind:value={baseDir}>
<option value={undefined} selected>None</option>
{#each dirOptions as dir}
<option value={fs.BaseDirectory[dir]}>{dir}</option>
{/each}
</select>
<input
@@ -199,20 +219,21 @@
</div>
<br />
<div>
<button class="btn" on:click={open}>Open</button>
<button class="btn" on:click={read}>Read</button>
<button class="btn" on:click={mkdir}>Mkdir</button>
<button class="btn" on:click={remove}>Remove</button>
<button class="btn" onclick={open}>Open</button>
<button class="btn" onclick={read}>Read</button>
<button class="btn" onclick={mkdir}>Mkdir</button>
<button class="btn" onclick={remove}>Remove</button>
<div class="flex flex-row">
<button class="btn" on:click={rename}>Rename</button>
<button class="btn" onclick={rename}>Rename</button>
<input class="input" bind:value={renameTo} placeholder="To" />
</div>
<button class="btn" type="button" on:click={setSrc}>Use as img src</button>
<button class="btn" type="button" onclick={setSrc}>Use as img src</button>
</div>
{#if file}
<div>
<button class="btn" on:click={truncate}>Truncate</button>
<button class="btn" on:click={stat}>Stat</button>
<button class="btn" onclick={write}>Write</button>
<button class="btn" onclick={truncate}>Truncate</button>
<button class="btn" onclick={stat}>Stat</button>
</div>
{/if}
@@ -241,8 +262,8 @@
</div>
<br />
<div>
<button class="btn" on:click={watch}>Watch</button>
<button class="btn" on:click={unwatch}>Unwatch</button>
<button class="btn" onclick={watch}>Watch</button>
<button class="btn" onclick={unwatch}>Unwatch</button>
</div>
</div>
+14 -4
View File
@@ -1,16 +1,21 @@
<script>
import { sendNotification } from '@tauri-apps/plugin-notification'
export let onMessage
let sound = ''
// send the notification directly
// the backend is responsible for checking the permission
function _sendNotification() {
new Notification('Notification title', {
body: 'This is the notification body'
sendNotification({
title: 'Notification title',
body: 'This is the notification body',
sound: sound || null
})
}
// alternatively, check the permission ourselves
function sendNotification() {
function triggerNotification() {
if (Notification.permission === 'default') {
Notification.requestPermission()
.then(function (response) {
@@ -29,6 +34,11 @@
}
</script>
<button class="btn" id="notification" on:click={sendNotification}>
<input
class="input grow"
placeholder="Notification sound..."
bind:value={sound}
/>
<button class="btn" id="notification" on:click={triggerNotification}>
Send test notification
</button>