refactor: migrate to svelte 5
This commit is contained in:
parent
f37019c3ef
commit
2d5c5f4130
23 changed files with 3529 additions and 126 deletions
|
@ -31,7 +31,7 @@
|
||||||
"prettier": "^3.5.0",
|
"prettier": "^3.5.0",
|
||||||
"prettier-plugin-svelte": "^3.3.3",
|
"prettier-plugin-svelte": "^3.3.3",
|
||||||
"puppeteer": "^22.15.0",
|
"puppeteer": "^22.15.0",
|
||||||
"svelte-check": "^3.8.6",
|
"svelte-check": "^4.0.0",
|
||||||
"wait-on": "^7.2.0"
|
"wait-on": "^7.2.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
@ -41,13 +41,13 @@
|
||||||
"@sveltejs/adapter-auto": "^3.3.1",
|
"@sveltejs/adapter-auto": "^3.3.1",
|
||||||
"@sveltejs/adapter-static": "^3.0.8",
|
"@sveltejs/adapter-static": "^3.0.8",
|
||||||
"@sveltejs/kit": "^2.17.1",
|
"@sveltejs/kit": "^2.17.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||||
"@tabler/icons-webfont": "^2.47.0",
|
"@tabler/icons-webfont": "^2.47.0",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"i18next": "^23.16.8",
|
"i18next": "^23.16.8",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"svelte": "^4.2.19",
|
"svelte": "^5.0.0",
|
||||||
"svelte-i18next": "^2.2.2",
|
"svelte-i18next": "^2.2.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
|
|
3333
pnpm-lock.yaml
generated
Normal file
3333
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
let heightOdd = false;
|
let heightOdd = $state(false);
|
||||||
let widthOdd = false;
|
let widthOdd = $state(false);
|
||||||
|
|
||||||
function updateOdd() {
|
function updateOdd() {
|
||||||
heightOdd = window.innerHeight % 2 === 1;
|
heightOdd = window.innerHeight % 2 === 1;
|
||||||
|
|
|
@ -7,17 +7,21 @@
|
||||||
const MAX_COUNT = 33;
|
const MAX_COUNT = 33;
|
||||||
const MARGIN_SIZE = 16;
|
const MARGIN_SIZE = 16;
|
||||||
|
|
||||||
let horizontalCount = START_COUNT;
|
let horizontalCount = $state(START_COUNT);
|
||||||
let verticalCount = START_COUNT;
|
let verticalCount = $state(START_COUNT);
|
||||||
let blockSize = 64;
|
let blockSize = $state(64);
|
||||||
let cornerBlocks = 2;
|
let cornerBlocks = $state(2);
|
||||||
|
|
||||||
let horizontalMargin = MARGIN_SIZE;
|
let horizontalMargin = $state(MARGIN_SIZE);
|
||||||
let verticalMargin = MARGIN_SIZE;
|
let verticalMargin = $state(MARGIN_SIZE);
|
||||||
let unloaded = true;
|
let unloaded = $state(true);
|
||||||
|
|
||||||
export let transparent = false;
|
interface Props {
|
||||||
export let subdued = false;
|
transparent?: boolean;
|
||||||
|
subdued?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { transparent = false, subdued = false }: Props = $props();
|
||||||
|
|
||||||
function updateCounts() {
|
function updateCounts() {
|
||||||
const gridWidth = window.innerWidth - MARGIN_SIZE;
|
const gridWidth = window.innerWidth - MARGIN_SIZE;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
let time = new Date();
|
let time = $state(new Date());
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
|
|
||||||
let screenResolution = '... x ...';
|
let screenResolution = $state('... x ...');
|
||||||
let windowResolution = '';
|
let windowResolution = $state('');
|
||||||
let dpr = "1";
|
let dpr = $state("1");
|
||||||
|
|
||||||
function updateResolution() {
|
function updateResolution() {
|
||||||
const realWidth = Math.round(screen.width) * window.devicePixelRatio;
|
const realWidth = Math.round(screen.width) * window.devicePixelRatio;
|
||||||
|
|
|
@ -8,25 +8,29 @@
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
const dispatch = createEventDispatcher<{ focus: void }>();
|
const dispatch = createEventDispatcher<{ focus: void }>();
|
||||||
|
|
||||||
export let bg = false;
|
interface Props {
|
||||||
|
bg?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
let sizes = {
|
let { bg = false }: Props = $props();
|
||||||
|
|
||||||
|
let sizes = $state({
|
||||||
blockSize: 64,
|
blockSize: 64,
|
||||||
horizontalCount: 16,
|
horizontalCount: 16,
|
||||||
verticalCount: 16,
|
verticalCount: 16,
|
||||||
horizontalMargin: 0,
|
horizontalMargin: 0,
|
||||||
verticalMargin: 0
|
verticalMargin: 0
|
||||||
};
|
});
|
||||||
|
|
||||||
$: columnWidth = sizes.horizontalCount % 2 === 0 ? 3 : 4;
|
let columnWidth = $derived(sizes.horizontalCount % 2 === 0 ? 3 : 4);
|
||||||
$: columnHeight = 2 * Math.floor((sizes.verticalCount * 0.75) / 2) + (sizes.verticalCount % 2);
|
let columnHeight = $derived(2 * Math.floor((sizes.verticalCount * 0.75) / 2) + (sizes.verticalCount % 2));
|
||||||
$: leftColumn = sizes.horizontalCount / 4 - columnWidth / 2;
|
let leftColumn = $derived(sizes.horizontalCount / 4 - columnWidth / 2);
|
||||||
$: circleBlocks =
|
let circleBlocks =
|
||||||
2 * Math.floor((Math.min(sizes.horizontalCount, sizes.verticalCount) * 0.66) / 2) +
|
$derived(2 * Math.floor((Math.min(sizes.horizontalCount, sizes.verticalCount) * 0.66) / 2) +
|
||||||
(sizes.horizontalCount % 2);
|
(sizes.horizontalCount % 2));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<div
|
<div
|
||||||
class="test-card"
|
class="test-card"
|
||||||
class:bg
|
class:bg
|
||||||
|
@ -37,7 +41,7 @@
|
||||||
--column-width: {columnWidth};
|
--column-width: {columnWidth};
|
||||||
--column-height: {columnHeight};
|
--column-height: {columnHeight};
|
||||||
--left-column: {leftColumn};"
|
--left-column: {leftColumn};"
|
||||||
on:dblclick={() => dispatch('focus') && document.body.requestFullscreen()}
|
ondblclick={() => dispatch('focus') && document.body.requestFullscreen()}
|
||||||
>
|
>
|
||||||
<BackgroundGrid on:change={(ev) => (sizes = ev.detail)} subdued={bg} />
|
<BackgroundGrid on:change={(ev) => (sizes = ev.detail)} subdued={bg} />
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
|
interface Props {
|
||||||
|
children?: import('svelte').Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { children }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a href=".." class="hide-idle"><i class="ti ti-arrow-back"></i> {$i18n.t('Back')}</a>
|
<a href=".." class="hide-idle"><i class="ti ti-arrow-back"></i> {$i18n.t('Back')}</a>
|
||||||
<slot />
|
{@render children?.()}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
a {
|
a {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import TestCard from '$lib/TestCard.svelte';
|
import TestCard from '$lib/TestCard.svelte';
|
||||||
|
|
||||||
let x = -1;
|
let x = $state(-1);
|
||||||
let y = -1;
|
let y = $state(-1);
|
||||||
let leftButton = false;
|
let leftButton = $state(false);
|
||||||
let rightButton = false;
|
let rightButton = $state(false);
|
||||||
|
|
||||||
function onMouseMove(ev: MouseEvent) {
|
function onMouseMove(ev: MouseEvent) {
|
||||||
x = ev.x;
|
x = ev.x;
|
||||||
|
@ -31,10 +31,10 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:body
|
<svelte:body
|
||||||
on:mousemove={onMouseMove}
|
onmousemove={onMouseMove}
|
||||||
on:mousedown={onMouseDown}
|
onmousedown={onMouseDown}
|
||||||
on:mouseup={onMouseUp}
|
onmouseup={onMouseUp}
|
||||||
on:contextmenu={(ev) => {
|
oncontextmenu={(ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import TestCard from '$lib/TestCard.svelte';
|
import TestCard from '$lib/TestCard.svelte';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/state';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
|
interface Props {
|
||||||
|
children?: import('svelte').Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { children }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<TestCard bg on:focus={() => goto('/card')} />
|
<TestCard bg on:focus={() => goto('/card')} />
|
||||||
<main class:sub={!$page.data.root}>
|
<main class:sub={!page.data.root}>
|
||||||
<a href=".." class="button button-back"><i class="ti ti-arrow-back" />{$i18n.t('Back')}</a>
|
<a href=".." class="button button-back"><i class="ti ti-arrow-back"></i>{$i18n.t('Back')}</a>
|
||||||
<slot />
|
{@render children?.()}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { run } from 'svelte/legacy';
|
||||||
|
|
||||||
import { version } from '../../../package.json';
|
import { version } from '../../../package.json';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
import type { Snapshot } from '@sveltejs/kit';
|
import type { Snapshot } from '@sveltejs/kit';
|
||||||
const buildDate = import.meta.env.VITE_BUILD_DATE || "???";
|
const buildDate = import.meta.env.VITE_BUILD_DATE || "???";
|
||||||
|
|
||||||
let search = '';
|
let search = $state('');
|
||||||
|
|
||||||
type Entry = {
|
type Entry = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -126,8 +128,8 @@
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
let filteredTests: Test[] = tests;
|
let filteredTests: Test[] = $state(tests);
|
||||||
let filteredCategories: Category[] = [];
|
let filteredCategories: Category[] = $state([]);
|
||||||
|
|
||||||
function doSearch(search: string) {
|
function doSearch(search: string) {
|
||||||
filteredTests = tests.filter((test) => {
|
filteredTests = tests.filter((test) => {
|
||||||
|
@ -141,7 +143,9 @@
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$: doSearch(search);
|
run(() => {
|
||||||
|
doSearch(search);
|
||||||
|
});
|
||||||
|
|
||||||
function setFilter(category: Category) {
|
function setFilter(category: Category) {
|
||||||
if (filteredCategories.includes(category)) {
|
if (filteredCategories.includes(category)) {
|
||||||
|
@ -151,13 +155,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: nonEmptyCategories = categories.filter((category) => {
|
let nonEmptyCategories = $derived(categories.filter((category) => {
|
||||||
const categoryTests = filteredTests.filter((test) => test.categories.includes(category.id));
|
const categoryTests = filteredTests.filter((test) => test.categories.includes(category.id));
|
||||||
return categoryTests.some(
|
return categoryTests.some(
|
||||||
(test) =>
|
(test) =>
|
||||||
!filteredCategories.length || filteredCategories.every((f) => test.categories.includes(f))
|
!filteredCategories.length || filteredCategories.every((f) => test.categories.includes(f))
|
||||||
);
|
);
|
||||||
});
|
}));
|
||||||
|
|
||||||
export const snapshot: Snapshot<string> = {
|
export const snapshot: Snapshot<string> = {
|
||||||
capture: () => JSON.stringify({ filtered: filteredCategories, search }),
|
capture: () => JSON.stringify({ filtered: filteredCategories, search }),
|
||||||
|
@ -172,13 +176,13 @@
|
||||||
<h1>Total Tech Test</h1>
|
<h1>Total Tech Test</h1>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<!-- svelte-ignore a11y-autofocus -->
|
<!-- svelte-ignore a11y_autofocus -->
|
||||||
<input type="search" placeholder={$i18n.t('Search')} bind:value={search} autofocus />
|
<input type="search" placeholder={$i18n.t('Search')} bind:value={search} autofocus />
|
||||||
|
|
||||||
<div class="options">
|
<div class="options">
|
||||||
{#each superCategories as category}
|
{#each superCategories as category}
|
||||||
<button
|
<button
|
||||||
on:click={() => setFilter(category.id)}
|
onclick={() => setFilter(category.id)}
|
||||||
class:active={!filteredCategories.length || filteredCategories.includes(category.id)}
|
class:active={!filteredCategories.length || filteredCategories.includes(category.id)}
|
||||||
class="super"
|
class="super"
|
||||||
>
|
>
|
||||||
|
@ -189,7 +193,7 @@
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
{#each categories as category}
|
{#each categories as category}
|
||||||
<button
|
<button
|
||||||
on:click={() => setFilter(category.id)}
|
onclick={() => setFilter(category.id)}
|
||||||
class:active={!filteredCategories.length || filteredCategories.includes(category.id)}
|
class:active={!filteredCategories.length || filteredCategories.includes(category.id)}
|
||||||
>
|
>
|
||||||
<i class="ti {category.icon}"></i>
|
<i class="ti {category.icon}"></i>
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import CycleButton from './cycle-button.svelte';
|
import CycleButton from './cycle-button.svelte';
|
||||||
|
interface Props {
|
||||||
|
children?: import('svelte').Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
let channelsEl: HTMLDivElement;
|
let { children }: Props = $props();
|
||||||
|
|
||||||
|
let channelsEl: HTMLDivElement = $state();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="channels" bind:this={channelsEl}>
|
<div class="channels" bind:this={channelsEl}>
|
||||||
<slot />
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<CycleButton element={channelsEl} />
|
<CycleButton element={channelsEl} />
|
||||||
|
|
|
@ -2,9 +2,13 @@
|
||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
|
|
||||||
export let element: HTMLElement;
|
interface Props {
|
||||||
|
element: HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
let cycling = false;
|
let { element }: Props = $props();
|
||||||
|
|
||||||
|
let cycling = $state(false);
|
||||||
let currentChannel: HTMLAudioElement | undefined;
|
let currentChannel: HTMLAudioElement | undefined;
|
||||||
async function cycleChannels() {
|
async function cycleChannels() {
|
||||||
cycling = true;
|
cycling = true;
|
||||||
|
@ -44,7 +48,7 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button on:click={onClick}>
|
<button onclick={onClick}>
|
||||||
<i class="ti ti-refresh"></i>
|
<i class="ti ti-refresh"></i>
|
||||||
{#if cycling}
|
{#if cycling}
|
||||||
{$i18n.t('Stop Cycling')}
|
{$i18n.t('Stop Cycling')}
|
||||||
|
|
|
@ -1,13 +1,26 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let src: string;
|
interface Props {
|
||||||
export let left = false;
|
src: string;
|
||||||
export let center = false;
|
left?: boolean;
|
||||||
export let right = false;
|
center?: boolean;
|
||||||
export let lfe = false;
|
right?: boolean;
|
||||||
export let inline = false;
|
lfe?: boolean;
|
||||||
|
inline?: boolean;
|
||||||
|
children?: import('svelte').Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
let currentTime = 0;
|
let {
|
||||||
let paused = true;
|
src,
|
||||||
|
left = false,
|
||||||
|
center = false,
|
||||||
|
right = false,
|
||||||
|
lfe = false,
|
||||||
|
inline = false,
|
||||||
|
children
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
|
let currentTime = $state(0);
|
||||||
|
let paused = $state(true);
|
||||||
function play() {
|
function play() {
|
||||||
currentTime = 0;
|
currentTime = 0;
|
||||||
paused = false;
|
paused = false;
|
||||||
|
@ -22,14 +35,14 @@
|
||||||
class:lfe
|
class:lfe
|
||||||
class:inline
|
class:inline
|
||||||
class:playing={!paused}
|
class:playing={!paused}
|
||||||
on:click={play}
|
onclick={play}
|
||||||
>
|
>
|
||||||
{#if !lfe}
|
{#if !lfe}
|
||||||
<i class="ti ti-volume"></i>
|
<i class="ti ti-volume"></i>
|
||||||
{:else}
|
{:else}
|
||||||
<i class="ti ti-wave-sine"></i>
|
<i class="ti ti-wave-sine"></i>
|
||||||
{/if}
|
{/if}
|
||||||
<span><slot /></span>
|
<span>{@render children?.()}</span>
|
||||||
<audio bind:currentTime bind:paused {src}></audio>
|
<audio bind:currentTime bind:paused {src}></audio>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import CycleButton from './cycle-button.svelte';
|
import CycleButton from './cycle-button.svelte';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
|
|
||||||
let speakersEl: HTMLElement;
|
let speakersEl: HTMLElement = $state();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="test">
|
<div class="test">
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
|
interface Props {
|
||||||
|
children?: import('svelte').Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { children }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h2><i class="ti ti-volume"></i> {$i18n.t('Audio test')}</h2>
|
<h2><i class="ti ti-volume"></i> {$i18n.t('Audio test')}</h2>
|
||||||
<slot />
|
{@render children?.()}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
|
|
||||||
let frequency = 60;
|
let frequency = $state(60);
|
||||||
let playing = false;
|
let playing = $state(false);
|
||||||
|
|
||||||
let audioCtx: AudioContext | undefined;
|
let audioCtx: AudioContext | undefined;
|
||||||
let oscillatorL: OscillatorNode | undefined;
|
let oscillatorL: OscillatorNode | undefined;
|
||||||
|
@ -58,9 +58,9 @@
|
||||||
<input type="number" bind:value={frequency} min="20" max="20000" disabled={playing} />Hz
|
<input type="number" bind:value={frequency} min="20" max="20000" disabled={playing} />Hz
|
||||||
</label>
|
</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button on:click={() => start('inPhase')}>{$i18n.t('In Phase')}</button>
|
<button onclick={() => start('inPhase')}>{$i18n.t('In Phase')}</button>
|
||||||
<button on:click={() => start('outOfPhase')}>{$i18n.t('Out of Phase')}</button>
|
<button onclick={() => start('outOfPhase')}>{$i18n.t('Out of Phase')}</button>
|
||||||
<button class="stop" on:click={stop} disabled={!playing}>{$i18n.t('Stop')}</button>
|
<button class="stop" onclick={stop} disabled={!playing}>{$i18n.t('Stop')}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import videoUrl from '@assets/avsync.webm';
|
import videoUrl from '@assets/avsync.webm';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
let paused = true;
|
let paused = $state(true);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h2><i class="ti ti-time-duration-off"></i> {$i18n.t('Audio/Video Synchronization')}</h2>
|
<h2><i class="ti ti-time-duration-off"></i> {$i18n.t('Audio/Video Synchronization')}</h2>
|
||||||
<!-- svelte-ignore a11y-media-has-caption -->
|
<!-- svelte-ignore a11y_media_has_caption -->
|
||||||
<video
|
<video
|
||||||
class:playing={!paused}
|
class:playing={!paused}
|
||||||
autoplay
|
autoplay
|
||||||
loop
|
loop
|
||||||
bind:paused
|
bind:paused
|
||||||
src={videoUrl}
|
src={videoUrl}
|
||||||
on:click={() => (paused = false)}
|
onclick={() => (paused = false)}
|
||||||
></video>
|
></video>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -1,25 +1,31 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { run } from 'svelte/legacy';
|
||||||
|
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
const dbg = debug('app:camera');
|
const dbg = debug('app:camera');
|
||||||
|
|
||||||
let video: HTMLVideoElement;
|
let video: HTMLVideoElement = $state();
|
||||||
let devices: MediaDeviceInfo[] = [];
|
let devices: MediaDeviceInfo[] = $state([]);
|
||||||
let currentDevice: string | undefined;
|
let currentDevice: string | undefined = $state();
|
||||||
|
|
||||||
let requestResolution: [number, number] | 'auto' = 'auto';
|
let requestResolution: [number, number] | 'auto' = $state('auto');
|
||||||
let requestFramerate: number | 'auto' = 'auto';
|
let requestFramerate: number | 'auto' = $state('auto');
|
||||||
let deviceInfo: {
|
let deviceInfo: {
|
||||||
resolution?: string;
|
resolution?: string;
|
||||||
frameRate?: number;
|
frameRate?: number;
|
||||||
} = {};
|
} = $state({});
|
||||||
let snapshot: string | undefined;
|
let snapshot: string | undefined = $state();
|
||||||
let flipped = false;
|
let flipped = $state(false);
|
||||||
|
|
||||||
$: dbg('devices %O', devices);
|
run(() => {
|
||||||
$: dbg('currentDevice %s', currentDevice);
|
dbg('devices %O', devices);
|
||||||
|
});
|
||||||
|
run(() => {
|
||||||
|
dbg('currentDevice %s', currentDevice);
|
||||||
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
refreshDevices();
|
refreshDevices();
|
||||||
|
@ -48,21 +54,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (currentDevice) {
|
run(() => {
|
||||||
navigator.mediaDevices
|
if (currentDevice) {
|
||||||
.getUserMedia({
|
navigator.mediaDevices
|
||||||
video: {
|
.getUserMedia({
|
||||||
deviceId: currentDevice,
|
video: {
|
||||||
width: requestResolution === 'auto' ? undefined : requestResolution[0],
|
deviceId: currentDevice,
|
||||||
height: requestResolution === 'auto' ? undefined : requestResolution[1],
|
width: requestResolution === 'auto' ? undefined : requestResolution[0],
|
||||||
frameRate: requestFramerate === 'auto' ? undefined : requestFramerate
|
height: requestResolution === 'auto' ? undefined : requestResolution[1],
|
||||||
}
|
frameRate: requestFramerate === 'auto' ? undefined : requestFramerate
|
||||||
})
|
}
|
||||||
.then((stream) => {
|
})
|
||||||
video.srcObject = stream;
|
.then((stream) => {
|
||||||
refreshDevices();
|
video.srcObject = stream;
|
||||||
});
|
refreshDevices();
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
async function takeSnapshot() {
|
async function takeSnapshot() {
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
|
@ -92,7 +100,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<button on:click={refreshDevices}>
|
<button onclick={refreshDevices}>
|
||||||
<i class="ti ti-refresh"></i>
|
<i class="ti ti-refresh"></i>
|
||||||
{$i18n.t('Refresh')}
|
{$i18n.t('Refresh')}
|
||||||
</button>
|
</button>
|
||||||
|
@ -124,13 +132,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="display" class:snapshot={Boolean(snapshot)}>
|
<div class="display" class:snapshot={Boolean(snapshot)}>
|
||||||
<!-- svelte-ignore a11y-media-has-caption -->
|
<!-- svelte-ignore a11y_media_has_caption -->
|
||||||
<video class:flipped bind:this={video} autoplay class:unloaded={!currentDevice}></video>
|
<video class:flipped bind:this={video} autoplay class:unloaded={!currentDevice}></video>
|
||||||
{#if snapshot}
|
{#if snapshot}
|
||||||
<!-- svelte-ignore a11y-missing-attribute -->
|
<!-- svelte-ignore a11y_missing_attribute -->
|
||||||
<!--suppress HtmlRequiredAltAttribute -->
|
<!--suppress HtmlRequiredAltAttribute -->
|
||||||
<img src={snapshot} />
|
<img src={snapshot} />
|
||||||
<button on:click={() => (snapshot = undefined)}><i class="ti ti-x"></i></button>
|
<button onclick={() => (snapshot = undefined)}><i class="ti ti-x"></i></button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -149,11 +157,11 @@
|
||||||
{/key}
|
{/key}
|
||||||
</ul>
|
</ul>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button on:click={takeSnapshot}>
|
<button onclick={takeSnapshot}>
|
||||||
<i class="ti ti-camera"></i>
|
<i class="ti ti-camera"></i>
|
||||||
{$i18n.t('Take picture')}
|
{$i18n.t('Take picture')}
|
||||||
</button>
|
</button>
|
||||||
<button on:click={() => (flipped = !flipped)}>
|
<button onclick={() => (flipped = !flipped)}>
|
||||||
<i class="ti ti-flip-vertical"></i>
|
<i class="ti ti-flip-vertical"></i>
|
||||||
{#if flipped}
|
{#if flipped}
|
||||||
{$i18n.t('Unflip image')}
|
{$i18n.t('Unflip image')}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { run } from 'svelte/legacy';
|
||||||
|
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
|
@ -6,10 +8,10 @@
|
||||||
|
|
||||||
const dbg = debug('app:camera');
|
const dbg = debug('app:camera');
|
||||||
|
|
||||||
let gamepads: Gamepad[] = [];
|
let gamepads: Gamepad[] = $state([]);
|
||||||
let currentGamepad: Gamepad | undefined;
|
let currentGamepad: Gamepad | undefined = $state();
|
||||||
let buttons: GamepadButton[] = [];
|
let buttons: GamepadButton[] = $state([]);
|
||||||
let axes: number[] = [];
|
let axes: number[] = $state([]);
|
||||||
|
|
||||||
const axisHistory: number[][] = [];
|
const axisHistory: number[][] = [];
|
||||||
const sizes: [number, number][] = [];
|
const sizes: [number, number][] = [];
|
||||||
|
@ -58,17 +60,23 @@
|
||||||
requestAnimationFrame(update);
|
requestAnimationFrame(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
run(() => {
|
||||||
if (currentGamepad) {
|
if (currentGamepad) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
$: dbg('Gamepads %O', gamepads);
|
run(() => {
|
||||||
$: dbg('Current gamepad %s', currentGamepad);
|
dbg('Gamepads %O', gamepads);
|
||||||
|
});
|
||||||
|
run(() => {
|
||||||
|
dbg('Current gamepad %s', currentGamepad);
|
||||||
|
});
|
||||||
|
|
||||||
$: currentGamepad?.vibrationActuator?.playEffect('dual-rumble', {
|
run(() => {
|
||||||
duration: 1000
|
currentGamepad?.vibrationActuator?.playEffect('dual-rumble', {
|
||||||
|
duration: 1000
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
@ -105,7 +113,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<button on:click={refreshGamepads}>
|
<button onclick={refreshGamepads}>
|
||||||
<i class="ti ti-refresh"></i>
|
<i class="ti ti-refresh"></i>
|
||||||
{$i18n.t('Refresh')}
|
{$i18n.t('Refresh')}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
|
|
||||||
let key: string;
|
let key: string = $state();
|
||||||
let code: string;
|
let code: string = $state();
|
||||||
let pressedKeys: string[] = [];
|
let pressedKeys: string[] = $state([]);
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
document.addEventListener('keydown', (event) => {
|
document.addEventListener('keydown', (event) => {
|
||||||
key = event.key;
|
key = event.key;
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { i18n } from '$lib/i18n';
|
import { i18n } from '$lib/i18n';
|
||||||
|
|
||||||
let time = 0;
|
let time = $state(0);
|
||||||
let fps = 0;
|
let fps = 0;
|
||||||
let start = 0;
|
let start = 0;
|
||||||
let displayFps = '?';
|
let displayFps = $state('?');
|
||||||
let fpsInterval: NodeJS.Timeout | undefined;
|
let fpsInterval: NodeJS.Timeout | undefined;
|
||||||
|
|
||||||
const times: number[] = [];
|
const times: number[] = [];
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
<div class="time">{time}</div>
|
<div class="time">{time}</div>
|
||||||
<div class="fps">{displayFps} {$i18n.t('FPS')}</div>
|
<div class="fps">{displayFps} {$i18n.t('FPS')}</div>
|
||||||
</div>
|
</div>
|
||||||
<button on:click={restart}>{$i18n.t('Restart')}</button>
|
<button onclick={restart}>{$i18n.t('Restart')}</button>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div,
|
div,
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
import '@tabler/icons-webfont/tabler-icons.css';
|
import '@tabler/icons-webfont/tabler-icons.css';
|
||||||
import '../index.css';
|
import '../index.css';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
interface Props {
|
||||||
|
children?: import('svelte').Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { children }: Props = $props();
|
||||||
|
|
||||||
let idleTimeout: NodeJS.Timeout | undefined;
|
let idleTimeout: NodeJS.Timeout | undefined;
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
@ -20,7 +25,7 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<slot />
|
{@render children?.()}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:global(.hide-idle) {
|
:global(.hide-idle) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue