mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-01-12 00:03:44 +00:00
fix: keybinds, improve animation perf
feat: subset fonts feat: extensions languages & sub/dub
This commit is contained in:
parent
70a4995bf8
commit
b8683300b4
19 changed files with 584 additions and 73 deletions
5
assets/logo.svg
Normal file
5
assets/logo.svg
Normal 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 |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
assets/twemoji.woff2
Normal file
BIN
assets/twemoji.woff2
Normal file
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
35
src/app.css
35
src/app.css
|
|
@ -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
4
src/app.d.ts
vendored
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
</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
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
189
src/lib/components/ui/player/keybinds.svelte
Normal file
189
src/lib/components/ui/player/keybinds.svelte
Normal 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>
|
||||
313
src/lib/components/ui/player/maps.ts
Normal file
313
src/lib/components/ui/player/maps.ts
Normal 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]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>()
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
BIN
static/Molot-webfont-subset.woff
Normal file
BIN
static/Molot-webfont-subset.woff
Normal file
Binary file not shown.
BIN
static/twemoji-subset.woff2
Normal file
BIN
static/twemoji-subset.woff2
Normal file
Binary file not shown.
Loading…
Reference in a new issue