Compare commits

..

No commits in common. "e6009c619c4061503ab30177194f5d731d2a0b01" and "f37019c3efce97d51c9f83fca20dc9a46c756784" have entirely different histories.

24 changed files with 131 additions and 3537 deletions

View file

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import '@fontsource/atkinson-hyperlegible'; import '@fontsource/b612';
import '@fontsource/atkinson-hyperlegible/700.css'; import '@fontsource/b612/700.css';
import 'normalize.css/normalize.css'; import 'normalize.css/normalize.css';
import { onMount, tick } from 'svelte'; import { onMount, tick } from 'svelte';
@ -80,8 +80,7 @@
justify-content: space-evenly; justify-content: space-evenly;
align-items: center; align-items: center;
font-family: 'Atkinson Hyperlegible', 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif; font-family: 'B612', 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif;
font-variant-numeric: tabular-nums;
} }
.circular { .circular {

View file

@ -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": "^4.0.0", "svelte-check": "^3.8.6",
"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": "^4.0.0", "@sveltejs/vite-plugin-svelte": "^3.1.2",
"@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": "^5.0.0", "svelte": "^4.2.19",
"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

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
let heightOdd = $state(false); let heightOdd = false;
let widthOdd = $state(false); let widthOdd = false;
function updateOdd() { function updateOdd() {
heightOdd = window.innerHeight % 2 === 1; heightOdd = window.innerHeight % 2 === 1;

View file

@ -7,21 +7,17 @@
const MAX_COUNT = 33; const MAX_COUNT = 33;
const MARGIN_SIZE = 16; const MARGIN_SIZE = 16;
let horizontalCount = $state(START_COUNT); let horizontalCount = START_COUNT;
let verticalCount = $state(START_COUNT); let verticalCount = START_COUNT;
let blockSize = $state(64); let blockSize = 64;
let cornerBlocks = $state(2); let cornerBlocks = 2;
let horizontalMargin = $state(MARGIN_SIZE); let horizontalMargin = MARGIN_SIZE;
let verticalMargin = $state(MARGIN_SIZE); let verticalMargin = MARGIN_SIZE;
let unloaded = $state(true); let unloaded = true;
interface Props { export let transparent = false;
transparent?: boolean; export let subdued = false;
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;

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
let time = $state(new Date()); let time = new Date();
onMount(() => { onMount(() => {
setInterval(() => { setInterval(() => {

View file

@ -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 = $state('... x ...'); let screenResolution = '... x ...';
let windowResolution = $state(''); let windowResolution = '';
let dpr = $state("1"); let dpr = "1";
function updateResolution() { function updateResolution() {
const realWidth = Math.round(screen.width) * window.devicePixelRatio; const realWidth = Math.round(screen.width) * window.devicePixelRatio;

View file

@ -8,32 +8,25 @@
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher<{ focus: void }>(); const dispatch = createEventDispatcher<{ focus: void }>();
interface Props { export let bg = false;
bg?: boolean;
}
let { bg = false }: Props = $props(); let sizes = {
let sizes = $state({
blockSize: 64, blockSize: 64,
horizontalCount: 16, horizontalCount: 16,
verticalCount: 16, verticalCount: 16,
horizontalMargin: 0, horizontalMargin: 0,
verticalMargin: 0 verticalMargin: 0
}); };
let columnWidth = $derived(sizes.horizontalCount % 2 === 0 ? 3 : 4); $: columnWidth = sizes.horizontalCount % 2 === 0 ? 3 : 4;
let columnHeight = $derived( $: columnHeight = 2 * Math.floor((sizes.verticalCount * 0.75) / 2) + (sizes.verticalCount % 2);
2 * Math.floor((sizes.verticalCount * 0.75) / 2) + (sizes.verticalCount % 2) $: leftColumn = sizes.horizontalCount / 4 - columnWidth / 2;
); $: circleBlocks =
let leftColumn = $derived(sizes.horizontalCount / 4 - columnWidth / 2);
let circleBlocks = $derived(
2 * Math.floor((Math.min(sizes.horizontalCount, sizes.verticalCount) * 0.66) / 2) + 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
@ -44,7 +37,7 @@
--column-width: {columnWidth}; --column-width: {columnWidth};
--column-height: {columnHeight}; --column-height: {columnHeight};
--left-column: {leftColumn};" --left-column: {leftColumn};"
ondblclick={() => dispatch('focus') && document.body.requestFullscreen()} on:dblclick={() => dispatch('focus') && document.body.requestFullscreen()}
> >
<BackgroundGrid on:change={(ev) => (sizes = ev.detail)} subdued={bg} /> <BackgroundGrid on:change={(ev) => (sizes = ev.detail)} subdued={bg} />
@ -88,8 +81,7 @@
height: 100vh; height: 100vh;
overflow: hidden; overflow: hidden;
font-family: 'Atkinson Hyperlegible', 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif; font-family: 'B612', 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif;
font-variant-numeric: tabular-nums;
font-size: min(4vw, 4vh); font-size: min(4vw, 4vh);
} }

View file

@ -1,14 +1,9 @@
<script lang="ts"> <script>
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>
{@render children?.()} <slot />
<style> <style>
a { a {

View file

@ -1,10 +1,10 @@
<script lang="ts"> <script lang="ts">
import TestCard from '$lib/TestCard.svelte'; import TestCard from '$lib/TestCard.svelte';
let x = $state(-1); let x = -1;
let y = $state(-1); let y = -1;
let leftButton = $state(false); let leftButton = false;
let rightButton = $state(false); let rightButton = 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
onmousemove={onMouseMove} on:mousemove={onMouseMove}
onmousedown={onMouseDown} on:mousedown={onMouseDown}
onmouseup={onMouseUp} on:mouseup={onMouseUp}
oncontextmenu={(ev) => { on:contextmenu={(ev) => {
ev.preventDefault(); ev.preventDefault();
return false; return false;
}} }}

View file

@ -1,19 +1,14 @@
<script lang="ts"> <script lang="ts">
import TestCard from '$lib/TestCard.svelte'; import TestCard from '$lib/TestCard.svelte';
import { page } from '$app/state'; import { page } from '$app/stores';
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"></i>{$i18n.t('Back')}</a> <a href=".." class="button button-back"><i class="ti ti-arrow-back" />{$i18n.t('Back')}</a>
{@render children?.()} <slot />
</main> </main>
<style> <style>

View file

@ -1,12 +1,10 @@
<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 = $state(''); let search = '';
type Entry = { type Entry = {
id: string; id: string;
@ -128,8 +126,8 @@
} }
]; ];
let filteredTests: Test[] = $state(tests); let filteredTests: Test[] = tests;
let filteredCategories: Category[] = $state([]); let filteredCategories: Category[] = [];
function doSearch(search: string) { function doSearch(search: string) {
filteredTests = tests.filter((test) => { filteredTests = tests.filter((test) => {
@ -143,9 +141,7 @@
); );
}); });
} }
run(() => { $: doSearch(search);
doSearch(search);
});
function setFilter(category: Category) { function setFilter(category: Category) {
if (filteredCategories.includes(category)) { if (filteredCategories.includes(category)) {
@ -155,13 +151,13 @@
} }
} }
let nonEmptyCategories = $derived(categories.filter((category) => { $: nonEmptyCategories = 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 }),
@ -176,13 +172,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
onclick={() => setFilter(category.id)} on:click={() => setFilter(category.id)}
class:active={!filteredCategories.length || filteredCategories.includes(category.id)} class:active={!filteredCategories.length || filteredCategories.includes(category.id)}
class="super" class="super"
> >
@ -193,7 +189,7 @@
<div class="separator"></div> <div class="separator"></div>
{#each categories as category} {#each categories as category}
<button <button
onclick={() => setFilter(category.id)} on:click={() => 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>

View file

@ -1,16 +1,11 @@
<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 { children }: Props = $props(); let channelsEl: HTMLDivElement;
let channelsEl: HTMLDivElement = $state();
</script> </script>
<div class="channels" bind:this={channelsEl}> <div class="channels" bind:this={channelsEl}>
{@render children?.()} <slot />
</div> </div>
<div class="controls"> <div class="controls">
<CycleButton element={channelsEl} /> <CycleButton element={channelsEl} />

View file

@ -2,13 +2,9 @@
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { i18n } from '$lib/i18n'; import { i18n } from '$lib/i18n';
interface Props { export let element: HTMLElement;
element: HTMLElement;
}
let { element }: Props = $props(); let cycling = false;
let cycling = $state(false);
let currentChannel: HTMLAudioElement | undefined; let currentChannel: HTMLAudioElement | undefined;
async function cycleChannels() { async function cycleChannels() {
cycling = true; cycling = true;
@ -48,7 +44,7 @@
}); });
</script> </script>
<button onclick={onClick}> <button on:click={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')}

View file

@ -1,26 +1,13 @@
<script lang="ts"> <script lang="ts">
interface Props { export let src: string;
src: string; export let left = false;
left?: boolean; export let center = false;
center?: boolean; export let right = false;
right?: boolean; export let lfe = false;
lfe?: boolean; export let inline = false;
inline?: boolean;
children?: import('svelte').Snippet;
}
let { let currentTime = 0;
src, let paused = true;
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;
@ -35,14 +22,14 @@
class:lfe class:lfe
class:inline class:inline
class:playing={!paused} class:playing={!paused}
onclick={play} on:click={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>{@render children?.()}</span> <span><slot /></span>
<audio bind:currentTime bind:paused {src}></audio> <audio bind:currentTime bind:paused {src}></audio>
</button> </button>

View file

@ -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 = $state(); let speakersEl: HTMLElement;
</script> </script>
<div class="test"> <div class="test">

View file

@ -1,11 +1,6 @@
<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>
{@render children?.()} <slot />

View file

@ -2,8 +2,8 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { i18n } from '$lib/i18n'; import { i18n } from '$lib/i18n';
let frequency = $state(60); let frequency = 60;
let playing = $state(false); let playing = 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 onclick={() => start('inPhase')}>{$i18n.t('In Phase')}</button> <button on:click={() => start('inPhase')}>{$i18n.t('In Phase')}</button>
<button onclick={() => start('outOfPhase')}>{$i18n.t('Out of Phase')}</button> <button on:click={() => start('outOfPhase')}>{$i18n.t('Out of Phase')}</button>
<button class="stop" onclick={stop} disabled={!playing}>{$i18n.t('Stop')}</button> <button class="stop" on:click={stop} disabled={!playing}>{$i18n.t('Stop')}</button>
</div> </div>
</div> </div>

View file

@ -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 = $state(true); let paused = 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}
onclick={() => (paused = false)} on:click={() => (paused = false)}
></video> ></video>
<style> <style>

View file

@ -1,31 +1,25 @@
<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 = $state(); let video: HTMLVideoElement;
let devices: MediaDeviceInfo[] = $state([]); let devices: MediaDeviceInfo[] = [];
let currentDevice: string | undefined = $state(); let currentDevice: string | undefined;
let requestResolution: [number, number] | 'auto' = $state('auto'); let requestResolution: [number, number] | 'auto' = 'auto';
let requestFramerate: number | 'auto' = $state('auto'); let requestFramerate: number | 'auto' = 'auto';
let deviceInfo: { let deviceInfo: {
resolution?: string; resolution?: string;
frameRate?: number; frameRate?: number;
} = $state({}); } = {};
let snapshot: string | undefined = $state(); let snapshot: string | undefined;
let flipped = $state(false); let flipped = false;
run(() => { $: dbg('devices %O', devices);
dbg('devices %O', devices); $: dbg('currentDevice %s', currentDevice);
});
run(() => {
dbg('currentDevice %s', currentDevice);
});
onMount(() => { onMount(() => {
refreshDevices(); refreshDevices();
@ -54,8 +48,7 @@
} }
} }
run(() => { $: if (currentDevice) {
if (currentDevice) {
navigator.mediaDevices navigator.mediaDevices
.getUserMedia({ .getUserMedia({
video: { video: {
@ -70,7 +63,6 @@
refreshDevices(); refreshDevices();
}); });
} }
});
async function takeSnapshot() { async function takeSnapshot() {
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
@ -100,7 +92,7 @@
{/each} {/each}
</select> </select>
</label> </label>
<button onclick={refreshDevices}> <button on:click={refreshDevices}>
<i class="ti ti-refresh"></i> <i class="ti ti-refresh"></i>
{$i18n.t('Refresh')} {$i18n.t('Refresh')}
</button> </button>
@ -132,13 +124,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 onclick={() => (snapshot = undefined)}><i class="ti ti-x"></i></button> <button on:click={() => (snapshot = undefined)}><i class="ti ti-x"></i></button>
{/if} {/if}
</div> </div>
@ -157,11 +149,11 @@
{/key} {/key}
</ul> </ul>
<div class="controls"> <div class="controls">
<button onclick={takeSnapshot}> <button on:click={takeSnapshot}>
<i class="ti ti-camera"></i> <i class="ti ti-camera"></i>
{$i18n.t('Take picture')} {$i18n.t('Take picture')}
</button> </button>
<button onclick={() => (flipped = !flipped)}> <button on:click={() => (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')}

View file

@ -1,6 +1,4 @@
<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';
@ -8,10 +6,10 @@
const dbg = debug('app:camera'); const dbg = debug('app:camera');
let gamepads: Gamepad[] = $state([]); let gamepads: Gamepad[] = [];
let currentGamepad: Gamepad | undefined = $state(); let currentGamepad: Gamepad | undefined;
let buttons: GamepadButton[] = $state([]); let buttons: GamepadButton[] = [];
let axes: number[] = $state([]); let axes: number[] = [];
const axisHistory: number[][] = []; const axisHistory: number[][] = [];
const sizes: [number, number][] = []; const sizes: [number, number][] = [];
@ -60,24 +58,18 @@
requestAnimationFrame(update); requestAnimationFrame(update);
} }
run(() => { $: {
if (currentGamepad) { if (currentGamepad) {
update(); update();
} }
}); }
run(() => { $: dbg('Gamepads %O', gamepads);
dbg('Gamepads %O', gamepads); $: dbg('Current gamepad %s', currentGamepad);
});
run(() => {
dbg('Current gamepad %s', currentGamepad);
});
run(() => { $: currentGamepad?.vibrationActuator?.playEffect('dual-rumble', {
currentGamepad?.vibrationActuator?.playEffect('dual-rumble', {
duration: 1000 duration: 1000
}); });
});
onMount(() => { onMount(() => {
refreshGamepads(); refreshGamepads();
@ -113,7 +105,7 @@
{/each} {/each}
</select> </select>
</label> </label>
<button onclick={refreshGamepads}> <button on:click={refreshGamepads}>
<i class="ti ti-refresh"></i> <i class="ti ti-refresh"></i>
{$i18n.t('Refresh')} {$i18n.t('Refresh')}
</button> </button>

View file

@ -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 = $state(); let key: string;
let code: string = $state(); let code: string;
let pressedKeys: string[] = $state([]); let pressedKeys: string[] = [];
onMount(() => { onMount(() => {
document.addEventListener('keydown', (event) => { document.addEventListener('keydown', (event) => {
key = event.key; key = event.key;

View file

@ -2,10 +2,10 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { i18n } from '$lib/i18n'; import { i18n } from '$lib/i18n';
let time = $state(0); let time = 0;
let fps = 0; let fps = 0;
let start = 0; let start = 0;
let displayFps = $state('?'); let displayFps = '?';
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 onclick={restart}>{$i18n.t('Restart')}</button> <button on:click={restart}>{$i18n.t('Restart')}</button>
<style> <style>
div, div,

View file

@ -2,14 +2,11 @@
import 'normalize.css/normalize.css'; import 'normalize.css/normalize.css';
import '@fontsource/atkinson-hyperlegible'; import '@fontsource/atkinson-hyperlegible';
import '@fontsource/atkinson-hyperlegible/700.css'; import '@fontsource/atkinson-hyperlegible/700.css';
import '@fontsource/b612';
import '@fontsource/b612/700.css';
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(() => {
@ -23,7 +20,7 @@
}); });
</script> </script>
{@render children?.()} <slot />
<style> <style>
:global(.hide-idle) { :global(.hide-idle) {