|
|
@ -1,178 +1,203 @@
|
|
|
|
<script>
|
|
|
|
<script>
|
|
|
|
import * as fs from "@tauri-apps/plugin-fs";
|
|
|
|
import * as fs from '@tauri-apps/plugin-fs'
|
|
|
|
import { convertFileSrc } from "@tauri-apps/api/core";
|
|
|
|
import * as os from '@tauri-apps/plugin-os'
|
|
|
|
import { arrayBufferToBase64 } from "../lib/utils";
|
|
|
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
|
|
|
|
|
|
|
import { arrayBufferToBase64 } from '../lib/utils'
|
|
|
|
export let onMessage;
|
|
|
|
import { onMount } from 'svelte'
|
|
|
|
export let insecureRenderHtml;
|
|
|
|
|
|
|
|
|
|
|
|
export let onMessage
|
|
|
|
let path = "";
|
|
|
|
export let insecureRenderHtml
|
|
|
|
let img;
|
|
|
|
|
|
|
|
let file;
|
|
|
|
let path = ''
|
|
|
|
let renameTo;
|
|
|
|
let img
|
|
|
|
let watchPath = "";
|
|
|
|
let file
|
|
|
|
let watchDebounceDelay = 0;
|
|
|
|
let renameTo
|
|
|
|
let watchRecursive = false;
|
|
|
|
let watchPath = ''
|
|
|
|
let unwatchFn;
|
|
|
|
let watchDebounceDelay = 0
|
|
|
|
let unwatchPath = "";
|
|
|
|
let watchRecursive = false
|
|
|
|
|
|
|
|
let unwatchFn
|
|
|
|
|
|
|
|
let unwatchPath = ''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let isMobile
|
|
|
|
|
|
|
|
onMount(async () => {
|
|
|
|
|
|
|
|
let platform = await os.platform()
|
|
|
|
|
|
|
|
isMobile = platform === 'android' || platform === 'ios'
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
function getDir() {
|
|
|
|
function getDir() {
|
|
|
|
const dirSelect = document.getElementById("dir");
|
|
|
|
const dirSelect = document.getElementById('dir')
|
|
|
|
return dirSelect.value ? parseInt(dir.value) : null;
|
|
|
|
return dirSelect.value ? parseInt(dir.value) : null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const DirOptions = Object.keys(fs.BaseDirectory)
|
|
|
|
const DirOptions = Object.keys(fs.BaseDirectory)
|
|
|
|
.filter((key) => isNaN(parseInt(key)))
|
|
|
|
.filter((key) => isNaN(parseInt(key)))
|
|
|
|
.map((dir) => [dir, fs.BaseDirectory[dir]]);
|
|
|
|
.map((dir) => [dir, fs.BaseDirectory[dir]])
|
|
|
|
|
|
|
|
|
|
|
|
function open() {
|
|
|
|
function open() {
|
|
|
|
fs.open(path, {
|
|
|
|
fs.open(path, {
|
|
|
|
baseDir: getDir(),
|
|
|
|
baseDir: getDir(),
|
|
|
|
read: true,
|
|
|
|
read: true,
|
|
|
|
write: true,
|
|
|
|
write: true,
|
|
|
|
create: true,
|
|
|
|
create: true
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.then((f) => {
|
|
|
|
.then((f) => {
|
|
|
|
file = f;
|
|
|
|
file = f
|
|
|
|
onMessage(`Opened ${path}`);
|
|
|
|
onMessage(`Opened ${path}`)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function mkdir() {
|
|
|
|
function mkdir() {
|
|
|
|
fs.mkdir(path, { baseDir: getDir() })
|
|
|
|
fs.mkdir(path, { baseDir: getDir(), recursive: true })
|
|
|
|
.then(() => {
|
|
|
|
.then(() => {
|
|
|
|
onMessage(`Created dir ${path}`);
|
|
|
|
onMessage(`Created dir ${path}`)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function remove() {
|
|
|
|
function remove() {
|
|
|
|
fs.remove(path, { baseDir: getDir() })
|
|
|
|
fs.remove(path, { baseDir: getDir() })
|
|
|
|
.then(() => {
|
|
|
|
.then(() => {
|
|
|
|
onMessage(`Removed ${path}`);
|
|
|
|
onMessage(`Removed ${path}`)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function rename() {
|
|
|
|
function rename() {
|
|
|
|
fs.rename(path, renameTo, {
|
|
|
|
fs.rename(path, renameTo, {
|
|
|
|
oldPathBaseDir: getDir(),
|
|
|
|
oldPathBaseDir: getDir(),
|
|
|
|
newPathBaseDir: getDir(),
|
|
|
|
newPathBaseDir: getDir()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
.then(() => {
|
|
|
|
onMessage(`Renamed ${path} to ${renameTo}`);
|
|
|
|
onMessage(`Renamed ${path} to ${renameTo}`)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function truncate() {
|
|
|
|
function truncate() {
|
|
|
|
file
|
|
|
|
file
|
|
|
|
.truncate(0)
|
|
|
|
.truncate(0)
|
|
|
|
.then(() => {
|
|
|
|
.then(() => {
|
|
|
|
onMessage(`Truncated file`);
|
|
|
|
onMessage(`Truncated file`)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.catch(onMessage)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function write() {
|
|
|
|
|
|
|
|
const encoder = new TextEncoder()
|
|
|
|
|
|
|
|
file
|
|
|
|
|
|
|
|
.write(encoder.encode('Hello from Tauri :)'))
|
|
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
|
|
onMessage(`wrote to file`)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function stat() {
|
|
|
|
function stat() {
|
|
|
|
file
|
|
|
|
file
|
|
|
|
.stat()
|
|
|
|
.stat()
|
|
|
|
.then((stat) => {
|
|
|
|
.then((stat) => {
|
|
|
|
onMessage(`File stat ${JSON.stringify(stat)}`);
|
|
|
|
onMessage(`File stat ${JSON.stringify(stat)}`)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function read() {
|
|
|
|
function read() {
|
|
|
|
const opts = {
|
|
|
|
const opts = {
|
|
|
|
baseDir: getDir(),
|
|
|
|
baseDir: getDir()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
fs.stat(path, opts)
|
|
|
|
fs.stat(path, opts)
|
|
|
|
.then((stat) => {
|
|
|
|
.then((stat) => {
|
|
|
|
const isFile = stat.isFile;
|
|
|
|
const isFile = stat.isFile
|
|
|
|
|
|
|
|
|
|
|
|
const promise = isFile
|
|
|
|
const promise = isFile
|
|
|
|
? fs.readFile(path, opts)
|
|
|
|
? fs.readFile(path, opts)
|
|
|
|
: fs.readDir(path, opts);
|
|
|
|
: fs.readDir(path, opts)
|
|
|
|
promise
|
|
|
|
promise
|
|
|
|
.then(function (response) {
|
|
|
|
.then(function (response) {
|
|
|
|
if (isFile) {
|
|
|
|
if (isFile) {
|
|
|
|
if (path.includes(".png") || path.includes(".jpg")) {
|
|
|
|
if (path.includes('.png') || path.includes('.jpg')) {
|
|
|
|
arrayBufferToBase64(
|
|
|
|
arrayBufferToBase64(
|
|
|
|
new Uint8Array(response),
|
|
|
|
new Uint8Array(response),
|
|
|
|
function (base64) {
|
|
|
|
function (base64) {
|
|
|
|
const src = "data:image/png;base64," + base64;
|
|
|
|
const src = 'data:image/png;base64,' + base64
|
|
|
|
insecureRenderHtml('<img src="' + src + '"></img>');
|
|
|
|
insecureRenderHtml('<img src="' + src + '"></img>')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const value = String.fromCharCode.apply(null, response);
|
|
|
|
const value = String.fromCharCode.apply(null, response)
|
|
|
|
insecureRenderHtml(
|
|
|
|
insecureRenderHtml(
|
|
|
|
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
|
|
|
|
'<textarea id="file-response"></textarea><button id="file-save">Save</button>'
|
|
|
|
);
|
|
|
|
)
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
const fileInput = document.getElementById("file-response");
|
|
|
|
const fileInput = document.getElementById('file-response')
|
|
|
|
fileInput.value = value;
|
|
|
|
fileInput.value = value
|
|
|
|
document
|
|
|
|
document
|
|
|
|
.getElementById("file-save")
|
|
|
|
.getElementById('file-save')
|
|
|
|
.addEventListener("click", function () {
|
|
|
|
.addEventListener('click', function () {
|
|
|
|
fs.writeTextFile(path, fileInput.value, {
|
|
|
|
fs.writeTextFile(path, fileInput.value, {
|
|
|
|
dir: getDir(),
|
|
|
|
dir: getDir()
|
|
|
|
}).catch(onMessage);
|
|
|
|
}).catch(onMessage)
|
|
|
|
});
|
|
|
|
})
|
|
|
|
});
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
onMessage(response);
|
|
|
|
onMessage(response)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function setSrc() {
|
|
|
|
function setSrc() {
|
|
|
|
img.src = convertFileSrc(path);
|
|
|
|
img.src = convertFileSrc(path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function watch() {
|
|
|
|
function watch() {
|
|
|
|
unwatch();
|
|
|
|
unwatch()
|
|
|
|
if (watchPath) {
|
|
|
|
if (watchPath) {
|
|
|
|
onMessage(`Watching ${watchPath} for changes`);
|
|
|
|
onMessage(`Watching ${watchPath} for changes`)
|
|
|
|
let options = {
|
|
|
|
let options = {
|
|
|
|
recursive: watchRecursive,
|
|
|
|
recursive: watchRecursive,
|
|
|
|
delayMs: parseInt(watchDebounceDelay),
|
|
|
|
delayMs: parseInt(watchDebounceDelay)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (options.delayMs === 0) {
|
|
|
|
if (options.delayMs === 0) {
|
|
|
|
fs.watchImmediate(watchPath, onMessage, options)
|
|
|
|
fs.watchImmediate(watchPath, onMessage, options)
|
|
|
|
.then((fn) => {
|
|
|
|
.then((fn) => {
|
|
|
|
unwatchFn = fn;
|
|
|
|
unwatchFn = fn
|
|
|
|
unwatchPath = watchPath;
|
|
|
|
unwatchPath = watchPath
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
fs.watch(watchPath, onMessage, options)
|
|
|
|
fs.watch(watchPath, onMessage, options)
|
|
|
|
.then((fn) => {
|
|
|
|
.then((fn) => {
|
|
|
|
unwatchFn = fn;
|
|
|
|
unwatchFn = fn
|
|
|
|
unwatchPath = watchPath;
|
|
|
|
unwatchPath = watchPath
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(onMessage);
|
|
|
|
.catch(onMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function unwatch() {
|
|
|
|
function unwatch() {
|
|
|
|
if (unwatchFn) {
|
|
|
|
if (unwatchFn) {
|
|
|
|
onMessage(`Stopped watching ${unwatchPath} for changes`);
|
|
|
|
onMessage(`Stopped watching ${unwatchPath} for changes`)
|
|
|
|
unwatchFn();
|
|
|
|
unwatchFn()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unwatchFn = undefined;
|
|
|
|
unwatchFn = undefined
|
|
|
|
unwatchPath = undefined;
|
|
|
|
unwatchPath = undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="flex flex-col">
|
|
|
|
<div class="flex flex-col">
|
|
|
|
|
|
|
|
{#if isMobile}
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
On mobile, paths outside of App* paths require the use of dialogs
|
|
|
|
|
|
|
|
regardless of Tauri's scope mechanism.
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<br />
|
|
|
|
|
|
|
|
{/if}
|
|
|
|
<div class="flex gap-1">
|
|
|
|
<div class="flex gap-1">
|
|
|
|
<select class="input" id="dir">
|
|
|
|
<select class="input" id="dir">
|
|
|
|
<option value="">None</option>
|
|
|
|
<option value="">None</option>
|
|
|
@ -200,6 +225,7 @@
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{#if file}
|
|
|
|
{#if file}
|
|
|
|
<div>
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<button class="btn" on:click={write}>Write</button>
|
|
|
|
<button class="btn" on:click={truncate}>Truncate</button>
|
|
|
|
<button class="btn" on:click={truncate}>Truncate</button>
|
|
|
|
<button class="btn" on:click={stat}>Stat</button>
|
|
|
|
<button class="btn" on:click={stat}>Stat</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|