fix: keybinds, improve animation perf

feat: subset fonts
feat: extensions languages & sub/dub
This commit is contained in:
ThaUnknown 2025-04-22 16:27:29 +02:00
parent 70a4995bf8
commit b8683300b4
No known key found for this signature in database
19 changed files with 584 additions and 73 deletions

5
assets/logo.svg Normal file
View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="430" height="430" viewBox="0 0 113.50625 113.50625" fill="currentColor">
<path d="M89.693749 72.830671v-19.84375l-18.520833-10.31875-14.552084-8.202083-33.072916 18.520833v19.84375l33.072916-18.520833ZM23.547916 47.695254l23.547916-13.229167L23.547916 21.23692Z" />
<path d="m23.547916 77.990044 33.072916 18.52084 33.072917-18.52084-18.520833-10.318746-14.552084 8.202083-14.552083-8.202083S23.547916 78.122337 23.547916 77.990044z" />
<path d="m56.620832 59.601503 10.054167 5.55625v.000001l-10.054167 5.55625-10.054166-5.55625v-.000001z"/>
</svg>

After

Width:  |  Height:  |  Size: 617 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/twemoji.woff2 Normal file

Binary file not shown.

View file

@ -143,7 +143,7 @@ importers:
version: 0.0.18(svelte@4.2.19)
eslint-config-standard-universal:
specifier: github:thaunknown/eslint-config-standard-universal
version: https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/88b9755b42d0fc7b7280ed2c9061954ed06ee7fb(@typescript-eslint/parser@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2))(jiti@1.21.6)
version: https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/a9994824ad3b4528533007eec7f31ed773a89bc0(@typescript-eslint/parser@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2))(jiti@1.21.6)
globals:
specifier: ^15.11.0
version: 15.11.0
@ -152,7 +152,7 @@ importers:
version: 1.8.10(@gql.tada/svelte-support@1.0.1(svelte@4.2.19)(typescript@5.7.2))(graphql@16.10.0)(typescript@5.7.2)
hayase-extensions:
specifier: github:hayase-app/extensions
version: https://codeload.github.com/hayase-app/extensions/tar.gz/6cb67c6061b0fcd41b5f1f683e6c030129e79eae
version: https://codeload.github.com/hayase-app/extensions/tar.gz/d3bb37d74626580a06aff13d6f6f7a912c7ea4ff
jassub:
specifier: ^1.7.18
version: 1.7.18
@ -1206,8 +1206,8 @@ packages:
peerDependencies:
eslint: '>=6.0.0'
eslint-config-standard-universal@https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/88b9755b42d0fc7b7280ed2c9061954ed06ee7fb:
resolution: {tarball: https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/88b9755b42d0fc7b7280ed2c9061954ed06ee7fb}
eslint-config-standard-universal@https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/a9994824ad3b4528533007eec7f31ed773a89bc0:
resolution: {tarball: https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/a9994824ad3b4528533007eec7f31ed773a89bc0}
version: 1.0.4
eslint-import-resolver-node@0.3.9:
@ -1504,9 +1504,9 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
hayase-extensions@https://codeload.github.com/hayase-app/extensions/tar.gz/6cb67c6061b0fcd41b5f1f683e6c030129e79eae:
resolution: {tarball: https://codeload.github.com/hayase-app/extensions/tar.gz/6cb67c6061b0fcd41b5f1f683e6c030129e79eae}
version: 1.0.5
hayase-extensions@https://codeload.github.com/hayase-app/extensions/tar.gz/d3bb37d74626580a06aff13d6f6f7a912c7ea4ff:
resolution: {tarball: https://codeload.github.com/hayase-app/extensions/tar.gz/d3bb37d74626580a06aff13d6f6f7a912c7ea4ff}
version: 1.0.6
idb-keyval@6.2.1:
resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==}
@ -2273,8 +2273,8 @@ packages:
resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==}
engines: {node: '>=16'}
svelte@5.26.3:
resolution: {integrity: sha512-SgDfx70CmW8Rev9XRu5sN/3Paa/mSh3DGxDDEgtecdjbhLrmgK18McanP+gNE8HYGm8tXiXZN3Fn31NcqBML+Q==}
svelte@5.28.1:
resolution: {integrity: sha512-iOa9WmfNG95lSOSJdMhdjJ4Afok7IRAQYXpbnxhd5EINnXseG0GVa9j6WPght4eX78XfFez45Fi+uRglGKPV/Q==}
engines: {node: '>=18'}
tabbable@6.2.0:
@ -3644,7 +3644,7 @@ snapshots:
eslint: 9.17.0(jiti@1.21.6)
semver: 7.7.1
eslint-config-standard-universal@https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/88b9755b42d0fc7b7280ed2c9061954ed06ee7fb(@typescript-eslint/parser@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2))(jiti@1.21.6):
eslint-config-standard-universal@https://codeload.github.com/thaunknown/eslint-config-standard-universal/tar.gz/a9994824ad3b4528533007eec7f31ed773a89bc0(@typescript-eslint/parser@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2))(jiti@1.21.6):
dependencies:
'@stylistic/eslint-plugin': 4.2.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
eslint: 9.17.0(jiti@1.21.6)
@ -3652,9 +3652,9 @@ snapshots:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.6))
eslint-plugin-n: 17.15.0(eslint@9.17.0(jiti@1.21.6))
eslint-plugin-promise: 7.2.1(eslint@9.17.0(jiti@1.21.6))
eslint-plugin-svelte: 3.1.0(eslint@9.17.0(jiti@1.21.6))(svelte@5.26.3)
eslint-plugin-svelte: 3.1.0(eslint@9.17.0(jiti@1.21.6))(svelte@5.28.1)
globals: 16.0.0
svelte: 5.26.3
svelte: 5.28.1
typescript: 5.7.2
typescript-eslint: 8.18.0(eslint@9.17.0(jiti@1.21.6))(typescript@5.7.2)
transitivePeerDependencies:
@ -3736,7 +3736,7 @@ snapshots:
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.6))
eslint: 9.17.0(jiti@1.21.6)
eslint-plugin-svelte@3.1.0(eslint@9.17.0(jiti@1.21.6))(svelte@5.26.3):
eslint-plugin-svelte@3.1.0(eslint@9.17.0(jiti@1.21.6))(svelte@5.28.1):
dependencies:
'@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@1.21.6))
'@jridgewell/sourcemap-codec': 1.5.0
@ -3748,9 +3748,9 @@ snapshots:
postcss-load-config: 3.1.4(postcss@8.4.49)
postcss-safe-parser: 7.0.1(postcss@8.4.49)
semver: 7.7.1
svelte-eslint-parser: 1.0.1(svelte@5.26.3)
svelte-eslint-parser: 1.0.1(svelte@5.28.1)
optionalDependencies:
svelte: 5.26.3
svelte: 5.28.1
transitivePeerDependencies:
- ts-node
@ -4007,7 +4007,7 @@ snapshots:
dependencies:
function-bind: 1.1.2
hayase-extensions@https://codeload.github.com/hayase-app/extensions/tar.gz/6cb67c6061b0fcd41b5f1f683e6c030129e79eae: {}
hayase-extensions@https://codeload.github.com/hayase-app/extensions/tar.gz/d3bb37d74626580a06aff13d6f6f7a912c7ea4ff: {}
idb-keyval@6.2.1: {}
@ -4735,7 +4735,7 @@ snapshots:
transitivePeerDependencies:
- picomatch
svelte-eslint-parser@1.0.1(svelte@5.26.3):
svelte-eslint-parser@1.0.1(svelte@5.28.1):
dependencies:
eslint-scope: 8.2.0
eslint-visitor-keys: 4.2.0
@ -4744,7 +4744,7 @@ snapshots:
postcss-scss: 4.0.9(postcss@8.4.49)
postcss-selector-parser: 7.1.0
optionalDependencies:
svelte: 5.26.3
svelte: 5.28.1
svelte-hmr@0.16.0(svelte@4.2.19):
dependencies:
@ -4788,7 +4788,7 @@ snapshots:
magic-string: 0.30.12
periscopic: 3.1.0
svelte@5.26.3:
svelte@5.28.1:
dependencies:
'@ampproject/remapping': 2.3.0
'@jridgewell/sourcemap-codec': 1.5.0

View file

@ -84,7 +84,12 @@
@font-face {
font-family: 'molotregular';
src: url('/Molot-webfont.woff') format('woff');
src: url('/Molot-webfont-subset.woff') format('woff');
}
@font-face {
font-family: "Twemoji";
src: url("/twemoji-subset.woff2") format("woff2");
}
@ -226,15 +231,6 @@ body {
transform: perspective(100vw) translate3d(0, 0, 0vw) rotateY(0deg) rotateX(0deg);
}
@keyframes spin3d {
from {
transform: rotateY(0deg) translate3d(0, 0, -5000px);
}
to {
transform: rotateY(360deg) translate3d(0, 0, -5000px);
}
}
@keyframes idle-fly {
@ -285,25 +281,10 @@ body {
}
}
@keyframes marquee-bg {
from {
/* this is the exact size of the backplate single div, this animation isn't very performant! */
background-position: -2111.74px 0;
}
to {
background-position: 0 0;
}
}
.animate-marquee {
animation: marquee 80s infinite linear;
}
.animate-marquee-bg {
animation: marquee-bg 80s infinite linear;
}
.bg-striped {
background: repeating-linear-gradient(45deg,
#202020,
@ -328,6 +309,10 @@ body {
font-family: 'molotregular';
}
.font-twemoji {
font-family: 'Twemoji';
}
.backface-visible {
backface-visibility: visible;
}

4
src/app.d.ts vendored
View file

@ -101,21 +101,25 @@ export interface Native {
}
declare global {
// eslint-disable-next-line no-unused-vars
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// eslint-disable-next-line no-unused-vars
interface PageState {
search?: VariablesOf<typeof Search>
}
// interface Platform {}
}
// eslint-disable-next-line no-unused-vars
interface HTMLMediaElement {
videoTracks?: Track[]
audioTracks?: Track[]
}
// eslint-disable-next-line no-unused-vars
interface ScreenOrientation {
lock: (orientation: 'any' | 'natural' | 'landscape' | 'portrait' | 'portrait-primary' | 'portrait-secondary' | 'landscape-primary' | 'landscape-secondary') => Promise<void>
}

View file

@ -11,6 +11,7 @@
let isAnimating = false
let isSpinning = false
let isFlying = false
let showBackplate = false
let timeout: number
// WE LOVE RACE CONDITIONS WOOOO YEAAH MY SANITY
@ -21,6 +22,7 @@
isFlying = true
await sleep(800)
if (!isFlying) return
showBackplate = true
isSpinning = true
isFlying = false
}
@ -32,15 +34,15 @@
isSpinning = isFlying = false
await sleep(10)
root.style.transform = plate.style.transform = ''
await sleep(790)
isAnimating = isSpinning = isFlying = false
await sleep(490)
isAnimating = showBackplate = isSpinning = isFlying = false
}
$: active = $lockedState === 'locked' || visibilityState === 'hidden' || ($idleState === 'active' && $activityState === 'active') || $isPlaying
function checkIdleState (active: boolean, idleAnimation: 'fancy' | 'fast' | 'off') {
function checkIdleState (active: boolean, idleAnimation: boolean) {
clearTimeout(timeout)
if (idleAnimation === 'off' || active) return reset()
if (!idleAnimation || active) return reset()
timeout = setTimeout(start, 120_000)
}
@ -52,22 +54,26 @@
<div class='preserve-3d absolute w-full h-full overflow-hidden flip backface-hidden backplate bg-black flex-col justify-center pointer-events-none hidden'
bind:this={plate}
class:!flex={isAnimating}
class:!flex={showBackplate}
class:backplate-fly={isFlying}
class:backplate-spin={isSpinning}>
{#each Array.from({ length: 5 }) as _, i (i)}
<div class='flex flex-row w-full font-molot font-bold -rotate-12' style:padding-left='{(4 - i) * 600 - 1000}px'>
{#each Array.from({ length: 3 }) as _, i (i)}
<div class='animate-marquee mt-32 leading-[0.8]'>
<div class='text-[24rem] bg-striped !bg-clip-text text-transparent tracking-wide' class:animate-marquee-bg={$settings.idleAnimation === 'fancy'}>
HAYASE.06&nbsp;
</div>
<div class='flex pl-1'>
<div class='bg-striped-muted rounded py-2 px-3 mt-1 mb-[2.5px] mr-2 ml-1 text-black flex items-center leading-[0.9]' class:animate-marquee-bg={$settings.idleAnimation === 'fancy'}>
TORRENTING<br />MADE<br />SIMPLE
{#each Array.from({ length: 6 }) as _, i (i)}
<div class='flex flex-row w-full font-molot font-bold leading-[0.8] ml-[--ml-offset] -rotate-12 text-white mt-64' style:--ml-offset='calc((-1 * {(i) * 600}px) - 10vw)'>
{#each Array.from({ length: 4 }) as _, i (i)}
<div>
<div class='bg-striped'>
<div class='text-[24rem] tracking-wide animate-marquee bg-black mix-blend-multiply'>
HAYASE.06&nbsp;
</div>
<div class='text-[5.44rem] bg-striped-muted !bg-clip-text text-transparent tracking-wider' class:animate-marquee-bg={$settings.idleAnimation === 'fancy'}>
MAGNET://SIMPLICITY TOPS EVERYTHING
</div>
<div class='bg-striped-muted'>
<div class='flex pl-1 animate-marquee bg-black mix-blend-multiply'>
<div class='rounded py-2 px-3 mt-1 mb-[2.5px] mr-2 ml-1 text-black bg-white flex items-center leading-[0.9]'>
TORRENTING<br />MADE<br />SIMPLE
</div>
<div class='text-[5.44rem] bg-striped-muted tracking-wider'>
MAGNET://SIMPLICITY TOPS EVERYTHING
</div>
</div>
</div>
</div>

View file

@ -11,6 +11,7 @@
import { Input } from '$lib/components/ui/input'
import { saved, storage } from '$lib/modules/extensions'
import * as Tooltip from '$lib/components/ui/tooltip'
import { codeToEmoji } from '$lib/utils'
const typeMap = {
nzb: 'NZB',
@ -87,6 +88,16 @@
{config.ratio} Ratio
</div>
{/if}
<div class='rounded px-3 py-0.5 font-bold bg-neutral-900 leading-snug capitalize'>
{config.media}
</div>
{#if config.languages}
<div class='font-twemoji text-xl leading-none content-center line-clamp-1'>
{#each config.languages as lang (lang)}
{codeToEmoji(lang)}
{/each}
</div>
{/if}
</div>
</div>
<ExtensionSettings {config} />

View file

@ -0,0 +1,189 @@
<script context='module' lang='ts'>
import { persisted } from 'svelte-persisted-store'
import { writable } from 'simple-store-svelte'
import { get } from 'svelte/store'
import { keys, layout, type KeyCode } from './maps.ts'
type Bind <T extends Record<string, unknown> = Record<string, unknown>> = T & {
fn: () => void
id: string
code?: KeyCode
}
export const binds = persisted<Partial<Record<KeyCode, Bind>>>('thaunknown/svelte-keybinds', {})
const noop = async (_: KeyCode) => true
let cnd = noop
export const condition = writable(noop)
condition.subscribe((fn) => {
if (typeof fn === 'function') cnd = fn
})
document.addEventListener('keydown', e => runBind(e, e.code as KeyCode))
async function runBind (e: MouseEvent | KeyboardEvent, code: KeyCode) {
const kbn = get(binds)
if (await cnd(code)) kbn[layout[code] ?? code]?.fn()
}
export function loadWithDefaults (defaults: Partial<Record<string, Bind>>) {
const def = toIDmap(defaults)
const saved = toIDmap(get(binds))
for (const id in saved) {
if (def[id]) {
saved[id] = { ...saved[id], ...def[id], code: saved[id]!.code }
} else {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete saved[id]
}
}
binds.set(toKeyMap({ ...def, ...saved }))
}
function toIDmap (target: Partial<Record<KeyCode, Bind>> = {}) {
const obj: Partial<Record<string, Bind>> = {}
for (const c in target) {
const code = c as KeyCode
const bind = target[code]
if (!bind) continue
obj[bind.id] = { ...bind, code }
}
return obj
}
function toKeyMap (target: Partial<Record<string, Bind>> = {}) {
const obj: Partial<Record<KeyCode, Bind>> = {}
for (const id in target) {
const bind = target[id]
if (!bind) continue
obj[bind.code!] = { ...bind, id }
}
return obj
}
</script>
<script lang='ts'>
export let clickable = false
let dragged: HTMLDivElement | null = null
function draggable (node: HTMLDivElement, code: KeyCode) {
let drag = false
node.addEventListener('dragstart', ({ target: _target }) => {
const target = _target as HTMLDivElement
dragged = target
target.classList.add('transparent')
drag = true
})
node.addEventListener('dragend', ({ target: _target }) => {
const target = _target as HTMLDivElement
target.classList.remove('transparent')
drag = false
})
node.addEventListener('dragover', (e) => {
const target = e.target as HTMLDivElement
e.dataTransfer!.dropEffect = 'move'
e.preventDefault()
if (!drag) target.classList.add('transparent')
})
node.addEventListener('dragleave', ({ target: _target }) => {
const target = _target as HTMLDivElement
if (!drag) target.classList.remove('transparent')
})
node.addEventListener('drop', ({ target: _target }) => {
const target = _target as HTMLDivElement
target.style.opacity = ''
const targetcode = dragged!.dataset.code as KeyCode
if ($binds[code]) {
const temp = $binds[targetcode]
$binds[targetcode] = $binds[code]
$binds[code] = temp
} else {
$binds[code] = $binds[targetcode]
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete $binds[targetcode]
}
})
}
</script>
<div class='svelte-keybinds'>
{#each Object.values(keys) as key (key.name)}
{@const { size, dark, name } = key}
<div
class:dark
draggable={!!$binds[name]}
data-code={name}
class='w-{size ?? 50}'
{...$$restProps}
use:draggable={name}
on:click={(e) => clickable && runBind(e, name)}>
<slot prop={$binds[name]} />
</div>
{/each}
</div>
<style>
.svelte-keybinds {
flex-shrink: 0;
user-select: none;
display: flex;
flex-wrap: wrap;
width: 82em;
background: rgba(0, 0, 0, 0.8);
border-radius: 0.4em;
padding: 1em;
color: #eee;
}
.svelte-keybinds .dark {
background: #191c20 !important;
}
.svelte-keybinds .transparent {
opacity: 0.2 !important;
}
.svelte-keybinds > div {
height: 4em;
margin: 0.5em;
background: #25282c;
border-radius: 0.4em;
cursor: pointer;
transition-property: opacity, transform;
transition-duration: 0.2s;
transition-timing-function: ease;
}
.svelte-keybinds > div > :global(*) {
pointer-events: none !important;
}
.svelte-keybinds > div:hover {
transform: scale(0.9);
}
.svelte-keybinds .w-50 {
width: 4em !important;
}
.svelte-keybinds .w-75 {
width: 6.5em !important;
}
.svelte-keybinds .w-85 {
width: 7.5em !important;
}
.svelte-keybinds .w-90 {
width: 8em !important;
}
.svelte-keybinds .w-100 {
width: 9em !important;
}
.svelte-keybinds .w-110 {
width: 10em !important;
}
.svelte-keybinds .w-115 {
width: 10.5em !important;
}
.svelte-keybinds .w-300 {
width: 29em !important;
}
</style>

View file

@ -0,0 +1,313 @@
export type KeyCode = 'Again' | 'AltLeft' | 'AltRight' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'AudioVolumeDown' | 'AudioVolumeMute' | 'AudioVolumeUp' | 'Backquote' | 'Backslash' | 'Backspace' | 'BracketLeft' | 'BracketRight' | 'BrowserBack' | 'BrowserFavorites' | 'BrowserForward' | 'BrowserHome' | 'BrowserRefresh' | 'BrowserSearch' | 'BrowserStop' | 'CapsLock' | 'Comma' | 'ContextMenu' | 'ControlLeft' | 'ControlRight' | 'Convert' | 'Copy' | 'Cut' | 'Delete' | 'Digit0' | 'Digit1' | 'Digit2' | 'Digit3' | 'Digit4' | 'Digit5' | 'Digit6' | 'Digit7' | 'Digit8' | 'Digit9' | 'Eject' | 'End' | 'Enter' | 'Equal' | 'F1' | 'F10' | 'F11' | 'F12' | 'F13' | 'F14' | 'F15' | 'F16' | 'F17' | 'F18' | 'F19' | 'F2' | 'F20' | 'F21' | 'F22' | 'F23' | 'F24' | 'F3' | 'F4' | 'F5' | 'F6' | 'F7' | 'F8' | 'F9' | 'Find' | 'Help' | 'Home' | 'Insert' | 'IntlBackslash' | 'IntlRo' | 'IntlYen' | 'KanaMode' | 'KeyA' | 'KeyB' | 'KeyC' | 'KeyD' | 'KeyE' | 'KeyF' | 'KeyG' | 'KeyH' | 'KeyI' | 'KeyJ' | 'KeyK' | 'KeyL' | 'KeyM' | 'KeyN' | 'KeyO' | 'KeyP' | 'KeyQ' | 'KeyR' | 'KeyS' | 'KeyT' | 'KeyU' | 'KeyV' | 'KeyW' | 'KeyX' | 'KeyY' | 'KeyZ' | 'Lang1' | 'Lang2' | 'Lang3' | 'Lang4' | 'Lang5' | 'LaunchApp1' | 'LaunchApp2' | 'LaunchMail' | 'MediaPlayPause' | 'MediaSelect' | 'MediaStop' | 'MediaTrackNext' | 'MediaTrackPrevious' | 'MetaLeft' | 'MetaRight' | 'Minus' | 'NonConvert' | 'NumLock' | 'Numpad0' | 'Numpad1' | 'Numpad2' | 'Numpad3' | 'Numpad4' | 'Numpad5' | 'Numpad6' | 'Numpad7' | 'Numpad8' | 'Numpad9' | 'NumpadAdd' | 'NumpadComma' | 'NumpadDecimal' | 'NumpadDivide' | 'NumpadEnter' | 'NumpadEqual' | 'NumpadMultiply' | 'NumpadParenLeft' | 'NumpadParenRight' | 'NumpadSubtract' | 'Open' | 'PageDown' | 'PageUp' | 'Paste' | 'Pause' | 'Period' | 'Power' | 'PrintScreen' | 'Quote' | 'ScrollLock' | 'Select' | 'Semicolon' | 'ShiftLeft' | 'ShiftRight' | 'Slash' | 'Sleep' | 'Space' | 'Tab' | 'Undo' | 'WakeUp' | 'Escape'
declare class KeyboardLayoutMap extends Map<KeyCode, string> { }
interface Keyboard {
getLayoutMap: () => Promise<KeyboardLayoutMap>
onlayoutchange: ((this: Keyboard, ev: Event) => void) | null
}
declare global {
// eslint-disable-next-line no-unused-vars
interface Navigator {
keyboard?: Keyboard
}
}
// Risky as objects dont guarantee order
export const keys: Partial<Record<KeyCode, {dark?: boolean, name: KeyCode, size?: number}>> = {
Escape: {
dark: true,
name: 'Escape'
},
Digit1: {
name: 'Digit1'
},
Digit2: {
name: 'Digit2'
},
Digit3: {
name: 'Digit3'
},
Digit4: {
name: 'Digit4'
},
Digit5: {
name: 'Digit5'
},
Digit6: {
name: 'Digit6'
},
Digit7: {
name: 'Digit7'
},
Digit8: {
name: 'Digit8'
},
Digit9: {
name: 'Digit9'
},
Digit0: {
name: 'Digit0'
},
Minus: {
name: 'Minus'
},
Equal: {
name: 'Equal'
},
Backspace: {
dark: true,
size: 100,
name: 'Backspace'
},
Delete: {
dark: true,
name: 'Delete'
},
Tab: {
dark: true,
size: 75,
name: 'Tab'
},
KeyQ: {
name: 'KeyQ'
},
KeyW: {
name: 'KeyW'
},
KeyE: {
name: 'KeyE'
},
KeyR: {
name: 'KeyR'
},
KeyT: {
name: 'KeyT'
},
KeyY: {
name: 'KeyY'
},
KeyU: {
name: 'KeyU'
},
KeyI: {
name: 'KeyI'
},
KeyO: {
name: 'KeyO'
},
KeyP: {
name: 'KeyP'
},
BracketLeft: {
name: 'BracketLeft'
},
BracketRight: {
name: 'BracketRight'
},
Backslash: {
dark: true,
size: 75,
name: 'Backslash'
},
Home: {
dark: true,
name: 'Home'
},
CapsLock: {
dark: true,
size: 90,
name: 'CapsLock'
},
KeyA: {
name: 'KeyA'
},
KeyS: {
name: 'KeyS'
},
KeyD: {
name: 'KeyD'
},
KeyF: {
name: 'KeyF'
},
KeyG: {
name: 'KeyG'
},
KeyH: {
name: 'KeyH'
},
KeyJ: {
name: 'KeyJ'
},
KeyK: {
name: 'KeyK'
},
KeyL: {
name: 'KeyL'
},
Semicolon: {
name: 'Semicolon'
},
Quote: {
name: 'Quote'
},
Enter: {
dark: true,
size: 110,
name: 'Enter'
},
PageUp: {
dark: true,
name: 'PageUp'
},
ShiftLeft: {
dark: true,
size: 115,
name: 'ShiftLeft'
},
KeyZ: {
name: 'KeyZ'
},
KeyX: {
name: 'KeyX'
},
KeyC: {
name: 'KeyC'
},
KeyV: {
name: 'KeyV'
},
KeyB: {
name: 'KeyB'
},
KeyN: {
name: 'KeyN'
},
KeyM: {
name: 'KeyM'
},
Comma: {
name: 'Comma'
},
Period: {
name: 'Period'
},
Slash: {
name: 'Slash'
},
ShiftRight: {
dark: true,
size: 85,
name: 'ShiftRight'
},
ArrowUp: {
dark: true,
name: 'ArrowUp'
},
PageDown: {
dark: true,
name: 'PageDown'
},
ControlLeft: {
dark: true,
size: 75,
name: 'ControlLeft'
},
MetaLeft: {
dark: true,
name: 'MetaLeft'
},
AltLeft: {
dark: true,
size: 75,
name: 'AltLeft'
},
Space: {
dark: true,
size: 300,
name: 'Space'
},
AltRight: {
dark: true,
size: 75,
name: 'AltRight'
},
ContextMenu: {
dark: true,
size: 75,
name: 'ContextMenu'
},
ArrowLeft: {
dark: true,
name: 'ArrowLeft'
},
ArrowDown: {
dark: true,
name: 'ArrowDown'
},
ArrowRight: {
dark: true,
name: 'ArrowRight'
}
}
// char => code for navigator.keyboard API
const codeMap: Record<string, KeyCode> = {
0: 'Digit0',
1: 'Digit1',
2: 'Digit2',
3: 'Digit3',
4: 'Digit4',
5: 'Digit5',
6: 'Digit6',
7: 'Digit7',
8: 'Digit8',
9: 'Digit9',
e: 'KeyE',
d: 'KeyD',
'-': 'Minus',
h: 'KeyH',
z: 'KeyZ',
'=': 'Equal',
n: 'KeyN',
p: 'KeyP',
']': 'BracketRight',
'[': 'BracketLeft',
s: 'KeyS',
';': 'Semicolon',
q: 'KeyQ',
o: 'KeyO',
'.': 'Period',
v: 'KeyV',
l: 'KeyL',
'`': 'Backquote',
g: 'KeyG',
j: 'KeyJ',
t: 'KeyT',
"'": 'Quote',
y: 'KeyY',
'\\': 'Backslash',
r: 'KeyR',
u: 'KeyU',
k: 'KeyK',
'/': 'Slash',
f: 'KeyF',
i: 'KeyI',
x: 'KeyX',
a: 'KeyA',
m: 'KeyM',
w: 'KeyW',
b: 'KeyB',
c: 'KeyC',
',': 'Comma'
}
export const layout: Partial<Record<KeyCode, KeyCode>> = {}
if (navigator.keyboard) {
navigator.keyboard.getLayoutMap().then((map) => {
for (const [key, value] of map.entries()) {
layout[key] = codeMap[value]
}
})
}

View file

@ -1,8 +1,8 @@
<script lang='ts'>
import { EllipsisVertical } from 'lucide-svelte'
import { tick } from 'svelte'
import Keybinds from 'svelte-keybinds'
import Keybinds from './keybinds.svelte'
import { normalizeSubs, normalizeTracks, type Chapter } from './util'
import type { Writable } from 'simple-store-svelte'

View file

@ -4,9 +4,9 @@
import { toast } from 'svelte-sonner'
import { fade } from 'svelte/transition'
import { onMount } from 'svelte'
import { condition, loadWithDefaults } from 'svelte-keybinds'
import VideoDeband from 'video-deband'
import { condition, loadWithDefaults } from './keybinds.svelte'
import Seekbar from './seekbar.svelte'
import { autoPiP, burnIn, getChaptersAniSkip, getChapterTitle, sanitizeChapters, type Chapter, type MediaInfo } from './util'
import Thumbnailer from './thumbnailer'

View file

@ -32,7 +32,7 @@ export default {
showDetailsInRPC: true,
torrentPath: '',
angle: 'default' as 'default' | 'd3d11'| 'd3d9' | 'warp' | 'gl' | 'gles' | 'swiftshader' | 'vulkan' | 'metal',
idleAnimation: 'fast' as 'fancy' | 'fast' | 'off',
idleAnimation: true,
enableExternal: false,
playerPath: '',
playerSeek: 2,

View file

@ -216,6 +216,10 @@ export async function traceAnime (image: File) { // WAIT lookup logic
}
}
export function codeToEmoji (c: string) {
return c.replace(/./g, (ch) => String.fromCodePoint(0x1f1a5 + ch.charCodeAt(0)))
}
export class HashMap<K extends object, T> {
map = new Map<string, T>()

View file

@ -47,7 +47,7 @@
<aside class='lg:grow lg:max-w-60'>
<SettingsNav {items} />
</aside>
<div class='flex-1 overflow-y-scroll' use:dragScroll>
<div class='flex-1 overflow-y-scroll pb-40' use:dragScroll>
<slot />
</div>
</div>

View file

@ -34,12 +34,6 @@
metal: 'Metal'
}
const idle = {
off: 'Off',
fast: 'Fast',
fancy: 'Fancy'
}
function changeAngle (value: string) {
native.setAngle(value)
}
@ -64,8 +58,8 @@
</SettingCard>
<div class='font-weight-bold text-xl font-bold'>UI Settings</div>
<SettingCard title='Idle Animation' description='Idle 3d animation settings. Fast turns off some heavy details. Fancy can cause performance issues on very low end devices.'>
<SingleCombo bind:value={$settings.idleAnimation} items={idle} class='w-40 shrink-0 border-input border' />
<SettingCard title='Idle Animation' description='Enable/Disable the 3d idle animation.'>
<Switch bind:checked={$settings.idleAnimation} />
</SettingCard>
{/if}
</div>

Binary file not shown.

BIN
static/twemoji-subset.woff2 Normal file

Binary file not shown.