snapshot of https://github.com/tauri-apps/tauri.git (dev, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Audit / audit-js (push) Failing after 0s
bench / bench (map[os:ubuntu-latest runner:xvfb-run --auto-servernum target:x86_64-unknown-linux-gnu], nightly) (push) Failing after 0s
covector version or publish / run-integration-tests (ubuntu-latest) (push) Failing after 0s
Audit / audit-rust (push) Failing after 0s
test `@tauri-apps/cli` / test (ubuntu-latest) (push) Failing after 0s
test `tauri-cli` / test (map[os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
deploy schema worker / deploy (push) Failing after 0s
lint rust / clippy (map[cargo:cargo include: os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
lint rust / clippy (map[cargo:cross include:--package api os:ubuntu-latest target:aarch64-linux-android]) (push) Failing after 0s
supply chain health status / check rust dependencies with cargo vet (push) Failing after 0s
test `@tauri-apps/api` e2e (mobile) / android (push) Failing after 0s
test `@tauri-apps/api` e2e / test (map[name:Linux os:ubuntu-latest]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
Udeps / changes (push) Failing after 0s
Udeps / setup (push) Has been skipped
Udeps / udeps (push) Has been skipped
bench / bench (map[os:macos-latest runner: target:aarch64-apple-darwin], nightly) (push) Has been cancelled
bench / bench (map[os:windows-latest runner: target:x86_64-pc-windows-msvc], nightly) (push) Has been cancelled
covector version or publish / run-integration-tests (macos-latest) (push) Has been cancelled
covector version or publish / run-integration-tests (windows-latest) (push) Has been cancelled
covector version or publish / version-or-publish (push) Has been cancelled
test `@tauri-apps/cli` / test (macos-latest) (push) Has been cancelled
test `tauri-cli` / test (map[args:--no-default-features --features native-tls-vendored os:windows-latest restore-only:true target:aarch64-pc-windows-msvc]) (push) Has been cancelled
test `tauri-cli` / test (map[os:macos-latest target:x86_64-apple-darwin]) (push) Has been cancelled
test `tauri-cli` / test (map[os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:macos-latest target:aarch64-apple-darwin]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include:--package api os:macos-latest target:aarch64-apple-ios]) (push) Has been cancelled
test `@tauri-apps/api` e2e (mobile) / ios (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:Windows os:windows-latest]) (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:macOS os:macos-latest]) (push) Has been cancelled
test `@tauri-apps/cli` / test (windows-latest) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
Some checks failed
Audit / audit-js (push) Failing after 0s
bench / bench (map[os:ubuntu-latest runner:xvfb-run --auto-servernum target:x86_64-unknown-linux-gnu], nightly) (push) Failing after 0s
covector version or publish / run-integration-tests (ubuntu-latest) (push) Failing after 0s
Audit / audit-rust (push) Failing after 0s
test `@tauri-apps/cli` / test (ubuntu-latest) (push) Failing after 0s
test `tauri-cli` / test (map[os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
deploy schema worker / deploy (push) Failing after 0s
lint rust / clippy (map[cargo:cargo include: os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
lint rust / clippy (map[cargo:cross include:--package api os:ubuntu-latest target:aarch64-linux-android]) (push) Failing after 0s
supply chain health status / check rust dependencies with cargo vet (push) Failing after 0s
test `@tauri-apps/api` e2e (mobile) / android (push) Failing after 0s
test `@tauri-apps/api` e2e / test (map[name:Linux os:ubuntu-latest]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
Udeps / changes (push) Failing after 0s
Udeps / setup (push) Has been skipped
Udeps / udeps (push) Has been skipped
bench / bench (map[os:macos-latest runner: target:aarch64-apple-darwin], nightly) (push) Has been cancelled
bench / bench (map[os:windows-latest runner: target:x86_64-pc-windows-msvc], nightly) (push) Has been cancelled
covector version or publish / run-integration-tests (macos-latest) (push) Has been cancelled
covector version or publish / run-integration-tests (windows-latest) (push) Has been cancelled
covector version or publish / version-or-publish (push) Has been cancelled
test `@tauri-apps/cli` / test (macos-latest) (push) Has been cancelled
test `tauri-cli` / test (map[args:--no-default-features --features native-tls-vendored os:windows-latest restore-only:true target:aarch64-pc-windows-msvc]) (push) Has been cancelled
test `tauri-cli` / test (map[os:macos-latest target:x86_64-apple-darwin]) (push) Has been cancelled
test `tauri-cli` / test (map[os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:macos-latest target:aarch64-apple-darwin]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include:--package api os:macos-latest target:aarch64-apple-ios]) (push) Has been cancelled
test `@tauri-apps/api` e2e (mobile) / ios (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:Windows os:windows-latest]) (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:macOS os:macos-latest]) (push) Has been cancelled
test `@tauri-apps/cli` / test (windows-latest) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
This commit is contained in:
43
examples/api/src/views/App.svelte
Normal file
43
examples/api/src/views/App.svelte
Normal file
@@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { show, hide, setDockVisibility } from '@tauri-apps/api/app'
|
||||
import type { ViewProps } from '../App.svelte'
|
||||
|
||||
let { onMessage }: ViewProps = $props()
|
||||
let dockVisible = $state(true)
|
||||
|
||||
function showApp() {
|
||||
hideApp()
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
show()
|
||||
.then(() => onMessage('Shown app'))
|
||||
.catch(onMessage)
|
||||
}, 2000)
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function hideApp() {
|
||||
return hide()
|
||||
.then(() => onMessage('Hide app'))
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
async function toggleDockVisibility() {
|
||||
await setDockVisibility(!dockVisible)
|
||||
dockVisible = !dockVisible
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn"
|
||||
id="show"
|
||||
title="Hides and shows the app after 2 seconds"
|
||||
onclick={showApp}>Show</button
|
||||
>
|
||||
<button class="btn" id="hide" onclick={hideApp}>Hide</button>
|
||||
<button class="btn" id="toggle-dock-visibility" onclick={toggleDockVisibility}
|
||||
>Toggle dock visibility</button
|
||||
>
|
||||
</div>
|
||||
70
examples/api/src/views/Communication.svelte
Normal file
70
examples/api/src/views/Communication.svelte
Normal file
@@ -0,0 +1,70 @@
|
||||
<script lang="ts">
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
|
||||
import { Channel, invoke } from '@tauri-apps/api/core'
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
import type { UnlistenFn } from '@tauri-apps/api/event'
|
||||
import type { ViewProps } from '../App.svelte'
|
||||
|
||||
let { onMessage }: ViewProps = $props()
|
||||
|
||||
const webviewWindow = getCurrentWebviewWindow()
|
||||
|
||||
let unlisten: UnlistenFn | undefined
|
||||
onMount(async () => {
|
||||
unlisten = await webviewWindow.listen('rust-event', onMessage)
|
||||
})
|
||||
onDestroy(() => {
|
||||
unlisten?.()
|
||||
})
|
||||
|
||||
function log() {
|
||||
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',
|
||||
body: {
|
||||
id: 5,
|
||||
name: 'test'
|
||||
}
|
||||
})
|
||||
.then(onMessage)
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function echo() {
|
||||
invoke('echo', {
|
||||
message: 'Tauri JSON request!'
|
||||
})
|
||||
.then(onMessage)
|
||||
.catch(onMessage)
|
||||
|
||||
invoke('echo', [1, 2, 3]).then(onMessage).catch(onMessage)
|
||||
}
|
||||
|
||||
function spam() {
|
||||
const channel = new Channel()
|
||||
channel.onmessage = onMessage
|
||||
invoke('spam', { channel })
|
||||
}
|
||||
|
||||
function emitEvent() {
|
||||
webviewWindow.emit('js-event', 'this is the payload string')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button class="btn" id="log" onclick={log}>Call Log API</button>
|
||||
<button class="btn" id="request" onclick={performRequest}>
|
||||
Call Request (async) API
|
||||
</button>
|
||||
<button class="btn" id="event" onclick={emitEvent}>
|
||||
Send event to Rust
|
||||
</button>
|
||||
<button class="btn" id="request" onclick={echo}>Echo</button>
|
||||
<button class="btn" id="request" onclick={spam}>Spam</button>
|
||||
</div>
|
||||
126
examples/api/src/views/Menu.svelte
Normal file
126
examples/api/src/views/Menu.svelte
Normal file
@@ -0,0 +1,126 @@
|
||||
<script lang="ts">
|
||||
import { Menu, Submenu, NativeIcon } from '@tauri-apps/api/menu'
|
||||
import MenuBuilder, {
|
||||
reorderMenuItems,
|
||||
type Item,
|
||||
type MenuItemClickDetail,
|
||||
type MenuItems
|
||||
} from '../components/MenuBuilder.svelte'
|
||||
import { defaultWindowIcon } from '@tauri-apps/api/app'
|
||||
import type { ViewProps } from '../App.svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
import type { Image } from '@tauri-apps/api/image'
|
||||
|
||||
let { onMessage }: ViewProps = $props()
|
||||
let items = $state<Item[]>([])
|
||||
|
||||
let menu: Menu | undefined
|
||||
let popupMenu: Menu | undefined
|
||||
let submenu: Submenu | undefined
|
||||
|
||||
const macOS = navigator.userAgent.includes('Macintosh')
|
||||
|
||||
async function createSubmenu(): Promise<Submenu> {
|
||||
return await Submenu.new({
|
||||
text: 'app',
|
||||
items: items.map((i) => i.menu).filter(Boolean) as MenuItems[]
|
||||
})
|
||||
}
|
||||
|
||||
async function createSubmenuWithNativeIcon(): Promise<Submenu> {
|
||||
return await Submenu.new({
|
||||
text: 'Submenu with NativeIcon',
|
||||
icon: NativeIcon.Folder,
|
||||
items: items.map((i) => i.menu).filter(Boolean) as MenuItems[]
|
||||
})
|
||||
}
|
||||
|
||||
async function createSubmenuWithImageIcon(): Promise<Submenu> {
|
||||
let icon: Image | undefined
|
||||
try {
|
||||
icon = (await defaultWindowIcon())!
|
||||
return await Submenu.new({
|
||||
text: 'Submenu with Image',
|
||||
icon,
|
||||
items: items.map((i) => i.menu).filter(Boolean) as MenuItems[]
|
||||
})
|
||||
} finally {
|
||||
icon?.close()
|
||||
}
|
||||
}
|
||||
|
||||
async function setMenu(newSubmenu: Submenu) {
|
||||
menu?.close()
|
||||
menu = undefined
|
||||
submenu?.close()
|
||||
submenu = newSubmenu
|
||||
|
||||
menu = await Menu.new({
|
||||
items: [submenu]
|
||||
})
|
||||
await (macOS ? menu.setAsAppMenu() : menu.setAsWindowMenu())
|
||||
}
|
||||
|
||||
async function create() {
|
||||
await setMenu(await createSubmenu())
|
||||
}
|
||||
|
||||
async function createWithNativeIcon() {
|
||||
await setMenu(await createSubmenuWithNativeIcon())
|
||||
}
|
||||
|
||||
async function createWithImageIcon() {
|
||||
await setMenu(await createSubmenuWithImageIcon())
|
||||
}
|
||||
|
||||
async function popup() {
|
||||
popupMenu?.close()
|
||||
popupMenu = undefined
|
||||
popupMenu = await Menu.new({
|
||||
items: items.map((i) => i.menu).filter(Boolean) as MenuItems[]
|
||||
})
|
||||
await popupMenu.popup()
|
||||
}
|
||||
|
||||
function onItemClick(detail: MenuItemClickDetail) {
|
||||
onMessage(`Item ${detail.text} clicked`)
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
menu?.close()
|
||||
submenu?.close()
|
||||
popupMenu?.close()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="grid gap-8 mb-4">
|
||||
<MenuBuilder
|
||||
bind:items
|
||||
itemClick={onItemClick}
|
||||
onItemAdded={async (item) => {
|
||||
if (item.menu) {
|
||||
await submenu?.append(item.menu)
|
||||
}
|
||||
}}
|
||||
onItemRemoved={async (item) => {
|
||||
if (item.menu) {
|
||||
await submenu?.remove(item.menu)
|
||||
}
|
||||
}}
|
||||
onItemMoved={(item, toIndex) => {
|
||||
if (submenu) {
|
||||
reorderMenuItems(submenu, item, toIndex)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn" onclick={create}>Create menu</button>
|
||||
<button class="btn" onclick={createWithNativeIcon}
|
||||
>Create menu with NativeIcon</button
|
||||
>
|
||||
<button class="btn" onclick={createWithImageIcon}
|
||||
>Create menu with Image icon</button
|
||||
>
|
||||
<button class="btn" onclick={popup}>Popup</button>
|
||||
</div>
|
||||
</div>
|
||||
134
examples/api/src/views/Tray.svelte
Normal file
134
examples/api/src/views/Tray.svelte
Normal file
@@ -0,0 +1,134 @@
|
||||
<script lang="ts">
|
||||
import { TrayIcon } from '@tauri-apps/api/tray'
|
||||
import MenuBuilder, {
|
||||
reorderMenuItems,
|
||||
type Item,
|
||||
type MenuItemClickDetail,
|
||||
type MenuItems
|
||||
} from '../components/MenuBuilder.svelte'
|
||||
import { Menu } from '@tauri-apps/api/menu'
|
||||
import type { ViewProps } from '../App.svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
let { onMessage }: ViewProps = $props()
|
||||
|
||||
let icon = $state<string>('../../.icons/tray_icon.png')
|
||||
let tooltip = $state<string>()
|
||||
let title = $state<string>()
|
||||
let iconAsTemplate = $state(false)
|
||||
let menuOnLeftClick = $state(true)
|
||||
let menuItems = $state<Item[]>([])
|
||||
|
||||
let menu: Menu | undefined
|
||||
let tray = $state<TrayIcon | undefined>()
|
||||
|
||||
function onItemClick(detail: MenuItemClickDetail) {
|
||||
onMessage(`Item ${detail.text} clicked`)
|
||||
}
|
||||
|
||||
async function create() {
|
||||
try {
|
||||
menu = await Menu.new({
|
||||
items: menuItems.map((i) => i.menu).filter(Boolean) as MenuItems[]
|
||||
})
|
||||
tray = await TrayIcon.new({
|
||||
icon,
|
||||
tooltip,
|
||||
title,
|
||||
iconAsTemplate,
|
||||
menuOnLeftClick,
|
||||
menu,
|
||||
action: (event) => onMessage(event)
|
||||
})
|
||||
} catch (error) {
|
||||
menu?.close()
|
||||
menu = undefined
|
||||
tray?.close()
|
||||
tray = undefined
|
||||
onMessage(error)
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
menu?.close()
|
||||
tray?.close()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="grid gap-8 mb-4">
|
||||
<MenuBuilder
|
||||
bind:items={menuItems}
|
||||
itemClick={onItemClick}
|
||||
onItemAdded={async (item) => {
|
||||
if (item.menu) {
|
||||
await menu?.append(item.menu)
|
||||
}
|
||||
}}
|
||||
onItemRemoved={async (item) => {
|
||||
if (item.menu) {
|
||||
await menu?.remove(item.menu)
|
||||
}
|
||||
}}
|
||||
onItemMoved={(item, toIndex) => {
|
||||
if (menu) {
|
||||
reorderMenuItems(menu, item, toIndex)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="flex items-center gap-8">
|
||||
<div class="grid gap-2 grid-cols-3 items-center">
|
||||
<input
|
||||
class="input grow"
|
||||
type="text"
|
||||
placeholder="Title"
|
||||
bind:value={title}
|
||||
/>
|
||||
|
||||
<input
|
||||
class="input grow"
|
||||
type="text"
|
||||
placeholder="Tooltip"
|
||||
bind:value={tooltip}
|
||||
/>
|
||||
|
||||
<label class="flex gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={menuOnLeftClick}
|
||||
/>
|
||||
Menu on left click
|
||||
</label>
|
||||
|
||||
<input
|
||||
class="input col-span-2"
|
||||
type="text"
|
||||
placeholder="Icon path"
|
||||
bind:value={icon}
|
||||
/>
|
||||
|
||||
<label class="flex gap-2">
|
||||
<input type="checkbox" class="checkbox" bind:checked={iconAsTemplate} />
|
||||
Icon as template
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
{#if tray}
|
||||
<button
|
||||
class="btn"
|
||||
onclick={() => {
|
||||
tray?.close()
|
||||
tray = undefined
|
||||
}}
|
||||
title="Remove the tray icon">Remove tray</button
|
||||
>
|
||||
{:else}
|
||||
<button class="btn" onclick={create} title="Creates the tray icon"
|
||||
>Create tray</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
72
examples/api/src/views/WebRTC.svelte
Normal file
72
examples/api/src/views/WebRTC.svelte
Normal file
@@ -0,0 +1,72 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
import type { ViewProps } from '../App.svelte'
|
||||
|
||||
let { onMessage }: ViewProps = $props()
|
||||
|
||||
let video: HTMLVideoElement
|
||||
let mediaStream: MediaStream | undefined
|
||||
|
||||
const constraints: MediaStreamConstraints = {
|
||||
audio: true,
|
||||
video: true
|
||||
}
|
||||
|
||||
function handleSuccess(stream: MediaStream) {
|
||||
const settings = stream.getTracks().map((track) => track.getSettings())
|
||||
onMessage(`Got streams: ${JSON.stringify(settings, null, 2)}`)
|
||||
const videoTracks = stream.getVideoTracks()
|
||||
onMessage(`Using video device: ${videoTracks[0]?.label ?? 'Unknown'}`)
|
||||
// @ts-expect-error
|
||||
window.stream = mediaStream // make variable available to browser console
|
||||
if (video) {
|
||||
video.srcObject = stream
|
||||
}
|
||||
}
|
||||
|
||||
function handleError(error: unknown) {
|
||||
if (!(error instanceof DOMException)) {
|
||||
onMessage(`getUserMedia error: ${error}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (error.name === 'ConstraintNotSatisfiedError') {
|
||||
// const v = constraints.video
|
||||
// onMessage(
|
||||
// `The resolution ${v.width.exact}x${v.height.exact} px is not supported by your device.`
|
||||
// )
|
||||
onMessage(
|
||||
`The constraints ${constraints} can not be satisified by your device.`
|
||||
)
|
||||
} else if (error.name === 'PermissionDeniedError') {
|
||||
onMessage(
|
||||
'Permissions have not been granted to use your camera and '
|
||||
+ 'microphone, you need to allow the page access to your devices in '
|
||||
+ 'order for the demo to work.'
|
||||
)
|
||||
}
|
||||
onMessage(`getUserMedia error: ${error}`)
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia(constraints)
|
||||
handleSuccess(stream)
|
||||
} catch (e) {
|
||||
handleError(e)
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
for (const track of mediaStream?.getTracks() ?? []) {
|
||||
track.stop()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="note-red grow">Not available for Linux</div>
|
||||
<video id="localVideo" autoplay playsinline bind:this={video}>
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
</div>
|
||||
62
examples/api/src/views/Welcome.svelte
Normal file
62
examples/api/src/views/Welcome.svelte
Normal file
@@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import {
|
||||
getName,
|
||||
getVersion,
|
||||
getTauriVersion,
|
||||
getBundleType,
|
||||
exit
|
||||
} from '@tauri-apps/api/app'
|
||||
import type { ViewProps } from '../App.svelte'
|
||||
|
||||
let { onMessage }: ViewProps = $props()
|
||||
|
||||
let version = $state('1.0.0')
|
||||
let tauriVersion = $state('1.0.0')
|
||||
let appName = $state('Unknown')
|
||||
let bundleType = $state('Unknown')
|
||||
|
||||
getName().then((n) => {
|
||||
appName = n
|
||||
})
|
||||
getVersion().then((v) => {
|
||||
version = v
|
||||
})
|
||||
getTauriVersion().then((v) => {
|
||||
tauriVersion = v
|
||||
})
|
||||
getBundleType().then((b) => {
|
||||
if (b) {
|
||||
bundleType = b
|
||||
}
|
||||
})
|
||||
|
||||
function contextMenu() {
|
||||
invoke('plugin:app-menu|popup')
|
||||
}
|
||||
|
||||
function exitApp() {
|
||||
exit().catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="grid gap-8 justify-items-start">
|
||||
<p>
|
||||
This is a demo of Tauri's API capabilities using the <code
|
||||
>@tauri-apps/api</code
|
||||
> package. It's used as the main validation app, serving as the test bed of our
|
||||
development process. In the future, this app will be used on Tauri's integration
|
||||
tests.
|
||||
</p>
|
||||
<pre>
|
||||
App name: <code>{appName}</code>
|
||||
App version: <code>{version}</code>
|
||||
Tauri version: <code>{tauriVersion}</code>
|
||||
Bundle type: <code>{bundleType}</code>
|
||||
</pre>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button class="btn" onclick={contextMenu}>Context menu</button>
|
||||
<button class="btn" id="exit" onclick={exitApp}>Exit</button>
|
||||
</div>
|
||||
</div>
|
||||
996
examples/api/src/views/Window.svelte
Normal file
996
examples/api/src/views/Window.svelte
Normal file
@@ -0,0 +1,996 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte'
|
||||
import {
|
||||
LogicalSize,
|
||||
UserAttentionType,
|
||||
PhysicalSize,
|
||||
PhysicalPosition,
|
||||
Effect,
|
||||
EffectState,
|
||||
ProgressBarStatus,
|
||||
availableMonitors
|
||||
} from '@tauri-apps/api/window'
|
||||
import { WebviewWindow } from '@tauri-apps/api/webviewWindow'
|
||||
import type {
|
||||
CursorIcon,
|
||||
Effects,
|
||||
Monitor,
|
||||
Theme
|
||||
} from '@tauri-apps/api/window'
|
||||
import type { UnlistenFn } from '@tauri-apps/api/event'
|
||||
import type { ViewProps } from '../App.svelte'
|
||||
|
||||
let { onMessage }: ViewProps = $props()
|
||||
|
||||
const webview = WebviewWindow.getCurrent()
|
||||
|
||||
const webviewMap = $state({
|
||||
[webview.label]: webview
|
||||
})
|
||||
let selectedWebviewLabel = $state(webview.label)
|
||||
let selectedWebview = $derived(webviewMap[selectedWebviewLabel])
|
||||
|
||||
let monitorMap = $state<Record<string, Monitor>>({})
|
||||
let selectedMonitor = $state('')
|
||||
|
||||
availableMonitors().then((monitors) => {
|
||||
monitorMap = Object.fromEntries(
|
||||
monitors.map((m, i) => [m.name ?? `Monitor ${i + 1}`, m])
|
||||
)
|
||||
})
|
||||
|
||||
let focusable = $state(true)
|
||||
|
||||
const cursorIconOptions: CursorIcon[] = [
|
||||
'default',
|
||||
'crosshair',
|
||||
'hand',
|
||||
'arrow',
|
||||
'move',
|
||||
'text',
|
||||
'wait',
|
||||
'help',
|
||||
'progress',
|
||||
// something cannot be done
|
||||
'notAllowed',
|
||||
'contextMenu',
|
||||
'cell',
|
||||
'verticalText',
|
||||
'alias',
|
||||
'copy',
|
||||
'noDrop',
|
||||
// something can be grabbed
|
||||
'grab',
|
||||
/// something is grabbed
|
||||
'grabbing',
|
||||
'allScroll',
|
||||
'zoomIn',
|
||||
'zoomOut',
|
||||
// edge is to be moved
|
||||
'eResize',
|
||||
'nResize',
|
||||
'neResize',
|
||||
'nwResize',
|
||||
'sResize',
|
||||
'seResize',
|
||||
'swResize',
|
||||
'wResize',
|
||||
'ewResize',
|
||||
'nsResize',
|
||||
'neswResize',
|
||||
'nwseResize',
|
||||
'colResize',
|
||||
'rowResize'
|
||||
]
|
||||
|
||||
const windowsEffects: Effect[] = [
|
||||
Effect.Mica,
|
||||
Effect.Blur,
|
||||
Effect.Acrylic,
|
||||
Effect.Tabbed,
|
||||
Effect.TabbedDark,
|
||||
Effect.TabbedLight
|
||||
]
|
||||
const isWindows = navigator.appVersion.includes('Windows')
|
||||
const isMacOS = navigator.appVersion.includes('Macintosh')
|
||||
const effectOptions: Effect[] = isWindows
|
||||
? windowsEffects
|
||||
: (Object.values(Effect) as Effect[]).filter(
|
||||
(e) => !windowsEffects.includes(e)
|
||||
)
|
||||
const effectStateOptions = Object.values(EffectState) as EffectState[]
|
||||
|
||||
const progressBarStatusOptions = Object.values(ProgressBarStatus)
|
||||
|
||||
let newWebviewLabel = $state<string>()
|
||||
|
||||
let resizable = $state(true)
|
||||
let maximizable = $state(true)
|
||||
let minimizable = $state(true)
|
||||
let closable = $state(true)
|
||||
let maximized = $state(false)
|
||||
let decorations = $state(true)
|
||||
let alwaysOnTop = $state(false)
|
||||
let alwaysOnBottom = $state(false)
|
||||
let contentProtected = $state(false)
|
||||
let fullscreen = $state(false)
|
||||
let simpleFullscreen = $state(false)
|
||||
let width = $state(100)
|
||||
let height = $state(100)
|
||||
let minWidth = $state<number | null>(null)
|
||||
let minHeight = $state<number | null>(null)
|
||||
let maxWidth = $state<number | null>(null)
|
||||
let maxHeight = $state<number | null>(null)
|
||||
let x = $state(0)
|
||||
let y = $state(0)
|
||||
let scaleFactor = $state(1)
|
||||
let innerPosition = $state(new PhysicalPosition(0, 0))
|
||||
let outerPosition = $state(new PhysicalPosition(0, 0))
|
||||
let innerSize = $state(new PhysicalSize(0, 0))
|
||||
let outerSize = $state(new PhysicalSize(0, 0))
|
||||
// name of the monitor containing the selected window's top-left corner
|
||||
let monitor = $derived(
|
||||
Object.entries(monitorMap).find(
|
||||
([, m]) =>
|
||||
outerPosition.x >= m.position.x
|
||||
&& outerPosition.x < m.position.x + m.size.width
|
||||
&& outerPosition.y >= m.position.y
|
||||
&& outerPosition.y < m.position.y + m.size.height
|
||||
)?.[0] ?? ''
|
||||
)
|
||||
let resizeEventUnlisten: UnlistenFn | undefined
|
||||
let moveEventUnlisten: UnlistenFn | undefined
|
||||
let cursorGrab = $state(false)
|
||||
let cursorVisible = $state(true)
|
||||
let cursorX = $state<number | null>(null)
|
||||
let cursorY = $state<number | null>(null)
|
||||
let cursorIcon = $state<CursorIcon>('default')
|
||||
let cursorIgnoreEvents = $state(false)
|
||||
let windowTitle = $state('Awesome Tauri Example!')
|
||||
|
||||
let theme = $state<Theme | 'auto'>('auto')
|
||||
|
||||
let effects = $state<Effect[]>([])
|
||||
let selectedEffect = $state<Effect>()
|
||||
let effectState = $state<EffectState>()
|
||||
let effectRadius = $state<number>()
|
||||
let effectInteractive = $state(false)
|
||||
let effectR = $state<number>(),
|
||||
effectG = $state<number>(),
|
||||
effectB = $state<number>(),
|
||||
effectA = $state<number>()
|
||||
|
||||
let selectedProgressBarStatus = $state<ProgressBarStatus>(
|
||||
ProgressBarStatus.None
|
||||
)
|
||||
let progress = $state(0)
|
||||
|
||||
let windowIconPath = $state('')
|
||||
|
||||
function setTitle() {
|
||||
selectedWebview.setTitle(windowTitle)
|
||||
}
|
||||
|
||||
async function hide() {
|
||||
let visible = await selectedWebview.isVisible()
|
||||
onMessage('window is ' + (visible ? 'visible' : 'invisible'))
|
||||
await selectedWebview.hide()
|
||||
|
||||
setTimeout(async () => {
|
||||
visible = await selectedWebview.isVisible()
|
||||
onMessage('window is ' + (visible ? 'visible' : 'invisible'))
|
||||
|
||||
await selectedWebview.show()
|
||||
visible = await selectedWebview.isVisible()
|
||||
onMessage('window is ' + (visible ? 'visible' : 'invisible'))
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
async function disable() {
|
||||
let enabled = await selectedWebview.isEnabled()
|
||||
onMessage('window is ' + (enabled ? 'enabled' : 'disabled'))
|
||||
|
||||
await selectedWebview.setEnabled(false)
|
||||
|
||||
setTimeout(async () => {
|
||||
enabled = await selectedWebview.isEnabled()
|
||||
onMessage('window is ' + (enabled ? 'enabled' : 'disabled'))
|
||||
|
||||
await selectedWebview.setEnabled(true)
|
||||
enabled = await selectedWebview.isEnabled()
|
||||
onMessage('window is ' + (enabled ? 'enabled' : 'disabled'))
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
function minimize() {
|
||||
selectedWebview.minimize()
|
||||
setTimeout(selectedWebview.unminimize, 2000)
|
||||
}
|
||||
|
||||
function changeIcon() {
|
||||
selectedWebview.setIcon(windowIconPath)
|
||||
}
|
||||
|
||||
function createWebviewWindow() {
|
||||
if (!newWebviewLabel) return
|
||||
|
||||
const label = `main-${newWebviewLabel}`
|
||||
const webview = new WebviewWindow(label)
|
||||
webviewMap[label] = webview
|
||||
webview.once('tauri://error', function (e) {
|
||||
onMessage('Error creating new webview ' + JSON.stringify(e))
|
||||
})
|
||||
webview.once('tauri://created', function () {
|
||||
onMessage('webview created')
|
||||
})
|
||||
}
|
||||
|
||||
function loadWindowSize() {
|
||||
selectedWebview.innerSize().then((response) => {
|
||||
innerSize = response
|
||||
width = innerSize.width
|
||||
height = innerSize.height
|
||||
})
|
||||
selectedWebview.outerSize().then((response) => {
|
||||
outerSize = response
|
||||
})
|
||||
}
|
||||
|
||||
function loadWindowPosition() {
|
||||
selectedWebview.innerPosition().then((response) => {
|
||||
innerPosition = response
|
||||
})
|
||||
selectedWebview.outerPosition().then((response) => {
|
||||
outerPosition = response
|
||||
x = outerPosition.x
|
||||
y = outerPosition.y
|
||||
})
|
||||
}
|
||||
|
||||
async function addWindowEventListeners(window: WebviewWindow | undefined) {
|
||||
if (!window) return
|
||||
resizeEventUnlisten?.()
|
||||
moveEventUnlisten?.()
|
||||
moveEventUnlisten = await window.listen('tauri://move', loadWindowPosition)
|
||||
resizeEventUnlisten = await window.listen('tauri://resize', loadWindowSize)
|
||||
}
|
||||
|
||||
async function requestUserAttention() {
|
||||
await selectedWebview.minimize()
|
||||
await selectedWebview.requestUserAttention(UserAttentionType.Critical)
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000))
|
||||
await selectedWebview.requestUserAttention(null)
|
||||
}
|
||||
|
||||
async function switchTheme() {
|
||||
switch (theme) {
|
||||
case 'dark':
|
||||
theme = 'light'
|
||||
break
|
||||
case 'light':
|
||||
theme = 'auto'
|
||||
break
|
||||
case 'auto':
|
||||
theme = 'dark'
|
||||
break
|
||||
}
|
||||
await selectedWebview.setTheme(theme === 'auto' ? null : theme)
|
||||
}
|
||||
|
||||
async function updateProgressBar() {
|
||||
selectedWebview.setProgressBar({
|
||||
status: selectedProgressBarStatus,
|
||||
progress
|
||||
})
|
||||
}
|
||||
|
||||
async function addEffect() {
|
||||
if (selectedEffect && !effects.includes(selectedEffect)) {
|
||||
effects = [...effects, selectedEffect]
|
||||
}
|
||||
|
||||
const payload: Effects = {
|
||||
effects,
|
||||
state: effectState,
|
||||
radius: effectRadius,
|
||||
interactive: effectInteractive
|
||||
}
|
||||
if (
|
||||
Number.isInteger(effectR)
|
||||
&& Number.isInteger(effectG)
|
||||
&& Number.isInteger(effectB)
|
||||
&& Number.isInteger(effectA)
|
||||
) {
|
||||
payload.color = [effectR!, effectG!, effectB!, effectA!]
|
||||
}
|
||||
|
||||
// see `:root.window-effects` in app.css
|
||||
document.documentElement.classList.add('window-effects')
|
||||
await selectedWebview.clearEffects()
|
||||
await selectedWebview.setEffects(payload)
|
||||
}
|
||||
|
||||
async function clearEffects() {
|
||||
effects = []
|
||||
await selectedWebview.clearEffects()
|
||||
document.documentElement.classList.remove('window-effects')
|
||||
}
|
||||
|
||||
async function updatePosition() {
|
||||
selectedWebview.setPosition(new PhysicalPosition(x, y))
|
||||
}
|
||||
|
||||
async function updateSize() {
|
||||
if (width && height) {
|
||||
selectedWebview.setSize(new PhysicalSize(width, height))
|
||||
}
|
||||
}
|
||||
|
||||
function refreshSelectedWebview() {
|
||||
loadWindowPosition()
|
||||
loadWindowSize()
|
||||
selectedWebview.scaleFactor().then((factor) => (scaleFactor = factor))
|
||||
addWindowEventListeners(selectedWebview)
|
||||
}
|
||||
|
||||
function updateResizable() {
|
||||
selectedWebview.setResizable(resizable)
|
||||
}
|
||||
|
||||
function updateMaximizable() {
|
||||
selectedWebview.setMaximizable(maximizable)
|
||||
}
|
||||
|
||||
function updateMinimizable() {
|
||||
selectedWebview.setMinimizable(minimizable)
|
||||
}
|
||||
|
||||
function updateClosable() {
|
||||
selectedWebview.setClosable(closable)
|
||||
}
|
||||
|
||||
function updateMaximized() {
|
||||
maximized ? selectedWebview.maximize() : selectedWebview.unmaximize()
|
||||
}
|
||||
|
||||
function updateDecorations() {
|
||||
selectedWebview.setDecorations(decorations)
|
||||
}
|
||||
|
||||
function updateAlwaysOnTop() {
|
||||
selectedWebview.setAlwaysOnTop(alwaysOnTop)
|
||||
}
|
||||
|
||||
function updateAlwaysOnBottom() {
|
||||
selectedWebview.setAlwaysOnBottom(alwaysOnBottom)
|
||||
}
|
||||
|
||||
function updateContentProtected() {
|
||||
selectedWebview.setContentProtected(contentProtected)
|
||||
}
|
||||
|
||||
function updateFullscreen() {
|
||||
selectedWebview.setFullscreen(fullscreen)
|
||||
}
|
||||
|
||||
function updateSimpleFullscreen() {
|
||||
selectedWebview.setSimpleFullscreen(simpleFullscreen)
|
||||
}
|
||||
|
||||
async function setFullscreenOnMonitor() {
|
||||
const target = monitorMap[selectedMonitor]
|
||||
if (!target) return
|
||||
await selectedWebview.setFullscreenOnMonitor(target.position)
|
||||
fullscreen = true
|
||||
}
|
||||
|
||||
function updateMinSize() {
|
||||
minWidth && minHeight
|
||||
? selectedWebview.setMinSize(new LogicalSize(minWidth, minHeight))
|
||||
: selectedWebview.setMinSize(null)
|
||||
}
|
||||
|
||||
function updateMaxSize() {
|
||||
maxWidth && maxHeight && maxWidth > 800 && maxHeight > 400
|
||||
? selectedWebview.setMaxSize(new LogicalSize(maxWidth, maxHeight))
|
||||
: selectedWebview.setMaxSize(null)
|
||||
}
|
||||
|
||||
function updateCursorGrab() {
|
||||
selectedWebview.setCursorGrab(cursorGrab)
|
||||
}
|
||||
|
||||
function updateCursorVisible() {
|
||||
selectedWebview.setCursorVisible(cursorVisible)
|
||||
}
|
||||
|
||||
function updateCursorIgnoreEvents() {
|
||||
selectedWebview.setIgnoreCursorEvents(cursorIgnoreEvents)
|
||||
}
|
||||
|
||||
function updateCursorIcon() {
|
||||
selectedWebview.setCursorIcon(cursorIcon)
|
||||
}
|
||||
|
||||
function updateCursorPosition() {
|
||||
cursorX !== null
|
||||
&& cursorY !== null
|
||||
&& selectedWebview.setCursorPosition(
|
||||
new PhysicalPosition(cursorX, cursorY)
|
||||
)
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
selectedWebview
|
||||
refreshSelectedWebview()
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
resizeEventUnlisten?.()
|
||||
moveEventUnlisten?.()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col *:grow gap-8 mb-4">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-4 pb-6 border-b-solid border-b-1 border-code"
|
||||
>
|
||||
{#if Object.keys(webviewMap).length >= 1}
|
||||
<div class="grid gap-1">
|
||||
<h4 class="my-2">Selected Window</h4>
|
||||
<select
|
||||
class="input"
|
||||
bind:value={selectedWebviewLabel}
|
||||
onchange={refreshSelectedWebview}
|
||||
>
|
||||
<option value="" disabled selected>Choose a window...</option>
|
||||
{#each Object.keys(webviewMap) as label}
|
||||
<option value={label}>{label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="grid gap-1">
|
||||
<h4 class="my-2">Create New Window</h4>
|
||||
<form
|
||||
class="flex gap-2"
|
||||
onsubmit={(ev) => {
|
||||
createWebviewWindow()
|
||||
ev.preventDefault()
|
||||
}}
|
||||
>
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="New window label.."
|
||||
bind:value={newWebviewLabel}
|
||||
/>
|
||||
<button class="btn" type="submit">Create</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{#if selectedWebview}
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<div class="grid gap-1 grow">
|
||||
<h4 class="my-2">Change Window Icon</h4>
|
||||
<form
|
||||
class="flex gap-2"
|
||||
onsubmit={(ev) => {
|
||||
changeIcon()
|
||||
ev.preventDefault()
|
||||
}}
|
||||
>
|
||||
<input
|
||||
class="input flex-1 min-w-10"
|
||||
placeholder="Window icon path"
|
||||
bind:value={windowIconPath}
|
||||
/>
|
||||
<button class="btn" type="submit">Change</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid gap-1 grow">
|
||||
<h4 class="my-2">Set Window Title</h4>
|
||||
<form
|
||||
class="flex gap-2"
|
||||
onsubmit={(ev) => {
|
||||
setTitle()
|
||||
ev.preventDefault()
|
||||
}}
|
||||
>
|
||||
<input class="input flex-1 min-w-10" bind:value={windowTitle} />
|
||||
<button class="btn" type="submit">Set</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
class="btn"
|
||||
title="Unminimizes after 2 seconds"
|
||||
onclick={() => selectedWebview.center()}
|
||||
>
|
||||
Center
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
title="Unminimizes after 2 seconds"
|
||||
onclick={minimize}
|
||||
>
|
||||
Minimize
|
||||
</button>
|
||||
<button class="btn" title="Visible again after 2 seconds" onclick={hide}>
|
||||
Hide
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
title="Enabled again after 2 seconds"
|
||||
onclick={disable}
|
||||
>
|
||||
Disable
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
onclick={requestUserAttention}
|
||||
title="Minimizes the window, requests attention for 3s and then resets it"
|
||||
>Request attention</button
|
||||
>
|
||||
<button class="btn" onclick={switchTheme}>Switch Theme ({theme})</button>
|
||||
<button
|
||||
class="btn"
|
||||
onclick={() => {
|
||||
focusable = !focusable
|
||||
selectedWebview.setFocusable(!focusable)
|
||||
}}
|
||||
>
|
||||
Set focusable to {!focusable}
|
||||
</button>
|
||||
|
||||
<form
|
||||
class="flex gap-2"
|
||||
onsubmit={(ev) => {
|
||||
setFullscreenOnMonitor()
|
||||
ev.preventDefault()
|
||||
}}
|
||||
>
|
||||
<button class="btn" type="submit" disabled={!selectedMonitor}>
|
||||
Set Fullscreen on Monitor
|
||||
</button>
|
||||
<select class="input" bind:value={selectedMonitor}>
|
||||
<option value="" disabled>Choose a monitor...</option>
|
||||
{#each Object.keys(monitorMap) as label}
|
||||
<option value={label}>{label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid cols-[repeat(auto-fill,minmax(180px,1fr))] *:flex *:gap-2">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={resizable}
|
||||
onchange={updateResizable}
|
||||
/>
|
||||
Resizable
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={maximizable}
|
||||
onchange={updateMaximizable}
|
||||
/>
|
||||
Maximizable
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={minimizable}
|
||||
onchange={updateMinimizable}
|
||||
/>
|
||||
Minimizable
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={closable}
|
||||
onchange={updateClosable}
|
||||
/>
|
||||
Closable
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={decorations}
|
||||
onchange={updateDecorations}
|
||||
/>
|
||||
Has decorations
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={alwaysOnTop}
|
||||
onchange={updateAlwaysOnTop}
|
||||
/>
|
||||
Always on top
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={alwaysOnBottom}
|
||||
onchange={updateAlwaysOnBottom}
|
||||
/>
|
||||
Always on bottom
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={contentProtected}
|
||||
onchange={updateContentProtected}
|
||||
/>
|
||||
Content protected
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={maximized}
|
||||
onchange={updateMaximized}
|
||||
/>
|
||||
Maximized
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={fullscreen}
|
||||
onchange={updateFullscreen}
|
||||
/>
|
||||
Fullscreen
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={simpleFullscreen}
|
||||
onchange={updateSimpleFullscreen}
|
||||
/>
|
||||
Simple fullscreen
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex flex-wrap *:flex-basis-30 gap-2">
|
||||
<div class="grid gap-1 *:grid">
|
||||
<label>
|
||||
X
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={x}
|
||||
onchange={updatePosition}
|
||||
min="0"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Y
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={y}
|
||||
onchange={updatePosition}
|
||||
min="0"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="grid gap-1 *:grid">
|
||||
<label>
|
||||
Width
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={width}
|
||||
onchange={updateSize}
|
||||
min="400"
|
||||
/>
|
||||
</label>
|
||||
<div>
|
||||
Height
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={height}
|
||||
onchange={updateSize}
|
||||
min="400"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-1 *:grid">
|
||||
<label>
|
||||
Min width
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={minWidth}
|
||||
onchange={updateMinSize}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Min height
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={minHeight}
|
||||
onchange={updateMinSize}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="grid gap-1 *:grid">
|
||||
<label>
|
||||
Max width
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={maxWidth}
|
||||
onchange={updateMaxSize}
|
||||
min="800"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Max height
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={maxHeight}
|
||||
onchange={updateMaxSize}
|
||||
min="400"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2 max-inline-2xl">
|
||||
<div>
|
||||
<div class="text-accent dark:text-darkAccent font-700 m-block-1">
|
||||
Inner Size
|
||||
</div>
|
||||
<span>Width: {innerSize.width}</span>
|
||||
<span>Height: {innerSize.height}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-accent dark:text-darkAccent font-700 m-block-1">
|
||||
Outer Size
|
||||
</div>
|
||||
<span>Width: {outerSize.width}</span>
|
||||
<span>Height: {outerSize.height}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-accent dark:text-darkAccent font-700 m-block-1">
|
||||
Inner Logical Size
|
||||
</div>
|
||||
<span>Width: {innerSize.toLogical(scaleFactor).width.toFixed(3)}</span>
|
||||
<span>Height: {innerSize.toLogical(scaleFactor).height.toFixed(3)}</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-accent dark:text-darkAccent font-700 m-block-1">
|
||||
Outer Logical Size
|
||||
</div>
|
||||
<span>Width: {outerSize.toLogical(scaleFactor).width.toFixed(3)}</span>
|
||||
<span>Height: {outerSize.toLogical(scaleFactor).height.toFixed(3)}</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-accent dark:text-darkAccent font-700 m-block-1">
|
||||
Inner Position
|
||||
</div>
|
||||
<span>x: {innerPosition.x}</span>
|
||||
<span>y: {innerPosition.y}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-accent dark:text-darkAccent font-700 m-block-1">
|
||||
Outer Position
|
||||
</div>
|
||||
<span>x: {outerPosition.x}</span>
|
||||
<span>y: {outerPosition.y}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-accent dark:text-darkAccent font-700 m-block-1">
|
||||
Inner Logical Position
|
||||
</div>
|
||||
<span>x: {innerPosition.toLogical(scaleFactor).x.toFixed(3)}</span>
|
||||
<span>y: {innerPosition.toLogical(scaleFactor).y.toFixed(3)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-accent dark:text-darkAccent font-700 m-block-1">
|
||||
Outer Logical Position
|
||||
</div>
|
||||
<span>x: {outerPosition.toLogical(scaleFactor).x.toFixed(3)}</span>
|
||||
<span>y: {outerPosition.toLogical(scaleFactor).y.toFixed(3)}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-accent dark:text-darkAccent font-700 m-block-1">
|
||||
Current Monitor
|
||||
</div>
|
||||
<span>{monitor}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<h4 class="my-2">Cursor</h4>
|
||||
<div class="flex gap-4 *:flex *:gap-2">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={cursorGrab}
|
||||
onchange={updateCursorGrab}
|
||||
/>
|
||||
Grab
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={cursorVisible}
|
||||
onchange={updateCursorVisible}
|
||||
/>
|
||||
Visible
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={cursorIgnoreEvents}
|
||||
onchange={updateCursorIgnoreEvents}
|
||||
/>
|
||||
Ignore events
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<label>
|
||||
Icon
|
||||
<select
|
||||
class="input"
|
||||
bind:value={cursorIcon}
|
||||
onchange={updateCursorIcon}
|
||||
>
|
||||
{#each cursorIconOptions as kind}
|
||||
<option value={kind}>{kind}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
X position
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={cursorX}
|
||||
onchange={updateCursorPosition}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Y position
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
bind:value={cursorY}
|
||||
onchange={updateCursorPosition}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex gap-2">
|
||||
<label>
|
||||
Progress Status
|
||||
<select
|
||||
class="input"
|
||||
bind:value={selectedProgressBarStatus}
|
||||
onchange={updateProgressBar}
|
||||
>
|
||||
{#each progressBarStatusOptions as status}
|
||||
<option value={status}>{status}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Progress
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
bind:value={progress}
|
||||
onchange={updateProgressBar}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if isWindows || isMacOS}
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<div>
|
||||
Applied effects: {effects.length ? effects.join(', ') : 'None'}
|
||||
</div>
|
||||
|
||||
<button class="btn" onclick={clearEffects}>Clear</button>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<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>
|
||||
|
||||
<label class="flex items-center gap-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox"
|
||||
bind:checked={effectInteractive}
|
||||
/>
|
||||
Interactive
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<label>
|
||||
Color
|
||||
<div class="flex gap-2 *:flex-basis-30">
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
placeholder="R"
|
||||
bind:value={effectR}
|
||||
/>
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
placeholder="G"
|
||||
bind:value={effectG}
|
||||
/>
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
placeholder="B"
|
||||
bind:value={effectB}
|
||||
/>
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
placeholder="A"
|
||||
bind:value={effectA}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<button class="btn" onclick={addEffect}>Add</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user