mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-05-01 12:08:06 +02:00
Merge remote-tracking branch 'origin/v2' into feat/camera
This commit is contained in:
+164
-153
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
<script>
|
||||
import { getMatches } from "tauri-plugin-cli-api";
|
||||
import { getMatches } from "@tauri-apps/plugin-cli";
|
||||
|
||||
export let onMessage;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user