Merge remote-tracking branch 'origin/v2' into feat/camera

This commit is contained in:
Lucas Nogueira
2023-09-29 09:07:02 -03:00
760 changed files with 27150 additions and 24730 deletions
+164 -153
View File
@@ -1,116 +1,124 @@
<script>
import { writable } from 'svelte/store'
import { open } from 'tauri-plugin-shell-api'
import { appWindow, getCurrent } from '@tauri-apps/api/window'
import * as os from '@tauri-apps/api/os'
import { writable } from "svelte/store";
import { open } from "@tauri-apps/plugin-shell";
import { getCurrent } from "@tauri-apps/plugin-window";
import * as os from "@tauri-apps/plugin-os";
import Welcome from './views/Welcome.svelte'
import Cli from './views/Cli.svelte'
import Communication from './views/Communication.svelte'
import Dialog from './views/Dialog.svelte'
import FileSystem from './views/FileSystem.svelte'
import Http from './views/Http.svelte'
import Notifications from './views/Notifications.svelte'
import Window from './views/Window.svelte'
import Shortcuts from './views/Shortcuts.svelte'
import Shell from './views/Shell.svelte'
import Updater from './views/Updater.svelte'
import Clipboard from './views/Clipboard.svelte'
import WebRTC from './views/WebRTC.svelte'
import Camera from './views/Camera.svelte'
import Welcome from "./views/Welcome.svelte";
import Cli from "./views/Cli.svelte";
import Communication from "./views/Communication.svelte";
import Dialog from "./views/Dialog.svelte";
import FileSystem from "./views/FileSystem.svelte";
import Http from "./views/Http.svelte";
import Notifications from "./views/Notifications.svelte";
import Window from "./views/Window.svelte";
import Shortcuts from "./views/Shortcuts.svelte";
import Shell from "./views/Shell.svelte";
import Updater from "./views/Updater.svelte";
import Clipboard from "./views/Clipboard.svelte";
import WebRTC from "./views/WebRTC.svelte";
import Scanner from "./views/Scanner.svelte";
import App from './views/App.svelte'
import App from "./views/App.svelte";
import { onMount } from 'svelte'
import { listen } from '@tauri-apps/api/event'
import { ask } from 'tauri-plugin-dialog-api'
import { onMount } from "svelte";
import { ask } from "@tauri-apps/plugin-dialog";
if (appWindow.label !== 'main') {
const appWindow = getCurrent();
if (appWindow.label !== "main") {
appWindow.onCloseRequested(async (event) => {
const confirmed = await confirm('Are you sure?')
const confirmed = await confirm("Are you sure?");
if (!confirmed) {
// user did not confirm closing the window; let's prevent it
event.preventDefault()
event.preventDefault();
}
})
});
}
appWindow.onFileDropEvent((event) => {
onMessage(`File drop: ${JSON.stringify(event.payload)}`)
})
onMessage(`File drop: ${JSON.stringify(event.payload)}`);
});
const userAgent = navigator.userAgent.toLowerCase()
const isMobile = userAgent.includes('android') || userAgent.includes('iphone')
const userAgent = navigator.userAgent.toLowerCase();
const isMobile =
userAgent.includes("android") || userAgent.includes("iphone");
const views = [
{
label: 'Welcome',
label: "Welcome",
component: Welcome,
icon: 'i-ph-hand-waving'
icon: "i-ph-hand-waving",
},
{
label: 'Communication',
label: "Communication",
component: Communication,
icon: 'i-codicon-radio-tower'
icon: "i-codicon-radio-tower",
},
!isMobile && {
label: 'CLI',
label: "CLI",
component: Cli,
icon: 'i-codicon-terminal'
icon: "i-codicon-terminal",
},
{
label: 'Dialog',
label: "Dialog",
component: Dialog,
icon: 'i-codicon-multiple-windows'
icon: "i-codicon-multiple-windows",
},
{
label: 'File system',
label: "File system",
component: FileSystem,
icon: 'i-codicon-files'
icon: "i-codicon-files",
},
{
label: 'HTTP',
label: "HTTP",
component: Http,
icon: 'i-ph-globe-hemisphere-west'
icon: "i-ph-globe-hemisphere-west",
},
!isMobile && {
label: 'Notifications',
label: "Notifications",
component: Notifications,
icon: 'i-codicon-bell-dot'
icon: "i-codicon-bell-dot",
},
!isMobile && {
label: 'App',
label: "App",
component: App,
icon: 'i-codicon-hubot'
icon: "i-codicon-hubot",
},
!isMobile && {
label: 'Window',
label: "Window",
component: Window,
icon: 'i-codicon-window'
icon: "i-codicon-window",
},
!isMobile && {
label: 'Shortcuts',
label: "Shortcuts",
component: Shortcuts,
icon: 'i-codicon-record-keys'
icon: "i-codicon-record-keys",
},
{
label: 'Shell',
label: "Shell",
component: Shell,
icon: 'i-codicon-terminal-bash'
icon: "i-codicon-terminal-bash",
},
!isMobile && {
label: 'Updater',
label: "Updater",
component: Updater,
icon: 'i-codicon-cloud-download'
icon: "i-codicon-cloud-download",
},
!isMobile && {
label: 'Clipboard',
label: "Clipboard",
component: Clipboard,
icon: 'i-codicon-clippy'
icon: "i-codicon-clippy",
},
{
label: 'WebRTC',
label: "WebRTC",
component: WebRTC,
icon: 'i-ph-broadcast'
icon: "i-ph-broadcast",
},
isMobile && {
label: "Scanner",
component: Scanner,
icon: "i-ph-scan",
},
isMobile && {
label: 'Camera',
@@ -119,73 +127,73 @@
},
]
let selected = views[0]
let selected = views[0];
function select(view) {
selected = view
selected = view;
}
// Window controls
let isWindowMaximized
let isWindowMaximized;
onMount(async () => {
const window = getCurrent()
isWindowMaximized = await window.isMaximized()
listen('tauri://resize', async () => {
isWindowMaximized = await window.isMaximized()
})
})
isWindowMaximized = await appWindow.isMaximized();
appWindow.onResized(async () => {
isWindowMaximized = await appWindow.isMaximized();
});
});
function minimize() {
getCurrent().minimize()
appWindow.minimize();
}
async function toggleMaximize() {
const window = getCurrent()
;(await window.isMaximized()) ? window.unmaximize() : window.maximize()
(await appWindow.isMaximized())
? appWindow.unmaximize()
: appWindow.maximize();
}
let confirmed_close = false
let confirmed_close = false;
async function close() {
if (!confirmed_close) {
confirmed_close = await ask(
'Are you sure that you want to close this window?',
"Are you sure that you want to close this window?",
{
title: 'Tauri API'
title: "Tauri API",
}
)
);
if (confirmed_close) {
getCurrent().close()
appWindow.close();
}
}
}
// dark/light
let isDark
let isDark;
onMount(() => {
isDark = localStorage && localStorage.getItem('theme') == 'dark'
applyTheme(isDark)
})
isDark = localStorage && localStorage.getItem("theme") == "dark";
applyTheme(isDark);
});
function applyTheme(isDark) {
const html = document.querySelector('html')
isDark ? html.classList.add('dark') : html.classList.remove('dark')
localStorage && localStorage.setItem('theme', isDark ? 'dark' : '')
const html = document.querySelector("html");
isDark ? html.classList.add("dark") : html.classList.remove("dark");
localStorage && localStorage.setItem("theme", isDark ? "dark" : "");
}
function toggleDark() {
isDark = !isDark
applyTheme(isDark)
isDark = !isDark;
applyTheme(isDark);
}
// Console
let messages = writable([])
let messages = writable([]);
function onMessage(value) {
messages.update((r) => [
{
html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
(typeof value === 'string' ? value : JSON.stringify(value, null, 1)) +
'</pre>'
(typeof value === "string" ? value : JSON.stringify(value, null, 1)) +
"</pre>",
},
...r
])
...r,
]);
}
// this function is renders HTML without sanitizing it so it's insecure
@@ -196,108 +204,110 @@
html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
html +
'</pre>'
"</pre>",
},
...r
])
...r,
]);
}
function clear() {
messages.update(() => [])
messages.update(() => []);
}
let consoleEl, consoleH, cStartY
let minConsoleHeight = 50
let consoleEl, consoleH, cStartY;
let minConsoleHeight = 50;
function startResizingConsole(e) {
cStartY = e.clientY
cStartY = e.clientY;
const styles = window.getComputedStyle(consoleEl)
consoleH = parseInt(styles.height, 10)
const styles = window.getComputedStyle(consoleEl);
consoleH = parseInt(styles.height, 10);
const moveHandler = (e) => {
const dy = e.clientY - cStartY
const newH = consoleH - dy
const dy = e.clientY - cStartY;
const newH = consoleH - dy;
consoleEl.style.height = `${
newH < minConsoleHeight ? minConsoleHeight : newH
}px`
}
}px`;
};
const upHandler = () => {
document.removeEventListener('mouseup', upHandler)
document.removeEventListener('mousemove', moveHandler)
}
document.addEventListener('mouseup', upHandler)
document.addEventListener('mousemove', moveHandler)
document.removeEventListener("mouseup", upHandler);
document.removeEventListener("mousemove", moveHandler);
};
document.addEventListener("mouseup", upHandler);
document.addEventListener("mousemove", moveHandler);
}
let isWindows
let isWindows;
onMount(async () => {
isWindows = (await os.platform()) === 'win32'
})
isWindows = (await os.platform()) === "win32";
});
// mobile
let isSideBarOpen = false
let sidebar
let sidebarToggle
let isDraggingSideBar = false
let draggingStartPosX = 0
let draggingEndPosX = 0
const clamp = (min, num, max) => Math.min(Math.max(num, min), max)
let isSideBarOpen = false;
let sidebar;
let sidebarToggle;
let isDraggingSideBar = false;
let draggingStartPosX = 0;
let draggingEndPosX = 0;
const clamp = (min, num, max) => Math.min(Math.max(num, min), max);
function toggleSidebar(sidebar, isSideBarOpen) {
sidebar.style.setProperty(
'--translate-x',
`${isSideBarOpen ? '0' : '-18.75'}rem`
)
"--translate-x",
`${isSideBarOpen ? "0" : "-18.75"}rem`
);
}
onMount(() => {
sidebar = document.querySelector('#sidebar')
sidebarToggle = document.querySelector('#sidebarToggle')
sidebar = document.querySelector("#sidebar");
sidebarToggle = document.querySelector("#sidebarToggle");
document.addEventListener('click', (e) => {
document.addEventListener("click", (e) => {
if (sidebarToggle.contains(e.target)) {
isSideBarOpen = !isSideBarOpen
isSideBarOpen = !isSideBarOpen;
} else if (isSideBarOpen && !sidebar.contains(e.target)) {
isSideBarOpen = false
isSideBarOpen = false;
}
})
});
document.addEventListener('touchstart', (e) => {
if (sidebarToggle.contains(e.target)) return
document.addEventListener("touchstart", (e) => {
if (sidebarToggle.contains(e.target)) return;
const x = e.touches[0].clientX
const x = e.touches[0].clientX;
if ((0 < x && x < 20 && !isSideBarOpen) || isSideBarOpen) {
isDraggingSideBar = true
draggingStartPosX = x
isDraggingSideBar = true;
draggingStartPosX = x;
}
})
});
document.addEventListener('touchmove', (e) => {
document.addEventListener("touchmove", (e) => {
if (isDraggingSideBar) {
const x = e.touches[0].clientX
draggingEndPosX = x
const delta = (x - draggingStartPosX) / 10
const x = e.touches[0].clientX;
draggingEndPosX = x;
const delta = (x - draggingStartPosX) / 10;
sidebar.style.setProperty(
'--translate-x',
"--translate-x",
`-${clamp(0, isSideBarOpen ? 0 - delta : 18.75 - delta, 18.75)}rem`
)
);
}
})
});
document.addEventListener('touchend', () => {
document.addEventListener("touchend", () => {
if (isDraggingSideBar) {
const delta = (draggingEndPosX - draggingStartPosX) / 10
isSideBarOpen = isSideBarOpen ? delta > -(18.75 / 2) : delta > 18.75 / 2
const delta = (draggingEndPosX - draggingStartPosX) / 10;
isSideBarOpen = isSideBarOpen
? delta > -(18.75 / 2)
: delta > 18.75 / 2;
}
isDraggingSideBar = false
})
})
isDraggingSideBar = false;
});
});
$: {
const sidebar = document.querySelector('#sidebar')
const sidebar = document.querySelector("#sidebar");
if (sidebar) {
toggleSidebar(sidebar, isSideBarOpen)
toggleSidebar(sidebar, isSideBarOpen);
}
}
</script>
@@ -316,7 +326,7 @@
children:items-center children:justify-center"
>
<span
title={isDark ? 'Switch to Light mode' : 'Switch to Dark mode'}
title={isDark ? "Switch to Light mode" : "Switch to Dark mode"}
class="hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
on:click={toggleDark}
>
@@ -334,7 +344,7 @@
<div class="i-codicon-chrome-minimize" />
</span>
<span
title={isWindowMaximized ? 'Restore' : 'Maximize'}
title={isWindowMaximized ? "Restore" : "Maximize"}
class="hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
on:click={toggleMaximize}
>
@@ -346,7 +356,7 @@
</span>
<span
title="Close"
class="hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText "
class="hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText"
on:click={close}
>
<div class="i-codicon-chrome-close" />
@@ -358,7 +368,7 @@
<!-- Sidebar toggle, only visible on small screens -->
<div
id="sidebarToggle"
class="z-2000 display-none lt-sm:flex justify-center items-center absolute top-2 left-2 w-8 h-8 rd-8
class="z-2000 sidebar-toggle display-none lt-sm:flex justify-center absolute items-center w-8 h-8 rd-8
bg-accent dark:bg-darkAccent active:bg-accentDark dark:active:bg-darkAccentDark"
>
{#if isSideBarOpen}
@@ -377,7 +387,7 @@
bg-darkPrimaryLighter transition-colors-250 overflow-hidden grid select-none px-2"
>
<img
on:click={() => open('https://tauri.app/')}
on:click={() => open("https://tauri.app/")}
class="self-center p-7 cursor-pointer"
src="tauri_logo.png"
alt="Tauri logo"
@@ -433,8 +443,8 @@
href="##"
class="nv {selected === view ? 'nv_selected' : ''}"
on:click={() => {
select(view)
isSideBarOpen = false
select(view);
isSideBarOpen = false;
}}
>
<div class="{view.icon} mr-2" />
@@ -446,6 +456,7 @@
</aside>
<main
class="flex-1 bg-primary dark:bg-darkPrimary transition-transform transition-colors-250 grid grid-rows-[2fr_auto]"
class:transparent={isMobile}
>
<div class="px-5 overflow-hidden grid grid-rows-[auto_1fr]">
<h1>{selected.label}</h1>
+20
View File
@@ -39,3 +39,23 @@ code.code-block {
transform: translateX(var(--translate-x));
}
}
.sidebar-toggle {
margin-top: 0.5rem;
margin-left: 0.5rem;
}
body {
overflow: hidden;
padding: env(safe-area-inset-top) env(safe-area-inset-right)
env(safe-area-inset-bottom) env(safe-area-inset-left);
}
#sidebar,
#console {
padding-bottom: calc(env(safe-area-inset-bottom) + 24px);
}
.transparent {
background-color: transparent;
}
+8 -8
View File
@@ -1,24 +1,24 @@
<script>
import { show, hide } from '@tauri-apps/api/app'
import { show, hide } from "@tauri-apps/plugin-app";
export let onMessage
export let onMessage;
function showApp() {
hideApp()
.then(() => {
setTimeout(() => {
show()
.then(() => onMessage('Shown app'))
.catch(onMessage)
}, 2000)
.then(() => onMessage("Shown app"))
.catch(onMessage);
}, 2000);
})
.catch(onMessage)
.catch(onMessage);
}
function hideApp() {
return hide()
.then(() => onMessage('Hide app'))
.catch(onMessage)
.then(() => onMessage("Hide app"))
.catch(onMessage);
}
</script>
+1 -1
View File
@@ -1,5 +1,5 @@
<script>
import { getMatches } from "tauri-plugin-cli-api";
import { getMatches } from "@tauri-apps/plugin-cli";
export let onMessage;
+7 -7
View File
@@ -1,23 +1,23 @@
<script>
import { writeText, readText } from 'tauri-plugin-clipboard-api'
import { writeText, readText } from "@tauri-apps/plugin-clipboard-manager";
export let onMessage
let text = 'clipboard message'
export let onMessage;
let text = "clipboard message";
function write() {
writeText(text)
.then(() => {
onMessage('Wrote to the clipboard')
onMessage("Wrote to the clipboard");
})
.catch(onMessage)
.catch(onMessage);
}
function read() {
readText()
.then((contents) => {
onMessage(`Clipboard contents: ${contents}`)
onMessage(`Clipboard contents: ${contents}`);
})
.catch(onMessage)
.catch(onMessage);
}
</script>
+21 -19
View File
@@ -1,41 +1,43 @@
<script>
import { listen, emit } from '@tauri-apps/api/event'
import { invoke } from '@tauri-apps/api/tauri'
import { onMount, onDestroy } from 'svelte'
import { getCurrent } from "@tauri-apps/plugin-window";
import { invoke } from "@tauri-apps/api/tauri";
import { onMount, onDestroy } from "svelte";
export let onMessage
let unlisten
const appWindow = getCurrent();
export let onMessage;
let unlisten;
onMount(async () => {
unlisten = await listen('rust-event', onMessage)
})
unlisten = await appWindow.listen("rust-event", onMessage);
});
onDestroy(() => {
if (unlisten) {
unlisten()
unlisten();
}
})
});
function log() {
invoke('log_operation', {
event: 'tauri-click',
payload: 'this payload is optional because we used Option in Rust'
})
invoke("log_operation", {
event: "tauri-click",
payload: "this payload is optional because we used Option in Rust",
});
}
function performRequest() {
invoke('perform_request', {
endpoint: 'dummy endpoint arg',
invoke("perform_request", {
endpoint: "dummy endpoint arg",
body: {
id: 5,
name: 'test'
}
name: "test",
},
})
.then(onMessage)
.catch(onMessage)
.catch(onMessage);
}
function emitEvent() {
emit('js-event', 'this is the payload string')
appWindow.emit("js-event", "this is the payload string");
}
</script>
+48 -45
View File
@@ -1,99 +1,102 @@
<script>
import { open, save, confirm } from 'tauri-plugin-dialog-api'
import { readBinaryFile } from 'tauri-plugin-fs-api'
import { open, save, confirm } from "@tauri-apps/plugin-dialog";
import { readBinaryFile } from "@tauri-apps/plugin-fs";
export let onMessage
export let insecureRenderHtml
let defaultPath = null
let filter = null
let multiple = false
let directory = false
export let onMessage;
export let insecureRenderHtml;
let defaultPath = null;
let filter = null;
let multiple = false;
let directory = false;
function arrayBufferToBase64(buffer, callback) {
var blob = new Blob([buffer], {
type: 'application/octet-binary'
})
var reader = new FileReader()
type: "application/octet-binary",
});
var reader = new FileReader();
reader.onload = function (evt) {
var dataurl = evt.target.result
callback(dataurl.substr(dataurl.indexOf(',') + 1))
}
reader.readAsDataURL(blob)
var dataurl = evt.target.result;
callback(dataurl.substr(dataurl.indexOf(",") + 1));
};
reader.readAsDataURL(blob);
}
async function prompt() {
confirm('Is Tauri awesome?', {
okLabel: 'Absolutely',
cancelLabel: 'Totally',
}).then(res => onMessage(res
? "Tauri is absolutely awesome"
: "Tauri is totally awesome"
)).catch(onMessage)
confirm("Is Tauri awesome?", {
okLabel: "Absolutely",
cancelLabel: "Totally",
})
.then((res) =>
onMessage(
res ? "Tauri is absolutely awesome" : "Tauri is totally awesome"
)
)
.catch(onMessage);
}
function openDialog() {
open({
title: 'My wonderful open dialog',
title: "My wonderful open dialog",
defaultPath,
filters: filter
? [
{
name: 'Tauri Example',
extensions: filter.split(',').map((f) => f.trim())
}
name: "Tauri Example",
extensions: filter.split(",").map((f) => f.trim()),
},
]
: [],
multiple,
directory
directory,
})
.then(function (res) {
if (Array.isArray(res)) {
onMessage(res)
onMessage(res);
} else {
var pathToRead = typeof res === 'string' ? res : res.path
var isFile = pathToRead.match(/\S+\.\S+$/g)
var pathToRead = typeof res === "string" ? res : res.path;
var isFile = pathToRead.match(/\S+\.\S+$/g);
readBinaryFile(pathToRead)
.then(function (response) {
if (isFile) {
if (
pathToRead.includes('.png') ||
pathToRead.includes('.jpg')
pathToRead.includes(".png") ||
pathToRead.includes(".jpg")
) {
arrayBufferToBase64(
new Uint8Array(response),
function (base64) {
var src = 'data:image/png;base64,' + base64
insecureRenderHtml('<img src="' + src + '"></img>')
var src = "data:image/png;base64," + base64;
insecureRenderHtml('<img src="' + src + '"></img>');
}
)
);
} else {
onMessage(res)
onMessage(res);
}
} else {
onMessage(res)
onMessage(res);
}
})
.catch(onMessage(res))
.catch(onMessage(res));
}
})
.catch(onMessage)
.catch(onMessage);
}
function saveDialog() {
save({
title: 'My wonderful save dialog',
title: "My wonderful save dialog",
defaultPath,
filters: filter
? [
{
name: 'Tauri Example',
extensions: filter.split(',').map((f) => f.trim())
}
name: "Tauri Example",
extensions: filter.split(",").map((f) => f.trim()),
},
]
: []
: [],
})
.then(onMessage)
.catch(onMessage)
.catch(onMessage);
}
</script>
+38 -38
View File
@@ -3,79 +3,79 @@
readBinaryFile,
writeTextFile,
readDir,
Dir
} from 'tauri-plugin-fs-api'
import { convertFileSrc } from '@tauri-apps/api/tauri'
Dir,
} from "@tauri-apps/plugin-fs";
import { convertFileSrc } from "@tauri-apps/api/tauri";
export let onMessage
export let insecureRenderHtml
export let onMessage;
export let insecureRenderHtml;
let pathToRead = ''
let img
let pathToRead = "";
let img;
function getDir() {
const dirSelect = document.getElementById('dir')
return dirSelect.value ? parseInt(dir.value) : null
const dirSelect = document.getElementById("dir");
return dirSelect.value ? parseInt(dir.value) : null;
}
function arrayBufferToBase64(buffer, callback) {
const blob = new Blob([buffer], {
type: 'application/octet-binary'
})
const reader = new FileReader()
type: "application/octet-binary",
});
const reader = new FileReader();
reader.onload = function (evt) {
const dataurl = evt.target.result
callback(dataurl.substr(dataurl.indexOf(',') + 1))
}
reader.readAsDataURL(blob)
const dataurl = evt.target.result;
callback(dataurl.substr(dataurl.indexOf(",") + 1));
};
reader.readAsDataURL(blob);
}
const DirOptions = Object.keys(Dir)
.filter((key) => isNaN(parseInt(key)))
.map((dir) => [dir, Dir[dir]])
.map((dir) => [dir, Dir[dir]]);
function read() {
const isFile = pathToRead.match(/\S+\.\S+$/g)
const isFile = pathToRead.match(/\S+\.\S+$/g);
const opts = {
dir: getDir()
}
dir: getDir(),
};
const promise = isFile
? readBinaryFile(pathToRead, opts)
: readDir(pathToRead, opts)
: readDir(pathToRead, opts);
promise
.then(function (response) {
if (isFile) {
if (pathToRead.includes('.png') || pathToRead.includes('.jpg')) {
if (pathToRead.includes(".png") || pathToRead.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 () {
writeTextFile(pathToRead, fileInput.value, {
dir: getDir()
}).catch(onMessage)
})
})
dir: getDir(),
}).catch(onMessage);
});
});
}
} else {
onMessage(response)
onMessage(response);
}
})
.catch(onMessage)
.catch(onMessage);
}
function setSrc() {
img.src = convertFileSrc(pathToRead)
img.src = convertFileSrc(pathToRead);
}
</script>
+51 -47
View File
@@ -1,60 +1,69 @@
<script>
import { getClient, Body, ResponseType } from 'tauri-plugin-http-api'
import { JsonView } from '@zerodevx/svelte-json-view'
import { fetch as tauriFetch } from "@tauri-apps/plugin-http";
import { JsonView } from "@zerodevx/svelte-json-view";
let httpMethod = 'GET'
let httpBody = ''
let httpMethod = "GET";
let httpBody = "";
export let onMessage
export let onMessage;
async function makeHttpRequest() {
const client = await getClient().catch((e) => {
onMessage(e)
throw e
})
let method = httpMethod || 'GET'
let method = httpMethod || "GET";
const options = {
url: 'http://localhost:3003',
method: method || 'GET'
method: method || "GET",
headers: {},
};
let bodyType;
if (method !== "GET") {
options.body = httpBody;
if (
(httpBody.startsWith("{") && httpBody.endsWith("}")) ||
(httpBody.startsWith("[") && httpBody.endsWith("]"))
) {
options.headers["Content-Type"] = "application/json";
bodyType = "json";
} else if (httpBody !== "") {
bodyType = "text";
}
}
if (
(httpBody.startsWith('{') && httpBody.endsWith('}')) ||
(httpBody.startsWith('[') && httpBody.endsWith(']'))
) {
options.body = Body.json(JSON.parse(httpBody))
} else if (httpBody !== '') {
options.body = Body.text(httpBody)
}
const response = await tauriFetch("http://localhost:3003", options);
const body =
bodyType === "json" ? await response.json() : await response.text();
client.request(options).then(onMessage).catch(onMessage)
onMessage({
url: response.url,
status: response.status,
ok: response.ok,
headers: Object.fromEntries(response.headers.entries()),
body,
});
}
/// http form
let foo = 'baz'
let bar = 'qux'
let result = null
let multipart = true
let foo = "baz";
let bar = "qux";
let result = null;
async function doPost() {
const client = await getClient().catch((e) => {
onMessage(e)
throw e
})
result = await client.request({
url: 'http://localhost:3003',
method: 'POST',
body: Body.form({
foo,
bar
}),
headers: multipart
? { 'Content-Type': 'multipart/form-data' }
: undefined,
responseType: ResponseType.Text
})
const form = new FormData();
form.append("foo", foo);
form.append("bar", bar);
const response = await tauriFetch("http://localhost:3003", {
method: "POST",
body: form,
});
result = {
url: response.url,
status: response.status,
ok: response.ok,
headers: Object.fromEntries(response.headers.entries()),
body: await response.text(),
};
}
</script>
@@ -87,11 +96,6 @@
<input class="input" bind:value={bar} />
</div>
<br />
<label>
<input type="checkbox" bind:checked={multipart} />
Multipart
</label>
<br />
<br />
<button class="btn" type="button" on:click={doPost}> Post it</button>
<br />
+155
View File
@@ -0,0 +1,155 @@
<script>
import { scan, checkPermissions, requestPermissions, Format, cancel } from "@tauri-apps/plugin-barcode-scanner";
export let onMessage;
let scanning = false;
let windowed = true;
let formats = [Format.QRCode];
const supportedFormats = [Format.QRCode, Format.EAN13];
async function startScan() {
let permission = await checkPermissions();
if (permission === 'prompt') {
permission = await requestPermissions();
}
if (permission === 'granted') {
scanning = true;
scan({ windowed, formats })
.then((res) => {
scanning = false;
onMessage(res);
})
.catch((error) => {
scanning = false;
onMessage(error);
});
} else {
onMessage('Permission denied')
}
}
async function cancelScan() {
await cancel();
scanning = false;
onMessage("cancelled");
}
</script>
<div class="full-height">
<div class:invisible={scanning}>
<div>
<input type="checkbox" id="scanner-windowed" bind:checked={windowed} />
<label for="scanner-windowed">Windowed</label>
</div>
<div>
<select class="input" id="format" multiple bind:value={formats}>
{#each supportedFormats as f}
<option value={f}>{f}</option>
{/each}
</select>
</div>
<button class="btn" type="button" on:click={startScan}>Scan</button>
</div>
<div class="scanning full-height" class:invisible={!scanning}>
<div class="scanner-background">
<!-- this background simulates the camera view -->
</div>
<div class="container full-height">
<div class="barcode-scanner--area--container">
<div class="relative">
<p>Aim your camera at a QR code</p>
<button class="btn" type="button" on:click={cancelScan}>Cancel</button>
</div>
<div class="square surround-cover">
<div class="barcode-scanner--area--outer surround-cover">
<div class="barcode-scanner--area--inner" />
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.invisible {
display: none;
}
.full-height {
height: 100%;
}
p {
color: #fff;
font-family: sans-serif;
text-align: center;
font-weight: 600;
}
.container {
width: 100%;
height: 100%;
overflow: hidden;
}
.container {
display: flex;
}
.relative {
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
z-index: 1;
}
.square {
width: 100%;
position: relative;
overflow: hidden;
transition: 0.3s;
}
.square:after {
content: "";
top: 0;
display: block;
padding-bottom: 100%;
}
.square > div {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.surround-cover {
box-shadow: 0 0 0 99999px rgba(0, 0, 0, 0.5);
}
.barcode-scanner--area--container {
width: 80%;
max-width: min(500px, 80vh);
margin: auto;
}
.barcode-scanner--area--outer {
display: flex;
border-radius: 1em;
}
.barcode-scanner--area--inner {
width: 100%;
margin: 1rem;
border: 2px solid #fff;
box-shadow: 0px 0px 2px 1px rgb(0 0 0 / 0.5),
inset 0px 0px 2px 1px rgb(0 0 0 / 0.5);
border-radius: 1rem;
}
.scanner-background {
background: linear-gradient(45deg, #673ab7, transparent);
background-position: 45% 50%;
background-size: cover;
background-repeat: no-repeat;
}
</style>
+30 -30
View File
@@ -1,65 +1,65 @@
<script>
import { Command } from 'tauri-plugin-shell-api'
import { Command } from "@tauri-apps/plugin-shell";
const windows = navigator.userAgent.includes('Windows')
let cmd = windows ? 'cmd' : 'sh'
let args = windows ? ['/C'] : ['-c']
const windows = navigator.userAgent.includes("Windows");
let cmd = windows ? "cmd" : "sh";
let args = windows ? ["/C"] : ["-c"];
export let onMessage
export let onMessage;
let script = 'echo "hello world"'
let cwd = null
let env = 'SOMETHING=value ANOTHER=2'
let encoding = ''
let stdin = ''
let child
let script = 'echo "hello world"';
let cwd = null;
let env = "SOMETHING=value ANOTHER=2";
let encoding = "";
let stdin = "";
let child;
function _getEnv() {
return env.split(' ').reduce((env, clause) => {
let [key, value] = clause.split('=')
return env.split(" ").reduce((env, clause) => {
let [key, value] = clause.split("=");
return {
...env,
[key]: value
}
}, {})
[key]: value,
};
}, {});
}
function spawn() {
child = null
child = null;
const command = Command.create(cmd, [...args, script], {
cwd: cwd || null,
env: _getEnv(),
encoding: encoding || undefined,
})
});
command.on('close', (data) => {
command.on("close", (data) => {
onMessage(
`command finished with code ${data.code} and signal ${data.signal}`
)
child = null
})
command.on('error', (error) => onMessage(`command error: "${error}"`))
);
child = null;
});
command.on("error", (error) => onMessage(`command error: "${error}"`));
command.stdout.on('data', (line) => onMessage(`command stdout: "${line}"`))
command.stderr.on('data', (line) => onMessage(`command stderr: "${line}"`))
command.stdout.on("data", (line) => onMessage(`command stdout: "${line}"`));
command.stderr.on("data", (line) => onMessage(`command stderr: "${line}"`));
command
.spawn()
.then((c) => {
child = c
child = c;
})
.catch(onMessage)
.catch(onMessage);
}
function kill() {
child
.kill()
.then(() => onMessage('killed child process'))
.catch(onMessage)
.then(() => onMessage("killed child process"))
.catch(onMessage);
}
function writeToStdin() {
child.write(stdin).catch(onMessage)
child.write(stdin).catch(onMessage);
}
</script>
+18 -18
View File
@@ -1,46 +1,46 @@
<script>
import { writable } from 'svelte/store'
import { writable } from "svelte/store";
import {
register as registerShortcut,
unregister as unregisterShortcut,
unregisterAll as unregisterAllShortcuts
} from 'tauri-plugin-global-shortcut-api'
unregisterAll as unregisterAllShortcuts,
} from "@tauri-apps/plugin-global-shortcut";
export let onMessage
const shortcuts = writable([])
let shortcut = 'CmdOrControl+X'
export let onMessage;
const shortcuts = writable([]);
let shortcut = "CmdOrControl+X";
function register() {
const shortcut_ = shortcut
const shortcut_ = shortcut;
registerShortcut(shortcut_, () => {
onMessage(`Shortcut ${shortcut_} triggered`)
onMessage(`Shortcut ${shortcut_} triggered`);
})
.then(() => {
shortcuts.update((shortcuts_) => [...shortcuts_, shortcut_])
onMessage(`Shortcut ${shortcut_} registered successfully`)
shortcuts.update((shortcuts_) => [...shortcuts_, shortcut_]);
onMessage(`Shortcut ${shortcut_} registered successfully`);
})
.catch(onMessage)
.catch(onMessage);
}
function unregister(shortcut) {
const shortcut_ = shortcut
const shortcut_ = shortcut;
unregisterShortcut(shortcut_)
.then(() => {
shortcuts.update((shortcuts_) =>
shortcuts_.filter((s) => s !== shortcut_)
)
onMessage(`Shortcut ${shortcut_} unregistered`)
);
onMessage(`Shortcut ${shortcut_} unregistered`);
})
.catch(onMessage)
.catch(onMessage);
}
function unregisterAll() {
unregisterAllShortcuts()
.then(() => {
shortcuts.update(() => [])
onMessage(`Unregistered all shortcuts`)
shortcuts.update(() => []);
onMessage(`Unregistered all shortcuts`);
})
.catch(onMessage)
.catch(onMessage);
}
</script>
+56 -46
View File
@@ -1,76 +1,86 @@
<script>
import { onMount, onDestroy } from 'svelte'
import { check } from "@tauri-apps/plugin-updater";
import { relaunch } from "@tauri-apps/plugin-process";
// This example show how updater events work when dialog is disabled.
// This allow you to use custom dialog for the updater.
// This is your responsibility to restart the application after you receive the STATUS: DONE.
export let onMessage;
import { checkUpdate, installUpdate } from '@tauri-apps/api/updater'
import { listen } from '@tauri-apps/api/event'
import { relaunch } from '@tauri-apps/api/process'
let isChecking, isInstalling, newUpdate;
let totalSize = 0,
downloadedSize = 0;
export let onMessage
let unlisten
onMount(async () => {
unlisten = await listen('tauri://update-status', onMessage)
})
onDestroy(() => {
if (unlisten) {
unlisten()
}
})
let isChecking, isInstalling, newUpdate
async function check() {
isChecking = true
async function checkUpdate() {
isChecking = true;
try {
const { shouldUpdate, manifest } = await checkUpdate()
onMessage(`Should update: ${shouldUpdate}`)
onMessage(manifest)
const update = await check();
onMessage(`Should update: ${update.response.available}`);
onMessage(update.response);
newUpdate = shouldUpdate
newUpdate = update;
} catch (e) {
onMessage(e)
onMessage(e);
} finally {
isChecking = false
isChecking = false;
}
}
async function install() {
isInstalling = true
isInstalling = true;
downloadedSize = 0;
try {
await installUpdate()
onMessage('Installation complete, restart required.')
await relaunch()
await newUpdate.downloadAndInstall((downloadProgress) => {
switch (downloadProgress.event) {
case "Started":
totalSize = downloadProgress.data.contentLength;
break;
case "Progress":
downloadedSize += downloadProgress.data.chunkLength;
break;
case "Finished":
break;
}
});
onMessage("Installation complete, restarting...");
await new Promise((resolve) => setTimeout(resolve, 2000));
await relaunch();
} catch (e) {
onMessage(e)
console.error(e);
onMessage(e);
} finally {
isInstalling = false
isInstalling = false;
}
}
$: progress = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0;
</script>
<div class="flex children:grow children:h10">
{#if !isChecking && !newUpdate}
<button class="btn" on:click={check}>Check update</button>
<button class="btn" on:click={checkUpdate}>Check update</button>
{:else if !isInstalling && newUpdate}
<button class="btn" on:click={install}>Install update</button>
{:else}
<button
class="btn text-accentText dark:text-darkAccentText flex items-center justify-center"
><div class="spinner animate-spin" /></button
>
<div class="progress">
<span>{progress}%</span>
<div class="progress-bar" style="width: {progress}%" />
</div>
{/if}
</div>
<style>
.spinner {
height: 1.2rem;
width: 1.2rem;
border-radius: 50rem;
color: currentColor;
border: 2px dashed currentColor;
.progress {
width: 100%;
height: 50px;
position: relative;
margin-top: 5%;
}
.progress > span {
font-size: 1.2rem;
}
.progress-bar {
height: 30px;
background-color: hsl(32, 94%, 46%);
border: 1px solid #333;
}
</style>
+13 -13
View File
@@ -1,27 +1,27 @@
<script>
import { getName, getVersion, getTauriVersion } from '@tauri-apps/api/app'
import { relaunch, exit } from '@tauri-apps/api/process'
import { getName, getVersion, getTauriVersion } from "@tauri-apps/plugin-app";
import { relaunch, exit } from "@tauri-apps/plugin-process";
let version = '0.0.0'
let tauriVersion = '0.0.0'
let appName = 'Unknown'
let version = "1.0.0";
let tauriVersion = "1.0.0";
let appName = "Unknown";
getName().then((n) => {
appName = n
})
appName = n;
});
getVersion().then((v) => {
version = v
})
version = v;
});
getTauriVersion().then((v) => {
tauriVersion = v
})
tauriVersion = v;
});
async function closeApp() {
await exit()
await exit();
}
async function relaunchApp() {
await relaunch()
await relaunch();
}
</script>
+289 -133
View File
@@ -1,221 +1,282 @@
<script>
import {
appWindow,
WebviewWindow,
getCurrent,
LogicalSize,
UserAttentionType,
PhysicalSize,
PhysicalPosition
} from '@tauri-apps/api/window'
import { open as openDialog } from 'tauri-plugin-dialog-api'
import { open } from 'tauri-plugin-shell-api'
PhysicalPosition,
Effect,
EffectState,
Window
} from "@tauri-apps/plugin-window";
import { open as openDialog } from "@tauri-apps/plugin-dialog";
import { open } from "@tauri-apps/plugin-shell";
let selectedWindow = appWindow.label
const appWindow = getCurrent();
let selectedWindow = appWindow.label;
const windowMap = {
[appWindow.label]: appWindow
}
[appWindow.label]: appWindow,
};
const cursorIconOptions = [
'default',
'crosshair',
'hand',
'arrow',
'move',
'text',
'wait',
'help',
'progress',
"default",
"crosshair",
"hand",
"arrow",
"move",
"text",
"wait",
"help",
"progress",
// something cannot be done
'notAllowed',
'contextMenu',
'cell',
'verticalText',
'alias',
'copy',
'noDrop',
"notAllowed",
"contextMenu",
"cell",
"verticalText",
"alias",
"copy",
"noDrop",
// something can be grabbed
'grab',
"grab",
/// something is grabbed
'grabbing',
'allScroll',
'zoomIn',
'zoomOut',
"grabbing",
"allScroll",
"zoomIn",
"zoomOut",
// edge is to be moved
'eResize',
'nResize',
'neResize',
'nwResize',
'sResize',
'seResize',
'swResize',
'wResize',
'ewResize',
'nsResize',
'neswResize',
'nwseResize',
'colResize',
'rowResize'
]
"eResize",
"nResize",
"neResize",
"nwResize",
"sResize",
"seResize",
"swResize",
"wResize",
"ewResize",
"nsResize",
"neswResize",
"nwseResize",
"colResize",
"rowResize",
];
export let onMessage
const windowsEffects = ["mica", "blur", "acrylic"];
const isWindows = navigator.appVersion.includes("windows");
const isMacOS = navigator.appVersion.includes("macos");
let effectOptions = isWindows
? windowsEffects
: Object.keys(Effect)
.map((effect) => Effect[effect])
.filter((e) => !windowsEffects.includes(e));
const effectStateOptions = Object.keys(EffectState).map(
(state) => EffectState[state]
);
let newWindowLabel
export let onMessage;
const mainEl = document.querySelector("main");
let urlValue = 'https://tauri.app'
let resizable = true
let maximized = false
let decorations = true
let alwaysOnTop = false
let contentProtected = true
let fullscreen = false
let width = null
let height = null
let minWidth = null
let minHeight = null
let maxWidth = null
let maxHeight = null
let x = null
let y = null
let scaleFactor = 1
let innerPosition = new PhysicalPosition(x, y)
let outerPosition = new PhysicalPosition(x, y)
let innerSize = new PhysicalSize(width, height)
let outerSize = new PhysicalSize(width, height)
let resizeEventUnlisten
let moveEventUnlisten
let cursorGrab = false
let cursorVisible = true
let cursorX = null
let cursorY = null
let cursorIcon = 'default'
let cursorIgnoreEvents = false
let windowTitle = 'Awesome Tauri Example!'
let newWindowLabel;
let urlValue = "https://tauri.app";
let resizable = true;
let maximizable = true;
let minimizable = true;
let closable = true;
let maximized = false;
let decorations = true;
let alwaysOnTop = false;
let contentProtected = true;
let fullscreen = false;
let width = null;
let height = null;
let minWidth = null;
let minHeight = null;
let maxWidth = null;
let maxHeight = null;
let x = null;
let y = null;
let scaleFactor = 1;
let innerPosition = new PhysicalPosition(x, y);
let outerPosition = new PhysicalPosition(x, y);
let innerSize = new PhysicalSize(width, height);
let outerSize = new PhysicalSize(width, height);
let resizeEventUnlisten;
let moveEventUnlisten;
let cursorGrab = false;
let cursorVisible = true;
let cursorX = null;
let cursorY = null;
let cursorIcon = "default";
let cursorIgnoreEvents = false;
let windowTitle = "Awesome Tauri Example!";
let effects = [];
let selectedEffect;
let effectState;
let effectRadius;
let effectR, effectG, effectB, effectA;
function openUrl() {
open(urlValue)
open(urlValue);
}
function setTitle_() {
windowMap[selectedWindow].setTitle(windowTitle)
windowMap[selectedWindow].setTitle(windowTitle);
}
function hide_() {
windowMap[selectedWindow].hide()
setTimeout(windowMap[selectedWindow].show, 2000)
windowMap[selectedWindow].hide();
setTimeout(windowMap[selectedWindow].show, 2000);
}
function minimize_() {
windowMap[selectedWindow].minimize()
setTimeout(windowMap[selectedWindow].unminimize, 2000)
windowMap[selectedWindow].minimize();
setTimeout(windowMap[selectedWindow].unminimize, 2000);
}
function getIcon() {
openDialog({
multiple: false
multiple: false,
}).then((path) => {
if (typeof path === 'string') {
windowMap[selectedWindow].setIcon(path)
if (typeof path === "string") {
windowMap[selectedWindow].setIcon(path);
}
})
});
}
function createWindow() {
if (!newWindowLabel) return
if (!newWindowLabel) return;
const webview = new WebviewWindow(newWindowLabel)
windowMap[newWindowLabel] = webview
webview.once('tauri://error', function () {
onMessage('Error creating new webview')
})
const webview = new Window(newWindowLabel);
windowMap[newWindowLabel] = webview;
webview.once("tauri://error", function () {
onMessage("Error creating new webview");
});
}
function loadWindowSize() {
windowMap[selectedWindow].innerSize().then((response) => {
innerSize = response
width = innerSize.width
height = innerSize.height
})
innerSize = response;
width = innerSize.width;
height = innerSize.height;
});
windowMap[selectedWindow].outerSize().then((response) => {
outerSize = response
})
outerSize = response;
});
}
function loadWindowPosition() {
windowMap[selectedWindow].innerPosition().then((response) => {
innerPosition = response
})
innerPosition = response;
});
windowMap[selectedWindow].outerPosition().then((response) => {
outerPosition = response
x = outerPosition.x
y = outerPosition.y
})
outerPosition = response;
x = outerPosition.x;
y = outerPosition.y;
});
}
async function addWindowEventListeners(window) {
if (!window) return
if (!window) return;
if (resizeEventUnlisten) {
resizeEventUnlisten()
resizeEventUnlisten();
}
if (moveEventUnlisten) {
moveEventUnlisten()
moveEventUnlisten();
}
moveEventUnlisten = await window.listen('tauri://move', loadWindowPosition)
resizeEventUnlisten = await window.listen('tauri://resize', loadWindowSize)
moveEventUnlisten = await window.listen("tauri://move", loadWindowPosition);
resizeEventUnlisten = await window.listen("tauri://resize", loadWindowSize);
}
async function requestUserAttention_() {
await windowMap[selectedWindow].minimize()
await windowMap[selectedWindow].minimize();
await windowMap[selectedWindow].requestUserAttention(
UserAttentionType.Critical
)
await new Promise((resolve) => setTimeout(resolve, 3000))
await windowMap[selectedWindow].requestUserAttention(null)
);
await new Promise((resolve) => setTimeout(resolve, 3000));
await windowMap[selectedWindow].requestUserAttention(null);
}
async function addEffect() {
if (!effects.includes(selectedEffect)) {
effects = [...effects, selectedEffect];
}
const payload = {
effects,
state: effectState,
radius: effectRadius,
};
if (
Number.isInteger(effectR) &&
Number.isInteger(effectG) &&
Number.isInteger(effectB) &&
Number.isInteger(effectA)
) {
payload.color = [effectR, effectG, effectB, effectA];
}
mainEl.classList.remove("bg-primary");
mainEl.classList.remove("dark:bg-darkPrimary");
await windowMap[selectedWindow].clearEffects();
await windowMap[selectedWindow].setEffects(payload);
}
async function clearEffects() {
effects = [];
await windowMap[selectedWindow].clearEffects();
mainEl.classList.add("bg-primary");
mainEl.classList.add("dark:bg-darkPrimary");
}
$: {
windowMap[selectedWindow]
loadWindowPosition()
loadWindowSize()
windowMap[selectedWindow];
loadWindowPosition();
loadWindowSize();
}
$: windowMap[selectedWindow]?.setResizable(resizable)
$: windowMap[selectedWindow]?.setResizable(resizable);
$: windowMap[selectedWindow]?.setMaximizable(maximizable);
$: windowMap[selectedWindow]?.setMinimizable(minimizable);
$: windowMap[selectedWindow]?.setClosable(closable);
$: maximized
? windowMap[selectedWindow]?.maximize()
: windowMap[selectedWindow]?.unmaximize()
$: windowMap[selectedWindow]?.setDecorations(decorations)
$: windowMap[selectedWindow]?.setAlwaysOnTop(alwaysOnTop)
$: windowMap[selectedWindow]?.setContentProtected(contentProtected)
$: windowMap[selectedWindow]?.setFullscreen(fullscreen)
: windowMap[selectedWindow]?.unmaximize();
$: windowMap[selectedWindow]?.setDecorations(decorations);
$: windowMap[selectedWindow]?.setAlwaysOnTop(alwaysOnTop);
$: windowMap[selectedWindow]?.setContentProtected(contentProtected);
$: windowMap[selectedWindow]?.setFullscreen(fullscreen);
$: width &&
height &&
windowMap[selectedWindow]?.setSize(new PhysicalSize(width, height))
windowMap[selectedWindow]?.setSize(new PhysicalSize(width, height));
$: minWidth && minHeight
? windowMap[selectedWindow]?.setMinSize(
new LogicalSize(minWidth, minHeight)
)
: windowMap[selectedWindow]?.setMinSize(null)
: windowMap[selectedWindow]?.setMinSize(null);
$: maxWidth > 800 && maxHeight > 400
? windowMap[selectedWindow]?.setMaxSize(
new LogicalSize(maxWidth, maxHeight)
)
: windowMap[selectedWindow]?.setMaxSize(null)
: windowMap[selectedWindow]?.setMaxSize(null);
$: x !== null &&
y !== null &&
windowMap[selectedWindow]?.setPosition(new PhysicalPosition(x, y))
windowMap[selectedWindow]?.setPosition(new PhysicalPosition(x, y));
$: windowMap[selectedWindow]
?.scaleFactor()
.then((factor) => (scaleFactor = factor))
$: addWindowEventListeners(windowMap[selectedWindow])
.then((factor) => (scaleFactor = factor));
$: addWindowEventListeners(windowMap[selectedWindow]);
$: windowMap[selectedWindow]?.setCursorGrab(cursorGrab)
$: windowMap[selectedWindow]?.setCursorVisible(cursorVisible)
$: windowMap[selectedWindow]?.setCursorIcon(cursorIcon)
$: windowMap[selectedWindow]?.setCursorGrab(cursorGrab);
$: windowMap[selectedWindow]?.setCursorVisible(cursorVisible);
$: windowMap[selectedWindow]?.setCursorIcon(cursorIcon);
$: cursorX !== null &&
cursorY !== null &&
windowMap[selectedWindow]?.setCursorPosition(
new PhysicalPosition(cursorX, cursorY)
)
$: windowMap[selectedWindow]?.setIgnoreCursorEvents(cursorIgnoreEvents)
);
$: windowMap[selectedWindow]?.setIgnoreCursorEvents(cursorIgnoreEvents);
</script>
<div class="flex flex-col children:grow gap-2">
@@ -280,6 +341,18 @@
Resizable
<input type="checkbox" bind:checked={resizable} />
</label>
<label>
Maximizable
<input type="checkbox" bind:checked={maximizable} />
</label>
<label>
Minimizable
<input type="checkbox" bind:checked={minimizable} />
</label>
<label>
Closable
<input type="checkbox" bind:checked={closable} />
</label>
<label>
Has decorations
<input type="checkbox" bind:checked={decorations} />
@@ -455,5 +528,88 @@
<button class="btn" id="open-url"> Open URL </button>
</form>
</div>
<br />
{#if isWindows || isMacOS}
<div class="flex flex-col gap-1">
<div class="flex">
<label>
Effect
<select class="input" bind:value={selectedEffect}>
{#each effectOptions as effect}
<option value={effect}>{effect}</option>
{/each}
</select>
</label>
<label>
State
<select class="input" bind:value={effectState}>
{#each effectStateOptions as state}
<option value={state}>{state}</option>
{/each}
</select>
</label>
<label>
Radius
<input class="input" type="number" bind:value={effectRadius} />
</label>
</div>
<div class="flex">
<label>
Color
<div class="flex">
<input
style="max-width: 120px;"
class="input"
type="number"
placeholder="R"
bind:value={effectR}
/>
<input
style="max-width: 120px;"
class="input"
type="number"
placeholder="G"
bind:value={effectG}
/>
<input
style="max-width: 120px;"
class="input"
type="number"
placeholder="B"
bind:value={effectB}
/>
<input
style="max-width: 120px;"
class="input"
type="number"
placeholder="A"
bind:value={effectA}
/>
</div>
</label>
</div>
<div class="flex">
<button class="btn" style="width: 80px;" on:click={addEffect}
>Add</button
>
</div>
<div class="flex">
<div>
Applied effects: {effects.length ? effects.join(",") : "None"}
</div>
<button class="btn" style="width: 80px;" on:click={clearEffects}
>Clear</button
>
</div>
</div>
{/if}
{/if}
</div>